blob: c38be7143b477316fc7671aa3e367230c5b7b259 [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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070028import android.net.Uri;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070029import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070030import android.os.UserHandle;
31import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070032import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070033import android.provider.ContactsContract.PhoneLookup;
34import android.provider.Settings;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070035import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080036import android.telecom.PhoneAccountHandle;
37import android.telecom.TelecomManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.telephony.PhoneNumberUtils;
39import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080040import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080041import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080042import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080044import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070045import android.util.Log;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import android.widget.Toast;
47
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048import com.android.internal.telephony.Phone;
49import com.android.internal.telephony.PhoneBase;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.TelephonyCapabilities;
Andrew Lee8d66d812014-11-24 14:54:02 -080051import com.android.phone.settings.VoicemailNotificationSettingsUtil;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080052import com.android.phone.settings.VoicemailProviderSettingsUtil;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053
Tyler Gunn9c1071f2014-12-09 10:07:54 -080054import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070055import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080056import java.util.Map;
57import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070058
Santos Cordon7d4ddf62013-07-10 11:58:08 -070059/**
60 * NotificationManager-related utility code for the Phone app.
61 *
62 * This is a singleton object which acts as the interface to the
63 * framework's NotificationManager, and is used to display status bar
64 * icons and control other status bar-related behavior.
65 *
66 * @see PhoneGlobals.notificationMgr
67 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070068public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080069 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070070 private static final boolean DBG =
71 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
72 // Do not check in with VDBG = true, since that may write PII to the system log.
73 private static final boolean VDBG = false;
74
Santos Cordon7d4ddf62013-07-10 11:58:08 -070075 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070076 static final int MMI_NOTIFICATION = 1;
77 static final int NETWORK_SELECTION_NOTIFICATION = 2;
78 static final int VOICEMAIL_NOTIFICATION = 3;
79 static final int CALL_FORWARD_NOTIFICATION = 4;
80 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
81 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070082
83 /** The singleton NotificationMgr instance. */
84 private static NotificationMgr sInstance;
85
86 private PhoneGlobals mApp;
87 private Phone mPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070088
89 private Context mContext;
90 private NotificationManager mNotificationManager;
91 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -070092 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070093 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080094 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080095 private TelecomManager mTelecomManager;
96 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070097
98 public StatusBarHelper statusBarHelper;
99
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100 // used to track the notification of selected network unavailable
101 private boolean mSelectedUnavailableNotify = false;
102
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800103 // used to track whether the message waiting indicator is visible, per subscription id.
104 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
105
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106 /**
107 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700108 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700109 */
110 private NotificationMgr(PhoneGlobals app) {
111 mApp = app;
112 mContext = app;
113 mNotificationManager =
114 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
115 mStatusBarManager =
116 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700117 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800118 mPhone = app.mCM.getDefaultPhone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700119 statusBarHelper = new StatusBarHelper();
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800120 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800121 mTelecomManager = TelecomManager.from(mContext);
122 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700123 }
124
125 /**
126 * Initialize the singleton NotificationMgr instance.
127 *
128 * This is only done once, at startup, from PhoneApp.onCreate().
129 * From then on, the NotificationMgr instance is available via the
130 * PhoneApp's public "notificationMgr" field, which is why there's no
131 * getInstance() method here.
132 */
133 /* package */ static NotificationMgr init(PhoneGlobals app) {
134 synchronized (NotificationMgr.class) {
135 if (sInstance == null) {
136 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700137 } else {
138 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
139 }
140 return sInstance;
141 }
142 }
143
144 /**
145 * Helper class that's a wrapper around the framework's
146 * StatusBarManager.disable() API.
147 *
148 * This class is used to control features like:
149 *
150 * - Disabling the status bar "notification windowshade"
151 * while the in-call UI is up
152 *
153 * - Disabling notification alerts (audible or vibrating)
154 * while a phone call is active
155 *
156 * - Disabling navigation via the system bar (the "soft buttons" at
157 * the bottom of the screen on devices with no hard buttons)
158 *
159 * We control these features through a single point of control to make
160 * sure that the various StatusBarManager.disable() calls don't
161 * interfere with each other.
162 */
163 public class StatusBarHelper {
164 // Current desired state of status bar / system bar behavior
165 private boolean mIsNotificationEnabled = true;
166 private boolean mIsExpandedViewEnabled = true;
167 private boolean mIsSystemBarNavigationEnabled = true;
168
Andrew Leed5165b02014-12-05 15:53:58 -0800169 private StatusBarHelper() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700170 }
171
172 /**
173 * Enables or disables auditory / vibrational alerts.
174 *
175 * (We disable these any time a voice call is active, regardless
176 * of whether or not the in-call UI is visible.)
177 */
178 public void enableNotificationAlerts(boolean enable) {
179 if (mIsNotificationEnabled != enable) {
180 mIsNotificationEnabled = enable;
181 updateStatusBar();
182 }
183 }
184
185 /**
186 * Enables or disables the expanded view of the status bar
187 * (i.e. the ability to pull down the "notification windowshade").
188 *
189 * (This feature is disabled by the InCallScreen while the in-call
190 * UI is active.)
191 */
192 public void enableExpandedView(boolean enable) {
193 if (mIsExpandedViewEnabled != enable) {
194 mIsExpandedViewEnabled = enable;
195 updateStatusBar();
196 }
197 }
198
199 /**
200 * Enables or disables the navigation via the system bar (the
201 * "soft buttons" at the bottom of the screen)
202 *
203 * (This feature is disabled while an incoming call is ringing,
204 * because it's easy to accidentally touch the system bar while
205 * pulling the phone out of your pocket.)
206 */
207 public void enableSystemBarNavigation(boolean enable) {
208 if (mIsSystemBarNavigationEnabled != enable) {
209 mIsSystemBarNavigationEnabled = enable;
210 updateStatusBar();
211 }
212 }
213
214 /**
215 * Updates the status bar to reflect the current desired state.
216 */
217 private void updateStatusBar() {
218 int state = StatusBarManager.DISABLE_NONE;
219
220 if (!mIsExpandedViewEnabled) {
221 state |= StatusBarManager.DISABLE_EXPAND;
222 }
223 if (!mIsNotificationEnabled) {
224 state |= StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
225 }
226 if (!mIsSystemBarNavigationEnabled) {
227 // Disable *all* possible navigation via the system bar.
228 state |= StatusBarManager.DISABLE_HOME;
229 state |= StatusBarManager.DISABLE_RECENT;
230 state |= StatusBarManager.DISABLE_BACK;
Christine Chenb685f172013-09-25 18:32:59 -0700231 state |= StatusBarManager.DISABLE_SEARCH;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700232 }
233
234 if (DBG) log("updateStatusBar: state = 0x" + Integer.toHexString(state));
235 mStatusBarManager.disable(state);
236 }
237 }
238
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700239 /** The projection to use when querying the phones table */
240 static final String[] PHONES_PROJECTION = new String[] {
241 PhoneLookup.NUMBER,
242 PhoneLookup.DISPLAY_NAME,
243 PhoneLookup._ID
244 };
245
246 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800247 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
248 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
249 * settings screen. The voicemail notification sound is suppressed.
250 *
251 * @param subId The subscription Id.
252 */
253 /* package */ void refreshMwi(int subId) {
254 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
255 // reference the single subid stored in the mMwiVisible map.
256 if (subId == SubscriptionInfoHelper.NO_SUB_ID) {
257 if (mMwiVisible.keySet().size() == 1) {
258 Set<Integer> keySet = mMwiVisible.keySet();
259 Iterator<Integer> keyIt = keySet.iterator();
260 if (!keyIt.hasNext()) {
261 return;
262 }
263 subId = keyIt.next();
264 }
265 }
266 if (mMwiVisible.containsKey(subId)) {
267 boolean mwiVisible = mMwiVisible.get(subId);
268 if (mwiVisible) {
269 updateMwi(subId, mwiVisible, false /* enableNotificationSound */);
270 }
271 }
272 }
273
274 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700275 * Updates the message waiting indicator (voicemail) notification.
276 *
277 * @param visible true if there are messages waiting
278 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800279 /* package */ void updateMwi(int subId, boolean visible) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800280 updateMwi(subId, visible, true /* enableNotificationSound */);
281 }
282
283 /**
284 * Updates the message waiting indicator (voicemail) notification.
285 *
286 * @param subId the subId to update.
287 * @param visible true if there are messages waiting
288 * @param enableNotificationSound {@code true} if the notification sound should be played.
289 */
290 void updateMwi(int subId, boolean visible, boolean enableNotificationSound) {
Andrew Leea82b8202014-11-21 16:18:28 -0800291 if (!PhoneGlobals.sVoiceCapable) {
292 // Do not show the message waiting indicator on devices which are not voice capable.
293 // These events *should* be blocked at the telephony layer for such devices.
294 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
295 return;
296 }
297
Yorke Lee67a62a22014-12-15 18:46:17 -0800298 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800299 mMwiVisible.put(subId, visible);
300
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700301 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800302 Phone phone = PhoneGlobals.getPhone(subId);
303 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800304 Log.w(LOG_TAG, "Found null phone for: " + subId);
305 return;
306 }
307
308 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
309 if (subInfo == null) {
310 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800311 return;
312 }
313
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 int resId = android.R.drawable.stat_notify_voicemail;
315
316 // This Notification can get a lot fancier once we have more
317 // information about the current voicemail messages.
318 // (For example, the current voicemail system can't tell
319 // us the caller-id or timestamp of a message, or tell us the
320 // message count.)
321
322 // But for now, the UI is ultra-simple: if the MWI indication
323 // is supposed to be visible, just show a single generic
324 // notification.
325
326 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800327 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700328 if (DBG) log("- got vm number: '" + vmNumber + "'");
329
Andrew Leea82b8202014-11-21 16:18:28 -0800330 // The voicemail number may be null because:
331 // (1) This phone has no voicemail number.
332 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
333 // happen when the device first boots if we get a MWI notification when we
334 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800335 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
336 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700337 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800338 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700339 }
340
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800341 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
342 int vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700343 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
344 notificationTitle = String.format(titleFormat, vmCount);
345 }
346
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800347 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
348 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
349
350 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700351 String notificationText;
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800352 if (TextUtils.isEmpty(vmNumber)) {
353 notificationText = mContext.getString(
354 R.string.notification_voicemail_no_vm_number);
355
356 // If the voicemail number if unknown, instead of calling voicemail, take the user
357 // to the voicemail settings.
358 notificationText = mContext.getString(
359 R.string.notification_voicemail_no_vm_number);
360 intent = new Intent(CallFeaturesSetting.ACTION_ADD_VOICEMAIL);
361 intent.putExtra(CallFeaturesSetting.SETUP_VOICEMAIL_EXTRA, true);
362 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
363 intent.setClass(mContext, CallFeaturesSetting.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700364 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800365 if (mTelephonyManager.getPhoneCount() > 1) {
366 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800367 } else {
368 notificationText = String.format(
369 mContext.getString(R.string.notification_voicemail_text_format),
370 PhoneNumberUtils.formatNumber(vmNumber));
371 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800372 intent = new Intent(
373 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
374 null));
375 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700376 }
377
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800378 PendingIntent pendingIntent =
379 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800380 Uri ringtoneUri = null;
381
382 if (enableNotificationSound) {
383 ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
384 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700385
386 Notification.Builder builder = new Notification.Builder(mContext);
387 builder.setSmallIcon(resId)
388 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800389 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700390 .setContentTitle(notificationTitle)
391 .setContentText(notificationText)
392 .setContentIntent(pendingIntent)
Yorke Leeacb5f742014-08-19 09:08:42 -0700393 .setSound(ringtoneUri)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700394 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
395 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700396
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800397 if (VoicemailNotificationSettingsUtil.isVibrationEnabled(phone)) {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700398 builder.setDefaults(Notification.DEFAULT_VIBRATE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700399 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700400
401 final Notification notification = builder.build();
402 List<UserInfo> users = mUserManager.getUsers(true);
403 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700404 final UserInfo user = users.get(i);
405 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700406 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700407 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
408 && !user.isManagedProfile()) {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700409 mNotificationManager.notifyAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800410 Integer.toString(subId) /* tag */,
411 VOICEMAIL_NOTIFICATION,
412 notification,
413 userHandle);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700414 }
415 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700416 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700417 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800418 Integer.toString(subId) /* tag */,
419 VOICEMAIL_NOTIFICATION,
420 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700421 }
422 }
423
424 /**
425 * Updates the message call forwarding indicator notification.
426 *
427 * @param visible true if there are messages waiting
428 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800429 /* package */ void updateCfi(int subId, boolean visible) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700430 if (DBG) log("updateCfi(): " + visible);
431 if (visible) {
432 // If Unconditional Call Forwarding (forward all calls) for VOICE
433 // is enabled, just show a notification. We'll default to expanded
434 // view for now, so the there is less confusion about the icon. If
435 // it is deemed too weird to have CF indications as expanded views,
436 // then we'll flip the flag back.
437
438 // TODO: We may want to take a look to see if the notification can
439 // display the target to forward calls to. This will require some
440 // effort though, since there are multiple layers of messages that
441 // will need to propagate that information.
442
Andrew Leed5165b02014-12-05 15:53:58 -0800443 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
444 if (subInfo == null) {
445 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
446 return;
447 }
448
449 String notificationTitle;
450 if (mTelephonyManager.getPhoneCount() > 1) {
451 notificationTitle = subInfo.getDisplayName().toString();
452 } else {
453 notificationTitle = mContext.getString(R.string.labelCF);
454 }
455
Andrew Lee99d0ac22014-10-10 13:18:04 -0700456 Notification.Builder builder = new Notification.Builder(mContext)
457 .setSmallIcon(R.drawable.stat_sys_phone_call_forward)
Andrew Leed5165b02014-12-05 15:53:58 -0800458 .setColor(subInfo.getIconTint())
459 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700460 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
461 .setShowWhen(false)
462 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700463
Andrew Lee99d0ac22014-10-10 13:18:04 -0700464 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800465 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700466 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800467 SubscriptionInfoHelper.addExtrasToIntent(
468 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
469 PendingIntent contentIntent =
470 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700471
472 List<UserInfo> users = mUserManager.getUsers(true);
473 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800474 final UserInfo user = users.get(i);
475 if (user.isManagedProfile()) {
476 continue;
477 }
478 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700479 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800480 mNotificationManager.notifyAsUser(
481 Integer.toString(subId) /* tag */,
482 CALL_FORWARD_NOTIFICATION,
483 builder.build(),
484 userHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700485 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700486 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700487 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800488 Integer.toString(subId) /* tag */,
489 CALL_FORWARD_NOTIFICATION,
490 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700491 }
492 }
493
494 /**
495 * Shows the "data disconnected due to roaming" notification, which
496 * appears when you lose data connectivity because you're roaming and
497 * you have the "data roaming" feature turned off.
498 */
499 /* package */ void showDataDisconnectedRoaming() {
500 if (DBG) log("showDataDisconnectedRoaming()...");
501
502 // "Mobile network settings" screen / dialog
503 Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700504 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700505
506 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
507
Andrew Lee99d0ac22014-10-10 13:18:04 -0700508 final Notification.Builder builder = new Notification.Builder(mContext)
509 .setSmallIcon(android.R.drawable.stat_sys_warning)
510 .setContentTitle(mContext.getText(R.string.roaming))
511 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
512 .setContentText(contentText);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700513
Andrew Lee99d0ac22014-10-10 13:18:04 -0700514 List<UserInfo> users = mUserManager.getUsers(true);
515 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800516 final UserInfo user = users.get(i);
517 if (user.isManagedProfile()) {
518 continue;
519 }
520 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700521 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
522 final Notification notif =
523 new Notification.BigTextStyle(builder).bigText(contentText).build();
524 mNotificationManager.notifyAsUser(
525 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle);
526 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700527 }
528
529 /**
530 * Turns off the "data disconnected due to roaming" notification.
531 */
532 /* package */ void hideDataDisconnectedRoaming() {
533 if (DBG) log("hideDataDisconnectedRoaming()...");
534 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
535 }
536
537 /**
538 * Display the network selection "no service" notification
539 * @param operator is the numeric operator number
540 */
541 private void showNetworkSelection(String operator) {
542 if (DBG) log("showNetworkSelection(" + operator + ")...");
543
Andrew Lee99d0ac22014-10-10 13:18:04 -0700544 Notification.Builder builder = new Notification.Builder(mContext)
545 .setSmallIcon(android.R.drawable.stat_sys_warning)
546 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
547 .setContentText(
548 mContext.getString(R.string.notification_network_selection_text, operator))
549 .setShowWhen(false)
550 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700551
552 // create the target network operators settings intent
553 Intent intent = new Intent(Intent.ACTION_MAIN);
554 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
555 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
556 // Use NetworkSetting to handle the selection intent
557 intent.setComponent(new ComponentName("com.android.phone",
558 "com.android.phone.NetworkSetting"));
Andrew Lee99d0ac22014-10-10 13:18:04 -0700559 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700560
Andrew Lee99d0ac22014-10-10 13:18:04 -0700561 List<UserInfo> users = mUserManager.getUsers(true);
562 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800563 final UserInfo user = users.get(i);
564 if (user.isManagedProfile()) {
565 continue;
566 }
567 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700568 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
569 mNotificationManager.notifyAsUser(
570 null /* tag */,
571 SELECTED_OPERATOR_FAIL_NOTIFICATION,
572 builder.build(),
573 userHandle);
574 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700575 }
576
577 /**
578 * Turn off the network selection "no service" notification
579 */
580 private void cancelNetworkSelection() {
581 if (DBG) log("cancelNetworkSelection()...");
Andrew Lee99d0ac22014-10-10 13:18:04 -0700582 mNotificationManager.cancelAsUser(
583 null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700584 }
585
586 /**
587 * Update notification about no service of user selected operator
588 *
589 * @param serviceState Phone service state
590 */
591 void updateNetworkSelection(int serviceState) {
592 if (TelephonyCapabilities.supportsNetworkSelection(mPhone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800593 int subId = mPhone.getSubId();
594 if (SubscriptionManager.isValidSubscriptionId(subId)) {
595 // get the shared preference of network_selection.
596 // empty is auto mode, otherwise it is the operator alpha name
597 // in case there is no operator name, check the operator numeric
598 SharedPreferences sp =
599 PreferenceManager.getDefaultSharedPreferences(mContext);
600 String networkSelection =
601 sp.getString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId, "");
602 if (TextUtils.isEmpty(networkSelection)) {
603 networkSelection =
604 sp.getString(PhoneBase.NETWORK_SELECTION_KEY + subId, "");
605 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700606
Amit Mahajana60be872015-01-15 16:05:08 -0800607 if (DBG) log("updateNetworkSelection()..." + "state = " +
608 serviceState + " new network " + networkSelection);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700609
Amit Mahajana60be872015-01-15 16:05:08 -0800610 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
611 && !TextUtils.isEmpty(networkSelection)) {
612 if (!mSelectedUnavailableNotify) {
613 showNetworkSelection(networkSelection);
614 mSelectedUnavailableNotify = true;
615 }
616 } else {
617 if (mSelectedUnavailableNotify) {
618 cancelNetworkSelection();
619 mSelectedUnavailableNotify = false;
620 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700621 }
622 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800623 if (DBG) log("updateNetworkSelection()..." + "state = " +
624 serviceState + " not updating network due to invalid subId " + subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700625 }
626 }
627 }
628
629 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
630 if (mToast != null) {
631 mToast.cancel();
632 }
633
634 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
635 mToast.show();
636 }
637
638 private void log(String msg) {
639 Log.d(LOG_TAG, msg);
640 }
641}