diff options
Diffstat (limited to 'java/com/android/dialer/app/calllog')
13 files changed, 370 insertions, 353 deletions
diff --git a/java/com/android/dialer/app/calllog/CallLogActivity.java b/java/com/android/dialer/app/calllog/CallLogActivity.java index 35e05bc39..1bb894c59 100644 --- a/java/com/android/dialer/app/calllog/CallLogActivity.java +++ b/java/com/android/dialer/app/calllog/CallLogActivity.java @@ -32,10 +32,9 @@ import android.view.ViewGroup; import com.android.contacts.common.list.ViewPagerTabs; import com.android.dialer.app.DialtactsActivity; import com.android.dialer.app.R; -import com.android.dialer.app.calllog.ClearCallLogDialog.Listener; import com.android.dialer.calldetails.CallDetailsActivity; +import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.database.CallLogQueryHandler; -import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ScreenEvent; import com.android.dialer.logging.UiAction; @@ -46,7 +45,7 @@ import com.android.dialer.util.ViewUtil; /** Activity for viewing call history. */ public class CallLogActivity extends TransactionSafeActivity - implements ViewPager.OnPageChangeListener, Listener { + implements ViewPager.OnPageChangeListener { private static final int TAB_INDEX_ALL = 0; private static final int TAB_INDEX_MISSED = 1; @@ -148,7 +147,7 @@ public class CallLogActivity extends TransactionSafeActivity startActivity(intent); return true; } else if (item.getItemId() == R.id.delete_all) { - ClearCallLogDialog.show(getFragmentManager(), this); + ClearCallLogDialog.show(getFragmentManager()); return true; } return super.onOptionsItemSelected(item); @@ -184,15 +183,6 @@ public class CallLogActivity extends TransactionSafeActivity } @Override - public void callHistoryDeleted() { - if (EnrichedCallComponent.get(this).getEnrichedCallManager().hasStoredData()) { - Snackbar.make( - findViewById(R.id.calllog_frame), getString(R.string.multiple_ec_data_deleted), 5_000) - .show(); - } - } - - @Override public void onBackPressed() { PerformanceReport.recordClick(UiAction.Type.PRESS_ANDROID_BACK_BUTTON); super.onBackPressed(); @@ -245,7 +235,7 @@ public class CallLogActivity extends TransactionSafeActivity @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == DialtactsActivity.ACTIVITY_REQUEST_CODE_CALL_DETAILS) { + if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) { if (resultCode == RESULT_OK && data != null && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) { diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java index e0cd1706d..61129a7ce 100644 --- a/java/com/android/dialer/app/calllog/CallLogAdapter.java +++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java @@ -74,7 +74,6 @@ import com.android.dialer.configprovider.ConfigProviderBindings; 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.HistoryResult; import com.android.dialer.lightbringer.Lightbringer; import com.android.dialer.lightbringer.LightbringerComponent; import com.android.dialer.lightbringer.LightbringerListener; @@ -90,8 +89,6 @@ import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.spam.Spam; import com.android.dialer.util.PermissionsUtil; import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Set; @@ -344,14 +341,29 @@ public class CallLogAdapter extends GroupingListAdapter // If enriched call capabilities were unknown on the initial load, // viewHolder.isCallComposerCapable may be unset. Check here if we have the capabilities // as a last attempt at getting them before showing the expanded view to the user - EnrichedCallCapabilities capabilities = - getEnrichedCallManager().getCapabilities(viewHolder.number); - viewHolder.isCallComposerCapable = - capabilities != null && capabilities.supportsCallComposer(); - generateAndMapNewCallDetailsEntriesHistoryResults( - viewHolder.number, - viewHolder.getDetailedPhoneDetails(), - getAllHistoricalData(viewHolder.number, viewHolder.getDetailedPhoneDetails())); + EnrichedCallCapabilities capabilities = null; + + if (viewHolder.number != null) { + capabilities = getEnrichedCallManager().getCapabilities(viewHolder.number); + } + + if (capabilities == null) { + capabilities = EnrichedCallCapabilities.NO_CAPABILITIES; + } + + viewHolder.isCallComposerCapable = capabilities.isCallComposerCapable(); + + if (capabilities.isTemporarilyUnavailable()) { + LogUtil.i( + "mExpandCollapseListener.onClick", + "%s is temporarily unavailable, requesting capabilities", + LogUtil.sanitizePhoneNumber(viewHolder.number)); + // Refresh the capabilities when temporarily unavailable, see go/ec-temp-unavailable. + // Similarly to when we request capabilities the first time, the 'Share and call' button + // won't pop in with the new capabilities. Instead the row needs to be collapsed and + // expanded again. + getEnrichedCallManager().requestCapabilities(viewHolder.number); + } if (viewHolder.rowId == mCurrentlyExpandedRowId) { // Hide actions, if the clicked item is the expanded item. @@ -454,7 +466,7 @@ public class CallLogAdapter extends GroupingListAdapter * Holds a list of URIs that are pending deletion or undo. If the activity ends before the undo * timeout, all of the pending URIs will be deleted. * - * <p>TODO: move this and OnVoicemailDeletedListener to somewhere like {@link + * <p>TODO(twyen): move this and OnVoicemailDeletedListener to somewhere like {@link * VisualVoicemailCallLogFragment}. The CallLogAdapter does not need to know about what to do with * hidden item or what to hide. */ @@ -817,12 +829,7 @@ 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); - CallDetailsEntries updatedCallDetailsEntries = - generateAndMapNewCallDetailsEntriesHistoryResults( - views.number, - callDetailsEntries, - getAllHistoricalData(views.number, callDetailsEntries)); - views.setDetailedPhoneDetails(updatedCallDetailsEntries); + views.setDetailedPhoneDetails(callDetailsEntries); views.lightbringerReady = getLightbringer().isReachable(mActivity, views.number); final AsyncTask<Void, Void, Boolean> loadDataTask = new AsyncTask<Void, Void, Boolean>() { @@ -879,46 +886,7 @@ public class CallLogAdapter extends GroupingListAdapter getEnrichedCallManager().requestCapabilities(number); return false; } - return capabilities.supportsCallComposer(); - } - - @NonNull - private Map<CallDetailsEntry, List<HistoryResult>> getAllHistoricalData( - @Nullable String number, @NonNull CallDetailsEntries entries) { - if (number == null) { - return Collections.emptyMap(); - } - - Map<CallDetailsEntry, List<HistoryResult>> historicalData = - getEnrichedCallManager().getAllHistoricalData(number, entries); - if (historicalData == null) { - getEnrichedCallManager().requestAllHistoricalData(number, entries); - return Collections.emptyMap(); - } - return historicalData; - } - - private static CallDetailsEntries generateAndMapNewCallDetailsEntriesHistoryResults( - @Nullable String number, - @NonNull CallDetailsEntries callDetailsEntries, - @NonNull Map<CallDetailsEntry, List<HistoryResult>> mappedResults) { - if (number == null) { - return callDetailsEntries; - } - CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder(); - for (CallDetailsEntry entry : callDetailsEntries.getEntriesList()) { - CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry); - List<HistoryResult> results = mappedResults.get(entry); - if (results != null) { - newEntry.addAllHistoryResults(mappedResults.get(entry)); - LogUtil.v( - "CallLogAdapter.generateAndMapNewCallDetailsEntriesHistoryResults", - "mapped %d results", - newEntry.getHistoryResultsList().size()); - } - mutableCallDetailsEntries.addEntries(newEntry.build()); - } - return mutableCallDetailsEntries.build(); + return capabilities.isCallComposerCapable(); } /** @@ -936,6 +904,10 @@ public class CallLogAdapter extends GroupingListAdapter (VERSION.SDK_INT >= VERSION_CODES.N) ? cursor.getString(CallLogQuery.VIA_NUMBER) : ""; final int numberPresentation = cursor.getInt(CallLogQuery.NUMBER_PRESENTATION); final ContactInfo cachedContactInfo = ContactInfoHelper.getContactInfo(cursor); + final int transcriptionState = + (VERSION.SDK_INT >= VERSION_CODES.O) + ? cursor.getInt(CallLogQuery.TRANSCRIPTION_STATE) + : PhoneCallDetailsHelper.TRANSCRIPTION_NOT_STARTED; final PhoneCallDetails details = new PhoneCallDetails(number, numberPresentation, postDialDigits); details.viaNumber = viaNumber; @@ -945,6 +917,7 @@ public class CallLogAdapter extends GroupingListAdapter details.features = getCallFeatures(cursor, count); details.geocode = cursor.getString(CallLogQuery.GEOCODED_LOCATION); details.transcription = cursor.getString(CallLogQuery.TRANSCRIPTION); + details.transcriptionState = transcriptionState; details.callTypes = getCallTypes(cursor, count); details.accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME); @@ -977,7 +950,7 @@ public class CallLogAdapter extends GroupingListAdapter } @MainThread - private static CallDetailsEntries createCallDetailsEntries(Cursor cursor, int count) { + private CallDetailsEntries createCallDetailsEntries(Cursor cursor, int count) { Assert.isMainThread(); int position = cursor.getPosition(); CallDetailsEntries.Builder entries = CallDetailsEntries.newBuilder(); @@ -990,6 +963,16 @@ public class CallLogAdapter extends GroupingListAdapter .setDate(cursor.getLong(CallLogQuery.DATE)) .setDuration(cursor.getLong(CallLogQuery.DURATION)) .setFeatures(cursor.getInt(CallLogQuery.FEATURES)); + + String phoneAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME); + if (getLightbringer().getPhoneAccountComponentName() != null + && getLightbringer() + .getPhoneAccountComponentName() + .flattenToString() + .equals(phoneAccountComponentName)) { + entry.setIsLightbringerCall(true); + } + entries.addEntries(entry.build()); cursor.moveToNext(); } 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 99c49b7af..60ed7dd09 100644 --- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java +++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.AsyncTask; +import android.os.Bundle; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract.CommonDataKinds.Phone; @@ -50,12 +51,8 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.android.contacts.common.ClipboardUtils; -import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.dialog.CallSubjectDialog; -import com.android.contacts.common.lettertiles.LetterTileDrawable; -import com.android.contacts.common.lettertiles.LetterTileDrawable.ContactType; -import com.android.contacts.common.util.UriUtils; import com.android.dialer.app.DialtactsActivity; import com.android.dialer.app.R; import com.android.dialer.app.calllog.CallLogAdapter.OnActionModeStateChangedListener; @@ -72,9 +69,14 @@ import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.compat.CompatUtils; +import com.android.dialer.compat.telephony.TelephonyManagerCompat; import com.android.dialer.configprovider.ConfigProviderBindings; +import com.android.dialer.constants.ActivityRequestCodes; +import com.android.dialer.contactphoto.ContactPhotoManager; import com.android.dialer.dialercontact.DialerContact; import com.android.dialer.dialercontact.SimDetails; +import com.android.dialer.lettertile.LetterTileDrawable; +import com.android.dialer.lettertile.LetterTileDrawable.ContactType; import com.android.dialer.lightbringer.Lightbringer; import com.android.dialer.lightbringer.LightbringerComponent; import com.android.dialer.logging.ContactSource; @@ -91,6 +93,7 @@ import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.CallUtil; import com.android.dialer.util.DialerUtils; +import com.android.dialer.util.UriUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -164,7 +167,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder * The callable phone number for the current call log entry. Cached here as the call back intent * is set only when the actions ViewStub is inflated. */ - public String number; + @Nullable public String number; /** The post-dial numbers that are dialed following the phone number. */ public String postDialDigits; /** The formatted phone number to display. */ @@ -584,8 +587,12 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder return; } + TextView callTypeOrLocationView = + ((TextView) callButtonView.findViewById(R.id.call_type_or_location_text)); + if (canPlaceCallToNumber) { callButtonView.setTag(IntentProvider.getReturnCallIntentProvider(number)); + callTypeOrLocationView.setVisibility(View.GONE); } if (!TextUtils.isEmpty(voicemailUri) && canPlaceCallToNumber) { @@ -594,13 +601,10 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder TextUtils.expandTemplate( mContext.getString(R.string.call_log_action_call), nameOrNumber == null ? "" : nameOrNumber)); - TextView callTypeOrLocationView = - ((TextView) callButtonView.findViewById(R.id.call_type_or_location_text)); + if (callType == Calls.VOICEMAIL_TYPE && !TextUtils.isEmpty(callTypeOrLocation)) { callTypeOrLocationView.setText(callTypeOrLocation); callTypeOrLocationView.setVisibility(View.VISIBLE); - } else { - callTypeOrLocationView.setVisibility(View.GONE); } callButtonView.setVisibility(View.VISIBLE); } @@ -774,12 +778,23 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder return; } - final TextView view = phoneCallDetailsViews.voicemailTranscriptionView; - if (!isExpanded || TextUtils.isEmpty(view.getText())) { - view.setVisibility(View.GONE); + View transcriptContainerView = phoneCallDetailsViews.transcriptionView; + TextView transcriptView = phoneCallDetailsViews.voicemailTranscriptionView; + TextView transcriptBrandingView = phoneCallDetailsViews.voicemailTranscriptionBrandingView; + if (TextUtils.isEmpty(transcriptView.getText())) { + Assert.checkArgument(TextUtils.isEmpty(transcriptBrandingView.getText())); + } + if (!isExpanded || TextUtils.isEmpty(transcriptView.getText())) { + transcriptContainerView.setVisibility(View.GONE); return; } - view.setVisibility(View.VISIBLE); + transcriptContainerView.setVisibility(View.VISIBLE); + transcriptView.setVisibility(View.VISIBLE); + if (TextUtils.isEmpty(transcriptBrandingView.getText())) { + phoneCallDetailsViews.voicemailTranscriptionBrandingView.setVisibility(View.GONE); + } else { + phoneCallDetailsViews.voicemailTranscriptionBrandingView.setVisibility(View.VISIBLE); + } } public void updatePhoto() { @@ -868,7 +883,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder Activity activity = (Activity) mContext; activity.startActivityForResult( CallComposerActivity.newIntent(activity, buildContact()), - DialtactsActivity.ACTIVITY_REQUEST_CODE_CALL_COMPOSE); + ActivityRequestCodes.DIALTACTS_CALL_COMPOSER); } else if (view.getId() == R.id.share_voicemail) { Logger.get(mContext).logImpression(DialerImpression.Type.VVM_SHARE_PRESSED); mVoicemailPlaybackPresenter.shareVoicemail(); @@ -886,6 +901,15 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder return; } + if (info != null && info.lookupKey != null) { + Bundle extras = new Bundle(); + if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) { + extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS); + } + extras.putBoolean(TelephonyManagerCompat.ALLOW_ASSISTED_DIAL, true); + intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); + } + // We check to see if we are starting a Lightbringer intent. The reason is Lightbringer // intents need to be started using startActivityForResult instead of the usual startActivity String packageName = intent.getPackage(); @@ -896,7 +920,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder } else if (CallDetailsActivity.isLaunchIntent(intent)) { PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_DETAIL); ((Activity) mContext) - .startActivityForResult(intent, DialtactsActivity.ACTIVITY_REQUEST_CODE_CALL_DETAILS); + .startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_CALL_DETAILS); } else { if (Intent.ACTION_CALL.equals(intent.getAction()) && intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, -1) @@ -912,7 +936,7 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder private void startLightbringerActivity(Intent intent) { try { Activity activity = (Activity) mContext; - activity.startActivityForResult(intent, DialtactsActivity.ACTIVITY_REQUEST_CODE_LIGHTBRINGER); + activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_LIGHTBRINGER); } catch (ActivityNotFoundException e) { Toast.makeText(mContext, R.string.activity_not_available, Toast.LENGTH_SHORT).show(); } @@ -931,7 +955,9 @@ public final class CallLogListItemViewHolder extends RecyclerView.ViewHolder contact.setNameOrNumber((String) nameOrNumber); } contact.setContactType(getContactType()); - contact.setNumber(number); + if (number != null) { + contact.setNumber(number); + } /* second line of contact view. */ if (!TextUtils.isEmpty(info.name)) { contact.setDisplayNumber(displayNumber); diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java index be1ebfb6d..0490b9932 100644 --- a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java +++ b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java @@ -24,7 +24,13 @@ import android.net.Uri; 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; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.PermissionsUtil; @@ -44,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 = @@ -64,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. * @@ -79,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() { @@ -108,6 +121,15 @@ public class CallLogNotificationsService extends IntentService { context.startService(serviceIntent); } + public static void cancelAllMissedCalls(Context context) { + LogUtil.enterBlock("CallLogNotificationsService.cancelAllMissedCalls"); + DialerExecutorComponent.get(context) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new CancelAllMissedCallsWorker()) + .build() + .executeSerial(context); + } + public static PendingIntent createMarkAllNewVoicemailsAsOldIntent(@NonNull Context context) { Intent intent = new Intent(context, CallLogNotificationsService.class); intent.setAction(CallLogNotificationsService.ACTION_MARK_ALL_NEW_VOICEMAILS_AS_OLD); @@ -122,13 +144,6 @@ public class CallLogNotificationsService extends IntentService { return PendingIntent.getService(context, 0, intent, 0); } - public static void cancelAllMissedCalls(@NonNull Context context) { - LogUtil.enterBlock("CallLogNotificationsService.cancelAllMissedCalls"); - Intent serviceIntent = new Intent(context, CallLogNotificationsService.class); - serviceIntent.setAction(ACTION_CANCEL_ALL_MISSED_CALLS); - context.startService(serviceIntent); - } - public static PendingIntent createCancelAllMissedCallsPendingIntent(@NonNull Context context) { Intent intent = new Intent(context, CallLogNotificationsService.class); intent.setAction(ACTION_CANCEL_ALL_MISSED_CALLS); @@ -143,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) { @@ -168,15 +191,17 @@ 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); MissedCallNotifier.getIstance(this).insertPostCallNotification(phoneNumber, note); break; case ACTION_CANCEL_ALL_MISSED_CALLS: - CallLogNotificationsQueryHelper.markAllMissedCallsInCallLogAsRead(this); - MissedCallNotifier.cancelAllMissedCallNotifications(this); - TelecomUtil.cancelMissedCallsNotification(this); + cancelAllMissedCalls(this); break; case ACTION_CANCEL_SINGLE_MISSED_CALL: Uri callUri = intent.getData(); @@ -196,4 +221,26 @@ public class CallLogNotificationsService extends IntentService { break; } } + + @WorkerThread + private static void cancelAllMissedCallsBackground(Context context) { + LogUtil.enterBlock("CallLogNotificationsService.cancelAllMissedCallsBackground"); + Assert.isWorkerThread(); + CallLogNotificationsQueryHelper.markAllMissedCallsInCallLogAsRead(context); + MissedCallNotifier.cancelAllMissedCallNotifications(context); + TelecomUtil.cancelMissedCallsNotification(context); + } + + /** Worker that cancels all missed call notifications and updates call log entries. */ + private static class CancelAllMissedCallsWorker implements Worker<Context, Void> { + + @Nullable + @Override + public Void doInBackground(@Nullable Context context) throws Throwable { + if (context != null) { + cancelAllMissedCallsBackground(context); + } + return null; + } + } } diff --git a/java/com/android/dialer/app/calllog/ClearCallLogDialog.java b/java/com/android/dialer/app/calllog/ClearCallLogDialog.java index 5c3d4d9fa..b16eb1beb 100644 --- a/java/com/android/dialer/app/calllog/ClearCallLogDialog.java +++ b/java/com/android/dialer/app/calllog/ClearCallLogDialog.java @@ -22,76 +22,63 @@ import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; import android.app.ProgressDialog; -import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; -import android.os.AsyncTask; import android.os.Bundle; import android.provider.CallLog.Calls; -import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import com.android.dialer.app.R; import com.android.dialer.common.Assert; +import com.android.dialer.common.concurrent.DialerExecutor; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.phonenumbercache.CachedNumberLookupService; import com.android.dialer.phonenumbercache.PhoneNumberCache; /** Dialog that clears the call log after confirming with the user */ public class ClearCallLogDialog extends DialogFragment { - private Listener listener; + private DialerExecutor<Void> clearCallLogTask; + private ProgressDialog progressDialog; /** Preferred way to show this dialog */ - public static void show(FragmentManager fragmentManager, @NonNull Listener listener) { + public static void show(FragmentManager fragmentManager) { ClearCallLogDialog dialog = new ClearCallLogDialog(); - dialog.listener = Assert.isNotNull(listener); dialog.show(fragmentManager, "deleteCallLog"); } @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + clearCallLogTask = + DialerExecutorComponent.get(getContext()) + .dialerExecutorFactory() + .createUiTaskBuilder( + getFragmentManager(), + "clearCallLogTask", + new ClearCallLogWorker(getActivity().getApplicationContext())) + .onSuccess(this::onSuccess) + .build(); + } + + @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - final ContentResolver resolver = getActivity().getContentResolver(); - final Context context = getActivity().getApplicationContext(); - final OnClickListener okListener = - new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final ProgressDialog progressDialog = - ProgressDialog.show( - getActivity(), getString(R.string.clearCallLogProgress_title), "", true, false); - progressDialog.setOwnerActivity(getActivity()); - CallLogNotificationsService.cancelAllMissedCalls(getContext()); - final AsyncTask<Void, Void, Void> task = - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - resolver.delete(Calls.CONTENT_URI, null, null); - CachedNumberLookupService cachedNumberLookupService = - PhoneNumberCache.get(context).getCachedNumberLookupService(); - if (cachedNumberLookupService != null) { - cachedNumberLookupService.clearAllCacheEntries(context); - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - final Activity activity = progressDialog.getOwnerActivity(); - - if (activity == null || activity.isDestroyed() || activity.isFinishing()) { - return; - } - - listener.callHistoryDeleted(); - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); - } - } - }; - // TODO: Once we have the API, we should configure this ProgressDialog - // to only show up after a certain time (e.g. 150ms) - progressDialog.show(); - task.execute(); - } + OnClickListener okListener = + (dialog, which) -> { + progressDialog = + ProgressDialog.show( + getActivity(), getString(R.string.clearCallLogProgress_title), "", true, false); + progressDialog.setOwnerActivity(getActivity()); + CallLogNotificationsService.cancelAllMissedCalls(getContext()); + + // TODO: Once we have the API, we should configure this ProgressDialog + // to only show up after a certain time (e.g. 150ms) + progressDialog.show(); + + clearCallLogTask.executeSerial(null); }; return new AlertDialog.Builder(getActivity()) .setTitle(R.string.clearCallLogConfirmation_title) @@ -103,7 +90,48 @@ public class ClearCallLogDialog extends DialogFragment { .create(); } - interface Listener { - void callHistoryDeleted(); + private static class ClearCallLogWorker implements Worker<Void, Void> { + private final Context appContext; + + private ClearCallLogWorker(Context appContext) { + this.appContext = appContext; + } + + @Nullable + @Override + public Void doInBackground(@Nullable Void unused) throws Throwable { + appContext.getContentResolver().delete(Calls.CONTENT_URI, null, null); + CachedNumberLookupService cachedNumberLookupService = + PhoneNumberCache.get(appContext).getCachedNumberLookupService(); + if (cachedNumberLookupService != null) { + cachedNumberLookupService.clearAllCacheEntries(appContext); + } + return null; + } + } + + private void onSuccess(Void unused) { + Assert.isNotNull(progressDialog); + Activity activity = progressDialog.getOwnerActivity(); + + if (activity == null || activity.isDestroyed() || activity.isFinishing()) { + return; + } + + maybeShowEnrichedCallSnackbar(activity); + + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + + private void maybeShowEnrichedCallSnackbar(Activity activity) { + if (EnrichedCallComponent.get(activity).getEnrichedCallManager().hasStoredData()) { + Snackbar.make( + activity.findViewById(R.id.calllog_frame), + getString(R.string.multiple_ec_data_deleted), + 5_000) + .show(); + } } } diff --git a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java index 428c71677..584f07fe3 100644 --- a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java @@ -18,7 +18,6 @@ package com.android.dialer.app.calllog; import android.annotation.TargetApi; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.os.Build.VERSION_CODES; @@ -35,6 +34,7 @@ import com.android.dialer.app.R; import com.android.dialer.calllogutils.PhoneAccountUtils; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelManager; /** Shows a notification in the status bar for legacy vociemail. */ @@ -77,9 +77,7 @@ public final class LegacyVoicemailNotifier { callVoicemailIntent, voicemailSettingsIntent, isRefresh); - context - .getSystemService(NotificationManager.class) - .notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification); + DialerNotificationManager.notify(context, NOTIFICATION_TAG, NOTIFICATION_ID, notification); } @NonNull @@ -122,7 +120,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); @@ -148,8 +149,7 @@ public final class LegacyVoicemailNotifier { public static void cancelNotification(@NonNull Context context) { LogUtil.enterBlock("LegacyVoicemailNotifier.cancelNotification"); Assert.checkArgument(BuildCompat.isAtLeastO()); - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + DialerNotificationManager.cancel(context, NOTIFICATION_TAG, NOTIFICATION_ID); } private LegacyVoicemailNotifier() {} diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java index e0e3fdf3f..b363b5ab6 100644 --- a/java/com/android/dialer/app/calllog/MissedCallNotifier.java +++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java @@ -17,7 +17,6 @@ package com.android.dialer.app.calllog; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -48,7 +47,9 @@ import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelId; +import com.android.dialer.notification.NotificationManagerUtils; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.phonenumberutil.PhoneNumberHelper; import com.android.dialer.util.DialerUtils; @@ -59,9 +60,17 @@ import java.util.Set; /** Creates a notification for calls that the user missed (neither answered nor rejected). */ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { - static final String NOTIFICATION_TAG_PREFIX = "MissedCall_"; - static final String NOTIFICATION_GROUP = "MissedCall"; + /** Prefix used to generate a unique tag for each missed call notification. */ + private static final String NOTIFICATION_TAG_PREFIX = "MissedCall_"; + /** Common ID for all missed call notifications. */ private static final int NOTIFICATION_ID = 1; + /** Tag for the group summary notification. */ + private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_MissedCall"; + /** + * Key used to associate all missed call notifications and the summary as belonging to a single + * group. + */ + private static final String GROUP_KEY = "MissedCallGroup"; private final Context context; private final CallLogNotificationsQueryHelper callLogNotificationsQueryHelper; @@ -202,33 +211,29 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { configureLedOnNotification(notification); LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification"); - getNotificationMgr().notify(getNotificationTagForGroupSummary(), NOTIFICATION_ID, notification); + DialerNotificationManager.notify( + context, GROUP_SUMMARY_NOTIFICATION_TAG, NOTIFICATION_ID, notification); if (useCallList) { // Do not repost active notifications to prevent erasing post call notes. - NotificationManager manager = getNotificationMgr(); Set<String> activeTags = new ArraySet<>(); - for (StatusBarNotification activeNotification : manager.getActiveNotifications()) { + for (StatusBarNotification activeNotification : + DialerNotificationManager.getActiveNotifications(context)) { activeTags.add(activeNotification.getTag()); } for (NewCall call : newCalls) { String callTag = getNotificationTagForCall(call); if (!activeTags.contains(callTag)) { - manager.notify(callTag, NOTIFICATION_ID, getNotificationForCall(call, null)); + DialerNotificationManager.notify( + context, callTag, NOTIFICATION_ID, getNotificationForCall(call, null)); } } } } public static void cancelAllMissedCallNotifications(@NonNull Context context) { - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String tag = notification.getTag(); - if (tag != null && tag.startsWith(NOTIFICATION_TAG_PREFIX)) { - notificationManager.cancel(tag, notification.getId()); - } - } + NotificationManagerUtils.cancelAllInGroup(context, GROUP_KEY); } public static void cancelSingleMissedCallNotification( @@ -239,31 +244,9 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { "unable to cancel notification, uri is null"); return; } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - String callTag = getNotificationTagForCallUri(callUri); - String summaryTag = getNotificationTagForGroupSummary(); - int notificationCount = 0; - - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String currentTag = notification.getTag(); - if (currentTag == null) { - continue; - } - if (currentTag.equals(callTag)) { - notificationManager.cancel(notification.getTag(), notification.getId()); - } else if (currentTag.startsWith(NOTIFICATION_TAG_PREFIX) && !currentTag.equals(summaryTag)) { - notificationCount++; - } - } - - if (notificationCount == 0) { - // There are no more missed call notifications. Remove the summary notification too. - notificationManager.cancel(summaryTag, NOTIFICATION_ID); - } - } - - private static String getNotificationTagForGroupSummary() { - return NOTIFICATION_TAG_PREFIX + "GroupSummary"; + // This will also dismiss the group summary if there are no more missed call notifications. + DialerNotificationManager.cancel( + context, getNotificationTagForCallUri(callUri), NOTIFICATION_ID); } private static String getNotificationTagForCall(@NonNull NewCall call) { @@ -280,11 +263,11 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { for (NewCall call : newCalls) { if (call.number.equals(number.replace("tel:", ""))) { // Update the first notification that matches our post call note sender. - getNotificationMgr() - .notify( - getNotificationTagForCall(call), - NOTIFICATION_ID, - getNotificationForCall(call, note)); + DialerNotificationManager.notify( + context, + getNotificationTagForCall(call), + NOTIFICATION_ID, + getNotificationForCall(call, note)); break; } } @@ -366,7 +349,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { private Notification.Builder createNotificationBuilder() { return new Notification.Builder(context) - .setGroup(NOTIFICATION_GROUP) + .setGroup(GROUP_KEY) .setSmallIcon(android.R.drawable.stat_notify_missed_call) .setColor(context.getResources().getColor(R.color.dialer_theme_color, null)) .setAutoCancel(true) @@ -466,8 +449,4 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> { private void closeSystemDialogs(Context context) { context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } - - private NotificationManager getNotificationMgr() { - return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - } } diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java index 0c720775a..c1a00e58d 100644 --- a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java +++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java @@ -45,6 +45,13 @@ public class PhoneCallDetailsHelper { /** The maximum number of icons will be shown to represent the call types in a group. */ private static final int MAX_CALL_TYPE_ICONS = 3; + // TODO(mdooley): remove when these api's become public + // Copied from android.provider.VoicemailContract + static final int TRANSCRIPTION_NOT_STARTED = 0; + static final int TRANSCRIPTION_IN_PROGRESS = 1; + static final int TRANSCRIPTION_FAILED = 2; + static final int TRANSCRIPTION_AVAILABLE = 3; + private final Context mContext; private final Resources mResources; private final CallLogCache mCallLogCache; @@ -145,14 +152,37 @@ public class PhoneCallDetailsHelper { 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); + boolean showTranscriptBranding = false; + if (!TextUtils.isEmpty(details.transcription)) { + views.voicemailTranscriptionView.setText(details.transcription); + + // Set the branding text if the voicemail was transcribed by google + // TODO(mdooley): the transcription state is only set by the google transcription code, + // but a better solution would be to check the SOURCE_PACKAGE + showTranscriptBranding = details.transcriptionState == TRANSCRIPTION_AVAILABLE; + } else { + if (details.transcriptionState == TRANSCRIPTION_IN_PROGRESS) { + views.voicemailTranscriptionView.setText( + mResources.getString(R.string.voicemail_transcription_in_progress)); + } else if (details.transcriptionState == TRANSCRIPTION_FAILED) { + views.voicemailTranscriptionView.setText( + mResources.getString(R.string.voicemail_transcription_failed)); + } + } + + if (showTranscriptBranding) { + views.voicemailTranscriptionBrandingView.setText( + mResources.getString(R.string.voicemail_transcription_branding_text)); + } else { + views.voicemailTranscriptionBrandingView.setText(""); + } } // Bold if not read Typeface typeface = details.isRead ? Typeface.SANS_SERIF : Typeface.DEFAULT_BOLD; views.nameView.setTypeface(typeface); views.voicemailTranscriptionView.setTypeface(typeface); + views.voicemailTranscriptionBrandingView.setTypeface(typeface); views.callLocationAndDate.setTypeface(typeface); views.callLocationAndDate.setTextColor( ContextCompat.getColor( diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java index e2e27a179..40c0894f0 100644 --- a/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java +++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsViews.java @@ -29,7 +29,9 @@ public final class PhoneCallDetailsViews { public final View callTypeView; public final CallTypeIconsView callTypeIcons; public final TextView callLocationAndDate; + public final View transcriptionView; public final TextView voicemailTranscriptionView; + public final TextView voicemailTranscriptionBrandingView; public final TextView callAccountLabel; private PhoneCallDetailsViews( @@ -37,13 +39,17 @@ public final class PhoneCallDetailsViews { View callTypeView, CallTypeIconsView callTypeIcons, TextView callLocationAndDate, + View transcriptionView, TextView voicemailTranscriptionView, + TextView voicemailTranscriptionBrandingView, TextView callAccountLabel) { this.nameView = nameView; this.callTypeView = callTypeView; this.callTypeIcons = callTypeIcons; this.callLocationAndDate = callLocationAndDate; + this.transcriptionView = transcriptionView; this.voicemailTranscriptionView = voicemailTranscriptionView; + this.voicemailTranscriptionBrandingView = voicemailTranscriptionBrandingView; this.callAccountLabel = callAccountLabel; } @@ -60,7 +66,9 @@ public final class PhoneCallDetailsViews { view.findViewById(R.id.call_type), (CallTypeIconsView) view.findViewById(R.id.call_type_icons), (TextView) view.findViewById(R.id.call_location_and_date), + view.findViewById(R.id.transcription), (TextView) view.findViewById(R.id.voicemail_transcription), + (TextView) view.findViewById(R.id.voicemail_transcription_branding), (TextView) view.findViewById(R.id.call_account_label)); } @@ -70,6 +78,8 @@ public final class PhoneCallDetailsViews { new View(context), new CallTypeIconsView(context), new TextView(context), + new View(context), + new TextView(context), new TextView(context), new TextView(context)); } diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java index 17018b38d..8bfd48b05 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailCallLogFragment.java @@ -17,12 +17,14 @@ package com.android.dialer.app.calllog; import android.app.KeyguardManager; +import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.media.AudioManager; import android.os.Bundle; import android.provider.CallLog; import android.provider.VoicemailContract; +import android.support.annotation.VisibleForTesting; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,15 +33,22 @@ import com.android.dialer.app.list.ListsFragment; import com.android.dialer.app.voicemail.VoicemailAudioManager; import com.android.dialer.app.voicemail.VoicemailErrorManager; import com.android.dialer.app.voicemail.VoicemailPlaybackPresenter; +import com.android.dialer.app.voicemail.error.VoicemailErrorMessageCreator; +import com.android.dialer.app.voicemail.error.VoicemailStatus; +import com.android.dialer.app.voicemail.error.VoicemailStatusWorker; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutor; +import com.android.dialer.common.concurrent.DialerExecutors; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.util.PermissionsUtil; +import java.util.List; public class VisualVoicemailCallLogFragment extends CallLogFragment { private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver(); private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; + private DialerExecutor<Context> mPreSyncVoicemailStatusCheckExecutor; private VoicemailErrorManager mVoicemailErrorManager; @@ -56,7 +65,6 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { public void onActivityCreated(Bundle savedInstanceState) { mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter.getInstance(getActivity(), savedInstanceState); - if (PermissionsUtil.hasReadVoicemailPermissions(getContext()) && PermissionsUtil.hasAddVoicemailPermissions(getContext())) { getActivity() @@ -69,6 +77,15 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { "read voicemail permission unavailable."); } super.onActivityCreated(savedInstanceState); + + mPreSyncVoicemailStatusCheckExecutor = + DialerExecutors.createUiTaskBuilder( + getActivity().getFragmentManager(), + "fetchVoicemailStatus", + new VoicemailStatusWorker()) + .onSuccess(this::onPreSyncVoicemailStatusChecked) + .build(); + mVoicemailErrorManager = new VoicemailErrorManager(getContext(), getAdapter().getAlertManager(), mModalAlertManager); @@ -136,14 +153,38 @@ public class VisualVoicemailCallLogFragment extends CallLogFragment { LogUtil.enterBlock("VisualVoicemailCallLogFragment.onVisible"); super.onVisible(); if (getActivity() != null) { - Intent intent = new Intent(VoicemailContract.ACTION_SYNC_VOICEMAIL); - intent.setPackage(getActivity().getPackageName()); - getActivity().sendBroadcast(intent); + mPreSyncVoicemailStatusCheckExecutor.executeParallel(getActivity()); Logger.get(getActivity()).logImpression(DialerImpression.Type.VVM_TAB_VIEWED); getActivity().setVolumeControlStream(VoicemailAudioManager.PLAYBACK_STREAM); } } + private void onPreSyncVoicemailStatusChecked(List<VoicemailStatus> statuses) { + if (!shouldAutoSync(new VoicemailErrorMessageCreator(), statuses)) { + return; + } + + Intent intent = new Intent(VoicemailContract.ACTION_SYNC_VOICEMAIL); + intent.setPackage(getActivity().getPackageName()); + getActivity().sendBroadcast(intent); + } + + @VisibleForTesting + static boolean shouldAutoSync( + VoicemailErrorMessageCreator errorMessageCreator, List<VoicemailStatus> statuses) { + for (VoicemailStatus status : statuses) { + if (!status.isActive()) { + continue; + } + if (errorMessageCreator.isSyncBlockingError(status)) { + LogUtil.i( + "VisualVoicemailCallLogFragment.shouldAutoSync", "auto-sync blocked due to " + status); + return false; + } + } + return true; + } + @Override public void onNotVisible() { LogUtil.enterBlock("VisualVoicemailCallLogFragment.onNotVisible"); diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java index 99fe466d8..cbadfd317 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java @@ -17,7 +17,6 @@ package com.android.dialer.app.calllog; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -26,7 +25,6 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; -import android.service.notification.StatusBarNotification; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.os.BuildCompat; @@ -43,7 +41,9 @@ import com.android.dialer.app.list.DialtactsPagerAdapter; import com.android.dialer.common.LogUtil; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.notification.NotificationChannelManager; +import com.android.dialer.notification.NotificationManagerUtils; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.telecom.TelecomUtil; import java.util.List; @@ -51,9 +51,17 @@ import java.util.Map; /** Shows a notification in the status bar for visual voicemail. */ final class VisualVoicemailNotifier { + /** Prefix used to generate a unique tag for each voicemail notification. */ private static final String NOTIFICATION_TAG_PREFIX = "VisualVoicemail_"; - private static final String NOTIFICATION_GROUP = "VisualVoicemail"; + /** Common ID for all voicemail notifications. */ private static final int NOTIFICATION_ID = 1; + /** Tag for the group summary notification. */ + private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_VisualVoicemail"; + /** + * Key used to associate all voicemail notifications and the summary as belonging to a single + * group. + */ + private static final String GROUP_KEY = "VisualVoicemailGroup"; public static void showNotifications( @NonNull Context context, @@ -82,12 +90,12 @@ final class VisualVoicemailNotifier { groupSummary.setChannelId(NotificationChannelManager.getVoicemailChannelId(context, handle)); } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - notificationManager.notify( - getNotificationTagForGroupSummary(), NOTIFICATION_ID, groupSummary.build()); + DialerNotificationManager.notify( + context, GROUP_SUMMARY_NOTIFICATION_TAG, NOTIFICATION_ID, groupSummary.build()); for (NewCall voicemail : newCalls) { - notificationManager.notify( + DialerNotificationManager.notify( + context, getNotificationTagForVoicemail(voicemail), NOTIFICATION_ID, createNotificationForVoicemail(context, voicemail, contactInfos)); @@ -96,13 +104,7 @@ final class VisualVoicemailNotifier { public static void cancelAllVoicemailNotifications(@NonNull Context context) { LogUtil.enterBlock("VisualVoicemailNotifier.cancelAllVoicemailNotifications"); - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String tag = notification.getTag(); - if (tag != null && tag.startsWith(NOTIFICATION_TAG_PREFIX)) { - notificationManager.cancel(tag, notification.getId()); - } - } + NotificationManagerUtils.cancelAllInGroup(context, GROUP_KEY); } public static void cancelSingleVoicemailNotification( @@ -112,27 +114,9 @@ final class VisualVoicemailNotifier { LogUtil.e("VisualVoicemailNotifier.cancelSingleVoicemailNotification", "uri is null"); return; } - NotificationManager notificationManager = context.getSystemService(NotificationManager.class); - String voicemailTag = getNotificationTagForUri(voicemailUri); - String summaryTag = getNotificationTagForGroupSummary(); - int notificationCount = 0; - - for (StatusBarNotification notification : notificationManager.getActiveNotifications()) { - String currentTag = notification.getTag(); - if (currentTag == null) { - continue; - } - if (currentTag.equals(voicemailTag)) { - notificationManager.cancel(notification.getTag(), notification.getId()); - } else if (currentTag.startsWith(NOTIFICATION_TAG_PREFIX) && !currentTag.equals(summaryTag)) { - notificationCount++; - } - } - - if (notificationCount == 0) { - // There are no more visual voicemail notifications. Remove the summary notification too. - notificationManager.cancel(summaryTag, NOTIFICATION_ID); - } + // This will also dismiss the group summary if there are no more voicemail notifications. + DialerNotificationManager.cancel( + context, getNotificationTagForUri(voicemailUri), NOTIFICATION_ID); } private static String getNotificationTagForVoicemail(@NonNull NewCall voicemail) { @@ -143,15 +127,11 @@ final class VisualVoicemailNotifier { return NOTIFICATION_TAG_PREFIX + voicemailUri; } - private static String getNotificationTagForGroupSummary() { - return NOTIFICATION_TAG_PREFIX + "GroupSummary"; - } - private static Notification.Builder createNotificationBuilder(@NonNull Context context) { return new Notification.Builder(context) .setSmallIcon(android.R.drawable.stat_notify_voicemail) .setColor(context.getColor(R.color.dialer_theme_color)) - .setGroup(NOTIFICATION_GROUP) + .setGroup(GROUP_KEY) .setOnlyAlertOnce(true) .setAutoCancel(true); } diff --git a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java b/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java deleted file mode 100644 index 2424b6dbd..000000000 --- a/java/com/android/dialer/app/calllog/calllogcache/CallLogCacheLollipopMr1.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.dialer.app.calllog.calllogcache; - -import android.content.Context; -import android.support.annotation.Nullable; -import android.telecom.PhoneAccountHandle; -import android.text.TextUtils; -import android.util.ArrayMap; -import com.android.dialer.calllogutils.PhoneAccountUtils; -import com.android.dialer.telecom.TelecomUtil; -import java.util.Map; - -/** - * This is the CallLogCache for versions of dialer Lollipop Mr1 and above with support for multi-SIM - * devices. - * - * <p>This class should not be initialized directly and instead be acquired from {@link - * CallLogCache#getCallLogCache}. - */ -class CallLogCacheLollipopMr1 extends CallLogCache { - - private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new ArrayMap<>(); - private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new ArrayMap<>(); - private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new ArrayMap<>(); - - /* package */ CallLogCacheLollipopMr1(Context context) { - super(context); - } - - @Override - public void reset() { - mPhoneAccountLabelCache.clear(); - mPhoneAccountColorCache.clear(); - mPhoneAccountCallWithNoteCache.clear(); - - super.reset(); - } - - @Override - public boolean isVoicemailNumber( - PhoneAccountHandle accountHandle, @Nullable CharSequence number) { - if (TextUtils.isEmpty(number)) { - return false; - } - return TelecomUtil.isVoicemailNumber(mContext, accountHandle, number.toString()); - } - - @Override - public String getAccountLabel(PhoneAccountHandle accountHandle) { - if (mPhoneAccountLabelCache.containsKey(accountHandle)) { - return mPhoneAccountLabelCache.get(accountHandle); - } else { - String label = PhoneAccountUtils.getAccountLabel(mContext, accountHandle); - mPhoneAccountLabelCache.put(accountHandle, label); - return label; - } - } - - @Override - public int getAccountColor(PhoneAccountHandle accountHandle) { - if (mPhoneAccountColorCache.containsKey(accountHandle)) { - return mPhoneAccountColorCache.get(accountHandle); - } else { - Integer color = PhoneAccountUtils.getAccountColor(mContext, accountHandle); - mPhoneAccountColorCache.put(accountHandle, color); - return color; - } - } - - @Override - public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) { - if (mPhoneAccountCallWithNoteCache.containsKey(accountHandle)) { - return mPhoneAccountCallWithNoteCache.get(accountHandle); - } else { - Boolean supportsCallWithNote = - PhoneAccountUtils.getAccountSupportsCallSubject(mContext, accountHandle); - mPhoneAccountCallWithNoteCache.put(accountHandle, supportsCallWithNote); - return supportsCallWithNote; - } - } -} |