Fix login detail pages loosing user changes on rotation / re-creation (closes #775) (#778)

Inject view models in composables using hilt
This commit is contained in:
Sunik Kupfer 2024-05-07 10:17:25 +02:00 committed by GitHub
parent 952cb52b95
commit ad24dd54c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 105 additions and 48 deletions

View file

@ -32,7 +32,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.hilt.navigation.compose.hiltViewModel
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
@ -56,10 +56,11 @@ object AdvancedLogin : LoginType {
initialLoginInfo: LoginInfo,
onLogin: (LoginInfo) -> Unit
) {
val model = viewModel<AdvancedLoginModel>()
LaunchedEffect(Unit) {
model.initialize(initialLoginInfo)
}
val model: AdvancedLoginModel = hiltViewModel(
creationCallback = { factory: AdvancedLoginModel.Factory ->
factory.create(loginInfo = initialLoginInfo)
}
)
val uiState = model.uiState
AdvancedLoginScreen(

View file

@ -10,9 +10,21 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.util.DavUtils.toURIorNull
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import org.apache.commons.lang3.StringUtils
class AdvancedLoginModel: ViewModel() {
@HiltViewModel(assistedFactory = AdvancedLoginModel.Factory::class)
class AdvancedLoginModel @AssistedInject constructor(
@Assisted val initialLoginInfo: LoginInfo,
): ViewModel() {
@AssistedFactory
interface Factory {
fun create(loginInfo: LoginInfo): AdvancedLoginModel
}
data class UiState(
val url: String = "",
@ -44,12 +56,12 @@ class AdvancedLoginModel: ViewModel() {
var uiState by mutableStateOf(UiState())
private set
fun initialize(loginInfo: LoginInfo) {
init {
uiState = uiState.copy(
url = loginInfo.baseUri?.toString()?.removePrefix("https://") ?: "",
username = loginInfo.credentials?.username ?: "",
password = loginInfo.credentials?.password ?: "",
certAlias = loginInfo.credentials?.certificateAlias ?: ""
url = initialLoginInfo.baseUri?.toString()?.removePrefix("https://") ?: "",
username = initialLoginInfo.credentials?.username ?: "",
password = initialLoginInfo.credentials?.password ?: "",
certAlias = initialLoginInfo.credentials?.certificateAlias ?: ""
)
}

View file

@ -30,7 +30,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.hilt.navigation.compose.hiltViewModel
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
@ -53,10 +53,11 @@ object EmailLogin : LoginType {
initialLoginInfo: LoginInfo,
onLogin: (LoginInfo) -> Unit
) {
val model = viewModel<EmailLoginModel>()
LaunchedEffect(initialLoginInfo) {
model.initialize(initialLoginInfo)
}
val model: EmailLoginModel = hiltViewModel(
creationCallback = { factory: EmailLoginModel.Factory ->
factory.create(loginInfo = initialLoginInfo)
}
)
val uiState = model.uiState
EmailLoginScreen(

View file

@ -10,8 +10,20 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.util.DavUtils.toURIorNull
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
class EmailLoginModel: ViewModel() {
@HiltViewModel(assistedFactory = EmailLoginModel.Factory::class)
class EmailLoginModel @AssistedInject constructor(
@Assisted val initialLoginInfo: LoginInfo
): ViewModel() {
@AssistedFactory
interface Factory {
fun create(loginInfo: LoginInfo): EmailLoginModel
}
data class UiState(
val email: String = "",
@ -35,7 +47,7 @@ class EmailLoginModel: ViewModel() {
var uiState by mutableStateOf(UiState())
private set
fun initialize(initialLoginInfo: LoginInfo) {
init {
uiState = uiState.copy(
email = initialLoginInfo.credentials?.username ?: "",
password = initialLoginInfo.credentials?.password ?: ""

View file

@ -45,7 +45,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.hilt.navigation.compose.hiltViewModel
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.Constants.withStatParams
import at.bitfire.davdroid.R
@ -86,10 +86,11 @@ object GoogleLogin : LoginType {
initialLoginInfo: LoginInfo,
onLogin: (LoginInfo) -> Unit
) {
val model = viewModel<GoogleLoginModel>()
LaunchedEffect(initialLoginInfo) {
model.initialize(initialLoginInfo)
}
val model: GoogleLoginModel = hiltViewModel(
creationCallback = { factory: GoogleLoginModel.Factory ->
factory.create(loginInfo = initialLoginInfo)
}
)
val uiState = model.uiState
LaunchedEffect(uiState.result) {

View file

@ -17,6 +17,9 @@ import androidx.lifecycle.viewModelScope
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.GoogleLogin
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import net.openid.appauth.AuthorizationRequest
@ -25,14 +28,19 @@ import net.openid.appauth.AuthorizationService
import org.apache.commons.lang3.StringUtils
import java.util.Locale
import java.util.logging.Level
import javax.inject.Inject
@HiltViewModel
class GoogleLoginModel @Inject constructor(
@HiltViewModel(assistedFactory = GoogleLoginModel.Factory::class)
class GoogleLoginModel @AssistedInject constructor(
@Assisted val initialLoginInfo: LoginInfo,
val context: Application,
val authService: AuthorizationService
): ViewModel() {
@AssistedFactory
interface Factory {
fun create(loginInfo: LoginInfo): GoogleLoginModel
}
val googleLogin = GoogleLogin(authService)
override fun onCleared() {
@ -55,9 +63,9 @@ class GoogleLoginModel @Inject constructor(
var uiState by mutableStateOf(UiState())
private set
fun initialize(loginInfo: LoginInfo) {
init {
uiState = uiState.copy(
email = loginInfo.credentials?.username ?: findGoogleAccount() ?: "",
email = initialLoginInfo.credentials?.username ?: findGoogleAccount() ?: "",
error = null,
result = null
)

View file

@ -42,7 +42,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.hilt.navigation.compose.hiltViewModel
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.Constants.withStatParams
import at.bitfire.davdroid.R
@ -69,10 +69,11 @@ object NextcloudLogin : LoginType {
initialLoginInfo: LoginInfo,
onLogin: (LoginInfo) -> Unit
) {
val model = viewModel<NextcloudLoginModel>()
LaunchedEffect(initialLoginInfo) {
model.initialize(initialLoginInfo)
}
val model: NextcloudLoginModel = hiltViewModel(
creationCallback = { factory: NextcloudLoginModel.Factory ->
factory.create(loginInfo = initialLoginInfo)
}
)
val context = LocalContext.current
val checkResultCallback = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {

View file

@ -12,19 +12,27 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.NextcloudLoginFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.util.logging.Level
import javax.inject.Inject
@HiltViewModel
class NextcloudLoginModel @Inject constructor(
@HiltViewModel(assistedFactory = NextcloudLoginModel.Factory::class)
class NextcloudLoginModel @AssistedInject constructor(
@Assisted val initialLoginInfo: LoginInfo,
val context: Application
//val state: SavedStateHandle
): ViewModel() {
@AssistedFactory
interface Factory {
fun create(loginInfo: LoginInfo): NextcloudLoginModel
}
/*companion object {
const val STATE_POLL_URL = "poll_url"
const val STATE_TOKEN = "token"
@ -59,8 +67,8 @@ class NextcloudLoginModel @Inject constructor(
var uiState by mutableStateOf(UiState())
private set
fun initialize(loginInfo: LoginInfo) {
val baseUri = loginInfo.baseUri
init {
val baseUri = initialLoginInfo.baseUri
if (baseUri != null)
uiState = uiState.copy(
baseUrl = baseUri.toString()

View file

@ -31,7 +31,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.hilt.navigation.compose.hiltViewModel
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
@ -53,10 +53,11 @@ object UrlLogin : LoginType {
initialLoginInfo: LoginInfo,
onLogin: (LoginInfo) -> Unit
) {
val model = viewModel<UrlLoginModel>()
LaunchedEffect(initialLoginInfo) {
model.initialize(initialLoginInfo)
}
val model: UrlLoginModel = hiltViewModel(
creationCallback = { factory: UrlLoginModel.Factory ->
factory.create(loginInfo = initialLoginInfo)
}
)
val uiState = model.uiState
UrlLoginScreen(

View file

@ -10,9 +10,21 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.util.DavUtils.toURIorNull
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import org.apache.commons.lang3.StringUtils
class UrlLoginModel: ViewModel() {
@HiltViewModel(assistedFactory = UrlLoginModel.Factory::class)
class UrlLoginModel @AssistedInject constructor(
@Assisted val initialLoginInfo: LoginInfo
): ViewModel() {
@AssistedFactory
interface Factory {
fun create(loginInfo: LoginInfo): UrlLoginModel
}
data class UiState(
val url: String = "",
@ -43,11 +55,11 @@ class UrlLoginModel: ViewModel() {
var uiState by mutableStateOf(UiState())
private set
fun initialize(loginInfo: LoginInfo) {
init {
uiState = UiState(
url = loginInfo.baseUri?.toString()?.removePrefix("https://") ?: "",
username = loginInfo.credentials?.username ?: "",
password = loginInfo.credentials?.password ?: ""
url = initialLoginInfo.baseUri?.toString()?.removePrefix("https://") ?: "",
username = initialLoginInfo.credentials?.username ?: "",
password = initialLoginInfo.credentials?.password ?: ""
)
}