Store SpecialCircumstance to SavedStateHandle (#799)

This commit is contained in:
Brian Yencho 2024-01-26 09:35:31 -06:00 committed by Álison Fernandes
parent 3264be998d
commit 7a163d82ed
4 changed files with 70 additions and 4 deletions

View file

@ -2,6 +2,7 @@ package com.x8bit.bitwarden
import android.content.Intent
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
@ -16,6 +17,8 @@ import kotlinx.coroutines.flow.update
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
private const val SPECIAL_CIRCUMSTANCE_KEY = "special-circumstance"
/**
* A view model that helps launch actions for the [MainActivity].
*/
@ -24,12 +27,27 @@ class MainViewModel @Inject constructor(
private val specialCircumstanceManager: SpecialCircumstanceManager,
private val intentManager: IntentManager,
settingsRepository: SettingsRepository,
private val savedStateHandle: SavedStateHandle,
) : BaseViewModel<MainState, Unit, MainAction>(
MainState(
theme = settingsRepository.appTheme,
),
) {
private var specialCircumstance: SpecialCircumstance?
get() = savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY]
set(value) {
savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY] = value
}
init {
// Immediately restore the special circumstance if we have one and then listen for changes
specialCircumstanceManager.specialCircumstance = specialCircumstance
specialCircumstanceManager
.specialCircumstanceStateFlow
.onEach { specialCircumstance = it }
.launchIn(viewModelScope)
settingsRepository
.appThemeStateFlow
.onEach { trySendAction(MainAction.Internal.ThemeUpdate(it)) }

View file

@ -1,15 +1,18 @@
package com.x8bit.bitwarden.data.platform.manager.model
import android.os.Parcelable
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import kotlinx.parcelize.Parcelize
/**
* Represents a special circumstance the app may be in. These circumstances could require some kind
* of navigation that is counter to what otherwise may happen based on the state of the app.
*/
sealed class SpecialCircumstance {
sealed class SpecialCircumstance : Parcelable {
/**
* The app was launched in order to create/share a new Send using the given [data].
*/
@Parcelize
data class ShareNewSend(
val data: IntentManager.ShareData,
val shouldFinishWhenComplete: Boolean,

View file

@ -2,9 +2,11 @@ package com.x8bit.bitwarden.ui.platform.manager.intent
import android.content.Intent
import android.net.Uri
import android.os.Parcelable
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.compose.runtime.Composable
import kotlinx.parcelize.Parcelize
/**
* A manager class for simplifying the handling of Android Intents within a given context.
@ -74,19 +76,21 @@ interface IntentManager {
/**
* Represents file information.
*/
@Parcelize
data class FileData(
val fileName: String,
val uri: Uri,
val sizeBytes: Long,
)
) : Parcelable
/**
* Represents data for a share request coming from outside the app.
*/
sealed class ShareData {
sealed class ShareData : Parcelable {
/**
* The data required to create a new Text Send.
*/
@Parcelize
data class TextSend(
val subject: String?,
val text: String,
@ -95,6 +99,7 @@ interface IntentManager {
/**
* The data required to create a new File Send.
*/
@Parcelize
data class FileSend(
val fileData: FileData,
) : ShareData()

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden
import android.content.Intent
import androidx.lifecycle.SavedStateHandle
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.util.getCaptchaCallbackTokenResult
@ -16,6 +17,7 @@ import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class MainViewModelTest : BaseViewModelTest() {
@ -34,6 +36,38 @@ class MainViewModelTest : BaseViewModelTest() {
private val intentManager: IntentManager = mockk {
every { getShareDataFromIntent(any()) } returns null
}
private val savedStateHandle = SavedStateHandle()
@Suppress("MaxLineLength")
@Test
fun `initialization should set a saved SpecialCircumstance to the SpecialCircumstanceManager if present`() {
assertNull(specialCircumstanceManager.specialCircumstance)
val specialCircumstance = mockk<SpecialCircumstance>()
createViewModel(
initialSpecialCircumstance = specialCircumstance,
)
assertEquals(
specialCircumstance,
specialCircumstanceManager.specialCircumstance,
)
}
@Test
fun `SpecialCircumstance updates should update the SavedStateHandle`() {
createViewModel()
assertNull(savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY])
val specialCircumstance = mockk<SpecialCircumstance>()
specialCircumstanceManager.specialCircumstance = specialCircumstance
assertEquals(
specialCircumstance,
savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY],
)
}
@Test
fun `on AppThemeChanged should update state`() {
@ -109,13 +143,19 @@ class MainViewModelTest : BaseViewModelTest() {
)
}
private fun createViewModel() = MainViewModel(
private fun createViewModel(
initialSpecialCircumstance: SpecialCircumstance? = null,
) = MainViewModel(
specialCircumstanceManager = specialCircumstanceManager,
settingsRepository = settingsRepository,
intentManager = intentManager,
savedStateHandle = savedStateHandle.apply {
set(SPECIAL_CIRCUMSTANCE_KEY, initialSpecialCircumstance)
},
)
companion object {
private const val SPECIAL_CIRCUMSTANCE_KEY = "special-circumstance"
private const val USER_ID = "userID"
private val DEFAULT_USER_STATE = UserState(
activeUserId = USER_ID,