From bfe7b49336bc8790c552c5518583b8a7e8d8338d Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Tue, 4 Jul 2023 02:05:10 +0200 Subject: [PATCH 1/4] Fix autofill recomposition issues --- app/src/main/java/com/jerboa/Utils.kt | 17 +++---- .../com/jerboa/ui/components/login/Login.kt | 44 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/jerboa/Utils.kt b/app/src/main/java/com/jerboa/Utils.kt index e7dd822dc..82a9bc8e6 100644 --- a/app/src/main/java/com/jerboa/Utils.kt +++ b/app/src/main/java/com/jerboa/Utils.kt @@ -29,9 +29,10 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.autofill.Autofill import androidx.compose.ui.autofill.AutofillNode +import androidx.compose.ui.autofill.AutofillTree import androidx.compose.ui.autofill.AutofillType -import androidx.compose.ui.composed import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color @@ -40,8 +41,6 @@ import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalAutofill -import androidx.compose.ui.platform.LocalAutofillTree import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit @@ -64,6 +63,7 @@ import com.jerboa.ui.components.home.SiteViewModel import com.jerboa.ui.components.inbox.InboxTab import com.jerboa.ui.components.person.UserTab import com.jerboa.ui.theme.SMALL_PADDING +import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.ocpsoft.prettytime.PrettyTime @@ -956,18 +956,15 @@ fun saveBitmapP( val mimeTypes = if (mimeType == null) null else arrayOf(mimeType) MediaScannerConnection.scanFile(context, arrayOf(dest.absolutePath), mimeTypes, null) } - @OptIn(ExperimentalComposeUiApi::class) -fun Modifier.onAutofill(vararg autofillType: AutofillType, onFill: (String) -> Unit): Modifier = composed { +fun Modifier.onAutofill(tree: AutofillTree, autofill: Autofill?, autofillTypes: ImmutableList, onFill: (String) -> Unit): Modifier { val autofillNode = AutofillNode( - autofillTypes = autofillType.toList(), + autofillTypes = autofillTypes, onFill = onFill, ) - LocalAutofillTree.current += autofillNode - - val autofill = LocalAutofill.current + tree += autofillNode - this + return this .onGloballyPositioned { autofillNode.boundingBox = it.boundsInWindow() } diff --git a/app/src/main/java/com/jerboa/ui/components/login/Login.kt b/app/src/main/java/com/jerboa/ui/components/login/Login.kt index 790c842bf..b3284c082 100644 --- a/app/src/main/java/com/jerboa/ui/components/login/Login.kt +++ b/app/src/main/java/com/jerboa/ui/components/login/Login.kt @@ -1,4 +1,4 @@ -@file:OptIn(ExperimentalMaterial3Api::class) +@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) package com.jerboa.ui.components.login @@ -22,6 +22,8 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.autofill.AutofillType import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalAutofill +import androidx.compose.ui.platform.LocalAutofillTree import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType @@ -36,6 +38,8 @@ import com.jerboa.R import com.jerboa.datatypes.types.Login import com.jerboa.db.Account import com.jerboa.onAutofill +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf @Composable fun MyTextField( @@ -44,7 +48,10 @@ fun MyTextField( placeholder: String? = null, text: String, onValueChange: (String) -> Unit, + autofillTypes: ImmutableList = persistentListOf(), ) { + var wasAutofilled by remember { mutableStateOf(false) } + OutlinedTextField( value = text, onValueChange = onValueChange, @@ -56,7 +63,11 @@ fun MyTextField( keyboardType = KeyboardType.Text, autoCorrect = false, ), - modifier = modifier, + modifier = modifier.background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) + .onAutofill(LocalAutofillTree.current, LocalAutofill.current, autofillTypes) { + onValueChange(it) + wasAutofilled = true + }, ) } @@ -66,10 +77,16 @@ fun PasswordField( password: String, onValueChange: (String) -> Unit, ) { + var wasAutofilled by remember { mutableStateOf(false) } var passwordVisibility by remember { mutableStateOf(false) } OutlinedTextField( - modifier = modifier, + modifier = modifier + .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) + .onAutofill(LocalAutofillTree.current, LocalAutofill.current, persistentListOf(AutofillType.Password)) { + onValueChange(it) + wasAutofilled = true + }, value = password, onValueChange = onValueChange, singleLine = true, @@ -105,7 +122,6 @@ fun LoginForm( var totp by rememberSaveable { mutableStateOf("") } val instanceOptions = DEFAULT_LEMMY_INSTANCES var expanded by remember { mutableStateOf(false) } - var wasAutofilled by remember { mutableStateOf(false) } val isValid = instance.isNotEmpty() && username.isNotEmpty() && password.isNotEmpty() @@ -164,36 +180,20 @@ fun LoginForm( } MyTextField( - modifier = Modifier - .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) - .onAutofill(AutofillType.Username, AutofillType.EmailAddress) { - username = it - wasAutofilled = true - }, label = stringResource(R.string.login_email_or_username), text = username, onValueChange = { username = it }, + autofillTypes = persistentListOf(AutofillType.Username, AutofillType.EmailAddress), ) PasswordField( - modifier = Modifier - .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) - .onAutofill(AutofillType.Password) { - password = it - wasAutofilled = true - }, password = password, onValueChange = { password = it }, ) MyTextField( - modifier = Modifier - .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) - .onAutofill(AutofillType.SmsOtpCode) { - totp = it - wasAutofilled = true - }, label = stringResource(R.string.login_totp), text = totp, onValueChange = { totp = it }, + autofillTypes = persistentListOf(AutofillType.SmsOtpCode), ) Button( enabled = isValid && !loading, From b371a2850948a1a3b022707ac18b20c420acd6f5 Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Tue, 4 Jul 2023 08:36:53 +0200 Subject: [PATCH 2/4] Fix dropdown issue --- .../com/jerboa/ui/components/login/Login.kt | 96 ++++++++++--------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/jerboa/ui/components/login/Login.kt b/app/src/main/java/com/jerboa/ui/components/login/Login.kt index b3284c082..5ed109aef 100644 --- a/app/src/main/java/com/jerboa/ui/components/login/Login.kt +++ b/app/src/main/java/com/jerboa/ui/components/login/Login.kt @@ -63,7 +63,8 @@ fun MyTextField( keyboardType = KeyboardType.Text, autoCorrect = false, ), - modifier = modifier.background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) + modifier = modifier + .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) .onAutofill(LocalAutofillTree.current, LocalAutofill.current, autofillTypes) { onValueChange(it) wasAutofilled = true @@ -83,7 +84,11 @@ fun PasswordField( OutlinedTextField( modifier = modifier .background(if (wasAutofilled) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent) - .onAutofill(LocalAutofillTree.current, LocalAutofill.current, persistentListOf(AutofillType.Password)) { + .onAutofill( + LocalAutofillTree.current, + LocalAutofill.current, + persistentListOf(AutofillType.Password) + ) { onValueChange(it) wasAutofilled = true }, @@ -109,7 +114,50 @@ fun PasswordField( ) } -@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) +@Composable +fun InstancePicker(expanded: Boolean, setExpanded: ((Boolean) -> Unit), instance: String, setInstance: ((String) -> Unit) ){ + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { + setExpanded(!expanded) + }, + ) { + OutlinedTextField( + modifier = Modifier.menuAnchor(), + label = { Text(stringResource(R.string.login_instance)) }, + placeholder = { Text(stringResource(R.string.login_instance_placeholder)) }, + value = instance, + singleLine = true, + onValueChange = setInstance, + trailingIcon = { + TrailingIcon(expanded = expanded) + }, + keyboardOptions = KeyboardOptions(autoCorrect = false, keyboardType = KeyboardType.Uri), + ) + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { + setExpanded(false) + }, + ) { + DEFAULT_LEMMY_INSTANCES.forEach { selectionOption -> + DropdownMenuItem( + modifier = Modifier.exposedDropdownSize(), + text = { + Text(text = selectionOption) + }, + onClick = { + setInstance(selectionOption) + setExpanded(false) + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, + ) + } + } + } +} + +@OptIn(ExperimentalComposeUiApi::class) @Composable fun LoginForm( modifier: Modifier = Modifier, @@ -120,7 +168,6 @@ fun LoginForm( var username by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") } var totp by rememberSaveable { mutableStateOf("") } - val instanceOptions = DEFAULT_LEMMY_INSTANCES var expanded by remember { mutableStateOf(false) } val isValid = @@ -139,45 +186,8 @@ fun LoginForm( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { - expanded = !expanded - }, - ) { - OutlinedTextField( - modifier = Modifier.menuAnchor(), - label = { Text(stringResource(R.string.login_instance)) }, - placeholder = { Text(stringResource(R.string.login_instance_placeholder)) }, - value = instance, - singleLine = true, - onValueChange = { instance = it }, - trailingIcon = { - TrailingIcon(expanded = expanded) - }, - keyboardOptions = KeyboardOptions(autoCorrect = false, keyboardType = KeyboardType.Uri), - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { - expanded = false - }, - ) { - instanceOptions.forEach { selectionOption -> - DropdownMenuItem( - modifier = Modifier.exposedDropdownSize(), - text = { - Text(text = selectionOption) - }, - onClick = { - instance = selectionOption - expanded = false - }, - contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, - ) - } - } - } + + InstancePicker(expanded = expanded, {expanded = it}, instance, {instance = it} ) MyTextField( label = stringResource(R.string.login_email_or_username), From b2f66dd553bc7e8d16f50d224f11314c4c90618a Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Wed, 5 Jul 2023 13:10:10 +0200 Subject: [PATCH 3/4] Fix formatting --- app/src/main/java/com/jerboa/Utils.kt | 1 + app/src/main/java/com/jerboa/ui/components/login/Login.kt | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/jerboa/Utils.kt b/app/src/main/java/com/jerboa/Utils.kt index 82a9bc8e6..4477c4cd0 100644 --- a/app/src/main/java/com/jerboa/Utils.kt +++ b/app/src/main/java/com/jerboa/Utils.kt @@ -956,6 +956,7 @@ fun saveBitmapP( val mimeTypes = if (mimeType == null) null else arrayOf(mimeType) MediaScannerConnection.scanFile(context, arrayOf(dest.absolutePath), mimeTypes, null) } + @OptIn(ExperimentalComposeUiApi::class) fun Modifier.onAutofill(tree: AutofillTree, autofill: Autofill?, autofillTypes: ImmutableList, onFill: (String) -> Unit): Modifier { val autofillNode = AutofillNode( diff --git a/app/src/main/java/com/jerboa/ui/components/login/Login.kt b/app/src/main/java/com/jerboa/ui/components/login/Login.kt index 5ed109aef..fa7deb754 100644 --- a/app/src/main/java/com/jerboa/ui/components/login/Login.kt +++ b/app/src/main/java/com/jerboa/ui/components/login/Login.kt @@ -72,6 +72,7 @@ fun MyTextField( ) } +@OptIn(ExperimentalComposeUiApi::class) @Composable fun PasswordField( modifier: Modifier = Modifier, @@ -87,7 +88,7 @@ fun PasswordField( .onAutofill( LocalAutofillTree.current, LocalAutofill.current, - persistentListOf(AutofillType.Password) + persistentListOf(AutofillType.Password), ) { onValueChange(it) wasAutofilled = true @@ -115,7 +116,7 @@ fun PasswordField( } @Composable -fun InstancePicker(expanded: Boolean, setExpanded: ((Boolean) -> Unit), instance: String, setInstance: ((String) -> Unit) ){ +fun InstancePicker(expanded: Boolean, setExpanded: ((Boolean) -> Unit), instance: String, setInstance: ((String) -> Unit)) { ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { @@ -186,8 +187,7 @@ fun LoginForm( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - - InstancePicker(expanded = expanded, {expanded = it}, instance, {instance = it} ) + InstancePicker(expanded = expanded, { expanded = it }, instance, { instance = it }) MyTextField( label = stringResource(R.string.login_email_or_username), From bd70c15149f58ea7a81a1d5393b3b1471cf1884a Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Sat, 8 Jul 2023 09:44:07 +0200 Subject: [PATCH 4/4] Fix conflicts issues --- app/src/main/java/com/jerboa/ui/components/login/Login.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/jerboa/ui/components/login/Login.kt b/app/src/main/java/com/jerboa/ui/components/login/Login.kt index edc3d7203..ac687de46 100644 --- a/app/src/main/java/com/jerboa/ui/components/login/Login.kt +++ b/app/src/main/java/com/jerboa/ui/components/login/Login.kt @@ -146,7 +146,7 @@ fun InstancePicker(expanded: Boolean, setExpanded: ((Boolean) -> Unit), instance if (filteringOptions.isNotEmpty()) { DropdownMenu( expanded = expanded, - onDismissRequest = {}, + onDismissRequest = { setExpanded(false) }, properties = PopupProperties(focusable = false), modifier = Modifier.exposedDropdownSize(true), ) { @@ -220,7 +220,10 @@ fun LoginForm( modifier = Modifier.padding(top = 10.dp), ) { if (loading) { - CircularProgressIndicator(modifier = Modifier.size(LocalTextStyle.current.fontSize.value.dp)) + CircularProgressIndicator( + modifier = Modifier.size(LocalTextStyle.current.fontSize.value.dp), + strokeWidth = 2.dp, + ) } else { Text(stringResource(R.string.login_login)) }