| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.dialer.app.calllog; |
| |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.net.Uri; |
| import android.os.Build.VERSION; |
| import android.os.Build.VERSION_CODES; |
| import android.support.annotation.Nullable; |
| import android.support.v4.util.Pair; |
| import android.telecom.PhoneAccount; |
| import android.telecom.PhoneAccountHandle; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| import com.android.contacts.common.compat.TelephonyManagerCompat; |
| import com.android.contacts.common.util.ContactDisplayUtils; |
| import com.android.dialer.app.DialtactsActivity; |
| import com.android.dialer.app.R; |
| import com.android.dialer.app.calllog.CallLogNotificationsHelper.NewCall; |
| import com.android.dialer.app.list.ListsFragment; |
| import com.android.dialer.blocking.FilteredNumbersUtil; |
| import com.android.dialer.telecom.TelecomUtil; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** Shows a voicemail notification in the status bar. */ |
| public class DefaultVoicemailNotifier { |
| |
| public static final String TAG = "VoicemailNotifier"; |
| |
| /** The tag used to identify notifications from this class. */ |
| private static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier"; |
| /** The identifier of the notification of new voicemails. */ |
| private static final int NOTIFICATION_ID = 1; |
| |
| /** The singleton instance of {@link DefaultVoicemailNotifier}. */ |
| private static DefaultVoicemailNotifier sInstance; |
| |
| private final Context mContext; |
| |
| private DefaultVoicemailNotifier(Context context) { |
| mContext = context; |
| } |
| |
| /** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */ |
| public static DefaultVoicemailNotifier getInstance(Context context) { |
| if (sInstance == null) { |
| ContentResolver contentResolver = context.getContentResolver(); |
| sInstance = new DefaultVoicemailNotifier(context); |
| } |
| return sInstance; |
| } |
| |
| /** |
| * Updates the notification and notifies of the call with the given URI. |
| * |
| * <p>Clears the notification if there are no new voicemails, and notifies if the given URI |
| * corresponds to a new voicemail. |
| * |
| * <p>It is not safe to call this method from the main thread. |
| */ |
| public void updateNotification(Uri newCallUri) { |
| // Lookup the list of new voicemails to include in the notification. |
| // TODO: Move this into a service, to avoid holding the receiver up. |
| final List<NewCall> newCalls = |
| CallLogNotificationsHelper.getInstance(mContext).getNewVoicemails(); |
| |
| if (newCalls == null) { |
| // Query failed, just return. |
| return; |
| } |
| |
| if (newCalls.isEmpty()) { |
| // No voicemails to notify about: clear the notification. |
| getNotificationManager().cancel(NOTIFICATION_TAG, NOTIFICATION_ID); |
| return; |
| } |
| |
| Resources resources = mContext.getResources(); |
| |
| // This represents a list of names to include in the notification. |
| String callers = null; |
| |
| // Maps each number into a name: if a number is in the map, it has already left a more |
| // recent voicemail. |
| final Map<String, String> names = new ArrayMap<>(); |
| |
| // Determine the call corresponding to the new voicemail we have to notify about. |
| NewCall callToNotify = null; |
| |
| // Iterate over the new voicemails to determine all the information above. |
| Iterator<NewCall> itr = newCalls.iterator(); |
| while (itr.hasNext()) { |
| NewCall newCall = itr.next(); |
| |
| // Skip notifying for numbers which are blocked. |
| if (FilteredNumbersUtil.shouldBlockVoicemail( |
| mContext, newCall.number, newCall.countryIso, newCall.dateMs)) { |
| itr.remove(); |
| |
| // Delete the voicemail. |
| mContext.getContentResolver().delete(newCall.voicemailUri, null, null); |
| continue; |
| } |
| |
| // Check if we already know the name associated with this number. |
| String name = names.get(newCall.number); |
| if (name == null) { |
| name = |
| CallLogNotificationsHelper.getInstance(mContext) |
| .getName(newCall.number, newCall.numberPresentation, newCall.countryIso); |
| names.put(newCall.number, name); |
| // This is a new caller. Add it to the back of the list of callers. |
| if (TextUtils.isEmpty(callers)) { |
| callers = name; |
| } else { |
| callers = |
| resources.getString(R.string.notification_voicemail_callers_list, callers, name); |
| } |
| } |
| // Check if this is the new call we need to notify about. |
| if (newCallUri != null |
| && newCall.voicemailUri != null |
| && ContentUris.parseId(newCallUri) == ContentUris.parseId(newCall.voicemailUri)) { |
| callToNotify = newCall; |
| } |
| } |
| |
| // All the potential new voicemails have been removed, e.g. if they were spam. |
| if (newCalls.isEmpty()) { |
| return; |
| } |
| |
| // If there is only one voicemail, set its transcription as the "long text". |
| String transcription = null; |
| if (newCalls.size() == 1) { |
| transcription = newCalls.get(0).transcription; |
| } |
| |
| if (newCallUri != null && callToNotify == null) { |
| Log.e(TAG, "The new call could not be found in the call log: " + newCallUri); |
| } |
| |
| // Determine the title of the notification and the icon for it. |
| final String title = |
| resources.getQuantityString( |
| R.plurals.notification_voicemail_title, newCalls.size(), newCalls.size()); |
| // TODO: Use the photo of contact if all calls are from the same person. |
| final int icon = android.R.drawable.stat_notify_voicemail; |
| |
| Pair<Uri, Integer> info = getNotificationInfo(callToNotify); |
| |
| Notification.Builder notificationBuilder = |
| new Notification.Builder(mContext) |
| .setSmallIcon(icon) |
| .setContentTitle(title) |
| .setContentText(callers) |
| .setColor(resources.getColor(R.color.dialer_theme_color)) |
| .setSound(info.first) |
| .setDefaults(info.second) |
| .setDeleteIntent(createMarkNewVoicemailsAsOldIntent()) |
| .setAutoCancel(true); |
| |
| if (!TextUtils.isEmpty(transcription)) { |
| notificationBuilder.setStyle(new Notification.BigTextStyle().bigText(transcription)); |
| } |
| |
| // Determine the intent to fire when the notification is clicked on. |
| final Intent contentIntent; |
| // Open the call log. |
| contentIntent = DialtactsActivity.getShowTabIntent(mContext, ListsFragment.TAB_INDEX_VOICEMAIL); |
| contentIntent.putExtra(DialtactsActivity.EXTRA_CLEAR_NEW_VOICEMAILS, true); |
| notificationBuilder.setContentIntent( |
| PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT)); |
| |
| // The text to show in the ticker, describing the new event. |
| if (callToNotify != null) { |
| CharSequence msg = |
| ContactDisplayUtils.getTtsSpannedPhoneNumber( |
| resources, |
| R.string.notification_new_voicemail_ticker, |
| names.get(callToNotify.number)); |
| notificationBuilder.setTicker(msg); |
| } |
| Log.i(TAG, "Creating voicemail notification"); |
| getNotificationManager().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notificationBuilder.build()); |
| } |
| |
| /** |
| * Determines which ringtone Uri and Notification defaults to use when updating the notification |
| * for the given call. |
| */ |
| private Pair<Uri, Integer> getNotificationInfo(@Nullable NewCall callToNotify) { |
| Log.v(TAG, "getNotificationInfo"); |
| if (callToNotify == null) { |
| Log.i(TAG, "callToNotify == null"); |
| return new Pair<>(null, 0); |
| } |
| PhoneAccountHandle accountHandle; |
| if (callToNotify.accountComponentName == null || callToNotify.accountId == null) { |
| Log.v(TAG, "accountComponentName == null || callToNotify.accountId == null"); |
| accountHandle = TelecomUtil.getDefaultOutgoingPhoneAccount(mContext, PhoneAccount.SCHEME_TEL); |
| if (accountHandle == null) { |
| Log.i(TAG, "No default phone account found, using default notification ringtone"); |
| return new Pair<>(null, Notification.DEFAULT_ALL); |
| } |
| |
| } else { |
| accountHandle = |
| new PhoneAccountHandle( |
| ComponentName.unflattenFromString(callToNotify.accountComponentName), |
| callToNotify.accountId); |
| } |
| if (accountHandle.getComponentName() != null) { |
| Log.v(TAG, "PhoneAccountHandle.ComponentInfo:" + accountHandle.getComponentName()); |
| } else { |
| Log.i(TAG, "PhoneAccountHandle.ComponentInfo: null"); |
| } |
| return new Pair<>( |
| TelephonyManagerCompat.getVoicemailRingtoneUri(getTelephonyManager(), accountHandle), |
| getNotificationDefaults(accountHandle)); |
| } |
| |
| private int getNotificationDefaults(PhoneAccountHandle accountHandle) { |
| if (VERSION.SDK_INT >= VERSION_CODES.N) { |
| return TelephonyManagerCompat.isVoicemailVibrationEnabled( |
| getTelephonyManager(), accountHandle) |
| ? Notification.DEFAULT_VIBRATE |
| : 0; |
| } |
| return Notification.DEFAULT_ALL; |
| } |
| |
| /** Creates a pending intent that marks all new voicemails as old. */ |
| private PendingIntent createMarkNewVoicemailsAsOldIntent() { |
| Intent intent = new Intent(mContext, CallLogNotificationsService.class); |
| intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD); |
| return PendingIntent.getService(mContext, 0, intent, 0); |
| } |
| |
| private NotificationManager getNotificationManager() { |
| return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); |
| } |
| |
| private TelephonyManager getTelephonyManager() { |
| return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| } |
| } |