diff options
Diffstat (limited to 'src')
6 files changed, 442 insertions, 26 deletions
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java index 91012b54e..df08e4db9 100644 --- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java +++ b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java @@ -78,6 +78,16 @@ public class CallLogNotificationsHelper { } /** + * Get all missed calls with the "new" flag set to 1. + * + * @return A list of NewCall objects where each object represents a new missed call. + */ + @Nullable + public List<NewCall> getNewMissedCalls() { + return mNewCallsQuery.query(Calls.MISSED_TYPE); + } + + /** * Given a number and number information (presentation and country ISO), get the best name * for display. If the name itself if already available, return that. Otherwise attempt to look * it up in the database. If that fails, fall back to displaying the number. diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java index d2a494d9a..4ff9576ca 100644 --- a/src/com/android/dialer/calllog/CallLogNotificationsService.java +++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java @@ -26,15 +26,16 @@ import com.android.contacts.common.util.PermissionsUtil; import com.android.dialer.util.TelecomUtil; /** - * Provides operations for managing notifications. + * Provides operations for managing call-related notifications. * <p> * It handles the following actions: * <ul> - * <li>{@link #ACTION_MARK_NEW_VOICEMAILS_AS_OLD}: marks all the new voicemails in the call log as - * old; this is called when a notification is dismissed.</li> - * <li>{@link #ACTION_UPDATE_NOTIFICATIONS}: updates the content of the new items notification; it - * may include an optional extra {@link #EXTRA_NEW_VOICEMAIL_URI}, containing the URI of the new - * voicemail that has triggered this update (if any).</li> + * <li>Updating voicemail notifications</li> + * <li>Marking new voicemails as old</li> + * <li>Updating missed call notifications</li> + * <li>Marking new missed calls as old</li> + * <li>Calling back from a missed call</li> + * <li>Sending an SMS from a missed call</li> * </ul> */ public class CallLogNotificationsService extends IntentService { @@ -45,21 +46,62 @@ public class CallLogNotificationsService extends IntentService { "com.android.dialer.calllog.ACTION_MARK_NEW_VOICEMAILS_AS_OLD"; /** - * Action to update the notifications. + * Action to update voicemail notifications. * <p> * May include an optional extra {@link #EXTRA_NEW_VOICEMAIL_URI}. */ - public static final String ACTION_UPDATE_NOTIFICATIONS = - "com.android.dialer.calllog.UPDATE_NOTIFICATIONS"; + public static final String ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS = + "com.android.dialer.calllog.UPDATE_VOICEMAIL_NOTIFICATIONS"; /** - * Extra to included with {@link #ACTION_UPDATE_NOTIFICATIONS} to identify the new voicemail - * that triggered an update. + * Extra to included with {@link #ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS} to identify the new + * voicemail that triggered an update. * <p> * It must be a {@link Uri}. */ public static final String EXTRA_NEW_VOICEMAIL_URI = "NEW_VOICEMAIL_URI"; + /** + * Action to update the missed call notifications. + * <p> + * Includes optional extras {@link #EXTRA_MISSED_CALL_NUMBER} and + * {@link #EXTRA_MISSED_CALL_COUNT}. + */ + public static final String ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS = + "com.android.dialer.calllog.UPDATE_MISSED_CALL_NOTIFICATIONS"; + + /** Action to mark all the new missed calls as old. */ + public static final String ACTION_MARK_NEW_MISSED_CALLS_AS_OLD = + "com.android.dialer.calllog.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD"; + + /** Action to call back a missed call. */ + public static final String ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION = + "com.android.dialer.calllog.CALL_BACK_FROM_MISSED_CALL_NOTIFICATION"; + + public static final String ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION = + "com.android.dialer.calllog.SEND_SMS_FROM_MISSED_CALL_NOTIFICATION"; + + /** + * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS}, + * {@link #ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION} and + * {@link #ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION} to identify the number to display, + * call or text back. + * <p> + * It must be a {@link String}. + */ + public static final String EXTRA_MISSED_CALL_NUMBER = "MISSED_CALL_NUMBER"; + + /** + * Extra to be included with {@link #ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS} to represent the + * number of missed calls. + * <p> + * It must be a {@link Integer} + */ + public static final String EXTRA_MISSED_CALL_COUNT = + "MISSED_CALL_COUNT"; + + public static final int UNKNOWN_MISSED_CALL_COUNT = -1; + private VoicemailQueryHandler mVoicemailQueryHandler; public CallLogNotificationsService() { @@ -77,16 +119,38 @@ public class CallLogNotificationsService extends IntentService { return; } - if (ACTION_MARK_NEW_VOICEMAILS_AS_OLD.equals(intent.getAction())) { - if (mVoicemailQueryHandler == null) { - mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver()); - } - mVoicemailQueryHandler.markNewVoicemailsAsOld(); - } else if (ACTION_UPDATE_NOTIFICATIONS.equals(intent.getAction())) { - Uri voicemailUri = (Uri) intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI); - DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri); - } else { - Log.d(TAG, "onHandleIntent: could not handle: " + intent); + String action = intent.getAction(); + switch (action) { + case ACTION_MARK_NEW_VOICEMAILS_AS_OLD: + if (mVoicemailQueryHandler == null) { + mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver()); + } + mVoicemailQueryHandler.markNewVoicemailsAsOld(); + break; + case ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS: + Uri voicemailUri = (Uri) intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI); + DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri); + break; + case ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS: + int count = intent.getIntExtra(EXTRA_MISSED_CALL_COUNT, + UNKNOWN_MISSED_CALL_COUNT); + String number = intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER); + MissedCallNotifier.getInstance(this).updateMissedCallNotification(count, number); + break; + case ACTION_MARK_NEW_MISSED_CALLS_AS_OLD: + CallLogNotificationsHelper.removeMissedCallNotifications(this); + break; + case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION: + MissedCallNotifier.getInstance(this).callBackFromMissedCall( + intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER)); + break; + case ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION: + MissedCallNotifier.getInstance(this).sendSmsFromMissedCall( + intent.getStringExtra(EXTRA_MISSED_CALL_NUMBER)); + break; + default: + Log.d(TAG, "onHandleIntent: could not handle: " + intent); + break; } } @@ -100,7 +164,8 @@ public class CallLogNotificationsService extends IntentService { public static void updateVoicemailNotifications(Context context, Uri voicemailUri) { if (TelecomUtil.hasReadWriteVoicemailPermissions(context)) { Intent serviceIntent = new Intent(context, CallLogNotificationsService.class); - serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS); + serviceIntent.setAction( + CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS); // If voicemailUri is null, then notifications for all voicemails will be updated. if (voicemailUri != null) { serviceIntent.putExtra( @@ -109,4 +174,21 @@ public class CallLogNotificationsService extends IntentService { context.startService(serviceIntent); } } + + /** + * Updates notifications for any new missed calls. + * + * @param context A valid context. + * @param count The number of new missed calls. + * @param number The phone number of the newest missed call. + */ + public static void updateMissedCallNotifications(Context context, int count, + String number) { + Intent serviceIntent = new Intent(context, CallLogNotificationsService.class); + serviceIntent.setAction( + CallLogNotificationsService.ACTION_UPDATE_MISSED_CALL_NOTIFICATIONS); + serviceIntent.putExtra(EXTRA_MISSED_CALL_COUNT, count); + serviceIntent.putExtra(EXTRA_MISSED_CALL_NUMBER, number); + context.startService(serviceIntent); + } } diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java index e075b9bf3..7f08fdc18 100644 --- a/src/com/android/dialer/calllog/ContactInfoHelper.java +++ b/src/com/android/dialer/calllog/ContactInfoHelper.java @@ -19,7 +19,6 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteFullException; import android.net.Uri; -import android.os.Build; import android.provider.CallLog.Calls; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; @@ -32,7 +31,6 @@ import android.text.TextUtils; import android.util.Log; import com.android.contacts.common.ContactsUtils; -import com.android.contacts.common.compat.ContactsCompat; import com.android.contacts.common.util.Constants; import com.android.contacts.common.util.PermissionsUtil; import com.android.contacts.common.util.PhoneNumberHelper; @@ -42,7 +40,6 @@ import com.android.dialer.service.CachedNumberLookupService; import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo; import com.android.dialer.util.TelecomUtil; import com.android.dialerbind.ObjectFactory; -import com.android.incallui.CallerInfo; import org.json.JSONException; import org.json.JSONObject; diff --git a/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java b/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java new file mode 100644 index 000000000..86d6cb9fb --- /dev/null +++ b/src/com/android/dialer/calllog/MissedCallNotificationReceiver.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 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.calllog; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.telecom.TelecomManager; +import android.util.Log; + +import com.android.dialer.calllog.CallLogNotificationsService; + +/** + * Receives broadcasts that should trigger a refresh of the missed call notification. This includes + * both an explicit broadcast from Telecom and a reboot. + */ +public class MissedCallNotificationReceiver extends BroadcastReceiver { + //TODO: Use compat class for these methods. + public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = + "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION"; + + public static final String EXTRA_NOTIFICATION_COUNT = + "android.telecom.extra.NOTIFICATION_COUNT"; + + public static final String EXTRA_NOTIFICATION_PHONE_NUMBER = + "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (!ACTION_SHOW_MISSED_CALLS_NOTIFICATION.equals(action)) { + return; + } + + int count = intent.getIntExtra(EXTRA_NOTIFICATION_COUNT, + CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT); + String number = intent.getStringExtra(EXTRA_NOTIFICATION_PHONE_NUMBER); + CallLogNotificationsService.updateMissedCallNotifications(context, count, number); + } +} diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java new file mode 100644 index 000000000..ad9af42b8 --- /dev/null +++ b/src/com/android/dialer/calllog/MissedCallNotifier.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 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.calllog; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.provider.CallLog.Calls; +import android.text.TextUtils; +import android.util.Log; + +import com.android.contacts.common.GeoUtil; +import com.android.contacts.common.util.PhoneNumberHelper; +import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall; +import com.android.dialer.DialtactsActivity; +import com.android.dialer.list.ListsFragment; +import com.android.dialer.util.DialerUtils; +import com.android.dialer.util.IntentUtil; +import com.android.dialer.util.IntentUtil.CallIntentBuilder; +import com.android.dialer.R; + +import java.util.List; + +/** + * Creates a notification for calls that the user missed (neither answered nor rejected). + * + */ +public class MissedCallNotifier { + public static final String TAG = "MissedCallNotifier"; + + /** The tag used to identify notifications from this class. */ + private static final String NOTIFICATION_TAG = "MissedCallNotifier"; + /** The identifier of the notification of new missed calls. */ + private static final int NOTIFICATION_ID = 1; + /** Preference file key for number of missed calls. */ + private static final String MISSED_CALL_COUNT = "missed_call_count"; + + private static MissedCallNotifier sInstance; + private Context mContext; + + /** Returns the singleton instance of the {@link MissedCallNotifier}. */ + public static MissedCallNotifier getInstance(Context context) { + if (sInstance == null) { + sInstance = new MissedCallNotifier(context); + } + return sInstance; + } + + private MissedCallNotifier(Context context) { + mContext = context; + } + + public void updateMissedCallNotification(int count, String number) { + final int titleResId; + final String expandedText; // The text in the notification's line 1 and 2. + + final List<NewCall> newCalls = + CallLogNotificationsHelper.getInstance(mContext).getNewMissedCalls(); + + if (count == CallLogNotificationsService.UNKNOWN_MISSED_CALL_COUNT) { + if (newCalls == null) { + // If the intent did not contain a count, and we are unable to get a count from the + // call log, then no notification can be shown. + return; + } + count = newCalls.size(); + } + + if (count == 0) { + // No voicemails to notify about: clear the notification. + clearMissedCalls(); + return; + } + + // The call log has been updated, use that information preferentially. + boolean useCallLog = newCalls != null && newCalls.size() == count; + NewCall newestCall = useCallLog ? newCalls.get(0) : null; + long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis(); + + // Display the first line of the notification: + // 1 missed call: <caller name || handle> + // More than 1 missed call: <number of calls> + "missed calls" + if (count == 1) { + titleResId = R.string.notification_missedCallTitle; + + //TODO: look up caller ID that is not in contacts. + expandedText = CallLogNotificationsHelper.getInstance(mContext) + .getName(useCallLog ? newestCall.number : number, + useCallLog ? newestCall.numberPresentation + : Calls.PRESENTATION_ALLOWED, + useCallLog ? newestCall.countryIso + : GeoUtil.getCurrentCountryIso(mContext)); + } else { + titleResId = R.string.notification_missedCallsTitle; + expandedText = + mContext.getString(R.string.notification_missedCallsMsg, count); + } + + // Create a public viewable version of the notification, suitable for display when sensitive + // notification content is hidden. + Notification.Builder publicBuilder = new Notification.Builder(mContext); + publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call) + .setColor(mContext.getResources().getColor(R.color.dialer_theme_color)) + // Show "Phone" for notification title. + .setContentTitle(mContext.getText(R.string.userCallActivityLabel)) + // Notification details shows that there are missed call(s), but does not reveal + // the missed caller information. + .setContentText(mContext.getText(titleResId)) + .setContentIntent(createCallLogPendingIntent()) + .setAutoCancel(true) + .setWhen(timeMs) + .setDeleteIntent(createClearMissedCallsPendingIntent()); + + // Create the notification suitable for display when sensitive information is showing. + Notification.Builder builder = new Notification.Builder(mContext); + builder.setSmallIcon(android.R.drawable.stat_notify_missed_call) + .setColor(mContext.getResources().getColor(R.color.dialer_theme_color)) + .setContentTitle(mContext.getText(titleResId)) + .setContentText(expandedText) + .setContentIntent(createCallLogPendingIntent()) + .setAutoCancel(true) + .setWhen(timeMs) + .setDeleteIntent(createClearMissedCallsPendingIntent()) + // Include a public version of the notification to be shown when the missed call + // notification is shown on the user's lock screen and they have chosen to hide + // sensitive notification information. + .setPublicVersion(publicBuilder.build()); + + // Add additional actions when there is only 1 missed call, like call-back and SMS. + if (count == 1) { + if (!TextUtils.isEmpty(number) + && !TextUtils.equals( + number, mContext.getString(R.string.handle_restricted))) { + builder.addAction(R.drawable.ic_phone_24dp, + mContext.getString(R.string.notification_missedCall_call_back), + createCallBackPendingIntent(number)); + + if (!PhoneNumberHelper.isUriNumber(number)) { + builder.addAction(R.drawable.ic_message_24dp, + mContext.getString(R.string.notification_missedCall_message), + createSendSmsFromNotificationPendingIntent(number)); + } + } + //TODO: add photo + } + + Notification notification = builder.build(); + configureLedOnNotification(notification); + + Log.i(TAG, "Adding missed call notification."); + getNotificationMgr().notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification); + } + + private void clearMissedCalls() { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + // Clear the list of new missed calls from the call log. + ContentValues values = new ContentValues(); + values.put(Calls.NEW, 0); + values.put(Calls.IS_READ, 1); + StringBuilder where = new StringBuilder(); + where.append(Calls.NEW); + where.append(" = 1 AND "); + where.append(Calls.TYPE); + where.append(" = ?"); + try { + mContext.getContentResolver().update(Calls.CONTENT_URI, values, + where.toString(), new String[]{ Integer.toString(Calls. + MISSED_TYPE) }); + } catch (IllegalArgumentException e) { + Log.w(TAG, "ContactsProvider update command failed", e); + } + getNotificationMgr().cancel(NOTIFICATION_TAG, NOTIFICATION_ID); + } + }); + } + + /** + * Trigger an intent to make a call from a missed call number. + */ + public void callBackFromMissedCall(String number) { + closeSystemDialogs(mContext); + CallLogNotificationsHelper.removeMissedCallNotifications(mContext); + DialerUtils.startActivityWithErrorToast( + mContext, + new CallIntentBuilder(number) + .build() + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + /** + * Trigger an intent to send an sms from a missed call number. + */ + public void sendSmsFromMissedCall(String number) { + closeSystemDialogs(mContext); + CallLogNotificationsHelper.removeMissedCallNotifications(mContext); + DialerUtils.startActivityWithErrorToast( + mContext, + IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + /** + * Creates a new pending intent that sends the user to the call log. + * + * @return The pending intent. + */ + private PendingIntent createCallLogPendingIntent() { + Intent contentIntent = new Intent(mContext, DialtactsActivity.class); + contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_HISTORY); + return PendingIntent.getActivity( + mContext, 0, contentIntent,PendingIntent.FLAG_UPDATE_CURRENT); + } + + /** Creates a pending intent that marks all new missed calls as old. */ + private PendingIntent createClearMissedCallsPendingIntent() { + Intent intent = new Intent(mContext, CallLogNotificationsService.class); + intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_MISSED_CALLS_AS_OLD); + return PendingIntent.getService(mContext, 0, intent, 0); + } + + private PendingIntent createCallBackPendingIntent(String number) { + Intent intent = new Intent(mContext, CallLogNotificationsService.class); + intent.setAction( + CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION); + intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number); + return PendingIntent.getService(mContext, 0, intent, 0); + } + + private PendingIntent createSendSmsFromNotificationPendingIntent(String number) { + Intent intent = new Intent(mContext, CallLogNotificationsService.class); + intent.setAction( + CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION); + intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number); + return PendingIntent.getService(mContext, 0, intent, 0); + } + + /** + * Configures a notification to emit the blinky notification light. + */ + private void configureLedOnNotification(Notification notification) { + notification.flags |= Notification.FLAG_SHOW_LIGHTS; + notification.defaults |= Notification.DEFAULT_LIGHTS; + } + + /** + * Closes open system dialogs and the notification shade. + */ + private void closeSystemDialogs(Context context) { + context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + private NotificationManager getNotificationMgr() { + return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + } +} diff --git a/src/com/android/dialer/calllog/VoicemailQueryHandler.java b/src/com/android/dialer/calllog/VoicemailQueryHandler.java index 26f9bd172..c6e644c32 100644 --- a/src/com/android/dialer/calllog/VoicemailQueryHandler.java +++ b/src/com/android/dialer/calllog/VoicemailQueryHandler.java @@ -59,7 +59,8 @@ public class VoicemailQueryHandler extends AsyncQueryHandler { if (token == UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN) { if (mContext != null) { Intent serviceIntent = new Intent(mContext, CallLogNotificationsService.class); - serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS); + serviceIntent.setAction( + CallLogNotificationsService.ACTION_UPDATE_VOICEMAIL_NOTIFICATIONS); mContext.startService(serviceIntent); } else { Log.w(TAG, "Unknown update completed: ignoring: " + token); |