blob: 1460d63e4749ec2f6326c8ab2904668a2e4a00f5 [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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.os.SystemProperties;
Andrew Lee99d0ac22014-10-10 13:18:04 -070031import android.os.UserHandle;
32import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070033import android.preference.PreferenceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.provider.ContactsContract.PhoneLookup;
35import android.provider.Settings;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070036import android.telecom.PhoneAccount;
Andrew Leed5165b02014-12-05 15:53:58 -080037import android.telecom.PhoneAccountHandle;
38import android.telecom.TelecomManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070039import android.telephony.PhoneNumberUtils;
40import android.telephony.ServiceState;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080041import android.telephony.SubscriptionInfo;
Andrew Leea82b8202014-11-21 16:18:28 -080042import android.telephony.SubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080043import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044import android.text.TextUtils;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080045import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import android.util.Log;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070047import android.widget.Toast;
48
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import com.android.internal.telephony.Phone;
50import com.android.internal.telephony.PhoneBase;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import com.android.internal.telephony.TelephonyCapabilities;
Andrew Leebf07f762015-04-07 19:05:50 -070052import com.android.phone.settings.VoicemailSettingsActivity;
Andrew Lee8d66d812014-11-24 14:54:02 -080053import com.android.phone.settings.VoicemailNotificationSettingsUtil;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080054import com.android.phone.settings.VoicemailProviderSettingsUtil;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070055
Tyler Gunn9c1071f2014-12-09 10:07:54 -080056import java.util.Iterator;
Andrew Lee99d0ac22014-10-10 13:18:04 -070057import java.util.List;
Tyler Gunn9c1071f2014-12-09 10:07:54 -080058import java.util.Map;
59import java.util.Set;
Andrew Lee99d0ac22014-10-10 13:18:04 -070060
Santos Cordon7d4ddf62013-07-10 11:58:08 -070061/**
62 * NotificationManager-related utility code for the Phone app.
63 *
64 * This is a singleton object which acts as the interface to the
65 * framework's NotificationManager, and is used to display status bar
66 * icons and control other status bar-related behavior.
67 *
68 * @see PhoneGlobals.notificationMgr
69 */
Chiao Cheng312b9c92013-09-16 15:40:53 -070070public class NotificationMgr {
Andrew Leea82b8202014-11-21 16:18:28 -080071 private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070072 private static final boolean DBG =
73 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
74 // Do not check in with VDBG = true, since that may write PII to the system log.
75 private static final boolean VDBG = false;
76
Santos Cordon7d4ddf62013-07-10 11:58:08 -070077 // notification types
Santos Cordonf68db2e2014-07-02 14:40:44 -070078 static final int MMI_NOTIFICATION = 1;
79 static final int NETWORK_SELECTION_NOTIFICATION = 2;
80 static final int VOICEMAIL_NOTIFICATION = 3;
81 static final int CALL_FORWARD_NOTIFICATION = 4;
82 static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
83 static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070084
85 /** The singleton NotificationMgr instance. */
86 private static NotificationMgr sInstance;
87
88 private PhoneGlobals mApp;
89 private Phone mPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090
91 private Context mContext;
92 private NotificationManager mNotificationManager;
93 private StatusBarManager mStatusBarManager;
Andrew Lee99d0ac22014-10-10 13:18:04 -070094 private UserManager mUserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070095 private Toast mToast;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080096 private SubscriptionManager mSubscriptionManager;
Andrew Leed5165b02014-12-05 15:53:58 -080097 private TelecomManager mTelecomManager;
98 private TelephonyManager mTelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070099
100 public StatusBarHelper statusBarHelper;
101
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700102 // used to track the notification of selected network unavailable
103 private boolean mSelectedUnavailableNotify = false;
104
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800105 // used to track whether the message waiting indicator is visible, per subscription id.
106 private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
107
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700108 /**
109 * Private constructor (this is a singleton).
Santos Cordonf68db2e2014-07-02 14:40:44 -0700110 * @see #init(PhoneGlobals)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700111 */
112 private NotificationMgr(PhoneGlobals app) {
113 mApp = app;
114 mContext = app;
115 mNotificationManager =
116 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
117 mStatusBarManager =
118 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700119 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800120 mPhone = app.mCM.getDefaultPhone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700121 statusBarHelper = new StatusBarHelper();
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800122 mSubscriptionManager = SubscriptionManager.from(mContext);
Andrew Leed5165b02014-12-05 15:53:58 -0800123 mTelecomManager = TelecomManager.from(mContext);
124 mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125 }
126
127 /**
128 * Initialize the singleton NotificationMgr instance.
129 *
130 * This is only done once, at startup, from PhoneApp.onCreate().
131 * From then on, the NotificationMgr instance is available via the
132 * PhoneApp's public "notificationMgr" field, which is why there's no
133 * getInstance() method here.
134 */
135 /* package */ static NotificationMgr init(PhoneGlobals app) {
136 synchronized (NotificationMgr.class) {
137 if (sInstance == null) {
138 sInstance = new NotificationMgr(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700139 } else {
140 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
141 }
142 return sInstance;
143 }
144 }
145
146 /**
147 * Helper class that's a wrapper around the framework's
148 * StatusBarManager.disable() API.
149 *
150 * This class is used to control features like:
151 *
152 * - Disabling the status bar "notification windowshade"
153 * while the in-call UI is up
154 *
155 * - Disabling notification alerts (audible or vibrating)
156 * while a phone call is active
157 *
158 * - Disabling navigation via the system bar (the "soft buttons" at
159 * the bottom of the screen on devices with no hard buttons)
160 *
161 * We control these features through a single point of control to make
162 * sure that the various StatusBarManager.disable() calls don't
163 * interfere with each other.
164 */
165 public class StatusBarHelper {
166 // Current desired state of status bar / system bar behavior
167 private boolean mIsNotificationEnabled = true;
168 private boolean mIsExpandedViewEnabled = true;
169 private boolean mIsSystemBarNavigationEnabled = true;
170
Andrew Leed5165b02014-12-05 15:53:58 -0800171 private StatusBarHelper() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700172 }
173
174 /**
175 * Enables or disables auditory / vibrational alerts.
176 *
177 * (We disable these any time a voice call is active, regardless
178 * of whether or not the in-call UI is visible.)
179 */
180 public void enableNotificationAlerts(boolean enable) {
181 if (mIsNotificationEnabled != enable) {
182 mIsNotificationEnabled = enable;
183 updateStatusBar();
184 }
185 }
186
187 /**
188 * Enables or disables the expanded view of the status bar
189 * (i.e. the ability to pull down the "notification windowshade").
190 *
191 * (This feature is disabled by the InCallScreen while the in-call
192 * UI is active.)
193 */
194 public void enableExpandedView(boolean enable) {
195 if (mIsExpandedViewEnabled != enable) {
196 mIsExpandedViewEnabled = enable;
197 updateStatusBar();
198 }
199 }
200
201 /**
202 * Enables or disables the navigation via the system bar (the
203 * "soft buttons" at the bottom of the screen)
204 *
205 * (This feature is disabled while an incoming call is ringing,
206 * because it's easy to accidentally touch the system bar while
207 * pulling the phone out of your pocket.)
208 */
209 public void enableSystemBarNavigation(boolean enable) {
210 if (mIsSystemBarNavigationEnabled != enable) {
211 mIsSystemBarNavigationEnabled = enable;
212 updateStatusBar();
213 }
214 }
215
216 /**
217 * Updates the status bar to reflect the current desired state.
218 */
219 private void updateStatusBar() {
220 int state = StatusBarManager.DISABLE_NONE;
221
222 if (!mIsExpandedViewEnabled) {
223 state |= StatusBarManager.DISABLE_EXPAND;
224 }
225 if (!mIsNotificationEnabled) {
226 state |= StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
227 }
228 if (!mIsSystemBarNavigationEnabled) {
229 // Disable *all* possible navigation via the system bar.
230 state |= StatusBarManager.DISABLE_HOME;
231 state |= StatusBarManager.DISABLE_RECENT;
232 state |= StatusBarManager.DISABLE_BACK;
Christine Chenb685f172013-09-25 18:32:59 -0700233 state |= StatusBarManager.DISABLE_SEARCH;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700234 }
235
236 if (DBG) log("updateStatusBar: state = 0x" + Integer.toHexString(state));
237 mStatusBarManager.disable(state);
238 }
239 }
240
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700241 /** The projection to use when querying the phones table */
242 static final String[] PHONES_PROJECTION = new String[] {
243 PhoneLookup.NUMBER,
244 PhoneLookup.DISPLAY_NAME,
245 PhoneLookup._ID
246 };
247
248 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800249 * Re-creates the message waiting indicator (voicemail) notification if it is showing. Used to
250 * refresh the voicemail intent on the indicator when the user changes it via the voicemail
251 * settings screen. The voicemail notification sound is suppressed.
252 *
253 * @param subId The subscription Id.
254 */
255 /* package */ void refreshMwi(int subId) {
256 // In a single-sim device, subId can be -1 which means "no sub id". In this case we will
257 // reference the single subid stored in the mMwiVisible map.
258 if (subId == SubscriptionInfoHelper.NO_SUB_ID) {
259 if (mMwiVisible.keySet().size() == 1) {
260 Set<Integer> keySet = mMwiVisible.keySet();
261 Iterator<Integer> keyIt = keySet.iterator();
262 if (!keyIt.hasNext()) {
263 return;
264 }
265 subId = keyIt.next();
266 }
267 }
268 if (mMwiVisible.containsKey(subId)) {
269 boolean mwiVisible = mMwiVisible.get(subId);
270 if (mwiVisible) {
271 updateMwi(subId, mwiVisible, false /* enableNotificationSound */);
272 }
273 }
274 }
275
276 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700277 * Updates the message waiting indicator (voicemail) notification.
278 *
279 * @param visible true if there are messages waiting
280 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800281 /* package */ void updateMwi(int subId, boolean visible) {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800282 updateMwi(subId, visible, true /* enableNotificationSound */);
283 }
284
285 /**
286 * Updates the message waiting indicator (voicemail) notification.
287 *
288 * @param subId the subId to update.
289 * @param visible true if there are messages waiting
290 * @param enableNotificationSound {@code true} if the notification sound should be played.
291 */
292 void updateMwi(int subId, boolean visible, boolean enableNotificationSound) {
Andrew Leea82b8202014-11-21 16:18:28 -0800293 if (!PhoneGlobals.sVoiceCapable) {
294 // Do not show the message waiting indicator on devices which are not voice capable.
295 // These events *should* be blocked at the telephony layer for such devices.
296 Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
297 return;
298 }
299
Yorke Lee67a62a22014-12-15 18:46:17 -0800300 Log.i(LOG_TAG, "updateMwi(): subId " + subId + " update to " + visible);
Andrew Leef8ad78f2014-12-15 16:17:29 -0800301 mMwiVisible.put(subId, visible);
302
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700303 if (visible) {
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800304 Phone phone = PhoneGlobals.getPhone(subId);
305 if (phone == null) {
Andrew Leed5165b02014-12-05 15:53:58 -0800306 Log.w(LOG_TAG, "Found null phone for: " + subId);
307 return;
308 }
309
310 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
311 if (subInfo == null) {
312 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800313 return;
314 }
315
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700316 int resId = android.R.drawable.stat_notify_voicemail;
317
318 // This Notification can get a lot fancier once we have more
319 // information about the current voicemail messages.
320 // (For example, the current voicemail system can't tell
321 // us the caller-id or timestamp of a message, or tell us the
322 // message count.)
323
324 // But for now, the UI is ultra-simple: if the MWI indication
325 // is supposed to be visible, just show a single generic
326 // notification.
327
328 String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800329 String vmNumber = phone.getVoiceMailNumber();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700330 if (DBG) log("- got vm number: '" + vmNumber + "'");
331
Andrew Leea82b8202014-11-21 16:18:28 -0800332 // The voicemail number may be null because:
333 // (1) This phone has no voicemail number.
334 // (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
335 // happen when the device first boots if we get a MWI notification when we
336 // register on the network before the SIM has loaded. In this case, the
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800337 // SubscriptionListener in CallNotifier will update this once the SIM is loaded.
338 if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700339 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
Andrew Leea82b8202014-11-21 16:18:28 -0800340 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700341 }
342
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800343 if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
344 int vmCount = phone.getVoiceMessageCount();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700345 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
346 notificationTitle = String.format(titleFormat, vmCount);
347 }
348
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800349 // This pathway only applies to PSTN accounts; only SIMS have subscription ids.
350 PhoneAccountHandle phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
351
352 Intent intent;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700353 String notificationText;
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800354 if (TextUtils.isEmpty(vmNumber)) {
355 notificationText = mContext.getString(
356 R.string.notification_voicemail_no_vm_number);
357
358 // If the voicemail number if unknown, instead of calling voicemail, take the user
359 // to the voicemail settings.
360 notificationText = mContext.getString(
361 R.string.notification_voicemail_no_vm_number);
Andrew Leebf07f762015-04-07 19:05:50 -0700362 intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800363 intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subId);
Andrew Leebf07f762015-04-07 19:05:50 -0700364 intent.setClass(mContext, VoicemailSettingsActivity.class);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700365 } else {
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800366 if (mTelephonyManager.getPhoneCount() > 1) {
367 notificationText = subInfo.getDisplayName().toString();
Andrew Leed5165b02014-12-05 15:53:58 -0800368 } else {
369 notificationText = String.format(
370 mContext.getString(R.string.notification_voicemail_text_format),
371 PhoneNumberUtils.formatNumber(vmNumber));
372 }
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800373 intent = new Intent(
374 Intent.ACTION_CALL, Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "",
375 null));
376 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700377 }
378
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800379 PendingIntent pendingIntent =
380 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800381 Uri ringtoneUri = null;
382
383 if (enableNotificationSound) {
384 ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
385 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700386
Nancy Chenb4a92702014-12-04 15:57:29 -0800387 Resources res = mContext.getResources();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700388 Notification.Builder builder = new Notification.Builder(mContext);
389 builder.setSmallIcon(resId)
390 .setWhen(System.currentTimeMillis())
Andrew Leed5165b02014-12-05 15:53:58 -0800391 .setColor(subInfo.getIconTint())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700392 .setContentTitle(notificationTitle)
393 .setContentText(notificationText)
394 .setContentIntent(pendingIntent)
Yorke Leeacb5f742014-08-19 09:08:42 -0700395 .setSound(ringtoneUri)
Nancy Chenb4a92702014-12-04 15:57:29 -0800396 .setColor(res.getColor(R.color.dialer_theme_color))
397 .setOngoing(res.getBoolean(R.bool.voicemail_notification_persistent));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700398
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800399 if (VoicemailNotificationSettingsUtil.isVibrationEnabled(phone)) {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700400 builder.setDefaults(Notification.DEFAULT_VIBRATE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700401 }
Andrew Lee99d0ac22014-10-10 13:18:04 -0700402
403 final Notification notification = builder.build();
404 List<UserInfo> users = mUserManager.getUsers(true);
405 for (int i = 0; i < users.size(); i++) {
Yorke Lee047b1f92014-10-24 10:22:41 -0700406 final UserInfo user = users.get(i);
407 final UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700408 if (!mUserManager.hasUserRestriction(
Yorke Lee047b1f92014-10-24 10:22:41 -0700409 UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
410 && !user.isManagedProfile()) {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700411 mNotificationManager.notifyAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800412 Integer.toString(subId) /* tag */,
413 VOICEMAIL_NOTIFICATION,
414 notification,
415 userHandle);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700416 }
417 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700418 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700419 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800420 Integer.toString(subId) /* tag */,
421 VOICEMAIL_NOTIFICATION,
422 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700423 }
424 }
425
426 /**
427 * Updates the message call forwarding indicator notification.
428 *
429 * @param visible true if there are messages waiting
430 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800431 /* package */ void updateCfi(int subId, boolean visible) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700432 if (DBG) log("updateCfi(): " + visible);
433 if (visible) {
434 // If Unconditional Call Forwarding (forward all calls) for VOICE
435 // is enabled, just show a notification. We'll default to expanded
436 // view for now, so the there is less confusion about the icon. If
437 // it is deemed too weird to have CF indications as expanded views,
438 // then we'll flip the flag back.
439
440 // TODO: We may want to take a look to see if the notification can
441 // display the target to forward calls to. This will require some
442 // effort though, since there are multiple layers of messages that
443 // will need to propagate that information.
444
Andrew Leed5165b02014-12-05 15:53:58 -0800445 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
446 if (subInfo == null) {
447 Log.w(LOG_TAG, "Found null subscription info for: " + subId);
448 return;
449 }
450
451 String notificationTitle;
452 if (mTelephonyManager.getPhoneCount() > 1) {
453 notificationTitle = subInfo.getDisplayName().toString();
454 } else {
455 notificationTitle = mContext.getString(R.string.labelCF);
456 }
457
Andrew Lee99d0ac22014-10-10 13:18:04 -0700458 Notification.Builder builder = new Notification.Builder(mContext)
459 .setSmallIcon(R.drawable.stat_sys_phone_call_forward)
Andrew Leed5165b02014-12-05 15:53:58 -0800460 .setColor(subInfo.getIconTint())
461 .setContentTitle(notificationTitle)
Andrew Lee99d0ac22014-10-10 13:18:04 -0700462 .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
463 .setShowWhen(false)
464 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700465
Andrew Lee99d0ac22014-10-10 13:18:04 -0700466 Intent intent = new Intent(Intent.ACTION_MAIN);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800467 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700468 intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800469 SubscriptionInfoHelper.addExtrasToIntent(
470 intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
471 PendingIntent contentIntent =
472 PendingIntent.getActivity(mContext, subId /* requestCode */, intent, 0);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700473
474 List<UserInfo> users = mUserManager.getUsers(true);
475 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800476 final UserInfo user = users.get(i);
477 if (user.isManagedProfile()) {
478 continue;
479 }
480 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700481 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800482 mNotificationManager.notifyAsUser(
483 Integer.toString(subId) /* tag */,
484 CALL_FORWARD_NOTIFICATION,
485 builder.build(),
486 userHandle);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700487 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700488 } else {
Andrew Lee99d0ac22014-10-10 13:18:04 -0700489 mNotificationManager.cancelAsUser(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800490 Integer.toString(subId) /* tag */,
491 CALL_FORWARD_NOTIFICATION,
492 UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700493 }
494 }
495
496 /**
497 * Shows the "data disconnected due to roaming" notification, which
498 * appears when you lose data connectivity because you're roaming and
499 * you have the "data roaming" feature turned off.
500 */
501 /* package */ void showDataDisconnectedRoaming() {
502 if (DBG) log("showDataDisconnectedRoaming()...");
503
504 // "Mobile network settings" screen / dialog
505 Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
Andrew Lee99d0ac22014-10-10 13:18:04 -0700506 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700507
508 final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
509
Andrew Lee99d0ac22014-10-10 13:18:04 -0700510 final Notification.Builder builder = new Notification.Builder(mContext)
511 .setSmallIcon(android.R.drawable.stat_sys_warning)
512 .setContentTitle(mContext.getText(R.string.roaming))
513 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
514 .setContentText(contentText);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700515
Andrew Lee99d0ac22014-10-10 13:18:04 -0700516 List<UserInfo> users = mUserManager.getUsers(true);
517 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800518 final UserInfo user = users.get(i);
519 if (user.isManagedProfile()) {
520 continue;
521 }
522 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700523 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
524 final Notification notif =
525 new Notification.BigTextStyle(builder).bigText(contentText).build();
526 mNotificationManager.notifyAsUser(
527 null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle);
528 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700529 }
530
531 /**
532 * Turns off the "data disconnected due to roaming" notification.
533 */
534 /* package */ void hideDataDisconnectedRoaming() {
535 if (DBG) log("hideDataDisconnectedRoaming()...");
536 mNotificationManager.cancel(DATA_DISCONNECTED_ROAMING_NOTIFICATION);
537 }
538
539 /**
540 * Display the network selection "no service" notification
541 * @param operator is the numeric operator number
542 */
543 private void showNetworkSelection(String operator) {
544 if (DBG) log("showNetworkSelection(" + operator + ")...");
545
Andrew Lee99d0ac22014-10-10 13:18:04 -0700546 Notification.Builder builder = new Notification.Builder(mContext)
547 .setSmallIcon(android.R.drawable.stat_sys_warning)
548 .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
549 .setContentText(
550 mContext.getString(R.string.notification_network_selection_text, operator))
551 .setShowWhen(false)
552 .setOngoing(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700553
554 // create the target network operators settings intent
555 Intent intent = new Intent(Intent.ACTION_MAIN);
556 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
557 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
558 // Use NetworkSetting to handle the selection intent
559 intent.setComponent(new ComponentName("com.android.phone",
560 "com.android.phone.NetworkSetting"));
Andrew Lee99d0ac22014-10-10 13:18:04 -0700561 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700562
Andrew Lee99d0ac22014-10-10 13:18:04 -0700563 List<UserInfo> users = mUserManager.getUsers(true);
564 for (int i = 0; i < users.size(); i++) {
Yorke Lee3faa5942014-11-05 16:50:04 -0800565 final UserInfo user = users.get(i);
566 if (user.isManagedProfile()) {
567 continue;
568 }
569 UserHandle userHandle = user.getUserHandle();
Andrew Lee99d0ac22014-10-10 13:18:04 -0700570 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
571 mNotificationManager.notifyAsUser(
572 null /* tag */,
573 SELECTED_OPERATOR_FAIL_NOTIFICATION,
574 builder.build(),
575 userHandle);
576 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700577 }
578
579 /**
580 * Turn off the network selection "no service" notification
581 */
582 private void cancelNetworkSelection() {
583 if (DBG) log("cancelNetworkSelection()...");
Andrew Lee99d0ac22014-10-10 13:18:04 -0700584 mNotificationManager.cancelAsUser(
585 null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700586 }
587
588 /**
589 * Update notification about no service of user selected operator
590 *
591 * @param serviceState Phone service state
592 */
593 void updateNetworkSelection(int serviceState) {
594 if (TelephonyCapabilities.supportsNetworkSelection(mPhone)) {
Amit Mahajana60be872015-01-15 16:05:08 -0800595 int subId = mPhone.getSubId();
596 if (SubscriptionManager.isValidSubscriptionId(subId)) {
597 // get the shared preference of network_selection.
598 // empty is auto mode, otherwise it is the operator alpha name
599 // in case there is no operator name, check the operator numeric
600 SharedPreferences sp =
601 PreferenceManager.getDefaultSharedPreferences(mContext);
602 String networkSelection =
603 sp.getString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId, "");
604 if (TextUtils.isEmpty(networkSelection)) {
605 networkSelection =
606 sp.getString(PhoneBase.NETWORK_SELECTION_KEY + subId, "");
607 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700608
Amit Mahajana60be872015-01-15 16:05:08 -0800609 if (DBG) log("updateNetworkSelection()..." + "state = " +
610 serviceState + " new network " + networkSelection);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700611
Amit Mahajana60be872015-01-15 16:05:08 -0800612 if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
613 && !TextUtils.isEmpty(networkSelection)) {
614 if (!mSelectedUnavailableNotify) {
615 showNetworkSelection(networkSelection);
616 mSelectedUnavailableNotify = true;
617 }
618 } else {
619 if (mSelectedUnavailableNotify) {
620 cancelNetworkSelection();
621 mSelectedUnavailableNotify = false;
622 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700623 }
624 } else {
Amit Mahajana60be872015-01-15 16:05:08 -0800625 if (DBG) log("updateNetworkSelection()..." + "state = " +
626 serviceState + " not updating network due to invalid subId " + subId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700627 }
628 }
629 }
630
631 /* package */ void postTransientNotification(int notifyId, CharSequence msg) {
632 if (mToast != null) {
633 mToast.cancel();
634 }
635
636 mToast = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
637 mToast.show();
638 }
639
640 private void log(String msg) {
641 Log.d(LOG_TAG, msg);
642 }
643}