blob: 9414030252f9567484c830bb22ddd518c65a7c15 [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;
26import android.net.Uri;
27import android.provider.CallLog;
28import android.provider.CallLog.Calls;
29import android.telecomm.CallState;
30import android.telephony.DisconnectCause;
31import android.text.BidiFormatter;
32import android.text.TextDirectionHeuristics;
33import android.text.TextUtils;
34
35/**
36 * Creates a notification for calls that the user missed (neither answered nor rejected).
37 * TODO(santoscordon): Make TelephonyManager.clearMissedCalls call into this class.
38 * STOPSHIP: Resolve b/13769374 about moving this class to InCall.
39 */
40class MissedCallNotifier extends CallsManagerListenerBase {
41
42 private static final int MISSED_CALL_NOTIFICATION_ID = 1;
43 private static final String SCHEME_SMSTO = "smsto";
44
45 private final Context mContext;
46 private final NotificationManager mNotificationManager;
47
48 // Used to track the number of missed calls.
49 private int mMissedCallCount = 0;
50
51 MissedCallNotifier(Context context) {
52 mContext = context;
53 mNotificationManager =
54 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
55 }
56
57 /** {@inheritDoc} */
58 @Override
59 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
60 if (oldState == CallState.RINGING && newState == CallState.DISCONNECTED &&
61 call.getDisconnectCause() == DisconnectCause.INCOMING_MISSED) {
62 showMissedCallNotification(call);
63 }
64 }
65
66 /** Clears missed call notification and marks the call log's missed calls as read. */
67 void clearMissedCalls() {
68 // Clear the list of new missed calls from the call log.
69 ContentValues values = new ContentValues();
70 values.put(Calls.NEW, 0);
71 values.put(Calls.IS_READ, 1);
72 StringBuilder where = new StringBuilder();
73 where.append(Calls.NEW);
74 where.append(" = 1 AND ");
75 where.append(Calls.TYPE);
76 where.append(" = ?");
77 mContext.getContentResolver().update(Calls.CONTENT_URI, values, where.toString(),
78 new String[]{ Integer.toString(Calls.MISSED_TYPE) });
79
80 cancelMissedCallNotification();
81 }
82
83 /**
84 * Create a system notification for the missed call.
85 *
86 * @param call The missed call.
87 */
88 private void showMissedCallNotification(Call call) {
89 mMissedCallCount++;
90
91 final int titleResId;
92 final String expandedText; // The text in the notification's line 1 and 2.
93
94 // Display the first line of the notification:
95 // 1 missed call: <caller name || handle>
96 // More than 1 missed call: <number of calls> + "missed calls"
97 if (mMissedCallCount == 1) {
98 titleResId = R.string.notification_missedCallTitle;
99 expandedText = getNameForCall(call);
100 } else {
101 titleResId = R.string.notification_missedCallsTitle;
102 expandedText =
103 mContext.getString(R.string.notification_missedCallsMsg, mMissedCallCount);
104 }
105
106 // Create the notification.
107 Notification.Builder builder = new Notification.Builder(mContext);
108 builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700109 .setWhen(call.getCreationTimeMillis())
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700110 .setContentTitle(mContext.getText(titleResId))
111 .setContentText(expandedText)
112 .setContentIntent(createCallLogPendingIntent())
113 .setAutoCancel(true)
114 .setDeleteIntent(createClearMissedCallsPendingIntent());
115
116 Uri handleUri = call.getHandle();
117 String handle = handleUri.getSchemeSpecificPart();
118
119 // Add additional actions when there is only 1 missed call, like call-back and SMS.
120 if (mMissedCallCount == 1) {
121 Log.d(this, "Add actions with number %s.", Log.piiHandle(handle));
122
123 builder.addAction(R.drawable.stat_sys_phone_call,
124 mContext.getString(R.string.notification_missedCall_call_back),
125 createCallBackPendingIntent(handleUri));
126
127 builder.addAction(R.drawable.ic_text_holo_dark,
128 mContext.getString(R.string.notification_missedCall_message),
129 createSendSmsFromNotificationPendingIntent(handleUri));
130
131 // TODO(santoscordon): Add photo for contact.
132 } else {
133 Log.d(this, "Suppress actions. handle: %s, missedCalls: %s." + Log.piiHandle(handle),
134 mMissedCallCount);
135 }
136
137 Notification notification = builder.build();
138 configureLedOnNotification(notification);
139 mNotificationManager.notify(MISSED_CALL_NOTIFICATION_ID, notification);
140 }
141
142 /** Cancels the "missed call" notification. */
143 private void cancelMissedCallNotification() {
144 // Reset the number of missed calls to 0.
145 mMissedCallCount = 0;
146 mNotificationManager.cancel(MISSED_CALL_NOTIFICATION_ID);
147 }
148
149 /**
150 * Returns the name to use in the missed call notification.
151 */
152 private String getNameForCall(Call call) {
153 // TODO(santoscordon): Get detailed caller information.
154
155 String handle = call.getHandle().getSchemeSpecificPart();
156 if (!TextUtils.isEmpty(handle)) {
157 // A handle should always be displayed LTR using {@link BidiFormatter} regardless of the
158 // content of the rest of the notification.
159 // TODO(santoscordon): Does this apply to SIP addresses?
160 BidiFormatter bidiFormatter = BidiFormatter.getInstance();
161 return bidiFormatter.unicodeWrap(handle, TextDirectionHeuristics.LTR);
162 } else {
163 // Use "unknown" if the call is unidentifiable.
164 return mContext.getString(R.string.unknown);
165 }
166 }
167
168 /**
169 * Creates a new pending intent that sends the user to the call log.
170 *
171 * @return The pending intent.
172 */
173 private PendingIntent createCallLogPendingIntent() {
174 Intent intent = new Intent(Intent.ACTION_VIEW, null);
175 intent.setType(CallLog.Calls.CONTENT_TYPE);
176
177 TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext);
178 taskStackBuilder.addNextIntent(intent);
179
180 return taskStackBuilder.getPendingIntent(0, 0);
181 }
182
183 /**
184 * Creates an intent to be invoked when the missed call notification is cleared.
185 */
186 private PendingIntent createClearMissedCallsPendingIntent() {
187 return createTelecommPendingIntent(
188 TelecommBroadcastReceiver.ACTION_CLEAR_MISSED_CALLS, null);
189 }
190
191 /**
192 * Creates an intent to be invoked when the user opts to "call back" from the missed call
193 * notification.
194 *
195 * @param handle The handle to call back.
196 */
197 private PendingIntent createCallBackPendingIntent(Uri handle) {
198 return createTelecommPendingIntent(
199 TelecommBroadcastReceiver.ACTION_CALL_BACK_FROM_NOTIFICATION, handle);
200 }
201
202 /**
203 * Creates an intent to be invoked when the user opts to "send sms" from the missed call
204 * notification.
205 */
206 private PendingIntent createSendSmsFromNotificationPendingIntent(Uri handle) {
207 return createTelecommPendingIntent(
208 TelecommBroadcastReceiver.ACTION_SEND_SMS_FROM_NOTIFICATION,
209 Uri.fromParts(SCHEME_SMSTO, handle.getSchemeSpecificPart(), null));
210 }
211
212 /**
213 * Creates generic pending intent from the specified parameters to be received by
214 * {@link TelecommBroadcastReceiver}.
215 *
216 * @param action The intent action.
217 * @param data The intent data.
218 */
219 private PendingIntent createTelecommPendingIntent(String action, Uri data) {
220 Intent intent = new Intent(action, data, mContext, TelecommBroadcastReceiver.class);
221 return PendingIntent.getBroadcast(mContext, 0, intent, 0);
222 }
223
224 /**
225 * Configures a notification to emit the blinky notification light.
226 */
227 private void configureLedOnNotification(Notification notification) {
228 notification.flags |= Notification.FLAG_SHOW_LIGHTS;
229 notification.defaults |= Notification.DEFAULT_LIGHTS;
230 }
231}