summaryrefslogtreecommitdiff
path: root/java/com/android/dialer
diff options
context:
space:
mode:
authorEric Erfanian <erfanian@google.com>2017-03-15 14:41:07 -0700
committerEric Erfanian <erfanian@google.com>2017-03-15 16:24:23 -0700
commitd5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9 (patch)
treeb54abbb51fb7d66e7755a1fbb5db023ff601090b /java/com/android/dialer
parent30436e7e6d3f2c8755a91b2b6222b74d465a9e87 (diff)
Update Dialer source from latest green build.
* Refactor voicemail component * Add new enriched calling components Test: treehugger, manual aosp testing Change-Id: I521a0f86327d4b42e14d93927c7d613044ed5942
Diffstat (limited to 'java/com/android/dialer')
-rw-r--r--java/com/android/dialer/app/AndroidManifest.xml12
-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.java87
-rw-r--r--java/com/android/dialer/app/SpecialCharSequenceMgr.java46
-rw-r--r--java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java214
-rw-r--r--java/com/android/dialer/app/calllog/CallLogActivity.java220
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAdapter.java94
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java243
-rw-r--r--java/com/android/dialer/app/calllog/CallLogFragment.java93
-rw-r--r--java/com/android/dialer/app/calllog/CallLogListItemHelper.java11
-rw-r--r--java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java72
-rw-r--r--java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java (renamed from java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java)83
-rw-r--r--java/com/android/dialer/app/calllog/CallLogNotificationsService.java80
-rw-r--r--java/com/android/dialer/app/calllog/CallLogReceiver.java4
-rw-r--r--java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java264
-rw-r--r--java/com/android/dialer/app/calllog/IntentProvider.java27
-rw-r--r--java/com/android/dialer/app/calllog/MissedCallNotifier.java405
-rw-r--r--java/com/android/dialer/app/calllog/PhoneAccountHandles.java41
-rw-r--r--java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java5
-rw-r--r--java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java1
-rw-r--r--java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java5
-rw-r--r--java/com/android/dialer/app/calllog/VoicemailQueryHandler.java25
-rw-r--r--java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java10
-rw-r--r--java/com/android/dialer/app/contactinfo/ContactInfoCache.java4
-rw-r--r--java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java2
-rw-r--r--java/com/android/dialer/app/dialpad/DialpadFragment.java4
-rw-r--r--java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java5
-rw-r--r--java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java3
-rw-r--r--java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java4
-rw-r--r--java/com/android/dialer/app/list/ListsFragment.java26
-rw-r--r--java/com/android/dialer/app/list/SearchFragment.java11
-rw-r--r--java/com/android/dialer/app/manifests/activities/AndroidManifest.xml12
-rw-r--r--java/com/android/dialer/app/res/drawable-xxxhdpi/search_shadow.9.pngbin0 -> 1148 bytes
-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.xml3
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_activity.xml40
-rw-r--r--java/com/android/dialer/app/res/layout/call_log_list_item.xml3
-rw-r--r--java/com/android/dialer/app/res/menu/call_log_options.xml22
-rw-r--r--java/com/android/dialer/app/res/menu/dialtacts_options.xml8
-rw-r--r--java/com/android/dialer/app/res/values/colors.xml8
-rw-r--r--java/com/android/dialer/app/res/values/dimens.xml5
-rw-r--r--java/com/android/dialer/app/res/values/strings.xml84
-rw-r--r--java/com/android/dialer/app/res/values/styles.xml5
-rw-r--r--java/com/android/dialer/app/settings/DialerSettingsActivity.java13
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java7
-rw-r--r--java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java84
-rw-r--r--java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java113
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java51
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java2
-rw-r--r--java/com/android/dialer/app/voicemail/error/VoicemailStatus.java7
-rw-r--r--java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java2
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/layout/voicemai_error_message_fragment.xml1
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/layout/voicemail_tos_fragment.xml1
-rw-r--r--java/com/android/dialer/app/voicemail/error/res/values/strings.xml7
-rw-r--r--java/com/android/dialer/app/widget/ActionBarController.java88
-rw-r--r--java/com/android/dialer/backup/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/backup/DialerBackupAgent.java18
-rw-r--r--java/com/android/dialer/backup/DialerBackupUtils.java44
-rw-r--r--java/com/android/dialer/backup/nano/VoicemailInfo.java (renamed from java/com/android/dialer/backup/proto/VoicemailInfo.java)280
-rw-r--r--java/com/android/dialer/binary/aosp/AndroidManifest.xml116
-rw-r--r--java/com/android/dialer/binary/aosp/AospDialerApplication.java30
-rw-r--r--java/com/android/dialer/binary/aosp/AospDialerRootComponent.java30
-rw-r--r--java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java34
-rw-r--r--java/com/android/dialer/binary/common/DialerApplication.java43
-rw-r--r--java/com/android/dialer/blocking/FilteredNumbersUtil.java6
-rw-r--r--java/com/android/dialer/buildtype/BuildType.java3
-rw-r--r--java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java (renamed from java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java)2
-rw-r--r--java/com/android/dialer/callcomposer/AndroidManifest.xml5
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerActivity.java346
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerFragment.java75
-rw-r--r--java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java4
-rw-r--r--java/com/android/dialer/callcomposer/CameraComposerFragment.java43
-rw-r--r--java/com/android/dialer/callcomposer/GalleryComposerFragment.java44
-rw-r--r--java/com/android/dialer/callcomposer/GalleryGridAdapter.java12
-rw-r--r--java/com/android/dialer/callcomposer/GalleryGridItemData.java35
-rw-r--r--java/com/android/dialer/callcomposer/MessageComposerFragment.java8
-rw-r--r--java/com/android/dialer/callcomposer/camera/ImagePersistTask.java4
-rw-r--r--java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml8
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml7
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml2
-rw-r--r--java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml77
-rw-r--r--java/com/android/dialer/callcomposer/res/values-h260dp/values.xml19
-rw-r--r--java/com/android/dialer/callcomposer/res/values-h480dp/values.xml19
-rw-r--r--java/com/android/dialer/callcomposer/res/values-w360dp/values.xml19
-rw-r--r--java/com/android/dialer/callcomposer/res/values-w500dp/values.xml19
-rw-r--r--java/com/android/dialer/callcomposer/res/values/dimens.xml4
-rw-r--r--java/com/android/dialer/callcomposer/res/values/strings.xml6
-rw-r--r--java/com/android/dialer/callcomposer/res/values/styles.xml11
-rw-r--r--java/com/android/dialer/callcomposer/res/values/values.xml20
-rw-r--r--java/com/android/dialer/calldetails/AndroidManifest.xml31
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsActivity.java130
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsAdapter.java97
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java195
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java67
-rw-r--r--java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java125
-rw-r--r--java/com/android/dialer/calldetails/nano/CallDetailsEntries.java440
-rw-r--r--java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml20
-rw-r--r--java/com/android/dialer/calldetails/res/layout/call_details_activity.xml37
-rw-r--r--java/com/android/dialer/calldetails/res/layout/call_details_entry.xml73
-rw-r--r--java/com/android/dialer/calldetails/res/layout/call_details_footer.xml43
-rw-r--r--java/com/android/dialer/calldetails/res/layout/contact_container.xml60
-rw-r--r--java/com/android/dialer/calldetails/res/layout/ec_data_container.xml42
-rw-r--r--java/com/android/dialer/calldetails/res/menu/call_details_menu.xml23
-rw-r--r--java/com/android/dialer/calldetails/res/values/dimens.xml40
-rw-r--r--java/com/android/dialer/calldetails/res/values/strings.xml42
-rw-r--r--java/com/android/dialer/calldetails/res/values/styles.xml48
-rw-r--r--java/com/android/dialer/callintent/nano/CallInitiationType.java29
-rw-r--r--java/com/android/dialer/calllogutils/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/calllogutils/CallEntryFormatter.java113
-rw-r--r--java/com/android/dialer/calllogutils/CallTypeHelper.java (renamed from java/com/android/dialer/app/calllog/CallTypeHelper.java)3
-rw-r--r--java/com/android/dialer/calllogutils/CallTypeIconsView.java (renamed from java/com/android/dialer/app/calllog/CallTypeIconsView.java)29
-rw-r--r--java/com/android/dialer/calllogutils/PhoneAccountUtils.java (renamed from java/com/android/dialer/app/calllog/PhoneAccountUtils.java)2
-rw-r--r--java/com/android/dialer/calllogutils/PhoneCallDetails.java (renamed from java/com/android/dialer/app/PhoneCallDetails.java)3
-rw-r--r--java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java (renamed from java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java)8
-rw-r--r--java/com/android/dialer/calllogutils/res/values/colors.xml24
-rw-r--r--java/com/android/dialer/calllogutils/res/values/dimens.xml19
-rw-r--r--java/com/android/dialer/calllogutils/res/values/strings.xml90
-rw-r--r--java/com/android/dialer/common/Assert.java30
-rw-r--r--java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java79
-rw-r--r--java/com/android/dialer/common/FallibleAsyncTask.java6
-rw-r--r--java/com/android/dialer/common/PerAccountSharedPreferences.java146
-rw-r--r--java/com/android/dialer/common/proguard.flags4
-rw-r--r--java/com/android/dialer/common/res/values/config.xml4
-rw-r--r--java/com/android/dialer/constants/Constants.java14
-rw-r--r--java/com/android/dialer/database/CallLogQueryHandler.java22
-rw-r--r--java/com/android/dialer/debug/bindings/stub/DebugBindings.java27
-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.java13
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallComponent.java48
-rw-r--r--java/com/android/dialer/enrichedcall/EnrichedCallManager.java131
-rw-r--r--java/com/android/dialer/enrichedcall/FuzzyPhoneNumberMatcher.java20
-rw-r--r--java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java16
-rw-r--r--java/com/android/dialer/enrichedcall/Session.java7
-rw-r--r--java/com/android/dialer/enrichedcall/VideoShareSession.java20
-rw-r--r--java/com/android/dialer/enrichedcall/historyquery/HistoryQuery.java31
-rw-r--r--java/com/android/dialer/enrichedcall/historyquery/nano/HistoryResult.java203
-rw-r--r--java/com/android/dialer/enrichedcall/stub/EnrichedCallManagerStub.java (renamed from java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java)65
-rw-r--r--java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java (renamed from java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java)5
-rw-r--r--java/com/android/dialer/enrichedcall/videoshare/VideoShareListener.java14
-rw-r--r--java/com/android/dialer/inject/ContextModule.java (renamed from java/com/android/dialer/inject/ApplicationModule.java)18
-rw-r--r--java/com/android/dialer/inject/HasRootComponent.java (renamed from java/com/android/dialer/inject/DialerAppComponent.java)16
-rw-r--r--java/com/android/dialer/interactions/PhoneNumberInteraction.java32
-rw-r--r--java/com/android/dialer/logging/nano/ContactLookupResult.java29
-rw-r--r--java/com/android/dialer/logging/nano/ContactSource.java29
-rw-r--r--java/com/android/dialer/logging/nano/DialerImpression.java69
-rw-r--r--java/com/android/dialer/logging/nano/InteractionEvent.java2
-rw-r--r--java/com/android/dialer/logging/nano/ReportingLocation.java29
-rw-r--r--java/com/android/dialer/logging/nano/ScreenEvent.java2
-rw-r--r--java/com/android/dialer/multimedia/AutoValue_MultimediaData.java165
-rw-r--r--java/com/android/dialer/multimedia/MultimediaData.java33
-rw-r--r--java/com/android/dialer/notification/AndroidManifest.xml21
-rw-r--r--java/com/android/dialer/notification/GroupedNotificationUtil.java66
-rw-r--r--java/com/android/dialer/notification/NotificationChannelManager.java232
-rw-r--r--java/com/android/dialer/notification/res/values/ids.xml27
-rw-r--r--java/com/android/dialer/notification/res/values/strings.xml25
-rw-r--r--java/com/android/dialer/oem/AndroidManifest.xml3
-rw-r--r--java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java153
-rw-r--r--java/com/android/dialer/oem/MotorolaUtils.java51
-rw-r--r--java/com/android/dialer/oem/res/values/motorola_config.xml64
-rw-r--r--java/com/android/dialer/p13n/inference/P13nRanking.java49
-rw-r--r--java/com/android/dialer/p13n/inference/protocol/P13nRanker.java10
-rw-r--r--java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java4
-rw-r--r--java/com/android/dialer/postcall/AndroidManifest.xml28
-rw-r--r--java/com/android/dialer/postcall/PostCall.java182
-rw-r--r--java/com/android/dialer/postcall/PostCallActivity.java151
-rw-r--r--java/com/android/dialer/postcall/res/layout/post_call_activity.xml38
-rw-r--r--java/com/android/dialer/postcall/res/values/strings.xml31
-rw-r--r--java/com/android/dialer/postcall/res/values/values.xml19
-rw-r--r--java/com/android/dialer/proguard/proguard.flags6
-rw-r--r--java/com/android/dialer/proguard/proguard_base.flags74
-rw-r--r--java/com/android/dialer/proguard/proguard_release.flags24
-rw-r--r--java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java161
-rw-r--r--java/com/android/dialer/shortcuts/CallContactActivity.java16
-rw-r--r--java/com/android/dialer/shortcuts/DialerShortcut.java6
-rw-r--r--java/com/android/dialer/shortcuts/res/values/strings.xml6
-rw-r--r--java/com/android/dialer/shortcuts/res/xml/shortcuts.xml4
-rw-r--r--java/com/android/dialer/simulator/SimulatorComponent.java46
-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/SimulatorCallLog.java6
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorContacts.java6
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorImpl.java40
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorModule.java22
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoicemail.java6
-rw-r--r--java/com/android/dialer/telecom/TelecomUtil.java33
-rw-r--r--java/com/android/dialer/theme/res/drawable-hdpi/ic_block_24dp.pngbin0 -> 478 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-hdpi/ic_call_arrow.png (renamed from java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png)bin538 -> 538 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-mdpi/ic_block_24dp.pngbin0 -> 335 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-mdpi/ic_call_arrow.png (renamed from java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png)bin455 -> 455 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xhdpi/ic_block_24dp.pngbin0 -> 665 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xhdpi/ic_call_arrow.png (renamed from java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png)bin627 -> 627 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xxhdpi/ic_block_24dp.pngbin0 -> 973 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xxhdpi/ic_call_arrow.png (renamed from java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png)bin1203 -> 1203 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_block_24dp.pngbin0 -> 1295 bytes
-rw-r--r--java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_call_arrow.png (renamed from java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png)bin1344 -> 1344 bytes
-rw-r--r--java/com/android/dialer/theme/res/values/dimens.xml5
-rw-r--r--java/com/android/dialer/theme/res/values/styles.xml11
-rw-r--r--java/com/android/dialer/util/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/util/PermissionsUtil.java4
-rw-r--r--java/com/android/dialer/util/SettingsUtil.java9
-rw-r--r--java/com/android/dialer/util/ViewUtil.java13
-rw-r--r--java/com/android/dialer/widget/MessageFragment.java172
-rw-r--r--java/com/android/dialer/widget/res/color/dialer_tint_state.xml23
-rw-r--r--java/com/android/dialer/widget/res/layout/fragment_message.xml81
-rw-r--r--java/com/android/dialer/widget/res/layout/selectable_text_view.xml25
-rw-r--r--java/com/android/dialer/widget/res/values/dimens.xml23
-rw-r--r--java/com/android/dialer/widget/res/values/strings.xml5
212 files changed, 6937 insertions, 3809 deletions
diff --git a/java/com/android/dialer/app/AndroidManifest.xml b/java/com/android/dialer/app/AndroidManifest.xml
index 80f294acc..5ce13dbd7 100644
--- a/java/com/android/dialer/app/AndroidManifest.xml
+++ b/java/com/android/dialer/app/AndroidManifest.xml
@@ -57,11 +57,7 @@
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">
+ <application android:theme="@style/Theme.AppCompat">
<activity
android:exported="false"
@@ -75,6 +71,12 @@
</intent-filter>
</activity>
+ <activity
+ android:label="@string/call_log_activity_title"
+ android:name="com.android.dialer.app.calllog.CallLogActivity"
+ android:theme="@style/DialtactsThemeWithoutActionBarOverlay">
+ </activity>
+
<receiver android:name="com.android.dialer.app.calllog.CallLogReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_VOICEMAIL"/>
diff --git a/java/com/android/dialer/app/CallDetailActivity.java b/java/com/android/dialer/app/CallDetailActivity.java
deleted file mode 100644
index cda2b2e2c..000000000
--- a/java/com/android/dialer/app/CallDetailActivity.java
+++ /dev/null
@@ -1,480 +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.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
deleted file mode 100644
index 3b979212b..000000000
--- a/java/com/android/dialer/app/DialerApplication.java
+++ /dev/null
@@ -1,77 +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.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
index 4c57cda70..b2837769f 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -63,15 +63,14 @@ 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.CallLogActivity;
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;
@@ -85,6 +84,7 @@ 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.callcomposer.CallComposerActivity;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.callintent.nano.CallSpecificAppData;
import com.android.dialer.common.Assert;
@@ -101,7 +101,10 @@ 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.postcall.PostCall;
import com.android.dialer.proguard.UsedByReflection;
+import com.android.dialer.simulator.Simulator;
+import com.android.dialer.simulator.SimulatorComponent;
import com.android.dialer.smartdial.SmartDialNameMatcher;
import com.android.dialer.smartdial.SmartDialPrefix;
import com.android.dialer.telecom.TelecomUtil;
@@ -124,7 +127,6 @@ public class DialtactsActivity extends TransactionSafeActivity
OnListFragmentScrolledListener,
CallLogFragment.HostInterface,
DialpadFragment.HostInterface,
- ListsFragment.HostInterface,
SpeedDialFragment.HostInterface,
SearchFragment.HostInterface,
OnDragDropListener,
@@ -478,6 +480,7 @@ public class DialtactsActivity extends TransactionSafeActivity
@Override
protected void onResume() {
+ LogUtil.d("DialtactsActivity.onResume", "");
Trace.beginSection(TAG + " onResume");
super.onResume();
@@ -490,6 +493,8 @@ public class DialtactsActivity extends TransactionSafeActivity
} else if (mShowDialpadOnResume) {
showDialpadFragment(false);
mShowDialpadOnResume = false;
+ } else {
+ PostCall.promptUserForMessageIfNecessary(this, mParentLayout);
}
// If there was a voice query result returned in the {@link #onActivityResult} callback, it
@@ -539,7 +544,7 @@ public class DialtactsActivity extends TransactionSafeActivity
}
if (getIntent().getBooleanExtra(EXTRA_CLEAR_NEW_VOICEMAILS, false)) {
- CallLogNotificationsService.markNewVoicemailsAsOld(this);
+ CallLogNotificationsService.markNewVoicemailsAsOld(this, null);
}
setSearchBoxHint();
@@ -588,6 +593,7 @@ public class DialtactsActivity extends TransactionSafeActivity
@Override
public void onAttachFragment(final Fragment fragment) {
+ LogUtil.d("DialtactsActivity.onAttachFragment", "fragment: %s", fragment);
if (fragment instanceof DialpadFragment) {
mDialpadFragment = (DialpadFragment) fragment;
if (!mIsDialpadShown && !mShowDialpadOnResume) {
@@ -616,7 +622,8 @@ public class DialtactsActivity extends TransactionSafeActivity
@MainThread
public Cursor rerankCursor(Cursor data) {
Assert.isMainThread();
- return mP13nRanker.rankCursor(data, PhoneQuery.PHONE_NUMBER);
+ String queryString = searchFragment.getQueryString();
+ return mP13nRanker.rankCursor(data, queryString == null ? 0 : queryString.length());
}
});
searchFragment.addOnLoadFinishedListener(
@@ -674,9 +681,9 @@ public class DialtactsActivity extends TransactionSafeActivity
}
int resId = item.getItemId();
- if (item.getItemId() == R.id.menu_delete_all) {
- ClearCallLogDialog.show(getFragmentManager());
- return true;
+ if (resId == R.id.menu_history) {
+ final Intent intent = new Intent(this, CallLogActivity.class);
+ startActivity(intent);
} else if (resId == R.id.menu_clear_frequents) {
ClearFrequentsDialog.show(getFragmentManager());
Logger.get(this).logScreenView(ScreenEvent.Type.CLEAR_FREQUENTS, this);
@@ -691,6 +698,11 @@ public class DialtactsActivity extends TransactionSafeActivity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ LogUtil.i(
+ "DialtactsActivity.onActivityResult",
+ "requestCode:%d, resultCode:%d",
+ requestCode,
+ resultCode);
if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
if (resultCode == RESULT_OK) {
final ArrayList<String> matches =
@@ -701,15 +713,16 @@ public class DialtactsActivity extends TransactionSafeActivity
LogUtil.i("DialtactsActivity.onActivityResult", "voice search - nothing heard");
}
} else {
- LogUtil.e("DialtactsActivity.onActivityResult", "voice search failed: " + resultCode);
+ LogUtil.e("DialtactsActivity.onActivityResult", "voice search failed");
}
} else if (requestCode == ACTIVITY_REQUEST_CODE_CALL_COMPOSE) {
- if (resultCode != RESULT_OK) {
+ if (resultCode == RESULT_FIRST_USER) {
LogUtil.i(
- "DialtactsActivity.onActivityResult",
- "returned from call composer, error occurred (resultCode=" + resultCode + ")");
+ "DialtactsActivity.onActivityResult", "returned from call composer, error occurred");
String message =
- getString(R.string.call_composer_connection_failed, getString(R.string.share_and_call));
+ getString(
+ R.string.call_composer_connection_failed,
+ data.getStringExtra(CallComposerActivity.KEY_CONTACT_NAME));
Snackbar.make(mParentLayout, message, Snackbar.LENGTH_LONG).show();
} else {
LogUtil.i("DialtactsActivity.onActivityResult", "returned from call composer, no error");
@@ -732,6 +745,7 @@ public class DialtactsActivity extends TransactionSafeActivity
* @see #onDialpadShown
*/
private void showDialpadFragment(boolean animate) {
+ LogUtil.d("DialtactActivity.showDialpadFragment", "animate: %b", animate);
if (mIsDialpadShown || mStateSaved) {
return;
}
@@ -767,6 +781,7 @@ public class DialtactsActivity extends TransactionSafeActivity
/** Callback from child DialpadFragment when the dialpad is shown. */
public void onDialpadShown() {
+ LogUtil.d("DialtactsActivity.onDialpadShown", "");
Assert.isNotNull(mDialpadFragment);
if (mDialpadFragment.getAnimate()) {
Assert.isNotNull(mDialpadFragment.getView()).startAnimation(mSlideIn);
@@ -838,12 +853,21 @@ public class DialtactsActivity extends TransactionSafeActivity
private void updateSearchFragmentPosition() {
SearchFragment fragment = null;
- if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
+ if (mSmartDialSearchFragment != null) {
fragment = mSmartDialSearchFragment;
- } else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
+ } else if (mRegularSearchFragment != null) {
fragment = mRegularSearchFragment;
}
- if (fragment != null && fragment.isVisible()) {
+ LogUtil.d(
+ "DialtactsActivity.updateSearchFragmentPosition",
+ "fragment: %s, isVisible: %b",
+ fragment,
+ fragment != null && fragment.isVisible());
+ if (fragment != null) {
+ // We need to force animation here even when fragment is not visible since it might not be
+ // visible immediately after screen orientation change and dialpad height would not be
+ // available immediately which is required to update position. By forcing an animation,
+ // position will be updated after a delay by when the dialpad height would be available.
fragment.updatePosition(true /* animate */);
}
}
@@ -858,11 +882,6 @@ public class DialtactsActivity extends TransactionSafeActivity
return !TextUtils.isEmpty(mSearchQuery);
}
- @Override
- public boolean shouldShowActionBar() {
- return mListsFragment.shouldShowActionBar();
- }
-
private void setNotInSearchUi() {
mInDialpadSearch = false;
mInRegularSearch = false;
@@ -1056,7 +1075,8 @@ public class DialtactsActivity extends TransactionSafeActivity
}
// DialtactsActivity will provide the options menu
fragment.setHasOptionsMenu(false);
- fragment.setShowEmptyListForNullQuery(true);
+ // Will show empty list if P13nRanker is not enabled. Else, re-ranked list by the ranker.
+ fragment.setShowEmptyListForNullQuery(mP13nRanker.shouldShowEmptyListForNullQuery());
if (!smartDialSearch) {
fragment.setQueryString(query);
}
@@ -1361,11 +1381,6 @@ public class DialtactsActivity extends TransactionSafeActivity
}
@Override
- public ActionBarController getActionBarController() {
- return mActionBarController;
- }
-
- @Override
public boolean isDialpadShown() {
return mIsDialpadShown;
}
@@ -1379,11 +1394,6 @@ public class DialtactsActivity extends TransactionSafeActivity
}
@Override
- public int getActionBarHideOffset() {
- return getActionBarSafely().getHideOffset();
- }
-
- @Override
public void setActionBarHideOffset(int offset) {
getActionBarSafely().setHideOffset(offset);
}
@@ -1461,8 +1471,19 @@ public class DialtactsActivity extends TransactionSafeActivity
&& mListsFragment.getSpeedDialFragment().hasFrequents()
&& hasContactsPermission);
- menu.findItem(R.id.menu_delete_all)
+ menu.findItem(R.id.menu_history)
.setVisible(PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
+
+ Context context = DialtactsActivity.this.getApplicationContext();
+ MenuItem simulatorMenuItem = menu.findItem(R.id.menu_simulator_submenu);
+ Simulator simulator = SimulatorComponent.get(context).getSimulator();
+ if (simulator.shouldShow()) {
+ simulatorMenuItem.setVisible(true);
+ simulatorMenuItem.setActionProvider(simulator.getActionProvider(context));
+ } else {
+ simulatorMenuItem.setVisible(false);
+ }
+
super.show();
}
}
diff --git a/java/com/android/dialer/app/SpecialCharSequenceMgr.java b/java/com/android/dialer/app/SpecialCharSequenceMgr.java
index 2ae19704a..712659c12 100644
--- a/java/com/android/dialer/app/SpecialCharSequenceMgr.java
+++ b/java/com/android/dialer/app/SpecialCharSequenceMgr.java
@@ -28,15 +28,14 @@ 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.support.v4.os.BuildCompat;
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;
@@ -46,8 +45,11 @@ 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.calllogutils.PhoneAccountUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.oem.MotorolaUtils;
import com.android.dialer.telecom.TelecomUtil;
import java.util.ArrayList;
import java.util.List;
@@ -100,12 +102,19 @@ public class SpecialCharSequenceMgr {
//get rid of the separators so that the string gets parsed correctly
String dialString = PhoneNumberUtils.stripSeparators(input);
- return handleDeviceIdDisplay(context, dialString)
+ if (handleDeviceIdDisplay(context, dialString)
|| handleRegulatoryInfoDisplay(context, dialString)
|| handlePinEntry(context, dialString)
|| handleAdnEntry(context, dialString, textField)
- || handleSecretCode(context, dialString);
+ || handleSecretCode(context, dialString)) {
+ return true;
+ }
+
+ if (MotorolaUtils.handleSpecialCharSequence(context, input)) {
+ return true;
+ }
+ return false;
}
/**
@@ -114,10 +123,7 @@ public class SpecialCharSequenceMgr {
* <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;
- }
+ Assert.isMainThread();
if (sPreviousAdnQueryHandler != null) {
sPreviousAdnQueryHandler.cancel();
@@ -126,14 +132,21 @@ public class SpecialCharSequenceMgr {
}
/**
- * 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.
+ * 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
+ * @return true if a secret code was encountered and intent is sent out
*/
static boolean handleSecretCode(Context context, String input) {
+ // Must use system service on O+ to avoid using broadcasts, which are not allowed on O+.
+ if (BuildCompat.isAtLeastO()) {
+ return context.getSystemService(TelephonyManager.class).sendDialerCode(input);
+ }
+
+ // System service call is not supported pre-O, so must use a broadcast for N-.
// Secret codes are in the form *#*#<code>#*#*
int len = input.length();
if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
@@ -144,7 +157,6 @@ public class SpecialCharSequenceMgr {
context.sendBroadcast(intent);
return true;
}
-
return false;
}
@@ -237,7 +249,7 @@ public class SpecialCharSequenceMgr {
private static void handleAdnQuery(QueryHandler handler, SimContactQueryCookie cookie, Uri uri) {
if (handler == null || cookie == null || uri == null) {
- Log.w(TAG, "queryAdn parameters incorrect");
+ LogUtil.w("SpecialCharSequenceMgr.handleAdnQuery", "queryAdn parameters incorrect");
return;
}
@@ -325,12 +337,14 @@ public class SpecialCharSequenceMgr {
private static boolean handleRegulatoryInfoDisplay(Context context, String input) {
if (input.equals(MMI_REGULATORY_INFO_DISPLAY)) {
- Log.d(TAG, "handleRegulatoryInfoDisplay() sending intent to settings app");
+ LogUtil.i(
+ "SpecialCharSequenceMgr.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);
+ LogUtil.e(
+ "SpecialCharSequenceMgr.handleRegulatoryInfoDisplay", "startActivity() failed: ", e);
}
return true;
}
diff --git a/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java b/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java
deleted file mode 100644
index ab6ef7362..000000000
--- a/java/com/android/dialer/app/calllog/CallDetailHistoryAdapter.java
+++ /dev/null
@@ -1,214 +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.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/CallLogActivity.java b/java/com/android/dialer/app/calllog/CallLogActivity.java
new file mode 100644
index 000000000..719ab4369
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/CallLogActivity.java
@@ -0,0 +1,220 @@
+/*
+ * 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.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.os.Bundle;
+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.ViewGroup;
+import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.dialer.app.DialtactsActivity;
+import com.android.dialer.app.R;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.util.TransactionSafeActivity;
+import com.android.dialer.util.ViewUtil;
+
+/** Activity for viewing call history. */
+public class CallLogActivity extends TransactionSafeActivity
+ implements ViewPager.OnPageChangeListener {
+
+ 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 ViewPager mViewPager;
+ private ViewPagerTabs mViewPagerTabs;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private CallLogFragment mAllCallsFragment;
+ private CallLogFragment mMissedCallsFragment;
+ private String[] mTabTitles;
+ private boolean mIsResumed;
+
+ @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.get(this).logScreenView(ScreenEvent.Type.CALL_LOG_FILTER, this);
+ }
+
+ private int getRtlPosition(int position) {
+ if (ViewUtil.isRtl()) {
+ return mViewPagerAdapter.getCount() - 1 - position;
+ }
+ return position;
+ }
+
+ /** Adapter for the view pager. */
+ 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;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index ea09a8c0a..fc5ffbb29 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -47,7 +47,6 @@ 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;
@@ -55,13 +54,19 @@ 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.calldetails.nano.CallDetailsEntries;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.calllogutils.PhoneAccountUtils;
+import com.android.dialer.calllogutils.PhoneCallDetails;
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.EnrichedCallComponent;
import com.android.dialer.enrichedcall.EnrichedCallManager;
import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
+import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult;
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.nano.DialerImpression;
import com.android.dialer.phonenumbercache.CallLogQuery;
@@ -70,6 +75,7 @@ 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.List;
import java.util.Map;
import java.util.Set;
@@ -96,7 +102,7 @@ public class CallLogAdapter extends GroupingListAdapter
protected final CallLogCache mCallLogCache;
private final CallFetcher mCallFetcher;
- private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
+ @NonNull private final FilteredNumberAsyncQueryHandler mFilteredNumberAsyncQueryHandler;
private final int mActivityType;
/** Instance of helper class for managing views. */
@@ -182,8 +188,6 @@ public class CallLogAdapter extends GroupingListAdapter
private boolean mIsSpamEnabled;
- @NonNull private final EnrichedCallManager mEnrichedCallManager;
-
public CallLogAdapter(
Activity activity,
ViewGroup alertContainer,
@@ -191,6 +195,7 @@ public class CallLogAdapter extends GroupingListAdapter
CallLogCache callLogCache,
ContactInfoCache contactInfoCache,
VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ @NonNull FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
int activityType) {
super();
@@ -218,7 +223,7 @@ public class CallLogAdapter extends GroupingListAdapter
mCallLogListItemHelper =
new CallLogListItemHelper(phoneCallDetailsHelper, resources, mCallLogCache);
mCallLogGroupBuilder = new CallLogGroupBuilder(this);
- mFilteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(mActivity);
+ mFilteredNumberAsyncQueryHandler = Assert.isNotNull(filteredNumberAsyncQueryHandler);
mContactsPreferences = new ContactsPreferences(mActivity);
@@ -232,7 +237,6 @@ public class CallLogAdapter extends GroupingListAdapter
mCallLogAlertManager =
new CallLogAlertManager(this, LayoutInflater.from(mActivity), alertContainer);
- mEnrichedCallManager = EnrichedCallManager.Accessor.getInstance(activity.getApplication());
}
private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
@@ -296,7 +300,7 @@ public class CallLogAdapter extends GroupingListAdapter
}
mContactsPreferences.refreshValue(ContactsPreferences.DISPLAY_ORDER_KEY);
mIsSpamEnabled = Spam.get(mActivity).isSpamEnabled();
- mEnrichedCallManager.registerCapabilitiesListener(this);
+ getEnrichedCallManager().registerCapabilitiesListener(this);
notifyDataSetChanged();
}
@@ -305,11 +309,11 @@ public class CallLogAdapter extends GroupingListAdapter
for (Uri uri : mHiddenItemUris) {
CallLogAsyncTaskUtil.deleteVoicemail(mActivity, uri, null);
}
- mEnrichedCallManager.unregisterCapabilitiesListener(this);
+ getEnrichedCallManager().unregisterCapabilitiesListener(this);
}
public void onStop() {
- mEnrichedCallManager.clearCachedData();
+ getEnrichedCallManager().clearCachedData();
}
public CallLogAlertManager getAlertManager() {
@@ -420,7 +424,9 @@ public class CallLogAdapter extends GroupingListAdapter
}
CallLogListItemViewHolder views = (CallLogListItemViewHolder) viewHolder;
views.isLoaded = false;
- PhoneCallDetails details = createPhoneCallDetails(c, getGroupSize(position), views);
+ int groupSize = getGroupSize(position);
+ CallDetailsEntries callDetailsEntries = createCallDetailsEntries(c, groupSize);
+ PhoneCallDetails details = createPhoneCallDetails(c, groupSize, views);
if (mHiddenRowIds.contains(c.getLong(CallLogQuery.ID))) {
views.callLogEntryView.setVisibility(View.GONE);
views.dayGroupHeader.setVisibility(View.GONE);
@@ -432,11 +438,14 @@ public class CallLogAdapter extends GroupingListAdapter
if (mCurrentlyExpandedRowId == views.rowId) {
views.inflateActionViewStub();
}
- loadAndRender(views, views.rowId, details);
+ loadAndRender(views, views.rowId, details, callDetailsEntries);
}
private void loadAndRender(
- final CallLogListItemViewHolder views, final long rowId, final PhoneCallDetails details) {
+ final CallLogListItemViewHolder views,
+ final long rowId,
+ final PhoneCallDetails details,
+ final CallDetailsEntries callDetailsEntries) {
// Reset block and spam information since this view could be reused which may contain
// outdated data.
views.isSpam = false;
@@ -464,12 +473,33 @@ public class CallLogAdapter extends GroupingListAdapter
&& Spam.get(mActivity)
.checkSpamStatusSynchronous(views.number, views.countryIso);
details.isSpam = views.isSpam;
- if (isCancelled()) {
- return false;
+ }
+ if (isCancelled()) {
+ return false;
+ }
+ setCallDetailsEntriesHistoryResults(
+ PhoneNumberUtils.formatNumberToE164(views.number, views.countryIso),
+ callDetailsEntries);
+ views.setDetailedPhoneDetails(callDetailsEntries);
+ return !isCancelled() && loadData(views, rowId, details);
+ }
+
+ private void setCallDetailsEntriesHistoryResults(
+ @Nullable String number, CallDetailsEntries callDetailsEntries) {
+ if (number == null) {
+ return;
+ }
+ Map<CallDetailsEntry, List<HistoryResult>> mappedResults =
+ getEnrichedCallManager().getAllHistoricalData(number, callDetailsEntries);
+ for (CallDetailsEntry entry : callDetailsEntries.entries) {
+ List<HistoryResult> results = mappedResults.get(entry);
+ if (results != null) {
+ entry.historyResults = mappedResults.get(entry).toArray(new HistoryResult[0]);
+ LogUtil.v(
+ "CallLogAdapter.setCallDetailsEntriesHistoryResults",
+ "mapped %d results",
+ entry.historyResults.length);
}
- return loadData(views, rowId, details);
- } else {
- return loadData(views, rowId, details);
}
}
@@ -499,9 +529,9 @@ public class CallLogAdapter extends GroupingListAdapter
return false;
}
- EnrichedCallCapabilities capabilities = mEnrichedCallManager.getCapabilities(e164Number);
+ EnrichedCallCapabilities capabilities = getEnrichedCallManager().getCapabilities(e164Number);
if (capabilities == null) {
- mEnrichedCallManager.requestCapabilities(e164Number);
+ getEnrichedCallManager().requestCapabilities(e164Number);
return false;
}
return capabilities.supportsCallComposer();
@@ -562,6 +592,27 @@ public class CallLogAdapter extends GroupingListAdapter
return details;
}
+ @MainThread
+ private static CallDetailsEntries createCallDetailsEntries(Cursor cursor, int count) {
+ Assert.isMainThread();
+ int position = cursor.getPosition();
+ CallDetailsEntries entries = new CallDetailsEntries();
+ entries.entries = new CallDetailsEntry[count];
+ for (int i = 0; i < count; i++) {
+ CallDetailsEntry entry = new CallDetailsEntry();
+ entry.callId = cursor.getLong(CallLogQuery.ID);
+ entry.callType = cursor.getInt(CallLogQuery.CALL_TYPE);
+ entry.dataUsage = cursor.getLong(CallLogQuery.DATA_USAGE);
+ entry.date = cursor.getLong(CallLogQuery.DATE);
+ entry.duration = cursor.getLong(CallLogQuery.DURATION);
+ entry.features |= cursor.getInt(CallLogQuery.FEATURES);
+ entries.entries[i] = entry;
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(position);
+ return entries;
+ }
+
/**
* 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.
@@ -907,6 +958,11 @@ public class CallLogAdapter extends GroupingListAdapter
notifyDataSetChanged();
}
+ @NonNull
+ private EnrichedCallManager getEnrichedCallManager() {
+ return EnrichedCallComponent.get(mActivity).getEnrichedCallManager();
+ }
+
/** Interface used to initiate a refresh of the content. */
public interface CallFetcher {
diff --git a/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java
index b4e6fc5ad..2198626d6 100644
--- a/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java
+++ b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java
@@ -16,37 +16,22 @@
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;
+import com.android.voicemail.VoicemailClient;
@TargetApi(VERSION_CODES.M)
public class CallLogAsyncTaskUtil {
@@ -58,166 +43,6 @@ public class CallLogAsyncTaskUtil {
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) {
@@ -235,6 +60,8 @@ public class CallLogAsyncTaskUtil {
.getContentResolver()
.update(voicemailUri, values, Voicemails.IS_READ + " = 0", null);
+ uploadVoicemailLocalChangesToServer(context);
+
Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
context.startService(intent);
@@ -256,7 +83,12 @@ public class CallLogAsyncTaskUtil {
new AsyncTask<Void, Void, Void>() {
@Override
public Void doInBackground(Void... params) {
- context.getContentResolver().delete(voicemailUri, null, null);
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.DELETED, "1");
+ context.getContentResolver().update(voicemailUri, values, null, null);
+ // TODO(b/35440541): check which source package is changed. Don't need
+ // to upload changes on foreign voicemails, they will get a PROVIDER_CHANGED
+ uploadVoicemailLocalChangesToServer(context);
return null;
}
@@ -305,11 +137,6 @@ public class CallLogAsyncTaskUtil {
});
}
- @VisibleForTesting
- public static void resetForTest() {
- sAsyncTaskExecutor = null;
- }
-
/** The enumeration of {@link AsyncTask} objects used in this class. */
public enum Tasks {
DELETE_VOICEMAIL,
@@ -321,56 +148,12 @@ public class CallLogAsyncTaskUtil {
}
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()]);
- }
+ private static void uploadVoicemailLocalChangesToServer(Context context) {
+ Intent intent = new Intent(VoicemailClient.ACTION_UPLOAD);
+ intent.setPackage(context.getPackageName());
+ context.sendBroadcast(intent);
}
}
diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java
index 1ae68cd65..4abef3430 100644
--- a/java/com/android/dialer/app/calllog/CallLogFragment.java
+++ b/java/com/android/dialer/app/calllog/CallLogFragment.java
@@ -53,6 +53,7 @@ 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.blocking.FilteredNumberAsyncQueryHandler;
import com.android.dialer.common.LogUtil;
import com.android.dialer.database.CallLogQueryHandler;
import com.android.dialer.phonenumbercache.ContactInfoHelper;
@@ -70,9 +71,17 @@ public class CallLogFragment extends Fragment
FragmentCompat.OnRequestPermissionsResultCallback,
CallLogModalAlertManager.Listener {
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";
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";
+ // 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;
@@ -104,8 +113,17 @@ public class CallLogFragment extends Fragment
// 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 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;
private final Handler mDisplayUpdateHandler =
new Handler() {
@Override
@@ -121,6 +139,48 @@ public class CallLogFragment extends Fragment
protected CallLogModalAlertManager mModalAlertManager;
private ViewGroup mModalAlertView;
+ 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) {
LogUtil.d("CallLogFragment.onCreate", toString());
@@ -128,13 +188,16 @@ public class CallLogFragment extends Fragment
mRefreshDataRequired = true;
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);
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);
+ mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
mKeyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
resolver.registerContentObserver(
@@ -226,7 +289,10 @@ public class CallLogFragment extends Fragment
}
protected void setupData() {
- int activityType = CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
+ int activityType =
+ mIsCallLogActivity
+ ? CallLogAdapter.ACTIVITY_TYPE_CALL_LOG
+ : CallLogAdapter.ACTIVITY_TYPE_DIALTACTS;
String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
mContactInfoCache =
@@ -244,6 +310,7 @@ public class CallLogFragment extends Fragment
CallLogCache.getCallLogCache(getActivity()),
mContactInfoCache,
getVoicemailPlaybackPresenter(),
+ new FilteredNumberAsyncQueryHandler(getActivity()),
activityType);
mRecyclerView.setAdapter(mAdapter);
fetchCalls();
@@ -324,6 +391,9 @@ public class CallLogFragment extends Fragment
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);
outState.putBoolean(KEY_HAS_READ_CALL_LOG_PERMISSION, mHasReadCallLogPermission);
outState.putBoolean(KEY_REFRESH_DATA_REQUIRED, mRefreshDataRequired);
@@ -334,8 +404,10 @@ public class CallLogFragment extends Fragment
@Override
public void fetchCalls() {
- mCallLogQueryHandler.fetchCalls(mCallTypeFilter);
- ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
+ mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+ if (!mIsCallLogActivity) {
+ ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
+ }
}
private void updateEmptyMessage(int filterType) {
@@ -366,7 +438,9 @@ public class CallLogFragment extends Fragment
"Unexpected filter type in CallLogFragment: " + filterType);
}
mEmptyListView.setDescription(messageId);
- if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
+ if (mIsCallLogActivity) {
+ mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
+ } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
}
}
@@ -420,7 +494,7 @@ public class CallLogFragment extends Fragment
if (mKeyguardManager != null
&& !mKeyguardManager.inKeyguardRestrictedInputMode()
&& mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
- CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
+ CallLogNotificationsQueryHelper.updateVoicemailNotifications(getActivity());
}
}
@@ -434,7 +508,8 @@ public class CallLogFragment extends Fragment
if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
FragmentCompat.requestPermissions(
this, new String[] {READ_CALL_LOG}, READ_CALL_LOG_PERMISSION_REQUEST_CODE);
- } else {
+ } else if (!mIsCallLogActivity) {
+ // Show dialpad if we are not in the call log activity.
((HostInterface) activity).showDialpad();
}
}
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemHelper.java b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java
index ea2119c83..a5df8cca1 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemHelper.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java
@@ -21,18 +21,16 @@ 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.calllogutils.PhoneCallDetails;
import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
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. */
@@ -105,7 +103,9 @@ import com.android.dialer.compat.AppCompatConstants;
*/
public void setActionContentDescriptions(CallLogListItemViewHolder views) {
if (views.nameOrNumber == null) {
- Log.e(TAG, "setActionContentDescriptions; name or number is null.");
+ LogUtil.e(
+ "CallLogListItemHelper.setActionContentDescriptions",
+ "setActionContentDescriptions; name or number is null.");
}
// Calling expandTemplate with a null parameter will cause a NullPointerException.
@@ -170,7 +170,6 @@ import com.android.dialer.compat.AppCompatConstants;
*
* <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.
*/
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
index 6abd36078..8a2d94499 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -25,9 +25,11 @@ import android.os.AsyncTask;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
@@ -56,7 +58,7 @@ 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.calldetails.nano.CallDetailsEntries;
import com.android.dialer.common.LogUtil;
import com.android.dialer.compat.CompatUtils;
import com.android.dialer.logging.Logger;
@@ -78,8 +80,6 @@ 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. */
@@ -201,6 +201,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
public boolean isAttachedToWindow;
public AsyncTask<Void, Void, ?> asyncTask;
+ private CallDetailsEntries callDetailsEntries;
private CallLogListItemViewHolder(
Context context,
@@ -549,10 +550,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
}
- 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.
@@ -577,13 +574,14 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
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);
+ voicemailPlaybackView,
+ rowId,
+ uri,
+ mVoicemailPrimaryActionButtonClicked,
+ sendVoicemailButtonView);
mVoicemailPrimaryActionButtonClicked = false;
CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
return;
@@ -621,14 +619,14 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
&& 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);
+ voicemailPlaybackView,
+ rowId,
+ uri,
+ mVoicemailPrimaryActionButtonClicked,
+ sendVoicemailButtonView);
mVoicemailPrimaryActionButtonClicked = false;
CallLogAsyncTaskUtil.markVoicemailAsRead(mContext, uri);
} else {
@@ -640,7 +638,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
detailsButtonView.setVisibility(View.GONE);
} else {
detailsButtonView.setVisibility(View.VISIBLE);
- detailsButtonView.setTag(IntentProvider.getCallDetailIntentProvider(rowId, callIds, null));
+ detailsButtonView.setTag(
+ IntentProvider.getCallDetailIntentProvider(callDetailsEntries, buildContact()));
}
boolean isBlockedOrSpam = blockId != null || (isSpamFeatureEnabled && isSpam);
@@ -776,6 +775,8 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
contactType = ContactPhotoManager.TYPE_VOICEMAIL;
} else if (isBusiness) {
contactType = ContactPhotoManager.TYPE_BUSINESS;
+ } else if (numberPresentation == TelecomManager.PRESENTATION_RESTRICTED) {
+ contactType = ContactPhotoManager.TYPE_GENERIC_AVATAR;
}
final String lookupKey =
@@ -854,20 +855,9 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
} 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),
+ CallComposerActivity.newIntent(activity, buildContact()),
DialtactsActivity.ACTIVITY_REQUEST_CODE_CALL_COMPOSE);
} else if (view.getId() == R.id.share_voicemail) {
Logger.get(mContext).logImpression(DialerImpression.Type.VVM_SHARE_PRESSED);
@@ -885,6 +875,21 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
}
+ private CallComposerContact buildContact() {
+ 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;
+ return contact;
+ }
+
private void logCallLogAction(int id) {
if (id == R.id.send_message_action) {
Logger.get(mContext).logImpression(DialerImpression.Type.CALL_LOG_SEND_MESSAGE);
@@ -931,6 +936,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder
}
}
+ public void setDetailedPhoneDetails(CallDetailsEntries callDetailsEntries) {
+ this.callDetailsEntries = callDetailsEntries;
+ }
+
+ @VisibleForTesting
+ public CallDetailsEntries getDetailedPhoneDetails() {
+ return callDetailsEntries;
+ }
+
public interface OnClickListener {
void onBlockReportSpam(
diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java b/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java
index 8f664d1a4..f12837e6f 100644
--- a/java/com/android/dialer/app/calllog/CallLogNotificationsHelper.java
+++ b/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java
@@ -18,37 +18,41 @@ package com.android.dialer.app.calllog;
import android.Manifest;
import android.annotation.TargetApi;
+import android.app.NotificationManager;
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.os.Build.VERSION_CODES;
import android.provider.CallLog.Calls;
import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.support.v4.os.UserManagerCompat;
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.calllogutils.PhoneNumberDisplayUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.notification.GroupedNotificationUtil;
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 {
+public class CallLogNotificationsQueryHelper {
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(
+ CallLogNotificationsQueryHelper(
Context context,
NewCallsQuery newCallsQuery,
ContactInfoHelper contactInfoHelper,
@@ -59,29 +63,60 @@ public class CallLogNotificationsHelper {
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;
+ /** Returns an instance of {@link CallLogNotificationsQueryHelper}. */
+ public static CallLogNotificationsQueryHelper getInstance(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+ String countryIso = GeoUtil.getCurrentCountryIso(context);
+ return new CallLogNotificationsQueryHelper(
+ context,
+ createNewCallsQuery(context, contentResolver),
+ new ContactInfoHelper(context, countryIso),
+ countryIso);
}
- /** Removes the missed call notifications. */
- public static void removeMissedCallNotifications(Context context) {
- TelecomUtil.cancelMissedCallsNotification(context);
+ /**
+ * Removes the missed call notifications and marks calls as read. If a callUri is provided, only
+ * that call is marked as read.
+ */
+ @WorkerThread
+ public static void removeMissedCallNotifications(Context context, @Nullable Uri callUri) {
+ // 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(context) && PermissionsUtil.hasPhonePermissions(context)) {
+ 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 {
+ context
+ .getContentResolver()
+ .update(
+ callUri == null ? Calls.CONTENT_URI : callUri,
+ values,
+ where.toString(),
+ new String[] {Integer.toString(Calls.MISSED_TYPE)});
+ } catch (IllegalArgumentException e) {
+ LogUtil.e(
+ "CallLogNotificationsQueryHelper.removeMissedCallNotifications",
+ "contacts provider update command failed",
+ e);
+ }
+ }
+
+ GroupedNotificationUtil.removeNotification(
+ context.getSystemService(NotificationManager.class),
+ callUri != null ? callUri.toString() : null,
+ R.id.notification_missed_call,
+ MissedCallNotifier.NOTIFICATION_TAG);
}
/** Update the voice mail notifications. */
public static void updateVoicemailNotifications(Context context) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
+ CallLogNotificationsService.updateVoicemailNotifications(context);
}
/** Create a new instance of {@link NewCallsQuery}. */
@@ -251,7 +286,7 @@ public class CallLogNotificationsHelper {
@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.");
+ LogUtil.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);
@@ -272,7 +307,7 @@ public class CallLogNotificationsHelper {
}
return newCalls;
} catch (RuntimeException e) {
- Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
+ LogUtil.w(TAG, "Exception when querying Contacts Provider for calls lookup");
return null;
}
}
diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
index 820528126..b0d48eee5 100644
--- a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
+++ b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
@@ -20,6 +20,7 @@ import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.support.annotation.Nullable;
import com.android.dialer.common.LogUtil;
import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.PermissionsUtil;
@@ -44,21 +45,10 @@ 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}.
- */
+ /** Action to update voicemail notifications. */
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
@@ -66,9 +56,15 @@ public class CallLogNotificationsService extends IntentService {
*/
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 update missed call notifications with a post call note. */
+ public static final String ACTION_INCOMING_POST_CALL =
+ "com.android.dialer.calllog.INCOMING_POST_CALL";
+
/** 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";
@@ -92,6 +88,21 @@ public class CallLogNotificationsService extends IntentService {
*/
public static final String EXTRA_MISSED_CALL_COUNT = "MISSED_CALL_COUNT";
+ /**
+ * Extra to be included with {@link #ACTION_INCOMING_POST_CALL} to represent a post call note.
+ *
+ * <p>It must be a {@link String}
+ */
+ public static final String EXTRA_POST_CALL_NOTE = "POST_CALL_NOTE";
+
+ /**
+ * Extra to be included with {@link #ACTION_INCOMING_POST_CALL} to represent the phone number the
+ * post call note came from.
+ *
+ * <p>It must be a {@link String}
+ */
+ public static final String EXTRA_POST_CALL_NUMBER = "POST_CALL_NUMBER";
+
public static final int UNKNOWN_MISSED_CALL_COUNT = -1;
private VoicemailQueryHandler mVoicemailQueryHandler;
@@ -103,10 +114,8 @@ public class CallLogNotificationsService extends IntentService {
* 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) {
+ public static void updateVoicemailNotifications(Context context) {
if (!TelecomUtil.isDefaultDialer(context)) {
LogUtil.i(
"CallLogNotificationsService.updateVoicemailNotifications",
@@ -116,10 +125,6 @@ public class CallLogNotificationsService extends IntentService {
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);
}
}
@@ -139,9 +144,25 @@ public class CallLogNotificationsService extends IntentService {
context.startService(serviceIntent);
}
- public static void markNewVoicemailsAsOld(Context context) {
+ public static void insertPostCallNote(Context context, String number, String postCallNote) {
+ Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+ serviceIntent.setAction(ACTION_INCOMING_POST_CALL);
+ serviceIntent.putExtra(EXTRA_POST_CALL_NUMBER, number);
+ serviceIntent.putExtra(EXTRA_POST_CALL_NOTE, postCallNote);
+ context.startService(serviceIntent);
+ }
+
+ public static void markNewVoicemailsAsOld(Context context, @Nullable Uri voicemailUri) {
Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
serviceIntent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
+ serviceIntent.setData(voicemailUri);
+ context.startService(serviceIntent);
+ }
+
+ public static void markNewMissedCallsAsOld(Context context, @Nullable Uri callUri) {
+ Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
+ serviceIntent.setAction(ACTION_MARK_NEW_MISSED_CALLS_AS_OLD);
+ serviceIntent.setData(callUri);
context.startService(serviceIntent);
}
@@ -172,11 +193,10 @@ public class CallLogNotificationsService extends IntentService {
if (mVoicemailQueryHandler == null) {
mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver());
}
- mVoicemailQueryHandler.markNewVoicemailsAsOld();
+ mVoicemailQueryHandler.markNewVoicemailsAsOld(intent.getData());
break;
case ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS:
- Uri voicemailUri = intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI);
- DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri);
+ DefaultVoicemailNotifier.getInstance(this).updateNotification();
break;
case ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS:
int count = intent.getIntExtra(EXTRA_MISSED_CALL_COUNT, UNKNOWN_MISSED_CALL_COUNT);
@@ -184,16 +204,24 @@ public class CallLogNotificationsService extends IntentService {
MissedCallNotifier.getInstance(this).updateMissedCallNotification(count, number);
updateBadgeCount(this, count);
break;
+ case ACTION_INCOMING_POST_CALL:
+ String note = intent.getStringExtra(EXTRA_POST_CALL_NOTE);
+ String phoneNumber = intent.getStringExtra(EXTRA_POST_CALL_NUMBER);
+ MissedCallNotifier.getInstance(this).insertPostCallNotification(phoneNumber, note);
+ break;
case ACTION_MARK_NEW_MISSED_CALLS_AS_OLD:
- CallLogNotificationsHelper.removeMissedCallNotifications(this);
+ CallLogNotificationsQueryHelper.removeMissedCallNotifications(this, intent.getData());
+ TelecomUtil.cancelMissedCallsNotification(this);
break;
case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION:
MissedCallNotifier.getInstance(this)
- .callBackFromMissedCall(intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
+ .callBackFromMissedCall(
+ intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER), intent.getData());
break;
case ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION:
MissedCallNotifier.getInstance(this)
- .sendSmsFromMissedCall(intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER));
+ .sendSmsFromMissedCall(
+ intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER), intent.getData());
break;
default:
LogUtil.d("CallLogNotificationsService.onHandleIntent", "could not handle: " + intent);
diff --git a/java/com/android/dialer/app/calllog/CallLogReceiver.java b/java/com/android/dialer/app/calllog/CallLogReceiver.java
index a781b0887..8fd1502bc 100644
--- a/java/com/android/dialer/app/calllog/CallLogReceiver.java
+++ b/java/com/android/dialer/app/calllog/CallLogReceiver.java
@@ -38,9 +38,9 @@ public class CallLogReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
checkVoicemailStatus(context);
- CallLogNotificationsService.updateVoicemailNotifications(context, intent.getData());
+ CallLogNotificationsService.updateVoicemailNotifications(context);
} else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- CallLogNotificationsService.updateVoicemailNotifications(context, null);
+ CallLogNotificationsService.updateVoicemailNotifications(context);
} else {
LogUtil.w("CallLogReceiver.onReceive", "could not handle: " + intent);
}
diff --git a/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java
index 651a0ccb8..cc1dc4f20 100644
--- a/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java
+++ b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java
@@ -19,31 +19,33 @@ 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.graphics.Bitmap;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
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.calllog.CallLogNotificationsQueryHelper.NewCall;
+import com.android.dialer.app.contactinfo.ContactPhotoLoader;
import com.android.dialer.app.list.ListsFragment;
import com.android.dialer.blocking.FilteredNumbersUtil;
-import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.notification.NotificationChannelManager;
+import com.android.dialer.notification.NotificationChannelManager.Channel;
+import com.android.dialer.phonenumbercache.ContactInfo;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -54,26 +56,23 @@ 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";
+ static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier";
/** The identifier of the notification of new voicemails. */
- private static final int NOTIFICATION_ID = 1;
+ private static final int NOTIFICATION_ID = R.id.notification_voicemail;
- /** The singleton instance of {@link DefaultVoicemailNotifier}. */
- private static DefaultVoicemailNotifier sInstance;
+ private final Context context;
+ private final CallLogNotificationsQueryHelper queryHelper;
- private final Context mContext;
-
- private DefaultVoicemailNotifier(Context context) {
- mContext = context;
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ DefaultVoicemailNotifier(Context context, CallLogNotificationsQueryHelper queryHelper) {
+ this.context = context;
+ this.queryHelper = queryHelper;
}
- /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
+ /** Returns an instance of {@link DefaultVoicemailNotifier}. */
public static DefaultVoicemailNotifier getInstance(Context context) {
- if (sInstance == null) {
- ContentResolver contentResolver = context.getContentResolver();
- sInstance = new DefaultVoicemailNotifier(context);
- }
- return sInstance;
+ return new DefaultVoicemailNotifier(
+ context, CallLogNotificationsQueryHelper.getInstance(context));
}
/**
@@ -84,34 +83,23 @@ public class DefaultVoicemailNotifier {
*
* <p>It is not safe to call this method from the main thread.
*/
- public void updateNotification(Uri newCallUri) {
+ public void updateNotification() {
// 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();
+ final List<NewCall> newCalls = queryHelper.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();
+ Resources resources = context.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;
+ final Map<String, ContactInfo> contactInfos = new ArrayMap<>();
// Iterate over the new voicemails to determine all the information above.
Iterator<NewCall> itr = newCalls.iterator();
@@ -120,95 +108,64 @@ public class DefaultVoicemailNotifier {
// Skip notifying for numbers which are blocked.
if (FilteredNumbersUtil.shouldBlockVoicemail(
- mContext, newCall.number, newCall.countryIso, newCall.dateMs)) {
+ context, newCall.number, newCall.countryIso, newCall.dateMs)) {
itr.remove();
// Delete the voicemail.
- mContext.getContentResolver().delete(newCall.voicemailUri, null, null);
+ context.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);
+ ContactInfo contactInfo = contactInfos.get(newCall.number);
+ if (contactInfo == null) {
+ contactInfo =
+ queryHelper.getContactInfo(
+ newCall.number, newCall.numberPresentation, newCall.countryIso);
+ contactInfos.put(newCall.number, contactInfo);
// This is a new caller. Add it to the back of the list of callers.
if (TextUtils.isEmpty(callers)) {
- callers = name;
+ callers = contactInfo.name;
} else {
callers =
- resources.getString(R.string.notification_voicemail_callers_list, callers, name);
+ resources.getString(
+ R.string.notification_voicemail_callers_list, callers, contactInfo.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()) {
+ // No voicemails to notify about: clear the notification.
+ CallLogNotificationsService.markNewVoicemailsAsOld(context, null);
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)
+ Notification.Builder groupSummary =
+ createNotificationBuilder()
+ .setContentTitle(
+ resources.getQuantityString(
+ R.plurals.notification_voicemail_title, newCalls.size(), newCalls.size()))
.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));
+ .setDeleteIntent(createMarkNewVoicemailsAsOldIntent(null))
+ .setGroupSummary(true)
+ .setContentIntent(newVoicemailIntent(null));
+
+ NotificationChannelManager.applyChannel(
+ groupSummary,
+ context,
+ Channel.VOICEMAIL,
+ PhoneAccountHandles.getAccount(context, newCalls.get(0)));
+
+ LogUtil.i(TAG, "Creating voicemail notification");
+ getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID, groupSummary.build());
+
+ for (NewCall voicemail : newCalls) {
+ getNotificationManager()
+ .notify(
+ voicemail.voicemailUri.toString(),
+ NOTIFICATION_ID,
+ createNotificationForVoicemail(voicemail, contactInfos));
}
-
- // 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());
}
/**
@@ -216,30 +173,15 @@ public class DefaultVoicemailNotifier {
* for the given call.
*/
private Pair<Uri, Integer> getNotificationInfo(@Nullable NewCall callToNotify) {
- Log.v(TAG, "getNotificationInfo");
+ LogUtil.v(TAG, "getNotificationInfo");
if (callToNotify == null) {
- Log.i(TAG, "callToNotify == null");
+ LogUtil.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");
+ PhoneAccountHandle accountHandle = PhoneAccountHandles.getAccount(context, callToNotify);
+ if (accountHandle == null) {
+ LogUtil.i(TAG, "No default phone account found, using default notification ringtone");
+ return new Pair<>(null, Notification.DEFAULT_ALL);
}
return new Pair<>(
TelephonyManagerCompat.getVoicemailRingtoneUri(getTelephonyManager(), accountHandle),
@@ -257,17 +199,79 @@ public class DefaultVoicemailNotifier {
}
/** Creates a pending intent that marks all new voicemails as old. */
- private PendingIntent createMarkNewVoicemailsAsOldIntent() {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ private PendingIntent createMarkNewVoicemailsAsOldIntent(@Nullable Uri voicemailUri) {
+ Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
+ intent.setData(voicemailUri);
+ return PendingIntent.getService(context, 0, intent, 0);
}
private NotificationManager getNotificationManager() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
private TelephonyManager getTelephonyManager() {
- return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ private Notification createNotificationForVoicemail(
+ @NonNull NewCall voicemail, @NonNull Map<String, ContactInfo> contactInfos) {
+ Pair<Uri, Integer> notificationInfo = getNotificationInfo(voicemail);
+ ContactInfo contactInfo = contactInfos.get(voicemail.number);
+
+ Notification.Builder notificationBuilder =
+ createNotificationBuilder()
+ .setContentTitle(
+ context
+ .getResources()
+ .getQuantityString(R.plurals.notification_voicemail_title, 1, 1))
+ .setContentText(
+ ContactDisplayUtils.getTtsSpannedPhoneNumber(
+ context.getResources(),
+ R.string.notification_new_voicemail_ticker,
+ contactInfo.name))
+ .setWhen(voicemail.dateMs)
+ .setSound(notificationInfo.first)
+ .setDefaults(notificationInfo.second)
+ .setDeleteIntent(createMarkNewVoicemailsAsOldIntent(voicemail.voicemailUri));
+
+ NotificationChannelManager.applyChannel(
+ notificationBuilder,
+ context,
+ Channel.VOICEMAIL,
+ PhoneAccountHandles.getAccount(context, voicemail));
+
+ ContactPhotoLoader loader = new ContactPhotoLoader(context, contactInfo);
+ Bitmap photoIcon = loader.loadPhotoIcon();
+ if (photoIcon != null) {
+ notificationBuilder.setLargeIcon(photoIcon);
+ }
+
+ if (!TextUtils.isEmpty(voicemail.transcription)) {
+ notificationBuilder.setStyle(
+ new Notification.BigTextStyle().bigText(voicemail.transcription));
+ }
+ notificationBuilder.setContentIntent(newVoicemailIntent(voicemail));
+
+ return notificationBuilder.build();
+ }
+
+ private Notification.Builder createNotificationBuilder() {
+ return new Notification.Builder(context)
+ .setSmallIcon(android.R.drawable.stat_notify_voicemail)
+ .setColor(context.getColor(R.color.dialer_theme_color))
+ .setGroup(NOTIFICATION_TAG)
+ .setOnlyAlertOnce(true)
+ .setAutoCancel(true);
+ }
+
+ private PendingIntent newVoicemailIntent(@Nullable NewCall voicemail) {
+ Intent intent = DialtactsActivity.getShowTabIntent(context, ListsFragment.TAB_INDEX_VOICEMAIL);
+ // TODO (b/35486204): scroll to this voicemail
+ if (voicemail != null) {
+ intent.setData(voicemail.voicemailUri);
+ }
+ intent.putExtra(DialtactsActivity.EXTRA_CLEAR_NEW_VOICEMAILS, true);
+ return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java
index 879ac353d..c53e3ec5e 100644
--- a/java/com/android/dialer/app/calllog/IntentProvider.java
+++ b/java/com/android/dialer/app/calllog/IntentProvider.java
@@ -16,7 +16,6 @@
package com.android.dialer.app.calllog;
-import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -25,10 +24,11 @@ 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.callcomposer.nano.CallComposerContact;
+import com.android.dialer.calldetails.CallDetailsActivity;
+import com.android.dialer.calldetails.nano.CallDetailsEntries;
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;
@@ -97,29 +97,16 @@ public abstract class IntentProvider {
/**
* 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.
+ * @param callDetailsEntries The call details of the other calls grouped together with the call.
+ * @param contact The contact with which this call details intent pertains to.
* @return The call details intent provider.
*/
public static IntentProvider getCallDetailIntentProvider(
- final long id, final long[] extraIds, final String voicemailUri) {
+ CallDetailsEntries callDetailsEntries, CallComposerContact contact) {
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;
+ return CallDetailsActivity.newInstance(context, callDetailsEntries, contact);
}
};
}
diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
index 2fa3dae65..5b5661615 100644
--- a/java/com/android/dialer/app/calllog/MissedCallNotifier.java
+++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
@@ -16,16 +16,20 @@
package com.android.dialer.app.calllog;
import android.app.Notification;
+import android.app.Notification.Builder;
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.graphics.drawable.Icon;
+import android.net.Uri;
import android.provider.CallLog.Calls;
+import android.service.notification.StatusBarNotification;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
import android.support.v4.os.UserManagerCompat;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
@@ -34,109 +38,117 @@ 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.calllog.CallLogNotificationsQueryHelper.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.notification.NotificationChannelManager;
+import com.android.dialer.notification.NotificationChannelManager.Channel;
import com.android.dialer.phonenumbercache.ContactInfo;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/** 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";
+ static final String NOTIFICATION_TAG = "MissedCallNotifier";
/** The identifier of the notification of new missed calls. */
- private static final int NOTIFICATION_ID = 1;
+ private static final int NOTIFICATION_ID = R.id.notification_missed_call;
- private static MissedCallNotifier sInstance;
- private Context mContext;
- private CallLogNotificationsHelper mCalllogNotificationsHelper;
+ private final Context context;
+ private final CallLogNotificationsQueryHelper callLogNotificationsQueryHelper;
@VisibleForTesting
- MissedCallNotifier(Context context, CallLogNotificationsHelper callLogNotificationsHelper) {
- mContext = context;
- mCalllogNotificationsHelper = callLogNotificationsHelper;
+ MissedCallNotifier(
+ Context context, CallLogNotificationsQueryHelper callLogNotificationsQueryHelper) {
+ this.context = context;
+ this.callLogNotificationsQueryHelper = callLogNotificationsQueryHelper;
}
- /** Returns the singleton instance of the {@link MissedCallNotifier}. */
+ /** Returns an instance of {@link MissedCallNotifier}. */
public static MissedCallNotifier getInstance(Context context) {
- if (sInstance == null) {
- CallLogNotificationsHelper callLogNotificationsHelper =
- CallLogNotificationsHelper.getInstance(context);
- sInstance = new MissedCallNotifier(context, callLogNotificationsHelper);
- }
- return sInstance;
+ CallLogNotificationsQueryHelper callLogNotificationsQueryHelper =
+ CallLogNotificationsQueryHelper.getInstance(context);
+ return new MissedCallNotifier(context, callLogNotificationsQueryHelper);
}
/**
- * Creates a missed call notification with a post call message if there are no existing missed
- * calls.
+ * Update missed call notifications from the call log. Accepts default information in case call
+ * log cannot be accessed.
+ *
+ * @param count the number of missed calls to display if call log cannot be accessed. May be
+ * {@link CallLogNotificationsService#UNKNOWN_MISSED_CALL_COUNT} if unknown.
+ * @param number the phone number of the most recent call to display if the call log cannot be
+ * accessed. May be null if unknown.
*/
- 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) {
+ @WorkerThread
+ public void updateMissedCallNotification(int count, @Nullable String number) {
final int titleResId;
CharSequence expandedText; // The text in the notification's line 1 and 2.
- final List<NewCall> newCalls = mCalllogNotificationsHelper.getNewMissedCalls();
+ List<NewCall> newCalls = callLogNotificationsQueryHelper.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;
+ if ((newCalls != null && newCalls.isEmpty()) || count == 0) {
+ // No calls to notify about: clear the notification.
+ CallLogNotificationsQueryHelper.removeMissedCallNotifications(context, null);
+ return;
+ }
+
+ if (newCalls != null) {
+ if (count != CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT
+ && count != newCalls.size()) {
+ LogUtil.w(
+ "MissedCallNotifier.updateMissedCallNotification",
+ "Call count does not match call log count."
+ + " count: "
+ + count
+ + " newCalls.size(): "
+ + newCalls.size());
}
count = newCalls.size();
}
- if (count == 0) {
- // No voicemails to notify about: clear the notification.
- clearMissedCalls();
+ if (count == CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT) {
+ // 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;
}
- // 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 groupSummary = createNotificationBuilder();
+ boolean useCallList = newCalls != null;
- 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) {
+ NewCall call =
+ useCallList
+ ? newCalls.get(0)
+ : new NewCall(
+ null,
+ null,
+ number,
+ Calls.PRESENTATION_ALLOWED,
+ null,
+ null,
+ null,
+ null,
+ System.currentTimeMillis());
+
//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);
-
+ callLogNotificationsQueryHelper.getContactInfo(
+ call.number, call.numberPresentation, call.countryIso);
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 =
@@ -147,134 +159,195 @@ public class MissedCallNotifier {
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);
+ ContactPhotoLoader loader = new ContactPhotoLoader(context, contactInfo);
Bitmap photoIcon = loader.loadPhotoIcon();
if (photoIcon != null) {
- builder.setLargeIcon(photoIcon);
+ groupSummary.setLargeIcon(photoIcon);
}
} else {
titleResId = R.string.notification_missedCallsTitle;
- expandedText = mContext.getString(R.string.notification_missedCallsMsg, count);
+ expandedText = context.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))
+ Notification.Builder publicSummaryBuilder = createNotificationBuilder();
+ publicSummaryBuilder
+ .setContentTitle(context.getText(titleResId))
.setContentIntent(createCallLogPendingIntent())
- .setAutoCancel(true)
- .setWhen(timeMs)
- .setShowWhen(true)
- .setDeleteIntent(createClearMissedCallsPendingIntent());
+ .setDeleteIntent(createClearMissedCallsPendingIntent(null));
+ // Create the notification summary suitable for display when sensitive information is showing.
+ groupSummary
+ .setContentTitle(context.getText(titleResId))
+ .setContentText(expandedText)
+ .setContentIntent(createCallLogPendingIntent())
+ .setDeleteIntent(createClearMissedCallsPendingIntent(null))
+ .setGroupSummary(useCallList)
+ .setOnlyAlertOnce(useCallList)
+ .setPublicVersion(publicSummaryBuilder.build());
+
+ NotificationChannelManager.applyChannel(
+ groupSummary,
+ context,
+ Channel.MISSED_CALL,
+ PhoneAccountHandles.getAccount(context, useCallList ? newCalls.get(0) : null));
+
+ Notification notification = groupSummary.build();
+ configureLedOnNotification(notification);
+
+ LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification");
+ getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
+
+ if (useCallList) {
+ // Do not repost active notifications to prevent erasing post call notes.
+ NotificationManager manager = getNotificationMgr();
+ Set<String> activeTags = new HashSet<>();
+ for (StatusBarNotification activeNotification : manager.getActiveNotifications()) {
+ activeTags.add(activeNotification.getTag());
+ }
+
+ for (NewCall call : newCalls) {
+ String callTag = call.callsUri.toString();
+ if (!activeTags.contains(callTag)) {
+ manager.notify(callTag, NOTIFICATION_ID, getNotificationForCall(call, null));
+ }
+ }
+ }
+ }
+
+ public void insertPostCallNotification(@NonNull String number, @NonNull String note) {
+ List<NewCall> newCalls = callLogNotificationsQueryHelper.getNewMissedCalls();
+ if (newCalls != null && !newCalls.isEmpty()) {
+ for (NewCall call : newCalls) {
+ if (call.number.equals(number.replace("tel:", ""))) {
+ // Update the first notification that matches our post call note sender.
+ getNotificationMgr()
+ .notify(
+ call.callsUri.toString(), NOTIFICATION_ID, getNotificationForCall(call, note));
+ break;
+ }
+ }
+ }
+ }
+
+ private Notification getNotificationForCall(
+ @NonNull NewCall call, @Nullable String postCallMessage) {
+ ContactInfo contactInfo =
+ callLogNotificationsQueryHelper.getContactInfo(
+ call.number, call.numberPresentation, call.countryIso);
+
+ // Create a public viewable version of the notification, suitable for display when sensitive
+ // notification content is hidden.
+ int titleResId =
+ contactInfo.userType == ContactsUtils.USER_TYPE_WORK
+ ? R.string.notification_missedWorkCallTitle
+ : R.string.notification_missedCallTitle;
+ Notification.Builder publicBuilder =
+ createNotificationBuilder(call).setContentTitle(context.getText(titleResId));
+
+ Notification.Builder builder = createNotificationBuilder(call);
+ CharSequence expandedText;
+ 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 (postCallMessage != null) {
+ expandedText =
+ context.getString(R.string.post_call_notification_message, expandedText, postCallMessage);
+ }
+
+ ContactPhotoLoader loader = new ContactPhotoLoader(context, contactInfo);
+ Bitmap photoIcon = loader.loadPhotoIcon();
+ if (photoIcon != null) {
+ builder.setLargeIcon(photoIcon);
+ }
// 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))
+ .setContentTitle(context.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))) {
+ // Add additional actions when the user isn't locked
+ if (UserManagerCompat.isUserUnlocked(context)) {
+ if (!TextUtils.isEmpty(call.number)
+ && !TextUtils.equals(call.number, context.getString(R.string.handle_restricted))) {
builder.addAction(
- R.drawable.ic_phone_24dp,
- mContext.getString(R.string.notification_missedCall_call_back),
- createCallBackPendingIntent(missedNumber));
+ new Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_phone_24dp),
+ context.getString(R.string.notification_missedCall_call_back),
+ createCallBackPendingIntent(call.number, call.callsUri))
+ .build());
- if (!PhoneNumberHelper.isUriNumber(missedNumber)) {
+ if (!PhoneNumberHelper.isUriNumber(call.number)) {
builder.addAction(
- R.drawable.ic_message_24dp,
- mContext.getString(R.string.notification_missedCall_message),
- createSendSmsFromNotificationPendingIntent(missedNumber));
+ new Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_message_24dp),
+ context.getString(R.string.notification_missedCall_message),
+ createSendSmsFromNotificationPendingIntent(call.number, call.callsUri))
+ .build());
}
}
}
Notification notification = builder.build();
configureLedOnNotification(notification);
+ return notification;
+ }
- LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification");
- getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
+ private Notification.Builder createNotificationBuilder() {
+ return new Notification.Builder(context)
+ .setGroup(NOTIFICATION_TAG)
+ .setSmallIcon(android.R.drawable.stat_notify_missed_call)
+ .setColor(context.getResources().getColor(R.color.dialer_theme_color, null))
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setShowWhen(true)
+ .setDefaults(Notification.DEFAULT_VIBRATE);
}
- 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);
- }
- });
+ private Notification.Builder createNotificationBuilder(@NonNull NewCall call) {
+ Builder builder =
+ createNotificationBuilder()
+ .setWhen(call.dateMs)
+ .setDeleteIntent(createClearMissedCallsPendingIntent(call.callsUri))
+ .setContentIntent(createCallLogPendingIntent(call.callsUri));
+
+ NotificationChannelManager.applyChannel(
+ builder, context, Channel.MISSED_CALL, PhoneAccountHandles.getAccount(context, call));
+ return builder;
}
/** Trigger an intent to make a call from a missed call number. */
- public void callBackFromMissedCall(String number) {
- closeSystemDialogs(mContext);
- CallLogNotificationsHelper.removeMissedCallNotifications(mContext);
+ @WorkerThread
+ public void callBackFromMissedCall(String number, Uri callUri) {
+ closeSystemDialogs(context);
+ CallLogNotificationsQueryHelper.removeMissedCallNotifications(context, callUri);
+ TelecomUtil.cancelMissedCallsNotification(context);
DialerUtils.startActivityWithErrorToast(
- mContext,
+ context,
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);
+ @WorkerThread
+ public void sendSmsFromMissedCall(String number, Uri callUri) {
+ closeSystemDialogs(context);
+ CallLogNotificationsQueryHelper.removeMissedCallNotifications(context, callUri);
+ TelecomUtil.cancelMissedCallsNotification(context);
DialerUtils.startActivityWithErrorToast(
- mContext, IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ context, IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
/**
@@ -283,34 +356,50 @@ public class MissedCallNotifier {
* @return The pending intent.
*/
private PendingIntent createCallLogPendingIntent() {
+ return createCallLogPendingIntent(null);
+ }
+
+ /**
+ * Creates a new pending intent that sends the user to the call log.
+ *
+ * @return The pending intent.
+ * @param callUri Uri of the call to jump to. May be null
+ */
+ private PendingIntent createCallLogPendingIntent(@Nullable Uri callUri) {
Intent contentIntent =
- DialtactsActivity.getShowTabIntent(mContext, ListsFragment.TAB_INDEX_HISTORY);
- return PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ DialtactsActivity.getShowTabIntent(context, ListsFragment.TAB_INDEX_HISTORY);
+ // TODO (b/35486204): scroll to call
+ contentIntent.setData(callUri);
+ return PendingIntent.getActivity(context, 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);
+ private PendingIntent createClearMissedCallsPendingIntent(@Nullable Uri callUri) {
+ Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD);
- return PendingIntent.getService(mContext, 0, intent, 0);
+ intent.setData(callUri);
+ return PendingIntent.getService(context, 0, intent, 0);
}
- private PendingIntent createCallBackPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ private PendingIntent createCallBackPendingIntent(String number, @NonNull Uri callUri) {
+ Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION);
intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
+ intent.setData(callUri);
// 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);
+ return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
- private PendingIntent createSendSmsFromNotificationPendingIntent(String number) {
- Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+ private PendingIntent createSendSmsFromNotificationPendingIntent(
+ String number, @NonNull Uri callUri) {
+ Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION);
intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
+ intent.setData(callUri);
// 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);
+ return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/** Configures a notification to emit the blinky notification light. */
@@ -325,6 +414,6 @@ public class MissedCallNotifier {
}
private NotificationManager getNotificationMgr() {
- return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
}
diff --git a/java/com/android/dialer/app/calllog/PhoneAccountHandles.java b/java/com/android/dialer/app/calllog/PhoneAccountHandles.java
new file mode 100644
index 000000000..6d51b853c
--- /dev/null
+++ b/java/com/android/dialer/app/calllog/PhoneAccountHandles.java
@@ -0,0 +1,41 @@
+package com.android.dialer.app.calllog;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import com.android.dialer.app.calllog.CallLogNotificationsQueryHelper.NewCall;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.telecom.TelecomUtil;
+
+/** Methods to help extract {@link PhoneAccount} information from database and Telecomm sources. */
+class PhoneAccountHandles {
+
+ @Nullable
+ public static PhoneAccountHandle getAccount(@NonNull Context context, @Nullable NewCall call) {
+ PhoneAccountHandle handle;
+ if (call == null || call.accountComponentName == null || call.accountId == null) {
+ LogUtil.v(
+ "PhoneAccountUtils.getAccount",
+ "accountComponentName == null || callToNotify.accountId == null");
+ handle = TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL);
+ if (handle == null) {
+ return null;
+ }
+ } else {
+ handle =
+ new PhoneAccountHandle(
+ ComponentName.unflattenFromString(call.accountComponentName), call.accountId);
+ }
+ if (handle.getComponentName() != null) {
+ LogUtil.v(
+ "PhoneAccountUtils.getAccount",
+ "PhoneAccountHandle.ComponentInfo:" + handle.getComponentName());
+ } else {
+ LogUtil.i("PhoneAccountUtils.getAccount", "PhoneAccountHandle.ComponentInfo: null");
+ }
+ return handle;
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
index b18270bb3..acbccb39f 100644
--- a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
@@ -27,9 +27,10 @@ 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.calllogutils.PhoneCallDetails;
+import com.android.dialer.oem.MotorolaUtils;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
import com.android.dialer.util.DialerUtils;
import java.util.ArrayList;
@@ -84,6 +85,8 @@ public class PhoneCallDetailsHelper {
// Show the video icon if the call had video enabled.
views.callTypeIcons.setShowVideo(
(details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO);
+ views.callTypeIcons.setShowHd(
+ MotorolaUtils.shouldShowHdIconInCallLog(mContext, details.features));
views.callTypeIcons.requestLayout();
views.callTypeIcons.setVisibility(View.VISIBLE);
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
index 476996826..e2e27a179 100644
--- a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.view.View;
import android.widget.TextView;
import com.android.dialer.app.R;
+import com.android.dialer.calllogutils.CallTypeIconsView;
/** Encapsulates the views that are used to display the details of a phone call in the call log. */
public final class PhoneCallDetailsViews {
diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java
index e539ceef6..6f101f580 100644
--- a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java
+++ b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java
@@ -40,10 +40,13 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment {
private VoicemailErrorManager mVoicemailAlertManager;
+ public VisualVoicemailCallLogFragment() {
+ super(CallLog.Calls.VOICEMAIL_TYPE);
+ }
+
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
- mCallTypeFilter = CallLog.Calls.VOICEMAIL_TYPE;
mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), state);
getActivity()
.getContentResolver()
diff --git a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java
index d6d8354ec..e73684e70 100644
--- a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java
+++ b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java
@@ -15,13 +15,18 @@
*/
package com.android.dialer.app.calllog;
+import android.app.NotificationManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.provider.CallLog.Calls;
-import android.util.Log;
+import android.support.annotation.Nullable;
+import com.android.dialer.app.R;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.notification.GroupedNotificationUtil;
/** Handles asynchronous queries to the call log for voicemail. */
public class VoicemailQueryHandler extends AsyncQueryHandler {
@@ -39,7 +44,7 @@ public class VoicemailQueryHandler extends AsyncQueryHandler {
}
/** Updates all new voicemails to mark them as old. */
- public void markNewVoicemailsAsOld() {
+ public void markNewVoicemailsAsOld(@Nullable Uri voicemailUri) {
// Mark all "new" voicemails as not new anymore.
StringBuilder where = new StringBuilder();
where.append(Calls.NEW);
@@ -47,6 +52,10 @@ public class VoicemailQueryHandler extends AsyncQueryHandler {
where.append(Calls.TYPE);
where.append(" = ?");
+ if (voicemailUri != null) {
+ where.append(" AND ").append(Calls.VOICEMAIL_URI).append(" = ?");
+ }
+
ContentValues values = new ContentValues(1);
values.put(Calls.NEW, "0");
@@ -56,7 +65,15 @@ public class VoicemailQueryHandler extends AsyncQueryHandler {
Calls.CONTENT_URI_WITH_VOICEMAIL,
values,
where.toString(),
- new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)});
+ voicemailUri == null
+ ? new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)}
+ : new String[] {Integer.toString(Calls.VOICEMAIL_TYPE), voicemailUri.toString()});
+
+ GroupedNotificationUtil.removeNotification(
+ mContext.getSystemService(NotificationManager.class),
+ voicemailUri != null ? voicemailUri.toString() : null,
+ R.id.notification_voicemail,
+ DefaultVoicemailNotifier.NOTIFICATION_TAG);
}
@Override
@@ -67,7 +84,7 @@ public class VoicemailQueryHandler extends AsyncQueryHandler {
serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS);
mContext.startService(serviceIntent);
} else {
- Log.w(TAG, "Unknown update completed: ignoring: " + token);
+ LogUtil.w(TAG, "Unknown update completed: ignoring: " + token);
}
}
}
diff --git a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java
index c342b7e3b..039998780 100644
--- a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java
+++ b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java
@@ -20,10 +20,10 @@ import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Pair;
-import com.android.dialer.app.calllog.PhoneAccountUtils;
+import com.android.dialer.calllogutils.PhoneAccountUtils;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -45,9 +45,9 @@ class CallLogCacheLollipopMr1 extends CallLogCache {
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<>();
+ private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new ArrayMap<>();
+ private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new ArrayMap<>();
+ private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new ArrayMap<>();
/* package */ CallLogCacheLollipopMr1(Context context) {
super(context);
diff --git a/java/com/android/dialer/app/contactinfo/ContactInfoCache.java b/java/com/android/dialer/app/contactinfo/ContactInfoCache.java
index 4135cb7b8..6c35711a8 100644
--- a/java/com/android/dialer/app/contactinfo/ContactInfoCache.java
+++ b/java/com/android/dialer/app/contactinfo/ContactInfoCache.java
@@ -29,8 +29,8 @@ 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
+ * 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.
*
* <p>This cache queues request for information and queries for information on a background thread,
diff --git a/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java b/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java
index a8c718502..71e4a16ad 100644
--- a/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java
+++ b/java/com/android/dialer/app/contactinfo/ContactPhotoLoader.java
@@ -104,7 +104,7 @@ public class ContactPhotoLoader {
final RoundedBitmapDrawable drawable =
RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
drawable.setAntiAlias(true);
- drawable.setCornerRadius(bitmap.getHeight() / 2);
+ drawable.setCircular(true);
return drawable;
} catch (IOException e) {
LogUtil.e("ContactPhotoLoader.createPhotoIconDrawable", e.toString());
diff --git a/java/com/android/dialer/app/dialpad/DialpadFragment.java b/java/com/android/dialer/app/dialpad/DialpadFragment.java
index 18bb250ce..4785ab16f 100644
--- a/java/com/android/dialer/app/dialpad/DialpadFragment.java
+++ b/java/com/android/dialer/app/dialpad/DialpadFragment.java
@@ -78,9 +78,9 @@ 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.calllogutils.PhoneAccountUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.dialpadview.DialpadKeyButton;
import com.android.dialer.dialpadview.DialpadView;
@@ -598,6 +598,7 @@ public class DialpadFragment extends Fragment
@Override
public void onStart() {
+ LogUtil.d("DialpadFragment.onStart", "first launch: %b", mFirstLaunch);
Trace.beginSection(TAG + " onStart");
super.onStart();
// if the mToneGenerator creation fails, just continue without it. It is
@@ -624,6 +625,7 @@ public class DialpadFragment extends Fragment
@Override
public void onResume() {
+ LogUtil.d("DialpadFragment.onResume", "");
Trace.beginSection(TAG + " onResume");
super.onResume();
diff --git a/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java
index eef920710..9ec6042c0 100644
--- a/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java
+++ b/java/com/android/dialer/app/filterednumber/BlockedNumbersSettingsActivity.java
@@ -135,11 +135,6 @@ public class BlockedNumbersSettingsActivity extends AppCompatActivity
}
@Override
- public int getActionBarHideOffset() {
- return 0;
- }
-
- @Override
public int getActionBarHeight() {
return 0;
}
diff --git a/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java b/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java
index 2125a1524..1cdeb2175 100644
--- a/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java
+++ b/java/com/android/dialer/app/legacybindings/DialerLegacyBindings.java
@@ -17,12 +17,14 @@
package com.android.dialer.app.legacybindings;
import android.app.Activity;
+import android.support.annotation.NonNull;
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;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
/**
* These are old bindings between Dialer and the container application. All new bindings should be
@@ -41,6 +43,7 @@ public interface DialerLegacyBindings {
CallLogCache callLogCache,
ContactInfoCache contactInfoCache,
VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ @NonNull FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
int activityType);
RegularSearchFragment newRegularSearchFragment();
diff --git a/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java
index f01df78f8..6e32843ba 100644
--- a/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java
+++ b/java/com/android/dialer/app/legacybindings/DialerLegacyBindingsStub.java
@@ -17,12 +17,14 @@
package com.android.dialer.app.legacybindings;
import android.app.Activity;
+import android.support.annotation.NonNull;
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;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
/** Default implementation for dialer legacy bindings. */
public class DialerLegacyBindingsStub implements DialerLegacyBindings {
@@ -35,6 +37,7 @@ public class DialerLegacyBindingsStub implements DialerLegacyBindings {
CallLogCache callLogCache,
ContactInfoCache contactInfoCache,
VoicemailPlaybackPresenter voicemailPlaybackPresenter,
+ @NonNull FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler,
int activityType) {
return new CallLogAdapter(
activity,
@@ -43,6 +46,7 @@ public class DialerLegacyBindingsStub implements DialerLegacyBindings {
callLogCache,
contactInfoCache,
voicemailPlaybackPresenter,
+ filteredNumberAsyncQueryHandler,
activityType);
}
diff --git a/java/com/android/dialer/app/list/ListsFragment.java b/java/com/android/dialer/app/list/ListsFragment.java
index 725ad3001..13938f29a 100644
--- a/java/com/android/dialer/app/list/ListsFragment.java
+++ b/java/com/android/dialer/app/list/ListsFragment.java
@@ -30,19 +30,16 @@ 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.CallLogNotificationsService;
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;
@@ -92,7 +89,6 @@ public class ListsFragment extends Fragment
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;
@@ -108,8 +104,7 @@ public class ListsFragment extends Fragment
private boolean mHasFetchedVoicemailStatus;
private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
private VoicemailStatusHelper mVoicemailStatusHelper;
- private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
- new ArrayList<OnPageChangeListener>();
+ private final ArrayList<OnPageChangeListener> mOnPageChangeListeners = new ArrayList<>();
private String[] mTabTitles;
private int[] mTabIcons;
/** The position of the currently selected tab. */
@@ -149,7 +144,6 @@ public class ListsFragment extends Fragment
Trace.beginSection(TAG + " onResume");
super.onResume();
- mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (getUserVisibleHint()) {
sendScreenViewForCurrentPosition();
}
@@ -329,7 +323,7 @@ public class ListsFragment extends Fragment
.putBoolean(
VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
hasActiveVoicemailProvider)
- .commit();
+ .apply();
}
if (hasActiveVoicemailProvider) {
@@ -403,7 +397,7 @@ public class ListsFragment extends Fragment
public void markMissedCallsAsReadAndRemoveNotifications() {
if (mCallLogQueryHandler != null) {
mCallLogQueryHandler.markMissedCallsAsRead();
- CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+ CallLogNotificationsService.markNewMissedCallsAsOld(getContext(), null);
}
}
@@ -413,11 +407,6 @@ public class ListsFragment extends Fragment
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;
}
@@ -486,11 +475,6 @@ public class ListsFragment extends Fragment
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<>();
@@ -518,7 +502,7 @@ public class ListsFragment extends Fragment
return mSpeedDialFragment;
case TAB_INDEX_HISTORY:
if (mHistoryFragment == null) {
- mHistoryFragment = new CallLogFragment();
+ mHistoryFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
}
return mHistoryFragment;
case TAB_INDEX_ALL_CONTACTS:
diff --git a/java/com/android/dialer/app/list/SearchFragment.java b/java/com/android/dialer/app/list/SearchFragment.java
index 4a7d48ae4..e6615aa8d 100644
--- a/java/com/android/dialer/app/list/SearchFragment.java
+++ b/java/com/android/dialer/app/list/SearchFragment.java
@@ -98,6 +98,7 @@ public class SearchFragment extends PhoneNumberPickerFragment {
@Override
public void onStart() {
+ LogUtil.d("SearchFragment.onStart", "");
super.onStart();
if (isSearchMode()) {
getAdapter().setHasHeader(0, false);
@@ -301,6 +302,7 @@ public class SearchFragment extends PhoneNumberPickerFragment {
* shown. This can be optionally animated.
*/
public void updatePosition(boolean animate) {
+ LogUtil.d("SearchFragment.updatePosition", "animate: %b", animate);
if (mActivity == null) {
// Activity will be set in onStart, and this method will be called again
return;
@@ -363,6 +365,13 @@ public class SearchFragment extends PhoneNumberPickerFragment {
return;
}
int spacerHeight = mActivity.isDialpadShown() ? mActivity.getDialpadHeight() : 0;
+ LogUtil.d(
+ "SearchFragment.resizeListView",
+ "spacerHeight: %d -> %d, isDialpadShown: %b, dialpad height: %d",
+ mSpacer.getHeight(),
+ spacerHeight,
+ mActivity.isDialpadShown(),
+ mActivity.getDialpadHeight());
if (spacerHeight != mSpacer.getHeight()) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpacer.getLayoutParams();
lp.height = spacerHeight;
@@ -418,8 +427,6 @@ public class SearchFragment extends PhoneNumberPickerFragment {
int getDialpadHeight();
- int getActionBarHideOffset();
-
int getActionBarHeight();
}
}
diff --git a/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml b/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml
index 247b34f4c..7e450c4cd 100644
--- a/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml
+++ b/java/com/android/dialer/app/manifests/activities/AndroidManifest.xml
@@ -29,18 +29,6 @@
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. -->
diff --git a/java/com/android/dialer/app/res/drawable-xxxhdpi/search_shadow.9.png b/java/com/android/dialer/app/res/drawable-xxxhdpi/search_shadow.9.png
new file mode 100644
index 000000000..ff55620d0
--- /dev/null
+++ b/java/com/android/dialer/app/res/drawable-xxxhdpi/search_shadow.9.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/layout/call_detail.xml b/java/com/android/dialer/app/res/layout/call_detail.xml
deleted file mode 100644
index 58a7bf0dc..000000000
--- a/java/com/android/dialer/app/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/java/com/android/dialer/app/res/layout/call_detail_footer.xml b/java/com/android/dialer/app/res/layout/call_detail_footer.xml
deleted file mode 100644
index 57713448e..000000000
--- a/java/com/android/dialer/app/res/layout/call_detail_footer.xml
+++ /dev/null
@@ -1,52 +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_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
deleted file mode 100644
index fd85f0af1..000000000
--- a/java/com/android/dialer/app/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: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
index 5958ee81c..0184a42f2 100644
--- 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
@@ -27,9 +27,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
- <view
+ <com.android.dialer.calllogutils.CallTypeIconsView
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"/>
diff --git a/java/com/android/dialer/app/res/layout/call_log_activity.xml b/java/com/android/dialer/app/res/layout/call_log_activity.xml
new file mode 100644
index 000000000..4e2b1887c
--- /dev/null
+++ b/java/com/android/dialer/app/res/layout/call_log_activity.xml
@@ -0,0 +1,40 @@
+<?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/calllog_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <com.android.contacts.common.list.ViewPagerTabs
+ android:id="@+id/viewpager_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/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/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
index c22ac861d..1592aa928 100644
--- 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
@@ -93,8 +93,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
- <view
- class="com.android.dialer.app.calllog.CallTypeIconsView"
+ <com.android.dialer.calllogutils.CallTypeIconsView
android:id="@+id/call_type_icons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/java/com/android/dialer/app/res/menu/call_log_options.xml b/java/com/android/dialer/app/res/menu/call_log_options.xml
new file mode 100644
index 000000000..e78b72e3c
--- /dev/null
+++ b/java/com/android/dialer/app/res/menu/call_log_options.xml
@@ -0,0 +1,22 @@
+<?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:orderInCategory="1"
+ android:showAsAction="never"
+ android:title="@string/call_log_delete_all"/>
+</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
index 434aa81d9..25a3e1811 100644
--- a/java/com/android/dialer/app/res/menu/dialtacts_options.xml
+++ b/java/com/android/dialer/app/res/menu/dialtacts_options.xml
@@ -16,13 +16,17 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_delete_all"
- android:title="@string/call_log_delete_all"/>
+ 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_clear_frequents"
android:title="@string/menu_clear_frequents"/>
<item
android:id="@+id/menu_call_settings"
android:title="@string/dialer_settings_label"/>
+ <item
+ android:id="@+id/menu_simulator_submenu"
+ android:title="@string/simulator_submenu_label"/>
</menu>
diff --git a/java/com/android/dialer/app/res/values/colors.xml b/java/com/android/dialer/app/res/values/colors.xml
index b88e55276..cf6b926be 100644
--- a/java/com/android/dialer/app/res/values/colors.xml
+++ b/java/com/android/dialer/app/res/values/colors.xml
@@ -16,7 +16,6 @@
<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>
@@ -84,13 +83,6 @@
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>
diff --git a/java/com/android/dialer/app/res/values/dimens.xml b/java/com/android/dialer/app/res/values/dimens.xml
index f3fd63350..7da29c7a3 100644
--- a/java/com/android/dialer/app/res/values/dimens.xml
+++ b/java/com/android/dialer/app/res/values/dimens.xml
@@ -28,7 +28,6 @@
<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>
@@ -68,7 +67,7 @@
<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_top_padding">1dp</dimen>
<dimen name="favorites_row_bottom_padding">0dp</dimen>
<dimen name="favorites_row_start_padding">1dp</dimen>
@@ -143,6 +142,4 @@
<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/strings.xml b/java/com/android/dialer/app/res/values/strings.xml
index 689ee1ba8..66bf70f1a 100644
--- a/java/com/android/dialer/app/res/values/strings.xml
+++ b/java/com/android/dialer/app/res/values/strings.xml
@@ -55,9 +55,6 @@
<!-- 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>
@@ -94,7 +91,7 @@
<!-- 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>
+ <string name="notification_missedCallsMsg"><xliff:g id="num_missed_calls">%d</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] -->
@@ -251,15 +248,13 @@
<!-- Label for the dialer app setting page [CHAR LIMIT=30]-->
<string name="dialer_settings_label">Settings</string>
+ <!-- Label for the simulator submenu. This is used to show actions that are useful for development
+ and testing. [CHAR LIMIT=30]-->
+ <string name="simulator_submenu_label">Simulator</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.) -->
@@ -275,52 +270,6 @@
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>
@@ -623,28 +572,10 @@
[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]-->
@@ -827,6 +758,9 @@
<!-- Label for the blocked numbers settings section [CHAR LIMIT=30] -->
<string name="manage_blocked_numbers_label">Call blocking</string>
+ <!-- Label for the voicemail settings section [CHAR LIMIT=30] -->
+ <string name="voicemail_settings_label">Voicemail</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">
@@ -955,6 +889,6 @@
<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>
+ <string name="call_composer_connection_failed"><xliff:g id="name">%1$s</xliff:g> is offline and can\'t be reached</string>
</resources>
diff --git a/java/com/android/dialer/app/res/values/styles.xml b/java/com/android/dialer/app/res/values/styles.xml
index ac4422ba2..24521ddaf 100644
--- a/java/com/android/dialer/app/res/values/styles.xml
+++ b/java/com/android/dialer/app/res/values/styles.xml
@@ -111,11 +111,6 @@
<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>
diff --git a/java/com/android/dialer/app/settings/DialerSettingsActivity.java b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
index b04674013..bbf1cfae5 100644
--- a/java/com/android/dialer/app/settings/DialerSettingsActivity.java
+++ b/java/com/android/dialer/app/settings/DialerSettingsActivity.java
@@ -33,8 +33,11 @@ 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 com.android.voicemail.VoicemailComponent;
import java.util.List;
+/** Activity for dialer settings. */
+@SuppressWarnings("FragmentInjection") // Activity not exported
@UsedByReflection(value = "AndroidManifest-app.xml")
public class DialerSettingsActivity extends AppCompatPreferenceActivity {
@@ -115,6 +118,16 @@ public class DialerSettingsActivity extends AppCompatPreferenceActivity {
target.add(blockedCallsHeader);
migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking(this);
}
+
+ String voicemailSettingsFragment =
+ VoicemailComponent.get(this).getVoicemailClient().getSettingsFragment();
+ if (isPrimaryUser && voicemailSettingsFragment != null) {
+ Header voicemailSettings = new Header();
+ voicemailSettings.titleRes = R.string.voicemail_settings_label;
+ voicemailSettings.fragment = voicemailSettingsFragment;
+ target.add(voicemailSettings);
+ }
+
if (isPrimaryUser
&& (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
|| TelephonyManagerCompat.isHearingAidCompatibilitySupported(telephonyManager))) {
diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
index fc6a37608..f40ed2794 100644
--- a/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
+++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
@@ -30,7 +30,6 @@ 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;
@@ -347,16 +346,10 @@ public class VoicemailPlaybackLayout extends LinearLayout
}
@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);
}
diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java
index 657022291..994160ff9 100644
--- a/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java
+++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java
@@ -39,6 +39,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
+import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.webkit.MimeTypeMap;
import com.android.common.io.MoreCloseables;
@@ -47,8 +48,11 @@ 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.ConfigProviderBindings;
import com.android.dialer.common.LogUtil;
import com.android.dialer.constants.Constants;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
import com.android.dialer.phonenumbercache.CallLogQuery;
import com.google.common.io.ByteStreams;
import java.io.File;
@@ -71,9 +75,9 @@ import javax.annotation.concurrent.ThreadSafe;
* 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 controls a single {@link com.android.dialer.app.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.
@@ -103,6 +107,8 @@ public class VoicemailPlaybackPresenter
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 final String CONFIG_SHARE_VOICEMAIL_ALLOWED = "share_voicemail_allowed";
+
private static VoicemailPlaybackPresenter sInstance;
private static ScheduledExecutorService mScheduledExecutorService;
/**
@@ -138,6 +144,7 @@ public class VoicemailPlaybackPresenter
private PowerManager.WakeLock mProximityWakeLock;
private VoicemailAudioManager mVoicemailAudioManager;
private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
+ private View shareVoicemailButtonView;
/** Initialize variables which are activity-independent and state-independent. */
protected VoicemailPlaybackPresenter(Activity activity) {
@@ -222,11 +229,17 @@ public class VoicemailPlaybackPresenter
/** 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) {
+ PlaybackView view,
+ long rowId,
+ Uri voicemailUri,
+ final boolean startPlayingImmediately,
+ View shareVoicemailButtonView) {
mRowId = rowId;
mView = view;
mView.setPresenter(this, voicemailUri);
mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
+ this.shareVoicemailButtonView = shareVoicemailButtonView;
+ showShareVoicemailButton(false);
// Handles cases where the same entry is binded again when scrolling in list, or where
// the MediaPlayer was retained after an orientation change.
@@ -236,6 +249,7 @@ public class VoicemailPlaybackPresenter
// media player.
mPosition = mMediaPlayer.getCurrentPosition();
onPrepared(mMediaPlayer);
+ showShareVoicemailButton(true);
} else {
if (!voicemailUri.equals(mVoicemailUri)) {
mVoicemailUri = voicemailUri;
@@ -247,19 +261,17 @@ public class VoicemailPlaybackPresenter
* 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());
- }
+ hasContent -> {
+ if (hasContent) {
+ showShareVoicemailButton(true);
+ prepareContent();
+ } else {
+ if (startPlayingImmediately) {
+ requestContent(PLAYBACK_REQUEST);
+ }
+ if (mView != null) {
+ mView.resetSeekBar();
+ mView.setClipPosition(0, mDuration.get());
}
}
});
@@ -547,6 +559,7 @@ public class VoicemailPlaybackPresenter
mPosition = 0;
mIsPlaying = false;
+ showShareVoicemailButton(false);
}
/** After done playing the voicemail clip, reset the clip position to the start. */
@@ -600,18 +613,16 @@ public class VoicemailPlaybackPresenter
* 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();
- }
+ hasContent -> {
+ if (!hasContent) {
+ // No local content, download from server. Queue playing if the request was
+ // issued,
+ mIsPlaying = requestContent(PLAYBACK_REQUEST);
+ } else {
+ showShareVoicemailButton(true);
+ // Queue playing once the media play loaded the content.
+ mIsPlaying = true;
+ prepareContent();
}
});
return;
@@ -813,6 +824,20 @@ public class VoicemailPlaybackPresenter
sInstance = null;
}
+ private void showShareVoicemailButton(boolean show) {
+ if (isShareVoicemailAllowed(mContext) && shareVoicemailButtonView != null) {
+ if (show) {
+ Logger.get(mContext).logImpression(DialerImpression.Type.VVM_SHARE_VISIBLE);
+ }
+ LogUtil.d("VoicemailPlaybackPresenter.showShareVoicemailButton", "show: %b", show);
+ shareVoicemailButtonView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private static boolean isShareVoicemailAllowed(Context context) {
+ return ConfigProviderBindings.get(context).getBoolean(CONFIG_SHARE_VOICEMAIL_ALLOWED, true);
+ }
+
/**
* 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.
@@ -1041,6 +1066,7 @@ public class VoicemailPlaybackPresenter
public void onPostExecute(Boolean hasContent) {
if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) {
mContext.getContentResolver().unregisterContentObserver(FetchResultHandler.this);
+ showShareVoicemailButton(true);
prepareContent();
}
}
diff --git a/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java b/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java
index e36406d17..190426e6e 100644
--- a/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java
+++ b/java/com/android/dialer/app/voicemail/error/OmtpVoicemailMessageCreator.java
@@ -17,10 +17,18 @@
package com.android.dialer.app.voicemail.error;
import android.content.Context;
+import android.preference.PreferenceManager;
import android.provider.VoicemailContract.Status;
import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
import com.android.dialer.app.voicemail.error.VoicemailErrorMessage.Action;
+import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.PerAccountSharedPreferences;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.voicemail.VoicemailClient;
+import com.android.voicemail.VoicemailComponent;
import java.util.ArrayList;
import java.util.List;
@@ -32,14 +40,18 @@ public class OmtpVoicemailMessageCreator {
private static final float QUOTA_NEAR_FULL_THRESHOLD = 0.9f;
private static final float QUOTA_FULL_THRESHOLD = 0.99f;
+ protected static final String VOICEMAIL_PROMO_DISMISSED_KEY =
+ "voicemail_archive_promo_was_dismissed";
+ protected static final String VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY =
+ "voicemail_archive_almost_full_promo_was_dismissed";
@Nullable
- public static VoicemailErrorMessage create(Context context, VoicemailStatus status) {
+ public static VoicemailErrorMessage create(
+ Context context, VoicemailStatus status, final VoicemailStatusReader statusReader) {
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);
+ return checkQuota(context, status, statusReader);
}
// Initial state when the source is activating. Other error might be written into data and
// notification channel during activation.
@@ -120,24 +132,98 @@ public class OmtpVoicemailMessageCreator {
}
@Nullable
- private static VoicemailErrorMessage checkQuota(Context context, VoicemailStatus status) {
+ private static VoicemailErrorMessage checkQuota(
+ Context context, VoicemailStatus status, VoicemailStatusReader statusReader) {
if (status.quotaOccupied != Status.QUOTA_UNAVAILABLE
&& status.quotaTotal != Status.QUOTA_UNAVAILABLE) {
+
+ PhoneAccountHandle phoneAccountHandle = status.getPhoneAccountHandle();
+
+ VoicemailClient voicemailClient = VoicemailComponent.get(context).getVoicemailClient();
+
+ PerAccountSharedPreferences sharedPreferenceForAccount =
+ new PerAccountSharedPreferences(
+ context, phoneAccountHandle, PreferenceManager.getDefaultSharedPreferences(context));
+
+ boolean isVoicemailArchiveEnabled =
+ VoicemailComponent.get(context)
+ .getVoicemailClient()
+ .isVoicemailArchiveEnabled(context, phoneAccountHandle);
+
if ((float) status.quotaOccupied / (float) status.quotaTotal >= QUOTA_FULL_THRESHOLD) {
- return new VoicemailErrorMessage(
+ return createInboxErrorMessage(
+ context,
+ status,
+ status.getPhoneAccountHandle(),
+ statusReader,
+ sharedPreferenceForAccount,
+ voicemailClient,
+ isVoicemailArchiveEnabled,
+ context.getString(R.string.voicemail_error_inbox_full_turn_archive_on_title),
+ context.getString(R.string.voicemail_error_inbox_full_turn_archive_on_message),
context.getString(R.string.voicemail_error_inbox_full_title),
- context.getString(R.string.voicemail_error_inbox_full_message));
+ context.getString(R.string.voicemail_error_inbox_full_message),
+ VOICEMAIL_PROMO_DISMISSED_KEY);
}
if ((float) status.quotaOccupied / (float) status.quotaTotal >= QUOTA_NEAR_FULL_THRESHOLD) {
- return new VoicemailErrorMessage(
+ return createInboxErrorMessage(
+ context,
+ status,
+ status.getPhoneAccountHandle(),
+ statusReader,
+ sharedPreferenceForAccount,
+ voicemailClient,
+ isVoicemailArchiveEnabled,
+ context.getString(R.string.voicemail_error_inbox_almost_full_turn_archive_on_title),
+ context.getString(R.string.voicemail_error_inbox_almost_full_turn_archive_on_message),
context.getString(R.string.voicemail_error_inbox_near_full_title),
- context.getString(R.string.voicemail_error_inbox_near_full_message));
+ context.getString(R.string.voicemail_error_inbox_near_full_message),
+ VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY);
}
}
return null;
}
+ private static VoicemailErrorMessage createInboxErrorMessage(
+ Context context,
+ VoicemailStatus status,
+ PhoneAccountHandle phoneAccountHandle,
+ VoicemailStatusReader statusReader,
+ PerAccountSharedPreferences sharedPreferenceForAccount,
+ VoicemailClient voicemailClient,
+ boolean isVoicemailArchiveEnabled,
+ String promoTitle,
+ String promoMessage,
+ String nonPromoTitle,
+ String nonPromoMessage,
+ String preferenceKey) {
+
+ boolean wasPromoDismissed = sharedPreferenceForAccount.getBoolean(preferenceKey, false);
+
+ if (!wasPromoDismissed && !isVoicemailArchiveEnabled) {
+ logArchiveImpression(
+ context,
+ preferenceKey,
+ DialerImpression.Type.VVM_USER_SHOWN_VM_ALMOST_FULL_PROMO,
+ DialerImpression.Type.VVM_USER_SHOWN_VM_FULL_PROMO);
+ return new VoicemailErrorMessage(
+ promoTitle,
+ promoMessage,
+ VoicemailErrorMessage.createDismissTurnArchiveOnAction(
+ context, statusReader, sharedPreferenceForAccount, preferenceKey),
+ VoicemailErrorMessage.createTurnArchiveOnAction(
+ context, status, voicemailClient, phoneAccountHandle, preferenceKey));
+ } else {
+ logArchiveImpression(
+ context,
+ preferenceKey,
+ DialerImpression.Type.VVM_USER_SHOWN_VM_ALMOST_FULL_ERROR_MESSAGE,
+ DialerImpression.Type.VVM_USER_SHOWN_VM_FULL_ERROR_MESSAGE);
+ return new VoicemailErrorMessage(nonPromoTitle, nonPromoMessage);
+ }
+ }
+
@Nullable
private static VoicemailErrorMessage createNoSignalMessage(
Context context, VoicemailStatus status) {
@@ -174,4 +260,15 @@ public class OmtpVoicemailMessageCreator {
}
return new VoicemailErrorMessage(title, description, actions);
}
+
+ protected static void logArchiveImpression(
+ Context context, String preference, int vmAlmostFullImpression, int vmFullImpression) {
+ if (preference.equals(VOICEMAIL_PROMO_DISMISSED_KEY)) {
+ Logger.get(context).logImpression(vmAlmostFullImpression);
+ } else if (preference.equals(VOICEMAIL_PROMO_ALMOST_FULL_DISMISSED_KEY)) {
+ Logger.get(context).logImpression(vmFullImpression);
+ } else {
+ throw Assert.createAssertionFailException("Invalid preference key " + preference);
+ }
+ }
}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
index 61572008b..f85d91186 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessage.java
@@ -22,12 +22,15 @@ import android.provider.Settings;
import android.provider.VoicemailContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import android.view.View;
import android.view.View.OnClickListener;
+import com.android.dialer.common.PerAccountSharedPreferences;
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.nano.DialerImpression;
import com.android.dialer.util.CallUtil;
+import com.android.voicemail.VoicemailClient;
import java.util.Arrays;
import java.util.List;
@@ -175,4 +178,52 @@ public class VoicemailErrorMessage {
}
});
}
+
+ @NonNull
+ public static Action createTurnArchiveOnAction(
+ final Context context,
+ final VoicemailStatus status,
+ VoicemailClient voicemailClient,
+ PhoneAccountHandle phoneAccountHandle,
+ String preference) {
+ return new Action(
+ context.getString(R.string.voicemail_action_turn_archive_on),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ OmtpVoicemailMessageCreator.logArchiveImpression(
+ context,
+ preference,
+ DialerImpression.Type.VVM_USER_ENABLED_ARCHIVE_FROM_VM_FULL_PROMO,
+ DialerImpression.Type.VVM_USER_ENABLED_ARCHIVE_FROM_VM_ALMOST_FULL_PROMO);
+
+ voicemailClient.setVoicemailArchiveEnabled(context, phoneAccountHandle, true);
+ Intent intent = new Intent(VoicemailContract.ACTION_SYNC_VOICEMAIL);
+ intent.setPackage(status.sourcePackage);
+ context.sendBroadcast(intent);
+ }
+ });
+ }
+
+ @NonNull
+ public static Action createDismissTurnArchiveOnAction(
+ final Context context,
+ VoicemailStatusReader statusReader,
+ PerAccountSharedPreferences sharedPreferenceForAccount,
+ String preferenceKeyToUpdate) {
+ return new Action(
+ context.getString(R.string.voicemail_action_dimiss),
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ OmtpVoicemailMessageCreator.logArchiveImpression(
+ context,
+ preferenceKeyToUpdate,
+ DialerImpression.Type.VVM_USER_DISMISSED_VM_FULL_PROMO,
+ DialerImpression.Type.VVM_USER_DISMISSED_VM_ALMOST_FULL_PROMO);
+ sharedPreferenceForAccount.edit().putBoolean(preferenceKeyToUpdate, true);
+ statusReader.refresh();
+ }
+ });
+ }
}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java
index 5ebef801d..7dc18f043 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailErrorMessageCreator.java
@@ -39,7 +39,7 @@ public class VoicemailErrorMessageCreator {
case Vvm3VoicemailMessageCreator.VVM_TYPE_VVM3:
return Vvm3VoicemailMessageCreator.create(context, status, statusReader);
default:
- return OmtpVoicemailMessageCreator.create(context, status);
+ return OmtpVoicemailMessageCreator.create(context, status, statusReader);
}
}
}
diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java b/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java
index a09941de2..c429d6dcc 100644
--- a/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java
+++ b/java/com/android/dialer/app/voicemail/error/VoicemailStatus.java
@@ -16,6 +16,7 @@
package com.android.dialer.app.voicemail.error;
+import android.content.ComponentName;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -25,6 +26,7 @@ import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.VoicemailContract.Status;
import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import com.android.dialer.database.VoicemailStatusQuery;
@@ -257,4 +259,9 @@ public class VoicemailStatus {
}
return cursor.getString(index);
}
+
+ public PhoneAccountHandle getPhoneAccountHandle() {
+ return new PhoneAccountHandle(
+ ComponentName.unflattenFromString(phoneAccountComponentName), phoneAccountId);
+ }
}
diff --git a/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java b/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java
index 6e9405cbf..356131bb3 100644
--- a/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java
+++ b/java/com/android/dialer/app/voicemail/error/Vvm3VoicemailMessageCreator.java
@@ -269,7 +269,7 @@ public class Vvm3VoicemailMessageCreator {
VoicemailErrorMessage.createSetPinAction(context));
}
- return OmtpVoicemailMessageCreator.create(context, status);
+ return OmtpVoicemailMessageCreator.create(context, status, statusReader);
}
@NonNull
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
index 0dfb1c2fd..4a40857a0 100644
--- 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
@@ -48,7 +48,6 @@
<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"
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
index 2b9d17328..c193eaa47 100644
--- 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
@@ -31,7 +31,6 @@
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"/>
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
index 1d39b9dcb..94d3dba11 100644
--- a/java/com/android/dialer/app/voicemail/error/res/values/strings.xml
+++ b/java/com/android/dialer/app/voicemail/error/res/values/strings.xml
@@ -54,6 +54,11 @@
<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_inbox_full_turn_archive_on_title">Turn on extra storage and backup</string>
+ <string name="voicemail_error_inbox_full_turn_archive_on_message">Your mailbox is full. To free up space, turn on extra storage so Google can manage and backup your voicemail messages.</string>
+
+ <string name="voicemail_error_inbox_almost_full_turn_archive_on_title">Turn on extra storage and backup</string>
+ <string name="voicemail_error_inbox_almost_full_turn_archive_on_message">Your mailbox is almost full. To free up space, turn on extra storage so Google can manage and backup your voicemail messages.</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>
@@ -63,6 +68,8 @@
<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_turn_archive_on">Turn on</string>
+ <string name="voicemail_action_dimiss">No Thanks</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>
diff --git a/java/com/android/dialer/app/widget/ActionBarController.java b/java/com/android/dialer/app/widget/ActionBarController.java
index 7fe056c51..d0eb326ab 100644
--- a/java/com/android/dialer/app/widget/ActionBarController.java
+++ b/java/com/android/dialer/app/widget/ActionBarController.java
@@ -16,12 +16,9 @@
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;
+import com.android.dialer.common.LogUtil;
/**
* Controls the various animated properties of the actionBar: showing/hiding, fading/revealing, and
@@ -30,8 +27,6 @@ import com.android.dialer.app.DialtactsActivity;
*/
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";
@@ -66,9 +61,8 @@ public class ActionBarController {
/** 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());
- }
+ LogUtil.d(
+ "ActionBarController.onSearchBoxTapped", "isInSearchUi " + mActivityUi.isInSearchUi());
if (!mActivityUi.isInSearchUi()) {
mSearchBox.expand(true /* animate */, true /* requestFocus */);
}
@@ -76,16 +70,11 @@ public class ActionBarController {
/** 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());
- }
+ LogUtil.d(
+ "ActionBarController.onSearchUIExited",
+ "isExpanded: %b, isFadedOut %b",
+ mSearchBox.isExpanded(),
+ mSearchBox.isFadedOut());
if (mSearchBox.isExpanded()) {
mSearchBox.collapse(true /* animate */);
}
@@ -93,11 +82,7 @@ public class ActionBarController {
mSearchBox.fadeIn();
}
- if (mActivityUi.shouldShowActionBar()) {
- slideActionBar(false /* slideUp */, false /* animate */);
- } else {
- slideActionBar(true /* slideUp */, false /* animate */);
- }
+ slideActionBar(false /* slideUp */, false /* animate */);
}
/**
@@ -105,18 +90,13 @@ public class ActionBarController {
* 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());
- }
+ LogUtil.d(
+ "ActionBarController.onDialpadDown",
+ "isInSearchUi: %b, hasSearchQuery: %b, isFadedOut: %b, isExpanded: %b",
+ mActivityUi.isInSearchUi(),
+ mActivityUi.hasSearchQuery(),
+ mSearchBox.isFadedOut(),
+ mSearchBox.isExpanded());
if (mActivityUi.isInSearchUi()) {
if (mActivityUi.hasSearchQuery()) {
if (mSearchBox.isFadedOut()) {
@@ -137,9 +117,7 @@ public class ActionBarController {
* state changes have actually occurred.
*/
public void onDialpadUp() {
- if (DEBUG) {
- Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi());
- }
+ LogUtil.d("ActionBarController.onDialpadUp", "isInSearchUi " + mActivityUi.isInSearchUi());
if (mActivityUi.isInSearchUi()) {
slideActionBar(true /* slideUp */, true /* animate */);
} else {
@@ -149,18 +127,14 @@ public class ActionBarController {
}
public void slideActionBar(boolean slideUp, boolean animate) {
- if (DEBUG) {
- Log.d(TAG, "Sliding actionBar - up: " + slideUp + " animate: " + animate);
- }
+ LogUtil.d("ActionBarController.slidingActionBar", "up: %b, animate: %b", slideUp, 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));
- }
+ animation -> {
+ final float value = (float) animation.getAnimatedValue();
+ setHideOffset((int) (mActivityUi.getActionBarHeight() * value));
});
animator.start();
} else {
@@ -173,20 +147,11 @@ public class ActionBarController {
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);
@@ -225,23 +190,14 @@ public class ActionBarController {
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/backup/AndroidManifest.xml b/java/com/android/dialer/backup/AndroidManifest.xml
index cfdb3d93d..1cbbe5339 100644
--- a/java/com/android/dialer/backup/AndroidManifest.xml
+++ b/java/com/android/dialer/backup/AndroidManifest.xml
@@ -21,7 +21,6 @@
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
+</manifest>
diff --git a/java/com/android/dialer/backup/DialerBackupAgent.java b/java/com/android/dialer/backup/DialerBackupAgent.java
index 391a93f29..2f8684aa2 100644
--- a/java/com/android/dialer/backup/DialerBackupAgent.java
+++ b/java/com/android/dialer/backup/DialerBackupAgent.java
@@ -31,6 +31,7 @@ import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.VoicemailContract;
import android.provider.VoicemailContract.Voicemails;
+import android.telecom.PhoneAccountHandle;
import android.util.Pair;
import com.android.dialer.backup.nano.VoicemailInfo;
import com.android.dialer.common.Assert;
@@ -42,6 +43,7 @@ import com.android.dialer.telecom.TelecomUtil;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.List;
import java.util.Locale;
/**
@@ -100,9 +102,11 @@ public class DialerBackupAgent extends BackupAgent {
ConfigProviderBindings.get(this).getBoolean("enable_autobackup", true);
boolean vmBackupEnabled =
ConfigProviderBindings.get(this).getBoolean("enable_vm_backup", false);
+ List<PhoneAccountHandle> phoneAccountsToArchive =
+ DialerBackupUtils.getPhoneAccountsToArchive(this);
if (autoBackupEnabled) {
- if (!maxVoicemailBackupReached && vmBackupEnabled) {
+ if (!maxVoicemailBackupReached && vmBackupEnabled && !phoneAccountsToArchive.isEmpty()) {
voicemailsBackedupSoFar = 0;
sizeOfVoicemailsBackedupSoFar = 0;
@@ -123,9 +127,12 @@ public class DialerBackupAgent extends BackupAgent {
uri,
null,
String.format(
- "(%s = ? AND deleted = 0 AND %s = ?)", Calls.TYPE, Voicemails.SOURCE_PACKAGE),
+ "(%s = ? AND deleted = 0 AND %s = ? AND ?)",
+ Calls.TYPE, Voicemails.SOURCE_PACKAGE),
new String[] {
- Integer.toString(CallLog.Calls.VOICEMAIL_TYPE), VOICEMAIL_SOURCE_PACKAGE
+ Integer.toString(CallLog.Calls.VOICEMAIL_TYPE),
+ VOICEMAIL_SOURCE_PACKAGE,
+ DialerBackupUtils.getPhoneAccountClause(phoneAccountsToArchive)
},
ORDER_BY_DATE,
null)) {
@@ -150,11 +157,12 @@ public class DialerBackupAgent extends BackupAgent {
LogUtil.i(
"DialerBackupAgent.onFullBackup",
"vm files backed up: %d, vm size backed up:%d, "
- + "max vm backup reached:%b, vm backup enabled:%b",
+ + "max vm backup reached:%b, vm backup enabled:%b phone accounts to archive: %d",
voicemailsBackedupSoFar,
sizeOfVoicemailsBackedupSoFar,
maxVoicemailBackupReached,
- vmBackupEnabled);
+ vmBackupEnabled,
+ phoneAccountsToArchive.size());
super.onFullBackup(data);
Logger.get(this).logImpression(DialerImpression.Type.BACKUP_FULL_BACKED_UP);
} else {
diff --git a/java/com/android/dialer/backup/DialerBackupUtils.java b/java/com/android/dialer/backup/DialerBackupUtils.java
index ff0cb4f7c..410772ff0 100644
--- a/java/com/android/dialer/backup/DialerBackupUtils.java
+++ b/java/com/android/dialer/backup/DialerBackupUtils.java
@@ -27,10 +27,14 @@ import android.provider.VoicemailContract;
import android.provider.VoicemailContract.Voicemails;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
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.voicemail.VoicemailComponent;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.protobuf.nano.MessageNano;
@@ -40,6 +44,8 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
/** Helper functions for DialerBackupAgent */
public class DialerBackupUtils {
@@ -317,4 +323,42 @@ public class DialerBackupUtils {
}
return false;
}
+
+ public static String getPhoneAccountClause(List<PhoneAccountHandle> phoneAccountsToArchive) {
+ Assert.checkArgument(!phoneAccountsToArchive.isEmpty());
+ StringBuilder whereQuery = new StringBuilder();
+
+ whereQuery.append("(");
+
+ for (int i = 0; i < phoneAccountsToArchive.size(); i++) {
+ whereQuery.append(
+ Voicemails.PHONE_ACCOUNT_ID + " = " + phoneAccountsToArchive.get(i).getId());
+
+ if (phoneAccountsToArchive.size() > 1 && i < phoneAccountsToArchive.size() - 1) {
+ whereQuery.append(" OR ");
+ }
+ }
+ whereQuery.append(")");
+ return whereQuery.toString();
+ }
+
+ public static List<PhoneAccountHandle> getPhoneAccountsToArchive(Context context) {
+ List<PhoneAccountHandle> phoneAccountsToBackUp = new ArrayList<>();
+
+ for (PhoneAccountHandle handle :
+ context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()) {
+
+ if (VoicemailComponent.get(context)
+ .getVoicemailClient()
+ .isVoicemailArchiveEnabled(context, handle)) {
+ phoneAccountsToBackUp.add(handle);
+ LogUtil.i(
+ "DialerBackupUtils.getPhoneAccountsToArchive", "enabled for: " + handle.toString());
+ } else {
+ LogUtil.i(
+ "DialerBackupUtils.getPhoneAccountsToArchive", "not enabled for: " + handle.toString());
+ }
+ }
+ return phoneAccountsToBackUp;
+ }
}
diff --git a/java/com/android/dialer/backup/proto/VoicemailInfo.java b/java/com/android/dialer/backup/nano/VoicemailInfo.java
index 9ff8423f3..f11595ec2 100644
--- a/java/com/android/dialer/backup/proto/VoicemailInfo.java
+++ b/java/com/android/dialer/backup/nano/VoicemailInfo.java
@@ -18,16 +18,17 @@
package com.android.dialer.backup.nano;
+/** This file is autogenerated, but javadoc required. */
@SuppressWarnings("hiding")
-public final class VoicemailInfo extends
- com.google.protobuf.nano.ExtendableMessageNano<VoicemailInfo> {
+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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new VoicemailInfo[0];
}
@@ -178,7 +179,8 @@ public final class VoicemailInfo extends
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)) {
+ 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("")) {
@@ -191,175 +193,196 @@ public final class VoicemailInfo extends
protected int computeSerializedSize() {
int size = super.computeSerializedSize();
if (this.date != null && !this.date.equals("")) {
- size += com.google.protobuf.nano.CodedOutputByteBufferNano
- .computeStringSize(1, this.date);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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 (!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);
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(19, this.archived);
}
return size;
}
@Override
- public VoicemailInfo mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ 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;
}
- 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;
- }
}
}
}
@@ -369,8 +392,7 @@ public final class VoicemailInfo extends
return com.google.protobuf.nano.MessageNano.mergeFrom(new VoicemailInfo(), data);
}
- public static VoicemailInfo parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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/binary/aosp/AndroidManifest.xml b/java/com/android/dialer/binary/aosp/AndroidManifest.xml
new file mode 100644
index 000000000..63edb8397
--- /dev/null
+++ b/java/com/android/dialer/binary/aosp/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"
+ coreApp="true"
+ package="com.android.dialer"
+ android:versionCode="100000"
+ android:versionName="10.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.binary.aosp.AospDialerApplication"
+ android:supportsRtl="true"
+ android:usesCleartextTraffic="false">
+ </application>
+
+</manifest>
diff --git a/java/com/android/dialer/binary/aosp/AospDialerApplication.java b/java/com/android/dialer/binary/aosp/AospDialerApplication.java
new file mode 100644
index 000000000..f657a3987
--- /dev/null
+++ b/java/com/android/dialer/binary/aosp/AospDialerApplication.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.binary.aosp;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.binary.common.DialerApplication;
+import com.android.dialer.inject.ContextModule;
+
+/**
+ * The application class for the AOSP Dialer. This is a version of the Dialer app that has no
+ * dependency on Google Play Services.
+ */
+public class AospDialerApplication extends DialerApplication {
+
+
+}
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
new file mode 100644
index 000000000..8628e90c2
--- /dev/null
+++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.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.binary.aosp;
+
+import com.android.dialer.binary.basecomponent.BaseDialerRootComponent;
+import com.android.dialer.enrichedcall.stub.StubEnrichedCallModule;
+import com.android.dialer.inject.ContextModule;
+import com.android.dialer.simulator.impl.SimulatorModule;
+import com.android.incallui.calllocation.stub.StubCallLocationModule;
+import com.android.incallui.maps.stub.StubMapsModule;
+import com.android.voicemail.impl.VoicemailModule;
+import dagger.Component;
+import javax.inject.Singleton;
+
+/** Root component for the AOSP Dialer application. */
+public interface AospDialerRootComponent extends BaseDialerRootComponent {}
diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
new file mode 100644
index 000000000..907671b01
--- /dev/null
+++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.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.binary.basecomponent;
+
+import com.android.dialer.enrichedcall.EnrichedCallComponent;
+import com.android.dialer.simulator.SimulatorComponent;
+import com.android.incallui.calllocation.CallLocationComponent;
+import com.android.incallui.maps.MapsComponent;
+import com.android.voicemail.VoicemailComponent;
+
+/**
+ * Base class for the core application-wide {@link Component}. All variants of the Dialer app should
+ * extend from this component.
+ */
+public interface BaseDialerRootComponent
+ extends CallLocationComponent.HasComponent,
+ EnrichedCallComponent.HasComponent,
+ MapsComponent.HasComponent,
+ SimulatorComponent.HasComponent,
+ VoicemailComponent.HasComponent {}
diff --git a/java/com/android/dialer/binary/common/DialerApplication.java b/java/com/android/dialer/binary/common/DialerApplication.java
new file mode 100644
index 000000000..c0be4328c
--- /dev/null
+++ b/java/com/android/dialer/binary/common/DialerApplication.java
@@ -0,0 +1,43 @@
+/*
+ * 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.binary.common;
+
+import android.app.Application;
+import android.os.Trace;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import com.android.dialer.blocking.BlockedNumbersAutoMigrator;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+
+/** A common application subclass for all Dialer build variants. */
+public abstract class DialerApplication extends Application {
+
+ private volatile Object rootComponent;
+
+ @Override
+ public void onCreate() {
+ Trace.beginSection("DialerApplication.onCreate");
+ super.onCreate();
+ new BlockedNumbersAutoMigrator(
+ this,
+ PreferenceManager.getDefaultSharedPreferences(this),
+ new FilteredNumberAsyncQueryHandler(this))
+ .autoMigrate();
+ Trace.endSection();
+ }
+
+}
diff --git a/java/com/android/dialer/blocking/FilteredNumbersUtil.java b/java/com/android/dialer/blocking/FilteredNumbersUtil.java
index 61ecf1886..f09370e6e 100644
--- a/java/com/android/dialer/blocking/FilteredNumbersUtil.java
+++ b/java/com/android/dialer/blocking/FilteredNumbersUtil.java
@@ -36,6 +36,8 @@ 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.notification.NotificationChannelManager;
+import com.android.dialer.notification.NotificationChannelManager.Channel;
import com.android.dialer.util.PermissionsUtil;
import java.util.concurrent.TimeUnit;
@@ -43,7 +45,8 @@ import java.util.concurrent.TimeUnit;
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;
+ public static final int CALL_BLOCKING_DISABLED_BY_EMERGENCY_CALL_NOTIFICATION_ID =
+ R.id.notification_call_blocking_disabled_by_emergency_call;
// 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
@@ -289,6 +292,7 @@ public class FilteredNumbersUtil {
context.getString(R.string.call_blocking_disabled_notification_text))
.setAutoCancel(true);
+ NotificationChannelManager.applyChannel(builder, context, Channel.MISC, null);
builder.setContentIntent(
PendingIntent.getActivity(
context,
diff --git a/java/com/android/dialer/buildtype/BuildType.java b/java/com/android/dialer/buildtype/BuildType.java
index c0a54a519..6b6bc2906 100644
--- a/java/com/android/dialer/buildtype/BuildType.java
+++ b/java/com/android/dialer/buildtype/BuildType.java
@@ -28,7 +28,7 @@ public class BuildType {
/** The type of build. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- BUGFOOD, FISHFOOD, DOGFOOD, RELEASE,
+ BUGFOOD, FISHFOOD, DOGFOOD, RELEASE, TEST,
})
public @interface Type {}
@@ -36,6 +36,7 @@ public class BuildType {
public static final int FISHFOOD = 2;
public static final int DOGFOOD = 3;
public static final int RELEASE = 4;
+ public static final int TEST = 5;
private static int cachedBuildType;
private static boolean didInitializeBuildType;
diff --git a/java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java b/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java
index e1f2cdc79..70b9f9e37 100644
--- a/java/com/android/dialer/buildtype/dogfood/BuildTypeAccessorImpl.java
+++ b/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java
@@ -25,6 +25,6 @@ public class BuildTypeAccessorImpl implements BuildTypeAccessor {
@Override
@BuildType.Type
public int getBuildType() {
- return BuildType.DOGFOOD;
+ return BuildType.RELEASE;
}
}
diff --git a/java/com/android/dialer/callcomposer/AndroidManifest.xml b/java/com/android/dialer/callcomposer/AndroidManifest.xml
index c99f22b90..369db6f4a 100644
--- a/java/com/android/dialer/callcomposer/AndroidManifest.xml
+++ b/java/com/android/dialer/callcomposer/AndroidManifest.xml
@@ -20,9 +20,8 @@
<application>
<activity
android:name="com.android.dialer.callcomposer.CallComposerActivity"
- android:exported="false"
+ android:exported="true"
android:theme="@style/Theme.AppCompat.CallComposer"
- android:windowSoftInputMode="adjustResize"
- android:screenOrientation="portrait"/>
+ android:windowSoftInputMode="adjustPan"/>
</application>
</manifest>
diff --git a/java/com/android/dialer/callcomposer/CallComposerActivity.java b/java/com/android/dialer/callcomposer/CallComposerActivity.java
index eef3d210d..f73563ff8 100644
--- a/java/com/android/dialer/callcomposer/CallComposerActivity.java
+++ b/java/com/android/dialer/callcomposer/CallComposerActivity.java
@@ -21,9 +21,9 @@ 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.content.res.Configuration;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -35,6 +35,7 @@ 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.util.Base64;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
@@ -60,6 +61,7 @@ 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.EnrichedCallComponent;
import com.android.dialer.enrichedcall.EnrichedCallManager;
import com.android.dialer.enrichedcall.EnrichedCallManager.State;
import com.android.dialer.enrichedcall.Session;
@@ -70,6 +72,7 @@ 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 com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import java.io.File;
/**
@@ -88,18 +91,18 @@ public class CallComposerActivity extends AppCompatActivity
OnPageChangeListener,
CallComposerListener,
OnLayoutChangeListener,
- AnimatorListener,
EnrichedCallManager.StateChangedListener {
- private static final int VIEW_PAGER_ANIMATION_DURATION_MILLIS = 300;
+ public static final String KEY_CONTACT_NAME = "contact_name";
+
private static final int ENTRANCE_ANIMATION_DURATION_MILLIS = 500;
+ private static final int EXIT_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;
@@ -111,6 +114,7 @@ public class CallComposerActivity extends AppCompatActivity
private RelativeLayout contactContainer;
private Toolbar toolbar;
private View sendAndCall;
+ private TextView sendAndCallText;
private ImageView cameraIcon;
private ImageView galleryIcon;
@@ -125,13 +129,8 @@ public class CallComposerActivity extends AppCompatActivity
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);
@@ -156,6 +155,7 @@ public class CallComposerActivity extends AppCompatActivity
windowContainer = (LinearLayout) findViewById(R.id.call_composer_container);
toolbar = (Toolbar) findViewById(R.id.toolbar);
sendAndCall = findViewById(R.id.send_and_call_button);
+ sendAndCallText = (TextView) findViewById(R.id.send_and_call_text);
interpolator = new FastOutSlowInInterpolator();
adapter =
@@ -166,14 +166,8 @@ public class CallComposerActivity extends AppCompatActivity
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();
- }
- });
+ toolbar.setNavigationIcon(R.drawable.quantum_ic_close_white_24);
+ toolbar.setNavigationOnClickListener(v -> finish());
background.addOnLayoutChangeListener(this);
cameraIcon.setOnClickListener(this);
@@ -183,20 +177,12 @@ public class CallComposerActivity extends AppCompatActivity
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);
+ sessionId = savedInstanceState.getLong(SESSION_ID_KEY, Session.NO_SESSION_ID);
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
@@ -204,37 +190,39 @@ public class CallComposerActivity extends AppCompatActivity
ViewUtil.doOnPreDraw(
windowContainer,
false,
- new Runnable() {
- @Override
- public void run() {
- runEntranceAnimation();
- }
+ () -> {
+ showFullscreen(inFullscreenMode);
+ 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);
+ getEnrichedCallManager().registerStateChangedListener(this);
+ if (sessionId == Session.NO_SESSION_ID) {
+ LogUtil.i("CallComposerActivity.onResume", "creating new session");
+ sessionId = getEnrichedCallManager().startCallComposerSession(contact.number);
+ } else if (getEnrichedCallManager().getSession(sessionId) == null) {
+ LogUtil.i(
+ "CallComposerActivity.onResume", "session closed while activity paused, creating new");
+ sessionId = getEnrichedCallManager().startCallComposerSession(contact.number);
+ } else {
+ LogUtil.i("CallComposerActivity.onResume", "session still open, using old");
+ }
+ if (sessionId == Session.NO_SESSION_ID) {
+ LogUtil.w("CallComposerActivity.onResume", "failed to create call composer session");
+ setFailedResultAndFinish();
+ }
refreshUiForCallComposerState();
}
@Override
protected void onPause() {
super.onPause();
- enrichedCallManager.unregisterStateChangedListener(this);
+ getEnrichedCallManager().unregisterStateChangedListener(this);
}
@Override
@@ -243,7 +231,7 @@ public class CallComposerActivity extends AppCompatActivity
}
private void refreshUiForCallComposerState() {
- Session session = enrichedCallManager.getSession(sessionId);
+ Session session = getEnrichedCallManager().getSession(sessionId);
if (session == null) {
return;
}
@@ -256,8 +244,7 @@ public class CallComposerActivity extends AppCompatActivity
if (state == EnrichedCallManager.STATE_START_FAILED
|| state == EnrichedCallManager.STATE_CLOSED) {
- setResult(RESULT_CANCELED);
- finish();
+ setFailedResultAndFinish();
}
}
@@ -293,7 +280,7 @@ public class CallComposerActivity extends AppCompatActivity
if (fragment instanceof MessageComposerFragment) {
MessageComposerFragment messageComposerFragment = (MessageComposerFragment) fragment;
- builder.setSubject(messageComposerFragment.getMessage());
+ builder.setText(messageComposerFragment.getMessage());
placeRCSCall(builder);
}
if (fragment instanceof GalleryComposerFragment) {
@@ -351,7 +338,7 @@ public class CallComposerActivity extends AppCompatActivity
}
private boolean sessionReady() {
- Session session = enrichedCallManager.getSession(sessionId);
+ Session session = getEnrichedCallManager().getSession(sessionId);
if (session == null) {
return false;
}
@@ -362,9 +349,10 @@ public class CallComposerActivity extends AppCompatActivity
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());
+ getEnrichedCallManager().sendCallComposerData(sessionId, builder.build());
TelecomUtil.placeCall(
this, new CallIntentBuilder(contact.number, CallInitiationType.Type.CALL_COMPOSER).build());
+ setResult(RESULT_OK);
finish();
}
@@ -379,24 +367,26 @@ public class CallComposerActivity extends AppCompatActivity
/** Animates {@code contactContainer} to align with content inside viewpager. */
@Override
public void onPageSelected(int position) {
+ if (position == CallComposerPagerAdapter.INDEX_MESSAGE) {
+ sendAndCallText.setText(R.string.send_and_call);
+ } else {
+ sendAndCallText.setText(R.string.share_and_call);
+ }
if (currentIndex == CallComposerPagerAdapter.INDEX_MESSAGE) {
UiUtil.hideKeyboardFrom(this, windowContainer);
- } else if (position == CallComposerPagerAdapter.INDEX_MESSAGE && inFullscreenMode) {
+ } else if (position == CallComposerPagerAdapter.INDEX_MESSAGE
+ && inFullscreenMode
+ && !isLandscapeLayout()) {
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());
- }
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageScrollStateChanged(int state) {}
@@ -407,13 +397,19 @@ public class CallComposerActivity extends AppCompatActivity
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();
+ if (!isSendAndCallHidingOrHidden) {
+ ((CallComposerFragment) adapter.instantiateItem(pager, currentIndex)).clearComposer();
+ } else {
+ // Unregister first to avoid receiving a callback when the session closes
+ getEnrichedCallManager().unregisterStateChangedListener(this);
+ getEnrichedCallManager().endCallComposerSession(sessionId);
+ runExitAnimation();
+ }
}
@Override
@@ -445,29 +441,9 @@ public class CallComposerActivity extends AppCompatActivity
}
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;
+ showFullscreen(contactContainer.getTop() < 0 || inFullscreenMode);
}
- @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}.
@@ -477,12 +453,26 @@ public class CallComposerActivity extends AppCompatActivity
if (arguments == null) {
throw new RuntimeException("CallComposerActivity.onHandleIntent, Arguments cannot be null.");
}
- contact =
- ProtoParsers.getFromInstanceState(
- arguments, ARG_CALL_COMPOSER_CONTACT, new CallComposerContact());
+ if (arguments.get(ARG_CALL_COMPOSER_CONTACT) instanceof String) {
+ byte[] bytes = Base64.decode(arguments.getString(ARG_CALL_COMPOSER_CONTACT), Base64.DEFAULT);
+ try {
+ contact = CallComposerContact.parseFrom(bytes);
+ } catch (InvalidProtocolBufferNanoException e) {
+ Assert.fail(e.toString());
+ }
+ } else {
+ contact =
+ ProtoParsers.getFromInstanceState(
+ arguments, ARG_CALL_COMPOSER_CONTACT, new CallComposerContact());
+ }
updateContactInfo();
}
+ @Override
+ public boolean isLandscapeLayout() {
+ return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
/**
* Populates the contact info fields based on the current contact information. Copied from {@link
* com.android.contacts.common.dialog.CallSubjectDialog}.
@@ -552,25 +542,6 @@ public class CallComposerActivity extends AppCompatActivity
}
}
- 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) {
@@ -578,84 +549,90 @@ public class CallComposerActivity extends AppCompatActivity
}
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);
+ int value = isLandscapeLayout() ? windowContainer.getWidth() : windowContainer.getHeight();
+ ValueAnimator contentAnimation = ValueAnimator.ofFloat(value, 0);
contentAnimation.setInterpolator(interpolator);
contentAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS);
contentAnimation.addUpdateListener(
- new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
+ animation -> {
+ if (isLandscapeLayout()) {
+ windowContainer.setX((Float) animation.getAnimatedValue());
+ } else {
windowContainer.setY((Float) animation.getAnimatedValue());
}
});
- AnimatorSet set = new AnimatorSet();
- set.play(contentAnimation).with(backgroundAnimation);
- set.start();
+ if (!isLandscapeLayout()) {
+ 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(
+ animator -> background.setBackgroundColor((int) animator.getAnimatedValue()));
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(contentAnimation).with(backgroundAnimation);
+ set.start();
+ } else {
+ contentAnimation.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());
+ int value = isLandscapeLayout() ? windowContainer.getWidth() : windowContainer.getHeight();
+ ValueAnimator contentAnimation = ValueAnimator.ofFloat(0, value);
contentAnimation.setInterpolator(interpolator);
- contentAnimation.setDuration(ENTRANCE_ANIMATION_DURATION_MILLIS);
+ contentAnimation.setDuration(EXIT_ANIMATION_DURATION_MILLIS);
contentAnimation.addUpdateListener(
- new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
+ animation -> {
+ if (isLandscapeLayout()) {
+ windowContainer.setX((Float) animation.getAnimatedValue());
+ } else {
windowContainer.setY((Float) animation.getAnimatedValue());
- if (animation.getAnimatedFraction() > .75) {
- finish();
- }
+ }
+ if (animation.getAnimatedFraction() > .95) {
+ finish();
}
});
- AnimatorSet set = new AnimatorSet();
- set.play(contentAnimation).with(backgroundAnimation);
- set.start();
+
+ if (!isLandscapeLayout()) {
+ 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(EXIT_ANIMATION_DURATION_MILLIS);
+ backgroundAnimation.addUpdateListener(
+ animator -> background.setBackgroundColor((int) animator.getAnimatedValue()));
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(contentAnimation).with(backgroundAnimation);
+ set.start();
+ } else {
+ contentAnimation.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);
+ public void showFullscreen(boolean fullscreen) {
+ inFullscreenMode = fullscreen;
ViewGroup.LayoutParams layoutParams = pager.getLayoutParams();
- if (show) {
+ if (isLandscapeLayout()) {
+ layoutParams.height = background.getHeight() - messageIcon.getHeight();
+ toolbar.setVisibility(View.INVISIBLE);
+ contactContainer.setVisibility(View.GONE);
+ } else if (fullscreen || getResources().getBoolean(R.bool.show_toolbar)) {
layoutParams.height = background.getHeight() - toolbar.getHeight() - messageIcon.getHeight();
+ toolbar.setVisibility(View.VISIBLE);
+ contactContainer.setVisibility(View.GONE);
} else {
layoutParams.height =
getResources().getDimensionPixelSize(R.dimen.call_composer_view_pager_height);
+ toolbar.setVisibility(View.INVISIBLE);
+ contactContainer.setVisibility(View.VISIBLE);
}
pager.setLayoutParams(layoutParams);
}
@@ -686,35 +663,32 @@ public class CallComposerActivity extends AppCompatActivity
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);
- }
+ () -> {
+ 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 onAnimationCancel(Animator animation) {}
- @Override
- public void onAnimationRepeat(Animator animation) {}
- });
- animator.start();
- }
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+ });
+ animator.start();
});
}
}
@@ -725,4 +699,14 @@ public class CallComposerActivity extends AppCompatActivity
galleryIcon.setAlpha(position == CallComposerPagerAdapter.INDEX_GALLERY ? 1 : alpha);
messageIcon.setAlpha(position == CallComposerPagerAdapter.INDEX_MESSAGE ? 1 : alpha);
}
+
+ private void setFailedResultAndFinish() {
+ setResult(RESULT_FIRST_USER, new Intent().putExtra(KEY_CONTACT_NAME, contact.nameOrNumber));
+ finish();
+ }
+
+ @NonNull
+ private EnrichedCallManager getEnrichedCallManager() {
+ return EnrichedCallComponent.get(this).getEnrichedCallManager();
+ }
}
diff --git a/java/com/android/dialer/callcomposer/CallComposerFragment.java b/java/com/android/dialer/callcomposer/CallComposerFragment.java
index d6f944955..ee1eb462a 100644
--- a/java/com/android/dialer/callcomposer/CallComposerFragment.java
+++ b/java/com/android/dialer/callcomposer/CallComposerFragment.java
@@ -17,13 +17,8 @@
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;
@@ -34,26 +29,10 @@ 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) {
+ if (FragmentUtils.getParent(this, CallComposerListener.class) == null) {
LogUtil.e(
"CallComposerFragment.onAttach",
"Container activity must implement CallComposerListener.");
@@ -61,56 +40,15 @@ public abstract class CallComposerFragment extends Fragment {
}
}
- /** 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;
- }
-
+ @Nullable
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);
- }
+ return FragmentUtils.getParent(this, CallComposerListener.class);
}
public abstract boolean shouldHide();
+ public abstract void clearComposer();
+
/** Interface used to listen to CallComposeFragments */
public interface CallComposerListener {
/** Let the listener know when a call is ready to be composed. */
@@ -121,5 +59,8 @@ public abstract class CallComposerFragment extends Fragment {
/** True is the listener is in fullscreen. */
boolean isFullscreen();
+
+ /** True if the layout is in landscape mode. */
+ boolean isLandscapeLayout();
}
}
diff --git a/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java b/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java
index 4d4058a0a..edf980ee9 100644
--- a/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java
+++ b/java/com/android/dialer/callcomposer/CallComposerPagerAdapter.java
@@ -18,11 +18,11 @@ package com.android.dialer.callcomposer;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.app.FragmentPagerAdapter;
import com.android.dialer.common.Assert;
/** ViewPager adapter for call compose UI. */
-public class CallComposerPagerAdapter extends FragmentStatePagerAdapter {
+public class CallComposerPagerAdapter extends FragmentPagerAdapter {
public static final int INDEX_CAMERA = 0;
public static final int INDEX_GALLERY = 1;
diff --git a/java/com/android/dialer/callcomposer/CameraComposerFragment.java b/java/com/android/dialer/callcomposer/CameraComposerFragment.java
index f2d0a94a7..583fb5446 100644
--- a/java/com/android/dialer/callcomposer/CameraComposerFragment.java
+++ b/java/com/android/dialer/callcomposer/CameraComposerFragment.java
@@ -56,6 +56,9 @@ import com.android.dialer.util.PermissionsUtil;
public class CameraComposerFragment extends CallComposerFragment
implements CameraManagerListener, OnClickListener, CameraManager.MediaCallback {
+ private static final String CAMERA_DIRECTION_KEY = "camera_direction";
+ private static final String CAMERA_URI_KEY = "camera_key";
+
private View permissionView;
private ImageButton exitFullscreen;
private ImageButton fullscreen;
@@ -68,11 +71,13 @@ public class CameraComposerFragment extends CallComposerFragment
private View allowPermission;
private CameraPreviewHost preview;
private ProgressBar loading;
+ private ImageView previewImageView;
private Uri cameraUri;
private boolean processingUri;
private String[] permissions = new String[] {Manifest.permission.CAMERA};
private CameraUriCallback uriCallback;
+ private int cameraDirection = CameraInfo.CAMERA_FACING_BACK;
public static CameraComposerFragment newInstance() {
return new CameraComposerFragment();
@@ -94,6 +99,7 @@ public class CameraComposerFragment extends CallComposerFragment
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);
+ previewImageView = (ImageView) root.findViewById(R.id.preview_image_view);
exitFullscreen.setOnClickListener(this);
fullscreen.setOnClickListener(this);
@@ -115,10 +121,12 @@ public class CameraComposerFragment extends CallComposerFragment
ContextCompat.getColor(getContext(), R.color.dialer_theme_color));
permissionView.setVisibility(View.VISIBLE);
} else {
+ if (bundle != null) {
+ cameraDirection = bundle.getInt(CAMERA_DIRECTION_KEY);
+ cameraUri = bundle.getParcelable(CAMERA_URI_KEY);
+ }
setupCamera();
}
-
- setTopView(cameraView);
return root;
}
@@ -126,8 +134,8 @@ public class CameraComposerFragment extends CallComposerFragment
CameraManager.get().setListener(this);
preview.setShown();
CameraManager.get().setRenderOverlay(focus);
- CameraManager.get().selectCamera(CameraInfo.CAMERA_FACING_BACK);
- setCameraUri(null);
+ CameraManager.get().selectCamera(cameraDirection);
+ setCameraUri(cameraUri);
}
@Override
@@ -146,10 +154,16 @@ public class CameraComposerFragment extends CallComposerFragment
}
@Override
+ public void clearComposer() {
+ processingUri = false;
+ setCameraUri(null);
+ }
+
+ @Override
public void onClick(View view) {
if (view == capture) {
float heightPercent = 1;
- if (!getListener().isFullscreen()) {
+ if (!getListener().isFullscreen() && !getListener().isLandscapeLayout()) {
heightPercent = Math.min((float) cameraView.getHeight() / preview.getView().getHeight(), 1);
}
@@ -162,8 +176,7 @@ public class CameraComposerFragment extends CallComposerFragment
((Animatable) swapCamera.getDrawable()).start();
CameraManager.get().swapCamera();
} else if (view == cancel) {
- processingUri = false;
- setCameraUri(null);
+ clearComposer();
} else if (view == exitFullscreen) {
getListener().showFullscreen(false);
fullscreen.setVisibility(View.VISIBLE);
@@ -314,6 +327,13 @@ public class CameraComposerFragment extends CallComposerFragment
boolean isCameraAvailable = CameraManager.get().isCameraAvailable();
boolean uriReadyOrProcessing = cameraUri != null || processingUri;
+ if (cameraUri != null) {
+ previewImageView.setImageURI(cameraUri);
+ previewImageView.setVisibility(View.VISIBLE);
+ } else {
+ previewImageView.setVisibility(View.GONE);
+ }
+
if (cameraUri == null && isCameraAvailable) {
CameraManager.get().resetPreview();
cancel.setVisibility(View.GONE);
@@ -328,7 +348,7 @@ public class CameraComposerFragment extends CallComposerFragment
capture.setVisibility(uriReadyOrProcessing ? View.GONE : View.VISIBLE);
cancel.setVisibility(uriReadyOrProcessing ? View.VISIBLE : View.GONE);
- if (uriReadyOrProcessing) {
+ if (uriReadyOrProcessing || getListener().isLandscapeLayout()) {
fullscreen.setVisibility(View.GONE);
exitFullscreen.setVisibility(View.GONE);
} else if (getListener().isFullscreen()) {
@@ -344,6 +364,13 @@ public class CameraComposerFragment extends CallComposerFragment
}
@Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(CAMERA_DIRECTION_KEY, CameraManager.get().getCameraInfo().facing);
+ outState.putParcelable(CAMERA_URI_KEY, cameraUri);
+ }
+
+ @Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (permissions.length > 0 && permissions[0].equals(this.permissions[0])) {
diff --git a/java/com/android/dialer/callcomposer/GalleryComposerFragment.java b/java/com/android/dialer/callcomposer/GalleryComposerFragment.java
index 623127945..b53d6a9d6 100644
--- a/java/com/android/dialer/callcomposer/GalleryComposerFragment.java
+++ b/java/com/android/dialer/callcomposer/GalleryComposerFragment.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcelable;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -45,11 +46,17 @@ import com.android.dialer.logging.Logger;
import com.android.dialer.logging.nano.DialerImpression;
import com.android.dialer.util.PermissionsUtil;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
/** Fragment used to compose call with image from the user's gallery. */
public class GalleryComposerFragment extends CallComposerFragment
implements LoaderCallbacks<Cursor>, OnClickListener {
+ private static final String SELECTED_DATA_KEY = "selected_data";
+ private static final String IS_COPY_KEY = "is_copy";
+ private static final String INSERTED_IMAGES_KEY = "inserted_images";
+
private static final int RESULT_LOAD_IMAGE = 1;
private static final int RESULT_OPEN_SETTINGS = 2;
@@ -62,6 +69,7 @@ public class GalleryComposerFragment extends CallComposerFragment
private CursorLoader cursorLoader;
private GalleryGridItemData selectedData = null;
private boolean selectedDataIsCopy;
+ private List<GalleryGridItemData> insertedImages = new ArrayList<>();
public static GalleryComposerFragment newInstance() {
return new GalleryComposerFragment();
@@ -89,10 +97,13 @@ public class GalleryComposerFragment extends CallComposerFragment
ContextCompat.getColor(getContext(), R.color.dialer_theme_color));
permissionView.setVisibility(View.VISIBLE);
} else {
+ if (bundle != null) {
+ selectedData = bundle.getParcelable(SELECTED_DATA_KEY);
+ selectedDataIsCopy = bundle.getBoolean(IS_COPY_KEY);
+ insertedImages = bundle.getParcelableArrayList(INSERTED_IMAGES_KEY);
+ }
setupGallery();
}
-
- setTopView(galleryGridView);
return view;
}
@@ -110,6 +121,10 @@ public class GalleryComposerFragment extends CallComposerFragment
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
+ if (insertedImages != null && !insertedImages.isEmpty()) {
+ adapter.insertEntries(insertedImages);
+ }
+ setSelected(selectedData, selectedDataIsCopy);
}
@Override
@@ -147,7 +162,7 @@ public class GalleryComposerFragment extends CallComposerFragment
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, RESULT_LOAD_IMAGE);
} else if (itemView.getData().equals(selectedData)) {
- setSelected(null, false);
+ clearComposer();
} else {
setSelected(new GalleryGridItemData(itemView.getData()), false);
}
@@ -179,7 +194,10 @@ public class GalleryComposerFragment extends CallComposerFragment
selectedData = data;
selectedDataIsCopy = isCopy;
adapter.setSelected(selectedData);
- getListener().composeCall(this);
+ CallComposerListener listener = getListener();
+ if (listener != null) {
+ getListener().composeCall(this);
+ }
}
@Override
@@ -190,6 +208,20 @@ public class GalleryComposerFragment extends CallComposerFragment
}
@Override
+ public void clearComposer() {
+ setSelected(null, false);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(SELECTED_DATA_KEY, selectedData);
+ outState.putBoolean(IS_COPY_KEY, selectedDataIsCopy);
+ outState.putParcelableArrayList(
+ INSERTED_IMAGES_KEY, (ArrayList<? extends Parcelable>) insertedImages);
+ }
+
+ @Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (permissions.length > 0 && permissions[0].equals(this.permissions[0])) {
@@ -238,7 +270,9 @@ public class GalleryComposerFragment extends CallComposerFragment
new Callback() {
@Override
public void onCopySuccessful(File file, String mimeType) {
- setSelected(adapter.insertEntry(file.getAbsolutePath(), mimeType), true);
+ GalleryGridItemData data = adapter.insertEntry(file.getAbsolutePath(), mimeType);
+ insertedImages.add(0, data);
+ setSelected(data, true);
}
@Override
diff --git a/java/com/android/dialer/callcomposer/GalleryGridAdapter.java b/java/com/android/dialer/callcomposer/GalleryGridAdapter.java
index 0a7fd769b..84257b2af 100644
--- a/java/com/android/dialer/callcomposer/GalleryGridAdapter.java
+++ b/java/com/android/dialer/callcomposer/GalleryGridAdapter.java
@@ -104,6 +104,18 @@ public class GalleryGridAdapter extends CursorAdapter {
}
}
+ public void insertEntries(@NonNull List<GalleryGridItemData> entries) {
+ Assert.checkArgument(entries.size() != 0);
+ LogUtil.i("GalleryGridAdapter.insertRows", "inserting %d rows", entries.size());
+ MatrixCursor extraRow = new MatrixCursor(GalleryGridItemData.IMAGE_PROJECTION);
+ for (GalleryGridItemData entry : entries) {
+ extraRow.addRow(new Object[] {0L, entry.getFilePath(), entry.getMimeType(), ""});
+ }
+ extraRow.moveToFirst();
+ Cursor extendedCursor = new MergeCursor(new Cursor[] {extraRow, getCursor()});
+ swapCursor(extendedCursor);
+ }
+
public GalleryGridItemData insertEntry(String filePath, String mimeType) {
LogUtil.i("GalleryGridAdapter.insertRow", mimeType + " " + filePath);
diff --git a/java/com/android/dialer/callcomposer/GalleryGridItemData.java b/java/com/android/dialer/callcomposer/GalleryGridItemData.java
index 402c6ce6d..43db96dd5 100644
--- a/java/com/android/dialer/callcomposer/GalleryGridItemData.java
+++ b/java/com/android/dialer/callcomposer/GalleryGridItemData.java
@@ -18,6 +18,8 @@ package com.android.dialer.callcomposer;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.provider.MediaStore.Images.Media;
import android.support.annotation.Nullable;
import android.text.TextUtils;
@@ -26,7 +28,7 @@ import java.io.File;
import java.util.Objects;
/** Provides data for GalleryGridItemView */
-public final class GalleryGridItemData {
+public final class GalleryGridItemData implements Parcelable {
public static final String[] IMAGE_PROJECTION =
new String[] {Media._ID, Media.DATA, Media.MIME_TYPE, Media.DATE_MODIFIED};
@@ -88,4 +90,35 @@ public final class GalleryGridItemData {
public int hashCode() {
return Objects.hash(filePath, mimeType, dateModifiedSeconds);
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(filePath);
+ dest.writeString(mimeType);
+ dest.writeLong(dateModifiedSeconds);
+ }
+
+ public static final Creator<GalleryGridItemData> CREATOR =
+ new Creator<GalleryGridItemData>() {
+ @Override
+ public GalleryGridItemData createFromParcel(Parcel in) {
+ return new GalleryGridItemData(in);
+ }
+
+ @Override
+ public GalleryGridItemData[] newArray(int size) {
+ return new GalleryGridItemData[size];
+ }
+ };
+
+ private GalleryGridItemData(Parcel in) {
+ filePath = in.readString();
+ mimeType = in.readString();
+ dateModifiedSeconds = in.readLong();
+ }
}
diff --git a/java/com/android/dialer/callcomposer/MessageComposerFragment.java b/java/com/android/dialer/callcomposer/MessageComposerFragment.java
index 521b71402..d8100033f 100644
--- a/java/com/android/dialer/callcomposer/MessageComposerFragment.java
+++ b/java/com/android/dialer/callcomposer/MessageComposerFragment.java
@@ -77,6 +77,7 @@ public class MessageComposerFragment extends CallComposerFragment
customMessage.addTextChangedListener(
new TextWatcher() {
@Override
+
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
@@ -90,8 +91,6 @@ public class MessageComposerFragment extends CallComposerFragment
}
view.findViewById(R.id.message_chat).setOnClickListener(this);
view.findViewById(R.id.message_question).setOnClickListener(this);
-
- setTopView(urgent);
return view;
}
@@ -140,4 +139,9 @@ public class MessageComposerFragment extends CallComposerFragment
public boolean shouldHide() {
return TextUtils.isEmpty(getMessage());
}
+
+ @Override
+ public void clearComposer() {
+ customMessage.getText().clear();
+ }
}
diff --git a/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java b/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java
index 150009495..a23014bf0 100644
--- a/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java
+++ b/java/com/android/dialer/callcomposer/camera/ImagePersistTask.java
@@ -74,7 +74,9 @@ public class ImagePersistTask extends FallibleAsyncTask<Void, Void, Uri> {
if (mHeightPercent != 1.0f) {
writeClippedBitmap(outputStream);
} else {
- outputStream.write(mBytes, 0, mBytes.length);
+ Bitmap bitmap = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
+ bitmap = CopyAndResizeImageTask.resizeForEnrichedCalling(bitmap);
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
}
}
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
index 75401b14b..a4198fcf9 100644
--- a/java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml
+++ b/java/com/android/dialer/callcomposer/cameraui/res/layout/camera_view.xml
@@ -46,6 +46,14 @@
android:background="@android:color/white"
android:visibility="gone" />
+ <ImageView
+ android:id="@+id/preview_image_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ android:background="#000000"
+ 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"
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
index 518b53ffd..f687f0b5c 100644
--- a/java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml
+++ b/java/com/android/dialer/callcomposer/res/layout/call_composer_activity.xml
@@ -120,13 +120,14 @@
android:visibility="invisible"
android:background="@color/compose_and_call_background">
<TextView
+ android:id="@+id/send_and_call_text"
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:text="@string/share_and_call"
android:textSize="@dimen/send_and_call_text_size"
android:fontFamily="sans-serif-medium"
android:textColor="@color/background_dialer_white"/>
@@ -140,8 +141,8 @@
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:titleTextAppearance="@style/toolbar_title_text"
+ android:subtitleTextAppearance="@style/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_gallery_composer.xml b/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml
index 58893ba50..a4bd4df03 100644
--- a/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml
+++ b/java/com/android/dialer/callcomposer/res/layout/fragment_gallery_composer.xml
@@ -27,7 +27,7 @@
android:paddingLeft="@dimen/gallery_item_padding"
android:paddingRight="@dimen/gallery_item_padding"
android:paddingTop="@dimen/gallery_item_padding"
- android:numColumns="3"/>
+ android:numColumns="@integer/gallery_composer_grid_view_rows"/>
<include
android:id="@+id/permission_view"
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
index 97f232b3a..577887be9 100644
--- a/java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml
+++ b/java/com/android/dialer/callcomposer/res/layout/fragment_message_composer.xml
@@ -15,43 +15,45 @@
~ 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">
+ 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"/>
+ android:id="@+id/message_urgent"
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ android:layout_marginTop="8dp"
+ 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"/>
+ android:id="@+id/message_chat"
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ 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"/>
+ android:id="@+id/message_question"
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ android:layout_marginBottom="8dp"
+ 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"/>
+ 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">
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
<EditText
android:id="@+id/custom_message"
@@ -59,21 +61,22 @@
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:hint="@string/message_composer_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"/>
+ android:layout_toStartOf="@+id/remaining_characters"
+ android:imeOptions="flagNoExtractUi"/>
<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"/>
+ android:id="@+id/remaining_characters"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/message_composer_item_padding"
+ android:layout_alignParentEnd="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/values-h260dp/values.xml b/java/com/android/dialer/callcomposer/res/values-h260dp/values.xml
new file mode 100644
index 000000000..c31f3b015
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values-h260dp/values.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>
+ <bool name="show_toolbar">true</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values-h480dp/values.xml b/java/com/android/dialer/callcomposer/res/values-h480dp/values.xml
new file mode 100644
index 000000000..77b77a553
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values-h480dp/values.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>
+ <bool name="show_toolbar">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values-w360dp/values.xml b/java/com/android/dialer/callcomposer/res/values-w360dp/values.xml
new file mode 100644
index 000000000..adff63518
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values-w360dp/values.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>
+ <integer name="gallery_composer_grid_view_rows">3</integer>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values-w500dp/values.xml b/java/com/android/dialer/callcomposer/res/values-w500dp/values.xml
new file mode 100644
index 000000000..3ec2b3513
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values-w500dp/values.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>
+ <integer name="gallery_composer_grid_view_rows">4</integer>
+</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
index 3ebda7a0f..5571170b2 100644
--- a/java/com/android/dialer/callcomposer/res/values/dimens.xml
+++ b/java/com/android/dialer/callcomposer/res/values/dimens.xml
@@ -17,10 +17,6 @@
<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>
diff --git a/java/com/android/dialer/callcomposer/res/values/strings.xml b/java/com/android/dialer/callcomposer/res/values/strings.xml
index 35a8cf9da..cc7762b64 100644
--- a/java/com/android/dialer/callcomposer/res/values/strings.xml
+++ b/java/com/android/dialer/callcomposer/res/values/strings.xml
@@ -22,9 +22,11 @@
<!-- 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="message_composer_custom_message_hint">Write a custom message</string>
+ <!-- Text for a button to make a phone call combined with a text message [CHAR LIMIT=26] -->
<string name="send_and_call">Send and call</string>
+ <!-- Text for a button to make a phone call combined with a picture or other media [CHAR LIMIT=26] -->
+ <string name="share_and_call">Share 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. -->
diff --git a/java/com/android/dialer/callcomposer/res/values/styles.xml b/java/com/android/dialer/callcomposer/res/values/styles.xml
index 891f6397d..29ac4ddaa 100644
--- a/java/com/android/dialer/callcomposer/res/values/styles.xml
+++ b/java/com/android/dialer/callcomposer/res/values/styles.xml
@@ -36,15 +36,6 @@
<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>
+ <item name="android:gravity">center_vertical</item>
</style>
</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callcomposer/res/values/values.xml b/java/com/android/dialer/callcomposer/res/values/values.xml
new file mode 100644
index 000000000..39b8e4071
--- /dev/null
+++ b/java/com/android/dialer/callcomposer/res/values/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>
+ <integer name="gallery_composer_grid_view_rows">2</integer>
+ <bool name="show_toolbar">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/AndroidManifest.xml b/java/com/android/dialer/calldetails/AndroidManifest.xml
new file mode 100644
index 000000000..b71207ba2
--- /dev/null
+++ b/java/com/android/dialer/calldetails/AndroidManifest.xml
@@ -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
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.dialer.calldetails">
+ <application>
+ <activity
+ android:label="@string/call_details"
+ android:name="com.android.dialer.calldetails.CallDetailsActivity"
+ android:theme="@style/Theme.AppCompat.NoActionBar">
+ <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>
+ </application>
+</manifest>
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java
new file mode 100644
index 000000000..6070640a0
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java
@@ -0,0 +1,130 @@
+/*
+ * 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.calldetails;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.MenuItem;
+import android.widget.Toolbar;
+import com.android.dialer.callcomposer.nano.CallComposerContact;
+import com.android.dialer.calldetails.nano.CallDetailsEntries;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.AsyncTaskExecutors;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.protos.ProtoParsers;
+
+/** Displays the details of a specific call log entry. */
+public class CallDetailsActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CALL_DETAILS_ENTRIES = "call_details_entries";
+ private static final String EXTRA_CONTACT = "contact";
+ private static final String TASK_DELETE = "task_delete";
+
+ private CallDetailsEntry[] entries;
+
+ public static Intent newInstance(
+ Context context, @NonNull CallDetailsEntries details, @NonNull CallComposerContact contact) {
+ Assert.isNotNull(details);
+ Assert.isNotNull(contact);
+
+ Intent intent = new Intent(context, CallDetailsActivity.class);
+ ProtoParsers.put(intent, EXTRA_CONTACT, contact);
+ ProtoParsers.put(intent, EXTRA_CALL_DETAILS_ENTRIES, details);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.call_details_activity);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setActionBar(toolbar);
+ toolbar.inflateMenu(R.menu.call_details_menu);
+ toolbar.setNavigationOnClickListener(v -> finish());
+ onHandleIntent(getIntent());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ onHandleIntent(intent);
+ }
+
+ private void onHandleIntent(Intent intent) {
+ Bundle arguments = intent.getExtras();
+ CallComposerContact contact =
+ ProtoParsers.getFromInstanceState(arguments, EXTRA_CONTACT, new CallComposerContact());
+ entries =
+ ProtoParsers.getFromInstanceState(
+ arguments, EXTRA_CALL_DETAILS_ENTRIES, new CallDetailsEntries())
+ .entries;
+ RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ recyclerView.setAdapter(new CallDetailsAdapter(this, contact, entries));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.call_detail_delete_menu_item) {
+ Logger.get(this).logImpression(DialerImpression.Type.USER_DELETED_CALL_LOG_ITEM);
+ AsyncTaskExecutors.createAsyncTaskExecutor().submit(TASK_DELETE, new DeleteCallsTask());
+ item.setEnabled(false);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /** Delete specified calls from the call log. */
+ private class DeleteCallsTask extends AsyncTask<Void, Void, Void> {
+
+ private final String callIds;
+
+ DeleteCallsTask() {
+ StringBuilder callIds = new StringBuilder();
+ for (CallDetailsEntry entry : entries) {
+ if (callIds.length() != 0) {
+ callIds.append(",");
+ }
+ callIds.append(entry.callId);
+ }
+ this.callIds = callIds.toString();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ getContentResolver()
+ .delete(Calls.CONTENT_URI, CallLog.Calls._ID + " IN (" + callIds + ")", null);
+ return null;
+ }
+
+ @Override
+ public void onPostExecute(Void result) {
+ finish();
+ }
+ }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapter.java b/java/com/android/dialer/calldetails/CallDetailsAdapter.java
new file mode 100644
index 000000000..954583077
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.calldetails;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import com.android.dialer.callcomposer.nano.CallComposerContact;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.calllogutils.CallTypeHelper;
+import com.android.dialer.common.Assert;
+
+/** Adapter for RecyclerView in {@link CallDetailsActivity}. */
+public class CallDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ private static final int HEADER_VIEW_TYPE = 1;
+ private static final int CALL_ENTRY_VIEW_TYPE = 2;
+ private static final int FOOTER_VIEW_TYPE = 3;
+
+ private final CallComposerContact contact;
+ private final CallDetailsEntry[] callDetailsEntries;
+ private final CallTypeHelper callTypeHelper;
+
+ public CallDetailsAdapter(
+ Context context, CallComposerContact contact, CallDetailsEntry[] callDetailsEntries) {
+ this.contact = Assert.isNotNull(contact);
+ this.callDetailsEntries = Assert.isNotNull(callDetailsEntries);
+ callTypeHelper = new CallTypeHelper(context.getResources());
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ switch (viewType) {
+ case HEADER_VIEW_TYPE:
+ return new CallDetailsHeaderViewHolder(
+ inflater.inflate(R.layout.contact_container, parent, false));
+ case CALL_ENTRY_VIEW_TYPE:
+ return new CallDetailsEntryViewHolder(
+ inflater.inflate(R.layout.call_details_entry, parent, false));
+ case FOOTER_VIEW_TYPE:
+ return new CallDetailsFooterViewHolder(
+ inflater.inflate(R.layout.call_details_footer, parent, false));
+ default:
+ Assert.fail("No ViewHolder available for viewType: " + viewType);
+ return null;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ if (position == 0) { // Header
+ ((CallDetailsHeaderViewHolder) holder).updateContactInfo(contact);
+ } else if (position == getItemCount() - 1) {
+ ((CallDetailsFooterViewHolder) holder).setPhoneNumber(contact.number);
+ } else {
+ CallDetailsEntryViewHolder viewHolder = (CallDetailsEntryViewHolder) holder;
+ viewHolder.setCallDetails(
+ contact.number,
+ callDetailsEntries[position - 1],
+ callTypeHelper,
+ position != getItemCount() - 2);
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) { // Header
+ return HEADER_VIEW_TYPE;
+ } else if (position == getItemCount() - 1) {
+ return FOOTER_VIEW_TYPE;
+ } else {
+ return CALL_ENTRY_VIEW_TYPE;
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return callDetailsEntries.length + 2; // Header + footer
+ }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
new file mode 100644
index 000000000..b1a70af0c
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsEntryViewHolder.java
@@ -0,0 +1,195 @@
+/*
+ * 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.calldetails;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.calllogutils.CallEntryFormatter;
+import com.android.dialer.calllogutils.CallTypeHelper;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.AppCompatConstants;
+import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult;
+import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+
+/** ViewHolder for call entries in {@link CallDetailsActivity}. */
+public class CallDetailsEntryViewHolder extends ViewHolder {
+
+ private final ImageView callTypeIcon;
+ private final TextView callTypeText;
+ private final TextView callTime;
+ private final TextView callDuration;
+
+ private final View multimediaImageContainer;
+ private final View multimediaDetailsContainer;
+ private final View multimediaDivider;
+
+ private final TextView multimediaDetails;
+
+ private final ImageView multimediaImage;
+
+ // TODO: Display this when location is stored - b/36160042
+ @SuppressWarnings("unused")
+ private final TextView multimediaAttachmentsNumber;
+
+ private final Context context;
+
+ public CallDetailsEntryViewHolder(View container) {
+ super(container);
+ context = container.getContext();
+
+ callTypeIcon = (ImageView) container.findViewById(R.id.call_direction);
+ callTypeText = (TextView) container.findViewById(R.id.call_type);
+ callTime = (TextView) container.findViewById(R.id.call_time);
+ callDuration = (TextView) container.findViewById(R.id.call_duration);
+
+ multimediaImageContainer = container.findViewById(R.id.multimedia_image_container);
+ multimediaDetailsContainer = container.findViewById(R.id.ec_container);
+ multimediaDivider = container.findViewById(R.id.divider);
+ multimediaDetails = (TextView) container.findViewById(R.id.multimedia_details);
+ multimediaImage = (ImageView) container.findViewById(R.id.multimedia_image);
+ multimediaAttachmentsNumber =
+ (TextView) container.findViewById(R.id.multimedia_attachments_number);
+ }
+
+ void setCallDetails(
+ String number,
+ CallDetailsEntry entry,
+ CallTypeHelper callTypeHelper,
+ boolean showMultimediaDivider) {
+ int callType = entry.callType;
+ boolean isVideoCall =
+ (entry.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
+ && CallUtil.isVideoEnabled(context);
+ boolean isPulledCall =
+ (entry.features & Calls.FEATURES_PULLED_EXTERNALLY) == Calls.FEATURES_PULLED_EXTERNALLY;
+
+ Drawable callIcon = getIconForCallType(context.getResources(), callType);
+ int color = getColorForCallType(context, callType);
+ callIcon.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
+ callTime.setTextColor(color);
+ callTypeIcon.setImageDrawable(callIcon);
+
+ callTypeText.setText(callTypeHelper.getCallTypeText(callType, isVideoCall, isPulledCall));
+ callTime.setText(CallEntryFormatter.formatDate(context, entry.date));
+ if (CallTypeHelper.isMissedCallType(callType)) {
+ callDuration.setVisibility(View.GONE);
+ } else {
+ callDuration.setVisibility(View.VISIBLE);
+ callDuration.setText(
+ CallEntryFormatter.formatDurationAndDataUsage(context, entry.duration, entry.dataUsage));
+ }
+ setMultimediaDetails(number, entry, showMultimediaDivider);
+ }
+
+ private void setMultimediaDetails(String number, CallDetailsEntry entry, boolean showDivider) {
+ multimediaDivider.setVisibility(showDivider ? View.VISIBLE : View.GONE);
+ if (entry.historyResults == null || entry.historyResults.length <= 0) {
+ LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no data, hiding UI");
+ multimediaDetailsContainer.setVisibility(View.GONE);
+ } else {
+
+ // TODO: b/36158891 Add room for 2 pieces of enriched call data. It's possible
+ // to have both call composer data and post call data for a single call.
+ HistoryResult historyResult = entry.historyResults[0];
+ multimediaDetailsContainer.setVisibility(View.VISIBLE);
+ multimediaDetailsContainer.setOnClickListener(
+ (v) -> {
+ DialerUtils.startActivityWithErrorToast(context, IntentUtil.getSendSmsIntent(number));
+ });
+ multimediaImageContainer.setClipToOutline(true);
+
+ if (!TextUtils.isEmpty(historyResult.imageUri)) {
+ LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "setting image");
+ multimediaImageContainer.setVisibility(View.VISIBLE);
+ multimediaImage.setImageURI(Uri.parse(historyResult.imageUri));
+ multimediaDetails.setText(
+ isIncoming(historyResult) ? R.string.received_a_photo : R.string.sent_a_photo);
+ } else {
+ LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no image");
+ }
+
+ // Set text after image to overwrite the received/sent a photo text
+ if (!TextUtils.isEmpty(historyResult.text)) {
+ LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "showing text");
+ multimediaDetails.setText(
+ context.getString(R.string.message_in_quotes, historyResult.text));
+ } else {
+ LogUtil.i("CallDetailsEntryViewHolder.setMultimediaDetails", "no text");
+ }
+ }
+ }
+
+ private static boolean isIncoming(@NonNull HistoryResult historyResult) {
+ return historyResult.type == Type.INCOMING_POST_CALL
+ || historyResult.type == Type.INCOMING_CALL_COMPOSER;
+ }
+
+ private static Drawable getIconForCallType(Resources resources, int callType) {
+ switch (callType) {
+ case AppCompatConstants.CALLS_OUTGOING_TYPE:
+ return resources.getDrawable(R.drawable.quantum_ic_call_made_white_24);
+ case AppCompatConstants.CALLS_BLOCKED_TYPE:
+ return resources.getDrawable(R.drawable.quantum_ic_block_white_24);
+ case AppCompatConstants.CALLS_INCOMING_TYPE:
+ case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE:
+ case AppCompatConstants.CALLS_REJECTED_TYPE:
+ return resources.getDrawable(R.drawable.quantum_ic_call_received_white_24);
+ case AppCompatConstants.CALLS_MISSED_TYPE:
+ 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 resources.getDrawable(R.drawable.quantum_ic_call_missed_white_24);
+ }
+ }
+
+ private static @ColorInt int getColorForCallType(Context context, int callType) {
+ switch (callType) {
+ case AppCompatConstants.CALLS_OUTGOING_TYPE:
+ case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
+ case AppCompatConstants.CALLS_BLOCKED_TYPE:
+ case AppCompatConstants.CALLS_INCOMING_TYPE:
+ case AppCompatConstants.CALLS_ANSWERED_EXTERNALLY_TYPE:
+ case AppCompatConstants.CALLS_REJECTED_TYPE:
+ return ContextCompat.getColor(context, R.color.dialer_secondary_text_color);
+ case AppCompatConstants.CALLS_MISSED_TYPE:
+ 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 ContextCompat.getColor(context, R.color.missed_call);
+ }
+ }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.java
new file mode 100644
index 000000000..36662bab9
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsFooterViewHolder.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.dialer.calldetails;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.contacts.common.ClipboardUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.CallUtil;
+import com.android.dialer.util.DialerUtils;
+
+/** ViewHolder container for {@link CallDetailsActivity} footer. */
+public class CallDetailsFooterViewHolder extends RecyclerView.ViewHolder
+ implements OnClickListener {
+
+ private final View copy;
+ private final View edit;
+
+ private String number;
+
+ public CallDetailsFooterViewHolder(View view) {
+ super(view);
+ copy = view.findViewById(R.id.call_detail_action_copy);
+ edit = view.findViewById(R.id.call_detail_action_edit_before_call);
+
+ copy.setOnClickListener(this);
+ edit.setOnClickListener(this);
+ }
+
+ public void setPhoneNumber(String number) {
+ this.number = number;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Context context = view.getContext();
+ if (view == copy) {
+ Logger.get(context).logImpression(DialerImpression.Type.CALL_DETAILS_COPY_NUMBER);
+ ClipboardUtils.copyText(context, null, number, true);
+ } else if (view == edit) {
+ Logger.get(context).logImpression(DialerImpression.Type.CALL_DETAILS_EDIT_BEFORE_CALL);
+ Intent dialIntent = new Intent(Intent.ACTION_DIAL, CallUtil.getCallUri(number));
+ DialerUtils.startActivityWithErrorToast(context, dialIntent);
+ } else {
+ Assert.fail("View on click not implemented: " + view);
+ }
+ }
+}
diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
new file mode 100644
index 000000000..1679c2baf
--- /dev/null
+++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java
@@ -0,0 +1,125 @@
+/*
+ * 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.calldetails;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.util.UriUtils;
+import com.android.dialer.callcomposer.nano.CallComposerContact;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.callintent.nano.CallInitiationType;
+import com.android.dialer.common.Assert;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.DialerUtils;
+
+/** ViewHolder for Header/Contact in {@link CallDetailsActivity}. */
+public class CallDetailsHeaderViewHolder extends RecyclerView.ViewHolder
+ implements OnClickListener {
+
+ private final View callBackButton;
+ private final TextView nameView;
+ private final TextView numberView;
+ private final QuickContactBadge contactPhoto;
+ private final Context context;
+
+ private CallComposerContact contact;
+
+ CallDetailsHeaderViewHolder(View container) {
+ super(container);
+ context = container.getContext();
+ callBackButton = container.findViewById(R.id.call_back_button);
+ nameView = (TextView) container.findViewById(R.id.contact_name);
+ numberView = (TextView) container.findViewById(R.id.phone_number);
+ contactPhoto = (QuickContactBadge) container.findViewById(R.id.quick_contact_photo);
+ callBackButton.setOnClickListener(this);
+ }
+
+ /**
+ * Populates the contact info fields based on the current contact information. Copied from {@link
+ * com.android.contacts.common.dialog.CallSubjectDialog}.
+ */
+ public void updateContactInfo(CallComposerContact contact) {
+ this.contact = contact;
+ setPhoto(
+ contact.photoId,
+ Uri.parse(contact.photoUri),
+ Uri.parse(contact.contactUri),
+ contact.nameOrNumber,
+ contact.isBusiness);
+
+ nameView.setText(contact.nameOrNumber);
+ if (!TextUtils.isEmpty(contact.numberLabel) && !TextUtils.isEmpty(contact.displayNumber)) {
+ numberView.setVisibility(View.VISIBLE);
+ String secondaryInfo =
+ context.getString(
+ com.android.contacts.common.R.string.call_subject_type_and_number,
+ contact.numberLabel,
+ contact.displayNumber);
+ numberView.setText(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);
+ contactPhoto.setOverlay(null);
+
+ int contactType =
+ isBusiness ? ContactPhotoManager.TYPE_BUSINESS : ContactPhotoManager.TYPE_DEFAULT;
+ String lookupKey = contactUri == null ? null : UriUtils.getLookupKeyFromUri(contactUri);
+
+ ContactPhotoManager.DefaultImageRequest request =
+ new ContactPhotoManager.DefaultImageRequest(
+ displayName, lookupKey, contactType, true /* isCircular */);
+
+ if (photoId == 0 && photoUri != null) {
+ contactPhoto.setImageDrawable(
+ context.getDrawable(R.drawable.product_logo_avatar_anonymous_color_120));
+ } else {
+ ContactPhotoManager.getInstance(context)
+ .loadThumbnail(
+ contactPhoto, photoId, false /* darkTheme */, true /* isCircular */, request);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == callBackButton) {
+ Logger.get(view.getContext()).logImpression(DialerImpression.Type.CALL_DETAILS_CALL_BACK);
+ DialerUtils.startActivityWithErrorToast(
+ view.getContext(),
+ new CallIntentBuilder(contact.number, CallInitiationType.Type.CALL_DETAILS).build());
+ } else {
+ Assert.fail("View OnClickListener not implemented: " + view);
+ }
+ }
+}
diff --git a/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java b/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java
new file mode 100644
index 000000000..aee8f3652
--- /dev/null
+++ b/java/com/android/dialer/calldetails/nano/CallDetailsEntries.java
@@ -0,0 +1,440 @@
+/*
+ * 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.calldetails.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class CallDetailsEntries
+ extends com.google.protobuf.nano.ExtendableMessageNano<CallDetailsEntries> {
+
+ /** This file is autogenerated, but javadoc required. */
+ public static final class CallDetailsEntry
+ extends com.google.protobuf.nano.ExtendableMessageNano<CallDetailsEntry> {
+
+ private static volatile CallDetailsEntry[] _emptyArray;
+ public static CallDetailsEntry[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new CallDetailsEntry[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // optional int64 call_id = 1;
+ public long callId;
+
+ // optional int32 call_type = 2;
+ public int callType;
+
+ // optional int32 features = 3;
+ public int features;
+
+ // optional int64 date = 4;
+ public long date;
+
+ // optional int64 duration = 5;
+ public long duration;
+
+ // optional int64 data_usage = 6;
+ public long dataUsage;
+
+ // repeated .com.android.dialer.enrichedcall.historyquery.proto.
+ // HistoryResult history_results = 7;
+ public com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult[] historyResults;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry)
+
+ public CallDetailsEntry() {
+ clear();
+ }
+
+ public CallDetailsEntry clear() {
+ callId = 0L;
+ callType = 0;
+ features = 0;
+ date = 0L;
+ duration = 0L;
+ dataUsage = 0L;
+ historyResults =
+ com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.emptyArray();
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof CallDetailsEntry)) {
+ return false;
+ }
+ CallDetailsEntry other = (CallDetailsEntry) o;
+ if (this.callId != other.callId) {
+ return false;
+ }
+ if (this.callType != other.callType) {
+ return false;
+ }
+ if (this.features != other.features) {
+ return false;
+ }
+ if (this.date != other.date) {
+ return false;
+ }
+ if (this.duration != other.duration) {
+ return false;
+ }
+ if (this.dataUsage != other.dataUsage) {
+ return false;
+ }
+ if (!com.google.protobuf.nano.InternalNano.equals(
+ this.historyResults, other.historyResults)) {
+ return false;
+ }
+ if (unknownFieldData == null || unknownFieldData.isEmpty()) {
+ return other.unknownFieldData == null || other.unknownFieldData.isEmpty();
+ } else {
+ return unknownFieldData.equals(other.unknownFieldData);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + getClass().getName().hashCode();
+ result = 31 * result + (int) (this.callId ^ (this.callId >>> 32));
+ result = 31 * result + this.callType;
+ result = 31 * result + this.features;
+ result = 31 * result + (int) (this.date ^ (this.date >>> 32));
+ result = 31 * result + (int) (this.duration ^ (this.duration >>> 32));
+ result = 31 * result + (int) (this.dataUsage ^ (this.dataUsage >>> 32));
+ result = 31 * result + com.google.protobuf.nano.InternalNano.hashCode(this.historyResults);
+ result =
+ 31 * result
+ + (unknownFieldData == null || unknownFieldData.isEmpty()
+ ? 0
+ : unknownFieldData.hashCode());
+ return result;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.callId != 0L) {
+ output.writeInt64(1, this.callId);
+ }
+ if (this.callType != 0) {
+ output.writeInt32(2, this.callType);
+ }
+ if (this.features != 0) {
+ output.writeInt32(3, this.features);
+ }
+ if (this.date != 0L) {
+ output.writeInt64(4, this.date);
+ }
+ if (this.duration != 0L) {
+ output.writeInt64(5, this.duration);
+ }
+ if (this.dataUsage != 0L) {
+ output.writeInt64(6, this.dataUsage);
+ }
+ if (this.historyResults != null && this.historyResults.length > 0) {
+ for (int i = 0; i < this.historyResults.length; i++) {
+ com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult element =
+ this.historyResults[i];
+ if (element != null) {
+ output.writeMessage(7, element);
+ }
+ }
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.callId != 0L) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(1, this.callId);
+ }
+ if (this.callType != 0) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(2, this.callType);
+ }
+ if (this.features != 0) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(3, this.features);
+ }
+ if (this.date != 0L) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(4, this.date);
+ }
+ if (this.duration != 0L) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(5, this.duration);
+ }
+ if (this.dataUsage != 0L) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(6, this.dataUsage);
+ }
+ if (this.historyResults != null && this.historyResults.length > 0) {
+ for (int i = 0; i < this.historyResults.length; i++) {
+ com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult element =
+ this.historyResults[i];
+ if (element != null) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeMessageSize(7, element);
+ }
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public CallDetailsEntry 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.callId = input.readInt64();
+ break;
+ }
+ case 16:
+ {
+ this.callType = input.readInt32();
+ break;
+ }
+ case 24:
+ {
+ this.features = input.readInt32();
+ break;
+ }
+ case 32:
+ {
+ this.date = input.readInt64();
+ break;
+ }
+ case 40:
+ {
+ this.duration = input.readInt64();
+ break;
+ }
+ case 48:
+ {
+ this.dataUsage = input.readInt64();
+ break;
+ }
+ case 58:
+ {
+ int arrayLength =
+ com.google.protobuf.nano.WireFormatNano.getRepeatedFieldArrayLength(input, 58);
+ int i = this.historyResults == null ? 0 : this.historyResults.length;
+ com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult[] newArray =
+ new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult
+ [i + arrayLength];
+ if (i != 0) {
+ java.lang.System.arraycopy(this.historyResults, 0, newArray, 0, i);
+ }
+ for (; i < newArray.length - 1; i++) {
+ newArray[i] =
+ new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult();
+ input.readMessage(newArray[i]);
+ input.readTag();
+ }
+ // Last one without readTag.
+ newArray[i] =
+ new com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult();
+ input.readMessage(newArray[i]);
+ this.historyResults = newArray;
+ break;
+ }
+ }
+ }
+ }
+
+ public static CallDetailsEntry parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new CallDetailsEntry(), data);
+ }
+
+ public static CallDetailsEntry parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException {
+ return new CallDetailsEntry().mergeFrom(input);
+ }
+ }
+
+ private static volatile CallDetailsEntries[] _emptyArray;
+ public static CallDetailsEntries[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new CallDetailsEntries[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // repeated .com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry entries = 1;
+ public com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry[] entries;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.calldetails.CallDetailsEntries)
+
+ public CallDetailsEntries() {
+ clear();
+ }
+
+ public CallDetailsEntries clear() {
+ entries = com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry.emptyArray();
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof CallDetailsEntries)) {
+ return false;
+ }
+ CallDetailsEntries other = (CallDetailsEntries) o;
+ if (!com.google.protobuf.nano.InternalNano.equals(this.entries, other.entries)) {
+ return false;
+ }
+ if (unknownFieldData == null || unknownFieldData.isEmpty()) {
+ return other.unknownFieldData == null || other.unknownFieldData.isEmpty();
+ } else {
+ return unknownFieldData.equals(other.unknownFieldData);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + getClass().getName().hashCode();
+ result = 31 * result + com.google.protobuf.nano.InternalNano.hashCode(this.entries);
+ result =
+ 31 * result
+ + (unknownFieldData == null || unknownFieldData.isEmpty()
+ ? 0
+ : unknownFieldData.hashCode());
+ return result;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.entries != null && this.entries.length > 0) {
+ for (int i = 0; i < this.entries.length; i++) {
+ com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry element =
+ this.entries[i];
+ if (element != null) {
+ output.writeMessage(1, element);
+ }
+ }
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.entries != null && this.entries.length > 0) {
+ for (int i = 0; i < this.entries.length; i++) {
+ com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry element =
+ this.entries[i];
+ if (element != null) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeMessageSize(1, element);
+ }
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public CallDetailsEntries 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:
+ {
+ int arrayLength =
+ com.google.protobuf.nano.WireFormatNano.getRepeatedFieldArrayLength(input, 10);
+ int i = this.entries == null ? 0 : this.entries.length;
+ com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry[] newArray =
+ new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry
+ [i + arrayLength];
+ if (i != 0) {
+ java.lang.System.arraycopy(this.entries, 0, newArray, 0, i);
+ }
+ for (; i < newArray.length - 1; i++) {
+ newArray[i] =
+ new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry();
+ input.readMessage(newArray[i]);
+ input.readTag();
+ }
+ // Last one without readTag.
+ newArray[i] =
+ new com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry();
+ input.readMessage(newArray[i]);
+ this.entries = newArray;
+ break;
+ }
+ }
+ }
+ }
+
+ public static CallDetailsEntries parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new CallDetailsEntries(), data);
+ }
+
+ public static CallDetailsEntries parseFrom(
+ com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException {
+ return new CallDetailsEntries().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml b/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml
new file mode 100644
index 000000000..421bdbfee
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/drawable/multimedia_image_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="2dp"/>
+</shape>
diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml b/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml
new file mode 100644
index 000000000..038a8745e
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/layout/call_details_activity.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/dialer_theme_color"
+ android:elevation="4dp"
+ android:titleTextAppearance="@style/toolbar_title_text"
+ android:title="@string/call_details"
+ android:navigationIcon="@drawable/quantum_ic_arrow_back_white_24"/>
+
+ <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_white"/>
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml
new file mode 100644
index 000000000..7f8bb8087
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/layout/call_details_entry.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/call_entry_padding">
+
+ <ImageView
+ android:id="@+id/call_direction"
+ android:layout_width="@dimen/call_entry_icon_size"
+ android:layout_height="@dimen/call_entry_icon_size"
+ android:layout_marginStart="@dimen/call_entry_padding"
+ android:layout_marginEnd="@dimen/call_entry_left_margin"/>
+
+ <TextView
+ android:id="@+id/call_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/call_direction"
+ style="@style/PrimaryText"/>
+
+ <TextView
+ android:id="@+id/call_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@id/call_direction"
+ android:layout_below="@+id/call_type"
+ android:layout_marginBottom="@dimen/call_entry_bottom_padding"
+ style="@style/SecondaryText"/>
+
+ <TextView
+ android:id="@+id/call_duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="@dimen/call_entry_padding"
+ style="@style/PrimaryText"/>
+
+ <include
+ layout="@layout/ec_data_container"
+ android:id="@+id/ec_container"
+ android:layout_height="@dimen/ec_container_height"
+ android:layout_width="match_parent"
+ android:layout_marginStart="@dimen/ec_text_left_margin"
+ android:layout_below="@+id/call_time"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_below="@id/ec_container"
+ android:layout_marginTop="@dimen/ec_divider_top_bottom_margin"
+ android:layout_marginBottom="@dimen/ec_divider_top_bottom_margin"
+ android:layout_marginStart="@dimen/ec_text_left_margin"
+ android:background="#12000000"
+ android:visibility="gone"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/layout/call_details_footer.xml b/java/com/android/dialer/calldetails/res/layout/call_details_footer.xml
new file mode 100644
index 000000000..885cb0989
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/layout/call_details_footer.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.
+-->
+<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="1dp"
+ android:layout_marginTop="@dimen/ec_divider_top_bottom_margin"
+ android:layout_marginBottom="@dimen/ec_divider_top_bottom_margin"
+ android:background="#12000000"/>
+
+ <TextView
+ android:id="@+id/call_detail_action_copy"
+ style="@style/CallDetailsActionItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/quantum_ic_content_copy_grey600_24"
+ android:text="@string/call_details_copy_number"/>
+
+ <TextView
+ android:id="@+id/call_detail_action_edit_before_call"
+ style="@style/CallDetailsActionItemStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/quantum_ic_edit_grey600_24"
+ android:text="@string/call_details_edit_number"/>
+</LinearLayout>
diff --git a/java/com/android/dialer/calldetails/res/layout/contact_container.xml b/java/com/android/dialer/calldetails/res/layout/contact_container.xml
new file mode 100644
index 000000000..95fe189b2
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/layout/contact_container.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:padding="@dimen/contact_container_padding">
+
+ <QuickContactBadge
+ android:id="@+id/quick_contact_photo"
+ android:layout_width="@dimen/call_details_contact_photo_size"
+ android:layout_height="@dimen/call_details_contact_photo_size"
+ android:focusable="true"/>
+
+ <TextView
+ android:id="@+id/contact_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/text_bottom_margin"
+ android:layout_marginStart="@dimen/photo_text_margin"
+ android:layout_toEndOf="@+id/quick_contact_photo"
+ android:layout_toStartOf="@+id/call_back_button"
+ style="@style/PrimaryText"/>
+
+ <TextView
+ android:id="@+id/phone_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/photo_text_margin"
+ android:layout_toEndOf="@+id/quick_contact_photo"
+ android:layout_toStartOf="@+id/call_back_button"
+ android:layout_below="@+id/contact_name"
+ style="@style/SecondaryText"/>
+
+ <ImageView
+ android:id="@+id/call_back_button"
+ android:layout_width="@dimen/call_back_button_size"
+ android:layout_height="@dimen/call_back_button_size"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/description_call_log_call_action"
+ android:src="@drawable/quantum_ic_call_white_24"
+ android:tint="@color/secondary_text_color"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml b/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml
new file mode 100644
index 000000000..5ad7912fa
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/layout/ec_data_container.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/ec_container_height">
+
+ <TextView
+ android:id="@+id/multimedia_details"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:maxLines="2"
+ style="@style/SecondaryText"/>
+
+ <FrameLayout
+ android:id="@+id/multimedia_image_container"
+ android:layout_width="@dimen/ec_photo_size"
+ android:layout_height="@dimen/ec_photo_size"
+ android:layout_alignParentEnd="true"
+ android:layout_marginEnd="@dimen/call_entry_padding"
+ android:layout_centerVertical="true"
+ android:background="@drawable/multimedia_image_background"
+ android:outlineProvider="background"
+ android:visibility="gone">
+
+ <ImageView
+ android:id="@+id/multimedia_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"/>
+
+ <com.android.incallui.autoresizetext.AutoResizeTextView
+ android:id="@+id/multimedia_attachments_number"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="4dp"
+ android:gravity="center"
+ android:textColor="@color/background_dialer_white"
+ android:textSize="100sp"
+ android:background="#80000000"
+ android:visibility="gone"/>
+ </FrameLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml b/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml
new file mode 100644
index 000000000..c2d1032da
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/menu/call_details_menu.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/call_detail_delete_menu_item"
+ android:icon="@drawable/quantum_ic_delete_white_24"
+ android:title="@string/delete"
+ android:showAsAction="ifRoom"/>
+</menu> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/values/dimens.xml b/java/com/android/dialer/calldetails/res/values/dimens.xml
new file mode 100644
index 000000000..b1a8f1c8e
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/values/dimens.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <dimen name="text_bottom_margin">2dp</dimen>
+ <dimen name="call_details_primary_text_size">16sp</dimen>
+ <dimen name="call_details_secondary_text_size">14sp</dimen>
+
+ <!-- contact container -->
+ <dimen name="contact_container_padding">16dp</dimen>
+ <dimen name="call_details_contact_photo_size">40dp</dimen>
+ <dimen name="photo_text_margin">16dp</dimen>
+ <dimen name="call_back_button_size">24dp</dimen>
+
+ <!-- call entry container -->
+ <dimen name="call_entry_icon_size">24dp</dimen>
+ <dimen name="call_entry_padding">16dp</dimen>
+ <dimen name="call_entry_bottom_padding">14dp</dimen>
+ <dimen name="call_entry_left_margin">32dp</dimen>
+
+ <!-- EC container -->
+ <dimen name="call_details_ec_text_size">12sp</dimen>
+ <dimen name="ec_container_height">48dp</dimen>
+ <dimen name="ec_text_left_margin">72dp</dimen>
+ <dimen name="ec_photo_size">40dp</dimen>
+ <dimen name="ec_divider_top_bottom_margin">8dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/calldetails/res/values/strings.xml b/java/com/android/dialer/calldetails/res/values/strings.xml
new file mode 100644
index 000000000..8a7cc4cfc
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title bar for call detail screen -->
+ <string name="call_details">Call details</string>
+
+ <!-- Menu item in call details used to remove a call or voicemail from the call log. -->
+ <string name="delete">Delete</string>
+
+ <!-- Option displayed in context menu to copy long pressed phone number. [CHAR LIMIT=48] -->
+ <string name="call_details_copy_number">Copy number</string>
+
+ <!-- Label for action to edit a number before calling it. [CHAR LIMIT=48] -->
+ <string name="call_details_edit_number">Edit number before call</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 shown when the call details show a image that was sent -->
+ <string name="sent_a_photo">Sent a photo</string>
+
+ <!-- String shown when the call details show a image that was received -->
+ <string name="received_a_photo">Received a photo</string>
+
+ <!-- Messages shown to the user are wrapped in quotes, e.g. the user would see "Some text" -->
+ <string name="message_in_quotes">\"<xliff:g id="message">%1$s</xliff:g>\"</string>
+</resources>
diff --git a/java/com/android/dialer/calldetails/res/values/styles.xml b/java/com/android/dialer/calldetails/res/values/styles.xml
new file mode 100644
index 000000000..4fffe1afb
--- /dev/null
+++ b/java/com/android/dialer/calldetails/res/values/styles.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <style name="PrimaryText">
+ <item name="android:textColor">#DE000000</item>
+ <item name="android:textSize">@dimen/call_details_primary_text_size</item>
+ <item name="android:maxLines">1</item>
+ </style>
+
+ <style name="SecondaryText">
+ <item name="android:textColor">#8A000000</item>
+ <item name="android:textSize">@dimen/call_details_secondary_text_size</item>
+ <item name="android:maxLines">1</item>
+ </style>
+
+ <style name="ECText">
+ <item name="android:textColor">#8A000000</item>
+ <item name="android:textSize">@dimen/call_details_ec_text_size</item>
+ <item name="android:maxLines">1</item>
+ </style>
+
+ <style name="CallDetailsActionItemStyle">
+ <item name="android:foreground">?android:attr/selectableItemBackground</item>
+ <item name="android:clickable">true</item>
+ <item name="android:drawablePadding">28dp</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingStart">28dp</item>
+ <item name="android:paddingEnd">28dp</item>
+ <item name="android:paddingTop">16dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ <item name="android:textColor">#8A000000</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/callintent/nano/CallInitiationType.java b/java/com/android/dialer/callintent/nano/CallInitiationType.java
index 4badd6e57..1dddb6ce8 100644
--- a/java/com/android/dialer/callintent/nano/CallInitiationType.java
+++ b/java/com/android/dialer/callintent/nano/CallInitiationType.java
@@ -11,17 +11,19 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * 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 CallInitiationType extends
- com.google.protobuf.nano.ExtendableMessageNano<CallInitiationType> {
+public final class CallInitiationType
+ extends com.google.protobuf.nano.ExtendableMessageNano<CallInitiationType> {
+ /** This file is autogenerated, but javadoc required. */
// enum Type
public interface Type {
public static final int UNKNOWN_INITIATION = 0;
@@ -44,11 +46,11 @@ public final class CallInitiationType extends
}
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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new CallInitiationType[0];
}
@@ -70,20 +72,20 @@ public final class CallInitiationType extends
}
@Override
- public CallInitiationType mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
}
- break;
- }
}
}
}
@@ -94,8 +96,7 @@ public final class CallInitiationType extends
}
public static CallInitiationType parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
- throws java.io.IOException {
+ com.google.protobuf.nano.CodedInputByteBufferNano input) throws java.io.IOException {
return new CallInitiationType().mergeFrom(input);
}
}
diff --git a/java/com/android/dialer/calllogutils/AndroidManifest.xml b/java/com/android/dialer/calllogutils/AndroidManifest.xml
new file mode 100644
index 000000000..228865a38
--- /dev/null
+++ b/java/com/android/dialer/calllogutils/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<!--
+ ~ 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
+ -->
+<manifest package="com.android.dialer.calllogutils"/> \ No newline at end of file
diff --git a/java/com/android/dialer/calllogutils/CallEntryFormatter.java b/java/com/android/dialer/calllogutils/CallEntryFormatter.java
new file mode 100644
index 000000000..bd6d53f48
--- /dev/null
+++ b/java/com/android/dialer/calllogutils/CallEntryFormatter.java
@@ -0,0 +1,113 @@
+/*
+ * 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.calllogutils;
+
+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.text.format.DateUtils;
+import android.text.format.Formatter;
+import com.android.dialer.util.DialerUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/** Utility class for formatting data and data usage in call log entries. */
+public class CallEntryFormatter {
+
+ /**
+ * 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.
+ */
+ public static CharSequence formatDate(Context context, long callDateMillis) {
+ CharSequence dateValue =
+ DateUtils.formatDateRange(
+ context,
+ 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 static CharSequence formatDuration(Context context, long elapsedSeconds) {
+ long minutes = 0;
+ long seconds = 0;
+
+ if (elapsedSeconds >= 60) {
+ minutes = elapsedSeconds / 60;
+ elapsedSeconds -= minutes * 60;
+ seconds = elapsedSeconds;
+ return context.getString(R.string.call_details_duration_format, minutes, seconds);
+ } else {
+ seconds = elapsedSeconds;
+ return context.getString(R.string.call_details_short_duration_format, 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.
+ */
+ public static CharSequence formatDurationAndDataUsage(
+ Context context, long elapsedSeconds, Long dataUsage) {
+ CharSequence duration = formatDuration(context, elapsedSeconds);
+ List<CharSequence> durationItems = new ArrayList<>();
+ if (dataUsage != null) {
+ durationItems.add(duration);
+ durationItems.add(Formatter.formatShortFileSize(context, dataUsage));
+ return DialerUtils.join(durationItems);
+ } else {
+ return duration;
+ }
+ }
+}
diff --git a/java/com/android/dialer/app/calllog/CallTypeHelper.java b/java/com/android/dialer/calllogutils/CallTypeHelper.java
index f3c27a1ac..d3b5b67d7 100644
--- a/java/com/android/dialer/app/calllog/CallTypeHelper.java
+++ b/java/com/android/dialer/calllogutils/CallTypeHelper.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.dialer.app.calllog;
+package com.android.dialer.calllogutils;
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. */
diff --git a/java/com/android/dialer/app/calllog/CallTypeIconsView.java b/java/com/android/dialer/calllogutils/CallTypeIconsView.java
index cd5c5460c..61208bc9a 100644
--- a/java/com/android/dialer/app/calllog/CallTypeIconsView.java
+++ b/java/com/android/dialer/calllogutils/CallTypeIconsView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.dialer.app.calllog;
+package com.android.dialer.calllogutils;
import android.content.Context;
import android.graphics.Bitmap;
@@ -26,7 +26,6 @@ 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;
@@ -41,6 +40,7 @@ public class CallTypeIconsView extends View {
private static Resources sResources;
private List<Integer> mCallTypes = new ArrayList<>(3);
private boolean mShowVideo = false;
+ private boolean mShowHd = false;
private int mWidth;
private int mHeight;
@@ -94,6 +94,15 @@ public class CallTypeIconsView extends View {
return mShowVideo;
}
+ public void setShowHd(boolean showHd) {
+ mShowHd = showHd;
+ if (showHd) {
+ mWidth += sResources.hdCall.getIntrinsicWidth();
+ mHeight = Math.max(mHeight, sResources.hdCall.getIntrinsicHeight());
+ invalidate();
+ }
+ }
+
public int getCount() {
return mCallTypes.size();
}
@@ -147,6 +156,13 @@ public class CallTypeIconsView extends View {
drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight());
drawable.draw(canvas);
}
+ // If showing HD call icon, draw it scaled appropriately.
+ if (mShowHd) {
+ final Drawable drawable = sResources.hdCall;
+ final int right = left + sResources.hdCall.getIntrinsicWidth();
+ drawable.setBounds(left, 0, right, sResources.hdCall.getIntrinsicHeight());
+ drawable.draw(canvas);
+ }
}
private static class Resources {
@@ -166,9 +182,12 @@ public class CallTypeIconsView extends View {
// Drawable representing a blocked call.
public final Drawable blocked;
- // Drawable repesenting a video call.
+ // Drawable repesenting a video call.
public final Drawable videoCall;
+ // Drawable represeting a hd call.
+ public final Drawable hdCall;
+
/** The margin to use for icons. */
public final int iconMargin;
@@ -204,6 +223,10 @@ public class CallTypeIconsView extends View {
videoCall.setColorFilter(
r.getColor(R.color.dialer_secondary_text_color), PorterDuff.Mode.MULTIPLY);
+ hdCall = getScaledBitmap(context, R.drawable.quantum_ic_hd_white_24);
+ hdCall.setColorFilter(
+ r.getColor(R.color.dialer_secondary_text_color), PorterDuff.Mode.MULTIPLY);
+
iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
}
diff --git a/java/com/android/dialer/app/calllog/PhoneAccountUtils.java b/java/com/android/dialer/calllogutils/PhoneAccountUtils.java
index c6d94d341..c639893ef 100644
--- a/java/com/android/dialer/app/calllog/PhoneAccountUtils.java
+++ b/java/com/android/dialer/calllogutils/PhoneAccountUtils.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.app.calllog;
+package com.android.dialer.calllogutils;
import android.content.ComponentName;
import android.content.Context;
diff --git a/java/com/android/dialer/app/PhoneCallDetails.java b/java/com/android/dialer/calllogutils/PhoneCallDetails.java
index 436f68eec..ba05a87e2 100644
--- a/java/com/android/dialer/app/PhoneCallDetails.java
+++ b/java/com/android/dialer/calllogutils/PhoneCallDetails.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.dialer.app;
+package com.android.dialer.calllogutils;
import android.content.Context;
import android.content.res.Resources;
@@ -27,7 +27,6 @@ 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. */
diff --git a/java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java b/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java
index 410d4cc37..9bebfacac 100644
--- a/java/com/android/dialer/app/calllog/PhoneNumberDisplayUtil.java
+++ b/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.dialer.app.calllog;
+package com.android.dialer.calllogutils;
import android.content.Context;
import android.provider.CallLog.Calls;
@@ -22,15 +22,13 @@ 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(
+ public static CharSequence getDisplayName(
Context context, CharSequence number, int presentation, boolean isVoicemail) {
if (presentation == Calls.PRESENTATION_UNKNOWN) {
return context.getResources().getString(R.string.unknown);
@@ -42,7 +40,7 @@ public class PhoneNumberDisplayUtil {
return context.getResources().getString(R.string.payphone);
}
if (isVoicemail) {
- return context.getResources().getString(R.string.voicemail);
+ return context.getResources().getString(R.string.voicemail_string);
}
if (PhoneNumberHelper.isLegacyUnknownNumbers(number)) {
return context.getResources().getString(R.string.unknown);
diff --git a/java/com/android/dialer/calllogutils/res/values/colors.xml b/java/com/android/dialer/calllogutils/res/values/colors.xml
new file mode 100644
index 000000000..dc4ec2493
--- /dev/null
+++ b/java/com/android/dialer/calllogutils/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <!-- Color for missed call icons. -->
+ <color name="missed_call">#ff2e58</color>
+ <!-- Color for answered or outgoing call icons. -->
+ <color name="answered_call">#00c853</color>
+ <!-- Color for blocked call icons. -->
+ <color name="blocked_call">@color/dialer_secondary_text_color</color>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/calllogutils/res/values/dimens.xml b/java/com/android/dialer/calllogutils/res/values/dimens.xml
new file mode 100644
index 000000000..0935ac188
--- /dev/null
+++ b/java/com/android/dialer/calllogutils/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <dimen name="call_type_icon_size">12dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/calllogutils/res/values/strings.xml b/java/com/android/dialer/calllogutils/res/values/strings.xml
new file mode 100644
index 000000000..6a6f10113
--- /dev/null
+++ b/java/com/android/dialer/calllogutils/res/values/strings.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- 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>
+
+ <!-- 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>
+
+ <!-- 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>
+
+ <!-- String used for displaying calls to the voicemail number in the call log -->
+ <string name="voicemail_string">Voicemail</string>
+
+ <!-- A nicely formatted call duration displayed when viewing call details. For example "42 min 28 sec" -->
+ <string name="call_details_duration_format"><xliff:g example="42" id="minutes">%s</xliff:g> min <xliff:g example="28" id="seconds">%s</xliff:g> sec</string>
+
+ <!-- A nicely formatted call duration displayed when viewing call details for duration less than 1 minute. For example "28 sec" -->
+ <string name="call_details_short_duration_format"><xliff:g example="28" id="seconds">%s</xliff:g> sec</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/common/Assert.java b/java/com/android/dialer/common/Assert.java
index 00b4f2595..943e1ddcf 100644
--- a/java/com/android/dialer/common/Assert.java
+++ b/java/com/android/dialer/common/Assert.java
@@ -19,6 +19,7 @@ package com.android.dialer.common;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import javax.annotation.CheckReturnValue;
/** Assertions which will result in program termination unless disabled by flags. */
public class Assert {
@@ -33,7 +34,9 @@ public class Assert {
* Called when a truly exceptional case occurs.
*
* @throws AssertionError
+ * @deprecated Use throw Assert.create*FailException() instead.
*/
+ @Deprecated
public static void fail() {
throw new AssertionError("Fail");
}
@@ -43,11 +46,38 @@ public class Assert {
*
* @param reason the optional reason to supply as the exception message
* @throws AssertionError
+ * @deprecated Use throw Assert.create*FailException() instead.
*/
+ @Deprecated
public static void fail(String reason) {
throw new AssertionError(reason);
}
+ @CheckReturnValue
+ public static AssertionError createAssertionFailException(String msg) {
+ return new AssertionError(msg);
+ }
+
+ @CheckReturnValue
+ public static UnsupportedOperationException createUnsupportedOperationFailException() {
+ return new UnsupportedOperationException();
+ }
+
+ @CheckReturnValue
+ public static UnsupportedOperationException createUnsupportedOperationFailException(String msg) {
+ return new UnsupportedOperationException(msg);
+ }
+
+ @CheckReturnValue
+ public static IllegalStateException createIllegalStateFailException() {
+ return new IllegalStateException();
+ }
+
+ @CheckReturnValue
+ public static IllegalStateException createIllegalStateFailException(String msg) {
+ return new IllegalStateException(msg);
+ }
+
/**
* Ensures the truth of an expression involving one or more parameters to the calling method.
*
diff --git a/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java b/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.java
deleted file mode 100644
index f9d7cea90..000000000
--- a/java/com/android/dialer/common/AutoValue_FallibleAsyncTask_FallibleTaskResult.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.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/FallibleAsyncTask.java b/java/com/android/dialer/common/FallibleAsyncTask.java
index fbdbda75f..f3abace1a 100644
--- a/java/com/android/dialer/common/FallibleAsyncTask.java
+++ b/java/com/android/dialer/common/FallibleAsyncTask.java
@@ -20,7 +20,7 @@ import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.dialer.common.FallibleAsyncTask.FallibleTaskResult;
-
+import com.google.auto.value.AutoValue;
/**
* A task that runs work in the background, passing Throwables from {@link
@@ -52,8 +52,8 @@ public abstract class FallibleAsyncTask<ParamsT, ProgressT, ResultT>
*
* @param <ResultT> the type of the result of the background computation
*/
-
- protected abstract static class FallibleTaskResult<ResultT> {
+ @AutoValue
+ public abstract static class FallibleTaskResult<ResultT> {
/** Creates an instance of FallibleTaskResult for the given throwable. */
private static <ResultT> FallibleTaskResult<ResultT> createFailureResult(@NonNull Throwable t) {
diff --git a/java/com/android/dialer/common/PerAccountSharedPreferences.java b/java/com/android/dialer/common/PerAccountSharedPreferences.java
new file mode 100644
index 000000000..0ed1b03a5
--- /dev/null
+++ b/java/com/android/dialer/common/PerAccountSharedPreferences.java
@@ -0,0 +1,146 @@
+/*
+ * 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.content.Context;
+import android.content.SharedPreferences;
+import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import java.util.Set;
+
+/**
+ * Class that helps us store dialer preferences that are phone account dependent. This is necessary
+ * for cases such as settings that are phone account dependent e.g endless vm. The logic is to
+ * essentially store the shared preference by appending the phone account id to the key.
+ */
+public class PerAccountSharedPreferences {
+ private final String sharedPrefsKeyPrefix;
+ private final SharedPreferences preferences;
+ private final PhoneAccountHandle phoneAccountHandle;
+
+ public PerAccountSharedPreferences(
+ Context context, PhoneAccountHandle handle, SharedPreferences prefs) {
+ preferences = prefs;
+ phoneAccountHandle = handle;
+ sharedPrefsKeyPrefix = "phone_account_dependent_";
+ }
+
+ /**
+ * Not to be used, currently only used by {@VisualVoicemailPreferences} for legacy reasons.
+ */
+ protected PerAccountSharedPreferences(
+ Context context, PhoneAccountHandle handle, SharedPreferences prefs, String prefix) {
+ Assert.checkArgument(prefix.equals("visual_voicemail_"));
+ preferences = prefs;
+ phoneAccountHandle = handle;
+ sharedPrefsKeyPrefix = prefix;
+ }
+
+ public class Editor {
+
+ private final SharedPreferences.Editor editor;
+
+ private Editor() {
+ editor = preferences.edit();
+ }
+
+ public void apply() {
+ editor.apply();
+ }
+
+ public Editor putBoolean(String key, boolean value) {
+ editor.putBoolean(getKey(key), value);
+ return this;
+ }
+
+ public Editor putFloat(String key, float value) {
+ editor.putFloat(getKey(key), value);
+ return this;
+ }
+
+ public Editor putInt(String key, int value) {
+ editor.putInt(getKey(key), value);
+ return this;
+ }
+
+ public Editor putLong(String key, long value) {
+ editor.putLong(getKey(key), value);
+ return this;
+ }
+
+ public Editor putString(String key, String value) {
+ editor.putString(getKey(key), value);
+ return this;
+ }
+
+ public Editor putStringSet(String key, Set<String> value) {
+ editor.putStringSet(getKey(key), value);
+ return this;
+ }
+ }
+
+ public Editor edit() {
+ return new Editor();
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ return getValue(key, defValue);
+ }
+
+ public float getFloat(String key, float defValue) {
+ return getValue(key, defValue);
+ }
+
+ public int getInt(String key, int defValue) {
+ return getValue(key, defValue);
+ }
+
+ 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);
+ }
+
+ public Set<String> getStringSet(String key, Set<String> defValue) {
+ return getValue(key, defValue);
+ }
+
+ public boolean contains(String key) {
+ return preferences.contains(getKey(key));
+ }
+
+ private <T> T getValue(String key, T defValue) {
+ if (!contains(key)) {
+ return defValue;
+ }
+ Object object = preferences.getAll().get(getKey(key));
+ if (object == null) {
+ return defValue;
+ }
+ return (T) object;
+ }
+
+ private String getKey(String key) {
+ return sharedPrefsKeyPrefix + key + "_" + phoneAccountHandle.getId();
+ }
+}
diff --git a/java/com/android/dialer/common/proguard.flags b/java/com/android/dialer/common/proguard.flags
new file mode 100644
index 000000000..4b6b84671
--- /dev/null
+++ b/java/com/android/dialer/common/proguard.flags
@@ -0,0 +1,4 @@
+-assumenosideeffects class com.android.dialer.common.LogUtil {
+ public static void v(...);
+ public static void d(...);
+}
diff --git a/java/com/android/dialer/common/res/values/config.xml b/java/com/android/dialer/common/res/values/config.xml
new file mode 100644
index 000000000..c4df279ba
--- /dev/null
+++ b/java/com/android/dialer/common/res/values/config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <bool name="spring_hd_codec">false</bool>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/constants/Constants.java b/java/com/android/dialer/constants/Constants.java
index 77773018a..d92c0bcfc 100644
--- a/java/com/android/dialer/constants/Constants.java
+++ b/java/com/android/dialer/constants/Constants.java
@@ -19,7 +19,6 @@ 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,
@@ -29,11 +28,22 @@ import com.android.dialer.constants.ConstantsImpl;
*/
@UsedByReflection(value = "Constants.java")
public abstract class Constants {
- private static Constants instance = new ConstantsImpl();
+ private static Constants instance;
private static boolean didInitializeInstance;
@NonNull
public static synchronized Constants get() {
+ if (!didInitializeInstance) {
+ didInitializeInstance = true;
+ try {
+ Class<?> clazz = Class.forName(Constants.class.getName() + "Impl");
+ instance = (Constants) clazz.getConstructor().newInstance();
+ } catch (ReflectiveOperationException e) {
+ Assert.fail(
+ "Unable to create an instance of ConstantsImpl. To fix this error include one of the "
+ + "constants modules (googledialer, aosp etc...) in your target.");
+ }
+ }
return instance;
}
diff --git a/java/com/android/dialer/database/CallLogQueryHandler.java b/java/com/android/dialer/database/CallLogQueryHandler.java
index ffca69f40..1f6bd5fb3 100644
--- a/java/com/android/dialer/database/CallLogQueryHandler.java
+++ b/java/com/android/dialer/database/CallLogQueryHandler.java
@@ -33,6 +33,7 @@ import android.os.Message;
import android.provider.CallLog.Calls;
import android.provider.VoicemailContract.Status;
import android.provider.VoicemailContract.Voicemails;
+import android.support.v4.os.BuildCompat;
import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
import com.android.dialer.common.LogUtil;
import com.android.dialer.compat.AppCompatConstants;
@@ -40,6 +41,7 @@ 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 com.android.voicemail.VoicemailComponent;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -126,13 +128,23 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
public void fetchVoicemailUnreadCount() {
if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
// Only count voicemails that have not been read and have not been deleted.
+ StringBuilder where =
+ new StringBuilder(Voicemails.IS_READ + "=0" + " AND " + Voicemails.DELETED + "=0 ");
+ List<String> selectionArgs = new ArrayList<>();
+
+ if (BuildCompat.isAtLeastO()) {
+ VoicemailComponent.get(mContext)
+ .getVoicemailClient()
+ .appendOmtpVoicemailSelectionClause(mContext, where, selectionArgs);
+ }
+
startQuery(
QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN,
null,
Voicemails.CONTENT_URI,
new String[] {Voicemails._ID},
- Voicemails.IS_READ + "=0" + " AND " + Voicemails.DELETED + "=0",
- null,
+ where.toString(),
+ selectionArgs.toArray(new String[selectionArgs.size()]),
null);
}
}
@@ -168,6 +180,12 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
selectionArgs.add(Long.toString(newerThan));
}
+ if (callType == Calls.VOICEMAIL_TYPE) {
+ VoicemailComponent.get(mContext)
+ .getVoicemailClient()
+ .appendOmtpVoicemailSelectionClause(mContext, where, selectionArgs);
+ }
+
final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
final String selection = where.length() > 0 ? where.toString() : null;
Uri uri =
diff --git a/java/com/android/dialer/debug/bindings/stub/DebugBindings.java b/java/com/android/dialer/debug/bindings/stub/DebugBindings.java
new file mode 100644
index 000000000..7df38341d
--- /dev/null
+++ b/java/com/android/dialer/debug/bindings/stub/DebugBindings.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.debug.bindings;
+
+import android.content.Context;
+
+/** Hooks into the debug module. */
+public class DebugBindings {
+
+ public static void registerConnectionService(Context context) {}
+
+ public static void addNewIncomingCall(Context context, String phoneNumber) {}
+}
diff --git a/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java b/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java
deleted file mode 100644
index 14299f92c..000000000
--- a/java/com/android/dialer/enrichedcall/AutoValue_EnrichedCallCapabilities.java
+++ /dev/null
@@ -1,76 +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.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
deleted file mode 100644
index edfefc479..000000000
--- a/java/com/android/dialer/enrichedcall/AutoValue_OutgoingCallComposerData.java
+++ /dev/null
@@ -1,127 +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.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
index b7d780950..c3c78c9c8 100644
--- a/java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallCapabilities.java
@@ -16,21 +16,24 @@
package com.android.dialer.enrichedcall;
-
+import com.google.auto.value.AutoValue;
/** Value type holding enriched call capabilities. */
-
+@AutoValue
public abstract class EnrichedCallCapabilities {
public static final EnrichedCallCapabilities NO_CAPABILITIES =
- EnrichedCallCapabilities.create(false, false);
+ EnrichedCallCapabilities.create(false, false, false);
public static EnrichedCallCapabilities create(
- boolean supportsCallComposer, boolean supportsPostCall) {
- return new AutoValue_EnrichedCallCapabilities(supportsCallComposer, supportsPostCall);
+ boolean supportsCallComposer, boolean supportsPostCall, boolean supportsVideoCall) {
+ return new AutoValue_EnrichedCallCapabilities(
+ supportsCallComposer, supportsPostCall, supportsVideoCall);
}
public abstract boolean supportsCallComposer();
public abstract boolean supportsPostCall();
+
+ public abstract boolean supportsVideoShare();
}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java b/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java
new file mode 100644
index 000000000..5291e292f
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallComponent.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.content.Context;
+import android.support.annotation.NonNull;
+import dagger.Subcomponent;
+import com.android.dialer.enrichedcall.stub.EnrichedCallManagerStub;
+
+/** Subcomponent that can be used to access the enriched call implementation. */
+public class EnrichedCallComponent {
+ private static EnrichedCallComponent instance;
+ private EnrichedCallManager enrichedCallManager;
+
+ @NonNull
+ public EnrichedCallManager getEnrichedCallManager() {
+ if (enrichedCallManager == null) {
+ enrichedCallManager = new EnrichedCallManagerStub();
+ }
+ return enrichedCallManager;
+ }
+
+ public static EnrichedCallComponent get(Context context) {
+ if (instance == null) {
+ instance = new EnrichedCallComponent();
+ }
+ return instance;
+ }
+
+ /** Used to refer to the root application component. */
+ public interface HasComponent {
+ EnrichedCallComponent enrichedCallComponent();
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallManager.java b/java/com/android/dialer/enrichedcall/EnrichedCallManager.java
index 6af8c409a..a36b2cc0d 100644
--- a/java/com/android/dialer/enrichedcall/EnrichedCallManager.java
+++ b/java/com/android/dialer/enrichedcall/EnrichedCallManager.java
@@ -16,38 +16,25 @@
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 android.support.annotation.WorkerThread;
+import com.android.dialer.calldetails.nano.CallDetailsEntries;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult;
+import com.android.dialer.enrichedcall.videoshare.VideoShareListener;
import com.android.dialer.multimedia.MultimediaData;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Map;
/** 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();
- }
- }
+ int POST_CALL_NOTE_MAX_CHAR = 60;
/** Receives updates when enriched call capabilities are ready. */
interface CapabilitiesListener {
@@ -148,6 +135,15 @@ public interface EnrichedCallManager {
void endCallComposerSession(long sessionId);
/**
+ * Sends a post call note to the given number.
+ *
+ * @throws IllegalArgumentException if message is longer than {@link #POST_CALL_NOTE_MAX_CHAR}
+ * characters
+ */
+ @MainThread
+ void sendPostCallNote(@NonNull String number, @NonNull String message);
+
+ /**
* Called once the capabilities are available for a corresponding call to {@link
* #requestCapabilities(String)}.
*
@@ -162,8 +158,8 @@ public interface EnrichedCallManager {
interface StateChangedListener {
/**
- * Callback fired when state changes. Listeners should call {@link #getSession(String)} to
- * retrieve the new state.
+ * Callback fired when state changes. Listeners should call {@link #getSession(long)} or {@link
+ * #getSession(String, String)} to retrieve the new state.
*/
void onEnrichedCallStateChanged();
}
@@ -177,10 +173,10 @@ public interface EnrichedCallManager {
@MainThread
void registerStateChangedListener(@NonNull StateChangedListener listener);
- /** Returns the {@link Session} for the given number, or {@code null} if no session exists. */
+ /** Returns the {@link Session} for the given unique call id, falling back to the number. */
@MainThread
@Nullable
- Session getSession(@NonNull String number);
+ Session getSession(@NonNull String uniqueCallId, @NonNull String number);
/** Returns the {@link Session} for the given sessionId, or {@code null} if no session exists. */
@MainThread
@@ -188,6 +184,18 @@ public interface EnrichedCallManager {
Session getSession(long sessionId);
/**
+ * Returns a mapping of enriched call data for all of the given {@link CallDetailsEntries}.
+ *
+ * <p>The mapping is created by finding the HistoryResults whose timestamps occurred during or
+ * close after a CallDetailsEntry. A CallDetailsEntry can have multiple HistoryResults in the
+ * event that both a CallComposer message and PostCall message were sent for the same call.
+ */
+ @WorkerThread
+ @NonNull
+ Map<CallDetailsEntry, List<HistoryResult>> getAllHistoricalData(
+ @NonNull String number, @NonNull CallDetailsEntries entries);
+
+ /**
* Unregisters the given {@link StateChangedListener}.
*
* <p>As a result of this method, the listener will not receive updates when the state of enriched
@@ -222,4 +230,77 @@ public interface EnrichedCallManager {
*/
@MainThread
void onIncomingCallComposerData(long sessionId, @NonNull MultimediaData multimediaData);
+
+ /**
+ * Called when post call data arrives for the given session.
+ *
+ * @throws IllegalStateException if there's no session for the given id
+ */
+ @MainThread
+ void onIncomingPostCallData(long sessionId, @NonNull MultimediaData multimediaData);
+
+ /**
+ * Registers the given {@link VideoShareListener}.
+ *
+ * <p>As a result of this method, the listener will receive updates when any video share state
+ * changes.
+ */
+ @MainThread
+ void registerVideoShareListener(@NonNull VideoShareListener listener);
+
+ /**
+ * Unregisters the given {@link VideoShareListener}.
+ *
+ * <p>As a result of this method, the listener will not receive updates when any video share state
+ * changes.
+ */
+ @MainThread
+ void unregisterVideoShareListener(@NonNull VideoShareListener listener);
+
+ /** Called when an incoming video share invite is received. */
+ @MainThread
+ void onIncomingVideoShareInvite(long sessionId, @NonNull String number);
+
+ /**
+ * Starts a video share 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 startVideoShareSession(@NonNull String number);
+
+ /**
+ * Accepts a video share session invite.
+ *
+ * @param sessionId the session to accept
+ * @return whether or not accepting the session succeeded
+ */
+ @MainThread
+ boolean acceptVideoShareSession(long sessionId);
+
+ /**
+ * Retrieve the session id for an incoming video share invite.
+ *
+ * @param number the remote number in any format
+ * @return the id for the session invite, or {@link Session#NO_SESSION_ID} if there is no invite
+ */
+ @MainThread
+ long getVideoShareInviteSessionId(@NonNull String number);
+
+ /**
+ * Ends the given video share session.
+ *
+ * @param sessionId the id of the session to end
+ */
+ @MainThread
+ void endVideoShareSession(long sessionId);
+
+ /**
+ * Returns the {@link VideoShareSession} for the given sessionId, or {@code null} if no session
+ * exists.
+ */
+ @MainThread
+ @Nullable
+ VideoShareSession getVideoShareSession(long sessionId);
}
diff --git a/java/com/android/dialer/enrichedcall/FuzzyPhoneNumberMatcher.java b/java/com/android/dialer/enrichedcall/FuzzyPhoneNumberMatcher.java
new file mode 100644
index 000000000..f589f94a6
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/FuzzyPhoneNumberMatcher.java
@@ -0,0 +1,20 @@
+package com.android.dialer.enrichedcall;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+
+/** Utility for comparing phone numbers. */
+public class FuzzyPhoneNumberMatcher {
+
+ /** Returns {@code true} if the given numbers can be interpreted to be the same. */
+ public static boolean matches(@NonNull String a, @NonNull String b) {
+ String aNormalized = Assert.isNotNull(a).replaceAll("[^0-9]", "");
+ String bNormalized = Assert.isNotNull(b).replaceAll("[^0-9]", "");
+ if (aNormalized.length() < 7 || bNormalized.length() < 7) {
+ return false;
+ }
+ String aMatchable = aNormalized.substring(aNormalized.length() - 7);
+ String bMatchable = bNormalized.substring(bNormalized.length() - 7);
+ return aMatchable.equals(bMatchable);
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java b/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java
index a8ee49d4e..56145ddd4 100644
--- a/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java
+++ b/java/com/android/dialer/enrichedcall/OutgoingCallComposerData.java
@@ -20,7 +20,7 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.dialer.common.Assert;
-
+import com.google.auto.value.AutoValue;
/**
* Value type holding references to all data that could be provided for the call composer.
@@ -29,19 +29,19 @@ import com.android.dialer.common.Assert;
*
* <pre>
* OutgoingCallComposerData.builder.build(); // throws exception, no data set
- * OutgoingCallComposerData
- * .setSubject(subject)
+ * OutgoingCallComposerData.builder
+ * .setText(subject)
* .build(); // Success
- * OutgoingCallComposerData
+ * OutgoingCallComposerData.builder
* .setImageData(uri, contentType)
* .build(); // Success
- * OutgoingCallComposerData
- * .setSubject(subject)
+ * OutgoingCallComposerData.builder
+ * .setText(subject)
* .setImageData(uri, contentType)
* .build(); // Success
* </pre>
*/
-
+@AutoValue
public abstract class OutgoingCallComposerData {
public static Builder builder() {
@@ -62,7 +62,7 @@ public abstract class OutgoingCallComposerData {
public abstract String getImageContentType();
/** Builds instances of {@link OutgoingCallComposerData}. */
-
+ @AutoValue.Builder
public abstract static class Builder {
public abstract Builder setSubject(String subject);
diff --git a/java/com/android/dialer/enrichedcall/Session.java b/java/com/android/dialer/enrichedcall/Session.java
index b0439fae9..b3f291438 100644
--- a/java/com/android/dialer/enrichedcall/Session.java
+++ b/java/com/android/dialer/enrichedcall/Session.java
@@ -17,6 +17,7 @@
package com.android.dialer.enrichedcall;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import com.android.dialer.enrichedcall.EnrichedCallManager.State;
import com.android.dialer.multimedia.MultimediaData;
@@ -38,6 +39,12 @@ public interface Session {
*/
long getSessionId();
+ /** Returns the id of the dialer call associated with this session, or null if there isn't one. */
+ @Nullable
+ String getUniqueDialerCallId();
+
+ void setUniqueDialerCallId(@NonNull String id);
+
/** Returns the number associated with the remote end of this session. */
@NonNull
String getRemoteNumber();
diff --git a/java/com/android/dialer/enrichedcall/VideoShareSession.java b/java/com/android/dialer/enrichedcall/VideoShareSession.java
new file mode 100644
index 000000000..07bc4ed09
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/VideoShareSession.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+/** Holds state information and data about video share sessions. */
+public interface VideoShareSession {}
diff --git a/java/com/android/dialer/enrichedcall/historyquery/HistoryQuery.java b/java/com/android/dialer/enrichedcall/historyquery/HistoryQuery.java
new file mode 100644
index 000000000..b7593cebb
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/historyquery/HistoryQuery.java
@@ -0,0 +1,31 @@
+package com.android.dialer.enrichedcall.historyquery;
+
+import android.support.annotation.NonNull;
+import com.android.dialer.common.LogUtil;
+import com.google.auto.value.AutoValue;
+
+/**
+ * Data object representing the pieces of information required to query for historical enriched call
+ * data.
+ */
+@AutoValue
+public abstract class HistoryQuery {
+
+ @NonNull
+ public static HistoryQuery create(@NonNull String number, long callStartTime, long callEndTime) {
+ return new AutoValue_HistoryQuery(number, callStartTime, callEndTime);
+ }
+
+ public abstract String getNumber();
+
+ public abstract long getCallStartTimestamp();
+
+ public abstract long getCallEndTimestamp();
+
+ @Override
+ public String toString() {
+ return String.format(
+ "HistoryQuery{number: %s, callStartTimestamp: %d, callEndTimestamp: %d}",
+ LogUtil.sanitizePhoneNumber(getNumber()), getCallStartTimestamp(), getCallEndTimestamp());
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/historyquery/nano/HistoryResult.java b/java/com/android/dialer/enrichedcall/historyquery/nano/HistoryResult.java
new file mode 100644
index 000000000..2fdc2da50
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/historyquery/nano/HistoryResult.java
@@ -0,0 +1,203 @@
+/*
+ * 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.enrichedcall.historyquery.proto.nano;
+
+/** This file is autogenerated, but javadoc required. */
+@SuppressWarnings("hiding")
+public final class HistoryResult
+ extends com.google.protobuf.nano.ExtendableMessageNano<HistoryResult> {
+
+ /** This file is autogenerated, but javadoc required. */
+ // enum Type
+ public interface Type {
+ public static final int INCOMING_CALL_COMPOSER = 1;
+ public static final int OUTGOING_CALL_COMPOSER = 2;
+ public static final int INCOMING_POST_CALL = 3;
+ public static final int OUTGOING_POST_CALL = 4;
+ }
+
+ private static volatile HistoryResult[] _emptyArray;
+
+ public static HistoryResult[] emptyArray() {
+ // Lazily initializes the empty array
+ if (_emptyArray == null) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
+ if (_emptyArray == null) {
+ _emptyArray = new HistoryResult[0];
+ }
+ }
+ }
+ return _emptyArray;
+ }
+
+ // optional .com.android.dialer.enrichedcall.historyquery.proto.HistoryResult.Type type = 1;
+ public int type;
+
+ // optional string text = 2;
+ public java.lang.String text;
+
+ // optional string image_uri = 4;
+ public java.lang.String imageUri;
+
+ // optional string image_content_type = 5;
+ public java.lang.String imageContentType;
+
+ // optional int64 timestamp = 7;
+ public long timestamp;
+
+ // @@protoc_insertion_point(class_scope:com.android.dialer.enrichedcall.historyquery.proto.HistoryResult)
+
+ public HistoryResult() {
+ clear();
+ }
+
+ public HistoryResult clear() {
+ type =
+ com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .INCOMING_CALL_COMPOSER;
+ text = "";
+ imageUri = "";
+ imageContentType = "";
+ timestamp = 0L;
+ unknownFieldData = null;
+ cachedSize = -1;
+ return this;
+ }
+
+ @Override
+ public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)
+ throws java.io.IOException {
+ if (this.type
+ != com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .INCOMING_CALL_COMPOSER) {
+ output.writeInt32(1, this.type);
+ }
+ if (this.text != null && !this.text.equals("")) {
+ output.writeString(2, this.text);
+ }
+ if (this.imageUri != null && !this.imageUri.equals("")) {
+ output.writeString(4, this.imageUri);
+ }
+ if (this.imageContentType != null && !this.imageContentType.equals("")) {
+ output.writeString(5, this.imageContentType);
+ }
+ if (this.timestamp != 0L) {
+ output.writeInt64(7, this.timestamp);
+ }
+ super.writeTo(output);
+ }
+
+ @Override
+ protected int computeSerializedSize() {
+ int size = super.computeSerializedSize();
+ if (this.type
+ != com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .INCOMING_CALL_COMPOSER) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt32Size(1, this.type);
+ }
+ if (this.text != null && !this.text.equals("")) {
+ size += com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(2, this.text);
+ }
+ if (this.imageUri != null && !this.imageUri.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(4, this.imageUri);
+ }
+ if (this.imageContentType != null && !this.imageContentType.equals("")) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeStringSize(
+ 5, this.imageContentType);
+ }
+ if (this.timestamp != 0L) {
+ size +=
+ com.google.protobuf.nano.CodedOutputByteBufferNano.computeInt64Size(7, this.timestamp);
+ }
+ return size;
+ }
+
+ @Override
+ public HistoryResult 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:
+ {
+ int initialPos = input.getPosition();
+ int value = input.readInt32();
+ switch (value) {
+ case com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .INCOMING_CALL_COMPOSER:
+ case com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .OUTGOING_CALL_COMPOSER:
+ case com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .INCOMING_POST_CALL:
+ case com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult.Type
+ .OUTGOING_POST_CALL:
+ this.type = value;
+ break;
+ default:
+ input.rewindToPosition(initialPos);
+ storeUnknownField(input, tag);
+ break;
+ }
+ break;
+ }
+ case 18:
+ {
+ this.text = input.readString();
+ break;
+ }
+ case 34:
+ {
+ this.imageUri = input.readString();
+ break;
+ }
+ case 42:
+ {
+ this.imageContentType = input.readString();
+ break;
+ }
+ case 56:
+ {
+ this.timestamp = input.readInt64();
+ break;
+ }
+ }
+ }
+ }
+
+ public static HistoryResult parseFrom(byte[] data)
+ throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {
+ return com.google.protobuf.nano.MessageNano.mergeFrom(new HistoryResult(), data);
+ }
+
+ public static HistoryResult parseFrom(com.google.protobuf.nano.CodedInputByteBufferNano input)
+ throws java.io.IOException {
+ return new HistoryResult().mergeFrom(input);
+ }
+}
diff --git a/java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java b/java/com/android/dialer/enrichedcall/stub/EnrichedCallManagerStub.java
index db9a799d3..01d1f2aac 100644
--- a/java/com/android/dialer/enrichedcall/EnrichedCallManagerStub.java
+++ b/java/com/android/dialer/enrichedcall/stub/EnrichedCallManagerStub.java
@@ -14,11 +14,24 @@
* limitations under the License
*/
-package com.android.dialer.enrichedcall;
+package com.android.dialer.enrichedcall.stub;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import android.util.ArrayMap;
+import com.android.dialer.calldetails.nano.CallDetailsEntries;
+import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry;
+import com.android.dialer.common.Assert;
+import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.enrichedcall.Session;
+import com.android.dialer.enrichedcall.VideoShareSession;
+import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult;
+import com.android.dialer.enrichedcall.videoshare.VideoShareListener;
import com.android.dialer.multimedia.MultimediaData;
+import java.util.List;
+import java.util.Map;
/** Stub implementation of {@link EnrichedCallManager}. */
public final class EnrichedCallManagerStub implements EnrichedCallManager {
@@ -52,6 +65,9 @@ public final class EnrichedCallManagerStub implements EnrichedCallManager {
public void endCallComposerSession(long sessionId) {}
@Override
+ public void sendPostCallNote(@NonNull String number, @NonNull String message) {}
+
+ @Override
public void onCapabilitiesReceived(
@NonNull String number, @NonNull EnrichedCallCapabilities capabilities) {}
@@ -60,7 +76,7 @@ public final class EnrichedCallManagerStub implements EnrichedCallManager {
@Nullable
@Override
- public Session getSession(@NonNull String number) {
+ public Session getSession(@NonNull String uniqueCallId, @NonNull String number) {
return null;
}
@@ -70,6 +86,15 @@ public final class EnrichedCallManagerStub implements EnrichedCallManager {
return null;
}
+ @NonNull
+ @Override
+ @WorkerThread
+ public Map<CallDetailsEntry, List<HistoryResult>> getAllHistoricalData(
+ @NonNull String number, @NonNull CallDetailsEntries entries) {
+ Assert.isWorkerThread();
+ return new ArrayMap<>();
+ }
+
@Override
public void unregisterStateChangedListener(@NonNull StateChangedListener listener) {}
@@ -81,4 +106,40 @@ public final class EnrichedCallManagerStub implements EnrichedCallManager {
@Override
public void onIncomingCallComposerData(long sessionId, @NonNull MultimediaData multimediaData) {}
+
+ @Override
+ public void onIncomingPostCallData(long sessionId, @NonNull MultimediaData multimediaData) {}
+
+ @Override
+ public void registerVideoShareListener(@NonNull VideoShareListener listener) {}
+
+ @Override
+ public void unregisterVideoShareListener(@NonNull VideoShareListener listener) {}
+
+ @Override
+ public void onIncomingVideoShareInvite(long sessionId, @NonNull String number) {}
+
+ @Override
+ public long startVideoShareSession(String number) {
+ return Session.NO_SESSION_ID;
+ }
+
+ @Override
+ public boolean acceptVideoShareSession(long sessionId) {
+ return false;
+ }
+
+ @Override
+ public long getVideoShareInviteSessionId(@NonNull String number) {
+ return Session.NO_SESSION_ID;
+ }
+
+ @Override
+ public void endVideoShareSession(long sessionId) {}
+
+ @Nullable
+ @Override
+ public VideoShareSession getVideoShareSession(long sessionId) {
+ return null;
+ }
}
diff --git a/java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java b/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java
index 39c55d040..0ec72111e 100644
--- a/java/com/android/dialer/enrichedcall/StubEnrichedCallModule.java
+++ b/java/com/android/dialer/enrichedcall/stub/StubEnrichedCallModule.java
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package com.android.dialer.enrichedcall;
+package com.android.dialer.enrichedcall.stub;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
@@ -29,4 +30,6 @@ public class StubEnrichedCallModule {
static EnrichedCallManager provideEnrichedCallManager() {
return new EnrichedCallManagerStub();
}
+
+ private StubEnrichedCallModule() {}
}
diff --git a/java/com/android/dialer/enrichedcall/videoshare/VideoShareListener.java b/java/com/android/dialer/enrichedcall/videoshare/VideoShareListener.java
new file mode 100644
index 000000000..bcc387a3f
--- /dev/null
+++ b/java/com/android/dialer/enrichedcall/videoshare/VideoShareListener.java
@@ -0,0 +1,14 @@
+package com.android.dialer.enrichedcall.videoshare;
+
+import android.support.annotation.MainThread;
+
+/** Receives updates when video share status has changed. */
+public interface VideoShareListener {
+
+ /**
+ * Callback fired when video share has changed (service connected / disconnected, video share
+ * invite received or canceled, or when a session changes).
+ */
+ @MainThread
+ void onVideoShareChanged();
+}
diff --git a/java/com/android/dialer/inject/ApplicationModule.java b/java/com/android/dialer/inject/ContextModule.java
index 99e5296ea..aa83f0105 100644
--- a/java/com/android/dialer/inject/ApplicationModule.java
+++ b/java/com/android/dialer/inject/ContextModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -16,24 +16,24 @@
package com.android.dialer.inject;
-import android.app.Application;
+import android.content.Context;
import android.support.annotation.NonNull;
import com.android.dialer.common.Assert;
import dagger.Module;
import dagger.Provides;
-/** Provides the singleton application object. */
+/** Provides the singleton context object. */
@Module
-public final class ApplicationModule {
+public final class ContextModule {
- @NonNull private final Application application;
+ @NonNull private final Context context;
- public ApplicationModule(@NonNull Application application) {
- this.application = Assert.isNotNull(application);
+ public ContextModule(@NonNull Context context) {
+ this.context = Assert.isNotNull(context);
}
@Provides
- Application provideApplication() {
- return application;
+ Context provideContext() {
+ return context;
}
}
diff --git a/java/com/android/dialer/inject/DialerAppComponent.java b/java/com/android/dialer/inject/HasRootComponent.java
index 9832ce804..0802b806a 100644
--- a/java/com/android/dialer/inject/DialerAppComponent.java
+++ b/java/com/android/dialer/inject/HasRootComponent.java
@@ -16,14 +16,10 @@
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();
+/**
+ * Used by packages to access the root component from the Application without creating a dependency
+ * cycle.
+ */
+public interface HasRootComponent {
+ Object component();
}
diff --git a/java/com/android/dialer/interactions/PhoneNumberInteraction.java b/java/com/android/dialer/interactions/PhoneNumberInteraction.java
index f36e5319c..b797629dc 100644
--- a/java/com/android/dialer/interactions/PhoneNumberInteraction.java
+++ b/java/com/android/dialer/interactions/PhoneNumberInteraction.java
@@ -81,8 +81,10 @@ 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;
+ public static final int REQUEST_CALL_PHONE = 2;
- private static final String[] PHONE_NUMBER_PROJECTION =
+ @VisibleForTesting
+ public static final String[] PHONE_NUMBER_PROJECTION =
new String[] {
Phone._ID,
Phone.NUMBER,
@@ -191,13 +193,14 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
* 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.
+ * @return true if the necessary permissions were found to start the interaction, false otherwise
*/
- public static void startInteractionForPhoneCall(
+ public static boolean startInteractionForPhoneCall(
TransactionSafeActivity activity,
Uri uri,
boolean isVideoCall,
CallSpecificAppData callSpecificAppData) {
- new PhoneNumberInteraction(
+ return new PhoneNumberInteraction(
activity, ContactDisplayUtils.INTERACTION_CALL, isVideoCall, callSpecificAppData)
.startInteraction(uri);
}
@@ -211,11 +214,19 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
* Initiates the interaction to result in either a phone call or sms message for a contact.
*
* @param uri Contact Uri
+ * @return true if the necessary permissions were found to start the interaction, false otherwise
*/
- 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.
+ private boolean startInteraction(Uri uri) {
+ // It's possible for a shortcut to have been created, and then 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.CALL_PHONE)
+ != PackageManager.PERMISSION_GRANTED) {
+ LogUtil.i("PhoneNumberInteraction.startInteraction", "No phone permissions");
+ ActivityCompat.requestPermissions(
+ (Activity) mContext, new String[] {Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE);
+ return false;
+ }
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
LogUtil.i("PhoneNumberInteraction.startInteraction", "No contact permissions");
@@ -223,7 +234,7 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
(Activity) mContext,
new String[] {Manifest.permission.READ_CONTACTS},
REQUEST_READ_CONTACTS);
- return;
+ return false;
}
if (mLoader != null) {
@@ -249,6 +260,7 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
mContext, queryUri, PHONE_NUMBER_PROJECTION, PHONE_NUMBER_SELECTION, null, null);
mLoader.registerListener(0, this);
mLoader.startLoading();
+ return true;
}
@Override
@@ -457,8 +469,8 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
* 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.
+ * boolean, CallSpecificAppData)} 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}.
diff --git a/java/com/android/dialer/logging/nano/ContactLookupResult.java b/java/com/android/dialer/logging/nano/ContactLookupResult.java
index 8960560fb..93f5f0135 100644
--- a/java/com/android/dialer/logging/nano/ContactLookupResult.java
+++ b/java/com/android/dialer/logging/nano/ContactLookupResult.java
@@ -11,17 +11,19 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * 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 ContactLookupResult extends
- com.google.protobuf.nano.ExtendableMessageNano<ContactLookupResult> {
+public final class ContactLookupResult
+ extends com.google.protobuf.nano.ExtendableMessageNano<ContactLookupResult> {
+ /** This file is autogenerated, but javadoc required. */
// enum Type
public interface Type {
public static final int UNKNOWN_LOOKUP_RESULT_TYPE = 0;
@@ -34,11 +36,11 @@ public final class ContactLookupResult extends
}
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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new ContactLookupResult[0];
}
@@ -60,20 +62,20 @@ public final class ContactLookupResult extends
}
@Override
- public ContactLookupResult mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
}
- break;
- }
}
}
}
@@ -84,8 +86,7 @@ public final class ContactLookupResult extends
}
public static ContactLookupResult parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
- throws java.io.IOException {
+ 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
index 35d8b8ca1..dbe40cd53 100644
--- a/java/com/android/dialer/logging/nano/ContactSource.java
+++ b/java/com/android/dialer/logging/nano/ContactSource.java
@@ -11,17 +11,19 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * 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 ContactSource extends
- com.google.protobuf.nano.ExtendableMessageNano<ContactSource> {
+public final class ContactSource
+ extends com.google.protobuf.nano.ExtendableMessageNano<ContactSource> {
+ /** This file is autogenerated, but javadoc required. */
// enum Type
public interface Type {
public static final int UNKNOWN_SOURCE_TYPE = 0;
@@ -33,11 +35,11 @@ public final class ContactSource extends
}
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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new ContactSource[0];
}
@@ -59,20 +61,20 @@ public final class ContactSource extends
}
@Override
- public ContactSource mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
}
- break;
- }
}
}
}
@@ -82,8 +84,7 @@ public final class ContactSource extends
return com.google.protobuf.nano.MessageNano.mergeFrom(new ContactSource(), data);
}
- public static ContactSource parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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
index 6bb56751f..80a006b55 100644
--- a/java/com/android/dialer/logging/nano/DialerImpression.java
+++ b/java/com/android/dialer/logging/nano/DialerImpression.java
@@ -14,12 +14,16 @@
* 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 DialerImpression extends
- com.google.protobuf.nano.ExtendableMessageNano<DialerImpression> {
+public final class DialerImpression
+ extends com.google.protobuf.nano.ExtendableMessageNano<DialerImpression> {
+ /** This file is autogenerated, but javadoc required. */
// enum Type
public interface Type {
public static final int UNKNOWN_AOSP_EVENT_TYPE = 1000;
@@ -33,7 +37,8 @@ public final class DialerImpression extends
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
+ 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;
@@ -41,7 +46,8 @@ public final class DialerImpression extends
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_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;
@@ -89,7 +95,8 @@ public final class DialerImpression extends
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 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;
@@ -116,15 +123,43 @@ public final class DialerImpression extends
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;
+ public static final int CALL_COMPOSER_ACTIVITY_SEND_AND_CALL_PRESSED_WHEN_SESSION_NOT_READY =
+ 1107;
+ public static final int POST_CALL_PROMPT_USER_TO_SEND_MESSAGE_CLICKED = 1108;
+ public static final int POST_CALL_PROMPT_USER_TO_SEND_MESSAGE = 1109;
+ public static final int POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE = 1110;
+ public static final int POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE_CLICKED = 1111;
+ public static final int IN_CALL_SCREEN_TURN_ON_MUTE = 1112;
+ public static final int IN_CALL_SCREEN_TURN_OFF_MUTE = 1113;
+ public static final int IN_CALL_SCREEN_SWAP_CAMERA = 1114;
+ public static final int IN_CALL_SCREEN_TURN_ON_VIDEO = 1115;
+ public static final int IN_CALL_SCREEN_TURN_OFF_VIDEO = 1116;
+ public static final int VIDEO_CALL_WITH_INCOMING_VOICE_CALL = 1117;
+ public static final int VIDEO_CALL_WITH_INCOMING_VIDEO_CALL = 1118;
+ public static final int VOICE_CALL_WITH_INCOMING_VOICE_CALL = 1119;
+ public static final int VOICE_CALL_WITH_INCOMING_VIDEO_CALL = 1120;
+ public static final int CALL_DETAILS_COPY_NUMBER = 1121;
+ public static final int CALL_DETAILS_EDIT_BEFORE_CALL = 1122;
+ public static final int CALL_DETAILS_CALL_BACK = 1123;
+ public static final int VVM_USER_DISMISSED_VM_ALMOST_FULL_PROMO = 1124;
+ public static final int VVM_USER_DISMISSED_VM_FULL_PROMO = 1125;
+ public static final int VVM_USER_ENABLED_ARCHIVE_FROM_VM_ALMOST_FULL_PROMO = 1126;
+ public static final int VVM_USER_ENABLED_ARCHIVE_FROM_VM_FULL_PROMO = 1127;
+ public static final int VVM_USER_SHOWN_VM_ALMOST_FULL_PROMO = 1128;
+ public static final int VVM_USER_SHOWN_VM_FULL_PROMO = 1129;
+ public static final int VVM_USER_SHOWN_VM_ALMOST_FULL_ERROR_MESSAGE = 1130;
+ public static final int VVM_USER_SHOWN_VM_FULL_ERROR_MESSAGE = 1131;
+ public static final int VVM_USER_TURNED_ARCHIVE_ON_FROM_SETTINGS = 1132;
+ public static final int VVM_USER_TURNED_ARCHIVE_OFF_FROM_SETTINGS = 1133;
+ public static final int VVM_ARCHIVE_AUTO_DELETED_VM_FROM_SERVER = 1134;
+ public static final int VVM_ARCHIVE_AUTO_DELETE_TURNED_OFF = 1135;
}
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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new DialerImpression[0];
}
@@ -146,20 +181,20 @@ public final class DialerImpression extends
}
@Override
- public DialerImpression mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
}
- break;
- }
}
}
}
@@ -169,10 +204,8 @@ public final class DialerImpression extends
return com.google.protobuf.nano.MessageNano.mergeFrom(new DialerImpression(), data);
}
- public static DialerImpression parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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
index 8d9430be9..7ca95fa45 100644
--- a/java/com/android/dialer/logging/nano/InteractionEvent.java
+++ b/java/com/android/dialer/logging/nano/InteractionEvent.java
@@ -23,8 +23,8 @@ package com.android.dialer.logging.nano;
public final class InteractionEvent
extends com.google.protobuf.nano.ExtendableMessageNano<InteractionEvent> {
- // enum Type
/** This file is autogenerated, but javadoc required. */
+ // enum Type
public interface Type {
public static final int UNKNOWN = 0;
public static final int CALL_BLOCKED = 15;
diff --git a/java/com/android/dialer/logging/nano/ReportingLocation.java b/java/com/android/dialer/logging/nano/ReportingLocation.java
index 1f05ce414..08ee04e7e 100644
--- a/java/com/android/dialer/logging/nano/ReportingLocation.java
+++ b/java/com/android/dialer/logging/nano/ReportingLocation.java
@@ -11,17 +11,19 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * 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 ReportingLocation extends
- com.google.protobuf.nano.ExtendableMessageNano<ReportingLocation> {
+public final class ReportingLocation
+ extends com.google.protobuf.nano.ExtendableMessageNano<ReportingLocation> {
+ /** This file is autogenerated, but javadoc required. */
// enum Type
public interface Type {
public static final int UNKNOWN_REPORTING_LOCATION = 0;
@@ -30,11 +32,11 @@ public final class ReportingLocation extends
}
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) {
+ synchronized (com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {
if (_emptyArray == null) {
_emptyArray = new ReportingLocation[0];
}
@@ -56,20 +58,20 @@ public final class ReportingLocation extends
}
@Override
- public ReportingLocation mergeFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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;
+ default:
+ {
+ if (!super.storeUnknownField(input, tag)) {
+ return this;
+ }
+ break;
}
- break;
- }
}
}
}
@@ -79,8 +81,7 @@ public final class ReportingLocation extends
return com.google.protobuf.nano.MessageNano.mergeFrom(new ReportingLocation(), data);
}
- public static ReportingLocation parseFrom(
- com.google.protobuf.nano.CodedInputByteBufferNano input)
+ 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
index be4e5eb9e..bd5b817e1 100644
--- a/java/com/android/dialer/logging/nano/ScreenEvent.java
+++ b/java/com/android/dialer/logging/nano/ScreenEvent.java
@@ -22,8 +22,8 @@ package com.android.dialer.logging.nano;
@SuppressWarnings("hiding")
public final class ScreenEvent extends com.google.protobuf.nano.ExtendableMessageNano<ScreenEvent> {
- // enum Type
/** This file is autogenerated, but javadoc required. */
+ // enum Type
public interface Type {
public static final int UNKNOWN = 0;
public static final int DIALPAD = 1;
diff --git a/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java b/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java
deleted file mode 100644
index cc6815094..000000000
--- a/java/com/android/dialer/multimedia/AutoValue_MultimediaData.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * 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
index ebd41a918..22bb7641c 100644
--- a/java/com/android/dialer/multimedia/MultimediaData.java
+++ b/java/com/android/dialer/multimedia/MultimediaData.java
@@ -21,10 +21,10 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.dialer.common.LogUtil;
+import com.google.auto.value.AutoValue;
-
-/** Holds the data associated with an enriched call session. */
-
+/** Holds data associated with a call. */
+@AutoValue
public abstract class MultimediaData {
public static final MultimediaData EMPTY = builder().build();
@@ -34,32 +34,33 @@ public abstract class MultimediaData {
return new AutoValue_MultimediaData.Builder().setImportant(false);
}
- /** Returns the call composer subject if set, or null if this isn't a call composer session. */
+ /**
+ * Returns the text part of this data.
+ *
+ * <p>This field is used for both the call composer session and the post call note.
+ */
@Nullable
- public abstract String getSubject();
+ public abstract String getText();
- /** Returns the call composer location if set, or null if this isn't a call composer session. */
+ /** Returns the location part of this data. */
@Nullable
public abstract Location getLocation();
- /** Returns {@code true} if this session contains image data. */
+ /** Returns {@code true} if this object 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. */
+ /** Returns the image uri part of this object's image. */
@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.
- */
+ /** Returns the content type part of this object's image, either image/png or image/jpeg. */
@Nullable
public abstract String getImageContentType();
- /** Returns {@code true} if this is a call composer session that's marked as important. */
+ /** Returns {@code true} if this data is marked as important. */
public abstract boolean isImportant();
/** Returns the string form of this MultimediaData with no PII. */
@@ -68,7 +69,7 @@ public abstract class MultimediaData {
return String.format(
"MultimediaData{subject: %s, location: %s, imageUrl: %s, imageContentType: %s, "
+ "important: %b}",
- LogUtil.sanitizePii(getSubject()),
+ LogUtil.sanitizePii(getText()),
LogUtil.sanitizePii(getLocation()),
LogUtil.sanitizePii(getImageUri()),
getImageContentType(),
@@ -76,10 +77,10 @@ public abstract class MultimediaData {
}
/** Creates instances of {@link MultimediaData}. */
-
+ @AutoValue.Builder
public abstract static class Builder {
- public abstract Builder setSubject(@NonNull String subject);
+ public abstract Builder setText(@NonNull String subject);
public abstract Builder setLocation(@NonNull Location location);
diff --git a/java/com/android/dialer/notification/AndroidManifest.xml b/java/com/android/dialer/notification/AndroidManifest.xml
new file mode 100644
index 000000000..c5484f263
--- /dev/null
+++ b/java/com/android/dialer/notification/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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
+ -->
+
+<manifest
+ package="com.android.dialer.notification"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="23" />
+</manifest>
diff --git a/java/com/android/dialer/notification/GroupedNotificationUtil.java b/java/com/android/dialer/notification/GroupedNotificationUtil.java
new file mode 100644
index 000000000..63ea51739
--- /dev/null
+++ b/java/com/android/dialer/notification/GroupedNotificationUtil.java
@@ -0,0 +1,66 @@
+/*
+ * 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.notification;
+
+import android.app.NotificationManager;
+import android.service.notification.StatusBarNotification;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import java.util.Objects;
+
+/** Utilities for dealing with grouped notifications */
+public final class GroupedNotificationUtil {
+
+ /**
+ * Remove notification(s) that were added as part of a group. Will ensure that if this is the last
+ * notification in the group the summary will be removed.
+ *
+ * @param tag String tag as included in {@link NotificationManager#notify(String, int,
+ * android.app.Notification)}. If null will remove all notifications under id
+ * @param id notification id as included with {@link NotificationManager#notify(String, int,
+ * android.app.Notification)}.
+ * @param summaryTag String tag of the summary notification
+ */
+ public static void removeNotification(
+ @NonNull NotificationManager notificationManager,
+ @Nullable String tag,
+ int id,
+ @NonNull String summaryTag) {
+ if (tag == null) {
+ // Clear all missed call notifications
+ for (StatusBarNotification notification : notificationManager.getActiveNotifications()) {
+ if (notification.getId() == id) {
+ notificationManager.cancel(notification.getTag(), id);
+ }
+ }
+ } else {
+ notificationManager.cancel(tag, id);
+
+ // See if other non-summary missed call notifications exist, and if not then clear the summary
+ boolean clearSummary = true;
+ for (StatusBarNotification notification : notificationManager.getActiveNotifications()) {
+ if (notification.getId() == id && !Objects.equals(summaryTag, notification.getTag())) {
+ clearSummary = false;
+ break;
+ }
+ }
+ if (clearSummary) {
+ notificationManager.cancel(summaryTag, id);
+ }
+ }
+ }
+}
diff --git a/java/com/android/dialer/notification/NotificationChannelManager.java b/java/com/android/dialer/notification/NotificationChannelManager.java
new file mode 100644
index 000000000..9ff57321e
--- /dev/null
+++ b/java/com/android/dialer/notification/NotificationChannelManager.java
@@ -0,0 +1,232 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringDef;
+import android.support.v4.os.BuildCompat;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.common.LogUtil;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Contains info on how to create {@link NotificationChannel NotificationChannels} */
+public class NotificationChannelManager {
+
+ private static NotificationChannelManager instance;
+
+ public static NotificationChannelManager getInstance() {
+ if (instance == null) {
+ instance = new NotificationChannelManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Set the channel of notification appropriately. Will create the channel if it does not already
+ * exist. Safe to call pre-O (will no-op).
+ *
+ * <p>phoneAccount should only be null if channelName is {@link Channel#MISC}.
+ */
+ public static void applyChannel(
+ @NonNull Notification.Builder notification,
+ @NonNull Context context,
+ @Channel String channelName,
+ @Nullable PhoneAccountHandle phoneAccount) {
+ if (phoneAccount == null) {
+ if (!Channel.MISC.equals(channelName)) {
+ IllegalArgumentException exception =
+ new IllegalArgumentException(
+ "Phone account handle must not be null unless on Channel.MISC");
+ if (BuildType.get() >= BuildType.RELEASE) {
+ LogUtil.e("NotificationChannelManager.applyChannel", null, exception);
+ } else {
+ throw exception;
+ }
+ }
+ }
+
+ if (BuildCompat.isAtLeastO()) {
+ NotificationChannel channel =
+ NotificationChannelManager.getInstance().getChannel(context, channelName, phoneAccount);
+ notification.setChannel(channel.getId());
+ }
+ }
+
+ /** The base Channel IDs for {@link NotificationChannel} */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Channel.INCOMING_CALL,
+ Channel.ONGOING_CALL,
+ Channel.MISSED_CALL,
+ Channel.VOICEMAIL,
+ Channel.EXTERNAL_CALL,
+ Channel.MISC
+ })
+ public @interface Channel {
+ String INCOMING_CALL = "incomingCall";
+ String ONGOING_CALL = "ongoingCall";
+ String MISSED_CALL = "missedCall";
+ String VOICEMAIL = "voicemail";
+ String EXTERNAL_CALL = "externalCall";
+ String MISC = "miscellaneous";
+ }
+
+ private NotificationChannelManager() {}
+
+ private NotificationChannel getChannel(
+ @NonNull Context context,
+ @Channel String channelName,
+ @Nullable PhoneAccountHandle phoneAccount) {
+ String channelId = channelNameToId(channelName, phoneAccount);
+ NotificationChannel channel = getNotificationManager(context).getNotificationChannel(channelId);
+ if (channel == null) {
+ channel = createChannel(context, channelName, phoneAccount);
+ }
+ return channel;
+ }
+
+ private static String channelNameToId(
+ @Channel String name, @Nullable PhoneAccountHandle phoneAccountHandle) {
+ if (phoneAccountHandle == null) {
+ return name;
+ } else {
+ return name + ":" + phoneAccountHandle.getId();
+ }
+ }
+
+ private NotificationChannel createChannel(
+ Context context,
+ @Channel String channelName,
+ @Nullable PhoneAccountHandle phoneAccountHandle) {
+ String channelId = channelNameToId(channelName, phoneAccountHandle);
+
+ if (phoneAccountHandle != null) {
+ PhoneAccount account = getTelecomManager(context).getPhoneAccount(phoneAccountHandle);
+ NotificationChannelGroup group =
+ new NotificationChannelGroup(
+ phoneAccountHandle.getId(),
+ (account == null) ? phoneAccountHandle.getId() : account.getLabel().toString());
+ getNotificationManager(context)
+ .createNotificationChannelGroup(group); // No-op if already exists
+ } else if (!Channel.MISC.equals(channelName)) {
+ LogUtil.w(
+ "NotificationChannelManager.createChannel",
+ "Null PhoneAccountHandle with channel " + channelName);
+ }
+
+ Uri silentRingtone = Uri.parse("");
+
+ CharSequence name;
+ int importance;
+ boolean canShowBadge;
+ boolean lights;
+ boolean vibration;
+ Uri sound;
+ switch (channelName) {
+ case Channel.INCOMING_CALL:
+ name = context.getText(R.string.notification_channel_incoming_call);
+ importance = NotificationManager.IMPORTANCE_MAX;
+ canShowBadge = false;
+ lights = true;
+ vibration = false;
+ sound = silentRingtone;
+ break;
+ case Channel.MISSED_CALL:
+ name = context.getText(R.string.notification_channel_missed_call);
+ importance = NotificationManager.IMPORTANCE_DEFAULT;
+ canShowBadge = true;
+ lights = true;
+ vibration = true;
+ sound = silentRingtone;
+ break;
+ case Channel.ONGOING_CALL:
+ name = context.getText(R.string.notification_channel_ongoing_call);
+ importance = NotificationManager.IMPORTANCE_DEFAULT;
+ canShowBadge = false;
+ lights = false;
+ vibration = false;
+ sound = null;
+ break;
+ case Channel.VOICEMAIL:
+ name = context.getText(R.string.notification_channel_voicemail);
+ importance = NotificationManager.IMPORTANCE_DEFAULT;
+ canShowBadge = true;
+ lights = true;
+ vibration = true;
+ sound =
+ TelephonyManagerCompat.getVoicemailRingtoneUri(
+ getTelephonyManager(context), phoneAccountHandle);
+ break;
+ case Channel.EXTERNAL_CALL:
+ name = context.getText(R.string.notification_channel_external_call);
+ importance = NotificationManager.IMPORTANCE_HIGH;
+ canShowBadge = false;
+ lights = true;
+ vibration = true;
+ sound = null;
+ break;
+ case Channel.MISC:
+ name = context.getText(R.string.notification_channel_misc);
+ importance = NotificationManager.IMPORTANCE_DEFAULT;
+ canShowBadge = false;
+ lights = true;
+ vibration = true;
+ sound = null;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown channel: " + channelName);
+ }
+
+ NotificationChannel channel = new NotificationChannel(channelId, name, importance);
+ channel.setShowBadge(canShowBadge);
+ if (sound != null) {
+ channel.setSound(
+ sound,
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+ }
+ channel.enableLights(lights);
+ channel.enableVibration(vibration);
+ getNotificationManager(context).createNotificationChannel(channel);
+ return channel;
+ }
+
+ private static NotificationManager getNotificationManager(@NonNull Context context) {
+ return context.getSystemService(NotificationManager.class);
+ }
+
+ private static TelephonyManager getTelephonyManager(@NonNull Context context) {
+ return context.getSystemService(TelephonyManager.class);
+ }
+
+ private static TelecomManager getTelecomManager(@NonNull Context context) {
+ return context.getSystemService(TelecomManager.class);
+ }
+}
diff --git a/java/com/android/dialer/notification/res/values/ids.xml b/java/com/android/dialer/notification/res/values/ids.xml
new file mode 100644
index 000000000..6bdb489a7
--- /dev/null
+++ b/java/com/android/dialer/notification/res/values/ids.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <item name="notification_incoming_call" type="id"/>
+ <item name="notification_ongoing_call" type="id"/>
+ <item name="notification_missed_call" type="id"/>
+ <item name="notification_voicemail" type="id"/>
+ <item name="notification_external_call" type="id"/>
+ <item name="notification_call_blocking_disabled_by_emergency_call" type="id"/>
+ <item name="notification_spam_call" type="id"/>
+ <item name="notification_feedback" type="id"/>
+</resources>
diff --git a/java/com/android/dialer/notification/res/values/strings.xml b/java/com/android/dialer/notification/res/values/strings.xml
new file mode 100644
index 000000000..2fc4962c6
--- /dev/null
+++ b/java/com/android/dialer/notification/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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 name="notification_channel_incoming_call">Incoming calls</string>
+ <string name="notification_channel_ongoing_call">Ongoing calls</string>
+ <string name="notification_channel_missed_call">Missed calls</string>
+ <string name="notification_channel_voicemail">Voicemails</string>
+ <string name="notification_channel_external_call">External calls</string>
+ <string name="notification_channel_misc">Miscellaneous</string>
+</resources>
diff --git a/java/com/android/dialer/oem/AndroidManifest.xml b/java/com/android/dialer/oem/AndroidManifest.xml
new file mode 100644
index 000000000..e161a6d14
--- /dev/null
+++ b/java/com/android/dialer/oem/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest
+ package="com.android.dialer.oem">
+</manifest> \ No newline at end of file
diff --git a/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java b/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java
new file mode 100644
index 000000000..18f621e01
--- /dev/null
+++ b/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * This file is derived in part from code issued under the following license.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.dialer.oem;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import com.android.dialer.common.LogUtil;
+import java.util.regex.Pattern;
+
+/**
+ * Util class to handle special char sequence and launch corresponding intent based the sequence.
+ */
+public class MotorolaHiddenMenuKeySequence {
+ private static final String EXTRA_HIDDEN_MENU_CODE = "HiddenMenuCode";
+ private static MotorolaHiddenMenuKeySequence instance = null;
+
+ private static String[] hiddenKeySequenceArray = null;
+ private static String[] hiddenKeySequenceIntentArray = null;
+ private static String[] hiddenKeyPatternArray = null;
+ private static String[] hiddenKeyPatternIntentArray = null;
+ private static boolean featureHiddenMenuEnabled = false;
+
+ /**
+ * Handle input char sequence.
+ *
+ * @param context context
+ * @param input input sequence
+ * @return true if the input matches any pattern
+ */
+ static boolean handleCharSequence(Context context, String input) {
+ getInstance(context);
+ if (!featureHiddenMenuEnabled) {
+ return false;
+ }
+ return handleKeySequence(context, input) || handleKeyPattern(context, input);
+ }
+
+ /**
+ * Public interface to return the Singleton instance
+ *
+ * @param context the Context
+ * @return the MotorolaHiddenMenuKeySequence singleton instance
+ */
+ private static synchronized MotorolaHiddenMenuKeySequence getInstance(Context context) {
+ if (null == instance) {
+ instance = new MotorolaHiddenMenuKeySequence(context);
+ }
+ return instance;
+ }
+
+ private MotorolaHiddenMenuKeySequence(Context context) {
+ featureHiddenMenuEnabled =
+ context.getResources().getBoolean(R.bool.motorola_feature_hidden_menu);
+ // In case we do have a SPN from resource we need to match from service; otherwise we are
+ // free to go
+ if (featureHiddenMenuEnabled) {
+
+ hiddenKeySequenceArray =
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence);
+ hiddenKeySequenceIntentArray =
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents);
+ hiddenKeyPatternArray =
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern);
+ hiddenKeyPatternIntentArray =
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents);
+
+ if (hiddenKeySequenceArray.length != hiddenKeySequenceIntentArray.length
+ || hiddenKeyPatternArray.length != hiddenKeyPatternIntentArray.length
+ || (hiddenKeySequenceArray.length == 0 && hiddenKeyPatternArray.length == 0)) {
+ LogUtil.e(
+ "MotorolaHiddenMenuKeySequence",
+ "the key sequence array is not matching, turn off feature."
+ + "key sequence: %d != %d, key pattern %d != %d",
+ hiddenKeySequenceArray.length,
+ hiddenKeySequenceIntentArray.length,
+ hiddenKeyPatternArray.length,
+ hiddenKeyPatternIntentArray.length);
+ featureHiddenMenuEnabled = false;
+ }
+ }
+ }
+
+ private static boolean handleKeyPattern(Context context, String input) {
+ int len = input.length();
+ if (len <= 3 || hiddenKeyPatternArray == null || hiddenKeyPatternIntentArray == null) {
+ return false;
+ }
+
+ for (int i = 0; i < hiddenKeyPatternArray.length; i++) {
+ if ((Pattern.compile(hiddenKeyPatternArray[i])).matcher(input).matches()) {
+ return sendIntent(context, input, hiddenKeyPatternIntentArray[i]);
+ }
+ }
+ return false;
+ }
+
+ private static boolean handleKeySequence(Context context, String input) {
+ int len = input.length();
+ if (len <= 3 || hiddenKeySequenceArray == null || hiddenKeySequenceIntentArray == null) {
+ return false;
+ }
+
+ for (int i = 0; i < hiddenKeySequenceArray.length; i++) {
+ if (hiddenKeySequenceArray[i].equals(input)) {
+ return sendIntent(context, input, hiddenKeySequenceIntentArray[i]);
+ }
+ }
+ return false;
+ }
+
+ private static boolean sendIntent(
+ final Context context, final String input, final String action) {
+ LogUtil.d("MotorolaHiddenMenuKeySequence.sendIntent", "input: %s", input);
+ try {
+ Intent intent = new Intent(action);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_HIDDEN_MENU_CODE, input);
+
+ ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
+
+ if (resolveInfo != null
+ && resolveInfo.activityInfo != null
+ && resolveInfo.activityInfo.enabled) {
+ context.startActivity(intent);
+ return true;
+ } else {
+ LogUtil.w("MotorolaHiddenMenuKeySequence.sendIntent", "not able to resolve the intent");
+ }
+ } catch (ActivityNotFoundException e) {
+ LogUtil.e(
+ "MotorolaHiddenMenuKeySequence.sendIntent", "handleHiddenMenu Key Pattern Exception", e);
+ }
+ return false;
+ }
+}
diff --git a/java/com/android/dialer/oem/MotorolaUtils.java b/java/com/android/dialer/oem/MotorolaUtils.java
new file mode 100644
index 000000000..29bf0b23d
--- /dev/null
+++ b/java/com/android/dialer/oem/MotorolaUtils.java
@@ -0,0 +1,51 @@
+package com.android.dialer.oem;
+
+import android.content.Context;
+import com.android.dialer.common.ConfigProviderBindings;
+
+/** Util class for Motorola OEM devices. */
+public class MotorolaUtils {
+
+ private static final String CONFIG_HD_CODEC_BLINKING_ICON_WHEN_CONNECTING_CALL_ENABLED =
+ "hd_codec_blinking_icon_when_connecting_enabled";
+ private static final String CONFIG_HD_CODEC_SHOW_ICON_IN_CALL_LOG_ENABLED =
+ "hd_codec_show_icon_in_call_log_enabled";
+
+ // This is used to check if a Motorola device supports HD voice call feature, which comes from
+ // system feature setting.
+ private static final String HD_CALL_FEATRURE = "com.motorola.software.sprint.hd_call";
+
+ // Feature flag indicates it's a HD call, currently this is only used by Motorola system build.
+ // TODO(b/35359461): Upstream and move it to android.provider.CallLog.
+ private static final int FEATURES_HD_CALL = 0x10000000;
+
+ public static boolean shouldBlinkHdIconWhenConnectingCall(Context context) {
+ return ConfigProviderBindings.get(context)
+ .getBoolean(CONFIG_HD_CODEC_BLINKING_ICON_WHEN_CONNECTING_CALL_ENABLED, true)
+ && isSupportingSprintHdCodec(context);
+ }
+
+ public static boolean shouldShowHdIconInCallLog(Context context, int features) {
+ return ConfigProviderBindings.get(context)
+ .getBoolean(CONFIG_HD_CODEC_SHOW_ICON_IN_CALL_LOG_ENABLED, true)
+ && isSupportingSprintHdCodec(context)
+ && (features & FEATURES_HD_CALL) == FEATURES_HD_CALL;
+ }
+
+ /**
+ * Handle special char sequence entered in dialpad. This may launch special intent based on input.
+ *
+ * @param context context
+ * @param input input string
+ * @return true if the input is consumed and the intent is launched
+ */
+ public static boolean handleSpecialCharSequence(Context context, String input) {
+ // TODO(b/35395377): Add check for Motorola devices.
+ return MotorolaHiddenMenuKeySequence.handleCharSequence(context, input);
+ }
+
+ private static boolean isSupportingSprintHdCodec(Context context) {
+ return context.getPackageManager().hasSystemFeature(HD_CALL_FEATRURE)
+ && context.getResources().getBoolean(R.bool.motorola_sprint_hd_codec);
+ }
+}
diff --git a/java/com/android/dialer/oem/res/values/motorola_config.xml b/java/com/android/dialer/oem/res/values/motorola_config.xml
new file mode 100644
index 000000000..f875d573d
--- /dev/null
+++ b/java/com/android/dialer/oem/res/values/motorola_config.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Flag to control if HD codec is supported by Sprint. -->
+ <bool name="motorola_sprint_hd_codec">false</bool>
+
+ <!-- Hidden menu configuration for Motorola. -->
+ <!-- Flag to control if the Hidden Menu sequence will be supported by Sprint. -->
+ <bool name="motorola_feature_hidden_menu">false</bool>
+
+ <!-- This defines the specific key seuquence that will be catched in the SpecialCharSequenceMgr
+ such as, ##OMADM# -->
+ <string-array name="motorola_hidden_menu_key_sequence">
+ <item>##66236#</item> <!--##OMADM#-->
+ <item>##2539#</item> <!--##AKEY#-->
+ <item>##786#</item> <!--##RTN#-->
+ <item>##72786#</item> <!--##SCRTN#-->
+ <item>##3282#</item> <!--##DATA#-->
+ <item>##33284#</item> <!--##DEBUG#-->
+ <item>##3424#</item> <!--##DIAG#-->
+ <item>##564#</item> <!--##LOG#-->
+ <item>##4567257#</item> <!--##GLMSCLR#-->
+ <item>##873283#</item> <!--##UPDATE#-->
+ <item>##6343#</item> <!--##MEID#-->
+ <item>##27263#</item> <!--##BRAND#-->
+ <item>##258#</item> <!--##BLV#-->
+ <item>##8422#</item> <!--##UICC#-->
+ <item>##4382#</item> <!--CMAS/WEA-->
+ </string-array>
+
+ <string name="motorola_hidden_menu_intent">com.motorola.intent.action.LAUNCH_HIDDEN_MENU</string>
+
+ <!-- This defines the intents that will be send out when the key quence is matched, this must be
+ in the same order with he KeySequence array. -->
+ <string-array name="motorola_hidden_menu_key_sequence_intents">
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>com.motorola.android.intent.action.omadm.sprint.hfa</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ </string-array>
+
+ <!-- This defines the specific key patterns that will be catched in the SpecialCharSequenceMgr
+ such as, ##[0-9]{3,7}# -->
+ <string-array name="motorola_hidden_menu_key_pattern">
+ <!--##MSL#, here MSL is 6 digits SPC code, ##OTKSL#, OTKSL is also digits code -->
+ <item>##[0-9]{6}#</item>
+ </string-array>
+
+ <!-- This defines the intents that will be send out when the key quence is matched, this must be
+ in the same order with he KeyPattern array. -->
+ <string-array name="motorola_hidden_menu_key_pattern_intents">
+ <item>@string/motorola_hidden_menu_intent</item>
+ </string-array>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/p13n/inference/P13nRanking.java b/java/com/android/dialer/p13n/inference/P13nRanking.java
index 6bfc0352a..0682e85db 100644
--- a/java/com/android/dialer/p13n/inference/P13nRanking.java
+++ b/java/com/android/dialer/p13n/inference/P13nRanking.java
@@ -22,6 +22,7 @@ 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.common.ConfigProviderBindings;
import com.android.dialer.p13n.inference.protocol.P13nRanker;
import com.android.dialer.p13n.inference.protocol.P13nRankerFactory;
import java.util.List;
@@ -38,37 +39,51 @@ public final class P13nRanking {
public static P13nRanker get(@NonNull Context context) {
Assert.isNotNull(context);
Assert.isMainThread();
+
if (ranker != null) {
return ranker;
}
+ if (!ConfigProviderBindings.get(context).getBoolean("p13n_ranker_should_enable", false)) {
+ setToIdentityRanker();
+ 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;
- }
- };
+ setToIdentityRanker();
}
return ranker;
}
+ private static void setToIdentityRanker() {
+ 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 queryLength) {
+ return phoneQueryResults;
+ }
+
+ @Override
+ public boolean shouldShowEmptyListForNullQuery() {
+ return true;
+ }
+ };
+ }
+
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
index 9a859a6db..41f1de49d 100644
--- a/java/com/android/dialer/p13n/inference/protocol/P13nRanker.java
+++ b/java/com/android/dialer/p13n/inference/protocol/P13nRanker.java
@@ -41,13 +41,15 @@ public interface P13nRanker {
* 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
+ * @param queryLength length of the search query that resulted in the cursor data, if below 0,
+ * assumes no length is specified, thus applies the default behavior which is same as when
+ * queryLength is greater than zero.
* @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);
+ Cursor rankCursor(@NonNull Cursor phoneQueryResults, int queryLength);
/**
* Refreshes ranking cache (pulls fresh contextual features, pre-caches inference results, etc.).
@@ -61,6 +63,10 @@ public interface P13nRanker {
@MainThread
void refresh(@Nullable P13nRefreshCompleteListener listener);
+ /** Decides if results should be displayed for no-query search. */
+ @MainThread
+ boolean shouldShowEmptyListForNullQuery();
+
/**
* Callback class for when ranking refresh has completed.
*
diff --git a/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java b/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java
index 03b77b91c..f443d56fb 100644
--- a/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java
+++ b/java/com/android/dialer/phonenumbercache/CachedNumberLookupService.java
@@ -18,7 +18,9 @@ package com.android.dialer.phonenumbercache;
import android.content.Context;
import android.net.Uri;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
import java.io.InputStream;
public interface CachedNumberLookupService {
@@ -35,6 +37,7 @@ public interface CachedNumberLookupService {
* 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.
*/
+ @WorkerThread
CachedContactInfo lookupCachedContactFromNumber(Context context, String number);
void addContact(Context context, CachedContactInfo info);
@@ -64,6 +67,7 @@ public interface CachedNumberLookupService {
int SOURCE_TYPE_PROFILE = 4;
int SOURCE_TYPE_CNAP = 5;
+ @NonNull
ContactInfo getContactInfo();
void setSource(int sourceType, String name, long directoryId);
diff --git a/java/com/android/dialer/postcall/AndroidManifest.xml b/java/com/android/dialer/postcall/AndroidManifest.xml
new file mode 100644
index 000000000..2bf07bca2
--- /dev/null
+++ b/java/com/android/dialer/postcall/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.postcall.PostCallActivity"
+ android:exported="false"
+ android:theme="@style/Theme.AppCompat.NoActionBar"
+ android:windowSoftInputMode="adjustResize"
+ android:screenOrientation="portrait"/>
+ </application>
+</manifest>
diff --git a/java/com/android/dialer/postcall/PostCall.java b/java/com/android/dialer/postcall/PostCall.java
new file mode 100644
index 000000000..cfe7c867b
--- /dev/null
+++ b/java/com/android/dialer/postcall/PostCall.java
@@ -0,0 +1,182 @@
+/*
+ * 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.postcall;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.support.design.widget.BaseTransientBottomBar.BaseCallback;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+import android.view.View.OnClickListener;
+import com.android.dialer.buildtype.BuildType;
+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.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
+
+/** Helper class to handle all post call actions. */
+public class PostCall {
+
+ private static final String KEY_POST_CALL_CALL_CONNECT_TIME = "post_call_call_connect_time";
+ private static final String KEY_POST_CALL_CALL_DISCONNECT_TIME = "post_call_call_disconnect_time";
+ private static final String KEY_POST_CALL_CALL_NUMBER = "post_call_call_number";
+ private static final String KEY_POST_CALL_MESSAGE_SENT = "post_call_message_sent";
+
+ public static void promptUserForMessageIfNecessary(Activity activity, View rootView) {
+ if (isEnabled(activity)) {
+ if (shouldPromptUserToViewSentMessage(activity)) {
+ promptUserToViewSentMessage(activity, rootView);
+ } else if (shouldPromptUserToSendMessage(activity)) {
+ promptUserToSendMessage(activity, rootView);
+ }
+ }
+ }
+
+ private static void promptUserToSendMessage(Activity activity, View rootView) {
+ LogUtil.i("PostCall.promptUserToSendMessage", "returned from call, showing post call SnackBar");
+ String message = activity.getString(R.string.post_call_message);
+ String addMessage = activity.getString(R.string.post_call_add_message);
+ OnClickListener onClickListener =
+ v -> {
+ Logger.get(activity)
+ .logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_SEND_MESSAGE_CLICKED);
+ activity.startActivity(PostCallActivity.newIntent(activity, getPhoneNumber(activity)));
+ };
+
+ Snackbar.make(rootView, message, Snackbar.LENGTH_INDEFINITE)
+ .setAction(addMessage, onClickListener)
+ .setActionTextColor(
+ activity.getResources().getColor(R.color.dialer_snackbar_action_text_color))
+ .show();
+ Logger.get(activity).logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_SEND_MESSAGE);
+ PreferenceManager.getDefaultSharedPreferences(activity)
+ .edit()
+ .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME)
+ .apply();
+ }
+
+ private static void promptUserToViewSentMessage(Activity activity, View rootView) {
+ LogUtil.i(
+ "PostCall.promptUserToViewSentMessage",
+ "returned from sending a post call message, message sent.");
+ String message = activity.getString(R.string.post_call_message_sent);
+ String addMessage = activity.getString(R.string.view);
+ OnClickListener onClickListener =
+ v -> {
+ Logger.get(activity)
+ .logImpression(
+ DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE_CLICKED);
+ Intent intent = IntentUtil.getSendSmsIntent(getPhoneNumber(activity));
+ DialerUtils.startActivityWithErrorToast(activity, intent);
+ };
+
+ Snackbar.make(rootView, message, Snackbar.LENGTH_INDEFINITE)
+ .setAction(addMessage, onClickListener)
+ .setActionTextColor(
+ activity.getResources().getColor(R.color.dialer_snackbar_action_text_color))
+ .addCallback(
+ new BaseCallback<Snackbar>() {
+ @Override
+ public void onDismissed(Snackbar snackbar, int i) {
+ super.onDismissed(snackbar, i);
+ clear(snackbar.getContext());
+ }
+ })
+ .show();
+ Logger.get(activity)
+ .logImpression(DialerImpression.Type.POST_CALL_PROMPT_USER_TO_VIEW_SENT_MESSAGE);
+ PreferenceManager.getDefaultSharedPreferences(activity)
+ .edit()
+ .remove(KEY_POST_CALL_MESSAGE_SENT)
+ .apply();
+ }
+
+ public static void onCallDisconnected(Context context, String number, long callConnectedMillis) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putLong(KEY_POST_CALL_CALL_CONNECT_TIME, callConnectedMillis)
+ .putLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, System.currentTimeMillis())
+ .putString(KEY_POST_CALL_CALL_NUMBER, number)
+ .apply();
+ }
+
+ public static void onMessageSent(Context context, String number) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(KEY_POST_CALL_CALL_NUMBER, number)
+ .putBoolean(KEY_POST_CALL_MESSAGE_SENT, true)
+ .apply();
+ }
+
+ private static void clear(Context context) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .remove(KEY_POST_CALL_CALL_DISCONNECT_TIME)
+ .remove(KEY_POST_CALL_CALL_NUMBER)
+ .remove(KEY_POST_CALL_MESSAGE_SENT)
+ .remove(KEY_POST_CALL_CALL_CONNECT_TIME)
+ .apply();
+ }
+
+ private static boolean shouldPromptUserToSendMessage(Context context) {
+ SharedPreferences manager = PreferenceManager.getDefaultSharedPreferences(context);
+ long disconnectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_DISCONNECT_TIME, -1);
+ long connectTimeMillis = manager.getLong(KEY_POST_CALL_CALL_CONNECT_TIME, -1);
+
+ long timeSinceDisconnect = System.currentTimeMillis() - disconnectTimeMillis;
+ long callDurationMillis = disconnectTimeMillis - connectTimeMillis;
+
+ ConfigProvider binding = ConfigProviderBindings.get(context);
+ return disconnectTimeMillis != -1
+ && connectTimeMillis != -1
+ && binding.getLong("postcall_last_call_threshold", 30_000) > timeSinceDisconnect
+ && binding.getLong("postcall_call_duration_threshold", 60_000) > callDurationMillis;
+ }
+
+ private static boolean shouldPromptUserToViewSentMessage(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(KEY_POST_CALL_MESSAGE_SENT, false);
+ }
+
+ private static String getPhoneNumber(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(KEY_POST_CALL_CALL_NUMBER, null);
+ }
+
+ private static boolean isEnabled(Context context) {
+ @BuildType.Type int type = BuildType.get();
+ switch (type) {
+ case BuildType.BUGFOOD:
+ case BuildType.DOGFOOD:
+ case BuildType.FISHFOOD:
+ case BuildType.TEST:
+ return ConfigProviderBindings.get(context).getBoolean("enable_post_call", true);
+ case BuildType.RELEASE:
+ return ConfigProviderBindings.get(context).getBoolean("enable_post_call_prod", true);
+ default:
+ Assert.fail();
+ return false;
+ }
+ }
+}
diff --git a/java/com/android/dialer/postcall/PostCallActivity.java b/java/com/android/dialer/postcall/PostCallActivity.java
new file mode 100644
index 000000000..8da03dcd1
--- /dev/null
+++ b/java/com/android/dialer/postcall/PostCallActivity.java
@@ -0,0 +1,151 @@
+/*
+ * 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.postcall;
+
+import android.Manifest.permission;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+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.v7.app.AppCompatActivity;
+import android.telephony.SmsManager;
+import android.widget.Toolbar;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.enrichedcall.EnrichedCallCapabilities;
+import com.android.dialer.enrichedcall.EnrichedCallComponent;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.android.dialer.util.PermissionsUtil;
+import com.android.dialer.widget.MessageFragment;
+
+/** Activity used to send post call messages after a phone call. */
+public class PostCallActivity extends AppCompatActivity implements MessageFragment.Listener {
+
+ public static final String KEY_PHONE_NUMBER = "phone_number";
+ public static final String KEY_MESSAGE = "message";
+ private static final int REQUEST_CODE_SEND_SMS = 1;
+
+ private boolean useRcs;
+
+ public static Intent newIntent(@NonNull Context context, @NonNull String number) {
+ Intent intent = new Intent(Assert.isNotNull(context), PostCallActivity.class);
+ intent.putExtra(KEY_PHONE_NUMBER, Assert.isNotNull(number));
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ setContentView(R.layout.post_call_activity);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.setTitle(getString(R.string.post_call_message));
+ toolbar.setNavigationOnClickListener(v -> finish());
+
+ useRcs = canUseRcs(getIntent().getStringExtra(KEY_PHONE_NUMBER));
+ LogUtil.i("PostCallActivity.onCreate", "useRcs: %b", useRcs);
+
+ int postCallCharLimit =
+ useRcs
+ ? getResources().getInteger(R.integer.post_call_char_limit)
+ : MessageFragment.NO_CHAR_LIMIT;
+ String[] messages =
+ new String[] {
+ getString(R.string.post_call_message_1),
+ getString(R.string.post_call_message_2),
+ getString(R.string.post_call_message_3)
+ };
+ MessageFragment fragment =
+ MessageFragment.builder()
+ .setCharLimit(postCallCharLimit)
+ .showSendIcon()
+ .setMessages(messages)
+ .build();
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.message_container, fragment)
+ .commit();
+ }
+
+ private boolean canUseRcs(@NonNull String number) {
+ EnrichedCallCapabilities capabilities =
+ getEnrichedCallManager().getCapabilities(Assert.isNotNull(number));
+ LogUtil.i(
+ "PostCallActivity.canUseRcs",
+ "number: %s, capabilities: %s",
+ LogUtil.sanitizePhoneNumber(number),
+ capabilities);
+ return capabilities != null && capabilities.supportsPostCall();
+ }
+
+ @Override
+ public void onMessageFragmentSendMessage(@NonNull String message) {
+ String number = Assert.isNotNull(getIntent().getStringExtra(KEY_PHONE_NUMBER));
+ getIntent().putExtra(KEY_MESSAGE, message);
+
+ if (useRcs) {
+ LogUtil.i("PostCallActivity.onMessageFragmentSendMessage", "sending post call Rcs.");
+ getEnrichedCallManager().sendPostCallNote(number, message);
+ PostCall.onMessageSent(this, number);
+ finish();
+ } else if (PermissionsUtil.hasPermission(this, permission.SEND_SMS)) {
+ LogUtil.i("PostCallActivity.sendMessage", "Sending post call SMS.");
+ SmsManager smsManager = SmsManager.getDefault();
+ smsManager.sendMultipartTextMessage(
+ number, null, smsManager.divideMessage(message), null, null);
+ PostCall.onMessageSent(this, number);
+ finish();
+ } else if (PermissionsUtil.isFirstRequest(this, permission.SEND_SMS)
+ || shouldShowRequestPermissionRationale(permission.SEND_SMS)) {
+ LogUtil.i("PostCallActivity.sendMessage", "Requesting SMS_SEND permission.");
+ requestPermissions(new String[] {permission.SEND_SMS}, REQUEST_CODE_SEND_SMS);
+ } else {
+ LogUtil.i(
+ "PostCallActivity.sendMessage", "Permission permanently denied, sending to settings.");
+ 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:" + this.getPackageName()));
+ startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onMessageFragmentAfterTextChange(String message) {}
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (permissions.length > 0 && permissions[0].equals(permission.SEND_SMS)) {
+ PermissionsUtil.permissionRequested(this, permissions[0]);
+ }
+ if (requestCode == REQUEST_CODE_SEND_SMS
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ onMessageFragmentSendMessage(getIntent().getStringExtra(KEY_MESSAGE));
+ }
+ }
+
+ @NonNull
+ private EnrichedCallManager getEnrichedCallManager() {
+ return EnrichedCallComponent.get(this).getEnrichedCallManager();
+ }
+}
diff --git a/java/com/android/dialer/postcall/res/layout/post_call_activity.xml b/java/com/android/dialer/postcall/res/layout/post_call_activity.xml
new file mode 100644
index 000000000..6ea8126c5
--- /dev/null
+++ b/java/com/android/dialer/postcall/res/layout/post_call_activity.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@color/background_dialer_white"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:id="@+id/message_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:background="@color/background_dialer_white"/>
+
+ <Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/actionBarSize"
+ android:titleTextAppearance="@style/toolbar_title_text"
+ android:subtitleTextAppearance="@style/toolbar_subtitle_text"
+ android:navigationIcon="@drawable/quantum_ic_close_white_24"
+ android:background="@color/dialer_theme_color"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/postcall/res/values/strings.xml b/java/com/android/dialer/postcall/res/values/strings.xml
new file mode 100644
index 000000000..d5e085a05
--- /dev/null
+++ b/java/com/android/dialer/postcall/res/values/strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <!-- Shown as a message that notifies asks the user if they want to send a post call message -->
+ <string name="post_call_message">Say why you called</string>
+ <!-- Premade message to be sent as a text/RCS message -->
+ <string name="post_call_message_1">This is urgent. Call me back.</string>
+ <!-- Premade message to be sent as a text/RCS message -->
+ <string name="post_call_message_2">Call me back when you have some time.</string>
+ <!-- Premade message to be sent as a text/RCS message -->
+ <string name="post_call_message_3">Not urgent, we can chat later.</string>
+ <!-- Asks the user if they want to send a post call message -->
+ <string name="post_call_add_message">Add message</string>
+ <!-- Shown to let the user know that their message was sent. -->
+ <string name="post_call_message_sent">Message sent</string>
+ <string name="view">View</string>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/postcall/res/values/values.xml b/java/com/android/dialer/postcall/res/values/values.xml
new file mode 100644
index 000000000..64fe9f6c8
--- /dev/null
+++ b/java/com/android/dialer/postcall/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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>
+ <integer name="post_call_char_limit">60</integer>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/proguard/proguard.flags b/java/com/android/dialer/proguard/proguard.flags
new file mode 100644
index 000000000..0f684a0b3
--- /dev/null
+++ b/java/com/android/dialer/proguard/proguard.flags
@@ -0,0 +1,6 @@
+# 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/java/com/android/dialer/proguard/proguard_base.flags b/java/com/android/dialer/proguard/proguard_base.flags
new file mode 100644
index 000000000..7b5794ec7
--- /dev/null
+++ b/java/com/android/dialer/proguard/proguard_base.flags
@@ -0,0 +1,74 @@
+# Copied from http://google3/java/com/google/android/apps/common/proguard/base.flags
+
+# This file is intended to contain proguard options that *nobody* would ever
+# not want, in *any* configuration - they ensure basic correctness, and have
+# no downsides. You probably do not want to make changes to this file.
+
+# The presence of both of these attributes causes dalvik and other jvms to print
+# stack traces on uncaught exceptions, which is necessary to get useful crash
+# reports.
+-keepattributes SourceFile,LineNumberTable
+
+# Preverification was introduced in Java 6 to enable faster classloading, but
+# dex doesn't use the java .class format, so it has no benefit and can cause
+# problems.
+-dontpreverify
+
+# Skipping analysis of some classes may make proguard strip something that's
+# needed.
+-dontskipnonpubliclibraryclasses
+
+# Case-insensitive filesystems can't handle when a.class and A.class exist in
+# the same directory.
+-dontusemixedcaseclassnames
+
+# This prevents the names of native methods from being obfuscated and prevents
+# UnsatisfiedLinkErrors.
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+# hackbod discourages the use of enums on android, but if you use them, they
+# should work. Allow instantiation via reflection by keeping the values method.
+-keepclassmembers enum * {
+ public static **[] values();
+}
+
+# Parcel reflectively accesses this field.
+-keepclassmembers class * implements android.os.Parcelable {
+ public static *** CREATOR;
+}
+
+# These methods are needed to ensure that serialization behaves as expected when
+# classes are obfuscated, shrunk, and/or optimized.
+-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();
+}
+
+# Don't warn about Guava. Any Guava-using app will fail the proguard stage without this dontwarn,
+# and since Guava is so widely used, we include it here in the base.
+-dontwarn com.google.common.**
+
+# Don't warn about Error Prone annotations (e.g. @CompileTimeConstant)
+-dontwarn com.google.errorprone.annotations.**
+
+# Based on http://ag/718466: android.app.Notification.setLatestEventInfo() was
+# removed in MNC, but is still referenced (safely) by the NotificationCompat
+# code.
+-dontwarn android.app.Notification
+
+# Silence notes about dynamically referenced classes from AOSP support
+# libraries.
+-dontnote android.graphics.Insets
+
+# AOSP support library: ICU references to gender and plurals messages.
+-dontnote libcore.icu.ICU
+-keep class libcore.icu.ICU { *** get(...);}
+
+# AOSP support library: Handle classes that use reflection.
+-dontnote android.support.v4.app.NotificationCompatJellybean
diff --git a/java/com/android/dialer/proguard/proguard_release.flags b/java/com/android/dialer/proguard/proguard_release.flags
new file mode 100644
index 000000000..1c845cfa3
--- /dev/null
+++ b/java/com/android/dialer/proguard/proguard_release.flags
@@ -0,0 +1,24 @@
+# Copied from http://google3/java/com/google/android/apps/common/proguard/release.flags
+
+# Used for building release binaries. Obfuscates, optimizes, and shrinks.
+
+# By default, proguard leaves all classes in their original package, which
+# needlessly repeats com.google.android.apps.etc.
+-repackageclasses ""
+
+# Allows proguard to make private and protected methods and fields public as
+# part of optimization. This lets proguard inline trivial getter/setter methods.
+-allowaccessmodification
+
+# The source file attribute must be present in order to print stack traces, but
+# we rename it in order to avoid leaking the pre-obfuscation class name.
+-renamesourcefileattribute PG
+
+# This allows proguard to strip isLoggable() blocks containing only debug log
+# code from release builds.
+-assumenosideeffects class android.util.Log {
+ static *** i(...);
+ static *** d(...);
+ static *** v(...);
+ static *** isLoggable(...);
+}
diff --git a/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java b/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java
deleted file mode 100644
index ef995c816..000000000
--- a/java/com/android/dialer/shortcuts/AutoValue_DialerShortcut.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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
index 1e9a01b39..40bf97b87 100644
--- a/java/com/android/dialer/shortcuts/CallContactActivity.java
+++ b/java/com/android/dialer/shortcuts/CallContactActivity.java
@@ -56,11 +56,20 @@ public class CallContactActivity extends TransactionSafeActivity
}
}
+ /**
+ * Attempt to make a call, finishing the activity if the required permissions are already granted.
+ * If the required permissions are not already granted, the activity is not finished so that the
+ * user can choose to grant or deny them.
+ */
private void makeCall() {
CallSpecificAppData callSpecificAppData = new CallSpecificAppData();
callSpecificAppData.callInitiationType = CallInitiationType.Type.LAUNCHER_SHORTCUT;
- PhoneNumberInteraction.startInteractionForPhoneCall(
- this, contactUri, false /* isVideoCall */, callSpecificAppData);
+ boolean interactionStarted =
+ PhoneNumberInteraction.startInteractionForPhoneCall(
+ this, contactUri, false /* isVideoCall */, callSpecificAppData);
+ if (interactionStarted) {
+ finish();
+ }
}
@Override
@@ -115,6 +124,7 @@ public class CallContactActivity extends TransactionSafeActivity
int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case PhoneNumberInteraction.REQUEST_READ_CONTACTS:
+ case PhoneNumberInteraction.REQUEST_CALL_PHONE:
{
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -122,8 +132,8 @@ public class CallContactActivity extends TransactionSafeActivity
} else {
Toast.makeText(this, R.string.dialer_shortcut_no_permissions, Toast.LENGTH_SHORT)
.show();
+ finish();
}
- finish();
break;
}
default:
diff --git a/java/com/android/dialer/shortcuts/DialerShortcut.java b/java/com/android/dialer/shortcuts/DialerShortcut.java
index f2fb3301a..a8d4204fe 100644
--- a/java/com/android/dialer/shortcuts/DialerShortcut.java
+++ b/java/com/android/dialer/shortcuts/DialerShortcut.java
@@ -22,7 +22,7 @@ import android.net.Uri;
import android.os.Build.VERSION_CODES;
import android.provider.ContactsContract.Contacts;
import android.support.annotation.NonNull;
-
+import com.google.auto.value.AutoValue;
/**
* Convenience data structure.
@@ -31,7 +31,7 @@ import android.support.annotation.NonNull;
* convenience methods for doing things like constructing labels.
*/
@TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1
-
+@AutoValue
abstract class DialerShortcut {
/** Marker value indicates that shortcut has no setRank. Used by pinned shortcuts. */
@@ -160,7 +160,7 @@ abstract class DialerShortcut {
return new AutoValue_DialerShortcut.Builder().setRank(NO_RANK);
}
-
+ @AutoValue.Builder
abstract static class Builder {
/**
diff --git a/java/com/android/dialer/shortcuts/res/values/strings.xml b/java/com/android/dialer/shortcuts/res/values/strings.xml
index 1e2c87f12..5f14a8100 100644
--- a/java/com/android/dialer/shortcuts/res/values/strings.xml
+++ b/java/com/android/dialer/shortcuts/res/values/strings.xml
@@ -30,8 +30,8 @@
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>
+ <!-- Error message to display when a tapping a shortcut fails because permissions are missing.
+ [CHAR LIMIT=70] -->
+ <string name="dialer_shortcut_no_permissions">Cannot call without permissions.</string>
</resources>
diff --git a/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml b/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml
index 5e8f58d1f..49149e3e1 100644
--- a/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml
+++ b/java/com/android/dialer/shortcuts/res/xml/shortcuts.xml
@@ -24,8 +24,6 @@
<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"/>
+ android:data="content://com.android.contacts/contacts"/>
</shortcut>
</shortcuts>
diff --git a/java/com/android/dialer/simulator/SimulatorComponent.java b/java/com/android/dialer/simulator/SimulatorComponent.java
new file mode 100644
index 000000000..a16592e34
--- /dev/null
+++ b/java/com/android/dialer/simulator/SimulatorComponent.java
@@ -0,0 +1,46 @@
+/*
+ * 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 dagger.Subcomponent;
+import com.android.dialer.simulator.impl.SimulatorImpl;
+
+/** Subcomponent that can be used to access the simulator implementation. */
+public class SimulatorComponent {
+ private static SimulatorComponent instance;
+ private Simulator simulator;
+
+ public Simulator getSimulator() {
+ if (simulator == null) {
+ simulator = new SimulatorImpl();
+ }
+ return simulator;
+ }
+
+ public static SimulatorComponent get(Context context) {
+ if (instance == null) {
+ instance = new SimulatorComponent();
+ }
+ return instance;
+ }
+
+ /** Used to refer to the root application component. */
+ public interface HasComponent {
+ SimulatorComponent simulatorComponent();
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java b/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
deleted file mode 100644
index 591819819..000000000
--- a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorCallLog_CallEntry.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 00295f359..000000000
--- a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorContacts_Contact.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 58934801c..000000000
--- a/java/com/android/dialer/simulator/impl/AutoValue_SimulatorVoicemail_Voicemail.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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/SimulatorCallLog.java b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
index 9ace047d0..f127d5603 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorCallLog.java
@@ -26,7 +26,7 @@ import android.provider.CallLog.Calls;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import com.android.dialer.common.Assert;
-
+import com.google.auto.value.AutoValue;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@@ -96,7 +96,7 @@ final class SimulatorCallLog {
}
}
-
+ @AutoValue
abstract static class CallEntry {
@NonNull
abstract String getNumber();
@@ -121,7 +121,7 @@ final class SimulatorCallLog {
return values;
}
-
+ @AutoValue.Builder
abstract static class Builder {
abstract Builder setNumber(@NonNull String number);
diff --git a/java/com/android/dialer/simulator/impl/SimulatorContacts.java b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
index 89315094a..c5e25b357 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorContacts.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorContacts.java
@@ -31,7 +31,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import com.android.dialer.common.Assert;
-
+import com.google.auto.value.AutoValue;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -190,7 +190,7 @@ final class SimulatorContacts {
}
}
-
+ @AutoValue
abstract static class Contact {
@NonNull
abstract String getAccountType();
@@ -221,7 +221,7 @@ final class SimulatorContacts {
.setEmails(new ArrayList<>());
}
-
+ @AutoValue.Builder
abstract static class Builder {
@NonNull private final List<PhoneNumber> phoneNumbers = new ArrayList<>();
@NonNull private final List<Email> emails = new ArrayList<>();
diff --git a/java/com/android/dialer/simulator/impl/SimulatorImpl.java b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
new file mode 100644
index 000000000..9c6826940
--- /dev/null
+++ b/java/com/android/dialer/simulator/impl/SimulatorImpl.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.dialer.simulator.impl;
+
+import android.content.Context;
+import android.view.ActionProvider;
+import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.simulator.Simulator;
+import javax.inject.Inject;
+
+/** The entry point for the simulator feature. */
+final public class SimulatorImpl implements Simulator {
+ @Inject
+ public SimulatorImpl() {}
+
+ @Override
+ public boolean shouldShow() {
+ return BuildType.get() == BuildType.BUGFOOD || LogUtil.isDebugEnabled();
+ }
+
+ @Override
+ public ActionProvider getActionProvider(Context context) {
+ return new SimulatorActionProvider(context);
+ }
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorModule.java b/java/com/android/dialer/simulator/impl/SimulatorModule.java
index 0f8ad3954..c0cca271b 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorModule.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorModule.java
@@ -16,19 +16,15 @@
package com.android.dialer.simulator.impl;
-import android.content.Context;
-import android.view.ActionProvider;
import com.android.dialer.simulator.Simulator;
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
-/** 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);
- }
+/** This module provides an instance of the simulator. */
+@Module
+public abstract class SimulatorModule {
+ @Binds
+ @Singleton
+ public abstract Simulator bindsSimulator(SimulatorImpl simulator);
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
index ffb9191dc..04de201ae 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoicemail.java
@@ -26,7 +26,7 @@ import android.support.annotation.WorkerThread;
import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import com.android.dialer.common.Assert;
-
+import com.google.auto.value.AutoValue;
import java.util.concurrent.TimeUnit;
/** Populates the device database with voicemail entries. */
@@ -105,7 +105,7 @@ final class SimulatorVoicemail {
context.getContentResolver().insert(Status.buildSourceUri(context.getPackageName()), values);
}
-
+ @AutoValue
abstract static class Voicemail {
@NonNull
abstract String getPhoneNumber();
@@ -134,7 +134,7 @@ final class SimulatorVoicemail {
return values;
}
-
+ @AutoValue.Builder
abstract static class Builder {
abstract Builder setPhoneNumber(@NonNull String phoneNumber);
diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java
index a11e7f77a..87ddda58b 100644
--- a/java/com/android/dialer/telecom/TelecomUtil.java
+++ b/java/com/android/dialer/telecom/TelecomUtil.java
@@ -23,12 +23,13 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.CallLog.Calls;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
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 com.android.dialer.common.LogUtil;
import java.util.ArrayList;
import java.util.List;
@@ -41,6 +42,8 @@ public class TelecomUtil {
private static final String TAG = "TelecomUtil";
private static boolean sWarningLogged = false;
+ private static Boolean isDefaultDialerForTesting;
+ private static Boolean hasPermissionForTesting;
public static void showInCallScreen(Context context, boolean showDialpad) {
if (hasReadPhoneStatePermission(context)) {
@@ -48,7 +51,7 @@ public class TelecomUtil {
getTelecomManager(context).showInCallScreen(showDialpad);
} catch (SecurityException e) {
// Just in case
- Log.w(TAG, "TelecomManager.showInCallScreen called without permission.");
+ LogUtil.w(TAG, "TelecomManager.showInCallScreen called without permission.");
}
}
}
@@ -59,7 +62,7 @@ public class TelecomUtil {
getTelecomManager(context).silenceRinger();
} catch (SecurityException e) {
// Just in case
- Log.w(TAG, "TelecomManager.silenceRinger called without permission.");
+ LogUtil.w(TAG, "TelecomManager.silenceRinger called without permission.");
}
}
}
@@ -69,7 +72,7 @@ public class TelecomUtil {
try {
getTelecomManager(context).cancelMissedCallsNotification();
} catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
+ LogUtil.w(TAG, "TelecomManager.cancelMissedCalls called without permission.");
}
}
}
@@ -79,7 +82,7 @@ public class TelecomUtil {
try {
return getTelecomManager(context).getAdnUriForPhoneAccount(handle);
} catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
+ LogUtil.w(TAG, "TelecomManager.getAdnUriForPhoneAccount called without permission.");
}
}
return null;
@@ -95,7 +98,7 @@ public class TelecomUtil {
return getTelecomManager(context).handleMmi(dialString, handle);
}
} catch (SecurityException e) {
- Log.w(TAG, "TelecomManager.handleMmi called without permission.");
+ LogUtil.w(TAG, "TelecomManager.handleMmi called without permission.");
}
}
return false;
@@ -186,11 +189,17 @@ public class TelecomUtil {
}
private static boolean hasPermission(Context context, String permission) {
+ if (hasPermissionForTesting != null) {
+ return hasPermissionForTesting;
+ }
return ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED;
}
public static boolean isDefaultDialer(Context context) {
+ if (isDefaultDialerForTesting != null) {
+ return isDefaultDialerForTesting;
+ }
final boolean result =
TextUtils.equals(
context.getPackageName(), getTelecomManager(context).getDefaultDialerPackage());
@@ -199,7 +208,7 @@ public class TelecomUtil {
} else {
if (!sWarningLogged) {
// Log only once to prevent spam.
- Log.w(TAG, "Dialer is not currently set to be default dialer");
+ LogUtil.w(TAG, "Dialer is not currently set to be default dialer");
sWarningLogged = true;
}
}
@@ -209,4 +218,14 @@ public class TelecomUtil {
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public static void setIsDefaultDialerForTesting(Boolean defaultDialer) {
+ isDefaultDialerForTesting = defaultDialer;
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ public static void setHasPermissionForTesting(Boolean hasPermission) {
+ hasPermissionForTesting = hasPermission;
+ }
}
diff --git a/java/com/android/dialer/theme/res/drawable-hdpi/ic_block_24dp.png b/java/com/android/dialer/theme/res/drawable-hdpi/ic_block_24dp.png
new file mode 100644
index 000000000..2ccc89d24
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable-hdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png b/java/com/android/dialer/theme/res/drawable-hdpi/ic_call_arrow.png
index 14a33e39f..14a33e39f 100644
--- a/java/com/android/dialer/app/res/drawable-hdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/theme/res/drawable-hdpi/ic_call_arrow.png
Binary files differ
diff --git a/java/com/android/dialer/theme/res/drawable-mdpi/ic_block_24dp.png b/java/com/android/dialer/theme/res/drawable-mdpi/ic_block_24dp.png
new file mode 100644
index 000000000..ec1b33f0e
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable-mdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png b/java/com/android/dialer/theme/res/drawable-mdpi/ic_call_arrow.png
index 169cf2934..169cf2934 100644
--- a/java/com/android/dialer/app/res/drawable-mdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/theme/res/drawable-mdpi/ic_call_arrow.png
Binary files differ
diff --git a/java/com/android/dialer/theme/res/drawable-xhdpi/ic_block_24dp.png b/java/com/android/dialer/theme/res/drawable-xhdpi/ic_block_24dp.png
new file mode 100644
index 000000000..7aba97b65
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable-xhdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png b/java/com/android/dialer/theme/res/drawable-xhdpi/ic_call_arrow.png
index 6f1366018..6f1366018 100644
--- a/java/com/android/dialer/app/res/drawable-xhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/theme/res/drawable-xhdpi/ic_call_arrow.png
Binary files differ
diff --git a/java/com/android/dialer/theme/res/drawable-xxhdpi/ic_block_24dp.png b/java/com/android/dialer/theme/res/drawable-xxhdpi/ic_block_24dp.png
new file mode 100644
index 000000000..fddfa54b8
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable-xxhdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png b/java/com/android/dialer/theme/res/drawable-xxhdpi/ic_call_arrow.png
index 0364ee015..0364ee015 100644
--- a/java/com/android/dialer/app/res/drawable-xxhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/theme/res/drawable-xxhdpi/ic_call_arrow.png
Binary files differ
diff --git a/java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_block_24dp.png b/java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_block_24dp.png
new file mode 100644
index 000000000..0378d1bed
--- /dev/null
+++ b/java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_block_24dp.png
Binary files differ
diff --git a/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png b/java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_call_arrow.png
index 8243c2536..8243c2536 100644
--- a/java/com/android/dialer/app/res/drawable-xxxhdpi/ic_call_arrow.png
+++ b/java/com/android/dialer/theme/res/drawable-xxxhdpi/ic_call_arrow.png
Binary files differ
diff --git a/java/com/android/dialer/theme/res/values/dimens.xml b/java/com/android/dialer/theme/res/values/dimens.xml
index 2d11ecc84..fa750c625 100644
--- a/java/com/android/dialer/theme/res/values/dimens.xml
+++ b/java/com/android/dialer/theme/res/values/dimens.xml
@@ -25,4 +25,9 @@
<!-- actionbar height + tab height -->
<dimen name="actionbar_and_tab_height">107dp</dimen>
<dimen name="actionbar_contentInsetStart">72dp</dimen>
+
+ <dimen name="toolbar_title_text_size">20sp</dimen>
+ <dimen name="toolbar_subtitle_text_size">14sp</dimen>
+
+ <dimen name="call_log_icon_margin">4dp</dimen>
</resources>
diff --git a/java/com/android/dialer/theme/res/values/styles.xml b/java/com/android/dialer/theme/res/values/styles.xml
index ac94d0687..b5e89ff48 100644
--- a/java/com/android/dialer/theme/res/values/styles.xml
+++ b/java/com/android/dialer/theme/res/values/styles.xml
@@ -53,4 +53,15 @@
<item name="android:background">@color/actionbar_background_color</item>
<item name="background">@color/actionbar_background_color</item>
</style>
+
+ <style name="toolbar_title_text">
+ <item name="android:textSize">@dimen/toolbar_title_text_size</item>
+ <item name="android:textColor">@color/background_dialer_white</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+
+ <style name="toolbar_subtitle_text">
+ <item name="android:textSize">@dimen/toolbar_subtitle_text_size</item>
+ <item name="android:textColor">@color/background_dialer_white</item>
+ </style>
</resources>
diff --git a/java/com/android/dialer/util/AndroidManifest.xml b/java/com/android/dialer/util/AndroidManifest.xml
index 499df9b4e..ba22c1781 100644
--- a/java/com/android/dialer/util/AndroidManifest.xml
+++ b/java/com/android/dialer/util/AndroidManifest.xml
@@ -1,3 +1,19 @@
+<!--
+ ~ 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
+ -->
+
<manifest
package="com.android.dialer.util">
</manifest>
diff --git a/java/com/android/dialer/util/PermissionsUtil.java b/java/com/android/dialer/util/PermissionsUtil.java
index 70b96dfe1..5741e734a 100644
--- a/java/com/android/dialer/util/PermissionsUtil.java
+++ b/java/com/android/dialer/util/PermissionsUtil.java
@@ -47,6 +47,10 @@ public class PermissionsUtil {
return hasPermission(context, permission.CAMERA);
}
+ public static boolean hasMicrophonePermissions(Context context) {
+ return hasPermission(context, permission.RECORD_AUDIO);
+ }
+
public static boolean hasPermission(Context context, String permission) {
return ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED;
diff --git a/java/com/android/dialer/util/SettingsUtil.java b/java/com/android/dialer/util/SettingsUtil.java
index c61c09b6c..5043c3d56 100644
--- a/java/com/android/dialer/util/SettingsUtil.java
+++ b/java/com/android/dialer/util/SettingsUtil.java
@@ -69,6 +69,15 @@ public class SettingsUtil {
}
}
}
+ getRingtoneName(context, handler, ringtoneUri, msg, defaultRingtone);
+ }
+
+ public static void getRingtoneName(Context context, Handler handler, Uri ringtoneUri, int msg) {
+ getRingtoneName(context, handler, ringtoneUri, msg, false);
+ }
+
+ public static void getRingtoneName(
+ Context context, Handler handler, Uri ringtoneUri, int msg, boolean defaultRingtone) {
CharSequence summary = context.getString(R.string.ringtone_unknown);
// Is it a silent ringtone?
if (ringtoneUri == null) {
diff --git a/java/com/android/dialer/util/ViewUtil.java b/java/com/android/dialer/util/ViewUtil.java
index de08e41a7..81a32f985 100644
--- a/java/com/android/dialer/util/ViewUtil.java
+++ b/java/com/android/dialer/util/ViewUtil.java
@@ -27,6 +27,7 @@ import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.TextView;
import java.util.Locale;
@@ -113,6 +114,18 @@ public class ViewUtil {
});
}
+ public static void doOnGlobalLayout(@NonNull final View view, final ViewRunnable runnable) {
+ view.getViewTreeObserver()
+ .addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ runnable.run(view);
+ }
+ });
+ }
+
/**
* Returns {@code true} if animations should be disabled.
*
diff --git a/java/com/android/dialer/widget/MessageFragment.java b/java/com/android/dialer/widget/MessageFragment.java
new file mode 100644
index 000000000..ab47f2463
--- /dev/null
+++ b/java/com/android/dialer/widget/MessageFragment.java
@@ -0,0 +1,172 @@
+/*
+ * 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.widget;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.FragmentUtils;
+
+/** Fragment used to compose call with message fragment. */
+public class MessageFragment extends Fragment implements OnClickListener, TextWatcher {
+ private static final String CHAR_LIMIT_KEY = "char_limit";
+ private static final String SHOW_SEND_ICON_KEY = "show_send_icon";
+ private static final String MESSAGE_LIST_KEY = "message_list";
+
+ public static final int NO_CHAR_LIMIT = -1;
+
+ private EditText customMessage;
+ private ImageView sendMessage;
+ private TextView remainingChar;
+ private int charLimit;
+
+ private static MessageFragment newInstance(Builder builder) {
+ MessageFragment fragment = new MessageFragment();
+ Bundle args = new Bundle();
+ args.putInt(CHAR_LIMIT_KEY, builder.charLimit);
+ args.putBoolean(SHOW_SEND_ICON_KEY, builder.showSendIcon);
+ args.putStringArray(MESSAGE_LIST_KEY, builder.messages);
+ 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) {
+ View view = inflater.inflate(R.layout.fragment_message, container, false);
+
+ sendMessage = (ImageView) view.findViewById(R.id.send_message);
+ if (getArguments().getBoolean(SHOW_SEND_ICON_KEY, false)) {
+ sendMessage.setVisibility(View.VISIBLE);
+ sendMessage.setEnabled(false);
+ sendMessage.setOnClickListener(this);
+ }
+
+ customMessage = (EditText) view.findViewById(R.id.custom_message);
+ customMessage.addTextChangedListener(this);
+ charLimit = getArguments().getInt(CHAR_LIMIT_KEY, NO_CHAR_LIMIT);
+ if (charLimit != NO_CHAR_LIMIT) {
+ remainingChar = (TextView) view.findViewById(R.id.remaining_characters);
+ remainingChar.setVisibility(View.VISIBLE);
+ remainingChar = (TextView) view.findViewById(R.id.remaining_characters);
+ remainingChar.setText("" + charLimit);
+ customMessage.setFilters(new InputFilter[] {new InputFilter.LengthFilter(charLimit)});
+ }
+
+ LinearLayout messageContainer = (LinearLayout) view.findViewById(R.id.message_container);
+ for (String message : getArguments().getStringArray(MESSAGE_LIST_KEY)) {
+ TextView textView = (TextView) inflater.inflate(R.layout.selectable_text_view, null);
+ textView.setOnClickListener(this);
+ textView.setText(message);
+ messageContainer.addView(textView);
+ }
+ return view;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == sendMessage) {
+ getListener().onMessageFragmentSendMessage(customMessage.getText().toString());
+ } else if (view.getId() == R.id.selectable_text_view) {
+ customMessage.setText(((TextView) view).getText());
+ customMessage.setSelection(customMessage.getText().length());
+ } else {
+ Assert.fail("Unknown view clicked");
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ sendMessage.setEnabled(s.length() > 0);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (charLimit != NO_CHAR_LIMIT) {
+ remainingChar.setText("" + (charLimit - s.length()));
+ }
+ getListener().onMessageFragmentAfterTextChange(s.toString());
+ }
+
+ private Listener getListener() {
+ return FragmentUtils.getParentUnsafe(this, Listener.class);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Builder for {@link MessageFragment}. */
+ public static class Builder {
+ private String[] messages;
+ private boolean showSendIcon;
+ private int charLimit = NO_CHAR_LIMIT;
+
+ /**
+ * @throws NullPointerException if message is null
+ * @throws IllegalArgumentException if messages.length is outside the range [1,3].
+ */
+ public Builder setMessages(String... messages) {
+ // Since we only allow up to 3 messages, crash if more are set.
+ Assert.checkArgument(messages.length > 0 && messages.length <= 3);
+ this.messages = messages;
+ return this;
+ }
+
+ public Builder showSendIcon() {
+ showSendIcon = true;
+ return this;
+ }
+
+ public Builder setCharLimit(int charLimit) {
+ this.charLimit = charLimit;
+ return this;
+ }
+
+ public MessageFragment build() {
+ return MessageFragment.newInstance(this);
+ }
+ }
+
+ /** Interface for parent activity to implement to listen for important events. */
+ public interface Listener {
+ void onMessageFragmentSendMessage(String message);
+
+ void onMessageFragmentAfterTextChange(String message);
+ }
+}
diff --git a/java/com/android/dialer/widget/res/color/dialer_tint_state.xml b/java/com/android/dialer/widget/res/color/dialer_tint_state.xml
new file mode 100644
index 000000000..c29f334ac
--- /dev/null
+++ b/java/com/android/dialer/widget/res/color/dialer_tint_state.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.
+ */
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@color/dialer_edit_text_hint_color" android:state_enabled="false"/>
+ <item android:color="@color/dialer_theme_color"/>
+</selector> \ No newline at end of file
diff --git a/java/com/android/dialer/widget/res/layout/fragment_message.xml b/java/com/android/dialer/widget/res/layout/fragment_message.xml
new file mode 100644
index 000000000..f09c54f57
--- /dev/null
+++ b/java/com/android/dialer/widget/res/layout/fragment_message.xml
@@ -0,0 +1,81 @@
+<?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:orientation="vertical"
+ android:gravity="bottom"
+ android:background="@color/background_dialer_white">
+
+ <LinearLayout
+ android:id="@+id/message_container"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/message_divider_height"
+ android:background="#12000000"/>
+
+ <RelativeLayout
+ 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/textview_item_padding"
+ android:textSize="@dimen/message_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_toStartOf="@+id/count_and_send_container"/>
+
+ <LinearLayout
+ android:id="@+id/count_and_send_container"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="@dimen/textview_item_padding"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/send_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:src="@drawable/quantum_ic_send_white_24"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:tint="@color/dialer_tint_state"/>
+
+ <TextView
+ android:id="@+id/remaining_characters"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:textSize="@dimen/message_remaining_char_text_size"
+ android:textColor="@color/dialer_edit_text_hint_color"/>
+ </LinearLayout>
+ </RelativeLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/widget/res/layout/selectable_text_view.xml b/java/com/android/dialer/widget/res/layout/selectable_text_view.xml
new file mode 100644
index 000000000..3d120d13d
--- /dev/null
+++ b/java/com/android/dialer/widget/res/layout/selectable_text_view.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
+ -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/selectable_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textColor="@color/dialer_primary_text_color"
+ android:padding="16dp"
+ android:background="@drawable/item_background_material_light"/> \ No newline at end of file
diff --git a/java/com/android/dialer/widget/res/values/dimens.xml b/java/com/android/dialer/widget/res/values/dimens.xml
new file mode 100644
index 000000000..6c4ea604f
--- /dev/null
+++ b/java/com/android/dialer/widget/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>
+ <!-- Message Fragment -->
+ <dimen name="message_item_text_size">16sp</dimen>
+ <dimen name="textview_item_padding">16dp</dimen>
+ <dimen name="message_remaining_char_text_size">12sp</dimen>
+ <dimen name="message_divider_height">1dp</dimen>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/widget/res/values/strings.xml b/java/com/android/dialer/widget/res/values/strings.xml
new file mode 100644
index 000000000..6904c2de1
--- /dev/null
+++ b/java/com/android/dialer/widget/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- 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>
+</resources> \ No newline at end of file