Troubleshoot notifications screen

This commit is contained in:
Benoit Marty 2024-03-26 11:36:31 +01:00
parent 6c9ea2b920
commit 2bfe125a77
80 changed files with 3086 additions and 99 deletions

View file

@ -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)

View file

@ -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()
}
}

View file

@ -0,0 +1,66 @@
/*
* 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.core.notifications.NotificationTroubleshootTest
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestDelegate
import io.element.android.libraries.core.notifications.NotificationTroubleshootTestState
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.permissions.api.PermissionStateProvider
import io.element.android.libraries.permissions.impl.action.PermissionActions
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
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,
) : NotificationTroubleshootTest {
override val order: Int = 0
private val delegate = NotificationTroubleshootTestDelegate(
defaultName = "Check permissions",
defaultDescription = "Ensure that the application can show notifications.",
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 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()
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,100 @@
/*
* 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.core.notifications.NotificationTroubleshootTestState
import io.element.android.libraries.permissions.impl.FakePermissionStateProvider
import io.element.android.libraries.permissions.impl.action.FakePermissionActions
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
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()
)
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()
)
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
)
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)
}
}
}