BIT-1360: Prompt for push notification permission when approving passwordless logins (#677)

This commit is contained in:
Sean Weiser
2024-01-19 09:35:05 -06:00
committed by Álison Fernandes
parent 3c4b823014
commit a706db2b28
6 changed files with 193 additions and 18 deletions

View File

@@ -30,6 +30,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.hilt.navigation.compose.hiltViewModel
import android.Manifest
import android.os.Build
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeout
import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
@@ -48,9 +50,11 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenTwoButtonDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenWideSwitch
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTimePickerDialog
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialColors
import com.x8bit.bitwarden.ui.platform.theme.LocalNonMaterialTypography
import com.x8bit.bitwarden.ui.platform.theme.LocalPermissionsManager
import com.x8bit.bitwarden.ui.platform.util.displayLabel
import com.x8bit.bitwarden.ui.platform.util.toFormattedPattern
import java.time.LocalTime
@@ -68,6 +72,7 @@ fun AccountSecurityScreen(
onNavigateToDeleteAccount: () -> Unit,
viewModel: AccountSecurityViewModel = hiltViewModel(),
intentManager: IntentManager = LocalIntentManager.current,
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
) {
val state by viewModel.stateFlow.collectAsState()
val context = LocalContext.current
@@ -76,6 +81,10 @@ fun AccountSecurityScreen(
when (event) {
AccountSecurityEvent.NavigateBack -> onNavigateBack()
AccountSecurityEvent.NavigateToApplicationDataSettings -> {
intentManager.startApplicationDetailsSettingsActivity()
}
AccountSecurityEvent.NavigateToDeleteAccount -> onNavigateToDeleteAccount()
AccountSecurityEvent.NavigateToFingerprintPhrase -> {
@@ -144,6 +153,10 @@ fun AccountSecurityScreen(
onApprovePasswordlessLoginsAction = remember(viewModel) {
{ viewModel.trySendAction(it) }
},
permissionsManager = permissionsManager,
onPushNotificationConfirm = remember(viewModel) {
{ viewModel.trySendAction(AccountSecurityAction.PushNotificationConfirm) }
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
@@ -615,14 +628,18 @@ private fun FingerPrintPhraseDialog(
)
}
@Suppress("LongMethod")
@Composable
private fun ApprovePasswordlessLoginsRow(
isApproveLoginRequestsEnabled: Boolean,
@Suppress("MaxLineLength")
onApprovePasswordlessLoginsAction: (AccountSecurityAction.ApprovePasswordlessLoginsToggle) -> Unit,
onPushNotificationConfirm: () -> Unit,
permissionsManager: PermissionsManager,
modifier: Modifier = Modifier,
) {
var shouldShowConfirmationDialog by remember { mutableStateOf(false) }
var shouldShowPermissionDialog by remember { mutableStateOf(false) }
BitwardenWideSwitch(
label = stringResource(
id = R.string.use_this_device_to_approve_login_requests_made_from_other_devices,
@@ -656,6 +673,12 @@ private fun ApprovePasswordlessLoginsRow(
AccountSecurityAction.ApprovePasswordlessLoginsToggle.Enabled,
)
shouldShowConfirmationDialog = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@Suppress("MaxLineLength")
if (!permissionsManager.checkPermission(Manifest.permission.POST_NOTIFICATIONS)) {
shouldShowPermissionDialog = true
}
}
},
onDismissClick = {
onApprovePasswordlessLoginsAction(
@@ -671,4 +694,25 @@ private fun ApprovePasswordlessLoginsRow(
},
)
}
if (shouldShowPermissionDialog) {
BitwardenTwoButtonDialog(
title = null,
message = stringResource(
id = R.string.receive_push_notifications_for_new_login_requests,
),
confirmButtonText = stringResource(id = R.string.settings),
dismissButtonText = stringResource(id = R.string.no_thanks),
onConfirmClick = {
shouldShowPermissionDialog = false
onPushNotificationConfirm()
},
onDismissClick = {
shouldShowPermissionDialog = false
},
onDismissRequest = {
shouldShowPermissionDialog = false
},
)
}
}

View File

@@ -77,6 +77,10 @@ class AccountSecurityViewModel @Inject constructor(
is AccountSecurityAction.ApprovePasswordlessLoginsToggle -> {
handleApprovePasswordlessLoginsToggle(action)
}
is AccountSecurityAction.PushNotificationConfirm -> {
handlePushNotificationConfirm()
}
}
private fun handleAccountFingerprintPhraseClick() {
@@ -129,8 +133,10 @@ class AccountSecurityViewModel @Inject constructor(
mutableStateFlow.update { it.copy(isApproveLoginRequestsEnabled = true) }
}
}
// TODO Add permission prompt - BIT-1360
sendEvent(AccountSecurityEvent.ShowToast("Handle Login requests on this device.".asText()))
}
private fun handlePushNotificationConfirm() {
sendEvent(AccountSecurityEvent.NavigateToApplicationDataSettings)
}
private fun handleLogoutClick() {
@@ -275,6 +281,11 @@ sealed class AccountSecurityEvent {
*/
data object NavigateBack : AccountSecurityEvent()
/**
* Navigate to the application's settings screen.
*/
data object NavigateToApplicationDataSettings : AccountSecurityEvent()
/**
* Navigate to the delete account screen.
*/
@@ -381,6 +392,11 @@ sealed class AccountSecurityAction {
val enabled: Boolean,
) : AccountSecurityAction()
/**
* User confirmed the push notification permission prompt.
*/
data object PushNotificationConfirm : AccountSecurityAction()
/**
* User toggled the approve passwordless logins switch.
*/

View File

@@ -32,6 +32,11 @@ interface IntentManager {
*/
fun startSystemAutofillSettingsActivity(): Boolean
/**
* Starts the application's settings activity.
*/
fun startApplicationDetailsSettingsActivity()
/**
* Start an activity to view the given [uri] in an external browser.
*/

View File

@@ -93,6 +93,12 @@ class IntentManagerImpl(
false
}
override fun startApplicationDetailsSettingsActivity() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:" + context.packageName)
startActivity(intent = intent)
}
override fun launchUri(uri: Uri) {
val newUri = if (uri.scheme == null) {
uri.buildUpon().scheme("https").build()