adding MainViewModel navigation to LoginApprovalViewModel

This commit is contained in:
Andre Rosado 2024-11-20 18:56:46 +00:00
parent 5ea17700b3
commit 114982d64a
No known key found for this signature in database
GPG key ID: 99F68267CCD45AA9
6 changed files with 65 additions and 17 deletions

View file

@ -25,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.composition.LocalManagerProvider
import com.x8bit.bitwarden.ui.platform.feature.debugmenu.manager.DebugMenuLaunchManager import com.x8bit.bitwarden.ui.platform.feature.debugmenu.manager.DebugMenuLaunchManager
import com.x8bit.bitwarden.ui.platform.feature.debugmenu.navigateToDebugMenuScreen import com.x8bit.bitwarden.ui.platform.feature.debugmenu.navigateToDebugMenuScreen
import com.x8bit.bitwarden.ui.platform.feature.rootnav.RootNavScreen import com.x8bit.bitwarden.ui.platform.feature.rootnav.RootNavScreen
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.loginapproval.navigateToLoginApproval
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@ -94,6 +95,13 @@ class MainActivity : AppCompatActivity() {
) )
.show() .show()
} }
is MainEvent.NavigateToLoginApproval -> {
navController.navigateToLoginApproval(
fingerprint = "",
requestId = event.requestId,
)
}
} }
} }
updateScreenCapture(isScreenCaptureAllowed = state.isScreenCaptureAllowed) updateScreenCapture(isScreenCaptureAllowed = state.isScreenCaptureAllowed)

View file

