1
0
forked from GRIN/grim

Build 38: use the native Android keyboard like Grim (revert the virtual-keyboard mistake)

Build 37 wrongly forced Grim's on-screen virtual keyboard on Android (.soft_keyboard())
after the emulator's broken IME misled me into thinking the native keyboard was dead.
That brought up a virtual keyboard AND double-typed. Reverted:

- Removed .soft_keyboard() from every field; they now use Grim's TextEdit defaults
  (no_soft_keyboard = is_android()) → the user's native keyboard on Android, exactly
  like Grim, no virtual keyboard, single input.
- Reverted edit.rs IMEAllowed/on_soft_input to Grim's exact behavior; kept only the
  additive display builders (hint_text/text_color/body) the Goblin design needs.
- Manifest: removed windowSoftInputMode and the keyboard/keyboardHidden/navigation
  configChanges flags I'd added (Grim has none) so input matches Grim; kept the
  uiMode/density flags + isFinishing() guard for the dark-mode crash fix.

The app's input path now diffs from Grim only by the additive display builders.
(The Android emulator's IME is aborted regardless — a known emulator quirk — so the
native keyboard is validated on a real device, where Grim works.)
This commit is contained in:
2ro
2026-06-12 22:14:49 -04:00
parent 2cc023b905
commit 1b5352da0d
5 changed files with 4 additions and 38 deletions
+1 -2
View File
@@ -40,8 +40,7 @@
<activity
android:launchMode="singleTask"
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize|stateUnchanged"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation|uiMode|density|locale|layoutDirection|fontScale|colorMode"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode|density|locale|layoutDirection|fontScale|colorMode"
android:exported="true">
<intent-filter>
-9
View File
@@ -1734,14 +1734,12 @@ impl GoblinWalletView {
.hint_text("https://node.example.com:3413")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.node_url_input, cb);
ui.add_space(8.0);
TextEdit::new(egui::Id::from("set_node_secret"))
.hint_text("API secret (optional)")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.node_secret_input, cb);
});
ui.add_space(10.0);
@@ -1823,7 +1821,6 @@ impl GoblinWalletView {
.hint_text("wss://relay.example.com")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.relay_input, cb);
});
ui.add_space(10.0);
@@ -2050,7 +2047,6 @@ impl GoblinWalletView {
.hint_text("Type RESET")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut rotate.reset_input, cb);
});
ui.add_space(8.0);
@@ -2060,7 +2056,6 @@ impl GoblinWalletView {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut rotate.password, cb);
});
ui.add_space(10.0);
@@ -2222,7 +2217,6 @@ impl GoblinWalletView {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut import.nsec, cb);
});
ui.add_space(8.0);
@@ -2232,7 +2226,6 @@ impl GoblinWalletView {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut import.password, cb);
});
ui.add_space(8.0);
@@ -2242,7 +2235,6 @@ impl GoblinWalletView {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut import.backup_password, cb);
});
ui.add_space(10.0);
@@ -2527,7 +2519,6 @@ impl GoblinWalletView {
.hint_text("yourname")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut claim.input, cb);
if claim.input != before {
claim.available = None;
-5
View File
@@ -324,7 +324,6 @@ impl OnboardingContent {
.hint_text("https://node.example.com")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.ext_url, cb);
});
}
@@ -384,7 +383,6 @@ impl OnboardingContent {
.hint_text("Wallet name")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.name, cb);
});
ui.add_space(8.0);
@@ -394,7 +392,6 @@ impl OnboardingContent {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.pass, cb);
});
ui.add_space(8.0);
@@ -404,7 +401,6 @@ impl OnboardingContent {
.password()
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.pass2, cb);
});
ui.add_space(10.0);
@@ -799,7 +795,6 @@ impl OnboardingContent {
.hint_text("yourname")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.claim.input, cb);
if self.claim.input != before {
self.claim.available = None;
-2
View File
@@ -288,7 +288,6 @@ impl SendFlow {
.hint_text("@handle, npub, or name")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.scan_qr();
te.ui(ui, &mut search, cb);
// scan_qr() already starts the camera on tap.
@@ -793,7 +792,6 @@ impl SendFlow {
.hint_text("Add a note…")
.text_color(t.surface_text)
.body()
.soft_keyboard()
.ui(ui, &mut self.note, cb);
note_focused = ui.ctx().memory(|m| m.has_focus(note_id));
});
+3 -20
View File
@@ -222,14 +222,8 @@ impl TextEdit {
// Reset keyboard state for newly focused.
if clicked || self.focus_request {
// Only ask for the native IME when this field uses it.
// Fields that opt into the on-screen keyboard must not
// also trip the (flaky, on some devices aborted) native
// soft keyboard.
if self.no_soft_keyboard {
ui.ctx()
.send_viewport_cmd(ViewportCommand::IMEAllowed(true));
}
ui.ctx()
.send_viewport_cmd(ViewportCommand::IMEAllowed(true));
KeyboardContent::reset_window_state();
}
@@ -269,10 +263,7 @@ impl TextEdit {
multiline: bool,
value: &mut String,
) -> bool {
// Native IME (Grim's default on Android) feeds LAST_SOFT_KEYBOARD_EVENT;
// the on-screen keyboard feeds KeyboardContent. A field using the
// on-screen keyboard reads the latter even on Android.
let event: Option<KeyboardEvent> = if is_android() && self.no_soft_keyboard {
let event: Option<KeyboardEvent> = if is_android() {
let mut w_input = LAST_SOFT_KEYBOARD_EVENT.write();
w_input.take()
} else {
@@ -437,14 +428,6 @@ impl TextEdit {
self
}
/// Use the on-screen keyboard on every platform, including Android (where
/// the default is the native IME). Reliable when the native keyboard is
/// unavailable or aborted by the windowing system.
pub fn soft_keyboard(mut self) -> Self {
self.no_soft_keyboard = false;
self
}
/// Set placeholder text shown when the field is empty.
pub fn hint_text(mut self, hint: impl Into<String>) -> Self {
self.hint = Some(hint.into());