diff --git a/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/AddEditHostScreen.kt b/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/AddEditHostScreen.kt index 3294fd7..1bec6e8 100644 --- a/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/AddEditHostScreen.kt +++ b/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/AddEditHostScreen.kt @@ -485,14 +485,24 @@ fun AddEditHostScreen( value = hostname, onValueChange = { hostname = it }, label = { Text("Hostname") }, + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.Uri, + autoCorrectEnabled = false, + ), + singleLine = true, modifier = Modifier.fillMaxWidth() ) - // Without a declared content type a password manager sees an unlabelled field and offers - // nothing. Username here and Password below give it the pair it needs. OutlinedTextField( value = username, onValueChange = { username = it }, label = { Text("Username") }, + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.PasswordVisible, + autoCorrectEnabled = false, + ), + singleLine = true, modifier = Modifier .fillMaxWidth() .semantics { contentType = ContentType.Username }, diff --git a/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/ImportSshConfigScreen.kt b/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/ImportSshConfigScreen.kt index fd8f021..fb2c541 100644 --- a/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/ImportSshConfigScreen.kt +++ b/app/src/main/java/io/github/lordofpolls/shellwave/feature/host/ImportSshConfigScreen.kt @@ -376,6 +376,12 @@ private fun ImportEntryCard( value = state.hostNameOverride, onValueChange = { state.hostNameOverride = it }, label = { Text("Hostname") }, + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.Uri, + autoCorrectEnabled = false, + ), + singleLine = true, modifier = Modifier.fillMaxWidth() ) OutlinedTextField( @@ -389,6 +395,12 @@ private fun ImportEntryCard( value = state.usernameOverride, onValueChange = { state.usernameOverride = it }, label = { Text("Username") }, + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.PasswordVisible, + autoCorrectEnabled = false, + ), + singleLine = true, modifier = Modifier.fillMaxWidth() ) diff --git a/app/src/main/java/io/github/lordofpolls/shellwave/terminal/TerminalInput.kt b/app/src/main/java/io/github/lordofpolls/shellwave/terminal/TerminalInput.kt index 5c06de4..888e5b6 100644 --- a/app/src/main/java/io/github/lordofpolls/shellwave/terminal/TerminalInput.kt +++ b/app/src/main/java/io/github/lordofpolls/shellwave/terminal/TerminalInput.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.input.KeyboardType import com.termux.terminal.KeyHandler import androidx.compose.ui.input.key.KeyEvent as ComposeKeyEvent @@ -54,9 +55,10 @@ private const val PLACEHOLDER = " " * previous session. * * The buffer holds the current line until Enter so the IME can revise it, which also means the IME - * sees that line, including a password typed at a no-echo prompt. Compose cannot request - * no-suggestions or no-personalised-learning without also disabling the autocorrect this exists - * for, so that exposure is accepted. + * sees that line, including a password typed at a no-echo prompt. The field uses + * [KeyboardType.PasswordVisible] so the IME neither offers suggestions nor learns from that text; + * the text stays unmasked (Compose masks only through a visual transformation) and the capture + * logic below is unaffected. */ @Composable fun TerminalInputCapture( @@ -111,6 +113,8 @@ fun TerminalInputCapture( KeyboardOptions( autoCorrectEnabled = false, capitalization = KeyboardCapitalization.None, + // Prevent keyboard from autocorrect/suggestions + keyboardType = KeyboardType.PasswordVisible, ), ) }