From 8369df095a73a77b3715f8ae7ba06089cebca4ce Mon Sep 17 00:00:00 2001 From: Eric Erfanian Date: Wed, 3 May 2017 10:27:13 -0700 Subject: This change reflects the Dialer V10 RC00 branch. RC00 is based on: branch: dialer-android_release_branch/153304843.1 synced to: 153304843 following the instructions at go/dialer-aosp-release. In this release: * Removes final apache sources. * Uses native lite compilation. More drops will follow with subsequent release candidates until we reach our final v10 release, in cadence with our prebuilt drops. Test: TreeHugger, on device Change-Id: Ic9684057230f9b579c777820c746cd21bf45ec0f --- .../app/calllog/BlockReportSpamListener.java | 13 +- .../dialer/app/calllog/CallLogActivity.java | 2 +- .../android/dialer/app/calllog/CallLogAdapter.java | 160 ++++++++++++----- .../dialer/app/calllog/CallLogListItemHelper.java | 2 +- .../app/calllog/CallLogListItemViewHolder.java | 192 ++++++++++++++++----- .../app/calllog/DefaultVoicemailNotifier.java | 110 +++++++++++- .../android/dialer/app/calllog/IntentProvider.java | 6 +- .../dialer/app/calllog/MissedCallNotifier.java | 2 +- .../dialer/app/calllog/PhoneCallDetailsHelper.java | 7 +- .../calllog/VisualVoicemailCallLogFragment.java | 18 +- .../dialer/app/calllog/VoicemailQueryHandler.java | 4 +- 11 files changed, 400 insertions(+), 116 deletions(-) (limited to 'java/com/android/dialer/app/calllog') diff --git a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java index 66f40bcd7..740d6b361 100644 --- a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java +++ b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java @@ -24,9 +24,10 @@ import android.support.v7.widget.RecyclerView; import com.android.dialer.blocking.BlockReportSpamDialogs; import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; import com.android.dialer.common.LogUtil; +import com.android.dialer.logging.ContactSource; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; -import com.android.dialer.logging.nano.ReportingLocation; +import com.android.dialer.logging.ReportingLocation; import com.android.dialer.spam.Spam; /** Listener to show dialogs for block and report spam actions. */ @@ -54,7 +55,7 @@ public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClic final String number, final String countryIso, final int callType, - final int contactSourceType) { + final ContactSource.Type contactSourceType) { BlockReportSpamDialogs.BlockReportSpamDialogFragment.newInstance( displayNumber, Spam.get(mContext).isDialogReportSpamCheckedByDefault(), @@ -98,7 +99,7 @@ public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClic final String number, final String countryIso, final int callType, - final int contactSourceType) { + final ContactSource.Type contactSourceType) { BlockReportSpamDialogs.BlockDialogFragment.newInstance( displayNumber, Spam.get(mContext).isSpamEnabled(), @@ -142,7 +143,7 @@ public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClic final String number, final String countryIso, final int callType, - final int contactSourceType, + final ContactSource.Type contactSourceType, final boolean isSpam, final Integer blockId) { BlockReportSpamDialogs.UnblockDialogFragment.newInstance( @@ -185,7 +186,7 @@ public class BlockReportSpamListener implements CallLogListItemViewHolder.OnClic final String number, final String countryIso, final int callType, - final int contactSourceType) { + final ContactSource.Type contactSourceType) { BlockReportSpamDialogs.ReportNotSpamDialogFragment.newInstance( displayNumber, new BlockReportSpamDialogs.OnConfirmListener() { diff --git a/java/com/android/dialer/app/calllog/CallLogActivity.java b/java/com/android/dialer/app/calllog/CallLogActivity.java index 719ab4369..443171d3f 100644 --- a/java/com/android/dialer/app/calllog/CallLogActivity.java +++ b/java/com/android/dialer/app/calllog/CallLogActivity.java @@ -33,7 +33,7 @@ import com.android.dialer.app.DialtactsActivity; import com.android.dialer.app.R; import com.android.dialer.database.CallLogQueryHandler; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.ScreenEvent; +import com.android.dialer.logging.ScreenEvent; import com.android.dialer.util.TransactionSafeActivity; import com.android.dialer.util.ViewUtil; diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java index 06f48aa20..d4872d5b3 100644 --- a/java/com/android/dialer/app/calllog/CallLogAdapter.java +++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java @@ -18,6 +18,7 @@ package com.android.dialer.app.calllog; import android.app.Activity; import android.content.ContentUris; +import android.content.DialogInterface; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; @@ -33,13 +34,14 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.telecom.PhoneAccountHandle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.SparseBooleanArray; +import android.util.SparseArray; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; @@ -59,8 +61,8 @@ import com.android.dialer.app.contactinfo.ContactInfoCache; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter.OnVoicemailDeletedListener; import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; -import com.android.dialer.calldetails.nano.CallDetailsEntries; -import com.android.dialer.calldetails.nano.CallDetailsEntries.CallDetailsEntry; +import com.android.dialer.calldetails.CallDetailsEntries; +import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry; import com.android.dialer.calllogutils.PhoneAccountUtils; import com.android.dialer.calllogutils.PhoneCallDetails; import com.android.dialer.common.Assert; @@ -71,12 +73,12 @@ import com.android.dialer.common.concurrent.AsyncTaskExecutors; import com.android.dialer.enrichedcall.EnrichedCallCapabilities; import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.enrichedcall.EnrichedCallManager; -import com.android.dialer.enrichedcall.historyquery.proto.nano.HistoryResult; +import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult; import com.android.dialer.lightbringer.Lightbringer; import com.android.dialer.lightbringer.LightbringerComponent; import com.android.dialer.lightbringer.LightbringerListener; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; import com.android.dialer.phonenumbercache.CallLogQuery; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.phonenumbercache.ContactInfoHelper; @@ -129,8 +131,8 @@ public class CallLogAdapter extends GroupingListAdapter private final CallLogAlertManager mCallLogAlertManager; - public static ActionMode mActionMode = null; - private final SparseBooleanArray selectedItems = new SparseBooleanArray(); + public ActionMode mActionMode = null; + private final SparseArray selectedItems = new SparseArray<>(); private final ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -155,7 +157,15 @@ public class CallLogAdapter extends GroupingListAdapter // Called when the user selects a contextual menu item @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return false; + if (item.getItemId() == R.id.action_bar_delete_menu_item) { + if (selectedItems.size() > 0) { + showDeleteSelectedItemsDialog(); + } + mode.finish(); + return true; + } else { + return false; + } } // Called when the user exits the action mode @@ -167,14 +177,66 @@ public class CallLogAdapter extends GroupingListAdapter } }; + private void showDeleteSelectedItemsDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + Assert.checkArgument(selectedItems.size() > 0); + String voicemailString = + selectedItems.size() == 1 + ? mActivity.getResources().getString(R.string.voicemailMultiSelectVoicemail) + : mActivity.getResources().getString(R.string.voicemailMultiSelectVoicemail); + String deleteVoicemailTitle = + mActivity + .getResources() + .getString(R.string.voicemailMultiSelectDialogTitle, voicemailString); + SparseArray voicemailsToDeleteOnConfirmation = selectedItems.clone(); + builder.setTitle(deleteVoicemailTitle); + + builder.setPositiveButton( + mActivity.getResources().getString(R.string.voicemailMultiSelectDeleteConfirm), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + deleteSelectedItems(voicemailsToDeleteOnConfirmation); + dialog.cancel(); + } + }); + + builder.setNegativeButton( + mActivity.getResources().getString(R.string.voicemailMultiSelectDeleteCancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + private void deleteSelectedItems(SparseArray voicemailsToDelete) { + for (int i = 0; i < voicemailsToDelete.size(); i++) { + String voicemailUri = voicemailsToDelete.get(voicemailsToDelete.keyAt(i)); + CallLogAsyncTaskUtil.deleteVoicemail(mActivity, Uri.parse(voicemailUri), null); + } + } + private final View.OnLongClickListener mLongPressListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (ConfigProviderBindings.get(v.getContext()) - .getBoolean("enable_call_log_multiselect", false)) { - v.startActionMode(mActionModeCallback); - return false; + .getBoolean("enable_call_log_multiselect", true) + && mVoicemailPlaybackPresenter != null) { + if (v.getId() == R.id.primary_action_view) { + if (mActionMode == null) { + mActionMode = v.startActionMode(mActionModeCallback); + } + CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) v.getTag(); + viewHolder.quickContactView.setVisibility(View.GONE); + viewHolder.checkBoxView.setVisibility(View.VISIBLE); + return false; + } } return true; } @@ -190,15 +252,20 @@ public class CallLogAdapter extends GroupingListAdapter return; } if (mActionMode != null && viewHolder.voicemailUri != null) { - if (selectedItems.get((int) ContentUris.parseId(Uri.parse(viewHolder.voicemailUri)))) { - selectedItems.delete((int) ContentUris.parseId(Uri.parse(viewHolder.voicemailUri))); + int id = getVoicemailId(viewHolder.voicemailUri); + if (selectedItems.get(id) != null) { + selectedItems.delete(id); viewHolder.checkBoxView.setVisibility(View.GONE); viewHolder.quickContactView.setVisibility(View.VISIBLE); } else { viewHolder.quickContactView.setVisibility(View.GONE); viewHolder.checkBoxView.setVisibility(View.VISIBLE); - selectedItems.put( - (int) ContentUris.parseId(Uri.parse(viewHolder.voicemailUri)), true); + selectedItems.put(getVoicemailId(viewHolder.voicemailUri), viewHolder.voicemailUri); + } + + if (selectedItems.size() == 0) { + mActionMode.finish(); + return; } mActionMode.setTitle(Integer.toString(selectedItems.size())); return; @@ -216,12 +283,10 @@ public class CallLogAdapter extends GroupingListAdapter getEnrichedCallManager().getCapabilities(viewHolder.number); viewHolder.isCallComposerCapable = capabilities != null && capabilities.supportsCallComposer(); - - CallDetailsEntries callDetailsEntries = viewHolder.getDetailedPhoneDetails(); - setCallDetailsEntriesHistoryResults( + generateAndMapNewCallDetailsEntriesHistoryResults( viewHolder.number, - callDetailsEntries, - getAllHistoricalData(viewHolder.number, callDetailsEntries)); + viewHolder.getDetailedPhoneDetails(), + getAllHistoricalData(viewHolder.number, viewHolder.getDetailedPhoneDetails())); if (viewHolder.rowId == mCurrentlyExpandedRowId) { // Hide actions, if the clicked item is the expanded item. @@ -241,6 +306,12 @@ public class CallLogAdapter extends GroupingListAdapter } }; + private static int getVoicemailId(String voicemailUri) { + Assert.checkArgument(voicemailUri != null); + Assert.checkArgument(voicemailUri.length() > 0); + return (int) ContentUris.parseId(Uri.parse(voicemailUri)); + } + /** * A list of {@link CallLogQuery#ID} that will be hidden. The hide might be temporary so instead * if removing an item, it will be shown as an invisible view. This simplifies the calculation of @@ -547,9 +618,12 @@ public class CallLogAdapter extends GroupingListAdapter // the value will be false while capabilities are requested. mExpandCollapseListener will // attempt to set the field properly in that case views.isCallComposerCapable = isCallComposerCapable(views.number); - setCallDetailsEntriesHistoryResults( - views.number, callDetailsEntries, getAllHistoricalData(views.number, callDetailsEntries)); - views.setDetailedPhoneDetails(callDetailsEntries); + CallDetailsEntries updatedCallDetailsEntries = + generateAndMapNewCallDetailsEntriesHistoryResults( + views.number, + callDetailsEntries, + getAllHistoricalData(views.number, callDetailsEntries)); + views.setDetailedPhoneDetails(updatedCallDetailsEntries); views.lightbringerReady = getLightbringer().isReachable(mActivity, views.number); final AsyncTask loadDataTask = new AsyncTask() { @@ -625,23 +699,27 @@ public class CallLogAdapter extends GroupingListAdapter return historicalData; } - private void setCallDetailsEntriesHistoryResults( + private static CallDetailsEntries generateAndMapNewCallDetailsEntriesHistoryResults( @Nullable String number, @NonNull CallDetailsEntries callDetailsEntries, @NonNull Map> mappedResults) { if (number == null) { - return; + return callDetailsEntries; } - for (CallDetailsEntry entry : callDetailsEntries.entries) { + CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder(); + for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) { + CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry); List results = mappedResults.get(entry); if (results != null) { - entry.historyResults = mappedResults.get(entry).toArray(new HistoryResult[0]); + newEntry.addAllHistoryResults(mappedResults.get(entry)); LogUtil.v( - "CallLogAdapter.setCallDetailsEntriesHistoryResults", + "CallLogAdapter.generateAndMapNewCallDetailsEntriesHistoryResults", "mapped %d results", - entry.historyResults.length); + newEntry.getHistoryResultsList().size()); } + mutableCallDetailsEntries.addEntries(newEntry.build()); } + return mutableCallDetailsEntries.build(); } /** @@ -703,21 +781,21 @@ public class CallLogAdapter extends GroupingListAdapter private static CallDetailsEntries createCallDetailsEntries(Cursor cursor, int count) { Assert.isMainThread(); int position = cursor.getPosition(); - CallDetailsEntries entries = new CallDetailsEntries(); - entries.entries = new CallDetailsEntry[count]; + CallDetailsEntries.Builder entries = CallDetailsEntries.newBuilder(); for (int i = 0; i < count; i++) { - CallDetailsEntry entry = new CallDetailsEntry(); - entry.callId = cursor.getLong(CallLogQuery.ID); - entry.callType = cursor.getInt(CallLogQuery.CALL_TYPE); - entry.dataUsage = cursor.getLong(CallLogQuery.DATA_USAGE); - entry.date = cursor.getLong(CallLogQuery.DATE); - entry.duration = cursor.getLong(CallLogQuery.DURATION); - entry.features |= cursor.getInt(CallLogQuery.FEATURES); - entries.entries[i] = entry; + CallDetailsEntry.Builder entry = + CallDetailsEntry.newBuilder() + .setCallId(cursor.getLong(CallLogQuery.ID)) + .setCallType(cursor.getInt(CallLogQuery.CALL_TYPE)) + .setDataUsage(cursor.getLong(CallLogQuery.DATA_USAGE)) + .setDate(cursor.getLong(CallLogQuery.DATE)) + .setDuration(cursor.getLong(CallLogQuery.DURATION)) + .setFeatures(cursor.getInt(CallLogQuery.FEATURES)); + entries.addEntries(entry.build()); cursor.moveToNext(); } cursor.moveToPosition(position); - return entries; + return entries.build(); } /** @@ -822,7 +900,7 @@ public class CallLogAdapter extends GroupingListAdapter details.contactUserType == ContactsUtils.USER_TYPE_WORK ? View.VISIBLE : View.GONE); if (views.voicemailUri != null - && selectedItems.get((int) ContentUris.parseId(Uri.parse(views.voicemailUri)))) { + && selectedItems.get(getVoicemailId(views.voicemailUri)) != null) { views.checkBoxView.setVisibility(View.VISIBLE); views.quickContactView.setVisibility(View.GONE); } else if (views.voicemailUri != null) { diff --git a/java/com/android/dialer/app/calllog/CallLogListItemHelper.java b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java index a5df8cca1..ac43b9ea7 100644 --- a/java/com/android/dialer/app/calllog/CallLogListItemHelper.java +++ b/java/com/android/dialer/app/calllog/CallLogListItemHelper.java @@ -269,7 +269,7 @@ import com.android.dialer.compat.AppCompatConstants; if (!TextUtils.isEmpty(details.getPreferredName())) { recipient = details.getPreferredName(); } else { - recipient = details.displayNumber + details.postDialDigits; + recipient = details.displayNumber; } return recipient; } diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java index 9adcddb3c..b57f9b04a 100644 --- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java +++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.AsyncTask; +import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.NonNull; @@ -31,7 +32,10 @@ import android.support.v7.widget.RecyclerView; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; import android.text.TextUtils; +import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; import android.view.ViewStub; @@ -41,7 +45,7 @@ import android.widget.QuickContactBadge; import android.widget.TextView; import com.android.contacts.common.ClipboardUtils; import com.android.contacts.common.ContactPhotoManager; -import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; +import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.dialog.CallSubjectDialog; import com.android.contacts.common.util.UriUtils; import com.android.dialer.app.DialtactsActivity; @@ -53,14 +57,16 @@ import com.android.dialer.blocking.BlockedNumbersMigrator; import com.android.dialer.blocking.FilteredNumberCompat; import com.android.dialer.blocking.FilteredNumbersUtil; import com.android.dialer.callcomposer.CallComposerActivity; -import com.android.dialer.callcomposer.nano.CallComposerContact; -import com.android.dialer.calldetails.nano.CallDetailsEntries; +import com.android.dialer.callcomposer.CallComposerContact; +import com.android.dialer.calldetails.CallDetailsEntries; import com.android.dialer.common.LogUtil; import com.android.dialer.compat.CompatUtils; import com.android.dialer.lightbringer.Lightbringer; import com.android.dialer.lightbringer.LightbringerComponent; +import com.android.dialer.logging.ContactSource; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.logging.ScreenEvent; import com.android.dialer.phonenumbercache.CachedNumberLookupService; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.phonenumbercache.PhoneNumberCache; @@ -76,7 +82,9 @@ import com.android.dialer.util.DialerUtils; * CallLogAdapter. */ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder - implements View.OnClickListener, MenuItem.OnMenuItemClickListener { + implements View.OnClickListener, + MenuItem.OnMenuItemClickListener, + View.OnCreateContextMenuListener { /** The root view of the call log list item */ public final View rootView; /** The quick contact badge for the contact. */ @@ -98,7 +106,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder private final CachedNumberLookupService mCachedNumberLookupService; private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; private final OnClickListener mBlockReportListener; - private final int mPhotoSize; /** Whether the data fields are populated by the worker thread, ready to be shown. */ public boolean isLoaded; /** The view containing call log item actions. Null until the ViewStub is inflated. */ @@ -237,7 +244,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder this.primaryActionButtonView = primaryActionButtonView; this.workIconView = (ImageView) rootView.findViewById(R.id.work_profile_icon); this.checkBoxView = (ImageView) rootView.findViewById(R.id.quick_contact_checkbox); - mPhotoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size); // Set text height to false on the TextViews so they don't have extra padding. phoneCallDetailsViews.nameView.setElegantTextHeight(false); @@ -249,7 +255,11 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder } primaryActionButtonView.setOnClickListener(this); primaryActionView.setOnClickListener(mExpandCollapseListener); - primaryActionView.setOnLongClickListener(longPressListener); + if (mVoicemailPlaybackPresenter != null) { + primaryActionView.setOnLongClickListener(longPressListener); + } else { + primaryActionView.setOnCreateContextMenuListener(this); + } } public static CallLogListItemViewHolder create( @@ -689,36 +699,23 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder return; } - final String lookupKey = - info.lookupUri != null ? UriUtils.getLookupKeyFromUri(info.lookupUri) : null; final String displayName = TextUtils.isEmpty(info.name) ? displayNumber : info.name; - final DefaultImageRequest request = - new DefaultImageRequest(displayName, lookupKey, getContactType(), true /* isCircular */); - - if (info.photoId == 0 && info.photoUri != null) { - ContactPhotoManager.getInstance(mContext) - .loadPhoto( - quickContactView, - info.photoUri, - mPhotoSize, - false /* darkTheme */, - true /* isCircular */, - request); - } else { - ContactPhotoManager.getInstance(mContext) - .loadThumbnail( - quickContactView, - info.photoId, - false /* darkTheme */, - true /* isCircular */, - request); - } + ContactPhotoManager.getInstance(mContext) + .loadDialerThumbnailOrPhoto( + quickContactView, + info.lookupUri, + info.photoId, + info.photoUri, + displayName, + getContactType()); } private int getContactType() { int contactType = ContactPhotoManager.TYPE_DEFAULT; if (mCallLogCache.isVoicemailNumber(accountHandle, number)) { contactType = ContactPhotoManager.TYPE_VOICEMAIL; + } else if (isSpam) { + contactType = ContactPhotoManager.TYPE_SPAM; } else if (mCachedNumberLookupService != null && mCachedNumberLookupService.isBusiness(info.sourceType)) { contactType = ContactPhotoManager.TYPE_BUSINESS; @@ -742,6 +739,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder CallSubjectDialog.start( (Activity) mContext, info.photoId, + info.photoUri, info.lookupUri, (String) nameOrNumber /* top line of contact view in call subject dialog */, number, @@ -802,18 +800,24 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder } private CallComposerContact buildContact() { - CallComposerContact contact = new CallComposerContact(); - contact.photoId = info.photoId; - contact.photoUri = info.photoUri == null ? null : info.photoUri.toString(); - contact.contactUri = info.lookupUri == null ? null : info.lookupUri.toString(); - contact.nameOrNumber = (String) nameOrNumber; - contact.contactType = getContactType(); - contact.number = number; + CallComposerContact.Builder contact = CallComposerContact.newBuilder(); + contact.setPhotoId(info.photoId); + if (info.photoUri != null) { + contact.setPhotoUri(info.photoUri.toString()); + } + if (info.lookupUri != null) { + contact.setContactUri(info.lookupUri.toString()); + } + contact.setNameOrNumber((String) nameOrNumber); + contact.setContactType(getContactType()); + contact.setNumber(number); /* second line of contact view. */ - contact.displayNumber = TextUtils.isEmpty(info.name) ? null : displayNumber; + if (!TextUtils.isEmpty(info.name)) { + contact.setDisplayNumber(displayNumber); + } /* phone number type (e.g. mobile) in second line of contact view */ - contact.numberLabel = numberType; - return contact; + contact.setNumberLabel(numberType); + return contact.build(); } private void logCallLogAction(int id) { @@ -876,6 +880,104 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder return LightbringerComponent.get(mContext).getLightbringer(); } + @Override + public void onCreateContextMenu( + final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + if (TextUtils.isEmpty(number)) { + return; + } + + if (callType == CallLog.Calls.VOICEMAIL_TYPE) { + menu.setHeaderTitle(mContext.getResources().getText(R.string.voicemail)); + } else { + menu.setHeaderTitle( + PhoneNumberUtilsCompat.createTtsSpannable( + BidiFormatter.getInstance().unicodeWrap(number, TextDirectionHeuristics.LTR))); + } + + menu.add( + ContextMenu.NONE, + R.id.context_menu_copy_to_clipboard, + ContextMenu.NONE, + R.string.action_copy_number_text) + .setOnMenuItemClickListener(this); + + // The edit number before call does not show up if any of the conditions apply: + // 1) Number cannot be called + // 2) Number is the voicemail number + // 3) Number is a SIP address + + if (PhoneNumberHelper.canPlaceCallsTo(number, numberPresentation) + && !mCallLogCache.isVoicemailNumber(accountHandle, number) + && !PhoneNumberHelper.isSipNumber(number)) { + menu.add( + ContextMenu.NONE, + R.id.context_menu_edit_before_call, + ContextMenu.NONE, + R.string.action_edit_number_before_call) + .setOnMenuItemClickListener(this); + } + + if (callType == CallLog.Calls.VOICEMAIL_TYPE + && phoneCallDetailsViews.voicemailTranscriptionView.length() > 0) { + menu.add( + ContextMenu.NONE, + R.id.context_menu_copy_transcript_to_clipboard, + ContextMenu.NONE, + R.string.copy_transcript_text) + .setOnMenuItemClickListener(this); + } + + String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso); + boolean isVoicemailNumber = mCallLogCache.isVoicemailNumber(accountHandle, number); + if (!isVoicemailNumber + && FilteredNumbersUtil.canBlockNumber(mContext, e164Number, number) + && FilteredNumberCompat.canAttemptBlockOperations(mContext)) { + boolean isBlocked = blockId != null; + if (isBlocked) { + menu.add( + ContextMenu.NONE, + R.id.context_menu_unblock, + ContextMenu.NONE, + R.string.call_log_action_unblock_number) + .setOnMenuItemClickListener(this); + } else { + if (isSpamFeatureEnabled) { + if (isSpam) { + menu.add( + ContextMenu.NONE, + R.id.context_menu_report_not_spam, + ContextMenu.NONE, + R.string.call_log_action_remove_spam) + .setOnMenuItemClickListener(this); + menu.add( + ContextMenu.NONE, + R.id.context_menu_block, + ContextMenu.NONE, + R.string.call_log_action_block_number) + .setOnMenuItemClickListener(this); + } else { + menu.add( + ContextMenu.NONE, + R.id.context_menu_block_report_spam, + ContextMenu.NONE, + R.string.call_log_action_block_report_number) + .setOnMenuItemClickListener(this); + } + } else { + menu.add( + ContextMenu.NONE, + R.id.context_menu_block, + ContextMenu.NONE, + R.string.call_log_action_block_number) + .setOnMenuItemClickListener(this); + } + } + } + + Logger.get(mContext).logScreenView(ScreenEvent.Type.CALL_LOG_CONTEXT_MENU, (Activity) mContext); + } + public interface OnClickListener { void onBlockReportSpam( @@ -883,21 +985,21 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder String number, String countryIso, int callType, - int contactSourceType); + ContactSource.Type contactSourceType); void onBlock( String displayNumber, String number, String countryIso, int callType, - int contactSourceType); + ContactSource.Type contactSourceType); void onUnblock( String displayNumber, String number, String countryIso, int callType, - int contactSourceType, + ContactSource.Type contactSourceType, boolean isSpam, Integer blockId); @@ -906,6 +1008,6 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder String number, String countryIso, int callType, - int contactSourceType); + ContactSource.Type contactSourceType); } } diff --git a/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java index dfe5776d8..0007d1863 100644 --- a/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/DefaultVoicemailNotifier.java @@ -16,6 +16,7 @@ package com.android.dialer.app.calllog; +import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -26,11 +27,17 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import android.os.PersistableBundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; +import android.support.v4.os.BuildCompat; import android.support.v4.util.Pair; +import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; @@ -42,9 +49,11 @@ import com.android.dialer.app.calllog.CallLogNotificationsQueryHelper.NewCall; import com.android.dialer.app.contactinfo.ContactPhotoLoader; import com.android.dialer.app.list.DialtactsPagerAdapter; import com.android.dialer.blocking.FilteredNumbersUtil; +import com.android.dialer.calllogutils.PhoneAccountUtils; +import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; import com.android.dialer.notification.NotificationChannelManager; import com.android.dialer.notification.NotificationChannelManager.Channel; import com.android.dialer.phonenumbercache.ContactInfo; @@ -58,9 +67,12 @@ public class DefaultVoicemailNotifier { public static final String TAG = "VoicemailNotifier"; /** The tag used to identify notifications from this class. */ - static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier"; + static final String VISUAL_VOICEMAIL_NOTIFICATION_TAG = "DefaultVoicemailNotifier"; /** The identifier of the notification of new voicemails. */ - private static final int NOTIFICATION_ID = R.id.notification_voicemail; + private static final int VISUAL_VOICEMAIL_NOTIFICATION_ID = R.id.notification_visual_voicemail; + + private static final int LEGACY_VOICEMAIL_NOTIFICATION_ID = R.id.notification_legacy_voicemail; + private static final String LEGACY_VOICEMAIL_NOTIFICATION_TAG = "legacy_voicemail"; private final Context context; private final CallLogNotificationsQueryHelper queryHelper; @@ -159,18 +171,102 @@ public class DefaultVoicemailNotifier { Channel.VOICEMAIL, PhoneAccountHandles.getAccount(context, newCalls.get(0))); - LogUtil.i(TAG, "Creating voicemail notification"); - getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID, groupSummary.build()); + LogUtil.i(TAG, "Creating visual voicemail notification"); + getNotificationManager() + .notify( + VISUAL_VOICEMAIL_NOTIFICATION_TAG, + VISUAL_VOICEMAIL_NOTIFICATION_ID, + groupSummary.build()); for (NewCall voicemail : newCalls) { getNotificationManager() .notify( voicemail.callsUri.toString(), - NOTIFICATION_ID, + VISUAL_VOICEMAIL_NOTIFICATION_ID, createNotificationForVoicemail(voicemail, contactInfos)); } } + /** + * Replicates how packages/services/Telephony/NotificationMgr.java handles legacy voicemail + * notification. The notification will not be stackable because no information is available for + * individual voicemails. + */ + @TargetApi(VERSION_CODES.O) + public void notifyLegacyVoicemail( + @NonNull PhoneAccountHandle phoneAccountHandle, + int count, + String voicemailNumber, + PendingIntent callVoicemailIntent, + PendingIntent voicemailSettingIntent) { + Assert.isNotNull(phoneAccountHandle); + Assert.checkArgument(BuildCompat.isAtLeastO()); + TelephonyManager telephonyManager = + context + .getSystemService(TelephonyManager.class) + .createForPhoneAccountHandle(phoneAccountHandle); + Assert.isNotNull(telephonyManager); + LogUtil.i(TAG, "Creating legacy voicemail notification"); + + PersistableBundle carrierConfig = telephonyManager.getCarrierConfig(); + + String notificationTitle = + context + .getResources() + .getQuantityString(R.plurals.notification_voicemail_title, count, count); + + TelecomManager telecomManager = context.getSystemService(TelecomManager.class); + PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle); + + String notificationText; + PendingIntent pendingIntent; + + if (voicemailSettingIntent != null) { + // If the voicemail number if unknown, instead of calling voicemail, take the user + // to the voicemail settings. + notificationText = context.getString(R.string.notification_voicemail_no_vm_number); + pendingIntent = voicemailSettingIntent; + } else { + if (PhoneAccountUtils.getSubscriptionPhoneAccounts(context).size() > 1) { + notificationText = phoneAccount.getShortDescription().toString(); + } else { + notificationText = + String.format( + context.getString(R.string.notification_voicemail_text_format), + PhoneNumberUtils.formatNumber(voicemailNumber)); + } + pendingIntent = callVoicemailIntent; + } + Notification.Builder builder = new Notification.Builder(context); + builder + .setSmallIcon(android.R.drawable.stat_notify_voicemail) + .setColor(context.getColor(R.color.dialer_theme_color)) + .setWhen(System.currentTimeMillis()) + .setContentTitle(notificationTitle) + .setContentText(notificationText) + .setContentIntent(pendingIntent) + .setSound(telephonyManager.getVoicemailRingtoneUri(phoneAccountHandle)) + .setOngoing( + carrierConfig.getBoolean( + CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL)); + + if (telephonyManager.isVoicemailVibrationEnabled(phoneAccountHandle)) { + builder.setDefaults(Notification.DEFAULT_VIBRATE); + } + + NotificationChannelManager.applyChannel( + builder, context, Channel.VOICEMAIL, phoneAccountHandle); + Notification notification = builder.build(); + getNotificationManager() + .notify(LEGACY_VOICEMAIL_NOTIFICATION_TAG, LEGACY_VOICEMAIL_NOTIFICATION_ID, notification); + } + + public void cancelLegacyNotification() { + LogUtil.i(TAG, "Clearing legacy voicemail notification"); + getNotificationManager() + .cancel(LEGACY_VOICEMAIL_NOTIFICATION_TAG, LEGACY_VOICEMAIL_NOTIFICATION_ID); + } + /** * Determines which ringtone Uri and Notification defaults to use when updating the notification * for the given call. @@ -268,7 +364,7 @@ public class DefaultVoicemailNotifier { return new Notification.Builder(context) .setSmallIcon(android.R.drawable.stat_notify_voicemail) .setColor(context.getColor(R.color.dialer_theme_color)) - .setGroup(NOTIFICATION_TAG) + .setGroup(VISUAL_VOICEMAIL_NOTIFICATION_TAG) .setOnlyAlertOnce(true) .setAutoCancel(true); } diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java index 8b77c3f51..a94c6781e 100644 --- a/java/com/android/dialer/app/calllog/IntentProvider.java +++ b/java/com/android/dialer/app/calllog/IntentProvider.java @@ -24,11 +24,11 @@ import android.provider.ContactsContract; import android.telecom.PhoneAccountHandle; import com.android.contacts.common.model.Contact; import com.android.contacts.common.model.ContactLoader; -import com.android.dialer.callcomposer.nano.CallComposerContact; +import com.android.dialer.callcomposer.CallComposerContact; import com.android.dialer.calldetails.CallDetailsActivity; -import com.android.dialer.calldetails.nano.CallDetailsEntries; +import com.android.dialer.calldetails.CallDetailsEntries; +import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; -import com.android.dialer.callintent.nano.CallInitiationType; import com.android.dialer.lightbringer.LightbringerComponent; import com.android.dialer.util.CallUtil; import com.android.dialer.util.IntentUtil; diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java index aa04d81eb..de07bb437 100644 --- a/java/com/android/dialer/app/calllog/MissedCallNotifier.java +++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java @@ -41,8 +41,8 @@ import com.android.dialer.app.R; import com.android.dialer.app.calllog.CallLogNotificationsQueryHelper.NewCall; import com.android.dialer.app.contactinfo.ContactPhotoLoader; import com.android.dialer.app.list.DialtactsPagerAdapter; +import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; -import com.android.dialer.callintent.nano.CallInitiationType; import com.android.dialer.common.LogUtil; import com.android.dialer.notification.NotificationChannelManager; import com.android.dialer.notification.NotificationChannelManager.Channel; diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java index bc78eda02..0c720775a 100644 --- a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java +++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java @@ -25,13 +25,14 @@ import android.support.v4.content.ContextCompat; import android.telecom.PhoneAccount; import android.text.TextUtils; import android.text.format.DateUtils; +import android.text.util.Linkify; import android.view.View; import android.widget.TextView; import com.android.dialer.app.R; import com.android.dialer.app.calllog.calllogcache.CallLogCache; import com.android.dialer.calllogutils.PhoneCallDetails; +import com.android.dialer.logging.ContactSource; import com.android.dialer.oem.MotorolaUtils; -import com.android.dialer.phonenumbercache.CachedNumberLookupService.CachedContactInfo; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.util.DialerUtils; import java.util.ArrayList; @@ -142,6 +143,8 @@ public class PhoneCallDetailsHelper { views.nameView.setText(nameText); if (isVoicemail) { + int relevantLinkTypes = Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS; + views.voicemailTranscriptionView.setAutoLinkMask(relevantLinkTypes); views.voicemailTranscriptionView.setText( TextUtils.isEmpty(details.transcription) ? null : details.transcription); } @@ -230,7 +233,7 @@ public class PhoneCallDetailsHelper { return false; } // For caller ID provided by Cequint we want to show the geo location. - if (details.sourceType == CachedContactInfo.SOURCE_TYPE_CEQUINT_CALLER_ID) { + if (details.sourceType == ContactSource.Type.SOURCE_TYPE_CEQUINT_CALLER_ID) { return true; } // Don't bother showing geo location for contacts. diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java index 5e820a750..7c25e7d39 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java @@ -31,15 +31,15 @@ import com.android.dialer.app.voicemail.VoicemailAudioManager; import com.android.dialer.app.voicemail.VoicemailErrorManager; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter; import com.android.dialer.common.LogUtil; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; public class VisualVoicemailCallLogFragment extends CallLogFragment { private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver(); private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; - private VoicemailErrorManager mVoicemailAlertManager; + private VoicemailErrorManager mVoicemailErrorManager; public VisualVoicemailCallLogFragment() { super(CallLog.Calls.VOICEMAIL_TYPE); @@ -63,14 +63,14 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mVoicemailAlertManager = + mVoicemailErrorManager = new VoicemailErrorManager(getContext(), getAdapter().getAlertManager(), mModalAlertManager); getActivity() .getContentResolver() .registerContentObserver( VoicemailContract.Status.CONTENT_URI, true, - mVoicemailAlertManager.getContentObserver()); + mVoicemailErrorManager.getContentObserver()); } @Override @@ -84,13 +84,13 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { public void onResume() { super.onResume(); mVoicemailPlaybackPresenter.onResume(); - mVoicemailAlertManager.onResume(); + mVoicemailErrorManager.onResume(); } @Override public void onPause() { mVoicemailPlaybackPresenter.onPause(); - mVoicemailAlertManager.onPause(); + mVoicemailErrorManager.onPause(); super.onPause(); } @@ -98,8 +98,9 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { public void onDestroy() { getActivity() .getContentResolver() - .unregisterContentObserver(mVoicemailAlertManager.getContentObserver()); + .unregisterContentObserver(mVoicemailErrorManager.getContentObserver()); mVoicemailPlaybackPresenter.onDestroy(); + mVoicemailErrorManager.onDestroy(); getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver); super.onDestroy(); } @@ -131,6 +132,9 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { public void onNotVisible() { LogUtil.enterBlock("VisualVoicemailCallLogFragment.onPageUnselected"); super.onNotVisible(); + if (getAdapter() != null && getAdapter().mActionMode != null) { + getAdapter().mActionMode.finish(); + } if (getActivity() != null) { getActivity().setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE); } diff --git a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java index 2aa3fb282..024394728 100644 --- a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java +++ b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java @@ -76,8 +76,8 @@ public class VoicemailQueryHandler extends AsyncQueryHandler { GroupedNotificationUtil.removeNotification( mContext.getSystemService(NotificationManager.class), voicemailUri != null ? voicemailUri.toString() : null, - R.id.notification_voicemail, - DefaultVoicemailNotifier.NOTIFICATION_TAG); + R.id.notification_visual_voicemail, + DefaultVoicemailNotifier.VISUAL_VOICEMAIL_NOTIFICATION_TAG); } @Override -- cgit v1.2.3