Merge pull request #5666 from k9mail/refactor_notification_code

Refactor notification code
This commit is contained in:
cketti 2021-09-16 16:51:37 +02:00 committed by GitHub
commit 0129dbb02d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 209 additions and 307 deletions

View file

@ -32,8 +32,8 @@ val coreNotificationModule = module {
single { SendFailedNotifications(get(), get(), get()) }
single { NewMailNotifications(get(), get(), get(), get()) }
single { NotificationContentCreator(get(), get()) }
single { WearNotifications(get(), get(), get()) }
single { DeviceNotifications(get(), get(), get(), get(), get()) }
single { SingleMessageNotifications(get(), get(), get()) }
single { MessageSummaryNotifications(get(), get(), get(), get(), get()) }
single { LockScreenNotification(get(), get()) }
single {
PushNotificationManager(

View file

@ -11,11 +11,11 @@ import com.fsck.k9.K9.NotificationQuickDelete
import com.fsck.k9.notification.NotificationGroupKeys.getGroupKey
import com.fsck.k9.notification.NotificationIds.getNewMailSummaryNotificationId
internal open class DeviceNotifications(
internal open class MessageSummaryNotifications(
notificationHelper: NotificationHelper,
actionCreator: NotificationActionCreator,
private val lockScreenNotification: LockScreenNotification,
private val wearNotifications: WearNotifications,
private val singleMessageNotifications: SingleMessageNotifications,
resourceProvider: NotificationResourceProvider
) : BaseNotifications(notificationHelper, actionCreator, resourceProvider) {
@ -28,7 +28,7 @@ internal open class DeviceNotifications(
}
notificationData.isSingleMessageNotification -> {
val holder = notificationData.holderForLatestNotification
createBigTextStyleSummaryNotification(account, holder)
createSingleMessageNotification(account, holder)
}
else -> {
createInboxStyleSummaryNotification(account, notificationData, unreadMessageCount)
@ -79,19 +79,14 @@ internal open class DeviceNotifications(
.setContentIntent(contentIntent)
}
private fun createBigTextStyleSummaryNotification(
private fun createSingleMessageNotification(
account: Account,
holder: NotificationHolder
): NotificationCompat.Builder {
val notificationId = getNewMailSummaryNotificationId(account)
val builder = createBigTextStyleNotification(account, holder, notificationId)
val builder = singleMessageNotifications.createSingleMessageNotificationBuilder(account, holder, notificationId)
builder.setGroupSummary(true)
val content = holder.content
addReplyAction(builder, content, notificationId)
addMarkAsReadAction(builder, content, notificationId)
addDeleteAction(builder, content, notificationId)
return builder
}
@ -130,7 +125,7 @@ internal open class DeviceNotifications(
addMarkAllAsReadAction(builder, notificationData)
addDeleteAllAction(builder, notificationData)
wearNotifications.addSummaryActions(builder, notificationData)
addWearActions(builder, notificationData)
val notificationId = getNewMailSummaryNotificationId(account)
val messageReferences = notificationData.getAllMessageReferences()
@ -140,19 +135,6 @@ internal open class DeviceNotifications(
return builder
}
private fun addMarkAsReadAction(
builder: NotificationCompat.Builder,
content: NotificationContent,
notificationId: Int
) {
val icon = resourceProvider.iconMarkAsRead
val title = resourceProvider.actionMarkAsRead()
val messageReference = content.messageReference
val action = actionCreator.createMarkMessageAsReadPendingIntent(messageReference, notificationId)
builder.addAction(icon, title, action)
}
private fun addMarkAllAsReadAction(builder: NotificationCompat.Builder, notificationData: NotificationData) {
val icon = resourceProvider.iconMarkAsRead
val title = resourceProvider.actionMarkAsRead()
@ -180,30 +162,73 @@ internal open class DeviceNotifications(
builder.addAction(icon, title, action)
}
private fun addDeleteAction(
builder: NotificationCompat.Builder,
content: NotificationContent,
notificationId: Int
) {
if (!isDeleteActionEnabled()) {
return
private fun addWearActions(builder: NotificationCompat.Builder, notificationData: NotificationData) {
val wearableExtender = NotificationCompat.WearableExtender()
addMarkAllAsReadWearAction(wearableExtender, notificationData)
if (isDeleteActionAvailableForWear()) {
addDeleteAllWearAction(wearableExtender, notificationData)
}
val icon = resourceProvider.iconDelete
val title = resourceProvider.actionDelete()
val messageReference = content.messageReference
val action = actionCreator.createDeleteMessagePendingIntent(messageReference, notificationId)
if (isArchiveActionAvailableForWear(notificationData.account)) {
addArchiveAllWearAction(wearableExtender, notificationData)
}
builder.addAction(icon, title, action)
builder.extend(wearableExtender)
}
private fun addReplyAction(builder: NotificationCompat.Builder, content: NotificationContent, notificationId: Int) {
val icon = resourceProvider.iconReply
val title = resourceProvider.actionReply()
val messageReference = content.messageReference
val replyToMessagePendingIntent = actionCreator.createReplyPendingIntent(messageReference, notificationId)
private fun addMarkAllAsReadWearAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconMarkAsRead
val title = resourceProvider.actionMarkAllAsRead()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createMarkAllAsReadPendingIntent(account, messageReferences, notificationId)
val markAsReadAction = NotificationCompat.Action.Builder(icon, title, action).build()
builder.addAction(icon, title, replyToMessagePendingIntent)
wearableExtender.addAction(markAsReadAction)
}
private fun addDeleteAllWearAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconDelete
val title = resourceProvider.actionDeleteAll()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createDeleteAllPendingIntent(account, messageReferences, notificationId)
val deleteAction = NotificationCompat.Action.Builder(icon, title, action).build()
wearableExtender.addAction(deleteAction)
}
private fun addArchiveAllWearAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconArchive
val title = resourceProvider.actionArchiveAll()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createArchiveAllPendingIntent(account, messageReferences, notificationId)
val archiveAction = NotificationCompat.Action.Builder(icon, title, action).build()
wearableExtender.addAction(archiveAction)
}
private fun isDeleteActionAvailableForWear(): Boolean {
return isDeleteActionEnabled() && !K9.isConfirmDeleteFromNotification
}
private fun isArchiveActionAvailableForWear(account: Account): Boolean {
return account.archiveFolderId != null
}
private val isPrivacyModeActive: Boolean

View file

@ -9,19 +9,12 @@ import com.fsck.k9.mailstore.LocalMessage
/**
* Handle notifications for new messages.
*
* We call the notification shown on the device *summary notification*, even when there's only one new message.
* Notifications on an Android Wear device are displayed as a stack of cards and that's why we call them *stacked
* notifications*. We have to keep track of stacked notifications individually and recreate/update the summary
* notification when one or more of the stacked notifications are added/removed.
*
* [NotificationData] keeps track of all data required to (re)create the actual system notifications.
*/
internal open class NewMailNotifications(
private val notificationHelper: NotificationHelper,
private val contentCreator: NotificationContentCreator,
private val deviceNotifications: DeviceNotifications,
private val wearNotifications: WearNotifications
private val messageSummaryNotifications: MessageSummaryNotifications,
private val singleMessageNotifications: SingleMessageNotifications
) {
private val notifications = SparseArray<NotificationData>()
private val lock = Any()
@ -38,7 +31,7 @@ internal open class NewMailNotifications(
cancelNotification(notificationId)
}
createStackedNotification(account, result.notificationHolder)
createSingleMessageNotification(account, result.notificationHolder)
createSummaryNotification(account, notificationData, false)
}
}
@ -53,7 +46,7 @@ internal open class NewMailNotifications(
cancelNotification(result.notificationId)
if (result.shouldCreateNotification) {
createStackedNotification(account, result.notificationHolder)
createSingleMessageNotification(account, result.notificationHolder)
}
updateSummaryNotification(account, notificationData)
@ -111,17 +104,17 @@ internal open class NewMailNotifications(
}
private fun createSummaryNotification(account: Account, notificationData: NotificationData, silent: Boolean) {
val notification = deviceNotifications.buildSummaryNotification(account, notificationData, silent)
val notification = messageSummaryNotifications.buildSummaryNotification(account, notificationData, silent)
val notificationId = NotificationIds.getNewMailSummaryNotificationId(account)
notificationManager.notify(notificationId, notification)
}
private fun createStackedNotification(account: Account, holder: NotificationHolder) {
private fun createSingleMessageNotification(account: Account, holder: NotificationHolder) {
if (isPrivacyModeEnabled) {
return
}
val notification = wearNotifications.buildStackedNotification(account, holder)
val notification = singleMessageNotifications.buildSingleMessageNotification(account, holder)
val notificationId = holder.notificationId
notificationManager.notify(notificationId, notification)
}

View file

@ -26,7 +26,7 @@ internal class NotificationData(val account: Account, private val initialUnreadM
get() = activeNotifications.first
private val isMaxNumberOfActiveNotificationsReached: Boolean
get() = activeNotifications.size == MAX_NUMBER_OF_STACKED_NOTIFICATIONS
get() = activeNotifications.size == MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS
fun addNotificationContent(content: NotificationContent): AddNotificationResult {
val notificationId: Int
@ -56,8 +56,8 @@ internal class NotificationData(val account: Account, private val initialUnreadM
}
private fun getNewNotificationId(): Int {
for (index in 0 until MAX_NUMBER_OF_STACKED_NOTIFICATIONS) {
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, index)
for (index in 0 until MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS) {
val notificationId = NotificationIds.getSingleMessageNotificationId(account, index)
if (!isNotificationInUse(notificationId)) {
markNotificationIdAsInUse(notificationId)
return notificationId
@ -153,7 +153,8 @@ internal class NotificationData(val account: Account, private val initialUnreadM
// Note: As of Jellybean, phone notifications show a maximum of 5 lines, while tablet notifications show 7 lines.
const val MAX_NUMBER_OF_MESSAGES_FOR_SUMMARY_NOTIFICATION = 5
// Note: This class assumes MAX_NUMBER_OF_STACKED_NOTIFICATIONS >= MAX_NUMBER_OF_MESSAGES_FOR_SUMMARY_NOTIFICATION
const val MAX_NUMBER_OF_STACKED_NOTIFICATIONS = 8
// Note: This class assumes that
// MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS >= MAX_NUMBER_OF_MESSAGES_FOR_SUMMARY_NOTIFICATION
const val MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS = 8
}
}

View file

@ -13,11 +13,11 @@ internal object NotificationIds {
private const val OFFSET_AUTHENTICATION_ERROR_OUTGOING = 4
private const val OFFSET_FETCHING_MAIL = 5
private const val OFFSET_NEW_MAIL_SUMMARY = 6
private const val OFFSET_NEW_MAIL_STACKED = 7
private const val NUMBER_OF_DEVICE_NOTIFICATIONS = 7
private const val NUMBER_OF_STACKED_NOTIFICATIONS = NotificationData.MAX_NUMBER_OF_STACKED_NOTIFICATIONS
private const val OFFSET_NEW_MAIL_SINGLE = 7
private const val NUMBER_OF_MISC_ACCOUNT_NOTIFICATIONS = 7
private const val NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS = NotificationData.MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS
private const val NUMBER_OF_NOTIFICATIONS_PER_ACCOUNT =
NUMBER_OF_DEVICE_NOTIFICATIONS + NUMBER_OF_STACKED_NOTIFICATIONS
NUMBER_OF_MISC_ACCOUNT_NOTIFICATIONS + NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS
@JvmStatic
fun getNewMailSummaryNotificationId(account: Account): Int {
@ -25,10 +25,10 @@ internal object NotificationIds {
}
@JvmStatic
fun getNewMailStackedNotificationId(account: Account, index: Int): Int {
require(index in 0 until NUMBER_OF_STACKED_NOTIFICATIONS) { "Invalid index: $index" }
fun getSingleMessageNotificationId(account: Account, index: Int): Int {
require(index in 0 until NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS) { "Invalid index: $index" }
return getBaseNotificationId(account) + OFFSET_NEW_MAIL_STACKED + index
return getBaseNotificationId(account) + OFFSET_NEW_MAIL_SINGLE + index
}
@JvmStatic

View file

@ -4,17 +4,23 @@ import android.app.Notification
import androidx.core.app.NotificationCompat
import com.fsck.k9.Account
import com.fsck.k9.K9
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.notification.NotificationIds.getNewMailSummaryNotificationId
internal open class WearNotifications(
internal open class SingleMessageNotifications(
notificationHelper: NotificationHelper,
actionCreator: NotificationActionCreator,
resourceProvider: NotificationResourceProvider
) : BaseNotifications(notificationHelper, actionCreator, resourceProvider) {
fun buildStackedNotification(account: Account, holder: NotificationHolder): Notification {
fun buildSingleMessageNotification(account: Account, holder: NotificationHolder): Notification {
val notificationId = holder.notificationId
return createSingleMessageNotificationBuilder(account, holder, notificationId).build()
}
fun createSingleMessageNotificationBuilder(
account: Account,
holder: NotificationHolder,
notificationId: Int
): NotificationCompat.Builder {
val content = holder.content
val builder = createBigTextStyleNotification(account, holder, notificationId)
builder.setNotificationSilent()
@ -26,68 +32,7 @@ internal open class WearNotifications(
addActions(builder, account, holder)
return builder.build()
}
fun addSummaryActions(builder: NotificationCompat.Builder, notificationData: NotificationData) {
val wearableExtender = NotificationCompat.WearableExtender()
addMarkAllAsReadAction(wearableExtender, notificationData)
if (isDeleteActionAvailableForWear()) {
addDeleteAllAction(wearableExtender, notificationData)
}
if (isArchiveActionAvailableForWear(notificationData.account)) {
addArchiveAllAction(wearableExtender, notificationData)
}
builder.extend(wearableExtender)
}
private fun addMarkAllAsReadAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconMarkAsRead
val title = resourceProvider.actionMarkAllAsRead()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createMarkAllAsReadPendingIntent(account, messageReferences, notificationId)
val markAsReadAction = NotificationCompat.Action.Builder(icon, title, action).build()
wearableExtender.addAction(markAsReadAction)
}
private fun addDeleteAllAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconDelete
val title = resourceProvider.actionDeleteAll()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createDeleteAllPendingIntent(account, messageReferences, notificationId)
val deleteAction = NotificationCompat.Action.Builder(icon, title, action).build()
wearableExtender.addAction(deleteAction)
}
private fun addArchiveAllAction(
wearableExtender: NotificationCompat.WearableExtender,
notificationData: NotificationData
) {
val icon = resourceProvider.wearIconArchive
val title = resourceProvider.actionArchiveAll()
val account = notificationData.account
val messageReferences = notificationData.getAllMessageReferences()
val notificationId = getNewMailSummaryNotificationId(account)
val action = actionCreator.createArchiveAllPendingIntent(account, messageReferences, notificationId)
val archiveAction = NotificationCompat.Action.Builder(icon, title, action).build()
wearableExtender.addAction(archiveAction)
return builder
}
private fun addActions(builder: NotificationCompat.Builder, account: Account, holder: NotificationHolder) {
@ -219,23 +164,10 @@ internal open class WearNotifications(
}
private fun isArchiveActionAvailableForWear(account: Account): Boolean {
return isMovePossible(account, account.archiveFolderId)
return account.archiveFolderId != null
}
private fun isSpamActionAvailableForWear(account: Account): Boolean {
return !K9.isConfirmSpam && isMovePossible(account, account.spamFolderId)
}
private fun isMovePossible(account: Account, destinationFolderId: Long?): Boolean {
if (destinationFolderId == null) {
return false
}
val controller = createMessagingController()
return controller.isMoveCapable(account)
}
protected open fun createMessagingController(): MessagingController {
return MessagingController.getInstance(context)
return account.spamFolderId != null && !K9.isConfirmSpam
}
}

View file

@ -27,13 +27,13 @@ class NewMailNotificationsTest : K9RobolectricTest() {
private val account = createAccount()
private val notificationManager = createNotificationManager()
private val contentCreator = createNotificationContentCreator()
private val deviceNotifications = createDeviceNotifications()
private val wearNotifications = createWearNotifications()
private val messageSummaryNotifications = createMessageSummaryNotifications()
private val singleMessageNotifications = createSingleMessageNotifications()
private val newMailNotifications = TestNewMailNotifications(
notificationHelper = createNotificationHelper(notificationManager),
contentCreator = contentCreator,
deviceNotifications = deviceNotifications,
wearNotifications = wearNotifications
messageSummaryNotifications = messageSummaryNotifications,
singleMessageNotifications = singleMessageNotifications
)
@Test
@ -44,15 +44,15 @@ class NewMailNotificationsTest : K9RobolectricTest() {
val holder = createNotificationHolder(content, notificationIndex)
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.newNotification(holder))
val wearNotification = createNotification()
val singleMessageNotification = createNotification()
val summaryNotification = createNotification()
addToWearNotifications(holder, wearNotification)
addToDeviceNotifications(summaryNotification)
addToSingleMessageNotifications(holder, singleMessageNotification)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, message, 42)
val wearNotificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
verify(notificationManager).notify(wearNotificationId, wearNotification)
val singleMessageNotificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
verify(notificationManager).notify(singleMessageNotificationId, singleMessageNotification)
val summaryNotificationId = NotificationIds.getNewMailSummaryNotificationId(account)
verify(notificationManager).notify(summaryNotificationId, summaryNotification)
}
@ -65,16 +65,16 @@ class NewMailNotificationsTest : K9RobolectricTest() {
val holder = createNotificationHolder(content, notificationIndex)
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.replaceNotification(holder))
val wearNotification = createNotification()
val singleMessageNotification = createNotification()
val summaryNotification = createNotification()
addToWearNotifications(holder, wearNotification)
addToDeviceNotifications(summaryNotification)
addToSingleMessageNotifications(holder, singleMessageNotification)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, message, 42)
val wearNotificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
verify(notificationManager).notify(wearNotificationId, wearNotification)
verify(notificationManager).cancel(wearNotificationId)
val singleMessageNotificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
verify(notificationManager).notify(singleMessageNotificationId, singleMessageNotification)
verify(notificationManager).cancel(singleMessageNotificationId)
val summaryNotificationId = NotificationIds.getNewMailSummaryNotificationId(account)
verify(notificationManager).notify(summaryNotificationId, summaryNotification)
}
@ -88,15 +88,15 @@ class NewMailNotificationsTest : K9RobolectricTest() {
val holder = createNotificationHolder(content, notificationIndex)
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.newNotification(holder))
val wearNotification = createNotification()
addToDeviceNotifications(wearNotification)
val singleMessageNotification = createNotification()
addToSummaryNotifications(singleMessageNotification)
newMailNotifications.addNewMailNotification(account, message, 42)
val wearNotificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val singleMessageNotificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val summaryNotificationId = NotificationIds.getNewMailSummaryNotificationId(account)
verify(notificationManager, never()).notify(eq(wearNotificationId), any())
verify(notificationManager).notify(summaryNotificationId, wearNotification)
verify(notificationManager, never()).notify(eq(singleMessageNotificationId), any())
verify(notificationManager).notify(summaryNotificationId, singleMessageNotification)
}
@Test
@ -113,20 +113,20 @@ class NewMailNotificationsTest : K9RobolectricTest() {
addToNotificationContentCreator(messageTwo, contentTwo)
whenAddingContentReturn(contentOne, AddNotificationResult.newNotification(holderOne))
whenAddingContentReturn(contentTwo, AddNotificationResult.newNotification(holderTwo))
val wearNotificationOne = createNotification()
val wearNotificationTwo = createNotification()
val singleMessageNotificationOne = createNotification()
val singleMessageNotificationTwo = createNotification()
val summaryNotification = createNotification()
addToWearNotifications(holderOne, wearNotificationOne)
addToWearNotifications(holderTwo, wearNotificationTwo)
addToDeviceNotifications(summaryNotification)
addToSingleMessageNotifications(holderOne, singleMessageNotificationOne)
addToSingleMessageNotifications(holderTwo, singleMessageNotificationTwo)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, messageOne, 42)
newMailNotifications.addNewMailNotification(account, messageTwo, 42)
val wearNotificationIdOne = NotificationIds.getNewMailStackedNotificationId(account, notificationIndexOne)
verify(notificationManager).notify(wearNotificationIdOne, wearNotificationOne)
val wearNotificationIdTwo = NotificationIds.getNewMailStackedNotificationId(account, notificationIndexTwo)
verify(notificationManager).notify(wearNotificationIdTwo, wearNotificationTwo)
val singleMessageNotificationIdOne = NotificationIds.getSingleMessageNotificationId(account, notificationIndexOne)
verify(notificationManager).notify(singleMessageNotificationIdOne, singleMessageNotificationOne)
val singleMessageNotificationIdTwo = NotificationIds.getSingleMessageNotificationId(account, notificationIndexTwo)
verify(notificationManager).notify(singleMessageNotificationIdTwo, singleMessageNotificationTwo)
val summaryNotificationId = NotificationIds.getNewMailSummaryNotificationId(account)
verify(notificationManager, times(2)).notify(summaryNotificationId, summaryNotification)
}
@ -151,7 +151,7 @@ class NewMailNotificationsTest : K9RobolectricTest() {
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.newNotification(holder))
val summaryNotification = createNotification()
addToDeviceNotifications(summaryNotification)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, message, 23)
whenRemovingContentReturn(messageReference, RemoveNotificationResult.unknownNotification())
@ -165,14 +165,14 @@ class NewMailNotificationsTest : K9RobolectricTest() {
enablePrivacyMode()
val messageReference = createMessageReference(1)
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val message = createLocalMessage()
val content = createNotificationContent()
val holder = createNotificationHolder(content, notificationIndex)
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.newNotification(holder))
val summaryNotification = createNotification()
addToDeviceNotifications(summaryNotification)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, message, 23)
whenRemovingContentReturn(messageReference, RemoveNotificationResult.cancelNotification(notificationId))
@ -187,14 +187,14 @@ class NewMailNotificationsTest : K9RobolectricTest() {
fun testRemoveNewMailNotificationClearingAllNotifications() {
val messageReference = createMessageReference(1)
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val message = createLocalMessage()
val content = createNotificationContent()
val holder = createNotificationHolder(content, notificationIndex)
addToNotificationContentCreator(message, content)
whenAddingContentReturn(content, AddNotificationResult.newNotification(holder))
val summaryNotification = createNotification()
addToDeviceNotifications(summaryNotification)
addToSummaryNotifications(summaryNotification)
newMailNotifications.addNewMailNotification(account, message, 23)
whenRemovingContentReturn(messageReference, RemoveNotificationResult.cancelNotification(notificationId))
whenever(newMailNotifications.notificationData.newMessagesCount).thenReturn(0)
@ -211,7 +211,7 @@ class NewMailNotificationsTest : K9RobolectricTest() {
fun testRemoveNewMailNotificationWithCreateNotification() {
val messageReference = createMessageReference(1)
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val message = createLocalMessage()
val contentOne = createNotificationContent()
val contentTwo = createNotificationContent()
@ -220,18 +220,18 @@ class NewMailNotificationsTest : K9RobolectricTest() {
addToNotificationContentCreator(message, contentOne)
whenAddingContentReturn(contentOne, AddNotificationResult.newNotification(holderOne))
val summaryNotification = createNotification()
addToDeviceNotifications(summaryNotification)
val wearNotificationOne = createNotification()
val wearNotificationTwo = createNotification()
addToWearNotifications(holderOne, wearNotificationOne)
addToWearNotifications(holderTwo, wearNotificationTwo)
addToSummaryNotifications(summaryNotification)
val singleMessageNotificationOne = createNotification()
val singleMessageNotificationTwo = createNotification()
addToSingleMessageNotifications(holderOne, singleMessageNotificationOne)
addToSingleMessageNotifications(holderTwo, singleMessageNotificationTwo)
newMailNotifications.addNewMailNotification(account, message, 23)
whenRemovingContentReturn(messageReference, RemoveNotificationResult.createNotification(holderTwo))
newMailNotifications.removeNewMailNotification(account, messageReference)
verify(notificationManager).cancel(notificationId)
verify(notificationManager).notify(notificationId, wearNotificationTwo)
verify(notificationManager).notify(notificationId, singleMessageNotificationTwo)
val summaryNotificationId = NotificationIds.getNewMailSummaryNotificationId(account)
verify(notificationManager, times(2)).notify(summaryNotificationId, summaryNotification)
}
@ -246,7 +246,7 @@ class NewMailNotificationsTest : K9RobolectricTest() {
@Test
fun testClearNewMailNotifications() {
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val message = createLocalMessage()
val content = createNotificationContent()
val holder = createNotificationHolder(content, notificationIndex)
@ -275,7 +275,7 @@ class NewMailNotificationsTest : K9RobolectricTest() {
}
private fun createNotificationHolder(content: NotificationContent, index: Int): NotificationHolder {
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, index)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, index)
return NotificationHolder(notificationId, content)
}
@ -295,10 +295,10 @@ class NewMailNotificationsTest : K9RobolectricTest() {
}
}
private fun createDeviceNotifications(): DeviceNotifications = mock()
private fun createMessageSummaryNotifications(): MessageSummaryNotifications = mock()
private fun addToDeviceNotifications(notificationToReturn: Notification) {
stubbing(deviceNotifications) {
private fun addToSummaryNotifications(notificationToReturn: Notification) {
stubbing(messageSummaryNotifications) {
on {
buildSummaryNotification(eq(account), eq(newMailNotifications.notificationData), anyBoolean())
} doReturn notificationToReturn
@ -307,14 +307,14 @@ class NewMailNotificationsTest : K9RobolectricTest() {
private fun createNotification(): Notification = mock()
private fun createWearNotifications(): WearNotifications = mock()
private fun createSingleMessageNotifications(): SingleMessageNotifications = mock()
private fun createMessageReference(number: Int): MessageReference {
return MessageReference("account", 1, number.toString(), null)
}
private fun addToWearNotifications(notificationHolder: NotificationHolder, notificationToReturn: Notification) {
whenever(wearNotifications.buildStackedNotification(account, notificationHolder))
private fun addToSingleMessageNotifications(notificationHolder: NotificationHolder, notificationToReturn: Notification) {
whenever(singleMessageNotifications.buildSingleMessageNotification(account, notificationHolder))
.thenReturn(notificationToReturn)
}
@ -347,10 +347,10 @@ class NewMailNotificationsTest : K9RobolectricTest() {
internal class TestNewMailNotifications(
notificationHelper: NotificationHelper,
contentCreator: NotificationContentCreator,
deviceNotifications: DeviceNotifications,
wearNotifications: WearNotifications
messageSummaryNotifications: MessageSummaryNotifications,
singleMessageNotifications: SingleMessageNotifications
) : NewMailNotifications(
notificationHelper, contentCreator, deviceNotifications, wearNotifications
notificationHelper, contentCreator, messageSummaryNotifications, singleMessageNotifications
) {
val notificationData = mock<NotificationData>()

View file

@ -28,7 +28,7 @@ class NotificationDataTest : RobolectricTest() {
val holder = result.notificationHolder
assertThat(holder).isNotNull()
assertThat(holder.notificationId).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 0))
assertThat(holder.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 0))
assertThat(holder.content).isEqualTo(content)
}
@ -46,7 +46,7 @@ class NotificationDataTest : RobolectricTest() {
val result = notificationData.addNotificationContent(createNotificationContent("9"))
assertThat(result.shouldCancelNotification).isTrue()
assertThat(result.notificationId).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 0))
assertThat(result.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 0))
}
@Test
@ -57,7 +57,7 @@ class NotificationDataTest : RobolectricTest() {
val result = notificationData.removeNotificationForMessage(content.messageReference)
assertThat(result.isUnknownNotification).isFalse()
assertThat(result.notificationId).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 0))
assertThat(result.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 0))
assertThat(result.shouldCreateNotification).isFalse()
}
@ -79,17 +79,17 @@ class NotificationDataTest : RobolectricTest() {
val result = notificationData.removeNotificationForMessage(latestContent.messageReference)
assertThat(result.isUnknownNotification).isFalse()
assertThat(result.notificationId).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 1))
assertThat(result.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
assertThat(result.shouldCreateNotification).isTrue()
assertNotNull(result.notificationHolder) { holder ->
assertThat(holder.notificationId).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 1))
assertThat(holder.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
assertThat(holder.content).isEqualTo(content)
}
}
@Test
fun testRemoveDoesNotLeakNotificationIds() {
for (i in 1..NotificationData.MAX_NUMBER_OF_STACKED_NOTIFICATIONS + 1) {
for (i in 1..NotificationData.MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS + 1) {
val content = createNotificationContent(i.toString())
notificationData.addNotificationContent(content)
notificationData.removeNotificationForMessage(content.messageReference)
@ -202,8 +202,8 @@ class NotificationDataTest : RobolectricTest() {
val notificationIds = notificationData.getActiveNotificationIds()
assertThat(notificationIds.size).isEqualTo(2)
assertThat(notificationIds[0]).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 1))
assertThat(notificationIds[1]).isEqualTo(NotificationIds.getNewMailStackedNotificationId(account, 0))
assertThat(notificationIds[0]).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
assertThat(notificationIds[1]).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 0))
}
@Test

View file

@ -94,8 +94,8 @@ class NotificationIdsTest {
NotificationIds.getAuthenticationErrorNotificationId(account, false),
NotificationIds.getFetchingMailNotificationId(account),
NotificationIds.getNewMailSummaryNotificationId(account),
) + (0 until NotificationData.MAX_NUMBER_OF_STACKED_NOTIFICATIONS).map { index ->
NotificationIds.getNewMailStackedNotificationId(account, index)
) + (0 until NotificationData.MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS).map { index ->
NotificationIds.getSingleMessageNotificationId(account, index)
}
}

View file

@ -25,16 +25,15 @@ import org.mockito.kotlin.whenever
private const val ACCOUNT_NUMBER = 42
private const val ACCOUNT_NAME = "accountName"
class WearNotificationsTest : RobolectricTest() {
class SingleMessageNotificationsTest : RobolectricTest() {
private val resourceProvider: NotificationResourceProvider = TestNotificationResourceProvider()
private val account = createAccount()
private val notification = mock<Notification>()
private val builder = createNotificationBuilder(notification)
private val actionCreator = mock<NotificationActionCreator>()
private val wearNotifications = TestWearNotifications(
private val notifications = SingleMessageNotifications(
notificationHelper = createNotificationHelper(builder),
actionCreator = actionCreator,
messagingController = createMessagingController(),
resourceProvider = resourceProvider
)
@ -42,7 +41,7 @@ class WearNotificationsTest : RobolectricTest() {
fun testBuildStackedNotification() {
disableOptionalActions()
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val messageReference = createMessageReference(1)
val content = createNotificationContent(messageReference)
val holder = createNotificationHolder(notificationId, content)
@ -58,7 +57,7 @@ class WearNotificationsTest : RobolectricTest() {
markAsReadPendingIntent
)
val result = wearNotifications.buildStackedNotification(account, holder)
val result = notifications.buildSingleMessageNotification(account, holder)
assertThat(result).isEqualTo(notification)
verifyExtendWasOnlyCalledOnce()
@ -71,7 +70,7 @@ class WearNotificationsTest : RobolectricTest() {
fun testBuildStackedNotificationWithDeleteActionEnabled() {
enableDeleteAction()
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val messageReference = createMessageReference(1)
val content = createNotificationContent(messageReference)
val holder = createNotificationHolder(notificationId, content)
@ -80,7 +79,7 @@ class WearNotificationsTest : RobolectricTest() {
deletePendingIntent
)
val result = wearNotifications.buildStackedNotification(account, holder)
val result = notifications.buildSingleMessageNotification(account, holder)
assertThat(result).isEqualTo(notification)
verifyExtendWasOnlyCalledOnce()
@ -91,7 +90,7 @@ class WearNotificationsTest : RobolectricTest() {
fun testBuildStackedNotificationWithArchiveActionEnabled() {
enableArchiveAction()
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val messageReference = createMessageReference(1)
val content = createNotificationContent(messageReference)
val holder = createNotificationHolder(notificationId, content)
@ -100,7 +99,7 @@ class WearNotificationsTest : RobolectricTest() {
archivePendingIntent
)
val result = wearNotifications.buildStackedNotification(account, holder)
val result = notifications.buildSingleMessageNotification(account, holder)
assertThat(result).isEqualTo(notification)
verifyExtendWasOnlyCalledOnce()
@ -111,7 +110,7 @@ class WearNotificationsTest : RobolectricTest() {
fun testBuildStackedNotificationWithMarkAsSpamActionEnabled() {
enableSpamAction()
val notificationIndex = 0
val notificationId = NotificationIds.getNewMailStackedNotificationId(account, notificationIndex)
val notificationId = NotificationIds.getSingleMessageNotificationId(account, notificationIndex)
val messageReference = createMessageReference(1)
val content = createNotificationContent(messageReference)
val holder = createNotificationHolder(notificationId, content)
@ -120,65 +119,13 @@ class WearNotificationsTest : RobolectricTest() {
markAsSpamPendingIntent
)
val result = wearNotifications.buildStackedNotification(account, holder)
val result = notifications.buildSingleMessageNotification(account, holder)
assertThat(result).isEqualTo(notification)
verifyExtendWasOnlyCalledOnce()
verifyAddAction(resourceProvider.wearIconMarkAsSpam, "Spam", markAsSpamPendingIntent)
}
@Test
fun testAddSummaryActions() {
disableOptionalSummaryActions()
val notificationId = NotificationIds.getNewMailSummaryNotificationId(account)
val messageReferences = createMessageReferenceList()
val notificationData = createNotificationData(messageReferences)
val markAllAsReadPendingIntent = createFakePendingIntent(1)
whenever(actionCreator.createMarkAllAsReadPendingIntent(account, messageReferences, notificationId)).thenReturn(
markAllAsReadPendingIntent
)
wearNotifications.addSummaryActions(builder, notificationData)
verifyExtendWasOnlyCalledOnce()
verifyAddAction(resourceProvider.wearIconMarkAsRead, "Mark All Read", markAllAsReadPendingIntent)
verifyNumberOfActions(1)
}
@Test
fun testAddSummaryActionsWithDeleteAllActionEnabled() {
enableDeleteAction()
val notificationId = NotificationIds.getNewMailSummaryNotificationId(account)
val messageReferences = createMessageReferenceList()
val notificationData = createNotificationData(messageReferences)
val deletePendingIntent = createFakePendingIntent(1)
whenever(actionCreator.createDeleteAllPendingIntent(account, messageReferences, notificationId)).thenReturn(
deletePendingIntent
)
wearNotifications.addSummaryActions(builder, notificationData)
verifyExtendWasOnlyCalledOnce()
verifyAddAction(resourceProvider.wearIconDelete, "Delete All", deletePendingIntent)
}
@Test
fun testAddSummaryActionsWithArchiveAllActionEnabled() {
enableArchiveAction()
val notificationId = NotificationIds.getNewMailSummaryNotificationId(account)
val messageReferences = createMessageReferenceList()
val notificationData = createNotificationData(messageReferences)
val archivePendingIntent = createFakePendingIntent(1)
whenever(actionCreator.createArchiveAllPendingIntent(account, messageReferences, notificationId)).thenReturn(
archivePendingIntent
)
wearNotifications.addSummaryActions(builder, notificationData)
verifyExtendWasOnlyCalledOnce()
verifyAddAction(resourceProvider.wearIconArchive, "Archive All", archivePendingIntent)
}
private fun disableOptionalActions() {
disableDeleteAction()
disableArchiveAction()
@ -306,17 +253,4 @@ class WearNotificationsTest : RobolectricTest() {
return argument.actions.size == expectedNumberOfActions
}
}
internal class TestWearNotifications(
notificationHelper: NotificationHelper,
actionCreator: NotificationActionCreator,
private val messagingController: MessagingController,
resourceProvider: NotificationResourceProvider
) : WearNotifications(
notificationHelper, actionCreator, resourceProvider
) {
override fun createMessagingController(): MessagingController {
return messagingController
}
}
}

View file

@ -35,15 +35,16 @@ private const val SUBJECT_2 = "subject2"
private const val SENDER_2 = "sender2"
private const val NOTIFICATION_ID = 23
class DeviceNotificationsTest : RobolectricTest() {
class SummaryNotificationsTest : RobolectricTest() {
private val notification = mock<Notification>()
private val bigTextStyle = mockBuilder<NotificationCompat.BigTextStyle>()
private val resourceProvider: NotificationResourceProvider = TestNotificationResourceProvider()
private val account = createFakeAccount()
private val notificationData = createFakeNotificationData(account)
private val builder = createFakeNotificationBuilder()
private val builder2 = createFakeNotificationBuilder()
private val lockScreenNotification = mock<LockScreenNotification>()
private val notifications = createDeviceNotifications(builder, lockScreenNotification)
private val notifications = createSummaryNotifications(builder, lockScreenNotification)
@Test
fun buildSummaryNotification_withPrivacyModeActive() {
@ -78,8 +79,8 @@ class DeviceNotificationsTest : RobolectricTest() {
verify(builder).setTicker(SUMMARY)
verify(builder).setContentText(SUBJECT)
verify(builder).setContentTitle(SENDER)
verify(builder).setStyle(notifications.bigTextStyle)
verify(notifications.bigTextStyle).bigText(PREVIEW)
verify(builder).setStyle(bigTextStyle)
verify(bigTextStyle).bigText(PREVIEW)
verify(builder).addAction(resourceProvider.iconReply, "Reply", null)
verify(builder).addAction(resourceProvider.iconMarkAsRead, "Mark Read", null)
verify(builder).addAction(resourceProvider.iconDelete, "Delete", null)
@ -187,15 +188,22 @@ class DeviceNotificationsTest : RobolectricTest() {
}
}
private fun createDeviceNotifications(
private fun createSummaryNotifications(
builder: NotificationCompat.Builder,
lockScreenNotification: LockScreenNotification
): TestDeviceNotifications {
return TestDeviceNotifications(
notificationHelper = createFakeNotificationHelper(builder),
): TestMessageSummaryNotifications {
val notificationHelper = createFakeNotificationHelper(builder)
val singleMessageNotifications = TestSingleMessageNotifications(
notificationHelper = notificationHelper,
actionCreator = mock(),
resourceProvider = resourceProvider
)
return TestMessageSummaryNotifications(
notificationHelper = notificationHelper,
actionCreator = mock(),
lockScreenNotification = lockScreenNotification,
wearNotifications = mock(),
singleMessageNotifications = singleMessageNotifications,
resourceProvider = resourceProvider
)
}
@ -210,28 +218,37 @@ class DeviceNotificationsTest : RobolectricTest() {
}
}
internal class TestDeviceNotifications(
internal class TestMessageSummaryNotifications(
notificationHelper: NotificationHelper,
actionCreator: NotificationActionCreator,
lockScreenNotification: LockScreenNotification,
wearNotifications: WearNotifications,
singleMessageNotifications: SingleMessageNotifications,
resourceProvider: NotificationResourceProvider
) : DeviceNotifications(
) : MessageSummaryNotifications(
notificationHelper,
actionCreator,
lockScreenNotification,
wearNotifications,
singleMessageNotifications,
resourceProvider
) {
val bigTextStyle = mockBuilder<NotificationCompat.BigTextStyle>()
val inboxStyle = mockBuilder<NotificationCompat.InboxStyle>()
override fun createBigTextStyle(builder: NotificationCompat.Builder?): NotificationCompat.BigTextStyle {
return bigTextStyle
}
override fun createInboxStyle(builder: NotificationCompat.Builder?): NotificationCompat.InboxStyle {
return inboxStyle
}
}
internal inner class TestSingleMessageNotifications(
notificationHelper: NotificationHelper,
actionCreator: NotificationActionCreator,
resourceProvider: NotificationResourceProvider
) : SingleMessageNotifications(
notificationHelper,
actionCreator,
resourceProvider
) {
override fun createBigTextStyle(builder: NotificationCompat.Builder?): NotificationCompat.BigTextStyle {
return bigTextStyle
}
}
}