@ -273,13 +273,16 @@ class MainViewModel @Inject constructor(
authRepository.switchAccount(passwordlessRequestData.userId) authRepository.switchAccount(passwordlessRequestData.userId)
} }
} }
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.PasswordlessRequest( sendEvent(MainEvent.NavigateToLoginApproval(passwordlessRequestData.loginRequestId))
passwordlessRequestData = passwordlessRequestData,
// Allow users back into the already-running app when completing the // specialCircumstanceManager.specialCircumstance =
// autofill task when this is not the first intent. // SpecialCircumstance.PasswordlessRequest(
shouldFinishWhenComplete = isFirstIntent, // passwordlessRequestData = passwordlessRequestData,
) // // Allow users back into the already-running app when completing the
// // autofill task when this is not the first intent.
// shouldFinishWhenComplete = isFirstIntent,
// )
} }
completeRegistrationData != null -> { completeRegistrationData != null -> {
@ -518,4 +521,11 @@ sealed class MainEvent {
* Show a toast with the given [message]. * Show a toast with the given [message].
*/ */
data class ShowToast(val message: Text) : MainEvent() data class ShowToast(val message: Text) : MainEvent()
/**
* Navigates to the Login Approval screen with the given fingerprint.
*/
data class NavigateToLoginApproval(
val requestId: String,
) : MainEvent()
} }

View file

@ -12,8 +12,10 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.util.createPasswordlessRequestDataIntent import com.x8bit.bitwarden.data.auth.util.createPasswordlessRequestDataIntent
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import com.x8bit.bitwarden.data.platform.manager.PushManager import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -28,6 +30,7 @@ class AuthRequestNotificationManagerImpl(
private val authDiskSource: AuthDiskSource, private val authDiskSource: AuthDiskSource,
pushManager: PushManager, pushManager: PushManager,
dispatcherManager: DispatcherManager, dispatcherManager: DispatcherManager,
private val appStateManager: AppStateManager,
) : AuthRequestNotificationManager { ) : AuthRequestNotificationManager {
private val ioScope = CoroutineScope(dispatcherManager.io) private val ioScope = CoroutineScope(dispatcherManager.io)
@ -40,6 +43,13 @@ class AuthRequestNotificationManagerImpl(
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
private fun handlePasswordlessRequestData(data: PasswordlessRequestData) { private fun handlePasswordlessRequestData(data: PasswordlessRequestData) {
val pendingIntent = createContentIntent(data)
if (appStateManager.appForegroundStateFlow.value == AppForegroundState.FOREGROUNDED) {
pendingIntent.send()
return
}
val notificationManager = NotificationManagerCompat.from(context) val notificationManager = NotificationManagerCompat.from(context)
// Construct the channel, calling this more than once is safe // Construct the channel, calling this more than once is safe
notificationManager.createNotificationChannel( notificationManager.createNotificationChannel(
@ -54,7 +64,7 @@ class AuthRequestNotificationManagerImpl(
if (!notificationManager.areNotificationsEnabled(NOTIFICATION_CHANNEL_ID)) return if (!notificationManager.areNotificationsEnabled(NOTIFICATION_CHANNEL_ID)) return
// Create the notification // Create the notification
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentIntent(createContentIntent(data)) .setContentIntent(pendingIntent)
.setContentTitle(context.getString(R.string.log_in_requested)) .setContentTitle(context.getString(R.string.log_in_requested))
.setContentText( .setContentText(
authDiskSource authDiskSource

View file

@ -21,6 +21,7 @@ import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManagerImpl import com.x8bit.bitwarden.data.auth.manager.UserLogoutManagerImpl
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.AppStateManager
import com.x8bit.bitwarden.data.platform.manager.PushManager import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
@ -49,12 +50,14 @@ object AuthManagerModule {
authDiskSource: AuthDiskSource, authDiskSource: AuthDiskSource,
pushManager: PushManager, pushManager: PushManager,
dispatcherManager: DispatcherManager, dispatcherManager: DispatcherManager,
appStateManager: AppStateManager,
): AuthRequestNotificationManager = ): AuthRequestNotificationManager =
AuthRequestNotificationManagerImpl( AuthRequestNotificationManagerImpl(
context = context, context = context,
authDiskSource = authDiskSource, authDiskSource = authDiskSource,
pushManager = pushManager, pushManager = pushManager,
dispatcherManager = dispatcherManager, dispatcherManager = dispatcherManager,
appStateManager = appStateManager,
) )
@Provides @Provides

View file

@ -10,16 +10,19 @@ import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
private const val FINGERPRINT: String = "fingerprint" private const val FINGERPRINT: String = "fingerprint"
private const val REQUEST_ID: String = "requestId"
private const val LOGIN_APPROVAL_PREFIX = "login_approval" private const val LOGIN_APPROVAL_PREFIX = "login_approval"
private const val LOGIN_APPROVAL_ROUTE = "$LOGIN_APPROVAL_PREFIX?$FINGERPRINT={$FINGERPRINT}" private const val LOGIN_APPROVAL_ROUTE =
"$LOGIN_APPROVAL_PREFIX?$FINGERPRINT={$FINGERPRINT}&$REQUEST_ID={$REQUEST_ID}"
/** /**
* Class to retrieve login approval arguments from the [SavedStateHandle]. * Class to retrieve login approval arguments from the [SavedStateHandle].
*/ */
@OmitFromCoverage @OmitFromCoverage
data class LoginApprovalArgs(val fingerprint: String?) { data class LoginApprovalArgs(val fingerprint: String?, val requestId: String?) {
constructor(savedStateHandle: SavedStateHandle) : this( constructor(savedStateHandle: SavedStateHandle) : this(
fingerprint = savedStateHandle.get<String>(FINGERPRINT), fingerprint = savedStateHandle.get<String>(FINGERPRINT),
requestId = savedStateHandle.get<String>(REQUEST_ID),
) )
} }
@ -37,6 +40,11 @@ fun NavGraphBuilder.loginApprovalDestination(
nullable = true nullable = true
defaultValue = null defaultValue = null
}, },
navArgument(REQUEST_ID) {
type = NavType.StringType
nullable = true
defaultValue = null
},
), ),
) { ) {
LoginApprovalScreen( LoginApprovalScreen(
@ -50,7 +58,8 @@ fun NavGraphBuilder.loginApprovalDestination(
*/ */
fun NavController.navigateToLoginApproval( fun NavController.navigateToLoginApproval(
fingerprint: String?, fingerprint: String?,
requestId: String? = null,
navOptions: NavOptions? = null, navOptions: NavOptions? = null,
) { ) {
navigate("$LOGIN_APPROVAL_PREFIX?$FINGERPRINT=$fingerprint", navOptions) navigate("$LOGIN_APPROVAL_PREFIX?$FINGERPRINT=$fingerprint&$REQUEST_ID=$requestId", navOptions)
} }

View file

@ -48,7 +48,7 @@ class LoginApprovalViewModel @Inject constructor(
?: requireNotNull(LoginApprovalArgs(savedStateHandle).fingerprint), ?: requireNotNull(LoginApprovalArgs(savedStateHandle).fingerprint),
masterPasswordHash = null, masterPasswordHash = null,
publicKey = "", publicKey = "",
requestId = "", requestId = LoginApprovalArgs(savedStateHandle).requestId.orEmpty(),
viewState = LoginApprovalState.ViewState.Loading, viewState = LoginApprovalState.ViewState.Loading,
dialogState = null, dialogState = null,
) )
@ -86,11 +86,19 @@ class LoginApprovalViewModel @Inject constructor(
} }
} }
?: run { ?: run {
authRepository if (state.requestId.isNotEmpty()) {
.getAuthRequestByFingerprintFlow(state.fingerprint) authRepository
.map { LoginApprovalAction.Internal.AuthRequestResultReceive(it) } .getAuthRequestByIdFlow(state.requestId)
.onEach(::sendAction) .map { LoginApprovalAction.Internal.AuthRequestResultReceive(it) }
.launchIn(viewModelScope) .onEach(::sendAction)
.launchIn(viewModelScope)
} else {
authRepository
.getAuthRequestByIdFlow(state.fingerprint)
.map { LoginApprovalAction.Internal.AuthRequestResultReceive(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
}
} }
} }