package com.fsck.k9.notification;
import java.util.ArrayList;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.support.v4.app.NotificationCompat.Action;
import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.NotificationCompat.Extender;
import android.support.v4.app.NotificationCompat.WearableExtender;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.K9.NotificationQuickDelete;
import com.fsck.k9.K9RobolectricTestRunner;
import com.fsck.k9.MockHelper;
import com.fsck.k9.R;
import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.controller.MessagingController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.robolectric.RuntimeEnvironment;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(K9RobolectricTestRunner.class)
public class WearNotificationsTest {
private static final int ACCOUNT_NUMBER = 42;
private static final String ACCOUNT_NAME = "accountName";
private Account account;
private Builder builder;
private NotificationActionCreator actionCreator;
private TestWearNotifications wearNotifications;
private Notification notification;
@Before
public void setUp() throws Exception {
account = createAccount();
notification = createNotification();
builder = createNotificationBuilder(notification);
actionCreator = createNotificationActionCreator();
NotificationController controller = createNotificationController(RuntimeEnvironment.application, builder);
MessagingController messagingController = createMessagingController();
wearNotifications = new TestWearNotifications(controller, actionCreator, messagingController);
}
@Test
public void testBuildStackedNotification() throws Exception {
disableOptionalActions();
int notificationIndex = 0;
int notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex);
MessageReference messageReference = createMessageReference(1);
NotificationContent content = createNotificationContent(messageReference);
NotificationHolder holder = createNotificationHolder(notificationId, content);
PendingIntent replyPendingIntent = createFakePendingIntent(1);
when(actionCreator.createReplyPendingIntent(messageReference, notificationId)).thenReturn(replyPendingIntent);
PendingIntent markAsReadPendingIntent = createFakePendingIntent(2);
when(actionCreator.createMarkMessageAsReadPendingIntent(messageReference, notificationId))
.thenReturn(markAsReadPendingIntent);
Notification result = wearNotifications.buildStackedNotification(account, holder);
assertEquals(notification, result);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_single_message_options_dark, "Reply", replyPendingIntent);
verifyAddAction(R.drawable.ic_action_mark_as_read_dark, "Mark Read", markAsReadPendingIntent);
verifyNumberOfActions(2);
}
@Test
public void testBuildStackedNotificationWithDeleteActionEnabled() throws Exception {
enableDeleteAction();
int notificationIndex = 0;
int notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex);
MessageReference messageReference = createMessageReference(1);
NotificationContent content = createNotificationContent(messageReference);
NotificationHolder holder = createNotificationHolder(notificationId, content);
PendingIntent deletePendingIntent = createFakePendingIntent(1);
when(actionCreator.createDeleteMessagePendingIntent(messageReference, notificationId))
.thenReturn(deletePendingIntent);
Notification result = wearNotifications.buildStackedNotification(account, holder);
assertEquals(notification, result);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_delete_dark, "Delete", deletePendingIntent);
}
@Test
public void testBuildStackedNotificationWithArchiveActionEnabled() throws Exception {
enableArchiveAction();
int notificationIndex = 0;
int notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex);
MessageReference messageReference = createMessageReference(1);
NotificationContent content = createNotificationContent(messageReference);
NotificationHolder holder = createNotificationHolder(notificationId, content);
PendingIntent archivePendingIntent = createFakePendingIntent(1);
when(actionCreator.createArchiveMessagePendingIntent(messageReference, notificationId))
.thenReturn(archivePendingIntent);
Notification result = wearNotifications.buildStackedNotification(account, holder);
assertEquals(notification, result);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_archive_dark, "Archive", archivePendingIntent);
}
@Test
public void testBuildStackedNotificationWithMarkAsSpamActionEnabled() throws Exception {
enableSpamAction();
int notificationIndex = 0;
int notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex);
MessageReference messageReference = createMessageReference(1);
NotificationContent content = createNotificationContent(messageReference);
NotificationHolder holder = createNotificationHolder(notificationId, content);
PendingIntent markAsSpamPendingIntent = createFakePendingIntent(1);
when(actionCreator.createMarkMessageAsSpamPendingIntent(messageReference, notificationId))
.thenReturn(markAsSpamPendingIntent);
Notification result = wearNotifications.buildStackedNotification(account, holder);
assertEquals(notification, result);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_spam_dark, "Spam", markAsSpamPendingIntent);
}
@Test
public void testAddSummaryActions() throws Exception {
disableOptionalSummaryActions();
int notificationId = NotificationIds.getNewMailSummaryNotificationId(account);
ArrayList<MessageReference> messageReferences = createMessageReferenceList();
NotificationData notificationData = createNotificationData(messageReferences);
PendingIntent markAllAsReadPendingIntent = createFakePendingIntent(1);
when(actionCreator.getMarkAllAsReadPendingIntent(account, messageReferences, notificationId))
.thenReturn(markAllAsReadPendingIntent);
wearNotifications.addSummaryActions(builder, notificationData);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_mark_as_read_dark, "Mark All Read", markAllAsReadPendingIntent);
verifyNumberOfActions(1);
}
@Test
public void testAddSummaryActionsWithDeleteAllActionEnabled() throws Exception {
enableDeleteAction();
int notificationId = NotificationIds.getNewMailSummaryNotificationId(account);
ArrayList<MessageReference> messageReferences = createMessageReferenceList();
NotificationData notificationData = createNotificationData(messageReferences);
PendingIntent deletePendingIntent = createFakePendingIntent(1);
when(actionCreator.getDeleteAllPendingIntent(account, messageReferences, notificationId))
.thenReturn(deletePendingIntent);
wearNotifications.addSummaryActions(builder, notificationData);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_delete_dark, "Delete All", deletePendingIntent);
}
@Test
public void testAddSummaryActionsWithArchiveAllActionEnabled() throws Exception {
enableArchiveAction();
int notificationId = NotificationIds.getNewMailSummaryNotificationId(account);
ArrayList<MessageReference> messageReferences = createMessageReferenceList();
NotificationData notificationData = createNotificationData(messageReferences);
PendingIntent archivePendingIntent = createFakePendingIntent(1);
when(actionCreator.createArchiveAllPendingIntent(account, messageReferences, notificationId))
.thenReturn(archivePendingIntent);
wearNotifications.addSummaryActions(builder, notificationData);
verifyExtendWasOnlyCalledOnce();
verifyAddAction(R.drawable.ic_action_archive_dark, "Archive All", archivePendingIntent);
}
private void disableOptionalActions() {
disableDeleteAction();
disableArchiveAction();
disableSpamAction();
}
private void disableDeleteAction() {
K9.setNotificationQuickDeleteBehaviour(NotificationQuickDelete.NEVER);
}
private void disableArchiveAction() {
when(account.getArchiveFolderName()).thenReturn(K9.FOLDER_NONE);
}
private void disableSpamAction() {
when(account.getSpamFolderName()).thenReturn(K9.FOLDER_NONE);
}
private void enableDeleteAction() {
K9.setNotificationQuickDeleteBehaviour(NotificationQuickDelete.ALWAYS);
K9.setConfirmDeleteFromNotification(false);
}
private void enableArchiveAction() {
when(account.getArchiveFolderName()).thenReturn("Archive");
}
private void enableSpamAction() {
when(account.getSpamFolderName()).thenReturn("Spam");
}
private void disableOptionalSummaryActions() {
disableDeleteAction();
disableArchiveAction();
}
private Builder createNotificationBuilder(Notification notification) {
Builder builder = MockHelper.mockBuilder(Builder.class);
when(builder.build()).thenReturn(notification);
return builder;
}
private NotificationController createNotificationController(Context context, Builder builder) {
NotificationController controller = mock(NotificationController.class);
when(controller.createNotificationBuilder()).thenReturn(builder);
when(controller.getAccountName(account)).thenReturn(ACCOUNT_NAME);
when(controller.getContext()).thenReturn(context);
return controller;
}
private NotificationActionCreator createNotificationActionCreator() {
return mock(NotificationActionCreator.class);
}
private Account createAccount() {
Account account = mock(Account.class);
when(account.getAccountNumber()).thenReturn(ACCOUNT_NUMBER);
return account;
}
private MessagingController createMessagingController() {
MessagingController messagingController = mock(MessagingController.class);
when(messagingController.isMoveCapable(account)).thenReturn(true);
return messagingController;
}
private NotificationContent createNotificationContent(MessageReference messageReference) {
return new NotificationContent(messageReference, null, null, null, null, false);
}
private NotificationHolder createNotificationHolder(int notificationId, NotificationContent content) {
return new NotificationHolder(notificationId, content);
}
private Notification createNotification() {
return mock(Notification.class);
}
private MessageReference createMessageReference(int number) {
return new MessageReference("account", "folder", String.valueOf(number), null);
}
private PendingIntent createFakePendingIntent(int requestCode) {
return PendingIntent.getActivity(RuntimeEnvironment.application, requestCode, null, 0);
}
private ArrayList<MessageReference> createMessageReferenceList() {
ArrayList<MessageReference> messageReferences = new ArrayList<MessageReference>();
messageReferences.add(createMessageReference(1));
messageReferences.add(createMessageReference(2));
return messageReferences;
}
private NotificationData createNotificationData(ArrayList<MessageReference> messageReferences) {
NotificationData notificationData = mock(NotificationData.class);
when(notificationData.getAccount()).thenReturn(account);
when(notificationData.getAllMessageReferences()).thenReturn(messageReferences);
return notificationData;
}
private Builder verifyExtendWasOnlyCalledOnce() {
return verify(builder, times(1)).extend(any(Extender.class));
}
private void verifyAddAction(int icon, String title, PendingIntent pendingIntent) {
verify(builder).extend(action(icon, title, pendingIntent));
}
private Builder verifyNumberOfActions(int expectedNumberOfActions) {
return verify(builder).extend(numberOfActions(expectedNumberOfActions));
}
private WearableExtender action(int icon, String title, PendingIntent pendingIntent) {
return argThat(new ActionMatcher(icon, title, pendingIntent));
}
private WearableExtender numberOfActions(int expectedNumberOfActions) {
return argThat(new NumberOfActionsMatcher(expectedNumberOfActions));
}
static class ActionMatcher extends ArgumentMatcher<WearableExtender> {
private int icon;
private String title;
private PendingIntent pendingIntent;
public ActionMatcher(int icon, String title, PendingIntent pendingIntent) {
this.icon = icon;
this.title = title;
this.pendingIntent = pendingIntent;
}
@Override
public boolean matches(Object argument) {
if (!(argument instanceof WearableExtender)) {
return false;
}
WearableExtender wearableExtender = (WearableExtender) argument;
for (Action action : wearableExtender.getActions()) {
if (action.icon == icon && action.title.equals(title) && action.actionIntent == pendingIntent) {
return true;
}
}
return false;
}
}
static class NumberOfActionsMatcher extends ArgumentMatcher<WearableExtender> {
private final int expectedNumberOfActions;
public NumberOfActionsMatcher(int expectedNumberOfActions) {
this.expectedNumberOfActions = expectedNumberOfActions;
}
@Override
public boolean matches(Object argument) {
if (!(argument instanceof WearableExtender)) {
return false;
}
WearableExtender wearableExtender = (WearableExtender) argument;
return wearableExtender.getActions().size() == expectedNumberOfActions;
}
}
static class TestWearNotifications extends WearNotifications {
private final MessagingController messagingController;
public TestWearNotifications(NotificationController controller, NotificationActionCreator actionCreator,
MessagingController messagingController) {
super(controller, actionCreator);
this.messagingController = messagingController;
}
@Override
MessagingController createMessagingController() {
return messagingController;
}
}
}