228 lines
8.8 KiB
Java
228 lines
8.8 KiB
Java
|
/*
|
||
|
* Copyright (C) 2010 The Android Open Source Project
|
||
|
*
|
||
|
* 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.email;
|
||
|
|
||
|
import com.android.email.activity.ContactStatusLoader;
|
||
|
import com.android.email.activity.Welcome;
|
||
|
import com.android.email.mail.Address;
|
||
|
import com.android.email.provider.EmailContent;
|
||
|
import com.android.email.provider.EmailContent.Account;
|
||
|
import com.android.email.provider.EmailContent.Message;
|
||
|
|
||
|
import android.app.Notification;
|
||
|
import android.app.NotificationManager;
|
||
|
import android.app.PendingIntent;
|
||
|
import android.content.Context;
|
||
|
import android.graphics.Bitmap;
|
||
|
import android.graphics.BitmapFactory;
|
||
|
import android.media.AudioManager;
|
||
|
import android.net.Uri;
|
||
|
import android.text.TextUtils;
|
||
|
|
||
|
/**
|
||
|
* Class that manages notifications.
|
||
|
*
|
||
|
* TODO Gather all notification related code here
|
||
|
*/
|
||
|
public class NotificationController {
|
||
|
public static final int NOTIFICATION_ID_SECURITY_NEEDED = 1;
|
||
|
public static final int NOTIFICATION_ID_EXCHANGE_CALENDAR_ADDED = 2;
|
||
|
public static final int NOTIFICATION_ID_WARNING = 3;
|
||
|
private static final int NOTIFICATION_ID_NEW_MESSAGES_BASE = 10;
|
||
|
|
||
|
private static NotificationController sInstance;
|
||
|
private final Context mContext;
|
||
|
private final NotificationManager mNotificationManager;
|
||
|
private final AudioManager mAudioManager;
|
||
|
|
||
|
/** Constructor */
|
||
|
private NotificationController(Context context) {
|
||
|
mContext = context.getApplicationContext();
|
||
|
mNotificationManager = (NotificationManager) context.getSystemService(
|
||
|
Context.NOTIFICATION_SERVICE);
|
||
|
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||
|
}
|
||
|
|
||
|
/** Singleton access */
|
||
|
public static synchronized NotificationController getInstance(Context context) {
|
||
|
if (sInstance == null) {
|
||
|
sInstance = new NotificationController(context);
|
||
|
}
|
||
|
return sInstance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the "new message" notification ID for an account. It just assumes
|
||
|
* accountID won't be too huge. Any other smarter/cleaner way?
|
||
|
*/
|
||
|
private int getNewMessageNotificationId(long accountId) {
|
||
|
return (int) (NOTIFICATION_ID_NEW_MESSAGES_BASE + accountId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dismiss new message notification
|
||
|
*
|
||
|
* @param accountId ID of the target account, or -1 for all accounts.
|
||
|
*/
|
||
|
public void cancelNewMessageNotification(long accountId) {
|
||
|
if (accountId == -1) {
|
||
|
new Utility.ForEachAccount(mContext) {
|
||
|
@Override
|
||
|
protected void performAction(long accountId) {
|
||
|
cancelNewMessageNotification(accountId);
|
||
|
}
|
||
|
}.execute();
|
||
|
} else {
|
||
|
mNotificationManager.cancel(getNewMessageNotificationId(accountId));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Show (or update) the "new message" notification.
|
||
|
*/
|
||
|
public void showNewMessageNotification(final long accountId, final int unseenMessageCount,
|
||
|
final int justFetchedCount) {
|
||
|
Utility.runAsync(new Runnable() {
|
||
|
@Override
|
||
|
public void run() {
|
||
|
Notification n = createNewMessageNotification(accountId, unseenMessageCount,
|
||
|
justFetchedCount);
|
||
|
if (n == null) {
|
||
|
return;
|
||
|
}
|
||
|
mNotificationManager.notify(getNewMessageNotificationId(accountId), n);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return The sender's photo, if available, or null.
|
||
|
*
|
||
|
* Don't call it on the UI thread.
|
||
|
*/
|
||
|
private Bitmap getSenderPhoto(Message message) {
|
||
|
Address sender = Address.unpackFirst(message.mFrom);
|
||
|
if (sender == null) {
|
||
|
return null;
|
||
|
}
|
||
|
String email = sender.getAddress();
|
||
|
if (TextUtils.isEmpty(email)) {
|
||
|
return null;
|
||
|
}
|
||
|
return ContactStatusLoader.load(mContext, email).mPhoto;
|
||
|
}
|
||
|
|
||
|
private Bitmap[] getNotificationBitmaps(Bitmap senderPhoto) {
|
||
|
// TODO Should we cache these objects? (bitmaps and arrays)
|
||
|
// They don't have to be on this process's memory once we post a notification request to
|
||
|
// the system, and decodeResource() seems to be reasonably fast. We don't want them to
|
||
|
// take up memory when not necessary.
|
||
|
Bitmap appIcon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.icon);
|
||
|
if (senderPhoto == null) {
|
||
|
return new Bitmap[] {appIcon};
|
||
|
} else {
|
||
|
return new Bitmap[] {senderPhoto, appIcon};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a notification
|
||
|
*
|
||
|
* Don't call it on the UI thread.
|
||
|
*
|
||
|
* TODO Test it when the UI is settled.
|
||
|
*/
|
||
|
private Notification createNewMessageNotification(long accountId, int unseenMessageCount,
|
||
|
int justFetchedCount) {
|
||
|
final Account account = Account.restoreAccountWithId(mContext, accountId);
|
||
|
if (account == null) {
|
||
|
return null;
|
||
|
}
|
||
|
// Get the latest message
|
||
|
final Message message = Message.getLatestMessage(mContext, accountId);
|
||
|
if (message == null) {
|
||
|
return null; // no message found???
|
||
|
}
|
||
|
|
||
|
final String senderName = Address.toFriendly(Address.unpack(message.mFrom));
|
||
|
final String subject = message.mSubject;
|
||
|
final Bitmap senderPhoto = getSenderPhoto(message);
|
||
|
|
||
|
// Intent to open inbox
|
||
|
PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
|
||
|
Welcome.createOpenAccountInboxIntent(mContext, accountId),
|
||
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||
|
|
||
|
final String notificationTitle;
|
||
|
if (justFetchedCount == 1) {
|
||
|
notificationTitle = senderName;
|
||
|
} else {
|
||
|
notificationTitle = mContext.getResources().getQuantityString(
|
||
|
R.plurals.notification_sender_name_multi_messages, justFetchedCount - 1,
|
||
|
senderName, justFetchedCount - 1);
|
||
|
}
|
||
|
final String content = subject;
|
||
|
final String numNewMessages;
|
||
|
final int numAccounts = EmailContent.count(mContext, Account.CONTENT_URI);
|
||
|
if (numAccounts == 1) {
|
||
|
numNewMessages = mContext.getResources().getQuantityString(
|
||
|
R.plurals.notification_num_new_messages_single_account, unseenMessageCount,
|
||
|
unseenMessageCount, account.mDisplayName);
|
||
|
} else {
|
||
|
numNewMessages = mContext.getResources().getQuantityString(
|
||
|
R.plurals.notification_num_new_messages_multi_account, unseenMessageCount,
|
||
|
unseenMessageCount, account.mDisplayName);
|
||
|
}
|
||
|
|
||
|
Notification notification = new Notification(R.drawable.stat_notify_email_generic,
|
||
|
mContext.getString(R.string.notification_new_title), System.currentTimeMillis());
|
||
|
notification.setLatestEventInfo(mContext, notificationTitle, subject, contentIntent);
|
||
|
|
||
|
notification.tickerTitle = notificationTitle;
|
||
|
// STOPSHIPO numNewMessages should be the 3rd line on expanded notification. But it's not
|
||
|
// clear how to do that yet.
|
||
|
// For now we just append it to subject.
|
||
|
notification.tickerSubtitle = subject + " " + numNewMessages;
|
||
|
notification.tickerIcons = getNotificationBitmaps(senderPhoto);
|
||
|
|
||
|
setupNotificationSoundAndVibrationFromAccount(notification, account);
|
||
|
return notification;
|
||
|
}
|
||
|
|
||
|
private boolean isRingerModeSilent() {
|
||
|
return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
|
||
|
}
|
||
|
|
||
|
private void setupNotificationSoundAndVibrationFromAccount(Notification notification,
|
||
|
Account account) {
|
||
|
final int flags = account.mFlags;
|
||
|
final String ringtoneUri = account.mRingtoneUri;
|
||
|
final boolean vibrate = (flags & Account.FLAGS_VIBRATE_ALWAYS) != 0;
|
||
|
final boolean vibrateWhenSilent = (flags & Account.FLAGS_VIBRATE_WHEN_SILENT) != 0;
|
||
|
|
||
|
notification.sound = (ringtoneUri == null) ? null : Uri.parse(ringtoneUri);
|
||
|
|
||
|
if (vibrate || (vibrateWhenSilent && isRingerModeSilent())) {
|
||
|
notification.defaults |= Notification.DEFAULT_VIBRATE;
|
||
|
}
|
||
|
|
||
|
// This code is identical to that used by Gmail and GTalk for notifications
|
||
|
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
|
||
|
notification.defaults |= Notification.DEFAULT_LIGHTS;
|
||
|
}
|
||
|
}
|