/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.phone;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.SystemProperties;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.gsm.TDPhone;
import static com.android.internal.telephony.MsmsConstants.SUBSCRIPTION_KEY;
/**
* NotificationManager-related utility code for the Phone app.
*
* This is a singleton object which acts as the interface to the
* framework's NotificationManager, and is used to display status bar
* icons and control other status bar-related behavior.
*
* @see PhoneApp.notificationMgr
*/
public class MsmsNotificationMgr extends NotificationMgr {
private static final String LOG_TAG = "MsmsNotificationMgr";
private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 1)
&& (SystemProperties.getInt("ro.debuggable", 0) == 1);
static final int VOICEMAIL_NOTIFICATION_SUB2 = 20;
static final int CALL_FORWARD_NOTIFICATION_SUB2 = 21;
static final int VIDEO_CALL_FORWARD_NOTIFICATION_SUB2 = 22;
/**
* Private constructor (this is a singleton).
* @see init()
*/
private MsmsNotificationMgr(PhoneApp app) {
super(app);
}
/**
* Initialize the singleton NotificationMgr instance.
*
* This is only done once, at startup, from PhoneApp.onCreate().
* From then on, the NotificationMgr instance is available via the
* PhoneApp's public "notificationMgr" field, which is why there's no
* getInstance() method here.
*/
/* package */ static MsmsNotificationMgr init(PhoneApp app) {
synchronized (MsmsNotificationMgr.class) {
if (sInstance == null) {
sInstance = new MsmsNotificationMgr(app);
// Update the notifications that need to be touched at startup.
sInstance.updateNotificationsAtStartup();
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
return (MsmsNotificationMgr) sInstance;
}
}
/**
* Updates the message waiting indicator (voicemail) notification.
*
* @param visible true if there are messages waiting
*/
/* package */
void updateMwi(boolean visible, Phone phone) {
if (DBG) log("updateMwi(): " + visible);
if (phone == null) {
if (DBG)
log("updateMwi(): phone is null.");
return;
}
int subscription = phone.getPhoneId();
if (visible) {
int resId = android.R.drawable.stat_notify_voicemail;
int[] iconId = {android.R.drawable.stat_notify_voicemail,
android.R.drawable.stat_notify_voicemail};
// This Notification can get a lot fancier once we have more
// information about the current voicemail messages.
// (For example, the current voicemail system can't tell
// us the caller-id or timestamp of a message, or tell us the
// message count.)
// But for now, the UI is ultra-simple: if the MWI indication
// is supposed to be visible, just show a single generic
// notification.
String notificationTitle = mContext.getString(R.string.notification_voicemail_title);
String vmNumber = phone.getVoiceMailNumber();
if (DBG) log("- got vm number: '" + vmNumber + "'"
+ " on Subscription: " + subscription);
resId = iconId[subscription];
// Watch out: vmNumber may be null, for two possible reasons:
//
// (1) This phone really has no voicemail number
//
// (2) This phone *does* have a voicemail number, but
// the SIM isn't ready yet.
//
// Case (2) *does* happen in practice if you have voicemail
// messages when the device first boots: we get an MWI
// notification as soon as we register on the network, but the
// SIM hasn't finished loading yet.
//
// So handle case (2) by retrying the lookup after a short
// delay.
if ((vmNumber == null) && !phone.getIccRecordsLoaded()) {
if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
// TODO: rather than retrying after an arbitrary delay, it
// would be cleaner to instead just wait for a
// SIM_RECORDS_LOADED notification.
// (Unfortunately right now there's no convenient way to
// get that notification in phone app code. We'd first
// want to add a call like registerForSimRecordsLoaded()
// to Phone.java and GSMPhone.java, and *then* we could
// listen for that in the CallNotifier class.)
// Limit the number of retries (in case the SIM is broken
// or missing and can *never* load successfully.)
if (mVmNumberRetriesRemaining-- > 0) {
if (DBG) log(" - Retrying in " + VM_NUMBER_RETRY_DELAY_MILLIS + " msec...");
((MsmsCallNotifier)PhoneApp.getInstance().notifier).sendMwiChangedDelayed(
VM_NUMBER_RETRY_DELAY_MILLIS, phone);
return;
} else {
Log.w(LOG_TAG, "NotificationMgr.updateMwi: getVoiceMailNumber() failed after "
+ MAX_VM_NUMBER_RETRIES + " retries; giving up.");
// ...and continue with vmNumber==null, just as if the
// phone had no VM number set up in the first place.
}
}
if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
int vmCount = phone.getVoiceMessageCount();
String titleFormat = mContext.getString(
R.string.notification_voicemail_title_count);
notificationTitle = String.format(titleFormat, vmCount);
}
String notificationText;
if (TextUtils.isEmpty(vmNumber)) {
notificationText = mContext.getString(
R.string.notification_voicemail_no_vm_number);
} else {
notificationText = String.format(
mContext.getString(R.string.notification_voicemail_text_format),
PhoneNumberUtils.formatNumber(vmNumber));
}
Intent intent = new Intent(Intent.ACTION_CALL,
Uri.fromParts("voicemail", "", null));
intent.putExtra(SUBSCRIPTION_KEY, subscription);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
Notification notification = new Notification(
resId, // icon
null, // tickerText
System.currentTimeMillis() // Show the time the MWI notification came in,
// since we don't know the actual time of the
// most recent voicemail message
);
notification.setLatestEventInfo(
mContext, // context
notificationTitle, // contentTitle
notificationText, // contentText
pendingIntent // contentIntent
);
notification.defaults |= Notification.DEFAULT_SOUND;
notification.flags |= Notification.FLAG_NO_CLEAR;
configureLedNotification(notification);
mNotificationManager.notify(VOICEMAIL_NOTIFICATION, notification);
} else {
mNotificationManager.cancel(VOICEMAIL_NOTIFICATION);
}
}
/**
* Updates the message call forwarding indicator notification.
*
* @param visible true if there are messages waiting
*/
/* package */ void updateCfi(boolean visible, int subscription) {
updateCfi(visible, CommandsInterface.SERVICE_CLASS_VOICE, subscription);
}
/**
* updateCfi by serviceClass
* @param visible
* @param serviceClass
* @param subscription
*/
void updateCfi(boolean visible, int serviceClass, int subscription) {
log("updateCfi(): " + visible + ";serviceClass:" + serviceClass + ";sub = " + subscription);
int notificationId = (subscription == 0) ? CALL_FORWARD_NOTIFICATION :
CALL_FORWARD_NOTIFICATION_SUB2;
int[] callfwdIcon = {R.drawable.stat_sys_phone_call_forward1, R.drawable.stat_sys_phone_call_forward2};
int iconId = callfwdIcon[subscription];
String intentClassName = "com.android.phone.GsmUmtsCallForwardOptions";
int simStringId = (subscription == 0) ? R.string.sim1 : R.string.sim2;
String expandedTitle = mContext.getString(simStringId) + mContext.getString(R.string.labelCF);
String expandedText = mContext.getString(simStringId) + mContext.getString(R.string.sum_cfu_enabled_indicator);
//for video call forwarding
if (TDPhone.SERVICE_CLASS_VIDEO == serviceClass) {
notificationId = (subscription == 0) ? VIDEO_CALL_FORWARD_NOTIFICATION
: VIDEO_CALL_FORWARD_NOTIFICATION_SUB2;
int[] videoCallFwdIcon = {
R.drawable.stat_sys_phone_video_call_forward1,
R.drawable.stat_sys_phone_video_call_forward2};
iconId = videoCallFwdIcon[subscription];
intentClassName = "com.android.phone.VideoPhoneCallForwardOptions";
expandedTitle = mContext.getString(simStringId) + mContext.getString(R.string.videophone_callforward_setting_title);
expandedText = mContext.getString(simStringId) + mContext.getString(R.string.videophone_setting) + mContext.getString(R.string.sum_cfu_enabled_indicator);
}
if (visible) {
// If Unconditional Call Forwarding (forward all calls) for VOICE
// is enabled, just show a notification. We'll default to expanded
// view for now, so the there is less confusion about the icon. If
// it is deemed too weird to have CF indications as expanded views,
// then we'll flip the flag back.
// TODO: We may want to take a look to see if the notification can
// display the target to forward calls to. This will require some
// effort though, since there are multiple layers of messages that
// will need to propagate that information.
Notification notification;
final boolean showExpandedNotification = true;
if (showExpandedNotification) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// intent.setClassName("com.android.phone", intentClassName); //don't use this
intent.setComponent(new ComponentName("com.android.phone", intentClassName));
intent.putExtra(CallSettingOptions.SUB_ID, subscription);
notification = new Notification(
iconId, // icon
null, // tickerText
0); // The "timestamp" of this notification is meaningless;
// we only care about whether CFI is currently on or not.
notification.setLatestEventInfo(
mContext, // context
expandedTitle, // expandedTitle
expandedText, // expandedText
PendingIntent.getActivity(mContext, subscription, intent, PendingIntent.FLAG_UPDATE_CURRENT)); // contentIntent
} else {
notification = new Notification(
iconId, // icon
null, // tickerText
System.currentTimeMillis() // when
);
}
notification.flags |= Notification.FLAG_ONGOING_EVENT; // also implies FLAG_NO_CLEAR
mNotificationManager.notify(
notificationId,
notification);
} else {
mNotificationManager.cancel(notificationId);
}
}
private void log(String msg) {
Log.d(LOG_TAG, msg);
}
}