mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-04 01:19:33 +00:00
Prompt for local network permission on logged-in home
This commit is contained in:
@@ -34,6 +34,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
implementation(projects.libraries.oauth.api)
|
implementation(projects.libraries.oauth.api)
|
||||||
|
implementation(projects.libraries.permissions.api)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
implementation(projects.libraries.push.api)
|
implementation(projects.libraries.push.api)
|
||||||
implementation(projects.libraries.pushproviders.api)
|
implementation(projects.libraries.pushproviders.api)
|
||||||
@@ -61,6 +62,7 @@ dependencies {
|
|||||||
testImplementation(projects.features.share.test)
|
testImplementation(projects.features.share.test)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.oauth.test)
|
testImplementation(projects.libraries.oauth.test)
|
||||||
|
testImplementation(projects.libraries.permissions.test)
|
||||||
testImplementation(projects.libraries.preferences.test)
|
testImplementation(projects.libraries.preferences.test)
|
||||||
testImplementation(projects.libraries.push.test)
|
testImplementation(projects.libraries.push.test)
|
||||||
testImplementation(projects.libraries.pushproviders.test)
|
testImplementation(projects.libraries.pushproviders.test)
|
||||||
|
|||||||
@@ -12,4 +12,7 @@ sealed interface LoggedInEvents {
|
|||||||
data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents
|
data class CloseErrorDialog(val doNotShowAgain: Boolean) : LoggedInEvents
|
||||||
data object CheckSlidingSyncProxyAvailability : LoggedInEvents
|
data object CheckSlidingSyncProxyAvailability : LoggedInEvents
|
||||||
data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents
|
data object LogoutAndMigrateToNativeSlidingSync : LoggedInEvents
|
||||||
|
data object DismissLocalNetworkPermissionPrompt : LoggedInEvents
|
||||||
|
|
||||||
|
data object RequestLocationNetworkPermission : LoggedInEvents
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package io.element.android.appnav.loggedin
|
package io.element.android.appnav.loggedin
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
@@ -15,6 +16,7 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@@ -36,6 +38,9 @@ import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion
|
|||||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||||
|
import io.element.android.libraries.permissions.api.LocalNetworkPermissionAdvisor
|
||||||
|
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||||
|
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||||
import io.element.android.libraries.push.api.PushService
|
import io.element.android.libraries.push.api.PushService
|
||||||
import io.element.android.libraries.push.api.PusherRegistrationFailure
|
import io.element.android.libraries.push.api.PusherRegistrationFailure
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
@@ -58,7 +63,12 @@ class LoggedInPresenter(
|
|||||||
private val encryptionService: EncryptionService,
|
private val encryptionService: EncryptionService,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
|
private val localNetworkPermissionAdvisor: LocalNetworkPermissionAdvisor,
|
||||||
|
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||||
) : Presenter<LoggedInState> {
|
) : Presenter<LoggedInState> {
|
||||||
|
private val localNetworkPermissionsPresenter: PermissionsPresenter =
|
||||||
|
permissionsPresenterFactory.create(Manifest.permission.ACCESS_LOCAL_NETWORK)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): LoggedInState {
|
override fun present(): LoggedInState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -117,6 +127,21 @@ class LoggedInPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val localNetworkPermissionState = localNetworkPermissionsPresenter.present()
|
||||||
|
var localNetworkPromptDismissedThisSession by remember { mutableStateOf(false) }
|
||||||
|
val advisorRequestsPrompt by produceState(initialValue = false, localNetworkPermissionState.permissionGranted) {
|
||||||
|
value = localNetworkPermissionAdvisor.shouldRequestPermissionFor(matrixClient.homeserverUrl)
|
||||||
|
}
|
||||||
|
LaunchedEffect(localNetworkPermissionState.permissionGranted) {
|
||||||
|
if (localNetworkPermissionState.permissionGranted) localNetworkPromptDismissedThisSession = false
|
||||||
|
}
|
||||||
|
val localNetworkPermissionDialog = when {
|
||||||
|
!advisorRequestsPrompt -> LocalNetworkPermissionDialog.None
|
||||||
|
localNetworkPromptDismissedThisSession -> LocalNetworkPermissionDialog.None
|
||||||
|
localNetworkPermissionState.shouldShowRationale -> LocalNetworkPermissionDialog.Rationale
|
||||||
|
else -> LocalNetworkPermissionDialog.Settings
|
||||||
|
}
|
||||||
|
|
||||||
fun handleEvent(event: LoggedInEvents) {
|
fun handleEvent(event: LoggedInEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is LoggedInEvents.CloseErrorDialog -> {
|
is LoggedInEvents.CloseErrorDialog -> {
|
||||||
@@ -134,6 +159,17 @@ class LoggedInPresenter(
|
|||||||
// Force the logout since Native Sliding Sync is already enforced by the SDK
|
// Force the logout since Native Sliding Sync is already enforced by the SDK
|
||||||
matrixClient.logout(userInitiated = true, ignoreSdkError = true)
|
matrixClient.logout(userInitiated = true, ignoreSdkError = true)
|
||||||
}
|
}
|
||||||
|
LoggedInEvents.DismissLocalNetworkPermissionPrompt -> {
|
||||||
|
localNetworkPromptDismissedThisSession = true
|
||||||
|
}
|
||||||
|
LoggedInEvents.RequestLocationNetworkPermission -> {
|
||||||
|
if (localNetworkPermissionDialog == LocalNetworkPermissionDialog.Settings) {
|
||||||
|
localNetworkPermissionState.eventSink(PermissionsEvent.OpenSystemSettingAndCloseDialog)
|
||||||
|
} else {
|
||||||
|
localNetworkPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||||
|
}
|
||||||
|
localNetworkPromptDismissedThisSession = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +179,7 @@ class LoggedInPresenter(
|
|||||||
ignoreRegistrationError = ignoreRegistrationError,
|
ignoreRegistrationError = ignoreRegistrationError,
|
||||||
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
|
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
|
||||||
appName = buildMeta.applicationName,
|
appName = buildMeta.applicationName,
|
||||||
|
localNetworkPermissionDialog = localNetworkPermissionDialog,
|
||||||
eventSink = ::handleEvent,
|
eventSink = ::handleEvent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,5 +16,12 @@ data class LoggedInState(
|
|||||||
val ignoreRegistrationError: Boolean,
|
val ignoreRegistrationError: Boolean,
|
||||||
val forceNativeSlidingSyncMigration: Boolean,
|
val forceNativeSlidingSyncMigration: Boolean,
|
||||||
val appName: String,
|
val appName: String,
|
||||||
|
val localNetworkPermissionDialog: LocalNetworkPermissionDialog,
|
||||||
val eventSink: (LoggedInEvents) -> Unit,
|
val eventSink: (LoggedInEvents) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
enum class LocalNetworkPermissionDialog {
|
||||||
|
None,
|
||||||
|
Rationale,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ open class LoggedInStateProvider : PreviewParameterProvider<LoggedInState> {
|
|||||||
aLoggedInState(showSyncSpinner = true),
|
aLoggedInState(showSyncSpinner = true),
|
||||||
aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())),
|
aLoggedInState(pusherRegistrationState = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable())),
|
||||||
aLoggedInState(forceNativeSlidingSyncMigration = true),
|
aLoggedInState(forceNativeSlidingSyncMigration = true),
|
||||||
|
aLoggedInState(localNetworkPermissionDialog = LocalNetworkPermissionDialog.Rationale),
|
||||||
|
aLoggedInState(localNetworkPermissionDialog = LocalNetworkPermissionDialog.Settings),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,11 +29,13 @@ fun aLoggedInState(
|
|||||||
pusherRegistrationState: AsyncData<Unit> = AsyncData.Uninitialized,
|
pusherRegistrationState: AsyncData<Unit> = AsyncData.Uninitialized,
|
||||||
forceNativeSlidingSyncMigration: Boolean = false,
|
forceNativeSlidingSyncMigration: Boolean = false,
|
||||||
appName: String = "Element X",
|
appName: String = "Element X",
|
||||||
|
localNetworkPermissionDialog: LocalNetworkPermissionDialog = LocalNetworkPermissionDialog.None,
|
||||||
) = LoggedInState(
|
) = LoggedInState(
|
||||||
showSyncSpinner = showSyncSpinner,
|
showSyncSpinner = showSyncSpinner,
|
||||||
pusherRegistrationState = pusherRegistrationState,
|
pusherRegistrationState = pusherRegistrationState,
|
||||||
ignoreRegistrationError = false,
|
ignoreRegistrationError = false,
|
||||||
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
|
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
|
||||||
appName = appName,
|
appName = appName,
|
||||||
|
localNetworkPermissionDialog = localNetworkPermissionDialog,
|
||||||
eventSink = {},
|
eventSink = {},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import io.element.android.appnav.R
|
import io.element.android.appnav.R
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogWithDoNotShowAgain
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogWithDoNotShowAgain
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
@@ -82,6 +83,39 @@ fun LoggedInView(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalNetworkPermissionDialog(state = state)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LocalNetworkPermissionDialog(state: LoggedInState) {
|
||||||
|
when (state.localNetworkPermissionDialog) {
|
||||||
|
LocalNetworkPermissionDialog.None -> Unit
|
||||||
|
LocalNetworkPermissionDialog.Rationale -> ConfirmationDialog(
|
||||||
|
title = stringResource(CommonStrings.screen_local_network_opt_in_title),
|
||||||
|
content = stringResource(CommonStrings.screen_local_network_opt_in_subtitle),
|
||||||
|
submitText = stringResource(CommonStrings.dialog_allow_access),
|
||||||
|
cancelText = stringResource(CommonStrings.action_not_now),
|
||||||
|
onSubmitClick = {
|
||||||
|
state.eventSink(LoggedInEvents.RequestLocationNetworkPermission)
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
state.eventSink(LoggedInEvents.DismissLocalNetworkPermissionPrompt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
LocalNetworkPermissionDialog.Settings -> ConfirmationDialog(
|
||||||
|
title = stringResource(CommonStrings.screen_local_network_opt_in_title),
|
||||||
|
content = stringResource(CommonStrings.screen_local_network_opt_in_subtitle),
|
||||||
|
submitText = stringResource(CommonStrings.action_open_settings),
|
||||||
|
cancelText = stringResource(CommonStrings.action_not_now),
|
||||||
|
onSubmitClick = {
|
||||||
|
state.eventSink(LoggedInEvents.RequestLocationNetworkPermission)
|
||||||
|
},
|
||||||
|
onDismiss = {
|
||||||
|
state.eventSink(LoggedInEvents.DismissLocalNetworkPermissionPrompt)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Throwable.getReason(): String? {
|
private fun Throwable.getReason(): String? {
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
|||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||||
|
import io.element.android.libraries.permissions.test.FakeLocalNetworkPermissionAdvisor
|
||||||
|
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
|
||||||
import io.element.android.libraries.push.api.PushService
|
import io.element.android.libraries.push.api.PushService
|
||||||
import io.element.android.libraries.push.api.PusherRegistrationFailure
|
import io.element.android.libraries.push.api.PusherRegistrationFailure
|
||||||
import io.element.android.libraries.push.test.FakePushService
|
import io.element.android.libraries.push.test.FakePushService
|
||||||
@@ -124,6 +126,8 @@ class LoggedInPresenterTest {
|
|||||||
encryptionService = encryptionService,
|
encryptionService = encryptionService,
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
networkMonitor = networkMonitor,
|
networkMonitor = networkMonitor,
|
||||||
|
localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(),
|
||||||
|
permissionsPresenterFactory = FakePermissionsPresenterFactory(),
|
||||||
).test {
|
).test {
|
||||||
encryptionService.emitRecoveryState(RecoveryState.UNKNOWN)
|
encryptionService.emitRecoveryState(RecoveryState.UNKNOWN)
|
||||||
encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE)
|
encryptionService.emitRecoveryState(RecoveryState.INCOMPLETE)
|
||||||
@@ -368,6 +372,8 @@ class LoggedInPresenterTest {
|
|||||||
encryptionService = encryptionService,
|
encryptionService = encryptionService,
|
||||||
buildMeta = buildMeta,
|
buildMeta = buildMeta,
|
||||||
networkMonitor = networkMonitor,
|
networkMonitor = networkMonitor,
|
||||||
|
localNetworkPermissionAdvisor = FakeLocalNetworkPermissionAdvisor(),
|
||||||
|
permissionsPresenterFactory = FakePermissionsPresenterFactory(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user