1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-01-08 04:08:15 -05:00

#619 "Add android wear support"

added EXTRA_MESSAGE_ID to simplify closing the correct notification and make things more robust.
This commit is contained in:
Marcus Wolschon 2015-05-12 22:18:53 +02:00
parent 764f380d45
commit 509573e014
3 changed files with 122 additions and 40 deletions

View File

@ -22,16 +22,38 @@ import com.fsck.k9.service.NotificationActionService;
public class NotificationDeleteConfirmation extends Activity {
private final static String EXTRA_ACCOUNT = "account";
private final static String EXTRA_MESSAGE_LIST = "messages";
private final static String EXTRA_NOTIFICATION_ID = NotificationActionService.EXTRA_NOTIFICATION_ID;
private final static int DIALOG_CONFIRM = 1;
/**
* The account to delete the messages on.
*/
private Account mAccount;
/**
* The messages to delete.
*/
private ArrayList<MessageReference> mMessageRefs;
/**
* ID of the notification that triggered this Activity.
* To make sure we close the correte notification afterwards because
* there may be multiple of them due to Android Wear stacked notifications.
*/
private int mNotificationID;
public static PendingIntent getIntent(Context context, final Account account, final Serializable refs) {
/**
*
* @param context context to create the PendingIntent.
* @param account The account to delete the messages on.
* @param refs The messages to delete.
* @param notificationID ID of the notification that triggered this Activity.
* @return PendingIntent that either deletes directly or shows a confirm-dialog on the phone (not on the wear device) first.
*/
public static PendingIntent getIntent(final Context context, final Account account, final Serializable refs, final int notificationID) {
Intent i = new Intent(context, NotificationDeleteConfirmation.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE_LIST, refs);
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
@ -49,11 +71,12 @@ public class NotificationDeleteConfirmation extends Activity {
mAccount = preferences.getAccount(intent.getStringExtra(EXTRA_ACCOUNT));
mMessageRefs = intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
mNotificationID = intent.getIntExtra(EXTRA_NOTIFICATION_ID, mAccount.getAccountNumber());
if (mAccount == null || mMessageRefs == null || mMessageRefs.isEmpty()) {
finish();
} else if (!K9.confirmDeleteFromNotification()) {
triggerDelete();
triggerDelete(mNotificationID);
finish();
} else {
showDialog(DIALOG_CONFIRM);
@ -71,7 +94,7 @@ public class NotificationDeleteConfirmation extends Activity {
new Runnable() {
@Override
public void run() {
triggerDelete();
triggerDelete(mNotificationID);
finish();
}
},
@ -100,8 +123,8 @@ public class NotificationDeleteConfirmation extends Activity {
super.onPrepareDialog(id, d);
}
private void triggerDelete() {
Intent i = NotificationActionService.getDeleteAllMessagesIntent(this, mAccount, mMessageRefs);
private void triggerDelete(final int notificationID) {
Intent i = NotificationActionService.getDeleteAllMessagesIntent(this, mAccount, mMessageRefs, notificationID);
startService(i);
}
}

View File

@ -4826,19 +4826,19 @@ public class MessagingController implements Runnable {
* Build the specific notification actions for a single message on Android Wear.
* @param totalMsgCount if this is a stacked notification, how many other messages are there?
*/
private void addWearActions(final NotificationCompat.Builder builder, final int totalMsgCount, final Account account, final Message message) {
private void addWearActions(final NotificationCompat.Builder builder, final int totalMsgCount, final Account account, final Message message, final int notificationID) {
ArrayList<MessageReference> subAllRefs = new ArrayList<MessageReference>();
subAllRefs.add(new MessageReference(account.getUuid(), message.getFolder().getName(), message.getUid(), message.getFlags().size()==0?null:message.getFlags().iterator().next()));
LinkedList<Message> msgList = new LinkedList<Message>();
msgList.add(message);
addWearActions(builder, totalMsgCount, 1, account, subAllRefs, msgList);
addWearActions(builder, totalMsgCount, 1, account, subAllRefs, msgList, notificationID);
}
/**
* Build the specific notification actions for a single or multiple message on Android Wear.
* @param totalMsgCount total message count (may be different from msgCount if this is a stacked notification)
* @param msgCount message count to be handled in this (stacked or summary) notification
*/
private void addWearActions(final NotificationCompat.Builder builder, final int totalMsgCount, final int msgCount, final Account account, final ArrayList<MessageReference> allRefs, final List<? extends Message> messages) {
private void addWearActions(final NotificationCompat.Builder builder, final int totalMsgCount, final int msgCount, final Account account, final ArrayList<MessageReference> allRefs, final List<? extends Message> messages, final int notificationID) {
// we need a new wearableExtender for each notification
final NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender();
@ -4858,7 +4858,7 @@ public class MessagingController implements Runnable {
new NotificationCompat.Action.Builder(
R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_delete),
NotificationDeleteConfirmation.getIntent(context, account, allRefs))
NotificationDeleteConfirmation.getIntent(context, account, allRefs, notificationID))
.build();
builder.extend(wearableExtender.addAction(wearActionDelete));
}
@ -4870,7 +4870,7 @@ public class MessagingController implements Runnable {
new NotificationCompat.Action.Builder(
R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_archive),
NotificationActionService.getArchiveAllMessagesIntent(context, account, allRefs, totalMsgCount > msgCount))
NotificationActionService.getArchiveAllMessagesIntent(context, account, allRefs, totalMsgCount > msgCount, notificationID))
.build();
builder.extend(wearableExtender.addAction(wearActionArchive));
}
@ -4881,7 +4881,7 @@ public class MessagingController implements Runnable {
new NotificationCompat.Action.Builder(
R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_spam),
NotificationActionService.getSpamAllMessagesIntent(context, account, allRefs, totalMsgCount > msgCount))
NotificationActionService.getSpamAllMessagesIntent(context, account, allRefs, totalMsgCount > msgCount, notificationID))
.build();
builder.extend(wearableExtender.addAction(wearActionSpam));
}
@ -4943,7 +4943,6 @@ public class MessagingController implements Runnable {
// Stacked notifications for Android Wear
// https://developer.android.com/training/wearables/notifications/stacks.html
// TODO: Bug! Stacked notification are shown on phone too, together with summary
// multiple messages pending, show inbox style
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(builder);
@ -4962,12 +4961,20 @@ public class MessagingController implements Runnable {
subBuilder.setGroup(NOTIFICATION_GROUP_KEY); // same group as summary
subBuilder.setAutoCancel(true); // summary closes all, stacked only itself
nID = 1000 + nID;
// reuse existing notification IDs if some of the stacked messages
// are already shown on the wear device.
Integer realnID = data.getStackedChildNotification(m);
if (realnID == null) {
realnID = nID;
}
// set content
setNotificationContent(context, m, getMessageSender(context, account, m), getMessageSubject(context, m), subBuilder, accountDescr);
// set actions
addWearActions(subBuilder, newMessages, account, m);
addWearActions(subBuilder, newMessages, account, m, realnID);
if (m.isSet(Flag.FLAGGED)) {
subBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
}
@ -4976,11 +4983,6 @@ public class MessagingController implements Runnable {
// and depend on quiet time and user settings
// this must be done before the summary notification
nID = 1000 + nID;
Integer realnID = data.getStackedChildNotification(m);
if (realnID == null) {
realnID = nID;
}
notifMgr.notify(realnID, subBuilder.build());
data.addStackedChildNotification(m, realnID);
}
@ -5014,12 +5016,12 @@ public class MessagingController implements Runnable {
? R.drawable.ic_action_single_message_options_dark_vector
: R.drawable.ic_action_single_message_options_dark,
context.getString(R.string.notification_action_reply),
NotificationActionService.getReplyIntent(context, account, message.makeMessageReference()));
NotificationActionService.getReplyIntent(context, account, message.makeMessageReference(), account.getAccountNumber()));
// add /different) actions to show on connected Android Wear devices
// do not add these to the a summary notification or they will affect all stacked
// notifications
addWearActions(builder, newMessages, newMessages, account, allRefs, data.messages);
addWearActions(builder, newMessages, newMessages, account, allRefs, data.messages, account.getAccountNumber());
}
// Mark Read on phone
@ -5028,7 +5030,7 @@ public class MessagingController implements Runnable {
? R.drawable.ic_action_mark_as_read_dark_vector
: R.drawable.ic_action_mark_as_read_dark,
context.getString(R.string.notification_action_mark_as_read),
NotificationActionService.getReadAllMessagesIntent(context, account, allRefs));
NotificationActionService.getReadAllMessagesIntent(context, account, allRefs, account.getAccountNumber()));
NotificationQuickDelete deleteOption = K9.getNotificationQuickDeleteBehaviour();
boolean showDeleteAction = deleteOption == NotificationQuickDelete.ALWAYS ||
@ -5045,7 +5047,7 @@ public class MessagingController implements Runnable {
? R.drawable.ic_action_delete_dark_vector
: R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_delete),
NotificationDeleteConfirmation.getIntent(context, account, allRefs));
NotificationDeleteConfirmation.getIntent(context, account, allRefs, account.getAccountNumber()));
}
} else { // no extended notifications supported
@ -5067,7 +5069,7 @@ public class MessagingController implements Runnable {
builder.setContentIntent(stack.getPendingIntent(
account.getAccountNumber(),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT));
builder.setDeleteIntent(NotificationActionService.getAcknowledgeIntent(context, account));
builder.setDeleteIntent(NotificationActionService.getAcknowledgeIntent(context, account, account.getAccountNumber()));
// Only ring or vibrate if we have not done so already on this account and fetch
boolean ringAndVibrate = false;
@ -5343,15 +5345,15 @@ public class MessagingController implements Runnable {
notificationManager.cancel(-1000 - account.getAccountNumber());
// cancel stacked notifications on Android Wear that share this as a summary notification
NotificationData data = notificationData.get(account.getAccountNumber());
if (data != null) {
Collection<Integer> stackedChildNotifications = data.getStackedChildNotifications();
if (stackedChildNotifications != null) {
for (Integer stackedNotificationId : stackedChildNotifications) {
notificationManager.cancel(stackedNotificationId);
}
}
}
//NotificationData data = notificationData.get(account.getAccountNumber());
//if (data != null) {
// Collection<Integer> stackedChildNotifications = data.getStackedChildNotifications();
// if (stackedChildNotifications != null) {
// for (Integer stackedNotificationId : stackedChildNotifications) {
// notificationManager.cancel(stackedNotificationId);
// }
// }
//}
notificationData.remove(account.getAccountNumber());
}

View File

@ -15,6 +15,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mailstore.LocalMessage;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@ -36,8 +37,23 @@ public class NotificationActionService extends CoreService {
private final static String EXTRA_MESSAGE = "message";
private final static String EXTRA_MESSAGE_LIST = "messages";
private final static String EXTRA_DONTCANCEL = "dontcancel";
/**
* ID of the notification that triggered an intent.
* Used to cancel exactly that one notification because due to
* Android Wear there may be multiple notifications per account.
*/
public final static String EXTRA_NOTIFICATION_ID = "notificationid";
public static PendingIntent getReplyIntent(Context context, final Account account, final MessageReference ref) {
/**
*
* @param context
* @param account
* @param ref
* @param notificationID ID of the notification, this intent is for.
* @see #EXTRA_NOTIFICATION_ID
* @return
*/
public static PendingIntent getReplyIntent(Context context, final Account account, final MessageReference ref, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE, ref);
@ -46,27 +62,56 @@ public class NotificationActionService extends CoreService {
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getReadAllMessagesIntent(Context context, final Account account, final Serializable refs) {
/**
*
* @param context
* @param account
* @param refs
* @param notificationID ID of the notification, this intent is for.
* @return
* @see #EXTRA_NOTIFICATION_ID
*/
public static PendingIntent getReadAllMessagesIntent(Context context, final Account account, final Serializable refs, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE_LIST, refs);
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setAction(READ_ALL_ACTION);
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getAcknowledgeIntent(Context context, final Account account) {
/**
*
* @param context
* @param account
* @param notificationID ID of the notification, this intent is for.
* @return
* @see #EXTRA_NOTIFICATION_ID
*/
public static PendingIntent getAcknowledgeIntent(Context context, final Account account, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setAction(ACKNOWLEDGE_ACTION);
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static Intent getDeleteAllMessagesIntent(Context context, final Account account, final Serializable refs) {
/**
*
* @param context
* @param account
* @param refs
* @param notificationID ID of the notification, this intent is for.
* @return
* @see #EXTRA_NOTIFICATION_ID
*/
public static Intent getDeleteAllMessagesIntent(Context context, final Account account, final Serializable refs, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE_LIST, refs);
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setAction(DELETE_ALL_ACTION);
return i;
@ -93,15 +138,18 @@ public class NotificationActionService extends CoreService {
* @param account
* @param refs
* @param dontCancel if true, after executing the intent, not all notifications for this account are canceled automatically
* @param notificationID ID of the notification, this intent is for.
* @return
* @see #EXTRA_NOTIFICATION_ID
*/
public static PendingIntent getArchiveAllMessagesIntent(Context context, final Account account, final Serializable refs, final boolean dontCancel) {
public static PendingIntent getArchiveAllMessagesIntent(Context context, final Account account, final Serializable refs, final boolean dontCancel, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE_LIST, refs);
if (dontCancel) {
i.putExtra(EXTRA_DONTCANCEL, true);
}
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setAction(ARCHIVE_ALL_ACTION);
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
@ -112,7 +160,7 @@ public class NotificationActionService extends CoreService {
* Check if for the given parameters the SpamAllMessages intent is possible for Android Wear.
* (No confirmation on the phone required and moving these messages to the spam-folder possible)<br/>
* Since we can not show a toast like on the phone screen, we must not offer actions that can not be performed.
* @see #getSpamAllMessagesIntent(android.content.Context, com.fsck.k9.Account, java.io.Serializable, boolean)
* @see #getSpamAllMessagesIntent(android.content.Context, com.fsck.k9.Account, java.io.Serializable, boolean, int)
* @param context the context to get a {@link MessagingController}
* @param account the account (must allow moving messages to allow true as a result)
* @param messages the messages to move to the spam folder (must be synchronized to allow true as a result)
@ -129,15 +177,18 @@ public class NotificationActionService extends CoreService {
* @param account
* @param refs
* @param dontCancel if true, after executing the intent, not all notifications for this account are canceled automatically
* @param notificationID ID of the notification, this intent is for.
* @return
* @see #EXTRA_NOTIFICATION_ID
*/
public static PendingIntent getSpamAllMessagesIntent(Context context, final Account account, final Serializable refs, final boolean dontCancel) {
public static PendingIntent getSpamAllMessagesIntent(Context context, final Account account, final Serializable refs, final boolean dontCancel, final int notificationID) {
Intent i = new Intent(context, NotificationActionService.class);
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
i.putExtra(EXTRA_MESSAGE_LIST, refs);
if (dontCancel) {
i.putExtra(EXTRA_DONTCANCEL, true);
}
i.putExtra(EXTRA_NOTIFICATION_ID, notificationID);
i.setAction(SPAM_ALL_ACTION);
return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT);
@ -267,7 +318,13 @@ public class NotificationActionService extends CoreService {
// notification and keep the other stacked notifications
if (!intent.hasExtra(EXTRA_DONTCANCEL)) {
// there's no point in keeping the notification after the user clicked on it
controller.notifyAccountCancel(this, account);
if (intent.hasExtra(EXTRA_NOTIFICATION_ID)) {
NotificationManager notificationManager =
(NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(intent.getIntExtra(EXTRA_NOTIFICATION_ID, account.getAccountNumber()));
} else {
controller.notifyAccountCancel(this, account);
}
}
} else {
Log.w(K9.LOG_TAG, "Could not find account for notification action.");