Add app password hint under password field (#1507)

* Add app password hint under password field

Signed-off-by: Sunik Kupfer <kupfer@bitfire.at>

* Change text to use prefer

Signed-off-by: Sunik Kupfer <kupfer@bitfire.at>

* Append path encoded

Signed-off-by: Sunik Kupfer <kupfer@bitfire.at>

* Update app password help URL and text

---------

Signed-off-by: Sunik Kupfer <kupfer@bitfire.at>
Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
Sunik Kupfer
2025-06-04 15:42:40 +02:00
committed by GitHub
parent 6fbaea9487
commit 0fed85fdc3
3 changed files with 54 additions and 26 deletions

View File

@@ -26,12 +26,14 @@ object Constants {
const val MANUAL_PATH_ACCOUNTS_COLLECTIONS = "accounts_collections.html"
const val MANUAL_FRAGMENT_SERVICE_DISCOVERY = "how-does-service-discovery-work"
const val MANUAL_PATH_INTRODUCTION = "introduction.html"
const val MANUAL_FRAGMENT_AUTHENTICATION_METHODS = "authentication-methods"
const val MANUAL_PATH_SETTINGS = "settings.html"
const val MANUAL_FRAGMENT_APP_SETTINGS = "app-wide-settings"
const val MANUAL_FRAGMENT_ACCOUNT_SETTINGS = "account-settings"
const val MANUAL_PATH_WEBDAV_PUSH = "webdav_push.html"
const val MANUAL_PATH_WEBDAV_MOUNTS = "webdav_mounts.html"
val COMMUNITY_URL = "https://github.com/bitfireAT/davx5-ose/discussions".toUri()

View File

@@ -4,7 +4,10 @@
package at.bitfire.davdroid.ui.composable
import android.net.Uri
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
@@ -25,7 +28,11 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
@Composable
fun PasswordTextField(
@@ -42,33 +49,51 @@ fun PasswordTextField(
) {
var passwordVisible by remember { mutableStateOf(false) }
OutlinedTextField(
value = password,
onValueChange = onPasswordChange,
label = labelText?.let { { Text(it) } },
leadingIcon = leadingIcon,
isError = isError,
singleLine = true,
enabled = enabled,
readOnly = readOnly,
modifier = modifier.focusGroup(),
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(
enabled = enabled,
onClick = { passwordVisible = !passwordVisible }
) {
if (passwordVisible)
Icon(Icons.Default.VisibilityOff, stringResource(R.string.login_password_hide))
else
Icon(Icons.Default.Visibility, stringResource(R.string.login_password_show))
Column {
OutlinedTextField(
value = password,
onValueChange = onPasswordChange,
label = labelText?.let { { Text(it) } },
leadingIcon = leadingIcon,
isError = isError,
singleLine = true,
enabled = enabled,
readOnly = readOnly,
modifier = modifier.focusGroup(),
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(
enabled = enabled,
onClick = { passwordVisible = !passwordVisible }
) {
if (passwordVisible)
Icon(Icons.Default.VisibilityOff, stringResource(R.string.login_password_hide))
else
Icon(Icons.Default.Visibility, stringResource(R.string.login_password_show))
}
}
}
)
)
Text(
modifier = Modifier.padding(vertical = 8.dp),
text = HtmlCompat.fromHtml(
stringResource(
R.string.settings_app_password_hint,
appPasswordHelpUrl().toString()
),
0
).toAnnotatedString()
)
}
}
fun appPasswordHelpUrl(): Uri = Constants.MANUAL_URL.buildUpon()
.appendPath(Constants.MANUAL_PATH_INTRODUCTION)
.fragment(Constants.MANUAL_FRAGMENT_AUTHENTICATION_METHODS)
.build()
@Composable
@Preview
fun PasswordTextField_Sample() {

View File

@@ -367,7 +367,8 @@
<string name="settings_ignore_vpns_off">VPN without underlying validated Internet connection is enough to run synchronization</string>
<string name="settings_authentication">Authentication</string>
<string name="settings_username">User name</string>
<string name="settings_password">Password</string>
<string name="settings_password">Password or app password</string>
<string name="settings_app_password_hint"><![CDATA[You may prefer to use an <a href="%1$s">app password</a>.]]></string>
<string name="settings_new_password">New password</string>
<string name="settings_password_summary">Update the password according to your server.</string>
<string name="settings_certificate_alias">Client certificate</string>