diff options
Diffstat (limited to 'java')
28 files changed, 615 insertions, 155 deletions
diff --git a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java index 551791850..987615f5f 100644 --- a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java +++ b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java @@ -206,11 +206,28 @@ public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClic } private void showSpamBlockingPromoDialog() { - if (spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) { - spamBlockingPromoHelper.showSpamBlockingPromoDialog( - fragmentManager, - success -> spamBlockingPromoHelper.showModifySettingOnCompleteSnackbar(rootView, success), - null /* onDissmissListener */); + if (!spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) { + return; } + + Logger.get(context).logImpression(DialerImpression.Type.SPAM_BLOCKING_CALL_LOG_PROMO_SHOWN); + spamBlockingPromoHelper.showSpamBlockingPromoDialog( + fragmentManager, + () -> { + Logger.get(context) + .logImpression(DialerImpression.Type.SPAM_BLOCKING_ENABLED_THROUGH_CALL_LOG_PROMO); + spamSettings.modifySpamBlockingSetting( + true, + success -> { + if (!success) { + Logger.get(context) + .logImpression( + DialerImpression.Type + .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_CALL_LOG_PROMO); + } + spamBlockingPromoHelper.showModifySettingOnCompleteSnackbar(rootView, success); + }); + }, + null /* onDismissListener */); } } diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java index 4e968f095..1e55c6358 100644 --- a/java/com/android/dialer/app/calllog/CallLogFragment.java +++ b/java/com/android/dialer/app/calllog/CallLogFragment.java @@ -449,7 +449,7 @@ public class CallLogFragment extends Fragment super.onStart(); CequintCallerIdManager cequintCallerIdManager = null; if (CequintCallerIdManager.isCequintCallerIdEnabled(getContext())) { - cequintCallerIdManager = CequintCallerIdManager.createInstanceForCallLog(); + cequintCallerIdManager = new CequintCallerIdManager(); } contactInfoCache.setCequintCallerIdManager(cequintCallerIdManager); } diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java index 0f9f8905d..92efd392b 100644 --- a/java/com/android/dialer/callintent/CallIntentBuilder.java +++ b/java/com/android/dialer/callintent/CallIntentBuilder.java @@ -152,6 +152,7 @@ public class CallIntentBuilder implements Parcelable { return isVideoCall; } + /** Default false. Should only be set to true if the number has a lookup URI. */ public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) { this.allowAssistedDial = allowAssistedDial; return this; diff --git a/java/com/android/dialer/calllog/database/contract/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto index f99693d4e..f42974d36 100644 --- a/java/com/android/dialer/calllog/database/contract/number_attributes.proto +++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto @@ -24,7 +24,7 @@ package com.android.dialer; import "java/com/android/dialer/logging/contact_source.proto"; // Information related to the phone number of the call. -// Next ID: 13 +// Next ID: 14 message NumberAttributes { // The name (which may be a person's name or business name, but not a number) // formatted exactly as it should appear to the user. If the user's locale or @@ -70,4 +70,8 @@ message NumberAttributes { // Whether the number can be reached via a carrier video call. optional bool can_support_carrier_video_call = 12; + + // Description of the number's geolocation (e.g., "Mountain View, CA"). + // This string is for display purpose only. + optional string geolocation = 13; }
\ No newline at end of file diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java index e346de011..a1a2a3b48 100644 --- a/java/com/android/dialer/calllogutils/CallLogEntryText.java +++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java @@ -214,7 +214,12 @@ public final class CallLogEntryText { // (1) there is no number type label, and // (2) the number is not spam. if (TextUtils.isEmpty(numberTypeLabel) && !row.getNumberAttributes().getIsSpam()) { - String location = row.getGeocodedLocation(); + // If number attributes contain a location (obtained from a PhoneLookup), use it instead + // of the one from the annotated call log. + String location = + !TextUtils.isEmpty(row.getNumberAttributes().getGeolocation()) + ? row.getNumberAttributes().getGeolocation() + : row.getGeocodedLocation(); if (!TextUtils.isEmpty(location)) { if (secondaryText.length() > 0) { secondaryText.append(", "); diff --git a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java index f4fab8405..9f07fdac5 100644 --- a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java +++ b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java @@ -56,6 +56,7 @@ public final class NumberAttributesConverter { .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) .setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isDefaultCp2InfoIncomplete()) .setContactSource(phoneLookupInfoConsolidator.getContactSource()) - .setCanSupportCarrierVideoCall(phoneLookupInfoConsolidator.canSupportCarrierVideoCall()); + .setCanSupportCarrierVideoCall(phoneLookupInfoConsolidator.canSupportCarrierVideoCall()) + .setGeolocation(phoneLookupInfoConsolidator.getGeolocation()); } } diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java index 79205a7d9..28663c17d 100644 --- a/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java +++ b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java @@ -17,8 +17,10 @@ package com.android.dialer.historyitemactions; import android.content.Context; +import android.content.res.ColorStateList; import android.os.Bundle; import android.support.design.widget.BottomSheetDialog; +import android.support.v4.content.ContextCompat; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -109,6 +111,12 @@ public class HistoryItemActionBottomSheet extends BottomSheetDialog implements O ((TextView) moduleView.findViewById(R.id.module_text)).setText(module.getStringId()); ((ImageView) moduleView.findViewById(R.id.module_image)) .setImageResource(module.getDrawableId()); + if (module.tintDrawable()) { + ((ImageView) moduleView.findViewById(R.id.module_image)) + .setImageTintList( + ColorStateList.valueOf( + ContextCompat.getColor(getContext(), R.color.secondary_text_color))); + } moduleView.setOnClickListener(this); moduleView.setTag(module); return moduleView; diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java index d64cbca53..e948924d7 100644 --- a/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java +++ b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java @@ -32,6 +32,11 @@ public interface HistoryItemActionModule { @DrawableRes int getDrawableId(); + /** Returns true if tint can be applied to the drawable. */ + default boolean tintDrawable() { + return true; + } + /** @return true if the bottom sheet should close, false otherwise */ boolean onClick(); } diff --git a/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml index 9aee67937..063051947 100644 --- a/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml +++ b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml @@ -28,7 +28,6 @@ android:layout_height="@dimen/contact_actions_image_size" android:layout_marginStart="@dimen/contact_actions_image_margin" android:layout_marginEnd="@dimen/contact_actions_image_margin" - android:tint="@color/dialer_secondary_text_color" android:scaleType="center"/> <TextView diff --git a/java/com/android/dialer/oem/CequintCallerIdManager.java b/java/com/android/dialer/oem/CequintCallerIdManager.java index 55cafc15e..53f5352b2 100644 --- a/java/com/android/dialer/oem/CequintCallerIdManager.java +++ b/java/com/android/dialer/oem/CequintCallerIdManager.java @@ -24,6 +24,7 @@ import android.os.Build.VERSION_CODES; import android.support.annotation.AnyThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; @@ -45,7 +46,8 @@ import java.util.concurrent.ConcurrentHashMap; @TargetApi(VERSION_CODES.N) public class CequintCallerIdManager { - private static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled"; + @VisibleForTesting + public static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled"; private static final int CALLER_ID_LOOKUP_USER_PROVIDED_CID = 0x0001; private static final int CALLER_ID_LOOKUP_SYSTEM_PROVIDED_CID = 0x0002; @@ -53,36 +55,43 @@ public class CequintCallerIdManager { private static final String[] EMPTY_PROJECTION = new String[] {}; - // Column names in Cequint provider. - private static final String CITY_NAME = "cid_pCityName"; - private static final String STATE_NAME = "cid_pStateName"; - private static final String STATE_ABBR = "cid_pStateAbbr"; - private static final String COUNTRY_NAME = "cid_pCountryName"; - private static final String COMPANY = "cid_pCompany"; - private static final String NAME = "cid_pName"; - private static final String FIRST_NAME = "cid_pFirstName"; - private static final String LAST_NAME = "cid_pLastName"; - private static final String IMAGE = "cid_pLogo"; - private static final String DISPLAY_NAME = "cid_pDisplayName"; + /** Column names in Cequint content provider. */ + @VisibleForTesting + public static final class CequintColumnNames { + public static final String CITY_NAME = "cid_pCityName"; + public static final String STATE_NAME = "cid_pStateName"; + public static final String STATE_ABBR = "cid_pStateAbbr"; + public static final String COUNTRY_NAME = "cid_pCountryName"; + public static final String COMPANY = "cid_pCompany"; + public static final String NAME = "cid_pName"; + public static final String FIRST_NAME = "cid_pFirstName"; + public static final String LAST_NAME = "cid_pLastName"; + public static final String PHOTO_URI = "cid_pLogo"; + public static final String DISPLAY_NAME = "cid_pDisplayName"; + } private static boolean hasAlreadyCheckedCequintCallerIdPackage; private static String cequintProviderAuthority; - // TODO(wangqi): Revisit it and maybe remove it if it's not necessary. - private final ConcurrentHashMap<String, CequintCallerIdContact> callLogCache; + // TODO(a bug): Revisit it and maybe remove it if it's not necessary. + private final ConcurrentHashMap<String, CequintCallerIdContact> callLogCache = + new ConcurrentHashMap<>(); /** Cequint caller ID contact information. */ @AutoValue public abstract static class CequintCallerIdContact { + @Nullable public abstract String name(); /** * Description of the geolocation (e.g., "Mountain View, CA"), which is for display purpose * only. */ + @Nullable public abstract String geolocation(); + @Nullable public abstract String photoUri(); static Builder builder() { @@ -91,11 +100,11 @@ public class CequintCallerIdManager { @AutoValue.Builder abstract static class Builder { - abstract Builder setName(String name); + abstract Builder setName(@Nullable String name); - abstract Builder setGeolocation(String geolocation); + abstract Builder setGeolocation(@Nullable String geolocation); - abstract Builder setPhotoUri(String photoUri); + abstract Builder setPhotoUri(@Nullable String photoUri); abstract CequintCallerIdContact build(); } @@ -125,17 +134,14 @@ public class CequintCallerIdManager { return cequintProviderAuthority != null; } - public static CequintCallerIdManager createInstanceForCallLog() { - return new CequintCallerIdManager(); - } - + /** Returns a {@link CequintCallerIdContact} for a call. */ @WorkerThread @Nullable - public static CequintCallerIdContact getCequintCallerIdContactForInCall( + public static CequintCallerIdContact getCequintCallerIdContactForCall( Context context, String number, String cnapName, boolean isIncoming) { Assert.isWorkerThread(); LogUtil.d( - "CequintCallerIdManager.getCequintCallerIdContactForInCall", + "CequintCallerIdManager.getCequintCallerIdContactForCall", "number: %s, cnapName: %s, isIncoming: %b", LogUtil.sanitizePhoneNumber(number), LogUtil.sanitizePii(cnapName), @@ -151,29 +157,51 @@ public class CequintCallerIdManager { return lookup(context, getIncallLookupUri(), number, flags); } + /** + * Returns a cached {@link CequintCallerIdContact} associated with the provided number. If no + * contact can be found in the cache, look up the number using the Cequint content provider. + * + * @deprecated This method is for the old call log only. New code should use {@link + * #getCequintCallerIdContactForNumber(Context, String)}. + */ + @Deprecated @WorkerThread @Nullable - public CequintCallerIdContact getCequintCallerIdContact(Context context, String number) { + public CequintCallerIdContact getCachedCequintCallerIdContact(Context context, String number) { Assert.isWorkerThread(); LogUtil.d( - "CequintCallerIdManager.getCequintCallerIdContact", + "CequintCallerIdManager.getCachedCequintCallerIdContact", "number: %s", LogUtil.sanitizePhoneNumber(number)); if (callLogCache.containsKey(number)) { return callLogCache.get(number); } CequintCallerIdContact cequintCallerIdContact = - lookup( - context, - getLookupUri(), - PhoneNumberUtils.stripSeparators(number), - new String[] {"system"}); + getCequintCallerIdContactForNumber(context, number); if (cequintCallerIdContact != null) { callLogCache.put(number, cequintCallerIdContact); } return cequintCallerIdContact; } + /** + * Returns a {@link CequintCallerIdContact} associated with the provided number by looking it up + * using the Cequint content provider. + */ + @WorkerThread + @Nullable + public static CequintCallerIdContact getCequintCallerIdContactForNumber( + Context context, String number) { + Assert.isWorkerThread(); + LogUtil.d( + "CequintCallerIdManager.getCequintCallerIdContactForNumber", + "number: %s", + LogUtil.sanitizePhoneNumber(number)); + + return lookup( + context, getLookupUri(), PhoneNumberUtils.stripSeparators(number), new String[] {"system"}); + } + @WorkerThread @Nullable private static CequintCallerIdContact lookup( @@ -185,16 +213,17 @@ public class CequintCallerIdManager { try (Cursor cursor = context.getContentResolver().query(uri, EMPTY_PROJECTION, number, flags, null)) { if (cursor != null && cursor.moveToFirst()) { - String city = getString(cursor, cursor.getColumnIndex(CITY_NAME)); - String state = getString(cursor, cursor.getColumnIndex(STATE_NAME)); - String stateAbbr = getString(cursor, cursor.getColumnIndex(STATE_ABBR)); - String country = getString(cursor, cursor.getColumnIndex(COUNTRY_NAME)); - String company = getString(cursor, cursor.getColumnIndex(COMPANY)); - String name = getString(cursor, cursor.getColumnIndex(NAME)); - String firstName = getString(cursor, cursor.getColumnIndex(FIRST_NAME)); - String lastName = getString(cursor, cursor.getColumnIndex(LAST_NAME)); - String photoUri = getString(cursor, cursor.getColumnIndex(IMAGE)); - String displayName = getString(cursor, cursor.getColumnIndex(DISPLAY_NAME)); + String city = getString(cursor, cursor.getColumnIndex(CequintColumnNames.CITY_NAME)); + String state = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_NAME)); + String stateAbbr = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_ABBR)); + String country = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COUNTRY_NAME)); + String company = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COMPANY)); + String name = getString(cursor, cursor.getColumnIndex(CequintColumnNames.NAME)); + String firstName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.FIRST_NAME)); + String lastName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.LAST_NAME)); + String photoUri = getString(cursor, cursor.getColumnIndex(CequintColumnNames.PHOTO_URI)); + String displayName = + getString(cursor, cursor.getColumnIndex(CequintColumnNames.DISPLAY_NAME)); String contactName = TextUtils.isEmpty(displayName) @@ -293,8 +322,4 @@ public class CequintCallerIdManager { private static Uri getIncallLookupUri() { return Uri.parse("content://" + cequintProviderAuthority + "/incalllookup"); } - - private CequintCallerIdManager() { - callLogCache = new ConcurrentHashMap<>(); - } } diff --git a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java index ce2cd18ad..36d0be40f 100644 --- a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java +++ b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java @@ -18,33 +18,85 @@ package com.android.dialer.phonelookup.cequint; import android.content.Context; import android.telecom.Call; +import android.text.TextUtils; import com.android.dialer.DialerPhoneNumber; +import com.android.dialer.common.Assert; +import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; +import com.android.dialer.common.concurrent.Annotations.LightweightExecutor; +import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.oem.CequintCallerIdManager; +import com.android.dialer.oem.CequintCallerIdManager.CequintCallerIdContact; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.CequintInfo; +import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; +import com.android.dialer.telecom.TelecomCallUtil; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import javax.inject.Inject; /** PhoneLookup implementation for Cequint. */ public class CequintPhoneLookup implements PhoneLookup<CequintInfo> { + private final Context appContext; + private final ListeningExecutorService backgroundExecutorService; + private final ListeningExecutorService lightweightExecutorService; + @Inject - CequintPhoneLookup() {} + CequintPhoneLookup( + @ApplicationContext Context appContext, + @BackgroundExecutor ListeningExecutorService backgroundExecutorService, + @LightweightExecutor ListeningExecutorService lightweightExecutorService) { + this.appContext = appContext; + this.backgroundExecutorService = backgroundExecutorService; + this.lightweightExecutorService = lightweightExecutorService; + } @Override public ListenableFuture<CequintInfo> lookup(Context appContext, Call call) { - // TODO(a bug): Override the default implementation in the PhoneLookup interface - // as a Cequint lookup requires info in the provided call. - return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) { + return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + } + + ListenableFuture<DialerPhoneNumber> dialerPhoneNumberFuture = + backgroundExecutorService.submit( + () -> { + DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil(); + return dialerPhoneNumberUtil.parse( + TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext)); + }); + String callerDisplayName = call.getDetails().getCallerDisplayName(); + boolean isIncomingCall = (call.getState() == Call.STATE_RINGING); + + return Futures.transformAsync( + dialerPhoneNumberFuture, + dialerPhoneNumber -> + backgroundExecutorService.submit( + () -> + buildCequintInfo( + CequintCallerIdManager.getCequintCallerIdContactForCall( + appContext, + Assert.isNotNull(dialerPhoneNumber).getNormalizedNumber(), + callerDisplayName, + isIncomingCall))), + lightweightExecutorService); } @Override public ListenableFuture<CequintInfo> lookup(DialerPhoneNumber dialerPhoneNumber) { - // TODO(a bug): Implement this method. - return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) { + return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + } + + return backgroundExecutorService.submit( + () -> + buildCequintInfo( + CequintCallerIdManager.getCequintCallerIdContactForNumber( + appContext, dialerPhoneNumber.getNormalizedNumber()))); } @Override @@ -75,16 +127,38 @@ public class CequintPhoneLookup implements PhoneLookup<CequintInfo> { @Override public void registerContentObservers() { - // No content observers for Cequint info. + // No need to register a content observer as the Cequint content provider doesn't support batch + // queries. } @Override public void unregisterContentObservers() { - // No content observers for Cequint info. + // Nothing to be done as no content observer is registered. } @Override public ListenableFuture<Void> clearData() { return Futures.immediateFuture(null); } + + /** + * Builds a {@link CequintInfo} proto based on the given {@link CequintCallerIdContact} returned + * by {@link CequintCallerIdManager}. + */ + private static CequintInfo buildCequintInfo(CequintCallerIdContact cequintCallerIdContact) { + CequintInfo.Builder cequintInfoBuilder = CequintInfo.newBuilder(); + + // Every field in CequintCallerIdContact can be null. + if (!TextUtils.isEmpty(cequintCallerIdContact.name())) { + cequintInfoBuilder.setName(cequintCallerIdContact.name()); + } + if (!TextUtils.isEmpty(cequintCallerIdContact.geolocation())) { + cequintInfoBuilder.setGeolocation(cequintCallerIdContact.geolocation()); + } + if (!TextUtils.isEmpty(cequintCallerIdContact.photoUri())) { + cequintInfoBuilder.setPhotoUri(cequintCallerIdContact.photoUri()); + } + + return cequintInfoBuilder.build(); + } } diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java index 07aea749d..23ecc8301 100644 --- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java +++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java @@ -46,6 +46,7 @@ public final class PhoneLookupInfoConsolidator { NameSource.CP2_DEFAULT_DIRECTORY, NameSource.CP2_EXTENDED_DIRECTORY, NameSource.PEOPLE_API, + NameSource.CEQUINT, NameSource.CNAP }) @interface NameSource { @@ -53,7 +54,8 @@ public final class PhoneLookupInfoConsolidator { int CP2_DEFAULT_DIRECTORY = 1; int CP2_EXTENDED_DIRECTORY = 2; int PEOPLE_API = 3; - int CNAP = 4; + int CEQUINT = 4; + int CNAP = 5; } /** @@ -78,6 +80,7 @@ public final class PhoneLookupInfoConsolidator { NameSource.CP2_DEFAULT_DIRECTORY, NameSource.CP2_EXTENDED_DIRECTORY, NameSource.PEOPLE_API, + NameSource.CEQUINT, NameSource.CNAP); private final @NameSource int nameSource; @@ -106,6 +109,8 @@ public final class PhoneLookupInfoConsolidator { return ContactSource.Type.SOURCE_TYPE_EXTENDED; case NameSource.PEOPLE_API: return getRefinedPeopleApiSource(); + case NameSource.CEQUINT: + return ContactSource.Type.SOURCE_TYPE_CEQUINT_CALLER_ID; case NameSource.CNAP: return ContactSource.Type.SOURCE_TYPE_CNAP; case NameSource.NONE: @@ -146,6 +151,8 @@ public final class PhoneLookupInfoConsolidator { return Assert.isNotNull(firstExtendedCp2Contact).getName(); case NameSource.PEOPLE_API: return phoneLookupInfo.getPeopleApiInfo().getDisplayName(); + case NameSource.CEQUINT: + return phoneLookupInfo.getCequintInfo().getName(); case NameSource.CNAP: return phoneLookupInfo.getCnapInfo().getName(); case NameSource.NONE: @@ -170,6 +177,7 @@ public final class PhoneLookupInfoConsolidator { case NameSource.CP2_EXTENDED_DIRECTORY: return Assert.isNotNull(firstExtendedCp2Contact).getPhotoThumbnailUri(); case NameSource.PEOPLE_API: + case NameSource.CEQUINT: case NameSource.CNAP: case NameSource.NONE: return ""; @@ -192,6 +200,8 @@ public final class PhoneLookupInfoConsolidator { return Assert.isNotNull(firstDefaultCp2Contact).getPhotoUri(); case NameSource.CP2_EXTENDED_DIRECTORY: return Assert.isNotNull(firstExtendedCp2Contact).getPhotoUri(); + case NameSource.CEQUINT: + return phoneLookupInfo.getCequintInfo().getPhotoUri(); case NameSource.PEOPLE_API: case NameSource.CNAP: case NameSource.NONE: @@ -213,6 +223,7 @@ public final class PhoneLookupInfoConsolidator { case NameSource.CP2_EXTENDED_DIRECTORY: return Math.max(Assert.isNotNull(firstExtendedCp2Contact).getPhotoId(), 0); case NameSource.PEOPLE_API: + case NameSource.CEQUINT: case NameSource.CNAP: case NameSource.NONE: return 0; @@ -235,6 +246,7 @@ public final class PhoneLookupInfoConsolidator { return Assert.isNotNull(firstExtendedCp2Contact).getLookupUri(); case NameSource.PEOPLE_API: return Assert.isNotNull(phoneLookupInfo.getPeopleApiInfo().getLookupUri()); + case NameSource.CEQUINT: case NameSource.CNAP: case NameSource.NONE: return ""; @@ -259,6 +271,30 @@ public final class PhoneLookupInfoConsolidator { case NameSource.CP2_EXTENDED_DIRECTORY: return Assert.isNotNull(firstExtendedCp2Contact).getLabel(); case NameSource.PEOPLE_API: + case NameSource.CEQUINT: + case NameSource.CNAP: + case NameSource.NONE: + return ""; + default: + throw Assert.createUnsupportedOperationFailException( + String.format("Unsupported name source: %s", nameSource)); + } + } + + /** + * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method + * returns the number's geolocation (which is for display purpose only). + * + * <p>If no geolocation can be obtained from the {@link PhoneLookupInfo}, an empty string will be + * returned. + */ + public String getGeolocation() { + switch (nameSource) { + case NameSource.CEQUINT: + return phoneLookupInfo.getCequintInfo().getGeolocation(); + case NameSource.CP2_DEFAULT_DIRECTORY: + case NameSource.CP2_EXTENDED_DIRECTORY: + case NameSource.PEOPLE_API: case NameSource.CNAP: case NameSource.NONE: return ""; @@ -320,6 +356,7 @@ public final class PhoneLookupInfoConsolidator { switch (nameSource) { case NameSource.CP2_DEFAULT_DIRECTORY: case NameSource.CP2_EXTENDED_DIRECTORY: + case NameSource.CEQUINT: case NameSource.CNAP: case NameSource.NONE: return false; @@ -343,6 +380,7 @@ public final class PhoneLookupInfoConsolidator { return Assert.isNotNull(firstDefaultCp2Contact).getCanSupportCarrierVideoCall(); case NameSource.CP2_EXTENDED_DIRECTORY: case NameSource.PEOPLE_API: + case NameSource.CEQUINT: case NameSource.CNAP: case NameSource.NONE: return false; @@ -396,6 +434,11 @@ public final class PhoneLookupInfoConsolidator { return NameSource.PEOPLE_API; } break; + case NameSource.CEQUINT: + if (!phoneLookupInfo.getCequintInfo().getName().isEmpty()) { + return NameSource.CEQUINT; + } + break; case NameSource.CNAP: if (!phoneLookupInfo.getCnapInfo().getName().isEmpty()) { return NameSource.CNAP; diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java index 4302436a7..6179b5dcf 100644 --- a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java +++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java @@ -621,7 +621,7 @@ public class ContactInfoHelper { return; } CequintCallerIdContact cequintCallerIdContact = - cequintCallerIdManager.getCequintCallerIdContact(context, number); + cequintCallerIdManager.getCachedCequintCallerIdContact(context, number); if (cequintCallerIdContact == null) { return; } diff --git a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java index 891ac44ad..6a8cde864 100644 --- a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java +++ b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java @@ -34,14 +34,14 @@ import com.android.dialer.logging.Logger; import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; import com.android.dialer.spam.SpamSettings; -import com.android.dialer.spam.SpamSettings.ModifySettingListener; +import com.android.dialer.spam.promo.SpamBlockingPromoDialogFragment.OnEnableListener; /** Helper class for showing spam blocking on-boarding promotions. */ public class SpamBlockingPromoHelper { static final String SPAM_BLOCKING_PROMO_PERIOD_MILLIS = "spam_blocking_promo_period_millis"; static final String SPAM_BLOCKING_PROMO_LAST_SHOW_MILLIS = "spam_blocking_promo_last_show_millis"; - static final String ENABLE_SPAM_BLOCKING_PROMO = "enable_spam_blocking_promo"; + public static final String ENABLE_SPAM_BLOCKING_PROMO = "enable_spam_blocking_promo"; private final Context context; private final SpamSettings spamSettings; @@ -81,33 +81,15 @@ public class SpamBlockingPromoHelper { * Shows a spam blocking promo dialog. * * @param fragmentManager the fragment manager to show the dialog. - * @param modifySettingListener the listener called after spam blocking setting is modified. + * @param onEnableListener the listener called when enable button is clicked. * @param onDismissListener the listener called when the dialog is dismissed. */ public void showSpamBlockingPromoDialog( FragmentManager fragmentManager, - ModifySettingListener modifySettingListener, + OnEnableListener onEnableListener, OnDismissListener onDismissListener) { updateLastShowSpamTimestamp(); - Logger.get(context).logImpression(DialerImpression.Type.SPAM_BLOCKING_CALL_LOG_PROMO_SHOWN); - SpamBlockingPromoDialogFragment.newInstance( - () -> { - Logger.get(context) - .logImpression( - DialerImpression.Type.SPAM_BLOCKING_ENABLED_THROUGH_CALL_LOG_PROMO); - spamSettings.modifySpamBlockingSetting( - true, - success -> { - if (!success) { - Logger.get(context) - .logImpression( - DialerImpression.Type - .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_CALL_LOG_PROMO); - } - modifySettingListener.onComplete(success); - }); - }, - onDismissListener) + SpamBlockingPromoDialogFragment.newInstance(onEnableListener, onDismissListener) .show(fragmentManager, SpamBlockingPromoDialogFragment.SPAM_BLOCKING_PROMO_DIALOG_TAG); } diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java index b76db1cf3..018f97888 100644 --- a/java/com/android/dialer/speeddial/SpeedDialFragment.java +++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java @@ -16,6 +16,7 @@ package com.android.dialer.speeddial; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -41,6 +42,12 @@ import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.SupportUiListener; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.duo.DuoComponent; +import com.android.dialer.historyitemactions.DividerModule; +import com.android.dialer.historyitemactions.HistoryItemActionBottomSheet; +import com.android.dialer.historyitemactions.HistoryItemActionModule; +import com.android.dialer.historyitemactions.HistoryItemBottomSheetHeaderInfo; +import com.android.dialer.historyitemactions.IntentModule; +import com.android.dialer.historyitemactions.SharedModules; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.precall.PreCall; @@ -54,8 +61,11 @@ import com.android.dialer.speeddial.draghelper.SpeedDialLayoutManager; import com.android.dialer.speeddial.loader.SpeedDialUiItem; import com.android.dialer.speeddial.loader.UiItemLoaderComponent; import com.android.dialer.util.IntentUtil; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; +import java.util.ArrayList; +import java.util.List; /** * Fragment for displaying: @@ -301,8 +311,61 @@ public class SpeedDialFragment extends Fragment { private final class SpeedDialSuggestedListener implements SuggestedContactsListener { @Override - public void onOverFlowMenuClicked(SpeedDialUiItem speedDialUiItem) { - // TODO(calderwoodra) show overflow menu for suggested contacts + public void onOverFlowMenuClicked( + SpeedDialUiItem speedDialUiItem, HistoryItemBottomSheetHeaderInfo headerInfo) { + List<HistoryItemActionModule> modules = new ArrayList<>(); + Channel defaultChannel = speedDialUiItem.defaultChannel(); + + // Add voice call module + Channel voiceChannel = speedDialUiItem.getDeterministicVoiceChannel(); + if (voiceChannel != null) { + modules.add( + IntentModule.newCallModule( + getContext(), + new CallIntentBuilder(voiceChannel.number(), CallInitiationType.Type.SPEED_DIAL) + .setAllowAssistedDial(true))); + } else { + modules.add(new DisambigDialogModule(speedDialUiItem, /* isVideo = */ false)); + } + + // Add video if we can determine the correct channel + Channel videoChannel = speedDialUiItem.getDeterministicVideoChannel(); + if (videoChannel != null) { + modules.add( + IntentModule.newCallModule( + getContext(), + new CallIntentBuilder(videoChannel.number(), CallInitiationType.Type.SPEED_DIAL) + .setIsVideoCall(true) + .setAllowAssistedDial(true))); + } else if (speedDialUiItem.hasVideoChannels()) { + modules.add(new DisambigDialogModule(speedDialUiItem, /* isVideo = */ true)); + } + + // Add sms module + Optional<HistoryItemActionModule> smsModule = + SharedModules.createModuleForSendingTextMessage( + getContext(), defaultChannel.number(), false); + if (smsModule.isPresent()) { + modules.add(smsModule.get()); + } + + modules.add(new DividerModule()); + + // TODO(calderwoodra): add to favorites module + // TODO(calderwoodra): remove from strequent module + + // Contact info module + modules.add( + new ContactInfoModule( + getContext(), + new Intent( + Intent.ACTION_VIEW, + Uri.withAppendedPath( + Contacts.CONTENT_URI, String.valueOf(speedDialUiItem.contactId()))), + R.string.contact_menu_contact_info, + R.drawable.context_menu_contact_icon)); + + HistoryItemActionBottomSheet.show(getContext(), headerInfo, modules); } @Override @@ -321,6 +384,53 @@ public class SpeedDialFragment extends Fragment { new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL) .setIsVideoCall(channel.isVideoTechnology())); } + + private final class ContactInfoModule extends IntentModule { + + public ContactInfoModule(Context context, Intent intent, int text, int image) { + super(context, intent, text, image); + } + + @Override + public boolean tintDrawable() { + return false; + } + } + + private final class DisambigDialogModule implements HistoryItemActionModule { + + private final SpeedDialUiItem speedDialUiItem; + private final boolean isVideo; + + DisambigDialogModule(SpeedDialUiItem speedDialUiItem, boolean isVideo) { + this.speedDialUiItem = speedDialUiItem; + this.isVideo = isVideo; + } + + @Override + public int getStringId() { + if (isVideo) { + return R.string.contact_menu_video_call; + } else { + return R.string.contact_menu_voice_call; + } + } + + @Override + public int getDrawableId() { + if (isVideo) { + return R.drawable.quantum_ic_videocam_vd_theme_24; + } else { + return R.drawable.quantum_ic_phone_vd_theme_24; + } + } + + @Override + public boolean onClick() { + DisambigDialog.show(speedDialUiItem, getChildFragmentManager()); + return true; + } + } } private static final class SpeedDialContextMenuItemListener implements ContextMenuItemListener { diff --git a/java/com/android/dialer/speeddial/SuggestionViewHolder.java b/java/com/android/dialer/speeddial/SuggestionViewHolder.java index 546ffbdff..578e0b328 100644 --- a/java/com/android/dialer/speeddial/SuggestionViewHolder.java +++ b/java/com/android/dialer/speeddial/SuggestionViewHolder.java @@ -17,27 +17,25 @@ package com.android.dialer.speeddial; import android.content.Context; -import android.provider.ContactsContract.Contacts; 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.dialer.common.Assert; -import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent; -import com.android.dialer.glidephotomanager.PhotoInfo; +import com.android.dialer.historyitemactions.HistoryItemBottomSheetHeaderInfo; import com.android.dialer.location.GeoUtil; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; import com.android.dialer.speeddial.loader.SpeedDialUiItem; +import com.android.dialer.widget.ContactPhotoView; /** ViewHolder for displaying suggested contacts in {@link SpeedDialFragment}. */ public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnClickListener { private final SuggestedContactsListener listener; - private final QuickContactBadge photoView; + private final ContactPhotoView photoView; private final TextView nameOrNumberView; private final TextView numberView; @@ -71,34 +69,31 @@ public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnC nameOrNumberView.setText(speedDialUiItem.name()); numberView.setText(secondaryInfo); - GlidePhotoManagerComponent.get(context) - .glidePhotoManager() - .loadQuickContactBadge( - photoView, - PhotoInfo.newBuilder() - .setPhotoId(speedDialUiItem.photoId()) - .setPhotoUri(speedDialUiItem.photoUri()) - .setName(speedDialUiItem.name()) - .setIsVideo(speedDialUiItem.defaultChannel().isVideoTechnology()) - .setLookupUri( - Contacts.getLookupUri(speedDialUiItem.contactId(), speedDialUiItem.lookupKey()) - .toString()) - .build()); + photoView.setPhoto(speedDialUiItem.getPhotoInfo()); } @Override public void onClick(View v) { if (v.getId() == R.id.overflow) { - listener.onOverFlowMenuClicked(speedDialUiItem); + listener.onOverFlowMenuClicked(speedDialUiItem, getHeaderInfo()); } else { listener.onRowClicked(speedDialUiItem.defaultChannel()); } } + private HistoryItemBottomSheetHeaderInfo getHeaderInfo() { + return HistoryItemBottomSheetHeaderInfo.newBuilder() + .setPhotoInfo(speedDialUiItem.getPhotoInfo()) + .setPrimaryText(nameOrNumberView.getText().toString()) + .setSecondaryText(numberView.getText().toString()) + .build(); + } + /** Listener/Callback for {@link SuggestionViewHolder} parents. */ public interface SuggestedContactsListener { - void onOverFlowMenuClicked(SpeedDialUiItem speedDialUiItem); + void onOverFlowMenuClicked( + SpeedDialUiItem speedDialUiItem, HistoryItemBottomSheetHeaderInfo headerInfo); /** Called when a suggested contact is clicked. */ void onRowClicked(Channel channel); diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java index a2bdfb89a..c5a3ea3ed 100644 --- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java +++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java @@ -18,10 +18,12 @@ package com.android.dialer.speeddial.loader; import android.database.Cursor; import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.support.annotation.Nullable; import android.text.TextUtils; import com.android.dialer.common.Assert; +import com.android.dialer.glidephotomanager.PhotoInfo; import com.android.dialer.speeddial.database.SpeedDialEntry; import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; import com.google.auto.value.AutoValue; @@ -139,6 +141,16 @@ public abstract class SpeedDialUiItem { return builder.build(); } + public PhotoInfo getPhotoInfo() { + return PhotoInfo.newBuilder() + .setPhotoId(photoId()) + .setPhotoUri(photoUri()) + .setName(name()) + .setIsVideo(defaultChannel() != null && defaultChannel().isVideoTechnology()) + .setLookupUri(Contacts.getLookupUri(contactId(), lookupKey()).toString()) + .build(); + } + public SpeedDialEntry buildSpeedDialEntry() { return SpeedDialEntry.builder() .setId(speedDialEntryId()) diff --git a/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml index ff95b5906..868606065 100644 --- a/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml +++ b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml @@ -21,7 +21,7 @@ android:minHeight="72dp" android:background="?android:attr/selectableItemBackground"> - <QuickContactBadge + <com.android.dialer.widget.ContactPhotoView android:id="@+id/avatar" android:layout_width="48dp" android:layout_height="48dp" @@ -59,5 +59,6 @@ android:scaleType="center" android:tint="@color/secondary_text_color" android:src="@drawable/quantum_ic_more_vert_white_24" - android:background="?android:selectableItemBackgroundBorderless"/> + android:background="?android:selectableItemBackgroundBorderless" + android:contentDescription="@string/content_description_overflow"/> </RelativeLayout>
\ No newline at end of file diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java index eefd4833c..81c7b724d 100644 --- a/java/com/android/incallui/ContactInfoCache.java +++ b/java/com/android/incallui/ContactInfoCache.java @@ -514,7 +514,7 @@ public class ContactInfoCache implements OnImageLoadCompleteListener { return; } CequintCallerIdContact cequintCallerIdContact = - CequintCallerIdManager.getCequintCallerIdContactForInCall( + CequintCallerIdManager.getCequintCallerIdContactForCall( context, callerInfo.phoneNumber, cnapName, isIncoming); if (cequintCallerIdContact == null) { diff --git a/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java b/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java new file mode 100644 index 000000000..8f081bebf --- /dev/null +++ b/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incallui.rtt.impl; + +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.view.View; + +/** ViewHolder class for RTT advisory text. */ +public class AdvisoryViewHolder extends ViewHolder { + + public AdvisoryViewHolder(@NonNull View itemView) { + super(itemView); + } +} diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java index 692266335..f1cde759c 100644 --- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java +++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java @@ -18,8 +18,10 @@ package com.android.incallui.rtt.impl; import android.content.Context; import android.graphics.drawable.Drawable; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -27,11 +29,29 @@ import android.view.ViewGroup; import com.android.dialer.common.LogUtil; import com.android.dialer.rtt.RttTranscript; import com.android.dialer.rtt.RttTranscriptMessage; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; /** Adapter class for holding RTT chat data. */ -public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolder> { +public class RttChatAdapter extends RecyclerView.Adapter<ViewHolder> { + + /** IntDef for the different types of rows that can be shown in the call log. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + RowType.ADVISORY, + RowType.MESSAGE, + }) + @interface RowType { + /** The transcript advisory message. */ + int ADVISORY = 1; + + /** RTT chat message. */ + int MESSAGE = 2; + } + + private static final int POSITION_ADVISORY = 0; private Drawable avatarDrawable; @@ -45,6 +65,7 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde private List<RttChatMessage> rttMessages = new ArrayList<>(); private int lastIndexOfLocalMessage = -1; private final MessageListener messageListener; + private boolean shouldShowAdvisory; RttChatAdapter(Context context, MessageListener listener) { this.context = context; @@ -52,29 +73,54 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde } @Override - public RttChatMessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public ViewHolder onCreateViewHolder(ViewGroup parent, @RowType int viewType) { LayoutInflater layoutInflater = LayoutInflater.from(context); - View view = layoutInflater.inflate(R.layout.rtt_chat_list_item, parent, false); - return new RttChatMessageViewHolder(view); + switch (viewType) { + case RowType.ADVISORY: + View view = layoutInflater.inflate(R.layout.rtt_transcript_advisory, parent, false); + return new AdvisoryViewHolder(view); + case RowType.MESSAGE: + view = layoutInflater.inflate(R.layout.rtt_chat_list_item, parent, false); + return new RttChatMessageViewHolder(view); + default: + throw new RuntimeException("Unknown row type."); + } } @Override public int getItemViewType(int position) { - return super.getItemViewType(position); + if (shouldShowAdvisory && position == POSITION_ADVISORY) { + return RowType.ADVISORY; + } else { + return RowType.MESSAGE; + } } @Override - public void onBindViewHolder(RttChatMessageViewHolder rttChatMessageViewHolder, int i) { - boolean isSameGroup = false; - if (i > 0) { - isSameGroup = rttMessages.get(i).isRemote == rttMessages.get(i - 1).isRemote; + public void onBindViewHolder(ViewHolder viewHolder, int itemPosition) { + switch (getItemViewType(itemPosition)) { + case RowType.ADVISORY: + return; + case RowType.MESSAGE: + RttChatMessageViewHolder rttChatMessageViewHolder = (RttChatMessageViewHolder) viewHolder; + int messagePosition = toMessagePosition(itemPosition); + boolean isSameGroup = false; + if (messagePosition > 0) { + isSameGroup = + rttMessages.get(messagePosition).isRemote + == rttMessages.get(messagePosition - 1).isRemote; + } + rttChatMessageViewHolder.setMessage( + rttMessages.get(messagePosition), isSameGroup, avatarDrawable); + return; + default: + throw new RuntimeException("Unknown row type."); } - rttChatMessageViewHolder.setMessage(rttMessages.get(i), isSameGroup, avatarDrawable); } @Override public int getItemCount() { - return rttMessages.size(); + return shouldShowAdvisory ? rttMessages.size() + 1 : rttMessages.size(); } private void updateCurrentLocalMessage(String newMessage) { @@ -96,11 +142,31 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde notifyItemRemoved(lastIndexOfLocalMessage); lastIndexOfLocalMessage = -1; } else { - notifyItemChanged(lastIndexOfLocalMessage); + notifyItemChanged(toItemPosition(lastIndexOfLocalMessage)); } } } + private int toMessagePosition(int itemPosition) { + if (shouldShowAdvisory) { + return itemPosition - 1; + } else { + return itemPosition; + } + } + + // Converts position in message list into item position in adapter. + private int toItemPosition(int messagePosition) { + if (messagePosition < 0) { + return messagePosition; + } + if (shouldShowAdvisory) { + return messagePosition + 1; + } else { + return messagePosition; + } + } + private void updateCurrentRemoteMessage(String newMessage) { RttChatMessage.updateRemoteRttChatMessage(rttMessages, newMessage); lastIndexOfLocalMessage = RttChatMessage.getLastIndexLocalMessage(rttMessages); @@ -110,14 +176,14 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde void addLocalMessage(String message) { updateCurrentLocalMessage(message); if (messageListener != null) { - messageListener.onUpdateLocalMessage(lastIndexOfLocalMessage); + messageListener.onUpdateLocalMessage(toItemPosition(lastIndexOfLocalMessage)); } } void submitLocalMessage() { LogUtil.enterBlock("RttChatAdapater.submitLocalMessage"); rttMessages.get(lastIndexOfLocalMessage).finish(); - notifyItemChanged(lastIndexOfLocalMessage); + notifyItemChanged(toItemPosition(lastIndexOfLocalMessage)); lastIndexOfLocalMessage = -1; } @@ -139,10 +205,21 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde } updateCurrentRemoteMessage(message); if (messageListener != null) { - messageListener.onUpdateRemoteMessage(RttChatMessage.getLastIndexRemoteMessage(rttMessages)); + messageListener.onUpdateRemoteMessage( + toItemPosition(RttChatMessage.getLastIndexRemoteMessage(rttMessages))); } } + void hideAdvisory() { + shouldShowAdvisory = false; + notifyItemRemoved(POSITION_ADVISORY); + } + + void showAdvisory() { + shouldShowAdvisory = true; + notifyItemInserted(POSITION_ADVISORY); + } + /** * Retrieve last local message and update the index. This is used when deleting to previous * message bubble. diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index 47036cd43..13e013fd3 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -467,6 +467,7 @@ public class RttChatFragment extends Fragment if (editText.requestFocus()) { UiUtil.openKeyboardFrom(getContext(), editText); } + adapter.showAdvisory(); } if (primaryCallState.state() == State.DIALING) { showWaitingForJoinBanner(); diff --git a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml index f995185b1..cff2b3f38 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml +++ b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml @@ -23,7 +23,7 @@ android:id="@+id/rtt_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="70dp" + android:paddingTop="56dp" android:paddingBottom="70dp" android:clipToPadding="false"/> diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml index 4b03ad8d5..8a5bba28d 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml +++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml @@ -19,30 +19,30 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> - <RelativeLayout + <LinearLayout android:layout_width="match_parent" - android:layout_height="?android:attr/actionBarSize" + android:layout_height="@dimen/rtt_banner_height" android:background="#F305228F" - android:elevation="3dp"> + android:elevation="3dp" + android:orientation="horizontal"> <ImageButton android:id="@+id/rtt_end_call_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginStart="16dp" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" + android:layout_width="@dimen/rtt_banner_height" + android:layout_height="@dimen/rtt_banner_height" + android:layout_marginStart="4dp" + android:layout_marginEnd="4dp" + android:padding="@dimen/rtt_banner_button_padding" android:background="@android:color/transparent" android:contentDescription="@string/incall_content_description_end_call" android:scaleType="fitXY" android:src="@drawable/quantum_ic_call_end_vd_theme_24" android:tint="#FFDF0000"/> <LinearLayout - android:layout_width="260dp" + android:layout_width="0dp" android:layout_height="match_parent" + android:layout_weight="1" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" - android:layout_marginStart="32dp" - android:layout_toEndOf="@id/rtt_end_call_button" android:orientation="vertical"> <TextView android:id="@+id/rtt_name_or_number" @@ -67,18 +67,17 @@ </LinearLayout> <ImageButton android:id="@+id/rtt_overflow_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginEnd="12dp" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" + android:layout_width="@dimen/rtt_banner_height" + android:layout_height="@dimen/rtt_banner_height" + android:layout_marginStart="8dp" + android:padding="@dimen/rtt_banner_button_padding" android:background="@android:color/transparent" android:contentDescription="@string/content_description_overflow" android:scaleType="fitXY" android:src="@drawable/quantum_ic_more_vert_vd_theme_24" android:tint="#FFFFFF"/> - </RelativeLayout> + </LinearLayout> <FrameLayout android:id="@id/rtt_on_hold_banner" android:layout_width="match_parent" diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml new file mode 100644 index 000000000..a2cf3e74f --- /dev/null +++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="24dp" + android:paddingBottom="16dp" + android:orientation="vertical"> + <ImageView + android:layout_width="16dp" + android:layout_height="16dp" + android:layout_gravity="center_horizontal" + android:src="@drawable/quantum_ic_question_answer_vd_theme_24" + android:tint="#DEFFFFFF" + android:tintMode="src_in"/> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="4dp" + android:paddingBottom="9dp" + android:paddingStart="64dp" + android:paddingEnd="64dp" + android:gravity="center_horizontal" + android:text="@string/rtt_transcript_advisory" + android:textColor="#FFFFFF" + android:textSize="12sp"/> +</LinearLayout>
\ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/dimens.xml b/java/com/android/incallui/rtt/impl/res/values/dimens.xml index c3d28da77..cab9da00c 100644 --- a/java/com/android/incallui/rtt/impl/res/values/dimens.xml +++ b/java/com/android/incallui/rtt/impl/res/values/dimens.xml @@ -20,4 +20,7 @@ <dimen name="rtt_overflow_menu_width">180dp</dimen> <dimen name="rtt_overflow_menu_elevation">8dp</dimen> <dimen name="rtt_avatar_size">40dp</dimen> + <dimen name="rtt_banner_height">56dp</dimen> + <!-- This is used to control image size inside the button. --> + <dimen name="rtt_banner_button_padding">12dp</dimen> </resources> diff --git a/java/com/android/incallui/rtt/impl/res/values/strings.xml b/java/com/android/incallui/rtt/impl/res/values/strings.xml index 1d09f5446..462eea563 100644 --- a/java/com/android/incallui/rtt/impl/res/values/strings.xml +++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml @@ -30,4 +30,7 @@ <!-- Text for status banner. [CHAR LIMIT=100] --> <string name="rtt_status_banner_text">Waiting for <xliff:g id="name">%s</xliff:g> to join RTT call…</string> + <!-- Text for RTT transcript advisory. [CHAR LIMIT=NONE] --> + <string name="rtt_transcript_advisory">The other party can see you typing. Transcripts stored on your device in the call history.</string> + </resources>
\ No newline at end of file diff --git a/java/com/android/incallui/spam/SpamNotificationActivity.java b/java/com/android/incallui/spam/SpamNotificationActivity.java index c04a071a1..e10dea381 100644 --- a/java/com/android/incallui/spam/SpamNotificationActivity.java +++ b/java/com/android/incallui/spam/SpamNotificationActivity.java @@ -43,6 +43,7 @@ import com.android.dialer.logging.ReportingLocation; import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.spam.SpamComponent; +import com.android.dialer.spam.SpamSettings; import com.android.dialer.spam.promo.SpamBlockingPromoHelper; import com.android.incallui.call.DialerCall; @@ -86,6 +87,8 @@ public class SpamNotificationActivity extends FragmentActivity { } }; private FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler; + private SpamSettings spamSettings; + private SpamBlockingPromoHelper spamBlockingPromoHelper; /** * Creates an intent to start this activity. @@ -160,6 +163,8 @@ public class SpamNotificationActivity extends FragmentActivity { super.onCreate(savedInstanceState); setFinishOnTouchOutside(true); filteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(this); + spamSettings = SpamComponent.get(this).spamSettings(); + spamBlockingPromoHelper = new SpamBlockingPromoHelper(getApplicationContext(), spamSettings); cancelNotification(); } @@ -417,7 +422,7 @@ public class SpamNotificationActivity extends FragmentActivity { dismiss(); spamNotificationActivity.maybeShowBlockReportSpamDialog( number, contactLookupResultType); - spamNotificationActivity.showSpamBlockingPromoDialog(); + spamNotificationActivity.maybeShowSpamBlockingPromoAndFinish(); } }) .setNegativeButton( @@ -515,23 +520,43 @@ public class SpamNotificationActivity extends FragmentActivity { dismiss(); spamNotificationActivity.maybeShowBlockReportSpamDialog( number, contactLookupResultType); + spamNotificationActivity.maybeShowSpamBlockingPromoAndFinish(); } }) .create(); } } - private void showSpamBlockingPromoDialog() { - SpamBlockingPromoHelper spamBlockingPromoHelper = - new SpamBlockingPromoHelper( - getApplicationContext(), SpamComponent.get(this).spamSettings()); + private void maybeShowSpamBlockingPromoAndFinish() { if (!spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) { finish(); - } else { - spamBlockingPromoHelper.showSpamBlockingPromoDialog( - getFragmentManager(), - success -> spamBlockingPromoHelper.showModifySettingOnCompleteToast(success), - dialog -> finish()); + return; } + Logger.get(this) + .logImpression(DialerImpression.Type.SPAM_BLOCKING_AFTER_CALL_NOTIFICATION_PROMO_SHOWN); + showSpamBlockingPromoDialog(); + } + + private void showSpamBlockingPromoDialog() { + spamBlockingPromoHelper.showSpamBlockingPromoDialog( + getFragmentManager(), + () -> { + Logger.get(this) + .logImpression( + DialerImpression.Type + .SPAM_BLOCKING_ENABLED_THROUGH_AFTER_CALL_NOTIFICATION_PROMO); + spamSettings.modifySpamBlockingSetting( + true, + success -> { + if (!success) { + Logger.get(this) + .logImpression( + DialerImpression.Type + .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_AFTER_CALL_NOTIFICATION_PROMO); + } + spamBlockingPromoHelper.showModifySettingOnCompleteToast(success); + }); + }, + dialog -> finish()); } } |