diff options
Diffstat (limited to 'java/com/android/dialer')
26 files changed, 302 insertions, 96 deletions
diff --git a/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java index b1ad0d9a2..78ec7a695 100644 --- a/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java +++ b/java/com/android/dialer/app/calllog/CallLogAsyncTaskUtil.java @@ -66,9 +66,8 @@ public class CallLogAsyncTaskUtil { .update(voicemailUri, values, Voicemails.IS_READ + " = 0", null) > 0) { uploadVoicemailLocalChangesToServer(context); + CallLogNotificationsService.markAllNewVoicemailsAsOld(context); } - - CallLogNotificationsService.markAllNewVoicemailsAsOld(context); return null; } }); diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java index 23a00d745..f7ea63c90 100644 --- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java +++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java @@ -551,12 +551,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder private void bindActionButtons() { boolean canPlaceCallToNumber = PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation); + // Hide the call buttons by default. We then set it to be visible when appropriate below. + // This saves us having to remember to set it to GONE in multiple places. + callButtonView.setVisibility(View.GONE); + videoCallButtonView.setVisibility(View.GONE); + if (isFullyUndialableVoicemail()) { // Sometimes the voicemail server will report the message is from some non phone number // source. If the number does not contains any dialable digit treat it as it is from a unknown // number, remove all action buttons but still show the voicemail playback layout. - callButtonView.setVisibility(View.GONE); - videoCallButtonView.setVisibility(View.GONE); detailsButtonView.setVisibility(View.GONE); createNewContactButtonView.setVisibility(View.GONE); addToExistingContactButtonView.setVisibility(View.GONE); @@ -585,11 +588,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder ((TextView) callButtonView.findViewById(R.id.call_type_or_location_text)); if (canPlaceCallToNumber) { - // Set up the call button but hide it by default (the primary action is to call so it is - // redundant). We then set it to be visible when appropriate below. This saves us having to - // remember to set it to GONE in multiple places. callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number)); - callButtonView.setVisibility(View.GONE); callTypeOrLocationView.setVisibility(View.GONE); } @@ -617,8 +616,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder } else if (lightbringerReady) { videoCallButtonView.setTag(IntentProvider.getLightbringerIntentProvider(number)); videoCallButtonView.setVisibility(View.VISIBLE); - } else { - videoCallButtonView.setVisibility(View.GONE); } // For voicemail calls, show the voicemail playback layout; hide otherwise. @@ -947,12 +944,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder String accountLabel = mCallLogCache.getAccountLabel(accountHandle); if (!TextUtils.isEmpty(accountLabel)) { SimDetails.Builder simDetails = SimDetails.newBuilder().setNetwork(accountLabel); - int color = mCallLogCache.getAccountColor(accountHandle); - if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) { - simDetails.setColor(R.color.secondary_text_color); - } else { - simDetails.setColor(color); - } + simDetails.setColor(mCallLogCache.getAccountColor(accountHandle)); contact.setSimDetails(simDetails.build()); } return contact.build(); diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java index 84aedf880..0490b9932 100644 --- a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java +++ b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java @@ -25,6 +25,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.app.voicemail.LegacyVoicemailNotificationReceiver; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; @@ -48,7 +50,8 @@ import com.android.dialer.util.PermissionsUtil; */ public class CallLogNotificationsService extends IntentService { - private static final String ACTION_MARK_ALL_NEW_VOICEMAILS_AS_OLD = + @VisibleForTesting + static final String ACTION_MARK_ALL_NEW_VOICEMAILS_AS_OLD = "com.android.dialer.calllog.ACTION_MARK_ALL_NEW_VOICEMAILS_AS_OLD"; private static final String ACTION_MARK_SINGLE_NEW_VOICEMAIL_AS_OLD = @@ -68,6 +71,10 @@ public class CallLogNotificationsService extends IntentService { public static final String ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION = "com.android.dialer.calllog.CALL_BACK_FROM_MISSED_CALL_NOTIFICATION"; + /** Action mark legacy voicemail as dismissed. */ + public static final String ACTION_LEGACY_VOICEMAIL_DISMISSED = + "com.android.dialer.calllog.ACTION_LEGACY_VOICEMAIL_DISMISSED"; + /** * Extra to be included with {@link #ACTION_INCOMING_POST_CALL} to represent a post call note. * @@ -83,6 +90,8 @@ public class CallLogNotificationsService extends IntentService { */ private static final String EXTRA_POST_CALL_NUMBER = "POST_CALL_NUMBER"; + private static final String EXTRA_PHONE_ACCOUNT_HANDLE = "PHONE_ACCOUNT_HANDLE"; + public static final int UNKNOWN_MISSED_CALL_COUNT = -1; public CallLogNotificationsService() { @@ -149,6 +158,14 @@ public class CallLogNotificationsService extends IntentService { return PendingIntent.getService(context, 0, intent, 0); } + public static PendingIntent createLegacyVoicemailDismissedPendingIntent( + @NonNull Context context, PhoneAccountHandle phoneAccountHandle) { + Intent intent = new Intent(context, CallLogNotificationsService.class); + intent.setAction(ACTION_LEGACY_VOICEMAIL_DISMISSED); + intent.putExtra(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); + return PendingIntent.getService(context, 0, intent, 0); + } + @Override protected void onHandleIntent(Intent intent) { if (intent == null) { @@ -174,6 +191,10 @@ public class CallLogNotificationsService extends IntentService { VoicemailQueryHandler.markSingleNewVoicemailAsRead(this, voicemailUri); VisualVoicemailNotifier.cancelSingleVoicemailNotification(this, voicemailUri); break; + case ACTION_LEGACY_VOICEMAIL_DISMISSED: + LegacyVoicemailNotificationReceiver.setDismissed( + this, intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE), true); + break; case ACTION_INCOMING_POST_CALL: String note = intent.getStringExtra(EXTRA_POST_CALL_NOTE); String phoneNumber = intent.getStringExtra(EXTRA_POST_CALL_NUMBER); diff --git a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java index 428c71677..c64e03e4e 100644 --- a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java @@ -122,7 +122,10 @@ public final class LegacyVoicemailNotifier { .setSound(pinnedTelephonyManager.getVoicemailRingtoneUri(handle)) .setOngoing(isOngoing) .setOnlyAlertOnce(isRefresh) - .setChannelId(NotificationChannelManager.getVoicemailChannelId(context, handle)); + .setChannelId(NotificationChannelManager.getVoicemailChannelId(context, handle)) + .setDeleteIntent( + CallLogNotificationsService.createLegacyVoicemailDismissedPendingIntent( + context, handle)); if (pinnedTelephonyManager.isVoicemailVibrationEnabled(handle)) { builder.setDefaults(Notification.DEFAULT_VIBRATE); diff --git a/java/com/android/dialer/app/list/ListsFragment.java b/java/com/android/dialer/app/list/ListsFragment.java index 3f03db1e8..dbb6c8b5c 100644 --- a/java/com/android/dialer/app/list/ListsFragment.java +++ b/java/com/android/dialer/app/list/ListsFragment.java @@ -75,7 +75,6 @@ public class ListsFragment extends Fragment implements OnPageChangeListener, Lis private SharedPreferences mPrefs; private boolean mHasFetchedVoicemailStatus; private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched; - private VoicemailStatusHelper mVoicemailStatusHelper; private final ArrayList<OnPageChangeListener> mOnPageChangeListeners = new ArrayList<>(); /** The position of the currently selected tab. */ private int mTabIndex = TAB_INDEX_SPEED_DIAL; @@ -99,7 +98,6 @@ public class ListsFragment extends Fragment implements OnPageChangeListener, Lis LogUtil.d("ListsFragment.onCreate", null); Trace.beginSection(TAG + " onCreate"); super.onCreate(savedInstanceState); - mVoicemailStatusHelper = new VoicemailStatusHelper(); mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); Trace.endSection(); } @@ -294,7 +292,7 @@ public class ListsFragment extends Fragment implements OnPageChangeListener, Lis // Update hasActiveVoicemailProvider, which controls the number of tabs displayed. boolean hasActiveVoicemailProvider = - mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0; + VoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0; if (hasActiveVoicemailProvider != mAdapter.hasActiveVoicemailProvider()) { mAdapter.setHasActiveVoicemailProvider(hasActiveVoicemailProvider); mAdapter.notifyDataSetChanged(); diff --git a/java/com/android/dialer/app/list/PhoneFavoriteTileView.java b/java/com/android/dialer/app/list/PhoneFavoriteTileView.java index 455085d85..29147e7b3 100644 --- a/java/com/android/dialer/app/list/PhoneFavoriteTileView.java +++ b/java/com/android/dialer/app/list/PhoneFavoriteTileView.java @@ -18,6 +18,7 @@ package com.android.dialer.app.list; import android.content.ClipData; import android.content.Context; +import android.net.Uri; import android.provider.ContactsContract.PinnedPositions; import android.text.TextUtils; import android.util.AttributeSet; @@ -26,10 +27,12 @@ import android.widget.ImageView; import com.android.contacts.common.MoreContactUtils; import com.android.contacts.common.list.ContactEntry; import com.android.contacts.common.list.ContactTileView; +import com.android.contacts.common.model.ContactLoader; import com.android.dialer.app.R; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallSpecificAppData; import com.android.dialer.callintent.SpeedDialContactType; +import com.android.dialer.common.LogUtil; import com.android.dialer.contactphoto.ContactPhotoManager.DefaultImageRequest; import com.android.dialer.lettertile.LetterTileDrawable; import com.android.dialer.logging.InteractionEvent; @@ -94,6 +97,7 @@ public abstract class PhoneFavoriteTileView extends ContactTileView { isPinned = (entry.pinned != PinnedPositions.UNPINNED); isStarred = entry.isFavorite; if (entry != null) { + sendViewNotification(getContext(), entry.lookupUri); // Grab the phone-number to call directly. See {@link onClick()}. mPhoneNumberString = entry.phoneNumber; @@ -186,4 +190,22 @@ public abstract class PhoneFavoriteTileView extends ContactTileView { public void setPosition(int position) { this.position = position; } + + /** + * Send a notification using a {@link ContactLoader} to inform the sync adapter that we are + * viewing a particular contact, so that it can download the high-res photo. + */ + private static void sendViewNotification(Context context, Uri contactUri) { + ContactLoader loader = new ContactLoader(context, contactUri, true /* postViewNotification */); + loader.registerListener( + 0, + (loader1, contact) -> { + try { + loader1.reset(); + } catch (RuntimeException e) { + LogUtil.e("PhoneFavoriteTileView.onLoadComplete", "error resetting loader", e); + } + }); + loader.startLoading(); + } } diff --git a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java index 4100521ab..a81d8665a 100644 --- a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java +++ b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java @@ -23,15 +23,15 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Build.VERSION_CODES; -import android.preference.PreferenceManager; +import android.support.annotation.VisibleForTesting; import android.support.v4.os.BuildCompat; -import android.support.v4.os.UserManagerCompat; import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import com.android.dialer.app.calllog.LegacyVoicemailNotifier; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.PerAccountSharedPreferences; +import com.android.dialer.util.DialerUtils; import com.android.voicemail.VoicemailComponent; /** @@ -43,15 +43,14 @@ import com.android.voicemail.VoicemailComponent; public class LegacyVoicemailNotificationReceiver extends BroadcastReceiver { private static final String LEGACY_VOICEMAIL_COUNT = "legacy_voicemail_count"; + @VisibleForTesting static final String LEGACY_VOICEMAIL_DISMISSED = "legacy_voicemail_dismissed"; /** - * Hidden extra for {@link TelephonyManager#ACTION_SHOW_VOICEMAIL_NOTIFICATION} for whether the - * notification is just a refresh or for a new voicemail. The phone should not play a ringtone or - * vibrate during a refresh if the notification is already showing. - * - * <p>TODO(b/62202833): make public + * Whether the notification is just a refresh or for a new voicemail. The phone should not play a + * ringtone or vibrate during a refresh if the notification is already showing. This is Hidden in + * O and public in O MR1. */ - private static final String EXTRA_IS_REFRESH = "is_refresh"; + @VisibleForTesting static final String EXTRA_IS_REFRESH = "is_refresh"; @Override public void onReceive(Context context, Intent intent) { @@ -72,8 +71,21 @@ public class LegacyVoicemailNotificationReceiver extends BroadcastReceiver { PhoneAccountHandle phoneAccountHandle = Assert.isNotNull(intent.getParcelableExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE)); int count = intent.getIntExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, -1); + boolean isRefresh = intent.getBooleanExtra(EXTRA_IS_REFRESH, false); + LogUtil.i("LegacyVoicemailNotificationReceiver.onReceive", "isRefresh: " + isRefresh); + PerAccountSharedPreferences preferences = getSharedPreferences(context, phoneAccountHandle); + if (isRefresh) { + if (preferences.getBoolean(LEGACY_VOICEMAIL_DISMISSED, false)) { + LogUtil.i( + "LegacyVoicemailNotificationReceiver.onReceive", + "notification dismissed, ignoring refresh"); + return; + } + } else { + setDismissed(context, phoneAccountHandle, false); + } - if (!hasVoicemailCountChanged(context, phoneAccountHandle, count)) { + if (!hasVoicemailCountChanged(preferences, count)) { LogUtil.i( "LegacyVoicemailNotificationReceiver.onReceive", "voicemail count hasn't changed, ignoring"); @@ -116,27 +128,24 @@ public class LegacyVoicemailNotificationReceiver extends BroadcastReceiver { voicemailNumber, callVoicemailIntent, voicemailSettingIntent, - intent.getBooleanExtra(EXTRA_IS_REFRESH, false)); + isRefresh); } - private static boolean hasVoicemailCountChanged( - Context context, PhoneAccountHandle phoneAccountHandle, int newCount) { - // Need credential encrypted storage to access preferences. - if (!UserManagerCompat.isUserUnlocked(context)) { - LogUtil.i( - "LegacyVoicemailNotificationReceiver.onReceive", - "User locked, bypassing voicemail count check"); - return true; - } + public static void setDismissed( + Context context, PhoneAccountHandle phoneAccountHandle, boolean dismissed) { + getSharedPreferences(context, phoneAccountHandle) + .edit() + .putBoolean(LEGACY_VOICEMAIL_DISMISSED, dismissed) + .apply(); + } + private static boolean hasVoicemailCountChanged( + PerAccountSharedPreferences preferences, int newCount) { if (newCount == -1) { // Carrier does not report voicemail count return true; } - PerAccountSharedPreferences preferences = - new PerAccountSharedPreferences( - context, phoneAccountHandle, PreferenceManager.getDefaultSharedPreferences(context)); // Carriers may send multiple notifications for the same voicemail. if (newCount != 0 && newCount == preferences.getInt(LEGACY_VOICEMAIL_COUNT, -1)) { return false; @@ -144,4 +153,13 @@ public class LegacyVoicemailNotificationReceiver extends BroadcastReceiver { preferences.edit().putInt(LEGACY_VOICEMAIL_COUNT, newCount).apply(); return true; } + + @VisibleForTesting + static PerAccountSharedPreferences getSharedPreferences( + Context context, PhoneAccountHandle phoneAccountHandle) { + return new PerAccountSharedPreferences( + context, + phoneAccountHandle, + DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context)); + } } diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java index 5c9bc01c2..6d5015a22 100644 --- a/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java +++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackPresenter.java @@ -57,6 +57,7 @@ import com.android.dialer.constants.Constants; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.phonenumbercache.CallLogQuery; +import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.PermissionsUtil; import com.google.common.io.ByteStreams; import java.io.File; @@ -515,6 +516,11 @@ public class VoicemailPlaybackPresenter mView.disableUiElements(); mIsPrepared = false; + if (mContext != null && TelecomUtil.isInCall(mContext)) { + handleError(new IllegalStateException("Cannot play voicemail when call is in progress")); + return; + } + try { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnPreparedListener(this); diff --git a/java/com/android/dialer/binary/common/DialerApplication.java b/java/com/android/dialer/binary/common/DialerApplication.java index 08666a21c..0d38541e5 100644 --- a/java/com/android/dialer/binary/common/DialerApplication.java +++ b/java/com/android/dialer/binary/common/DialerApplication.java @@ -17,18 +17,17 @@ package com.android.dialer.binary.common; import android.app.Application; -import android.os.StrictMode; import android.os.Trace; import android.support.annotation.NonNull; import android.support.v4.os.BuildCompat; import com.android.dialer.blocking.BlockedNumbersAutoMigrator; import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; -import com.android.dialer.buildtype.BuildType; import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.common.concurrent.DefaultDialerExecutorFactory; import com.android.dialer.inject.HasRootComponent; import com.android.dialer.notification.NotificationChannelManager; import com.android.dialer.persistentlog.PersistentLogger; +import com.android.dialer.strictmode.DialerStrictMode; /** A common application subclass for all Dialer build variants. */ public abstract class DialerApplication extends Application implements HasRootComponent { @@ -38,9 +37,8 @@ public abstract class DialerApplication extends Application implements HasRootCo @Override public void onCreate() { Trace.beginSection("DialerApplication.onCreate"); - if (BuildType.get() == BuildType.BUGFOOD) { - enableStrictMode(); - } + DialerStrictMode.onApplicationCreate(); + super.onCreate(); new BlockedNumbersAutoMigrator( this.getApplicationContext(), @@ -56,13 +54,6 @@ public abstract class DialerApplication extends Application implements HasRootCo Trace.endSection(); } - private void enableStrictMode() { - StrictMode.setThreadPolicy( - new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().penaltyDeath().build()); - StrictMode.setVmPolicy( - new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyDeath().build()); - } - /** * Returns a new instance of the root component for the application. Sub classes should define a * root component that extends all the sub components "HasComponent" intefaces. The component diff --git a/java/com/android/dialer/callcomposer/CallComposerActivity.java b/java/com/android/dialer/callcomposer/CallComposerActivity.java index ddc1e87f8..e6e55134b 100644 --- a/java/com/android/dialer/callcomposer/CallComposerActivity.java +++ b/java/com/android/dialer/callcomposer/CallComposerActivity.java @@ -670,12 +670,20 @@ public class CallComposerActivity extends AppCompatActivity public void onAnimationStart(Animator animation) { isSendAndCallHidingOrHidden = shouldHide; sendAndCall.setVisibility(View.VISIBLE); + cameraIcon.setVisibility(View.VISIBLE); + galleryIcon.setVisibility(View.VISIBLE); + messageIcon.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd(Animator animation) { if (isSendAndCallHidingOrHidden) { sendAndCall.setVisibility(View.INVISIBLE); + } else { + // hide buttons to prevent overdrawing and talkback discoverability + cameraIcon.setVisibility(View.GONE); + galleryIcon.setVisibility(View.GONE); + messageIcon.setVisibility(View.GONE); } } diff --git a/java/com/android/dialer/callcomposer/GalleryCursorLoader.java b/java/com/android/dialer/callcomposer/GalleryCursorLoader.java index 39d6a4a6d..d33bfb398 100644 --- a/java/com/android/dialer/callcomposer/GalleryCursorLoader.java +++ b/java/com/android/dialer/callcomposer/GalleryCursorLoader.java @@ -16,7 +16,6 @@ package com.android.dialer.callcomposer; -import android.annotation.SuppressLint; import android.content.Context; import android.net.Uri; import android.provider.MediaStore.Files; @@ -44,11 +43,10 @@ public class GalleryCursorLoader extends CursorLoader { SORT_ORDER); } - @SuppressLint("DefaultLocale") private static String createSelection() { - return String.format( - "mime_type IN ('image/jpeg', 'image/jpg', 'image/png', 'image/webp')" - + " AND media_type in (%d)", - FileColumns.MEDIA_TYPE_IMAGE); + return "mime_type IN ('image/jpeg', 'image/jpg', 'image/png', 'image/webp')" + + " AND media_type in (" + + FileColumns.MEDIA_TYPE_IMAGE + + ")"; } } diff --git a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java index 7d5757bbd..410a3a012 100644 --- a/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java +++ b/java/com/android/dialer/calldetails/CallDetailsHeaderViewHolder.java @@ -19,6 +19,7 @@ package com.android.dialer.calldetails; import android.content.Context; import android.net.Uri; import android.support.v7.widget.RecyclerView; +import android.telecom.PhoneAccount; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; @@ -93,7 +94,9 @@ public class CallDetailsHeaderViewHolder extends RecyclerView.ViewHolder if (!TextUtils.isEmpty(contact.getSimDetails().getNetwork())) { networkView.setVisibility(View.VISIBLE); networkView.setText(contact.getSimDetails().getNetwork()); - networkView.setTextColor(context.getResources().getColor(contact.getSimDetails().getColor())); + if (contact.getSimDetails().getColor() != PhoneAccount.NO_HIGHLIGHT_COLOR) { + networkView.setTextColor(contact.getSimDetails().getColor()); + } } if (TextUtils.isEmpty(contact.getNumber())) { diff --git a/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java b/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java index fad25a409..6ee469572 100644 --- a/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java +++ b/java/com/android/dialer/configprovider/SharedPrefConfigProvider.java @@ -20,11 +20,11 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; -import android.os.StrictMode; import android.support.annotation.Nullable; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.strictmode.DialerStrictMode; import com.android.dialer.util.DialerUtils; import javax.inject.Inject; @@ -95,37 +95,26 @@ class SharedPrefConfigProvider implements ConfigProvider { @Override public String getString(String key, String defaultValue) { - return bypassStrictMode( + // Reading shared prefs on the main thread is generally safe since a single instance is cached. + return DialerStrictMode.bypass( () -> getSharedPrefs(appContext).getString(PREF_PREFIX + key, defaultValue)); } @Override public long getLong(String key, long defaultValue) { - return bypassStrictMode( + // Reading shared prefs on the main thread is generally safe since a single instance is cached. + return DialerStrictMode.bypass( () -> getSharedPrefs(appContext).getLong(PREF_PREFIX + key, defaultValue)); } @Override public boolean getBoolean(String key, boolean defaultValue) { - return bypassStrictMode( + // Reading shared prefs on the main thread is generally safe since a single instance is cached. + return DialerStrictMode.bypass( () -> getSharedPrefs(appContext).getBoolean(PREF_PREFIX + key, defaultValue)); } private static SharedPreferences getSharedPrefs(Context appContext) { return DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(appContext); } - - private interface Provider<T> { - T get(); - } - - // Reading shared prefs on the main thread is generally safe since a single instance is cached. - private static <T> T bypassStrictMode(Provider<T> provider) { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - return provider.get(); - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } - } } diff --git a/java/com/android/dialer/contactphoto/ContactPhotoManagerImpl.java b/java/com/android/dialer/contactphoto/ContactPhotoManagerImpl.java index 4ad7ea465..5dbdf5e48 100644 --- a/java/com/android/dialer/contactphoto/ContactPhotoManagerImpl.java +++ b/java/com/android/dialer/contactphoto/ContactPhotoManagerImpl.java @@ -566,7 +566,7 @@ class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback { if (request.mIsCircular) { final RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(resources, bitmap); drawable.setAntiAlias(true); - drawable.setCornerRadius(bitmap.getHeight() / 2); + drawable.setCornerRadius(drawable.getIntrinsicHeight() / 2); return drawable; } else { return new BitmapDrawable(resources, bitmap); diff --git a/java/com/android/dialer/lightbringer/Lightbringer.java b/java/com/android/dialer/lightbringer/Lightbringer.java index 9b8a18061..9120b24db 100644 --- a/java/com/android/dialer/lightbringer/Lightbringer.java +++ b/java/com/android/dialer/lightbringer/Lightbringer.java @@ -28,6 +28,8 @@ import android.telecom.PhoneAccountHandle; public interface Lightbringer { + boolean isEnabled(); + @MainThread boolean isReachable(@NonNull Context context, @Nullable String number); diff --git a/java/com/android/dialer/lightbringer/stub/LightbringerStub.java b/java/com/android/dialer/lightbringer/stub/LightbringerStub.java index 92230a49e..c98ae091b 100644 --- a/java/com/android/dialer/lightbringer/stub/LightbringerStub.java +++ b/java/com/android/dialer/lightbringer/stub/LightbringerStub.java @@ -35,6 +35,11 @@ public class LightbringerStub implements Lightbringer { @Inject public LightbringerStub() {} + @Override + public boolean isEnabled() { + return false; + } + @MainThread @Override public boolean isReachable(@NonNull Context context, @Nullable String number) { diff --git a/java/com/android/dialer/logging/LoggingBindings.java b/java/com/android/dialer/logging/LoggingBindings.java index 85ccfdfa1..ca9a0533e 100644 --- a/java/com/android/dialer/logging/LoggingBindings.java +++ b/java/com/android/dialer/logging/LoggingBindings.java @@ -80,4 +80,11 @@ public interface LoggingBindings { QuickContactBadge quickContact, InteractionEvent.Type interactionEvent, boolean shouldPerformClick); + + /** Logs People Api lookup result with error */ + void logPeopleApiLookupReportWithError( + long latency, int httpResponseCode, PeopleApiLookupError.Type errorType); + + /** Logs successful People Api lookup result */ + void logSuccessfulPeopleApiLookupReport(long latency, int httpResponseCode); } diff --git a/java/com/android/dialer/logging/LoggingBindingsStub.java b/java/com/android/dialer/logging/LoggingBindingsStub.java index 38929969c..2dbcc3ffb 100644 --- a/java/com/android/dialer/logging/LoggingBindingsStub.java +++ b/java/com/android/dialer/logging/LoggingBindingsStub.java @@ -54,4 +54,11 @@ public class LoggingBindingsStub implements LoggingBindings { QuickContactBadge quickContact, InteractionEvent.Type interactionEvent, boolean shouldPerformClick) {} + + @Override + public void logPeopleApiLookupReportWithError( + long latency, int httpResponseCode, PeopleApiLookupError.Type errorType) {} + + @Override + public void logSuccessfulPeopleApiLookupReport(long latency, int httpResponseCode) {} } diff --git a/java/com/android/dialer/logging/dialer_impression.proto b/java/com/android/dialer/logging/dialer_impression.proto index 8ccaf2dea..92ff0a675 100644 --- a/java/com/android/dialer/logging/dialer_impression.proto +++ b/java/com/android/dialer/logging/dialer_impression.proto @@ -478,5 +478,10 @@ message DialerImpression { // In in call UI UPGRADE_TO_VIDEO_CALL_BUTTON_SHOWN = 1236; + + // Bubble primary button first click to expand bubble + BUBBLE_PRIMARY_BUTTON_EXPAND = 1237; + // Bubble prinary button second click to return to call + BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL = 1238; } } diff --git a/java/com/android/dialer/logging/people_api_lookup_error.proto b/java/com/android/dialer/logging/people_api_lookup_error.proto new file mode 100644 index 000000000..e37d10aee --- /dev/null +++ b/java/com/android/dialer/logging/people_api_lookup_error.proto @@ -0,0 +1,19 @@ +syntax = "proto2"; + +package com.android.dialer.logging; +option java_package = "com.android.dialer.logging"; +option java_multiple_files = true; +option optimize_for = LITE_RUNTIME; + + + + +message PeopleApiLookupError { + enum Type { + UNKNOWN = 0; + HTTP_RESPONSE_ERROR = 1; + WRONG_KIND_VALUE = 2; + NO_ITEM_FOUND = 3; + JSON_PARSING_ERROR = 4; + } +} diff --git a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java index ee6e61c2e..0d22a824c 100644 --- a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java +++ b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java @@ -60,6 +60,23 @@ public class QueryFilteringUtil { return queryIndex == query.length(); } + /** + * Returns true if the subparts of the name (split by white space) begin with the query. + * + * <p>Examples: + * + * <ul> + * <li>#nameContainsQuery("b", "Brandon") returns true + * <li>#nameContainsQuery("o", "Bob") returns false + * <li>#nameContainsQuery("o", "Bob Olive") returns true + * </ul> + */ + public static boolean nameContainsQuery(String query, String name) { + return Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase())) + .matcher(name.toLowerCase()) + .find(); + } + /** @return true if the number belongs to the query. */ public static boolean numberMatchesNumberQuery(String query, String number) { return PhoneNumberUtils.isGlobalPhoneNumber(query) diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java b/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java index 51992646a..05e98cc84 100644 --- a/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java +++ b/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java @@ -142,7 +142,7 @@ public final class SearchContactCursor implements Cursor { if (TextUtils.isEmpty(query) || QueryFilteringUtil.nameMatchesT9Query(query, previousName) || QueryFilteringUtil.numberMatchesNumberQuery(query, previousMostQualifiedNumber) - || previousName.contains(query)) { + || QueryFilteringUtil.nameContainsQuery(query, previousName)) { queryFilteredPositions.add(previousMostQualifiedPosition); } } diff --git a/java/com/android/dialer/strictmode/DialerStrictMode.java b/java/com/android/dialer/strictmode/DialerStrictMode.java new file mode 100644 index 000000000..c9bbeafbf --- /dev/null +++ b/java/com/android/dialer/strictmode/DialerStrictMode.java @@ -0,0 +1,95 @@ +/* + * 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.strictmode; + +import android.os.StrictMode; +import android.os.StrictMode.ThreadPolicy; +import android.os.StrictMode.VmPolicy; +import com.android.dialer.buildtype.BuildType; + +/** + * Enables strict mode for the application, and provides means of temporarily disabling it. + * + * <p>NOTE: All methods in this class are stripped by proguard in release builds. + */ +public final class DialerStrictMode { + + /** Initializes strict mode on application start. */ + public static void onApplicationCreate() { + enableDeathPenalty(); + } + + /** + * Disables the strict mode death penalty. If strict mode is enabled for the build, warnings are + * printed instead of the application crashing. + * + * <p>You should typically do this only temporarily and restore the death penalty in a finally + * block using {@link #enableDeathPenalty()}. + */ + public static void disableDeathPenalty() { + if (isStrictModeAllowed()) { + StrictMode.setThreadPolicy(threadPolicyTemplate().build()); + StrictMode.setVmPolicy(vmPolicyTemplate().build()); + } + } + + /** + * Restore the death penalty. This should typically be called in a finally block after calling + * {@link #disableDeathPenalty()}. + */ + public static void enableDeathPenalty() { + if (isStrictModeAllowed()) { + StrictMode.setThreadPolicy(threadPolicyTemplate().penaltyDeath().build()); + StrictMode.setVmPolicy(vmPolicyTemplate().penaltyDeath().build()); + } + } + + private static ThreadPolicy.Builder threadPolicyTemplate() { + return new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog(); + } + + private static VmPolicy.Builder vmPolicyTemplate() { + return new StrictMode.VmPolicy.Builder().detectAll().penaltyLog(); + } + + private static boolean isStrictModeAllowed() { + return BuildType.get() == BuildType.BUGFOOD; + } + + /** Functional interface intended to be used with {@link #bypass(Provider)}. */ + public interface Provider<T> { + T get(); + } + + /** + * Convenience method for disabling and enabling the death penalty using lambdas. + * + * <p>For example: + * + * <p><code> + * DialerStrictMode.bypass(() -> doDiskAccessOnMainThread()); + * </code> + */ + public static <T> T bypass(Provider<T> provider) { + disableDeathPenalty(); + try { + return provider.get(); + } finally { + enableDeathPenalty(); + } + } +} diff --git a/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java b/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java index a1fc29edf..3f519ad82 100644 --- a/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java +++ b/java/com/android/dialer/voicemailstatus/VisualVoicemailEnabledChecker.java @@ -45,7 +45,6 @@ public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listen private SharedPreferences mPrefs; private boolean mHasActiveVoicemailProvider; private CallLogQueryHandler mCallLogQueryHandler; - private VoicemailStatusHelper mVoicemailStatusHelper; private Context mContext; private Callback mCallback; @@ -53,7 +52,6 @@ public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listen mContext = context; mCallback = callback; mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); - mVoicemailStatusHelper = new VoicemailStatusHelper(); mHasActiveVoicemailProvider = mPrefs.getBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false); } @@ -77,7 +75,7 @@ public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listen @Override public void onVoicemailStatusFetched(Cursor statusCursor) { boolean hasActiveVoicemailProvider = - mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0; + VoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0; if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) { mHasActiveVoicemailProvider = hasActiveVoicemailProvider; mPrefs diff --git a/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java index 9df45c211..313fc1be1 100644 --- a/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java +++ b/java/com/android/dialer/voicemailstatus/VoicemailStatusHelper.java @@ -21,15 +21,17 @@ import android.provider.VoicemailContract.Status; import com.android.dialer.database.VoicemailStatusQuery; /** - * Interface used by the call log UI to determine what user message, if any, related to voicemail + * Utility used by the call log UI to determine what user message, if any, related to voicemail * source status needs to be shown. The messages are returned in the order of importance. * - * <p>The implementation of this interface interacts with the voicemail content provider to fetch - * statuses of all the registered voicemail sources and determines if any status message needs to be - * shown. The user of this interface must observe/listen to provider changes and invoke this class - * to check if any message needs to be shown. + * <p>This class interacts with the voicemail content provider to fetch statuses of all the + * registered voicemail sources and determines if any status message needs to be shown. The user of + * this class must observe/listen to provider changes and invoke this class to check if any message + * needs to be shown. */ -public class VoicemailStatusHelper { +public final class VoicemailStatusHelper { + + private VoicemailStatusHelper() {} /** * Returns the number of active voicemail sources installed. @@ -39,7 +41,7 @@ public class VoicemailStatusHelper { * @param cursor The caller is responsible for the life cycle of the cursor and resetting the * position */ - public int getNumberActivityVoicemailSources(Cursor cursor) { + public static int getNumberActivityVoicemailSources(Cursor cursor) { int count = 0; if (!cursor.moveToFirst()) { return 0; @@ -60,8 +62,10 @@ public class VoicemailStatusHelper { * activation is attempted, it will transition into CONFIGURING then into OK or other error state, * NOT_CONFIGURED is never set through an error. */ - private boolean isVoicemailSourceActive(Cursor cursor) { + private static boolean isVoicemailSourceActive(Cursor cursor) { return cursor.getString(VoicemailStatusQuery.SOURCE_PACKAGE_INDEX) != null + // getInt() returns 0 when null + && !cursor.isNull(VoicemailStatusQuery.CONFIGURATION_STATE_INDEX) && cursor.getInt(VoicemailStatusQuery.CONFIGURATION_STATE_INDEX) != Status.CONFIGURATION_STATE_NOT_CONFIGURED; } diff --git a/java/com/android/dialer/widget/MessageFragment.java b/java/com/android/dialer/widget/MessageFragment.java index 615ad3b77..7a0fcfd0f 100644 --- a/java/com/android/dialer/widget/MessageFragment.java +++ b/java/com/android/dialer/widget/MessageFragment.java @@ -134,10 +134,9 @@ public class MessageFragment extends Fragment @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (getMessage() == null) { - return false; + if (!TextUtils.isEmpty(getMessage())) { + getListener().onMessageFragmentSendMessage(getMessage()); } - getListener().onMessageFragmentSendMessage(getMessage()); return true; } |