Add support for autofilling login text fields (#293)
* Add support for autofilling login text fields Support autofilling login text fields via Android Autofill with the experimental AndroidX Compose API for it. Based on https://bryanherbst.com/2021/04/13/compose-autofill/ (with permission). Signed-off-by: Hans Christian Schmitz <git@hcsch.eu> * Move autofill implementation to designsystem library Signed-off-by: Hans Christian Schmitz <git@hcsch.eu> --------- Signed-off-by: Hans Christian Schmitz <git@hcsch.eu>
This commit is contained in:
committed by
GitHub
parent
ad0886391b
commit
30a0da74f0
@@ -49,7 +49,9 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.autofill.AutofillType
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
@@ -77,6 +79,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.designsystem.theme.components.autofill
|
||||
import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
@@ -206,6 +209,7 @@ internal fun ChangeServerSection(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
internal fun LoginForm(
|
||||
state: LoginRootState,
|
||||
@@ -239,7 +243,11 @@ internal fun LoginForm(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onTabOrEnterKeyFocusNext(focusManager)
|
||||
.testTag(TestTags.loginEmailUsername),
|
||||
.testTag(TestTags.loginEmailUsername)
|
||||
.autofill(autofillTypes = listOf(AutofillType.Username), onFill = {
|
||||
loginFieldState = it
|
||||
eventSink(LoginRootEvents.SetLogin(it))
|
||||
}),
|
||||
label = {
|
||||
Text(text = stringResource(R.string.screen_login_username_hint))
|
||||
},
|
||||
@@ -279,7 +287,11 @@ internal fun LoginForm(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onTabOrEnterKeyFocusNext(focusManager)
|
||||
.testTag(TestTags.loginPassword),
|
||||
.testTag(TestTags.loginPassword)
|
||||
.autofill(autofillTypes = listOf(AutofillType.Password), onFill = {
|
||||
passwordFieldState = it
|
||||
eventSink(LoginRootEvents.SetPassword(it))
|
||||
}),
|
||||
onValueChange = {
|
||||
passwordFieldState = it
|
||||
eventSink(LoginRootEvents.SetPassword(it))
|
||||
|
||||
@@ -29,8 +29,17 @@ import androidx.compose.material3.TextFieldColors
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.autofill.AutofillNode
|
||||
import androidx.compose.ui.autofill.AutofillType
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.layout.boundsInWindow
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalAutofill
|
||||
import androidx.compose.ui.platform.LocalAutofillTree
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -118,3 +127,26 @@ private fun ContentToPreview() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
fun Modifier.autofill(autofillTypes: List<AutofillType>, onFill: (String) -> Unit) = composed {
|
||||
val autofillNode = AutofillNode(autofillTypes, onFill = onFill)
|
||||
LocalAutofillTree.current += autofillNode
|
||||
|
||||
val autofill = LocalAutofill.current
|
||||
|
||||
this
|
||||
.onGloballyPositioned {
|
||||
// Inform autofill framework of where our composable is so it can show the popup in the right place
|
||||
autofillNode.boundingBox = it.boundsInWindow()
|
||||
}
|
||||
.onFocusChanged {
|
||||
autofill?.run {
|
||||
if (it.isFocused) {
|
||||
requestAutofillForNode(autofillNode)
|
||||
} else {
|
||||
cancelAutofillForNode(autofillNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user