blob: 79c9c0fc5ba2057a072c56cade9565a9455fdcc8 [file] [log] [blame]
Santos Cordona0e5f1a2014-03-31 21:43:00 -07001/*
2 * Copyright 2014, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.telecomm;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.app.TaskStackBuilder;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
Santos Cordon99c8a6f2014-05-28 18:28:47 -070026import android.graphics.Bitmap;
27import android.graphics.drawable.BitmapDrawable;
28import android.graphics.drawable.Drawable;
Santos Cordona0e5f1a2014-03-31 21:43:00 -070029import android.net.Uri;
30import android.provider.CallLog;
31import android.provider.CallLog.Calls;
32import android.telecomm.CallState;
33import android.telephony.DisconnectCause;
34import android.text.BidiFormatter;
35import android.text.TextDirectionHeuristics;
36import android.text.TextUtils;
37
38/**
39 * Creates a notification for calls that the user missed (neither answered nor rejected).
40 * TODO(santoscordon): Make TelephonyManager.clearMissedCalls call into this class.
41 * STOPSHIP: Resolve b/13769374 about moving this class to InCall.
42 */
43class MissedCallNotifier extends CallsManagerListenerBase {
44
45 private static final int MISSED_CALL_NOTIFICATION_ID = 1;
46 private static final String SCHEME_SMSTO = "smsto";
47
48 private final Context mContext;
49 private final NotificationManager mNotificationManager;
50
51 // Used to track the number of missed calls.
52 private int mMissedCallCount = 0;
53
54 MissedCallNotifier(Context context) {
55 mContext = context;
56 mNotificationManager =
57 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
58 }
59
60 /** {@inheritDoc} */
61 @Override
62 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
63 if (oldState == CallState.RINGING && newState == CallState.DISCONNECTED &&
64 call.getDisconnectCause() == DisconnectCause.INCOMING_MISSED) {
65 showMissedCallNotification(call);
66 }
67 }
68
69 /** Clears missed call notification and marks the call log's missed calls as read. */
70 void clearMissedCalls() {
71 // Clear the list of new missed calls from the call log.
72 ContentValues values = new ContentValues();
73 values.put(Calls.NEW, 0);
74 values.put(Calls.IS_READ, 1);
75 StringBuilder where = new StringBuilder();
76 where.append(Calls.NEW);
77 where.append(" = 1 AND ");
78 where.append(Calls.TYPE);
79 where.append(" = ?");
80 mContext.getContentResolver().update(Calls.CONTENT_URI, values, where.toString(),
81 new String[]{ Integer.toString(Calls.MISSED_TYPE) });
82
83 cancelMissedCallNotification();
84 }
85
86 /**
87 * Create a system notification for the missed call.
88 *
89 * @param call The missed call.
90 */
91 private void showMissedCallNotification(Call call) {
92 mMissedCallCount++;
93
94 final int titleResId;
95 final String expandedText; // The text in the notification's line 1 and 2.
96
97 // Display the first line of the notification:
98 // 1 missed call: <caller name || handle>
99 // More than 1 missed call: <number of calls> + "missed calls"
100 if (mMissedCallCount == 1) {
101 titleResId = R.string.notification_missedCallTitle;
102 expandedText = getNameForCall(call);
103 } else {
104 titleResId = R.string.notification_missedCallsTitle;
105 expandedText =
106 mContext.getString(R.string.notification_missedCallsMsg, mMissedCallCount);
107 }
108
109 // Create the notification.
110 Notification.Builder builder = new Notification.Builder(mContext);
111 builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700112 .setWhen(call.getCreationTimeMillis())
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700113 .setContentTitle(mContext.getText(titleResId))
114 .setContentText(expandedText)
115 .setContentIntent(createCallLogPendingIntent())
116 .setAutoCancel(true)
117 .setDeleteIntent(createClearMissedCallsPendingIntent());
118
119 Uri handleUri = call.getHandle();
120 String handle = handleUri.getSchemeSpecificPart();
121
122 // Add additional actions when there is only 1 missed call, like call-back and SMS.
123 if (mMissedCallCount == 1) {
124 Log.d(this, "Add actions with number %s.", Log.piiHandle(handle));
125
126 builder.addAction(R.drawable.stat_sys_phone_call,
127 mContext.getString(R.string.notification_missedCall_call_back),
128 createCallBackPendingIntent(handleUri));
129
130 builder.addAction(R.drawable.ic_text_holo_dark,
131 mContext.getString(R.string.notification_missedCall_message),
132 createSendSmsFromNotificationPendingIntent(handleUri));
133
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700134 Bitmap photoIcon = call.getPhotoIcon();
135 if (photoIcon != null) {
136 builder.setLargeIcon(photoIcon);
137 } else {
138 Drawable photo = call.getPhoto();
139 if (photo != null && photo instanceof BitmapDrawable) {
140 builder.setLargeIcon(((BitmapDrawable) photo).getBitmap());
141 }
142 }
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700143 } else {
Sailesh Nepal9b616c52014-04-10 20:20:35 -0700144 Log.d(this, "Suppress actions. handle: %s, missedCalls: %d.", Log.piiHandle(handle),
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700145 mMissedCallCount);
146 }
147
148 Notification notification = builder.build();
149 configureLedOnNotification(notification);
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700150
151 Log.i(this, "Adding missed call notification for %s.", call);
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700152 mNotificationManager.notify(MISSED_CALL_NOTIFICATION_ID, notification);
153 }
154
155 /** Cancels the "missed call" notification. */
156 private void cancelMissedCallNotification() {
157 // Reset the number of missed calls to 0.
158 mMissedCallCount = 0;
159 mNotificationManager.cancel(MISSED_CALL_NOTIFICATION_ID);
160 }
161
162 /**
163 * Returns the name to use in the missed call notification.
164 */
165 private String getNameForCall(Call call) {
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700166 String handle = call.getHandle().getSchemeSpecificPart();
Santos Cordon99c8a6f2014-05-28 18:28:47 -0700167 String name = call.getName();
168
169 if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
170 return name;
171 } else if (!TextUtils.isEmpty(handle)) {
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700172 // A handle should always be displayed LTR using {@link BidiFormatter} regardless of the
173 // content of the rest of the notification.
174 // TODO(santoscordon): Does this apply to SIP addresses?
175 BidiFormatter bidiFormatter = BidiFormatter.getInstance();
176 return bidiFormatter.unicodeWrap(handle, TextDirectionHeuristics.LTR);
177 } else {
178 // Use "unknown" if the call is unidentifiable.
179 return mContext.getString(R.string.unknown);
180 }
181 }
182
183 /**
184 * Creates a new pending intent that sends the user to the call log.
185 *
186 * @return The pending intent.
187 */
188 private PendingIntent createCallLogPendingIntent() {
189 Intent intent = new Intent(Intent.ACTION_VIEW, null);
190 intent.setType(CallLog.Calls.CONTENT_TYPE);
191
192 TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext);
193 taskStackBuilder.addNextIntent(intent);
194
195 return taskStackBuilder.getPendingIntent(0, 0);
196 }
197
198 /**
199 * Creates an intent to be invoked when the missed call notification is cleared.
200 */
201 private PendingIntent createClearMissedCallsPendingIntent() {
202 return createTelecommPendingIntent(
203 TelecommBroadcastReceiver.ACTION_CLEAR_MISSED_CALLS, null);
204 }
205
206 /**
207 * Creates an intent to be invoked when the user opts to "call back" from the missed call
208 * notification.
209 *
210 * @param handle The handle to call back.
211 */
212 private PendingIntent createCallBackPendingIntent(Uri handle) {
213 return createTelecommPendingIntent(
214 TelecommBroadcastReceiver.ACTION_CALL_BACK_FROM_NOTIFICATION, handle);
215 }
216
217 /**
218 * Creates an intent to be invoked when the user opts to "send sms" from the missed call
219 * notification.
220 */
221 private PendingIntent createSendSmsFromNotificationPendingIntent(Uri handle) {
222 return createTelecommPendingIntent(
223 TelecommBroadcastReceiver.ACTION_SEND_SMS_FROM_NOTIFICATION,
224 Uri.fromParts(SCHEME_SMSTO, handle.getSchemeSpecificPart(), null));
225 }
226
227 /**
228 * Creates generic pending intent from the specified parameters to be received by
229 * {@link TelecommBroadcastReceiver}.
230 *
231 * @param action The intent action.
232 * @param data The intent data.
233 */
234 private PendingIntent createTelecommPendingIntent(String action, Uri data) {
235 Intent intent = new Intent(action, data, mContext, TelecommBroadcastReceiver.class);
236 return PendingIntent.getBroadcast(mContext, 0, intent, 0);
237 }
238
239 /**
240 * Configures a notification to emit the blinky notification light.
241 */
242 private void configureLedOnNotification(Notification notification) {
243 notification.flags |= Notification.FLAG_SHOW_LIGHTS;
244 notification.defaults |= Notification.DEFAULT_LIGHTS;
245 }
246}