Merge pull request #2596 from element-hq/feature/bma/troubleshootNotification
Troubleshoot notifications screen
This commit is contained in:
commit
f4076dcb7c
235 changed files with 4554 additions and 708 deletions
|
|
@ -65,6 +65,7 @@ dependencies {
|
|||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
testImplementation(projects.features.login.impl)
|
||||
testImplementation(projects.tests.testutils)
|
||||
|
|
|
|||
|
|
@ -22,13 +22,10 @@ import app.cash.turbine.test
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.libraries.push.test.FakePushService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -73,20 +70,7 @@ class LoggedInPresenterTest {
|
|||
return LoggedInPresenter(
|
||||
matrixClient = FakeMatrixClient(roomListService = roomListService),
|
||||
networkMonitor = FakeNetworkMonitor(networkStatus),
|
||||
pushService = object : PushService {
|
||||
override fun notificationStyleChanged() {
|
||||
}
|
||||
|
||||
override fun getAvailablePushProviders(): List<PushProvider> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributor: Distributor) {
|
||||
}
|
||||
|
||||
override suspend fun testPush() {
|
||||
}
|
||||
}
|
||||
pushService = FakePushService(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
changelog.d/2601.feature
Normal file
1
changelog.d/2601.feature
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add a notification troubleshoot screen
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invites_decline_chat_message">"Вы ўпэўненыя, што жадаеце адхіліць запрашэнне ў %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_message">"Вы ўпэўненыя, што хочаце адхіліць запрашэнне ў %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Адхіліць запрашэнне"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Вы ўпэўненыя, што жадаеце адмовіцца ад прыватных зносін з %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Вы ўпэўненыя, што хочаце адмовіцца ад прыватных зносін з %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Адхіліць чат"</string>
|
||||
<string name="screen_invites_empty_list">"Няма запрашэнняў"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) запрасіў вас"</string>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Вы ўпэўнены, што хочаце пакінуць гэту размову? Гэта размова не з\'яўляецца публічнай, і вы не зможаце далучыцца зноў без запрашэння."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Вы ўпэўнены, што жадаеце пакінуць гэты пакой? Вы тут адзіны карыстальнік. Калі вы выйдзеце, ніхто не зможа далучыцца ў будучыні, у тым ліку і вы."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Вы ўпэўнены, што жадаеце пакінуць гэты пакой? Гэты пакой не агульнадаступны, і вы не зможаце далучыцца да яго зноў без запрашэння."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Вы ўпэўнены, што хочаце пакінуць гэты пакой? Вы тут адзіны карыстальнік. Калі вы выйдзеце, ніхто не зможа далучыцца ў будучыні, у тым ліку і вы."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Вы ўпэўнены, што жхочаце пакінуць гэты пакой? Гэты пакой не агульнадаступны, і вы не зможаце далучыцца да яго зноў без запрашэння."</string>
|
||||
<string name="leave_room_alert_subtitle">"Вы ўпэўнены, што хочаце пакінуць пакой?"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="screen_app_lock_settings_change_pin">"Змяніць PIN-код"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Дазволіць біяметрычную разблакіроўку"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"Выдаліць PIN-код"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Вы ўпэўнены, што жадаеце выдаліць PIN-код?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Вы ўпэўнены, што хочаце выдаліць PIN-код?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"Выдаліць PIN-код?"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Дазволіць %1$s"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"Я хацеў бы выкарыстоўваць PIN-код"</string>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_app_lock_settings_change_pin">"Byt PIN-kod"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Tillåt biometrisk upplåsning"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"Ta bort PIN-kod"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"Ta bort PIN-koden?"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Loggar ut …"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="emoji_picker_category_places">"Падарожжы & Месцы"</string>
|
||||
<string name="emoji_picker_category_symbols">"Сімвалы"</string>
|
||||
<string name="screen_report_content_block_user">"Заблакіраваць карыстальніка"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Адзначце, ці жадаеце вы схаваць усе бягучыя і будучыя паведамленні ад гэтага карыстальніка"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Адзначце, ці хочаце вы схаваць усе бягучыя і будучыя паведамленні ад гэтага карыстальніка"</string>
|
||||
<string name="screen_report_content_explanation">"Гэтае паведамленне будзе перададзена адміністратару вашага хатняга сервера. Яны не змогуць прачытаць зашыфраваныя паведамленні."</string>
|
||||
<string name="screen_report_content_hint">"Прычына, па якой вы паскардзіліся на гэты змест"</string>
|
||||
<string name="screen_room_attachment_source_camera">"Камера"</string>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<string name="screen_create_poll_anonymous_desc">"Паказаць вынікі толькі пасля заканчэння апытання"</string>
|
||||
<string name="screen_create_poll_anonymous_headline">"Схаваць галасы"</string>
|
||||
<string name="screen_create_poll_answer_hint">"Варыянт %1$d"</string>
|
||||
<string name="screen_create_poll_cancel_confirmation_content_android">"Вашы змены не былі захаваны. Вы ўпэўнены, што жадаеце вярнуцца?"</string>
|
||||
<string name="screen_create_poll_cancel_confirmation_content_android">"Вашы змены не былі захаваны. Вы ўпэўнены, што хочаце вярнуцца?"</string>
|
||||
<string name="screen_create_poll_question_desc">"Пытанне або тэма"</string>
|
||||
<string name="screen_create_poll_question_hint">"Пра што апытанне?"</string>
|
||||
<string name="screen_create_poll_title">"Стварэнне апытання"</string>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace = "io.element.android.features.preferences.impl"
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anvil {
|
||||
|
|
@ -44,12 +49,14 @@ dependencies {
|
|||
implementation(projects.libraries.pushstore.api)
|
||||
implementation(projects.libraries.indicator.api)
|
||||
implementation(projects.libraries.preferences.api)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.mediapickers.api)
|
||||
implementation(projects.libraries.mediaupload.api)
|
||||
implementation(projects.libraries.permissions.api)
|
||||
implementation(projects.libraries.push.api)
|
||||
implementation(projects.features.rageshake.api)
|
||||
implementation(projects.features.lockscreen.api)
|
||||
implementation(projects.features.analytics.api)
|
||||
|
|
@ -71,12 +78,14 @@ dependencies {
|
|||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(libs.test.mockk)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.mediapickers.test)
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.libraries.pushstore.test)
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
testImplementation(projects.features.rageshake.impl)
|
||||
|
|
@ -86,4 +95,6 @@ dependencies {
|
|||
testImplementation(projects.services.toolbox.test)
|
||||
testImplementation(projects.features.analytics.impl)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.bumble.appyx.core.node.Node
|
|||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.pop
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -47,6 +48,7 @@ import io.element.android.libraries.architecture.createNode
|
|||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
|
|
@ -54,6 +56,7 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
@Assisted buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val lockScreenEntryPoint: LockScreenEntryPoint,
|
||||
private val notificationTroubleShootEntryPoint: NotificationTroubleShootEntryPoint,
|
||||
private val logoutEntryPoint: LogoutEntryPoint,
|
||||
) : BaseFlowNode<PreferencesFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
|
|
@ -85,6 +88,9 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
@Parcelize
|
||||
data object NotificationSettings : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object TroubleshootNotifications : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data object LockScreenSettings : NavTarget
|
||||
|
||||
|
|
@ -177,9 +183,22 @@ class PreferencesFlowNode @AssistedInject constructor(
|
|||
override fun editDefaultNotificationMode(isOneToOne: Boolean) {
|
||||
backstack.push(NavTarget.EditDefaultNotificationSetting(isOneToOne))
|
||||
}
|
||||
|
||||
override fun onTroubleshootNotificationsClicked() {
|
||||
backstack.push(NavTarget.TroubleshootNotifications)
|
||||
}
|
||||
}
|
||||
createNode<NotificationSettingsNode>(buildContext, listOf(notificationSettingsCallback))
|
||||
}
|
||||
NavTarget.TroubleshootNotifications -> {
|
||||
notificationTroubleShootEntryPoint.nodeBuilder(this, buildContext)
|
||||
.callback(object : NotificationTroubleShootEntryPoint.Callback {
|
||||
override fun onDone() {
|
||||
backstack.pop()
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
||||
is NavTarget.EditDefaultNotificationSetting -> {
|
||||
val callback = object : EditDefaultNotificationSettingNode.Callback {
|
||||
override fun openRoomNotificationSettings(roomId: RoomId) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class NotificationSettingsNode @AssistedInject constructor(
|
|||
) : Node(buildContext, plugins = plugins) {
|
||||
interface Callback : Plugin {
|
||||
fun editDefaultNotificationMode(isOneToOne: Boolean)
|
||||
fun onTroubleshootNotificationsClicked()
|
||||
}
|
||||
|
||||
private val callbacks = plugins<Callback>()
|
||||
|
|
@ -43,6 +44,10 @@ class NotificationSettingsNode @AssistedInject constructor(
|
|||
callbacks.forEach { it.editDefaultNotificationMode(isOneToOne) }
|
||||
}
|
||||
|
||||
private fun onTroubleshootNotificationsClicked() {
|
||||
callbacks.forEach { it.onTroubleshootNotificationsClicked() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
|
|
@ -50,6 +55,7 @@ class NotificationSettingsNode @AssistedInject constructor(
|
|||
state = state,
|
||||
onOpenEditDefault = { openEditDefault(isOneToOne = it) },
|
||||
onBackPressed = ::navigateUp,
|
||||
onTroubleshootNotificationsClicked = ::onTroubleshootNotificationsClicked,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
|||
) : Presenter<NotificationSettingsState> {
|
||||
@Composable
|
||||
override fun present(): NotificationSettingsState {
|
||||
val userPushStore = remember { userPushStoreFactory.create(matrixClient.sessionId) }
|
||||
val userPushStore = remember { userPushStoreFactory.getOrCreate(matrixClient.sessionId) }
|
||||
val systemNotificationsEnabled: MutableState<Boolean> = remember {
|
||||
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,26 +23,48 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
|||
open class NotificationSettingsStateProvider : PreviewParameterProvider<NotificationSettingsState> {
|
||||
override val values: Sequence<NotificationSettingsState>
|
||||
get() = sequenceOf(
|
||||
aNotificationSettingsState(),
|
||||
aNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
|
||||
aNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))),
|
||||
aValidNotificationSettingsState(),
|
||||
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Loading),
|
||||
aValidNotificationSettingsState(changeNotificationSettingAction = AsyncAction.Failure(Throwable("error"))),
|
||||
aInvalidNotificationSettingsState(),
|
||||
aInvalidNotificationSettingsState(fixFailed = true),
|
||||
)
|
||||
}
|
||||
|
||||
fun aNotificationSettingsState(
|
||||
fun aValidNotificationSettingsState(
|
||||
changeNotificationSettingAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
atRoomNotificationsEnabled: Boolean = true,
|
||||
callNotificationsEnabled: Boolean = true,
|
||||
inviteForMeNotificationsEnabled: Boolean = true,
|
||||
appNotificationEnabled: Boolean = true,
|
||||
eventSink: (NotificationSettingsEvents) -> Unit = {},
|
||||
) = NotificationSettingsState(
|
||||
matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
|
||||
atRoomNotificationsEnabled = true,
|
||||
callNotificationsEnabled = true,
|
||||
inviteForMeNotificationsEnabled = true,
|
||||
atRoomNotificationsEnabled = atRoomNotificationsEnabled,
|
||||
callNotificationsEnabled = callNotificationsEnabled,
|
||||
inviteForMeNotificationsEnabled = inviteForMeNotificationsEnabled,
|
||||
defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||
defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES,
|
||||
),
|
||||
appSettings = NotificationSettingsState.AppSettings(
|
||||
systemNotificationsEnabled = false,
|
||||
appNotificationsEnabled = true,
|
||||
appNotificationsEnabled = appNotificationEnabled,
|
||||
),
|
||||
changeNotificationSettingAction = changeNotificationSettingAction,
|
||||
eventSink = {}
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
fun aInvalidNotificationSettingsState(
|
||||
fixFailed: Boolean = false,
|
||||
eventSink: (NotificationSettingsEvents) -> Unit = {},
|
||||
) = NotificationSettingsState(
|
||||
matrixSettings = NotificationSettingsState.MatrixSettings.Invalid(
|
||||
fixFailed = fixFailed,
|
||||
),
|
||||
appSettings = NotificationSettingsState.AppSettings(
|
||||
systemNotificationsEnabled = false,
|
||||
appNotificationsEnabled = true,
|
||||
),
|
||||
changeNotificationSettingAction = AsyncAction.Uninitialized,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
|||
fun NotificationSettingsView(
|
||||
state: NotificationSettingsState,
|
||||
onOpenEditDefault: (isOneToOne: Boolean) -> Unit,
|
||||
onTroubleshootNotificationsClicked: () -> Unit,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -77,6 +78,7 @@ fun NotificationSettingsView(
|
|||
// TODO We are removing the call notification toggle until support for call notifications has been added
|
||||
// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
|
||||
onInviteForMeNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(it)) },
|
||||
onTroubleshootNotificationsClicked = onTroubleshootNotificationsClicked,
|
||||
)
|
||||
}
|
||||
AsyncActionView(
|
||||
|
|
@ -99,6 +101,7 @@ private fun NotificationSettingsContentView(
|
|||
// TODO We are removing the call notification toggle until support for call notifications has been added
|
||||
// onCallsNotificationsChanged: (Boolean) -> Unit,
|
||||
onInviteForMeNotificationsChanged: (Boolean) -> Unit,
|
||||
onTroubleshootNotificationsClicked: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) {
|
||||
|
|
@ -163,6 +166,13 @@ private fun NotificationSettingsContentView(
|
|||
onCheckedChange = onInviteForMeNotificationsChanged
|
||||
)
|
||||
}
|
||||
PreferenceCategory(title = stringResource(id = R.string.troubleshoot_notifications_entry_point_section)) {
|
||||
PreferenceText(
|
||||
modifier = Modifier,
|
||||
title = stringResource(id = R.string.troubleshoot_notifications_entry_point_title),
|
||||
onClick = onTroubleshootNotificationsClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,15 +214,6 @@ internal fun NotificationSettingsViewPreview(@PreviewParameter(NotificationSetti
|
|||
state = state,
|
||||
onBackPressed = {},
|
||||
onOpenEditDefault = {},
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun InvalidNotificationSettingsViewPreview() = ElementPreview {
|
||||
InvalidNotificationSettingsView(
|
||||
showError = false,
|
||||
onContinueClicked = {},
|
||||
onDismissError = {},
|
||||
onTroubleshootNotificationsClicked = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"налады сістэмы"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Сістэмныя апавяшчэнні выключаны"</string>
|
||||
<string name="screen_notification_settings_title">"Апавяшчэнні"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Выпраўленне непаладак"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Выпраўленне непаладак з апавяшчэннямі"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -51,4 +51,6 @@ Pokud budete pokračovat, některá nastavení se mohou změnit."</string>
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"systémová nastavení"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Systémová oznámení byla vypnuta"</string>
|
||||
<string name="screen_notification_settings_title">"Oznámení"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Odstraňování problémů"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Odstraňování problémů s upozorněními"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@ Wenn du fortfährst, können sich einige deiner Einstellungen ändern."</string>
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"Systemeinstellungen"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Systembenachrichtigungen deaktiviert"</string>
|
||||
<string name="screen_notification_settings_title">"Benachrichtigungen"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Fehlerbehebung"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Fehlerbehebung für Benachrichtigungen"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"paramètres du système"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Les notifications du système sont désactivées"</string>
|
||||
<string name="screen_notification_settings_title">"Notifications"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Dépannage"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Résoudre les problèmes liés aux notifications"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@ Ha folytatja, egyes beállítások megváltozhatnak."</string>
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"rendszerbeállításokat"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"A rendszerértesítések ki vannak kapcsolva"</string>
|
||||
<string name="screen_notification_settings_title">"Értesítések"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Hibaelhárítás"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Értesítések hibaelhárítása"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -51,4 +51,6 @@ Jika Anda melanjutkan, beberapa pengaturan Anda dapat berubah."</string>
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"pengaturan sistem"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Pemberitahuan sistem dimatikan"</string>
|
||||
<string name="screen_notification_settings_title">"Notifikasi"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Pemecahan masalah"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Pecahkan masalah notifikasi"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"настройки системы"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Системные уведомления выключены"</string>
|
||||
<string name="screen_notification_settings_title">"Уведомления"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Устранение неполадок"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Уведомления об устранении неполадок"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -51,4 +51,6 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť."</stri
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"nastavenia systému"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"Systémové oznámenia sú vypnuté"</string>
|
||||
<string name="screen_notification_settings_title">"Oznámenia"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Riešenie problémov"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Oznámenia riešení problémov"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,4 +49,6 @@ If you proceed, some of your settings may change."</string>
|
|||
<string name="screen_notification_settings_system_notifications_action_required_content_link">"system settings"</string>
|
||||
<string name="screen_notification_settings_system_notifications_turned_off">"System notifications turned off"</string>
|
||||
<string name="screen_notification_settings_title">"Notifications"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_section">"Troubleshoot"</string>
|
||||
<string name="troubleshoot_notifications_entry_point_title">"Troubleshoot notifications"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.features.preferences.impl.notifications
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.ensureCalledOnceWithParam
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class NotificationSettingsViewTest {
|
||||
@get:Rule
|
||||
val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun `clicking on back invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
ensureCalledOnce {
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onBackPressed = it
|
||||
)
|
||||
rule.pressBack()
|
||||
}
|
||||
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on troubleshoot notification invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
ensureCalledOnce {
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onTroubleshootNotificationsClicked = it
|
||||
)
|
||||
rule.clickOn(R.string.troubleshoot_notifications_entry_point_title)
|
||||
}
|
||||
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on group chats invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
ensureCalledOnceWithParam(false) {
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onOpenEditDefault = it
|
||||
)
|
||||
rule.clickOn(R.string.screen_notification_settings_group_chats)
|
||||
}
|
||||
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on direct chats invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
ensureCalledOnceWithParam(true) {
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onOpenEditDefault = it
|
||||
)
|
||||
rule.clickOn(R.string.screen_notification_settings_direct_chats)
|
||||
}
|
||||
eventsRecorder.assertSingle(NotificationSettingsEvents.RefreshSystemNotificationsEnabled)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on disable notifications emits the expected events`() {
|
||||
testNotificationToggle(true)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on enable notifications emits the expected events`() {
|
||||
testNotificationToggle(false)
|
||||
}
|
||||
|
||||
private fun testNotificationToggle(initialState: Boolean) {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
appNotificationEnabled = initialState,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_notification_settings_enable_notifications)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.SetNotificationsEnabled(!initialState)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on disable notify me on at room emits the expected events`() {
|
||||
testAtRoomToggle(true)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on enable notify me on at room emits the expected events`() {
|
||||
testAtRoomToggle(false)
|
||||
}
|
||||
|
||||
private fun testAtRoomToggle(initialState: Boolean) {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
atRoomNotificationsEnabled = initialState,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_notification_settings_room_mention_label)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.SetAtRoomNotificationsEnabled(!initialState)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on disable notify me on invitation emits the expected events`() {
|
||||
testInvitationToggle(true)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `clicking on enable notify me on invitation emits the expected events`() {
|
||||
testInvitationToggle(false)
|
||||
}
|
||||
|
||||
private fun testInvitationToggle(initialState: Boolean) {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
inviteForMeNotificationsEnabled = initialState,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_notification_settings_invite_for_me_label)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(!initialState)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `with an error configuration, clicking on continue emits the expected events`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aValidNotificationSettingsState(
|
||||
changeNotificationSettingAction = AsyncAction.Failure(AN_EXCEPTION),
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.ClearNotificationChangeError
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `with invalid configuration, clicking on continue emits the expected events`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aInvalidNotificationSettingsState(
|
||||
fixFailed = false,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_continue)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.FixConfigurationMismatch
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Config(qualifiers = "h1024dp")
|
||||
@Test
|
||||
fun `with invalid configuration and error, clicking on OK emits the expected events`() {
|
||||
val eventsRecorder = EventsRecorder<NotificationSettingsEvents>()
|
||||
rule.setNotificationSettingsView(
|
||||
state = aInvalidNotificationSettingsState(
|
||||
fixFailed = true,
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled,
|
||||
NotificationSettingsEvents.ClearConfigurationMismatchError
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setNotificationSettingsView(
|
||||
state: NotificationSettingsState,
|
||||
onOpenEditDefault: (isOneToOne: Boolean) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onTroubleshootNotificationsClicked: () -> Unit = EnsureNeverCalled(),
|
||||
onBackPressed: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
NotificationSettingsView(
|
||||
state = state,
|
||||
onOpenEditDefault = onOpenEditDefault,
|
||||
onTroubleshootNotificationsClicked = onTroubleshootNotificationsClicked,
|
||||
onBackPressed = onBackPressed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,25 +23,31 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.Test
|
||||
|
||||
class VersionFormatterTest {
|
||||
@Test
|
||||
fun `version formatter should return simplified version for other branch`() = runTest {
|
||||
val sut = DefaultVersionFormatter(
|
||||
stringProvider = FakeStringProvider(defaultResult = VERSION),
|
||||
buildMeta = aBuildMeta(gitBranchName = "main")
|
||||
)
|
||||
assertThat(sut.get()).isEqualTo(VERSION)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `version formatter should return simplified version for main branch`() = runTest {
|
||||
val sut = DefaultVersionFormatter(
|
||||
stringProvider = FakeStringProvider(defaultResult = VERSION),
|
||||
buildMeta = aBuildMeta(
|
||||
gitBranchName = "main",
|
||||
versionName = "versionName",
|
||||
versionCode = 123
|
||||
)
|
||||
)
|
||||
assertThat(sut.get()).isEqualTo("${VERSION}versionName, 123")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `version formatter should return simplified version for other branch`() = runTest {
|
||||
val sut = DefaultVersionFormatter(
|
||||
stringProvider = FakeStringProvider(defaultResult = VERSION),
|
||||
buildMeta = aBuildMeta(
|
||||
versionName = "versionName",
|
||||
versionCode = 123,
|
||||
gitBranchName = "branch",
|
||||
gitRevision = "1234567890",
|
||||
)
|
||||
)
|
||||
assertThat(sut.get()).isEqualTo("$VERSION\nbranch (1234567890)")
|
||||
assertThat(sut.get()).isEqualTo("${VERSION}versionName, 123\nbranch (1234567890)")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="crash_detection_dialog_content">"Пры апошнім выкарыстанні %1$s адбыўся збой. Жадаеце падзяліцца справаздачай аб збоі?"</string>
|
||||
<string name="rageshake_detection_dialog_content">"Падобна, што вы трасеце тэлефон. Жадаеце адкрыць экран паведамлення пра памылку?"</string>
|
||||
<string name="crash_detection_dialog_content">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"</string>
|
||||
<string name="rageshake_detection_dialog_content">"Падобна, што вы трасеце тэлефон. Хочаце адкрыць экран паведамлення пра памылку?"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Парог выяўлення"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,6 @@
|
|||
<string name="screen_bug_report_include_logs">"Дазволіць журналы"</string>
|
||||
<string name="screen_bug_report_include_screenshot">"Адправіць здымак экрана"</string>
|
||||
<string name="screen_bug_report_logs_description">"Каб пераканацца, што ўсё працуе правільна, у паведамленне будуць уключаны часопісы. Каб адправіць паведамленне без часопісаў, адключыце гэтую наладу."</string>
|
||||
<string name="screen_bug_report_rash_logs_alert_title">"Пры апошнім выкарыстанні %1$s адбыўся збой. Жадаеце падзяліцца справаздачай аб збоі?"</string>
|
||||
<string name="screen_bug_report_rash_logs_alert_title">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"</string>
|
||||
<string name="screen_bug_report_view_logs">"Прагляд журналаў"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<string name="screen_room_change_role_confirm_demote_self_description">"Вы не зможаце адмяніць гэтае змяненне, бо паніжаеце сябе. Калі вы апошні адміністратар у пакоі, вярнуць права будзе немагчыма."</string>
|
||||
<string name="screen_room_change_role_confirm_demote_self_title">"Панізіць сябе?"</string>
|
||||
<string name="screen_room_change_role_invited_member_name">"%1$s (У чаканні)"</string>
|
||||
<string name="screen_room_change_role_invited_member_name_android">"(У чаканні)"</string>
|
||||
<string name="screen_room_change_role_moderators_title">"Рэдагаваць мадэратараў"</string>
|
||||
<string name="screen_room_change_role_section_administrators">"Адміністратары"</string>
|
||||
<string name="screen_room_change_role_section_moderators">"Мадэратары"</string>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<string name="screen_room_change_role_confirm_demote_self_description">"Vous ne pourrez pas annuler ce changement car vous vous rétrogradez, si vous êtes le dernier utilisateur privilégié du salon il sera impossible de retrouver les privilèges."</string>
|
||||
<string name="screen_room_change_role_confirm_demote_self_title">"Vous rétrograder ?"</string>
|
||||
<string name="screen_room_change_role_invited_member_name">"%1$s (En attente)"</string>
|
||||
<string name="screen_room_change_role_invited_member_name_android">"(En attente)"</string>
|
||||
<string name="screen_room_change_role_moderators_title">"Modifier les modérateurs"</string>
|
||||
<string name="screen_room_change_role_section_administrators">"Administrateurs"</string>
|
||||
<string name="screen_room_change_role_section_moderators">"Modérateurs"</string>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<string name="screen_room_change_role_confirm_demote_self_description">"You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges."</string>
|
||||
<string name="screen_room_change_role_confirm_demote_self_title">"Demote yourself?"</string>
|
||||
<string name="screen_room_change_role_invited_member_name">"%1$s (Pending)"</string>
|
||||
<string name="screen_room_change_role_invited_member_name_android">"(Pending)"</string>
|
||||
<string name="screen_room_change_role_moderators_title">"Edit Moderators"</string>
|
||||
<string name="screen_room_change_role_section_administrators">"Admins"</string>
|
||||
<string name="screen_room_change_role_section_moderators">"Moderators"</string>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<string name="screen_key_backup_disable_description">"Адключэнне рэзервовага капіравання прывядзе да выдалення бягучай рэзервовай копіі ключа шыфравання і адключэння іншых функцый бяспекі. У гэтым выпадку вы:"</string>
|
||||
<string name="screen_key_backup_disable_description_point_1">"Няма зашыфраванай гісторыі паведамленняў на новых прыладах"</string>
|
||||
<string name="screen_key_backup_disable_description_point_2">"Калі вы выходзіце з сістэмы, то губляеце доступ да зашыфраваных паведамленняў %1$s усюды"</string>
|
||||
<string name="screen_key_backup_disable_title">"Вы ўпэўнены, што жадаеце адключыць рэзервовае капіраванне?"</string>
|
||||
<string name="screen_key_backup_disable_title">"Вы ўпэўнены, што хочаце адключыць рэзервовае капіраванне?"</string>
|
||||
<string name="screen_recovery_key_change_description">"Атрымайце новы ключ аднаўлення, калі вы страцілі існуючы. Пасля змены ключа аднаўлення ваш стары больш не будзе працаваць."</string>
|
||||
<string name="screen_recovery_key_change_generate_key">"Стварыць новы ключ аднаўлення"</string>
|
||||
<string name="screen_recovery_key_change_generate_key_description">"Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы"</string>
|
||||
|
|
|
|||
|
|
@ -177,7 +177,9 @@ kotlinpoet = "com.squareup:kotlinpoet:1.16.0"
|
|||
# Analytics
|
||||
posthog = "com.posthog:posthog-android:3.1.16"
|
||||
sentry = "io.sentry:sentry-android:7.6.0"
|
||||
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.14.0"
|
||||
# Note: only 0.19.0 will compile properly
|
||||
# main branch can be tested replacing the version with main-SNAPSHOT
|
||||
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.15.0"
|
||||
|
||||
# Emojibase
|
||||
matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.1.3"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package io.element.android.libraries.androidutils.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
|
@ -73,6 +74,9 @@ fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResu
|
|||
val intent = Intent()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
if (this !is Activity && activityResultLauncher == null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
||||
} else {
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
|
|
@ -154,6 +158,9 @@ fun Context.openUrlInExternalApp(
|
|||
errorMessage: String = getString(R.string.error_no_compatible_app_found),
|
||||
) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
if (this !is Activity) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
try {
|
||||
startActivity(intent)
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,10 @@ dependencies {
|
|||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.services.toolbox.api)
|
||||
api(projects.libraries.permissions.api)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
|
|
@ -57,6 +59,7 @@ dependencies {
|
|||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.permissions.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package io.element.android.libraries.permissions.impl
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
|
|
@ -33,7 +35,10 @@ class DefaultPermissionStateProvider @Inject constructor(
|
|||
private val permissionsStore: PermissionsStore,
|
||||
) : PermissionStateProvider {
|
||||
override fun isPermissionGranted(permission: String): Boolean {
|
||||
return context.checkSelfPermission(permission) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||
return ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
permission,
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
override suspend fun setPermissionDenied(permission: String, value: Boolean) = permissionsStore.setPermissionDenied(permission, value)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package io.element.android.libraries.permissions.impl.action
|
|||
|
||||
import android.content.Context
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.system.openAppSettingsPage
|
||||
import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
|
|
@ -28,6 +28,6 @@ class AndroidPermissionActions @Inject constructor(
|
|||
@ApplicationContext private val context: Context
|
||||
) : PermissionActions {
|
||||
override fun openSettings() {
|
||||
context.openAppSettingsPage()
|
||||
context.startNotificationSettingsIntent()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.permissions.impl.troubleshoot
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
||||
import io.element.android.libraries.permissions.impl.R
|
||||
import io.element.android.libraries.permissions.impl.action.PermissionActions
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class NotificationTroubleshootCheckPermissionTest @Inject constructor(
|
||||
private val permissionStateProvider: PermissionStateProvider,
|
||||
private val sdkVersionProvider: BuildVersionSdkIntProvider,
|
||||
private val permissionActions: PermissionActions,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order: Int = 0
|
||||
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_check_permission_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_check_permission_description),
|
||||
hasQuickFix = true,
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val result = if (sdkVersionProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
||||
permissionStateProvider.isPermissionGranted(Manifest.permission.POST_NOTIFICATIONS)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
delegate.done(result)
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
|
||||
override suspend fun quickFix(coroutineScope: CoroutineScope) {
|
||||
// Do not bother about asking the permission inline, just lead the user to the settings
|
||||
permissionActions.openSettings()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Пераканайцеся, што праграма можа паказваць апавяшчэнні."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Праверце дазволы"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Ujistěte se, že aplikace může zobrazovat oznámení."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Kontrola oprávnění"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Stelle sicher, dass die Anwendung Benachrichtigungen anzeigen kann."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Berechtigungen überprüfen"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Vérifie que l’application peut afficher des notifications."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Vérifier les autorisations"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Ellenőrizze, hogy az alkalmazás képes-e értesítéseket megjeleníteni."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Engedélyek ellenőrzése"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Убедитесь, что приложение может показывать уведомления."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Проверка разрешений"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Uistite sa, že aplikácia dokáže zobrazovať upozornenia."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Skontrolovať povolenia"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_check_permission_description">"Check that the application can show notifications."</string>
|
||||
<string name="troubleshoot_notifications_test_check_permission_title">"Check permissions"</string>
|
||||
</resources>
|
||||
|
|
@ -16,11 +16,14 @@
|
|||
|
||||
package io.element.android.libraries.permissions.impl.action
|
||||
|
||||
class FakePermissionActions : PermissionActions {
|
||||
class FakePermissionActions(
|
||||
val openSettingsAction: () -> Unit = {}
|
||||
) : PermissionActions {
|
||||
var openSettingsCalled = false
|
||||
private set
|
||||
|
||||
override fun openSettings() {
|
||||
openSettingsAction()
|
||||
openSettingsCalled = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.permissions.impl.troubleshoot
|
||||
|
||||
import android.os.Build
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.permissions.impl.FakePermissionStateProvider
|
||||
import io.element.android.libraries.permissions.impl.action.FakePermissionActions
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class NotificationTroubleshootCheckPermissionTestTest {
|
||||
@Test
|
||||
fun `test NotificationTroubleshootCheckPermissionTest below TIRAMISU success`() = runTest {
|
||||
val sut = NotificationTroubleshootCheckPermissionTest(
|
||||
permissionStateProvider = FakePermissionStateProvider(),
|
||||
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkInt = Build.VERSION_CODES.TIRAMISU - 1),
|
||||
permissionActions = FakePermissionActions(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test NotificationTroubleshootCheckPermissionTest TIRAMISU success`() = runTest {
|
||||
val sut = NotificationTroubleshootCheckPermissionTest(
|
||||
permissionStateProvider = FakePermissionStateProvider(),
|
||||
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkInt = Build.VERSION_CODES.TIRAMISU),
|
||||
permissionActions = FakePermissionActions(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test NotificationTroubleshootCheckPermissionTest TIRAMISU error`() = runTest {
|
||||
val permissionStateProvider = FakePermissionStateProvider(
|
||||
permissionGranted = false
|
||||
)
|
||||
val actions = FakePermissionActions(
|
||||
openSettingsAction = {
|
||||
permissionStateProvider.setPermissionGranted()
|
||||
}
|
||||
)
|
||||
val sut = NotificationTroubleshootCheckPermissionTest(
|
||||
permissionStateProvider = permissionStateProvider,
|
||||
sdkVersionProvider = FakeBuildVersionSdkIntProvider(sdkInt = Build.VERSION_CODES.TIRAMISU),
|
||||
permissionActions = actions,
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(true))
|
||||
// Quick fix
|
||||
launch {
|
||||
sut.quickFix(this)
|
||||
// Run the test again (IRL it will be done thanks to the resuming of the application)
|
||||
sut.run(this)
|
||||
}
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.api
|
||||
|
||||
interface GetCurrentPushProvider {
|
||||
suspend fun getCurrentPushProvider(): String?
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ interface PushService {
|
|||
*/
|
||||
suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributor: Distributor)
|
||||
|
||||
// TODO Move away
|
||||
suspend fun testPush()
|
||||
/**
|
||||
* Return false in case of early error.
|
||||
*/
|
||||
suspend fun testPush(): Boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@
|
|||
|
||||
package io.element.android.libraries.push.api.gateway
|
||||
|
||||
sealed class PushGatewayFailure : Throwable(cause = null) {
|
||||
data object PusherRejected : PushGatewayFailure()
|
||||
sealed class PushGatewayFailure : Exception() {
|
||||
class PusherRejected : PushGatewayFailure()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ dependencies {
|
|||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
api(projects.libraries.pushproviders.api)
|
||||
api(projects.libraries.pushstore.api)
|
||||
api(projects.libraries.push.api)
|
||||
|
|
@ -69,6 +70,8 @@ dependencies {
|
|||
testImplementation(libs.coil.test)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.libraries.pushproviders.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
testImplementation(projects.services.toolbox.impl)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import io.element.android.services.appnavstate.api.currentSessionId
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultGetCurrentPushProvider @Inject constructor(
|
||||
private val pushStoreFactory: UserPushStoreFactory,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
) : GetCurrentPushProvider {
|
||||
override suspend fun getCurrentPushProvider(): String? {
|
||||
return appNavigationStateService
|
||||
.appNavigationState
|
||||
.value
|
||||
.navigationState
|
||||
.currentSessionId()
|
||||
?.let { pushStoreFactory.getOrCreate(it) }
|
||||
?.getPushProviderName()
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ package io.element.android.libraries.push.impl
|
|||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
|
|
@ -32,6 +33,7 @@ class DefaultPushService @Inject constructor(
|
|||
private val pushersManager: PushersManager,
|
||||
private val userPushStoreFactory: UserPushStoreFactory,
|
||||
private val pushProviders: Set<@JvmSuppressWildcards PushProvider>,
|
||||
private val getCurrentPushProvider: GetCurrentPushProvider,
|
||||
) : PushService {
|
||||
override fun notificationStyleChanged() {
|
||||
defaultNotificationDrawerManager.notificationStyleChanged()
|
||||
|
|
@ -47,7 +49,7 @@ class DefaultPushService @Inject constructor(
|
|||
* Get current push provider, compare with provided one, then unregister and register if different, and store change.
|
||||
*/
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributor: Distributor) {
|
||||
val userPushStore = userPushStoreFactory.create(matrixClient.sessionId)
|
||||
val userPushStore = userPushStoreFactory.getOrCreate(matrixClient.sessionId)
|
||||
val currentPushProviderName = userPushStore.getPushProviderName()
|
||||
if (currentPushProviderName != pushProvider.name) {
|
||||
// Unregister previous one if any
|
||||
|
|
@ -58,7 +60,11 @@ class DefaultPushService @Inject constructor(
|
|||
userPushStore.setPushProviderName(pushProvider.name)
|
||||
}
|
||||
|
||||
override suspend fun testPush() {
|
||||
pushersManager.testPush()
|
||||
override suspend fun testPush(): Boolean {
|
||||
val currentPushProvider = getCurrentPushProvider.getCurrentPushProvider()
|
||||
val pushProvider = pushProviders.find { it.name == currentPushProvider } ?: return false
|
||||
val config = pushProvider.getCurrentUserPushConfig() ?: return false
|
||||
pushersManager.testPush(config)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ import io.element.android.libraries.core.meta.BuildMeta
|
|||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData
|
||||
import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest
|
||||
import io.element.android.libraries.pushproviders.api.CurrentUserPushConfig
|
||||
import io.element.android.libraries.pushproviders.api.PusherSubscriber
|
||||
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
|
||||
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
|
||||
|
|
@ -45,16 +47,14 @@ class PushersManager @Inject constructor(
|
|||
private val pushClientSecret: PushClientSecret,
|
||||
private val userPushStoreFactory: UserPushStoreFactory,
|
||||
) : PusherSubscriber {
|
||||
// TODO Move this to the PushProvider API
|
||||
suspend fun testPush() {
|
||||
suspend fun testPush(config: CurrentUserPushConfig) {
|
||||
pushGatewayNotifyRequest.execute(
|
||||
PushGatewayNotifyRequest.Params(
|
||||
// unifiedPushHelper.getPushGateway() ?: return
|
||||
url = "TODO",
|
||||
url = config.url,
|
||||
appId = PushConfig.PUSHER_APP_ID,
|
||||
// unifiedPushHelper.getEndpointOrToken().orEmpty()
|
||||
pushKey = "TODO",
|
||||
eventId = TEST_EVENT_ID
|
||||
pushKey = config.pushKey,
|
||||
eventId = TEST_EVENT_ID,
|
||||
roomId = TEST_ROOM_ID,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ class PushersManager @Inject constructor(
|
|||
* Register a pusher to the server if not done yet.
|
||||
*/
|
||||
override suspend fun registerPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) {
|
||||
val userDataStore = userPushStoreFactory.create(matrixClient.sessionId)
|
||||
val userDataStore = userPushStoreFactory.getOrCreate(matrixClient.sessionId)
|
||||
if (userDataStore.getCurrentRegisteredPushKey() == pushKey) {
|
||||
Timber.tag(loggerTag.value)
|
||||
.d("Unnecessary to register again the same pusher, but do it in case the pusher has been removed from the server")
|
||||
|
|
@ -112,5 +112,6 @@ class PushersManager @Inject constructor(
|
|||
|
||||
companion object {
|
||||
val TEST_EVENT_ID = EventId("\$THIS_IS_A_FAKE_EVENT_ID")
|
||||
val TEST_ROOM_ID = RoomId("!room:domain")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
|
|
@ -32,12 +31,13 @@ class NotificationDisplayer @Inject constructor(
|
|||
) {
|
||||
private val notificationManager = NotificationManagerCompat.from(context)
|
||||
|
||||
fun showNotificationMessage(tag: String?, id: Int, notification: Notification) {
|
||||
fun showNotificationMessage(tag: String?, id: Int, notification: Notification): Boolean {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
Timber.w("Not allowed to notify.")
|
||||
return
|
||||
return false
|
||||
}
|
||||
notificationManager.notify(tag, id, notification)
|
||||
return true
|
||||
}
|
||||
|
||||
fun cancelNotificationMessage(tag: String?, id: Int) {
|
||||
|
|
@ -53,15 +53,21 @@ class NotificationDisplayer @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("LaunchActivityFromNotification")
|
||||
fun displayDiagnosticNotification(notification: Notification) {
|
||||
showNotificationMessage(
|
||||
fun displayDiagnosticNotification(notification: Notification): Boolean {
|
||||
return showNotificationMessage(
|
||||
tag = "DIAGNOSTIC",
|
||||
id = NOTIFICATION_ID_DIAGNOSTIC,
|
||||
notification = notification
|
||||
)
|
||||
}
|
||||
|
||||
fun dismissDiagnosticNotification() {
|
||||
cancelNotificationMessage(
|
||||
tag = "DIAGNOSTIC",
|
||||
id = NOTIFICATION_ID_DIAGNOSTIC
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the foreground notification service.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,9 +19,15 @@ package io.element.android.libraries.push.impl.notifications
|
|||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.push.impl.troubleshoot.NotificationClickHandler
|
||||
import javax.inject.Inject
|
||||
|
||||
class TestNotificationReceiver : BroadcastReceiver() {
|
||||
@Inject lateinit var notificationClickHandler: NotificationClickHandler
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
// TODO The test notification has been clicked, notify the ui
|
||||
context.bindings<TestNotificationReceiverBinding>().inject(this)
|
||||
notificationClickHandler.handleNotificationClick()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.notifications
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.libraries.di.AppScope
|
||||
|
||||
@ContributesTo(AppScope::class)
|
||||
interface TestNotificationReceiverBinding {
|
||||
fun inject(service: TestNotificationReceiver)
|
||||
}
|
||||
|
|
@ -299,6 +299,7 @@ class NotificationCreator @Inject constructor(
|
|||
}
|
||||
|
||||
fun createDiagnosticNotification(): Notification {
|
||||
val intent = pendingIntentFactory.createTestPendingIntent()
|
||||
return NotificationCompat.Builder(context, notificationChannels.getChannelIdForTest())
|
||||
.setContentTitle(buildMeta.applicationName)
|
||||
.setContentText(stringProvider.getString(R.string.notification_test_push_notification_content))
|
||||
|
|
@ -308,7 +309,8 @@ class NotificationCreator @Inject constructor(
|
|||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setAutoCancel(true)
|
||||
.setContentIntent(pendingIntentFactory.createTestPendingIntent())
|
||||
.setContentIntent(intent)
|
||||
.setDeleteIntent(intent)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.push.impl.PushersManager
|
|||
import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager
|
||||
import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
|
||||
import io.element.android.libraries.push.impl.store.DefaultPushDataStore
|
||||
import io.element.android.libraries.push.impl.troubleshoot.DiagnosticPushHandler
|
||||
import io.element.android.libraries.pushproviders.api.PushData
|
||||
import io.element.android.libraries.pushproviders.api.PushHandler
|
||||
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
|
||||
|
|
@ -51,6 +52,7 @@ class DefaultPushHandler @Inject constructor(
|
|||
// private val actionIds: NotificationActionIds,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val matrixAuthenticationService: MatrixAuthenticationService,
|
||||
private val diagnosticPushHandler: DiagnosticPushHandler,
|
||||
) : PushHandler {
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob())
|
||||
|
||||
|
|
@ -75,8 +77,7 @@ class DefaultPushHandler @Inject constructor(
|
|||
|
||||
// Diagnostic Push
|
||||
if (pushData.eventId == PushersManager.TEST_EVENT_ID) {
|
||||
// val intent = Intent(actionIds.push)
|
||||
// TODO The test push has been received, notify the ui
|
||||
diagnosticPushHandler.handlePush()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +122,7 @@ class DefaultPushHandler @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
val userPushStore = userPushStoreFactory.create(userId)
|
||||
val userPushStore = userPushStoreFactory.getOrCreate(userId)
|
||||
if (!userPushStore.getNotificationEnabledForDevice().first()) {
|
||||
// TODO We need to check if this is an incoming call
|
||||
Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.")
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import kotlinx.serialization.Serializable
|
|||
internal data class PushGatewayNotification(
|
||||
@SerialName("event_id")
|
||||
val eventId: String,
|
||||
@SerialName("room_id")
|
||||
val roomId: String,
|
||||
/**
|
||||
* Required. This is an array of devices that the notification should be sent to.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package io.element.android.libraries.push.impl.pushgateway
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.network.RetrofitFactory
|
||||
import io.element.android.libraries.push.api.gateway.PushGatewayFailure
|
||||
import javax.inject.Inject
|
||||
|
|
@ -27,7 +28,8 @@ class PushGatewayNotifyRequest @Inject constructor(
|
|||
val url: String,
|
||||
val appId: String,
|
||||
val pushKey: String,
|
||||
val eventId: EventId
|
||||
val eventId: EventId,
|
||||
val roomId: RoomId,
|
||||
)
|
||||
|
||||
suspend fun execute(params: Params) {
|
||||
|
|
@ -40,6 +42,7 @@ class PushGatewayNotifyRequest @Inject constructor(
|
|||
PushGatewayNotifyBody(
|
||||
PushGatewayNotification(
|
||||
eventId = params.eventId.value,
|
||||
roomId = params.roomId.value,
|
||||
devices = listOf(
|
||||
PushGatewayDevice(
|
||||
params.appId,
|
||||
|
|
@ -51,7 +54,7 @@ class PushGatewayNotifyRequest @Inject constructor(
|
|||
)
|
||||
|
||||
if (response.rejectedPushKeys.contains(params.pushKey)) {
|
||||
throw PushGatewayFailure.PusherRejected
|
||||
throw PushGatewayFailure.PusherRejected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class CurrentPushProviderTest @Inject constructor(
|
||||
private val getCurrentPushProvider: GetCurrentPushProvider,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 110
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_description),
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val provider = getCurrentPushProvider.getCurrentPushProvider()
|
||||
if (provider != null) {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_success, provider),
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_current_push_provider_failure),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
class DiagnosticPushHandler @Inject constructor() {
|
||||
private val _state = MutableSharedFlow<Unit>()
|
||||
val state: SharedFlow<Unit> = _state
|
||||
|
||||
suspend fun handlePush() {
|
||||
_state.emit(Unit)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
class NotificationClickHandler @Inject constructor() {
|
||||
private val _state = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
|
||||
val state: SharedFlow<Unit> = _state
|
||||
|
||||
fun handleNotificationClick() {
|
||||
_state.tryEmit(Unit)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class NotificationTest @Inject constructor(
|
||||
private val notificationCreator: NotificationCreator,
|
||||
private val notificationDisplayer: NotificationDisplayer,
|
||||
private val notificationClickHandler: NotificationClickHandler,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 50
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_description),
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val notification = notificationCreator.createDiagnosticNotification()
|
||||
val result = notificationDisplayer.displayDiagnosticNotification(notification)
|
||||
if (result) {
|
||||
coroutineScope.listenToNotificationClick()
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_waiting),
|
||||
status = NotificationTroubleshootTestState.Status.WaitingForUser
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_permission_failure),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.listenToNotificationClick() = launch {
|
||||
val job = launch {
|
||||
notificationClickHandler.state.first()
|
||||
Timber.d("Notification clicked!")
|
||||
}
|
||||
runCatching {
|
||||
withTimeout(30.seconds) {
|
||||
job.join()
|
||||
}
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_success),
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
job.cancel()
|
||||
notificationDisplayer.dismissDiagnosticNotification()
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_display_notification_failure),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
)
|
||||
}.invokeOnCompletion {
|
||||
// Ensure that the notification is cancelled when the screen is left
|
||||
notificationDisplayer.dismissDiagnosticNotification()
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.push.api.gateway.PushGatewayFailure
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class PushLoopbackTest @Inject constructor(
|
||||
private val pushService: PushService,
|
||||
private val diagnosticPushHandler: DiagnosticPushHandler,
|
||||
private val clock: SystemClock,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
override val order = 500
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_description),
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val startTime = clock.epochMillis()
|
||||
val completable = CompletableDeferred<Long>()
|
||||
val job = coroutineScope.launch {
|
||||
diagnosticPushHandler.state.first()
|
||||
completable.complete(clock.epochMillis() - startTime)
|
||||
}
|
||||
val testPushResult = try {
|
||||
pushService.testPush()
|
||||
} catch (pusherRejected: PushGatewayFailure.PusherRejected) {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_1),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
job.cancel()
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to test push")
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_2, e.message),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
job.cancel()
|
||||
return
|
||||
}
|
||||
if (!testPushResult) {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_3),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
job.cancel()
|
||||
return
|
||||
}
|
||||
runCatching {
|
||||
withTimeout(10.seconds) {
|
||||
completable.await()
|
||||
}
|
||||
}.fold(
|
||||
onSuccess = { duration ->
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_success, duration),
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
job.cancel()
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_push_loop_back_failure_4),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.push.impl.R
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTest
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestDelegate
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(AppScope::class)
|
||||
class PushProvidersTest @Inject constructor(
|
||||
pushProviders: Set<@JvmSuppressWildcards PushProvider>,
|
||||
private val stringProvider: StringProvider,
|
||||
) : NotificationTroubleshootTest {
|
||||
private val sortedPushProvider = pushProviders.sortedBy { it.index }
|
||||
override val order = 100
|
||||
private val delegate = NotificationTroubleshootTestDelegate(
|
||||
defaultName = stringProvider.getString(R.string.troubleshoot_notifications_test_detect_push_provider_title),
|
||||
defaultDescription = stringProvider.getString(R.string.troubleshoot_notifications_test_detect_push_provider_description),
|
||||
fakeDelay = NotificationTroubleshootTestDelegate.SHORT_DELAY,
|
||||
)
|
||||
override val state: StateFlow<NotificationTroubleshootTestState> = delegate.state
|
||||
|
||||
override suspend fun run(coroutineScope: CoroutineScope) {
|
||||
delegate.start()
|
||||
val result = sortedPushProvider.isNotEmpty()
|
||||
if (result) {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getQuantityString(
|
||||
resId = R.plurals.troubleshoot_notifications_test_detect_push_provider_success,
|
||||
quantity = sortedPushProvider.size,
|
||||
sortedPushProvider.size,
|
||||
sortedPushProvider.joinToString { it.name }
|
||||
),
|
||||
status = NotificationTroubleshootTestState.Status.Success
|
||||
)
|
||||
} else {
|
||||
delegate.updateState(
|
||||
description = stringProvider.getString(R.string.troubleshoot_notifications_test_detect_push_provider_failure),
|
||||
status = NotificationTroubleshootTestState.Status.Failure(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reset() = delegate.reset()
|
||||
}
|
||||
|
|
@ -56,4 +56,29 @@
|
|||
<string name="push_distributor_background_sync_android">"Фонавая сінхранізацыя"</string>
|
||||
<string name="push_distributor_firebase_android">"Сэрвісы Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Службы Google Play не знойдзены. Апавяшчэнні могуць не працаваць належным чынам."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Атрымаць назву бягучага пастаўшчыка."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Пастаўшчыкі push-апавяшчэнняў не выбраны."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Бягучы пастаўшчык push-апавяшчэнняў: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Бягучы пастаўшчык push-апавяшчэнняў"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Пераканайцеся, што ў праграме ёсць хаця б адзін пастаўшчык push-апавяшчэнняў."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Пастаўшчыкі push-апавяшчэнняў не знойдзены."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"Знайшлі %1$d пастаўшчыка push-апавяшчэнняў: %2$s"</item>
|
||||
<item quantity="few">"Знайшлі %1$d пастаўшчыкоў push-апавяшчэнняў: %2$s"</item>
|
||||
<item quantity="many">"Знайшлі %1$d пастаўшчыкоў push-апавяшчэнняў: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Выяўленне пастаўшчыкоў push-паслуг"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Праверце, ці можа праграма паказваць апавяшчэнні."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Апавяшчэнне не было націснута."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Немагчыма паказаць апавяшчэнне."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Апавяшчэнне было націснута!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Паказаць апавяшчэнне"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Націсніце на апавяшчэнне, каб працягнуць тэст."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Пераканайцеся, што праграма атрымлівае push-апавяшчэнні."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Памылка: pusher адхіліў запыт."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Памылка: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Памылка, немагчыма праверыць push-апавяшчэнне."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Памылка, тайм-аўт у чаканні push-апавяшчэння."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Зварот цыклу назад заняў %1$d мс."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Тэст Націсніце кнопку вярнуцца"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -36,4 +36,5 @@
|
|||
<item quantity="one">"%d стая"</item>
|
||||
<item quantity="other">"%d стаи"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Грешка: %1$s"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -56,4 +56,29 @@
|
|||
<string name="push_distributor_background_sync_android">"Synchronizace na pozadí"</string>
|
||||
<string name="push_distributor_firebase_android">"Služby Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Nebyly nalezeny žádné funkční služby Google Play. Oznámení nemusí fungovat správně."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Získat název aktuálního poskytovatele."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Nebyli vybráni žádní push poskytovatelé."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Aktuální push poskytovatel: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Aktuální push poskytovatel"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Ujistěte se, že aplikace má alespoň jednoho push poskytovatele."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Nebyli nalezeni žádní push poskytovatelé."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"Nalezen %1$d push poskytovatel: %2$s"</item>
|
||||
<item quantity="few">"Nalezeni %1$d push poskytovatelé: %2$s"</item>
|
||||
<item quantity="other">"Nalezeno %1$d push poskytovatelů: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Zjistit push poskytovatele"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Zkontrolujte, zda aplikace může zobrazit oznámení."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Na oznámení nebylo kliknuto."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Oznámení nelze zobrazit."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Na oznámení bylo kliknuto!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Zobrazit oznámení"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Kliknutím na oznámení pokračujte v testu."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Ujistěte se, že aplikace přijímá push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Chyba: pusher odmítl požadavek."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Chyba: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Chyba, nelze otestovat push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Chyba, časový limit čekání na push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Push zpětná smyčka trvala %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Otestovat push zpětnou smyčku"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -50,4 +50,28 @@
|
|||
<string name="push_distributor_background_sync_android">"Hintergrundsynchronisation"</string>
|
||||
<string name="push_distributor_firebase_android">"Google-Dienste"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Ermittele den Namen des aktuellen Anbieters."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Kein Push-Anbieter ausgewählt."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Aktueller Push-Anbieter:%1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Aktueller Push-Anbieter"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Stelle sicher, dass die Anwendung mindestens einen Push-Anbieter hat."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Keine Push-Anbieter gefunden."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"%1$d Push-Anbieter gefunden: %2$s"</item>
|
||||
<item quantity="other">"%1$d Push-Anbieter gefunden: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Push-Anbieter erkennen"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Prüfe, ob die Anwendung Benachrichtigungen anzeigen kann."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Die Benachrichtigung wurde nicht angeklickt."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Die Benachrichtigung kann nicht angezeigt werden."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Die Benachrichtigung wurde angeklickt!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Benachrichtigung anzeigen"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Bitte klicke auf die Benachrichtigung, um den Test fortzusetzen."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Stelle sicher, dass die Anwendung Push-Nachrichten empfängt."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Fehler: Der Pusher hat die Anfrage abgelehnt."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Fehler:%1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Fehler: Push kann nicht getestet werden."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Fehler: Timeout beim Warten auf Push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Push-Loop-Back Dauer: %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Teste Push-Loop-Back"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -50,4 +50,28 @@
|
|||
<string name="push_distributor_background_sync_android">"Synchronisation en arrière-plan"</string>
|
||||
<string name="push_distributor_firebase_android">"Services Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Aucun service Google Play valide n’a été trouvé. Les notifications peuvent ne pas fonctionner correctement."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Obtenir le nom du fournisseur de Push actuel."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Aucun fournisseur de Push n’est sélectionné."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Fournisseur de Push actuel : %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Fournisseur de Push actuel"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Vérifier que l’application possède au moins un fournisseur de Push."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Aucun fournisseur de Push n’a été trouvé."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"%1$d fournisseur de Push détecté : %2$s"</item>
|
||||
<item quantity="other">"%1$d fournisseurs de Push détectés : %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Détecter les fournisseurs de Push"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Vérifier que l’application peut afficher des notifications."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Vous n’avez pas cliqué sur la notification."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Impossible d’afficher la notification."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Vous avez cliqué sur la notification!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Affichage des notifications"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Veuillez cliquer sur la notification pour continuer le test."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Vérifier que l’application reçoit les Push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Erreur : le Pusher a rejeté la demande."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Erreur :%1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Erreur, impossible de tester les Push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Erreur, le délai d’attente du Push est dépassé."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"La demande d’envoi de Push et sa réception ont pris %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Tester la réception des Push"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -50,4 +50,28 @@
|
|||
<string name="push_distributor_background_sync_android">"Háttérszinkronizálás"</string>
|
||||
<string name="push_distributor_firebase_android">"Google szolgáltatások"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"A Google Play szolgáltatások nem találhatók. Előfordulhat, hogy az értesítések nem működnek megfelelően."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"A jelenlegi szolgáltató nevének lekérdezése."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Nincs kiválasztva leküldéses értesítési szolgáltató."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Jelenlegi leküldéses értesítési szolgáltató: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Jelenlegi leküldéses értesítési szolgáltató"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Győződjön meg arról, hogy az alkalmazás legalább egy leküldéses értesítési szolgáltatóval rendelkezik."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Nem található leküldéses értesítési szolgáltató."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"%1$d leküldéses szolgáltató találva: %2$s"</item>
|
||||
<item quantity="other">"%1$d leküldéses szolgáltató találva: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Leküldéses értesítési szolgáltatók észlelése"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Ellenőrizze, hogy az alkalmazás képes-e megjeleníteni az értesítést."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Az értesítésre nem kattintottak rá."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Az értesítés nem jeleníthető meg."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Az értesítésre rákattintottak!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Értesítés megjelenítése"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"A teszt folytatásához kattintson az értesítésre."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Győződjön meg arról, hogy az alkalmazás megkapja-e a leküldéses értesítést."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Hiba: a leküldő elutasította a kérést."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Hiba: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Hiba, nem lehet tesztelni a leküldéses értesítést."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Hiba, időtúllépés a leküldéses értesítésre való várakozás során."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"A leküldéses értesítés folyamata %1$d ezredmásodpercig tartott."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Tesztelje a leküldéses értesítés folyamatát"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,27 @@
|
|||
<string name="push_distributor_background_sync_android">"Sinkronisasi latar belakang"</string>
|
||||
<string name="push_distributor_firebase_android">"Layanan Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Tidak ditemukan Layanan Google Play yang valid. Pemberitahuan mungkin tidak berfungsi dengan baik."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Dapatkan nama penyedia saat ini."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Tidak ada penyedia notifikasi dorongan yang dipilih."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Penyedia notifikasi dorongan saat ini: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Penyedia notifikasi dorongan saat ini"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Pastikan aplikasi memiliki setidaknya satu penyedia notifikasi dorongan."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Tidak ada penyedia notifikasi dorongan yang ditemukan."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="other">"Ditemukan %1$d penyedia notifikasi dorongan: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Deteksi penyedia notifikasi dorongan"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Periksa apakah aplikasi dapat menampilkan notifikasi."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Notifikasi belum diklik."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Tidak dapat menampilkan notifikasi."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Notifikasi telah diklik!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Tampilan notifikasi"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Silakan klik pada notifikasi untuk melanjutkan tes."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Pastikan aplikasi menerima notifikasi dorongan."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Kesalahan: pendorong telah menolak permintaan."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Kesalahan: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Terjadi kesalahan, tidak dapat menguji notifikasi dorongan."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Terjadi kesalahan, melebihi batas waktu menunggu notifikasi dorongan."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Ulangan notifikasi dorongan membutuhkan %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Uji ulangan notifikasi dorongan lagi"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -56,4 +56,29 @@
|
|||
<string name="push_distributor_background_sync_android">"Фоновая синхронизация"</string>
|
||||
<string name="push_distributor_firebase_android">"Сервисы Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Не найдены действующие службы Google Play. Уведомления могут работать некорректно."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Получение имени текущего поставщика."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Поставщики push-уведомлений не выбраны."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Текущий поставщик push-уведомлений: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Текущий поставщик push-уведомлений"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Убедитесь, что у приложения есть хотя бы один поставщик push-сообщений."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Поставщики push-уведомлений не найдены."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"Найден %1$d push-провайдер: %2$s"</item>
|
||||
<item quantity="few">"Найдено %1$d push-провайдеров: %2$s"</item>
|
||||
<item quantity="many">"Найдено %1$d push-провайдеров: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Обнаружение поставщиков push-уведомлений"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Убедитесь, что приложение может отображать уведомление."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Уведомление не было нажато."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Невозможно отобразить уведомление."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Уведомление было нажато!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Отобразить уведомление"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Нажмите на уведомление, чтобы продолжить тест."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Убедитесь, что приложение получает push-сообщение."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Ошибка: pusher отклонил запрос."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Ошибка: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Ошибка, невозможно протестировать отправку."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Ошибка, тайм-аут ожидания push-уведомления."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Обратная отправка push-уведомления, заняла %1$d мс."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Тест обратной отправки push-уведомления"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -56,4 +56,29 @@
|
|||
<string name="push_distributor_background_sync_android">"Synchronizácia na pozadí"</string>
|
||||
<string name="push_distributor_firebase_android">"Služby Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Nenašli sa žiadne platné služby Google Play. Oznámenia nemusia fungovať správne."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Získaťe názov aktuálneho poskytovateľa."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Nie sú vybraní žiadni poskytovatelia push."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Aktuálny poskytovateľ push: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Aktuálny poskytovateľ push"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Uistite sa, že aplikácia má aspoň jedného poskytovateľa push."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Nenašli sa žiadni poskytovatelia push."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"Nájdený %1$d poskytovateľ služby push: %2$s"</item>
|
||||
<item quantity="few">"Nájdení %1$d poskytovatelia služby push: %2$s"</item>
|
||||
<item quantity="other">"Nájdených %1$d poskytovateľov služby push: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Zistiť poskytovateľov push"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Skontrolujte, či aplikácia dokáže zobraziť upozornenie."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"Na oznámenie nebolo kliknuté."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Nie je možné zobraziť upozornenie."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"Na oznámenie bolo kliknuté!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Zobraziť upozornenie"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Kliknite na upozornenie a pokračujte v teste."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Uistite sa, že aplikácia prijíma push oznámenia."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Chyba: pusher odmietol požiadavku."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Chyba: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Chyba, nie je možné testovať push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Chyba, časový limit na push vypršal."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Push loop back trvalo %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Testovať Push loop back"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -50,4 +50,28 @@
|
|||
<string name="push_distributor_background_sync_android">"Background synchronization"</string>
|
||||
<string name="push_distributor_firebase_android">"Google Services"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"No valid Google Play Services found. Notifications may not work properly."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_description">"Get the name of the current provider."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure">"No push providers selected."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_success">"Current push provider: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Current push provider"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Ensure that the application has at least one push provider."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"No push providers found."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="one">"Found %1$d push provider: %2$s"</item>
|
||||
<item quantity="other">"Found %1$d push providers: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Detect push providers"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Check that the application can display notification."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_failure">"The notification has not been clicked."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_permission_failure">"Cannot display the notification."</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_success">"The notification has been clicked!"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_title">"Display notification"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_waiting">"Please click on the notification to continue the test."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_description">"Ensure that the application is receiving push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_1">"Error: pusher has rejected the request."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_2">"Error: %1$s."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_3">"Error, cannot test push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_failure_4">"Error, timeout waiting for push."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_success">"Push loop back took %1$d ms."</string>
|
||||
<string name="troubleshoot_notifications_test_push_loop_back_title">"Test Push loop back"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ import io.element.android.libraries.matrix.test.A_SPACE_ID
|
|||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoaderHolder
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
|
|
@ -115,12 +115,12 @@ class DefaultNotificationDrawerManagerTest {
|
|||
appNavigationStateService = appNavigationStateService
|
||||
),
|
||||
notificationRenderer = NotificationRenderer(
|
||||
NotificationIdProvider(),
|
||||
NotificationDisplayer(context),
|
||||
NotificationFactory(
|
||||
FakeAndroidNotificationFactory().instance,
|
||||
FakeRoomGroupMessageCreator().instance,
|
||||
FakeSummaryGroupMessageCreator().instance,
|
||||
notificationIdProvider = NotificationIdProvider(),
|
||||
notificationDisplayer = NotificationDisplayer(context),
|
||||
notificationFactory = NotificationFactory(
|
||||
notificationCreator = MockkNotificationCreator().instance,
|
||||
roomGroupMessageCreator = MockkRoomGroupMessageCreator().instance,
|
||||
summaryGroupMessageCreator = MockkSummaryGroupMessageCreator().instance,
|
||||
)
|
||||
),
|
||||
notificationEventPersistence = InMemoryNotificationEventPersistence(initialData = initialData),
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
|||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SPACE_ID
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeOutdatedEventDetector
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkOutdatedEventDetector
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent
|
||||
|
|
@ -42,7 +42,7 @@ private val VIEWING_A_ROOM = aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_I
|
|||
private val VIEWING_A_THREAD = aNavigationState(A_SESSION_ID, A_SPACE_ID, A_ROOM_ID, A_THREAD_ID)
|
||||
|
||||
class NotifiableEventProcessorTest {
|
||||
private val outdatedDetector = FakeOutdatedEventDetector()
|
||||
private val mockkOutdatedDetector = MockkOutdatedEventDetector()
|
||||
|
||||
@Test
|
||||
fun `given simple events when processing then keep simple events`() {
|
||||
|
|
@ -97,7 +97,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given out of date message event when processing then removes message event`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID))
|
||||
outdatedDetector.givenEventIsOutOfDate(events[0])
|
||||
mockkOutdatedDetector.givenEventIsOutOfDate(events[0])
|
||||
|
||||
val eventProcessor = createProcessor(navigationState = NOT_VIEWING_A_ROOM)
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given in date message event when processing then keep message event`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID))
|
||||
outdatedDetector.givenEventIsInDate(events[0])
|
||||
mockkOutdatedDetector.givenEventIsInDate(events[0])
|
||||
val eventProcessor = createProcessor(navigationState = NOT_VIEWING_A_ROOM)
|
||||
|
||||
val result = eventProcessor.process(events, renderedEvents = emptyList())
|
||||
|
|
@ -128,7 +128,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given viewing the same room main timeline when processing main timeline message event then removes message`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID, threadId = null))
|
||||
events.forEach { outdatedDetector.givenEventIsOutOfDate(it) }
|
||||
events.forEach { mockkOutdatedDetector.givenEventIsOutOfDate(it) }
|
||||
val eventProcessor = createProcessor(isInForeground = true, navigationState = VIEWING_A_ROOM)
|
||||
|
||||
val result = eventProcessor.process(events, renderedEvents = emptyList())
|
||||
|
|
@ -143,7 +143,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given viewing the same thread timeline when processing thread message event then removes message`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID, threadId = A_THREAD_ID))
|
||||
events.forEach { outdatedDetector.givenEventIsOutOfDate(it) }
|
||||
events.forEach { mockkOutdatedDetector.givenEventIsOutOfDate(it) }
|
||||
val eventProcessor = createProcessor(isInForeground = true, navigationState = VIEWING_A_THREAD)
|
||||
|
||||
val result = eventProcessor.process(events, renderedEvents = emptyList())
|
||||
|
|
@ -158,7 +158,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given viewing main timeline of the same room when processing thread timeline message event then keep message`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID, threadId = A_THREAD_ID))
|
||||
outdatedDetector.givenEventIsInDate(events[0])
|
||||
mockkOutdatedDetector.givenEventIsInDate(events[0])
|
||||
val eventProcessor = createProcessor(isInForeground = true, navigationState = VIEWING_A_ROOM)
|
||||
|
||||
val result = eventProcessor.process(events, renderedEvents = emptyList())
|
||||
|
|
@ -173,7 +173,7 @@ class NotifiableEventProcessorTest {
|
|||
@Test
|
||||
fun `given viewing thread timeline of the same room when processing main timeline message event then keep message`() {
|
||||
val events = listOf(aNotifiableMessageEvent(eventId = AN_EVENT_ID, roomId = A_ROOM_ID))
|
||||
outdatedDetector.givenEventIsInDate(events[0])
|
||||
mockkOutdatedDetector.givenEventIsInDate(events[0])
|
||||
val eventProcessor = createProcessor(isInForeground = true, navigationState = VIEWING_A_THREAD)
|
||||
|
||||
val result = eventProcessor.process(events, renderedEvents = emptyList())
|
||||
|
|
@ -213,8 +213,8 @@ class NotifiableEventProcessorTest {
|
|||
navigationState: NavigationState
|
||||
): NotifiableEventProcessor {
|
||||
return NotifiableEventProcessor(
|
||||
outdatedDetector.instance,
|
||||
FakeAppNavigationStateService(MutableStateFlow(AppNavigationState(navigationState, isInForeground))),
|
||||
outdatedDetector = mockkOutdatedDetector.instance,
|
||||
appNavigationStateService = FakeAppNavigationStateService(MutableStateFlow(AppNavigationState(navigationState, isInForeground))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
|||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeAndroidNotificationFactory
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkRoomGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkSummaryGroupMessageCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent
|
||||
import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent
|
||||
|
|
@ -41,19 +41,19 @@ private val A_MESSAGE_EVENT = aNotifiableMessageEvent(eventId = AN_EVENT_ID, roo
|
|||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class NotificationFactoryTest {
|
||||
private val androidNotificationFactory = FakeAndroidNotificationFactory()
|
||||
private val roomGroupMessageCreator = FakeRoomGroupMessageCreator()
|
||||
private val summaryGroupMessageCreator = FakeSummaryGroupMessageCreator()
|
||||
private val mockkNotificationCreator = MockkNotificationCreator()
|
||||
private val mockkRoomGroupMessageCreator = MockkRoomGroupMessageCreator()
|
||||
private val mockkSummaryGroupMessageCreator = MockkSummaryGroupMessageCreator()
|
||||
|
||||
private val notificationFactory = NotificationFactory(
|
||||
androidNotificationFactory.instance,
|
||||
roomGroupMessageCreator.instance,
|
||||
summaryGroupMessageCreator.instance
|
||||
notificationCreator = mockkNotificationCreator.instance,
|
||||
roomGroupMessageCreator = mockkRoomGroupMessageCreator.instance,
|
||||
summaryGroupMessageCreator = mockkSummaryGroupMessageCreator.instance
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given a room invitation when mapping to notification then is Append`() = testWith(notificationFactory) {
|
||||
val expectedNotification = androidNotificationFactory.givenCreateRoomInvitationNotificationFor(AN_INVITATION_EVENT)
|
||||
val expectedNotification = mockkNotificationCreator.givenCreateRoomInvitationNotificationFor(AN_INVITATION_EVENT)
|
||||
val roomInvitation = listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, AN_INVITATION_EVENT))
|
||||
|
||||
val result = roomInvitation.toNotifications()
|
||||
|
|
@ -90,7 +90,7 @@ class NotificationFactoryTest {
|
|||
|
||||
@Test
|
||||
fun `given a simple event when mapping to notification then is Append`() = testWith(notificationFactory) {
|
||||
val expectedNotification = androidNotificationFactory.givenCreateSimpleInvitationNotificationFor(A_SIMPLE_EVENT)
|
||||
val expectedNotification = mockkNotificationCreator.givenCreateSimpleInvitationNotificationFor(A_SIMPLE_EVENT)
|
||||
val roomInvitation = listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_SIMPLE_EVENT))
|
||||
|
||||
val result = roomInvitation.toNotifications()
|
||||
|
|
@ -128,7 +128,7 @@ class NotificationFactoryTest {
|
|||
@Test
|
||||
fun `given room with message when mapping to notification then delegates to room group message creator`() = testWith(notificationFactory) {
|
||||
val events = listOf(A_MESSAGE_EVENT)
|
||||
val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor(
|
||||
val expectedNotification = mockkRoomGroupMessageCreator.givenCreatesRoomMessageFor(
|
||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
events,
|
||||
A_ROOM_ID
|
||||
|
|
@ -197,7 +197,7 @@ class NotificationFactoryTest {
|
|||
)
|
||||
)
|
||||
val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted")))
|
||||
val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor(
|
||||
val expectedNotification = mockkRoomGroupMessageCreator.givenCreatesRoomMessageFor(
|
||||
MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL),
|
||||
withRedactedRemoved,
|
||||
A_ROOM_ID,
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
|||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeImageLoader
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationFactory
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationFactory
|
||||
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -51,14 +51,14 @@ private val ONE_SHOT_META = OneShotNotification.Append.Meta(key = "ignored", sum
|
|||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class NotificationRendererTest {
|
||||
private val notificationDisplayer = FakeNotificationDisplayer()
|
||||
private val notificationFactory = FakeNotificationFactory()
|
||||
private val mockkNotificationDisplayer = MockkNotificationDisplayer()
|
||||
private val mockkNotificationFactory = MockkNotificationFactory()
|
||||
private val notificationIdProvider = NotificationIdProvider()
|
||||
|
||||
private val notificationRenderer = NotificationRenderer(
|
||||
notificationIdProvider = notificationIdProvider,
|
||||
notificationDisplayer = notificationDisplayer.instance,
|
||||
notificationFactory = notificationFactory.instance,
|
||||
notificationDisplayer = mockkNotificationDisplayer.instance,
|
||||
notificationFactory = mockkNotificationFactory.instance,
|
||||
)
|
||||
|
||||
@Test
|
||||
|
|
@ -67,8 +67,8 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifySummaryCancelled()
|
||||
notificationDisplayer.verifyNoOtherInteractions()
|
||||
mockkNotificationDisplayer.verifySummaryCancelled()
|
||||
mockkNotificationDisplayer.verifyNoOtherInteractions()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -77,7 +77,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID))
|
||||
cancelNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID))
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID))
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
showNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomMessagesNotificationId(A_SESSION_ID), A_NOTIFICATION)
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID))
|
||||
cancelNotificationMessage(tag = AN_EVENT_ID.value, notificationIdProvider.getRoomEventNotificationId(A_SESSION_ID))
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = AN_EVENT_ID.value, notificationIdProvider.getRoomEventNotificationId(A_SESSION_ID))
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
showNotificationMessage(tag = AN_EVENT_ID.value, notificationIdProvider.getRoomEventNotificationId(A_SESSION_ID), A_NOTIFICATION)
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID))
|
||||
cancelNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID))
|
||||
}
|
||||
|
|
@ -175,7 +175,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
cancelNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomInvitationNotificationId(A_SESSION_ID))
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -194,7 +194,7 @@ class NotificationRendererTest {
|
|||
|
||||
renderEventsAsNotifications()
|
||||
|
||||
notificationDisplayer.verifyInOrder {
|
||||
mockkNotificationDisplayer.verifyInOrder {
|
||||
showNotificationMessage(tag = A_ROOM_ID.value, notificationIdProvider.getRoomEventNotificationId(A_SESSION_ID), A_NOTIFICATION)
|
||||
showNotificationMessage(tag = null, notificationIdProvider.getSummaryNotificationId(A_SESSION_ID), A_SUMMARY_NOTIFICATION.notification)
|
||||
}
|
||||
|
|
@ -221,7 +221,7 @@ class NotificationRendererTest {
|
|||
useCompleteNotificationFormat: Boolean = USE_COMPLETE_NOTIFICATION_FORMAT,
|
||||
summaryNotification: SummaryNotification = A_SUMMARY_NOTIFICATION
|
||||
) {
|
||||
notificationFactory.givenNotificationsFor(
|
||||
mockkNotificationFactory.givenNotificationsFor(
|
||||
groupedEvents = A_PROCESSED_EVENTS,
|
||||
matrixUser = MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL),
|
||||
useCompleteNotificationFormat = useCompleteNotificationFormat,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.push.impl.notifications.model.SimpleNotifiab
|
|||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeAndroidNotificationFactory {
|
||||
class MockkNotificationCreator {
|
||||
val instance = mockk<NotificationCreator>()
|
||||
|
||||
fun givenCreateRoomInvitationNotificationFor(event: InviteNotifiableEvent): Notification {
|
||||
|
|
@ -37,4 +37,10 @@ class FakeAndroidNotificationFactory {
|
|||
every { instance.createSimpleEventNotification(event) } returns mockNotification
|
||||
return mockNotification
|
||||
}
|
||||
|
||||
fun givenCreateDiagnosticNotification(): Notification {
|
||||
val mockNotification = mockk<Notification>()
|
||||
every { instance.createDiagnosticNotification() } returns mockNotification
|
||||
return mockNotification
|
||||
}
|
||||
}
|
||||
|
|
@ -20,13 +20,18 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
|||
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
|
||||
import io.element.android.libraries.push.impl.notifications.NotificationIdProvider
|
||||
import io.mockk.confirmVerified
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyOrder
|
||||
|
||||
class FakeNotificationDisplayer {
|
||||
class MockkNotificationDisplayer {
|
||||
val instance = mockk<NotificationDisplayer>(relaxed = true)
|
||||
|
||||
fun givenDisplayDiagnosticNotificationResult(result: Boolean) {
|
||||
every { instance.displayDiagnosticNotification(any()) } returns result
|
||||
}
|
||||
|
||||
fun verifySummaryCancelled() {
|
||||
verify { instance.cancelNotificationMessage(tag = null, NotificationIdProvider().getSummaryNotificationId(A_SESSION_ID)) }
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ import io.mockk.coEvery
|
|||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeNotificationFactory {
|
||||
class MockkNotificationFactory {
|
||||
val instance = mockk<NotificationFactory>()
|
||||
|
||||
fun givenNotificationsFor(
|
||||
|
|
@ -21,7 +21,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableEven
|
|||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeOutdatedEventDetector {
|
||||
class MockkOutdatedEventDetector {
|
||||
val instance = mockk<OutdatedEventDetector>()
|
||||
|
||||
fun givenEventIsOutOfDate(notifiableEvent: NotifiableEvent) {
|
||||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.push.impl.notifications.model.NotifiableMess
|
|||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeRoomGroupMessageCreator {
|
||||
class MockkRoomGroupMessageCreator {
|
||||
val instance = mockk<RoomGroupMessageCreator>()
|
||||
|
||||
fun givenCreatesRoomMessageFor(
|
||||
|
|
@ -19,6 +19,6 @@ package io.element.android.libraries.push.impl.notifications.fake
|
|||
import io.element.android.libraries.push.impl.notifications.SummaryGroupMessageCreator
|
||||
import io.mockk.mockk
|
||||
|
||||
class FakeSummaryGroupMessageCreator {
|
||||
class MockkSummaryGroupMessageCreator {
|
||||
val instance = mockk<SummaryGroupMessageCreator>()
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.push.test.FakeGetCurrentPushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class CurrentPushProviderTestTest {
|
||||
@Test
|
||||
fun `test CurrentPushProviderTest with a push provider`() = runTest {
|
||||
val sut = CurrentPushProviderTest(
|
||||
getCurrentPushProvider = FakeGetCurrentPushProvider("foo"),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
assertThat(lastItem.description).contains("foo")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test CurrentPushProviderTest without push provider`() = runTest {
|
||||
val sut = CurrentPushProviderTest(
|
||||
getCurrentPushProvider = FakeGetCurrentPushProvider(null),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationCreator
|
||||
import io.element.android.libraries.push.impl.notifications.fake.MockkNotificationDisplayer
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class NotificationTestTest {
|
||||
private val mockkNotificationCreator = MockkNotificationCreator().apply {
|
||||
givenCreateDiagnosticNotification()
|
||||
}
|
||||
private val mockkNotificationDisplayer = MockkNotificationDisplayer().apply {
|
||||
givenDisplayDiagnosticNotificationResult(true)
|
||||
}
|
||||
|
||||
private val notificationClickHandler = NotificationClickHandler()
|
||||
|
||||
@Test
|
||||
fun `test NotificationTest notification cannot be displayed`() = runTest {
|
||||
mockkNotificationDisplayer.givenDisplayDiagnosticNotificationResult(false)
|
||||
val sut = createNotificationTest()
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isInstanceOf(NotificationTroubleshootTestState.Status.Failure::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test NotificationTest user does not click on notification`() = runTest {
|
||||
val sut = createNotificationTest()
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.WaitingForUser)
|
||||
assertThat(awaitItem().status).isInstanceOf(NotificationTroubleshootTestState.Status.Failure::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test NotificationTest user clicks on notification`() = runTest {
|
||||
val sut = createNotificationTest()
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.WaitingForUser)
|
||||
notificationClickHandler.handleNotificationClick()
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNotificationTest(): NotificationTest {
|
||||
return NotificationTest(
|
||||
notificationCreator = mockkNotificationCreator.instance,
|
||||
notificationDisplayer = mockkNotificationDisplayer.instance,
|
||||
notificationClickHandler = notificationClickHandler,
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
|
||||
import io.element.android.libraries.push.api.gateway.PushGatewayFailure
|
||||
import io.element.android.libraries.push.test.FakePushService
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class PushLoopbackTestTest {
|
||||
@Test
|
||||
fun `test PushLoopbackTest timeout - push is not received`() = runTest {
|
||||
val diagnosticPushHandler = DiagnosticPushHandler()
|
||||
val sut = PushLoopbackTest(
|
||||
pushService = FakePushService(),
|
||||
diagnosticPushHandler = diagnosticPushHandler,
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test PushLoopbackTest PusherRejected error`() = runTest {
|
||||
val diagnosticPushHandler = DiagnosticPushHandler()
|
||||
val sut = PushLoopbackTest(
|
||||
pushService = FakePushService(
|
||||
testPushBlock = {
|
||||
throw PushGatewayFailure.PusherRejected()
|
||||
}
|
||||
),
|
||||
diagnosticPushHandler = diagnosticPushHandler,
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test PushLoopbackTest setup error`() = runTest {
|
||||
val diagnosticPushHandler = DiagnosticPushHandler()
|
||||
val sut = PushLoopbackTest(
|
||||
pushService = FakePushService(
|
||||
testPushBlock = { false }
|
||||
),
|
||||
diagnosticPushHandler = diagnosticPushHandler,
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test PushLoopbackTest other error`() = runTest {
|
||||
val diagnosticPushHandler = DiagnosticPushHandler()
|
||||
val sut = PushLoopbackTest(
|
||||
pushService = FakePushService(
|
||||
testPushBlock = {
|
||||
throw AN_EXCEPTION
|
||||
}
|
||||
),
|
||||
diagnosticPushHandler = diagnosticPushHandler,
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
assertThat(lastItem.description).contains(A_FAILURE_REASON)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test PushLoopbackTest push is received`() = runTest {
|
||||
val diagnosticPushHandler = DiagnosticPushHandler()
|
||||
val sut = PushLoopbackTest(
|
||||
pushService = FakePushService(testPushBlock = {
|
||||
diagnosticPushHandler.handlePush()
|
||||
true
|
||||
}),
|
||||
diagnosticPushHandler = diagnosticPushHandler,
|
||||
clock = FakeSystemClock(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.impl.troubleshoot
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.pushproviders.test.FakePushProvider
|
||||
import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootTestState
|
||||
import io.element.android.services.toolbox.test.strings.FakeStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class PushProvidersTestTest {
|
||||
@Test
|
||||
fun `test PushProvidersTest with empty list`() = runTest {
|
||||
val sut = PushProvidersTest(
|
||||
pushProviders = emptySet(),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Failure(false))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test PushProvidersTest with 2 push providers`() = runTest {
|
||||
val sut = PushProvidersTest(
|
||||
pushProviders = setOf(
|
||||
FakePushProvider(name = "foo"),
|
||||
FakePushProvider(name = "bar"),
|
||||
),
|
||||
stringProvider = FakeStringProvider(),
|
||||
)
|
||||
launch {
|
||||
sut.run(this)
|
||||
}
|
||||
sut.state.test {
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.Idle(true))
|
||||
assertThat(awaitItem().status).isEqualTo(NotificationTroubleshootTestState.Status.InProgress)
|
||||
val lastItem = awaitItem()
|
||||
assertThat(lastItem.status).isEqualTo(NotificationTroubleshootTestState.Status.Success)
|
||||
assertThat(lastItem.description).contains("2")
|
||||
assertThat(lastItem.description).contains("foo")
|
||||
assertThat(lastItem.description).contains("bar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,5 +25,6 @@ android {
|
|||
dependencies {
|
||||
api(projects.libraries.push.api)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.pushproviders.api)
|
||||
implementation(projects.tests.testutils)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.test
|
||||
|
||||
import io.element.android.libraries.push.api.GetCurrentPushProvider
|
||||
|
||||
class FakeGetCurrentPushProvider(
|
||||
private val currentPushProvider: String?
|
||||
) : GetCurrentPushProvider {
|
||||
override suspend fun getCurrentPushProvider(): String? = currentPushProvider
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.push.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.push.api.PushService
|
||||
import io.element.android.libraries.pushproviders.api.Distributor
|
||||
import io.element.android.libraries.pushproviders.api.PushProvider
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakePushService(
|
||||
private val testPushBlock: suspend () -> Boolean = { true }
|
||||
) : PushService {
|
||||
override fun notificationStyleChanged() {
|
||||
}
|
||||
|
||||
override fun getAvailablePushProviders(): List<PushProvider> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override suspend fun registerWith(matrixClient: MatrixClient, pushProvider: PushProvider, distributor: Distributor) {
|
||||
}
|
||||
|
||||
override suspend fun testPush(): Boolean = simulateLongTask {
|
||||
testPushBlock()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* 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 io.element.android.libraries.pushproviders.api
|
||||
|
||||
data class CurrentUserPushConfig(
|
||||
val url: String,
|
||||
val pushKey: String,
|
||||
)
|
||||
|
|
@ -49,8 +49,5 @@ interface PushProvider {
|
|||
*/
|
||||
suspend fun unregister(matrixClient: MatrixClient)
|
||||
|
||||
/**
|
||||
* Attempt to troubleshoot the push provider.
|
||||
*/
|
||||
suspend fun troubleshoot(): Result<Unit>
|
||||
suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ dependencies {
|
|||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.services.toolbox.api)
|
||||
|
||||
implementation(projects.libraries.pushstore.api)
|
||||
implementation(projects.libraries.pushproviders.api)
|
||||
|
|
@ -51,8 +54,11 @@ dependencies {
|
|||
exclude(group = "com.google.firebase", module = "firebase-measurement-connector")
|
||||
}
|
||||
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class FirebaseNewTokenHandler @Inject constructor(
|
|||
sessionStore.getAllSessions().toUserList()
|
||||
.map { SessionId(it) }
|
||||
.forEach { userId ->
|
||||
val userDataStore = userPushStoreFactory.create(userId)
|
||||
val userDataStore = userPushStoreFactory.getOrCreate(userId)
|
||||
if (userDataStore.getPushProviderName() == FirebaseConfig.NAME) {
|
||||
matrixAuthenticationService.restoreSession(userId).getOrNull()?.use { client ->
|
||||
pusherSubscriber.registerPusher(client, firebaseToken, FirebaseConfig.PUSHER_HTTP_URL)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue