blob: 4d5316ab59dbd485dcaeb2248b7f3f2eddce78d7 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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.phone;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.app.StatusBarManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070023import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070024import android.content.Context;
25import android.content.Intent;
26import android.content.SharedPreferences;
Andrew Lee99d0ac22014-10-10 13:18:04 -070027import android.content.pm.UserInfo;
Nancy Chenb4a92702014-12-04 15:57:29 -080028import android.content.res.Resources;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070029import android.net.Uri;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070030import android.os.PersistableBundle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070031import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070032import android.os.UserHandle;
33import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070035import android.provider.ContactsContract.PhoneLookup;
36import android.provider.Settings;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070037import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080038import android.telecom.PhoneAccountHandle;
39import android.telecom.TelecomManager;
Jonathan Basseri3649bdb2015-04-30 22:39:11 -070040import android.telephony.CarrierConfigManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070041import android.telephony.PhoneNumberUtils;
42import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080043import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080044import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080045import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080047import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048import android.util.Log;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import android.widget.Toast;
50
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import com.android.internal.telephony.Phone;
52import com.android.internal.telephony.PhoneBase;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import com.android.internal.telephony.TelephonyCapabilities;
Andrew Leebf07f762015-04-07 19:05:50 -070054import com.android.phone.settings.VoicemailSettingsActivity;
Nancy Chen2cf7f292015-05-15 11:00:10 -070055import com.android.phone.vvm.omtp.sync.VoicemailStatusQueryHelper;
Andrew Lee8d66d812014-11-24 14:54:02 -080056import com.android.phone.settings.VoicemailNotificationSettingsUtil;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080057import com.android.phone.settings.VoicemailProviderSettingsUtil;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058
Tyler Gunn9c1071f2014-12-09 10:07:54 -080059import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070060import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080061import java.util.Map;
62import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070063
Santos Cordon7d4ddf62013-07-10 11:58:08 -070064/**
65 * NotificationManager-related utility code for the Phone app.
66 *
67 * This is a singleton object which acts as the interface to the
68 * framework's NotificationManager, and is used to display status bar
69 * icons and control other status bar-related behavior.
70 *
71 * @see PhoneGlobals.notificationMgr
72 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070073public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080074 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070075 private static final boolean DBG =
76 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
77 // Do not check in with VDBG = true, since that may write PII to the system log.
78 private static final boolean VDBG = false;
79
Santos Cordon7d4ddf62013-07-10 11:58:08 -070080 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070081 static final int MMI_NOTIFICATION = 1;
82 static final int NETWORK_SELECTION_NOTIFICATION = 2;
83 static final int VOICEMAIL_NOTIFICATION = 3;
84 static final int CALL_FORWARD_NOTIFICATION = 4;
85 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
86 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070087
88 /** The singleton NotificationMgr instance. */
89 private static NotificationMgr sInstance;
90
91 private PhoneGlobals mApp;
92 private Phone mPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070093
94 private Context mContext;
95 private NotificationManager mNotificationManager;
Bryce Lee5dc90842015-08-11 07:57:14 -070096 private final ComponentName mNotificationComponent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070097 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -070098 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070099 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800100 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -0800101 private TelecomManager mTelecomManager;
102 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700103
104 public StatusBarHelper statusBarHelper;
105
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106 // used to track the notification of selected network unavailable
107 private boolean mSelectedUnavailableNotify = false;
108
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800109 // used to track whether the message waiting indicator is visible, per subscription id.
110 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
111
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700112 /**
113 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700114 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700115 */
116 private NotificationMgr(PhoneGlobals app) {
117 mApp = app;
118 mContext = app;
119 mNotificationManager =
120 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
121 mStatusBarManager =
122 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700123 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800124 mPhone = app.mCM.getDefaultPhone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125 statusBarHelper = new StatusBarHelper();
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800126 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800127 mTelecomManager = TelecomManager.from(mContext);
128 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Bryce Lee5dc90842015-08-11 07:57:14 -0700129
130 final String notificationComponent = mContext.getString(
131 R.string.config_customVoicemailComponent);
132
133 mNotificationComponent = notificationComponent != null
134 ? ComponentName.unflattenFromString(notificationComponent) : null;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700135 }
136
137 /**
138 * Initialize the singleton NotificationMgr instance.
139 *
140 * This is only done once, at startup, from PhoneApp.onCreate().
141 * From then on, the NotificationMgr instance is available via the
142 * PhoneApp's public "notificationMgr" field, which is why there's no
143 * getInstance() method here.
144 */
145 /* package */ static NotificationMgr init(PhoneGlobals app) {
146 synchronized (NotificationMgr.class) {
147 if (sInstance == null) {
148 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700149 } else {
150 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
151 }
152 return sInstance;
153 }
154 }
155
156 /**
157 * Helper class that's a wrapper around the framework's
158 * StatusBarManager.disable() API.
159 *
160 * This class is used to control features like:
161 *
162 * - Disabling the status bar "notification windowshade"
163 * while the in-call UI is up
164 *
165 * - Disabling notification alerts (audible or vibrating)
166 * while a phone call is active
167 *
168 * - Disabling navigation via the system bar (the "soft buttons" at
169 * the bottom of the screen on devices with no hard buttons)
170 *
171 * We control these features through a single point of control to make
172 * sure that the various StatusBarManager.disable() calls don't
173 * interfere with each other.
174 */
175 public class StatusBarHelper {
176 // Current desired state of status bar / system bar behavior
177 private boolean mIsNotificationEnabled = true;
178 private boolean mIsExpandedViewEnabled = true;
179 private boolean mIsSystemBarNavigationEnabled = true;
180
Andrew Leed5165b02014-12-05 15:53:58 -0800181 private StatusBarHelper() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700182 }
183
184 /**
185 * Enables or disables auditory / vibrational alerts.
186 *
187 * (We disable these any time a voice call is active, regardless
188 * of whether or not the in-call UI is visible.)
189 */
190 public void enableNotificationAlerts(boolean enable) {
191 if (mIsNotificationEnabled != enable) {
192 mIsNotificationEnabled = enable;
193 updateStatusBar();
194 }
195 }
196
197 /**
198 * Enables or disables the expanded view of the status bar
199 * (i.e. the ability to pull down the "notification windowshade").
200 *
201 * (This feature is disabled by the InCallScreen while the in-call
202 * UI is active.)
203 */
204 public void enableExpandedView(boolean enable) {
205 if (mIsExpandedViewEnabled != enable) {
206 mIsExpandedViewEnabled = enable;
207 updateStatusBar();
208 }
209 }
210
211 /**
212 * Enables or disables the navigation via the system bar (the
213 * "soft buttons" at the bottom of the screen)
214 *
215 * (This feature is disabled while an incoming call is ringing,
216 * because it's easy to accidentally touch the system bar while
217 * pulling the phone out of your pocket.)
218 */
219 public void enableSystemBarNavigation(boolean enable) {
220 if (mIsSystemBarNavigationEnabled != enable) {
221 mIsSystemBarNavigationEnabled = enable;
222 updateStatusBar();
223 }
224 }
225
226 /**
227 * Updates the status bar to reflect the current desired state.
228 */
229 private void updateStatusBar() {
230 int state = StatusBarManager.DISABLE_NONE;
231
232 if (!mIsExpandedViewEnabled) {
233 state |= StatusBarManager.DISABLE_EXPAND;
234 }
235 if (!mIsNotificationEnabled) {
236 state |= StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
237 }
238 if (!mIsSystemBarNavigationEnabled) {
239 // Disable *all* possible navigation via the system bar.
240 state |= StatusBarManager.DISABLE_HOME;
241 state |= StatusBarManager.DISABLE_RECENT;
242 state |= StatusBarManager.DISABLE_BACK;
Christine Chenb685f172013-09-25 18:32:59 -0700243 state |= StatusBarManager.DISABLE_SEARCH;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700244 }
245
246 if (DBG) log("updateStatusBar: state = 0x" + Integer.toHexString(state));
247 mStatusBarManager.disable(state);
248 }
249 }
250
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700251 /** The projection to use when querying the phones table */
252 static final String[] PHONES_PROJECTION = new String[] {
253 PhoneLookup.NUMBER,
254 PhoneLookup.DISPLAY_NAME,
255 PhoneLookup._ID
256 };
257
258 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800259 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
260 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
261 * settings screen. The voicemail notification sound is suppressed.
262 *
263 * @param subId The subscription Id.
264 */
265 /* package */ void refreshMwi(int subId) {
266 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
267 // reference the single subid stored in the mMwiVisible map.
268 if (subId == SubscriptionInfoHelper.NO_SUB_ID) {
269 if (mMwiVisible.keySet().size() == 1) {
270 Set<Integer> keySet = mMwiVisible.keySet();
271 Iterator<Integer> keyIt = keySet.iterator();
272 if (!keyIt.hasNext()) {
273 return;
274 }
275 subId = keyIt.next();
276 }
277 }
278 if (mMwiVisible.containsKey(subId)) {
279 boolean mwiVisible = mMwiVisible.get(subId);
280 if (mwiVisible) {
281 updateMwi(subId, mwiVisible, false /* enableNotificationSound */);
282 }
283 }
284 }
285
286 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700287 * Updates the message waiting indicator (voicemail) notification.
288 *
289 * @param visible true if there are messages waiting
290 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800291 /* package */ void updateMwi(int subId, boolean visible) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800292 updateMwi(subId, visible, true /* enableNotificationSound */);
293 }
294
295 /**
296 * Updates the message waiting indicator (voicemail) notification.
297 *
298 * @param subId the subId to update.
299 * @param visible true if there are messages waiting
300 * @param enableNotificationSound {@code true} if the notification sound should be played.
301 */
302 void updateMwi(int subId, boolean visible, boolean enableNotificationSound) {
Andrew Leea82b8202014-11-21 16:18:28 -0800303 if (!PhoneGlobals.sVoiceCapable) {
304 // Do not show the message waiting indicator on devices which are not voice capable.
305 // These events *should* be blocked at the telephony layer for such devices.
306 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
307 return;
308 }
309
Nancy Chen2cf7f292015-05-15 11:00:10 -0700310 Phone phone = PhoneGlobals.getPhone(subId);
311 if (visible && phone != null) {
312 VoicemailStatusQueryHelper queryHelper = new VoicemailStatusQueryHelper(mContext);
313 PhoneAccountHandle phoneAccount = PhoneUtils.makePstnPhoneAccountHandle(phone);
314 if (queryHelper.isNotificationsChannelActive(phoneAccount)) {
315 Log.v(LOG_TAG, "Notifications channel active for visual voicemail, hiding mwi.");
316 visible = false;
317 }
318 }
319
Yorke Lee67a62a22014-12-15 18:46:17 -0800320 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800321 mMwiVisible.put(subId, visible);
322
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700323 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800324 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800325 Log.w(LOG_TAG, "Found null phone for: " + subId);
326 return;
327 }
328
329 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
330 if (subInfo == null) {
331 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800332 return;
333 }
334
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700335 int resId = android.R.drawable.stat_notify_voicemail;
336
337 // This Notification can get a lot fancier once we have more
338 // information about the current voicemail messages.
339 // (For example, the current voicemail system can't tell
340 // us the caller-id or timestamp of a message, or tell us the
341 // message count.)
342
343 // But for now, the UI is ultra-simple: if the MWI indication
344 // is supposed to be visible, just show a single generic
345 // notification.
346
347 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800348 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700349 if (DBG) log("- got vm number: '" + vmNumber + "'");
350
Andrew Leea82b8202014-11-21 16:18:28 -0800351 // The voicemail number may be null because:
352 // (1) This phone has no voicemail number.
353 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
354 // happen when the device first boots if we get a MWI notification when we
355 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800356 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
357 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700358 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800359 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700360 }
361
Bryce Lee5dc90842015-08-11 07:57:14 -0700362 Integer vmCount = null;
363
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800364 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700365 vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700366 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
367 notificationTitle = String.format(titleFormat, vmCount);
368 }
369
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800370 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
371 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
372
373 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700374 String notificationText;
Bryce Lee5dc90842015-08-11 07:57:14 -0700375 boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
376
377 if (isSettingsIntent) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800378 notificationText = mContext.getString(
379 R.string.notification_voicemail_no_vm_number);
380
381 // If the voicemail number if unknown, instead of calling voicemail, take the user
382 // to the voicemail settings.
383 notificationText = mContext.getString(
384 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700385 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800386 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700387 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700388 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800389 if (mTelephonyManager.getPhoneCount() > 1) {
390 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800391 } else {
392 notificationText = String.format(
393 mContext.getString(R.string.notification_voicemail_text_format),
394 PhoneNumberUtils.formatNumber(vmNumber));
395 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800396 intent = new Intent(
397 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700398 null));
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800399 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700400 }
401
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800402 PendingIntent pendingIntent =
403 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800404 Uri ringtoneUri = null;
405
406 if (enableNotificationSound) {
407 ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
408 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700409
Nancy Chenb4a92702014-12-04 15:57:29 -0800410 Resources res = mContext.getResources();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700411 PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700412 mPhone.getSubId());
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700413 Notification.Builder builder = new Notification.Builder(mContext);
414 builder.setSmallIcon(resId)
415 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800416 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700417 .setContentTitle(notificationTitle)
418 .setContentText(notificationText)
419 .setContentIntent(pendingIntent)
Yorke Leeacb5f742014-08-19 09:08:42 -0700420 .setSound(ringtoneUri)
Nancy Chenb4a92702014-12-04 15:57:29 -0800421 .setColor(res.getColor(R.color.dialer_theme_color))
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700422 .setOngoing(carrierConfig.getBoolean(
Jonathan Basseri9504c6b2015-06-04 14:23:32 -0700423 CarrierConfigManager.KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700424
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800425 if (VoicemailNotificationSettingsUtil.isVibrationEnabled(phone)) {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700426 builder.setDefaults(Notification.DEFAULT_VIBRATE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700427 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700428
429 final Notification notification = builder.build();
430 List<UserInfo> users = mUserManager.getUsers(true);
431 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700432 final UserInfo user = users.get(i);
433 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700434 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700435 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
Jonathan Basseri3649bdb2015-04-30 22:39:11 -0700436 && !user.isManagedProfile()) {
Bryce Lee5dc90842015-08-11 07:57:14 -0700437 if (!sendNotificationCustomComponent(vmCount, vmNumber, pendingIntent,
438 isSettingsIntent)) {
439 mNotificationManager.notifyAsUser(
440 Integer.toString(subId) /* tag */,
441 VOICEMAIL_NOTIFICATION,
442 notification,
443 userHandle);
444 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700445 }
446 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700447 } else {
Bryce Lee5dc90842015-08-11 07:57:14 -0700448 if (!sendNotificationCustomComponent(0, null, null, false)) {
449 mNotificationManager.cancelAsUser(
450 Integer.toString(subId) /* tag */,
451 VOICEMAIL_NOTIFICATION,
452 UserHandle.ALL);
453 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700454 }
455 }
456
457 /**
Bryce Lee5dc90842015-08-11 07:57:14 -0700458 * Sends a broadcast with the voicemail notification information to a custom component to
459 * handle. This method is also used to indicate to the custom component when to clear the
460 * notification. A pending intent can be passed to the custom component to indicate an action to
461 * be taken as it would by a notification produced in this class.
462 * @param count The number of pending voicemail messages to indicate on the notification. A
463 * Value of 0 is passed here to indicate that the notification should be cleared.
464 * @param number The voicemail phone number if specified.
465 * @param pendingIntent The intent that should be passed as the action to be taken.
466 * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
467 * otherwise, {@code false} to indicate the intent launches voicemail.
468 * @return {@code true} if a custom component was notified of the notification.
469 */
470 private boolean sendNotificationCustomComponent(Integer count, String number,
471 PendingIntent pendingIntent, boolean isSettingsIntent) {
472 if (mNotificationComponent != null) {
473 Intent intent = new Intent();
474 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
475 intent.setComponent(mNotificationComponent);
476 intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
477
478 if (count != null) {
479 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
480 }
481
482 // Additional information about the voicemail notification beyond the count is only
483 // present when the count not specified or greater than 0. The value of 0 represents
484 // clearing the notification, which does not require additional information.
485 if (count == null || count > 0) {
486 if (!TextUtils.isEmpty(number)) {
487 intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
488 }
489
490 if (pendingIntent != null) {
491 intent.putExtra(isSettingsIntent
492 ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
493 : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
494 pendingIntent);
495 }
496 }
497
498 mContext.sendBroadcast(intent);
499 return true;
500 }
501
502 return false;
503 }
504
505 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700506 * Updates the message call forwarding indicator notification.
507 *
508 * @param visible true if there are messages waiting
509 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800510 /* package */ void updateCfi(int subId, boolean visible) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700511 if (DBG) log("updateCfi(): " + visible);
512 if (visible) {
513 // If Unconditional Call Forwarding (forward all calls) for VOICE
514 // is enabled, just show a notification. We'll default to expanded
515 // view for now, so the there is less confusion about the icon. If
516 // it is deemed too weird to have CF indications as expanded views,
517 // then we'll flip the flag back.
518
519 // TODO: We may want to take a look to see if the notification can
520 // display the target to forward calls to. This will require some
521 // effort though, since there are multiple layers of messages that
522 // will need to propagate that information.
523
Andrew Leed5165b02014-12-05 15:53:58 -0800524 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
525 if (subInfo == null) {
526 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
527 return;
528 }
529
530 String notificationTitle;
531 if (mTelephonyManager.getPhoneCount() > 1) {
532 notificationTitle = subInfo.getDisplayName().toString();
533 } else {
534 notificationTitle = mContext.getString(R.string.labelCF);
535 }
536
Andrew Lee99d0ac22014-10-10 13:18:04 -0700537 Notification.Builder builder = new Notification.Builder(mContext)
538 .setSmallIcon(R.drawable.stat_sys_phone_call_forward)
Andrew Leed5165b02014-12-05 15:53:58 -0800539 .setColor(subInfo.getIconTint())
540 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700541 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
542 .setShowWhen(false)
543 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700544
Andrew Lee99d0ac22014-10-10 13:18:04 -0700545 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800546 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700547 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800548 SubscriptionInfoHelper.addExtrasToIntent(
549 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
550 PendingIntent contentIntent =
551 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700552
553 List<UserInfo> users = mUserManager.getUsers(true);
554 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800555 final UserInfo user = users.get(i);
556 if (user.isManagedProfile()) {
557 continue;
558 }
559 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700560 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800561 mNotificationManager.notifyAsUser(
562 Integer.toString(subId) /* tag */,
563 CALL_FORWARD_NOTIFICATION,
564 builder.build(),
565 userHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700566 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700567 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700568 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800569 Integer.toString(subId) /* tag */,
570 CALL_FORWARD_NOTIFICATION,
571 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700572 }
573 }
574
575 /**
576 * Shows the "data disconnected due to roaming" notification, which
577 * appears when you lose data connectivity because you're roaming and
578 * you have the "data roaming" feature turned off.
579 */
580 /* package */ void showDataDisconnectedRoaming() {
581 if (DBG) log("showDataDisconnectedRoaming()...");
582
583 // "Mobile network settings" screen / dialog
584 Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700585 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700586
587 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
588
Andrew Lee99d0ac22014-10-10 13:18:04 -0700589 final Notification.Builder builder = new Notification.Builder(mContext)
590 .setSmallIcon(android.R.drawable.stat_sys_warning)
591 .setContentTitle(mContext.getText(R.string.roaming))
592 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
593 .setContentText(contentText);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700594
Andrew Lee99d0ac22014-10-10 13:18:04 -0700595 List<UserInfo> users = mUserManager.getUsers(true);
596 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800597 final UserInfo user = users.get(i);
598 if (user.isManagedProfile()) {
599 continue;
600 }
601 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700602 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
603 final Notification notif =
604 new Notification.BigTextStyle(builder).bigText(contentText).build();
605 mNotificationManager.notifyAsUser(
606 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle);
607 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700608 }
609
610 /**
611 * Turns off the "data disconnected due to roaming" notification.
612 */
613 /* package */ void hideDataDisconnectedRoaming() {
614 if (DBG) log("hideDataDisconnectedRoaming()...");
615 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
616 }
617
618 /**
619 * Display the network selection "no service" notification
620 * @param operator is the numeric operator number
621 */
622 private void showNetworkSelection(String operator) {
623 if (DBG) log("showNetworkSelection(" + operator + ")...");
624
Andrew Lee99d0ac22014-10-10 13:18:04 -0700625 Notification.Builder builder = new Notification.Builder(mContext)
626 .setSmallIcon(android.R.drawable.stat_sys_warning)
627 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
628 .setContentText(
629 mContext.getString(R.string.notification_network_selection_text, operator))
630 .setShowWhen(false)
631 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700632
633 // create the target network operators settings intent
634 Intent intent = new Intent(Intent.ACTION_MAIN);
635 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
636 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
637 // Use NetworkSetting to handle the selection intent
638 intent.setComponent(new ComponentName("com.android.phone",
639 "com.android.phone.NetworkSetting"));
Sanket Padawe3e1073d2015-07-15 18:28:12 -0700640 intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, mPhone.getSubId());
Andrew Lee99d0ac22014-10-10 13:18:04 -0700641 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700642
Andrew Lee99d0ac22014-10-10 13:18:04 -0700643 List<UserInfo> users = mUserManager.getUsers(true);
644 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800645 final UserInfo user = users.get(i);
646 if (user.isManagedProfile()) {
647 continue;
648 }
649 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700650 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
651 mNotificationManager.notifyAsUser(
652 null /* tag */,
653 SELECTED_OPERATOR_FAIL_NOTIFICATION,
654 builder.build(),
655 userHandle);
656 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700657 }
658
659 /**
660 * Turn off the network selection "no service" notification
661 */
662 private void cancelNetworkSelection() {
663 if (DBG) log("cancelNetworkSelection()...");
Andrew Lee99d0ac22014-10-10 13:18:04 -0700664 mNotificationManager.cancelAsUser(
665 null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700666 }
667
668 /**
669 * Update notification about no service of user selected operator
670 *
671 * @param serviceState Phone service state
672 */
673 void updateNetworkSelection(int serviceState) {
674 if (TelephonyCapabilities.supportsNetworkSelection(mPhone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800675 int subId = mPhone.getSubId();
676 if (SubscriptionManager.isValidSubscriptionId(subId)) {
677 // get the shared preference of network_selection.
678 // empty is auto mode, otherwise it is the operator alpha name
679 // in case there is no operator name, check the operator numeric
680 SharedPreferences sp =
681 PreferenceManager.getDefaultSharedPreferences(mContext);
682 String networkSelection =
683 sp.getString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId, "");
684 if (TextUtils.isEmpty(networkSelection)) {
685 networkSelection =
686 sp.getString(PhoneBase.NETWORK_SELECTION_KEY + subId, "");
687 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700688
Amit Mahajana60be872015-01-15 16:05:08 -0800689 if (DBG) log("updateNetworkSelection()..." + "state = " +
690 serviceState + " new network " + networkSelection);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700691
Amit Mahajana60be872015-01-15 16:05:08 -0800692 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
693 && !TextUtils.isEmpty(networkSelection)) {
694 if (!mSelectedUnavailableNotify) {
695 showNetworkSelection(networkSelection);
696 mSelectedUnavailableNotify = true;
697 }
698 } else {
699 if (mSelectedUnavailableNotify) {
700 cancelNetworkSelection();
701 mSelectedUnavailableNotify = false;
702 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700703 }
704 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800705 if (DBG) log("updateNetworkSelection()..." + "state = " +
706 serviceState + " not updating network due to invalid subId " + subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700707 }
708 }
709 }
710
711 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
712 if (mToast != null) {
713 mToast.cancel();
714 }
715
716 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
717 mToast.show();
718 }
719
720 private void log(String msg) {
721 Log.d(LOG_TAG, msg);
722 }
723}