Display missed call notifications in Dialer.
am: cc42b59535

* commit 'cc42b5953588110cc89b43e72d2b6579757017a1':
  Display missed call notifications in Dialer.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 596c486..21ba20f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -273,6 +273,12 @@
             android:exported="false"
         />
 
+        <receiver android:name=".calllog.MissedCallNotificationReceiver">
+            <intent-filter>
+                <action android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
+            </intent-filter>
+        </receiver>
+
         <!-- Service to update a contact -->
         <service
             android:name=".contact.ContactUpdateService"
diff --git a/res/drawable-hdpi/ic_phone_24dp.png b/res/drawable-hdpi/ic_phone_24dp.png
new file mode 100644
index 0000000..b27dfba
--- /dev/null
+++ b/res/drawable-hdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_phone_24dp.png b/res/drawable-mdpi/ic_phone_24dp.png
new file mode 100644
index 0000000..c1766b8
--- /dev/null
+++ b/res/drawable-mdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_phone_24dp.png b/res/drawable-xhdpi/ic_phone_24dp.png
new file mode 100644
index 0000000..83167f4
--- /dev/null
+++ b/res/drawable-xhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_phone_24dp.png b/res/drawable-xxhdpi/ic_phone_24dp.png
new file mode 100644
index 0000000..8fff728
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_phone_24dp.png b/res/drawable-xxxhdpi/ic_phone_24dp.png
new file mode 100644
index 0000000..30d141d
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_phone_24dp.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2838e68..7e87b2f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -99,6 +99,28 @@
     <!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
     <string name="clearCallLogProgress_title">Clearing call history\u2026</string>
 
+    <!-- Title used for the activity for placing a call. This name appears
+         in activity disambig dialogs -->
+    <string name="userCallActivityLabel" product="default">Phone</string>
+
+    <!-- Notification strings -->
+    <!-- Missed call notification label, used when there's exactly one missed call -->
+    <string name="notification_missedCallTitle">Missed call</string>
+    <!-- Missed call notification label, used when there are two or more missed calls -->
+    <string name="notification_missedCallsTitle">Missed calls</string>
+    <!-- Missed call notification message used when there are multiple missed calls -->
+    <string name="notification_missedCallsMsg"><xliff:g id="num_missed_calls">%s</xliff:g> missed calls</string>
+    <!-- Message for "call back" Action, which is displayed in the missed call notificaiton.
+         The user will be able to call back to the person or the phone number.
+         [CHAR LIMIT=18] -->
+    <string name="notification_missedCall_call_back">Call back</string>
+    <!-- Message for "reply via sms" action, which is displayed in the missed call notification.
+         The user will be able to send text messages using the phone number.
+         [CHAR LIMIT=18] -->
+    <string name="notification_missedCall_message">Message</string>
+        <!-- DO NOT TRANSLATE. Hardcoded number used for restricted incoming phone numbers. -->
+    <string name="handle_restricted">RESTRICTED</string>
+
     <!-- Title of the notification of new voicemails. [CHAR LIMIT=30] -->
     <plurals name="notification_voicemail_title">
         <item quantity="one">Voicemail</item>
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
index 91012b5..df08e4d 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
@@ -78,6 +78,16 @@
     }
 
     /**
+     * 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 d2a494d..4ff9576 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -26,15 +26,16 @@
 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 @@
             "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 @@
             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 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 @@
             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 e075b9b..7f08fdc 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -19,7 +19,6 @@
 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.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.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 0000000..86d6cb9
--- /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 0000000..ad9af42
--- /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 26f9bd1..c6e644c 100644
--- a/src/com/android/dialer/calllog/VoicemailQueryHandler.java
+++ b/src/com/android/dialer/calllog/VoicemailQueryHandler.java
@@ -59,7 +59,8 @@
         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);