2023-10-03 22:34:51 +03:00
|
|
|
package com.x8bit.bitwarden
|
|
|
|
|
|
|
|
import android.content.Intent
|
2024-01-17 02:41:34 +03:00
|
|
|
import android.os.Parcelable
|
2024-01-26 18:35:31 +03:00
|
|
|
import androidx.lifecycle.SavedStateHandle
|
2024-01-17 02:41:34 +03:00
|
|
|
import androidx.lifecycle.viewModelScope
|
2024-01-28 21:25:15 +03:00
|
|
|
import com.bitwarden.core.CipherView
|
|
|
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
2024-01-27 22:48:39 +03:00
|
|
|
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
|
2024-01-23 01:14:06 +03:00
|
|
|
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
|
|
|
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
2024-01-17 02:41:34 +03:00
|
|
|
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
|
|
|
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
|
|
|
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
|
2024-01-20 00:01:43 +03:00
|
|
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
2023-10-03 22:34:51 +03:00
|
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
2024-01-17 02:41:34 +03:00
|
|
|
import kotlinx.coroutines.flow.launchIn
|
|
|
|
import kotlinx.coroutines.flow.onEach
|
|
|
|
import kotlinx.coroutines.flow.update
|
|
|
|
import kotlinx.parcelize.Parcelize
|
2023-10-03 22:34:51 +03:00
|
|
|
import javax.inject.Inject
|
|
|
|
|
2024-01-26 18:35:31 +03:00
|
|
|
private const val SPECIAL_CIRCUMSTANCE_KEY = "special-circumstance"
|
|
|
|
|
2023-10-03 22:34:51 +03:00
|
|
|
/**
|
|
|
|
* A view model that helps launch actions for the [MainActivity].
|
|
|
|
*/
|
|
|
|
@HiltViewModel
|
|
|
|
class MainViewModel @Inject constructor(
|
2024-01-28 21:25:15 +03:00
|
|
|
private val autofillSelectionManager: AutofillSelectionManager,
|
2024-01-23 01:14:06 +03:00
|
|
|
private val specialCircumstanceManager: SpecialCircumstanceManager,
|
2024-01-20 00:01:43 +03:00
|
|
|
private val intentManager: IntentManager,
|
2024-01-17 02:41:34 +03:00
|
|
|
settingsRepository: SettingsRepository,
|
2024-01-26 18:35:31 +03:00
|
|
|
private val savedStateHandle: SavedStateHandle,
|
2024-01-26 19:26:17 +03:00
|
|
|
) : BaseViewModel<MainState, MainEvent, MainAction>(
|
2024-01-17 02:41:34 +03:00
|
|
|
MainState(
|
|
|
|
theme = settingsRepository.appTheme,
|
|
|
|
),
|
|
|
|
) {
|
2024-01-26 18:35:31 +03:00
|
|
|
private var specialCircumstance: SpecialCircumstance?
|
|
|
|
get() = savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY]
|
|
|
|
set(value) {
|
|
|
|
savedStateHandle[SPECIAL_CIRCUMSTANCE_KEY] = value
|
|
|
|
}
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
init {
|
2024-01-26 18:35:31 +03:00
|
|
|
// Immediately restore the special circumstance if we have one and then listen for changes
|
|
|
|
specialCircumstanceManager.specialCircumstance = specialCircumstance
|
|
|
|
|
|
|
|
specialCircumstanceManager
|
|
|
|
.specialCircumstanceStateFlow
|
|
|
|
.onEach { specialCircumstance = it }
|
|
|
|
.launchIn(viewModelScope)
|
|
|
|
|
2024-01-28 21:25:15 +03:00
|
|
|
autofillSelectionManager
|
|
|
|
.autofillSelectionFlow
|
|
|
|
.onEach { trySendAction(MainAction.Internal.AutofillSelectionReceive(it)) }
|
|
|
|
.launchIn(viewModelScope)
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
settingsRepository
|
|
|
|
.appThemeStateFlow
|
|
|
|
.onEach { trySendAction(MainAction.Internal.ThemeUpdate(it)) }
|
|
|
|
.launchIn(viewModelScope)
|
2024-01-26 19:26:17 +03:00
|
|
|
|
|
|
|
settingsRepository
|
|
|
|
.isScreenCaptureAllowedStateFlow
|
|
|
|
.onEach { isAllowed ->
|
|
|
|
sendEvent(MainEvent.ScreenCaptureSettingChange(isAllowed))
|
|
|
|
}
|
|
|
|
.launchIn(viewModelScope)
|
2024-01-17 02:41:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun handleAction(action: MainAction) {
|
2023-10-03 22:34:51 +03:00
|
|
|
when (action) {
|
2024-01-28 21:25:15 +03:00
|
|
|
is MainAction.Internal.AutofillSelectionReceive -> {
|
|
|
|
handleAutofillSelectionReceive(action)
|
|
|
|
}
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
is MainAction.Internal.ThemeUpdate -> handleAppThemeUpdated(action)
|
2024-01-20 00:01:43 +03:00
|
|
|
is MainAction.ReceiveFirstIntent -> handleFirstIntentReceived(action)
|
2024-01-17 02:41:34 +03:00
|
|
|
is MainAction.ReceiveNewIntent -> handleNewIntentReceived(action)
|
2023-10-03 22:34:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 21:25:15 +03:00
|
|
|
private fun handleAutofillSelectionReceive(
|
|
|
|
action: MainAction.Internal.AutofillSelectionReceive,
|
|
|
|
) {
|
|
|
|
sendEvent(MainEvent.CompleteAutofill(cipherView = action.cipherView))
|
|
|
|
}
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
private fun handleAppThemeUpdated(action: MainAction.Internal.ThemeUpdate) {
|
|
|
|
mutableStateFlow.update { it.copy(theme = action.theme) }
|
|
|
|
}
|
|
|
|
|
2024-01-20 00:01:43 +03:00
|
|
|
private fun handleFirstIntentReceived(action: MainAction.ReceiveFirstIntent) {
|
2024-01-22 18:08:28 +03:00
|
|
|
handleIntent(
|
|
|
|
intent = action.intent,
|
|
|
|
isFirstIntent = true,
|
|
|
|
)
|
2024-01-20 00:01:43 +03:00
|
|
|
}
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
private fun handleNewIntentReceived(action: MainAction.ReceiveNewIntent) {
|
2024-01-22 18:08:28 +03:00
|
|
|
handleIntent(
|
|
|
|
intent = action.intent,
|
|
|
|
isFirstIntent = false,
|
|
|
|
)
|
|
|
|
}
|
2023-10-03 22:34:51 +03:00
|
|
|
|
2024-01-22 18:08:28 +03:00
|
|
|
private fun handleIntent(
|
|
|
|
intent: Intent,
|
|
|
|
isFirstIntent: Boolean,
|
|
|
|
) {
|
2024-01-27 22:48:39 +03:00
|
|
|
val autofillSelectionData = intent.getAutofillSelectionDataOrNull()
|
2024-01-22 18:08:28 +03:00
|
|
|
val shareData = intentManager.getShareDataFromIntent(intent)
|
|
|
|
when {
|
2024-01-27 22:48:39 +03:00
|
|
|
autofillSelectionData != null -> {
|
|
|
|
specialCircumstanceManager.specialCircumstance =
|
|
|
|
SpecialCircumstance.AutofillSelection(
|
|
|
|
autofillSelectionData = autofillSelectionData,
|
|
|
|
// Allow users back into the already-running app when completing the
|
|
|
|
// autofill task when this is not the first intent.
|
|
|
|
shouldFinishWhenComplete = isFirstIntent,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-01-20 00:01:43 +03:00
|
|
|
shareData != null -> {
|
2024-01-23 01:14:06 +03:00
|
|
|
specialCircumstanceManager.specialCircumstance =
|
|
|
|
SpecialCircumstance.ShareNewSend(
|
2024-01-20 00:01:43 +03:00
|
|
|
data = shareData,
|
|
|
|
// Allow users back into the already-running app when completing the
|
2024-01-22 18:08:28 +03:00
|
|
|
// Send task when this is not the first intent.
|
|
|
|
shouldFinishWhenComplete = isFirstIntent,
|
2024-01-20 00:01:43 +03:00
|
|
|
)
|
|
|
|
}
|
2023-10-03 22:34:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
/**
|
|
|
|
* Models state for the [MainActivity].
|
|
|
|
*/
|
|
|
|
@Parcelize
|
|
|
|
data class MainState(
|
|
|
|
val theme: AppTheme,
|
|
|
|
) : Parcelable
|
|
|
|
|
2023-10-03 22:34:51 +03:00
|
|
|
/**
|
|
|
|
* Models actions for the [MainActivity].
|
|
|
|
*/
|
|
|
|
sealed class MainAction {
|
2024-01-20 00:01:43 +03:00
|
|
|
/**
|
|
|
|
* Receive first Intent by the application.
|
|
|
|
*/
|
|
|
|
data class ReceiveFirstIntent(val intent: Intent) : MainAction()
|
|
|
|
|
2023-10-03 22:34:51 +03:00
|
|
|
/**
|
|
|
|
* Receive Intent by the application.
|
|
|
|
*/
|
|
|
|
data class ReceiveNewIntent(val intent: Intent) : MainAction()
|
2024-01-17 02:41:34 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Actions for internal use by the ViewModel.
|
|
|
|
*/
|
|
|
|
sealed class Internal : MainAction() {
|
2024-01-28 21:25:15 +03:00
|
|
|
/**
|
|
|
|
* Indicates the user has manually selected the given [cipherView] for autofill.
|
|
|
|
*/
|
|
|
|
data class AutofillSelectionReceive(
|
|
|
|
val cipherView: CipherView,
|
|
|
|
) : Internal()
|
|
|
|
|
2024-01-17 02:41:34 +03:00
|
|
|
/**
|
|
|
|
* Indicates that the app theme has changed.
|
|
|
|
*/
|
|
|
|
data class ThemeUpdate(
|
|
|
|
val theme: AppTheme,
|
|
|
|
) : Internal()
|
|
|
|
}
|
2023-10-03 22:34:51 +03:00
|
|
|
}
|
2024-01-26 19:26:17 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents events that are emitted by the [MainViewModel].
|
|
|
|
*/
|
|
|
|
sealed class MainEvent {
|
2024-01-28 21:25:15 +03:00
|
|
|
/**
|
|
|
|
* Event indicating that the user has chosen the given [cipherView] for autofill and that the
|
|
|
|
* process is ready to complete.
|
|
|
|
*/
|
|
|
|
data class CompleteAutofill(val cipherView: CipherView) : MainEvent()
|
2024-01-26 19:26:17 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Event indicating a change in the screen capture setting.
|
|
|
|
*/
|
|
|
|
data class ScreenCaptureSettingChange(val isAllowed: Boolean) : MainEvent()
|
|
|
|
}
|