Do summary notifications like the Api defines it: * Schedule and summarize without delay (in order for summerization to work) * Always have a summary notification: simplify code with this and make more reliable * Do not care about single notification count (the system already does that as well) * **Bugfix: Schedule summary first: This avoids a rate limit problem that (then) not groups at all** Testing this is probably the most difficult part. For example I couldn't get any notification to ring with older Api versions in the debugger. (Same as for current develop) However one hack to always get notifications: Fix "minId" in "fetchNewNotifications()" to a somewhat older value. Next possible step: Have only one summary notification at all (for all channels/notification types). You can still configure single channels differently. Or: For very many notifications: Only use a true summary one (something like "you have 28 favorites and 7 boosts"). Generally: The notification timeline must be improved now. Because that must be the go-to solution for any large number of notifications. It must be easy to read. E. g. with grouping per post.
152 lines
5.5 KiB
Kotlin
152 lines
5.5 KiB
Kotlin
package com.keylesspalace.tusky
|
|
|
|
import android.app.Activity
|
|
import android.app.NotificationManager
|
|
import android.content.ComponentName
|
|
import android.content.Intent
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
import androidx.test.platform.app.InstrumentationRegistry
|
|
import androidx.viewpager2.widget.ViewPager2
|
|
import androidx.work.testing.WorkManagerTestInitHelper
|
|
import at.connyduck.calladapter.networkresult.NetworkResult
|
|
import com.keylesspalace.tusky.appstore.EventHub
|
|
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
|
|
import com.keylesspalace.tusky.components.systemnotifications.NotificationHelper
|
|
import com.keylesspalace.tusky.db.entity.AccountEntity
|
|
import com.keylesspalace.tusky.entity.Account
|
|
import com.keylesspalace.tusky.entity.Notification
|
|
import com.keylesspalace.tusky.entity.TimelineAccount
|
|
import com.keylesspalace.tusky.util.getSerializableExtraCompat
|
|
import java.util.Date
|
|
import kotlinx.coroutines.test.TestScope
|
|
import org.junit.After
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Assert.assertNotNull
|
|
import org.junit.Before
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.mockito.kotlin.doReturn
|
|
import org.mockito.kotlin.mock
|
|
import org.robolectric.Robolectric
|
|
import org.robolectric.Shadows.shadowOf
|
|
import org.robolectric.android.util.concurrent.BackgroundExecutor.runInBackground
|
|
import org.robolectric.annotation.Config
|
|
|
|
@Config(sdk = [28])
|
|
@RunWith(AndroidJUnit4::class)
|
|
class MainActivityTest {
|
|
|
|
private val context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
private val account = Account(
|
|
id = "1",
|
|
localUsername = "",
|
|
username = "",
|
|
displayName = "",
|
|
createdAt = Date(),
|
|
note = "",
|
|
url = "",
|
|
avatar = "",
|
|
header = ""
|
|
)
|
|
private val accountEntity = AccountEntity(
|
|
id = 1,
|
|
domain = "test.domain",
|
|
accessToken = "fakeToken",
|
|
clientId = "fakeId",
|
|
clientSecret = "fakeSecret",
|
|
isActive = true
|
|
)
|
|
|
|
@Before
|
|
fun setup() {
|
|
WorkManagerTestInitHelper.initializeTestWorkManager(context)
|
|
}
|
|
|
|
@After
|
|
fun teardown() {
|
|
WorkManagerTestInitHelper.closeWorkDatabase()
|
|
}
|
|
|
|
@Test
|
|
fun `clicking notification of type FOLLOW shows notification tab`() {
|
|
val intent = showNotification(Notification.Type.FOLLOW)
|
|
|
|
val activity = startMainActivity(intent)
|
|
val currentTab = activity.findViewById<ViewPager2>(R.id.viewPager).currentItem
|
|
|
|
val notificationTab = defaultTabs().indexOfFirst { it.id == NOTIFICATIONS }
|
|
|
|
assertEquals(currentTab, notificationTab)
|
|
}
|
|
|
|
@Test
|
|
fun `clicking notification of type FOLLOW_REQUEST shows follow requests`() {
|
|
val intent = showNotification(Notification.Type.FOLLOW_REQUEST)
|
|
|
|
val activity = startMainActivity(intent)
|
|
val nextActivity = shadowOf(activity).peekNextStartedActivity()
|
|
|
|
assertNotNull(nextActivity)
|
|
assertEquals(ComponentName(context, AccountListActivity::class.java.name), nextActivity.component)
|
|
assertEquals(AccountListActivity.Type.FOLLOW_REQUESTS, nextActivity.getSerializableExtraCompat("type"))
|
|
}
|
|
|
|
private fun showNotification(type: Notification.Type): Intent {
|
|
val notificationManager = context.getSystemService(NotificationManager::class.java)
|
|
val shadowNotificationManager = shadowOf(notificationManager)
|
|
|
|
NotificationHelper.createNotificationChannelsForAccount(accountEntity, context)
|
|
|
|
runInBackground {
|
|
val notification = NotificationHelper.makeBaseNotification(
|
|
context,
|
|
notificationManager,
|
|
Notification(
|
|
type = type,
|
|
id = "id",
|
|
account = TimelineAccount(
|
|
id = "1",
|
|
localUsername = "connyduck",
|
|
username = "connyduck@mastodon.example",
|
|
displayName = "Conny Duck",
|
|
note = "This is their bio",
|
|
url = "https://mastodon.example/@ConnyDuck",
|
|
avatar = "https://mastodon.example/system/accounts/avatars/000/150/486/original/ab27d7ddd18a10ea.jpg"
|
|
),
|
|
status = null,
|
|
report = null
|
|
),
|
|
accountEntity
|
|
)
|
|
notificationManager.notify("id", 1, notification)
|
|
}
|
|
|
|
val notification = shadowNotificationManager.allNotifications.first()
|
|
return shadowOf(notification.contentIntent).savedIntent
|
|
}
|
|
|
|
private fun startMainActivity(intent: Intent): Activity {
|
|
val controller = Robolectric.buildActivity(MainActivity::class.java, intent)
|
|
val activity = controller.get()
|
|
activity.eventHub = EventHub()
|
|
activity.accountManager = mock {
|
|
on { activeAccount } doReturn accountEntity
|
|
}
|
|
activity.draftsAlert = mock {}
|
|
activity.shareShortcutHelper = mock {}
|
|
activity.externalScope = TestScope()
|
|
activity.mastodonApi = mock {
|
|
onBlocking { accountVerifyCredentials() } doReturn NetworkResult.success(account)
|
|
onBlocking { announcements() } doReturn NetworkResult.success(emptyList())
|
|
}
|
|
activity.preferences = mock(defaultAnswer = {
|
|
when (it.method.returnType) {
|
|
String::class.java -> "test"
|
|
Boolean::class.java -> false
|
|
else -> null
|
|
}
|
|
})
|
|
controller.create().start()
|
|
return activity
|
|
}
|
|
}
|