diff --git a/android/java/uk/nandi/sleek/SleekIme.java b/android/java/uk/nandi/sleek/SleekIme.java index f16c58e..5f2fdb0 100644 --- a/android/java/uk/nandi/sleek/SleekIme.java +++ b/android/java/uk/nandi/sleek/SleekIme.java @@ -6,6 +6,9 @@ import android.text.InputType; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.Window; +import android.view.WindowManager; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -27,6 +30,8 @@ public final class SleekIme { private static final String TAG = "SleekIme"; private static volatile ImeBridgeEditText field; + /** Avoid racing hideSoftInput against a posted showSoftInput. */ + private static volatile boolean showPending; static { try { @@ -72,25 +77,120 @@ public final class SleekIme { private static void setActiveOnUiThread(Activity activity, boolean active) { try { ensureField(activity); + if (field == null) { + Log.w(TAG, "setActive(" + active + "): IME bridge field unavailable"); + return; + } InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm == null) { + Log.w(TAG, "setActive(" + active + "): InputMethodManager null"); return; } if (active) { - field.requestFocus(); - imm.showSoftInput(field, InputMethodManager.SHOW_IMPLICIT); + showPending = true; + showKeyboardWhenReady(activity, field, imm); } else { - imm.hideSoftInputFromWindow(field.getWindowToken(), 0); - field.clearFocus(); - onPreedit(""); + if (showPending) { + field.removeCallbacks(SHOW_KEYBOARD); + showPending = false; + } + hideKeyboard(field, imm); } } catch (Throwable t) { Log.w(TAG, "setActive(" + active + ") failed", t); } } + private static final ShowKeyboardTask SHOW_KEYBOARD = new ShowKeyboardTask(); + + private static final class ShowKeyboardTask implements Runnable { + private Activity activity; + private ImeBridgeEditText target; + private InputMethodManager imm; + private int attempts; + + void bind(Activity activity, ImeBridgeEditText target, InputMethodManager imm) { + this.activity = activity; + this.target = target; + this.imm = imm; + this.attempts = 0; + } + + @Override + public void run() { + if (!showPending || target == null || imm == null) { + return; + } + if (!target.isAttachedToWindow() || target.getWindowToken() == null) { + if (++attempts < 20) { + target.postDelayed(this, 16); + } else { + Log.w(TAG, "showSoftInput: bridge never attached"); + showPending = false; + } + return; + } + Window window = activity != null ? activity.getWindow() : null; + if (window != null) { + window.setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } + target.requestFocus(); + boolean shown = imm.showSoftInput(target, InputMethodManager.SHOW_IMPLICIT); + Log.d( + TAG, + "showSoftInput implicit=" + + shown + + " focus=" + + target.isFocused() + + " bounds=" + + target.getWidth() + + "x" + + target.getHeight()); + if (!shown && ++attempts < 8) { + target.postDelayed(this, 50); + return; + } + showPending = false; + } + } + + private static void showKeyboardWhenReady( + Activity activity, ImeBridgeEditText field, InputMethodManager imm) { + SHOW_KEYBOARD.bind(activity, field, imm); + if (field.isAttachedToWindow() && field.getWidth() > 0 && field.getHeight() > 0) { + field.post(SHOW_KEYBOARD); + return; + } + ViewTreeObserver observer = field.getViewTreeObserver(); + if (observer.isAlive()) { + observer.addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + ViewTreeObserver obs = field.getViewTreeObserver(); + if (obs.isAlive()) { + obs.removeOnGlobalLayoutListener(this); + } + if (showPending) { + field.post(SHOW_KEYBOARD); + } + } + }); + } else { + field.post(SHOW_KEYBOARD); + } + } + + private static void hideKeyboard(ImeBridgeEditText field, InputMethodManager imm) { + imm.hideSoftInputFromWindow(field.getWindowToken(), 0); + field.clearFocus(); + onPreedit(""); + } + private static void ensureField(Activity activity) { if (field != null) { return; @@ -104,18 +204,25 @@ public final class SleekIme { View content = activity.findViewById(android.R.id.content); if (content instanceof FrameLayout) { + // Non-zero size so layout/IME serving succeeds (1×1 was "not served"). FrameLayout.LayoutParams lp = - new FrameLayout.LayoutParams(1, 1); + new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); ((FrameLayout) content).addView(edit, lp); } else if (content instanceof ViewGroup) { ViewGroup.LayoutParams lp = - new ViewGroup.LayoutParams(1, 1); + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); ((ViewGroup) content).addView(edit, lp); } else { Log.w(TAG, "content root is not a ViewGroup; IME bridge unavailable"); return; } + edit.setShowSoftInputOnFocus(true); field = edit; + Log.d(TAG, "IME bridge EditText attached to content root"); } /** Invisible editor whose {@link InputConnection} forwards IME events to Rust. */ @@ -130,7 +237,8 @@ public final class SleekIme { InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; - outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI; + outAttrs.imeOptions = + EditorInfo.IME_ACTION_NONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; return new BridgeInputConnection(this); } } @@ -143,31 +251,37 @@ public final class SleekIme { @Override public boolean setComposingText(CharSequence text, int newCursorPosition) { + // Gboard glide typing requires composing spans in the Editable; super + // maintains that state while we forward preedit to egui via JNI. + boolean ok = super.setComposingText(text, newCursorPosition); String s = text == null ? "" : text.toString(); nativePreedit(s); - return true; + return ok; } @Override public boolean finishComposingText() { + boolean ok = super.finishComposingText(); nativePreedit(""); - return true; + return ok; } @Override public boolean commitText(CharSequence text, int newCursorPosition) { + boolean ok = super.commitText(text, newCursorPosition); if (text != null && text.length() > 0) { nativeCommit(text.toString()); } - return true; + return ok; } @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { + boolean ok = super.deleteSurroundingText(beforeLength, afterLength); if (beforeLength > 0 || afterLength > 0) { nativeDeleteSurrounding(beforeLength, afterLength); } - return true; + return ok; } } } diff --git a/android/src/android_ime.rs b/android/src/android_ime.rs index 21d043d..b8b6e20 100644 --- a/android/src/android_ime.rs +++ b/android/src/android_ime.rs @@ -63,19 +63,29 @@ pub fn drain_ime_events(ctx: &egui::Context) { } /// Wire egui-winit IME allow/deny to the Java bridge (replaces NativeActivity show/hide). -pub fn set_ime_allowed(allowed: bool) { +/// +/// Returns `true` when the Java bridge handled the request; `false` lets egui-winit +/// fall back to winit `set_ime_allowed` (tap typing without swipe). +pub fn set_ime_allowed(allowed: bool) -> bool { let mut last = LAST_ACTIVE.lock().expect("ime active"); if *last == Some(allowed) { - return; + return true; } - *last = Some(allowed); if allowed { push_ime(QueuedIme::Enabled); } else { push_ime(QueuedIme::Disabled); } - if let Err(e) = call_set_active(allowed) { - log::warn!("android IME setActive({allowed}): {e}"); + match call_set_active(allowed) { + Ok(()) => { + *last = Some(allowed); + log::info!("android IME setActive({allowed}) ok"); + true + } + Err(e) => { + log::warn!("android IME setActive({allowed}): {e}; falling back to winit"); + false + } } } diff --git a/android/src/assets/sleek_activity.dex b/android/src/assets/sleek_activity.dex index 079fc7afba9a95e372848b49da85199addef1b5c..429f6e73619a6f705d2fb4572ec718580713cb1a 100644 GIT binary patch delta 14240 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 delta 11390 zcmZ2+m~qEi#`=`h3N8azpl3=9+47#I>67#L(Y7#P$U7#LRYFfd$ZU|@K_%fP@5(ksHi z@W6qA;f5#!1D_cKgMb+WgRmI`gP0jZJ%hX%1B03w1B00v1B0Cz1B1UA14FbK14Eh_ z14FhM14F4914D-y1H%e428P3C3=H?o7#Mz=F)#?4GcXvKGcb6XGcd%OGcc5yGcfd+ zGcZguXJD9T&cLwHoPlAfIRnEsa|VX9<_rvP%o!N|nlmtPS}-ukS}-seS}-t}SkyBx zSXwYJcv~#oq**X9z#M7`9n3Fzm8mV7O$#!0^F>fkD`kfkDrbfx*L) zfg#h9fuY5cfnl2^1H*Sq1_m=L28Q%{D+Y$SRtya1tr!^CtQi>etr-|1tr-}4tQi<~ zTQe{`vu0pWuwh`ZwP9cgv0-57vSDCYZo|N^%Z7pBnhgWPYa0dzMq363Sz87MV_OCW z4_gL?99ss4Zd(S1`L+xUTWlE^&e$?Ays~9rV6tOi5VvDsu(M-eh_GW|$hTu)sGn%Z zz_8Aaf#Ied1H*Sa1_pI|1_nQS28Jqo28NaP3=Bu?85rK%GcfQvFfbT8FffEUFff!k zFfhz?U|`tcz`$_Dfq~(j0|NuIBLjn~BLjoEBLjoCBLhRCBLhQ&BLl+}M+S!VjtmTk z9T^yIIWjQ(ab#fNcVb{rb7Ekybz-Pz2z6p$$a7*~XmDa+SmeaOaKwp$;h7Ty1BWvM zgO)P`gO@V{LzXiG!+d83h6~ON41b*&7$jU67_?m&7@S-f7{Xl`7&2WL7`j{-7$&lqlt zT^Sf;Tp1V?T^Sg(T^Se*T^Sh6Tp1XwTp1YbT^SghTp1YLT^Sg>T^SexTp1WbTp1XO zTp1WTTp1YVx-u|qa%EuH@5;b%*_DCehARWZ16Kxy*RBi*)c;msq@X>>T;hP5o!(R^u25wIV1_e(B1|3fZ24hbK1}jen26s;e1|Lrb zhG-gGJF^q@_ZN=%6u3YcKI+c9QR>hxb4Hh@X&{W;hhfy!)G4`h95o*4F7x>7HeP%={S`9Q+v=y!;s$!u%N+68#w% z^8FbY8vGd;+Wi?Adi)s}X8SWREb(Vx*yYc_u-~77;gmlE!!>^fhS&ZK4E4YK85sBi z7#I`+7#Q>d7#Q3G7#M;A7#K1G7#P|D7#L;;FfgnOU|={Jz`$@NfPvw000YCP00xF1 z0SpZP0~i>10~r`Z0vQ;z0~r{s0vQ;50vQ-00vQ;x0~r_!0~r|F0~r`52Qn}$4rE~1 z5y-%BFpz=abRYx6g+K;|Yk>@23{L_X7(N9uF#Hc>U|z327&bC6Fl=IAV32~=Uo|ES49W})42%p63@s)Qf7#J8pY>-2Z85kG@7#JA#nAAfIf>qTZMIsCg z3?M}yL&cz)L2OXqg1CkZ3=Gl?3=B`8j*tNfGC<6cM`9}?u|bgn;)2Xk0WlaD7)%)$ z7}V++7#Kc44FUxpNDib36nro?DEMG(Q1F4+Acun3Fo%L-8zc_Ws}Ev89RyMb;)29M z4hOM8zBgfDVEAFez)%lT;DF>~XOId81_qEKFxwQt21O0XAUCKQkPkp?kOx5KxI@h` zVqjnZu{of6L2Qs-bEvq0DFXvYy*DWSVLtGI8VYh5h%Eut9KgW9pkm6v07|GaP&M-z z7#KioklqDQHYm|8gt9>vFM_f`32+IN4N8E^p==#f28Klp^$>$hz#ItM28r#0#P%^| zU|0;*9Ae7A05dcONlglpcn*?y2@<;oiQQ6<#OXoOJjIlO0p`*gplU!-24jO92V*msL2^nw#Qb`2LP&rn98jVIHHCy26je} zLTv_qFs;lW0;bg%M8UKVLnI>u11p0Wg9HPJc4Ke^^X(Yi!E_*lHzNZBI|D>N2ZIE| z7X}6fP6lR%?_gSn;TM?ZX7~%Hnd`x|3O9oe12ZE7125EIJ_a*}^9&3Od<+~68Q?h8 zVtB{Ez#s^f7lO(QF^Di+Wnf?sV(@3U#=yWJ%pk<@5lqK3d;-&c3_gqu3?d934BHt% zv?#+tFzv(;!^prO&QKrAz{1GDAi-eF@PPqDGcmA&`AiI4jGzh?;t)v&CWhY(AU+er zA21!mzy_9QVi0Bo)dyk>Tfp@KM7=bF0s}8tT%5riOuI0oGcqv9Fz7JwKDwFgwG4a1GDJ@BmEnKxr-pNk&lB z%*D_Org^}$8iNJcTr~!W!$CC2!Ric544jM%4D}ieObpzNpn6$_fdfo4F$jQZbA~KN zP#q01K!?GTVF$Qbp~~c1=HRP35=lThB-qTl+I)X<#8s4d*DiliNOfUFNM-o zj0_C=4Dt*=LFKFkp=__25qna3xf_L12}hsQnxdM4g)BTTo}w4A{ZGM+@J=zF+?#uW&rU+7!<(bIt=xU zpc)U7E8H3M7j7;0sOlz6|DIpZYVHGUPHcFa$6}GHhi4(a{Xsz%&y>9#}k%p$1%VS%cLFLOl=! zH3vk4JQU1e#!wIPX)uE;gBv3QLkLts2!jKI3z+Z7;0mT47(BrxpaU$)1H*MN&B|~eOeZj?gB=vaU<;;$ z89czWGeZ!V)@29-)7IeJ6vj|*#!v(mCe^*;4SW=|F~LD4hbP%^9*8L6r(5B%+}q z5zS!5@DNGPzDBbs6+@< zKPVxB${z-h1t10PPz9k-1)!7+VuIu+gfcM1L*>K36%nYM0LeluSP;srCInPHGcX7WK=kL-n=mlIItocpr$vHWEuaz)qH`7l1H+C` z28If#d=ymP3?j_{l0Om3z%T_WAC2Zfko=8M28Jae3=CgG7#Nrtn897S#|8`x^`LH3 zG$SuVG!q{~fj|nl(gjI_`f;xe7#KkEAqu<@4Y+ zEMW+<15^O;F|aT&fuXD+Wb9OmVFrT~<6(wE1setdhC+re1_lNm23dwY0Z9gChC+rO z5TDW5kc~lzxlo{&ff-EqGcYsAF-S4oWhhjbIQhAxX1$If1A`~H&pU%biP4>*Kp>le zkwJ>-2t%Gi9)ltS4`ZG}4nrV_7ARm~WZ+>eRItRbkp*F+BO8Md3&=(mFx}6 z!~ijwfkBG#3D{%-1|Ehyg&YBf00th`Jb?lRRt6qckfY{HX-Yd7g4(kTNZtY&$iz@6 zFk$jzDaCqkLr5PunvtKOKmb&N@-Z-jgNK13&=AyqVqlPBn!zB&aEt-uTqXt~CMkx? zAWt(eG4L_4GcYl*FfcPj8iGm$6nmfnXUPb1HXlQwKpz7e+;c)gc?wbtij$kAMUxX4 zSQvO%@(dIi3|SZ$@&pthfy2-Z3V3EAmOKYB1~HRDfqn)i1}Qd8#zKXO3`q=9Y@ld6 z&QPde%^=30A;`-n#R!V{48}qOD+WcjJON<_HU=IxA&~mXzok=|N)0C`$rv!&O`a@c zE$G0&#=yniST4rdz_e$2o`C)2`!b>sQBGkl2S$^XTnr#lR#`(=I|f+>L0(>7DL#+| zXBi3&Y#H=;m3Y+{3Jf4}EDQ_`so*r71s-YujdRR41hobkKu%>S6qq%+TGo(hvEk%( zvKsP^49pB%%oWPMjr-6Mn)4;5k3YtRtA_co>zKd>IM^${3g!l$aqbW(Fk| zFl+K@dDVIc1}RQP1`7@oka-|Cb6IlfF(`3B(l7^u6qgD(4X*(CN+FM-0z?ZGfPJaN z1yRKXR#C;k!w$+v)1h=bh!!YdU}xZAFH)#vC}Pk^wBRs>*~1|uWxPVK9>pqYutAht zRm`BFgX{%)oaWY(=7$o78*FThnhX*DmAHKwiUg_|Kz#@h&CI~Xx*-t6Vga)X6v`Q- z8HyCD7_`BpHJJ2fP-aYGC{n0lkY?cFmgi0qV-V!#<%Z-74N!r|pvbMvoiw>wu~>2@ zxS)m#URNb#gNZfC{W12z@Wj&%K&ka8pK85h90P~ z#|p|oj9wz3N&u9BWQ-tgLKOCl3@i+c3<~u|pvFEZXGt>%F0}BHe zU!g)W!y5)Jo;-(QhAW4St3lO>GN>G4*wm>!1)|4*frnL|6{3fMfr}NSCz64SHP4}V zvaE_KW87qC6cNgD?XRqZD%+Se+rL zLQ=>9S5Q(+oM3gZ%4#7<9TNwN7$XA@gA^k>SPWYAFl=Dpsb`R4WI+);0IHV+Kr@*j z4Ku(6H6sHk*)TCMGcYjZ8iBgq3=EcxoD4#YQcTw17*u57V^CrUWGFDGWe{M>Q{Z3# z=>a7|5UzkGwL*dgMo)l zih~&}*2ciYmZy-M zhYOVV7BOfD!BqL8s6s@Q#vM>(!Bm9_L6spHGr3kH+{twU6Zc=nElf-djNoedoDl=K zJE_DF#84mr8acCI1cezWk!i3L38aC`L{O;)!i)?zj3$d|+VD8?gVG)&12e-Dqseia zVT?;AZ`IW2{%ynn9wU-s*g5&VrX-`;WKJ!0)=LahZ1R&$wLAqq!PS@s6Z;Gc4mO57 z0q4p6T9&$gpd8GVrx3)jl7WXSXcYrP)2c#+M1~9oE{>C38r*pTAq<=hTwGw$$zQc} zxk2OCTpS=Bos$E#mYp4_Od%9uELxpu6$g)wMA3enCcX9`e)^#nJ&G?>^G!GR*+4B|6c zFi9{ZFmf;mGs`iU81ga{GJrk62y%`P0}o@6AOnL2d!9hZo%_wV=@eP({JSz{F5!Jh@9(p&pmRt&kip%}~hT4+?p>!wVUFL40PAM~oPF zn1TeE8G@9U844AW7!(<}7&WBx1VX_TaGpRQ0}}%evjwvd1C-5_C*TB>O$W!nJkA>t3cz|j*Mj@d{ zAy6C13nV81>Vfb;b>s=SfV{|fO^|^-Pr()3^MduvHW^RW(w9+x&5$Pm8omZ~PSP0! z7`WhC4MBcpvS0$0UYV0~^~LKC8bkU6`3hVN0t^KLpqYR?0no5K4`ZRgN@zh1iWmll zlg6OPVqnNu;08HMVGF3EgQn)9F=${Hq=tt27^Edc9bf?6IN4B!spWM%{P z`cqI(fV{!LaMKtxqRYTw!N|{0BoGTqJWRHrI!pl67zerPGy@}pkRWI%8e}3U8$W{D zYQxCFV9UhHP^fT*K@qA3mLOjngNC3%bp|L=o&{wa0Z_tYu7`GfKSG1Tl93fW;I6@% z2e%>Zh+-&E*vDYNAS9Tla0DE+M;Qbdgq-pe zjxi`O2r=a;9A;2p5Mg3sU}DTuU}R8c;9*uUvS6@a;$=``GG{0-h-MIClxMbJ$TNsx zU}n+~P%zSAuwW8kP-4<%C@^pX%jz)X8MrerGchn^GN^&7W#*X-QVdrZ>Oq}!kV_Cj zVgz-4p~59_h`@Yh0TUMhjaDJrvUV_Wh4U!tU18z^piwYHGsW8kG#mhGrZ6+`)iWqD zL@*Q>Y)5LFfXZ4>attv6&G>*4%t;0&1}O$vMo>e7iGhbnp2?KKk_qH&3uZnBP;ll6 z9AaPqvxEfm3?dnr7&Sy8;-HBs9#CF4h+tr1Okz@F$P?HPGLH#la_|KPCI$@!UMA#W zfH-hV0MZuAXE0#kVU}Xx23O{g{$v5D5Mh>Lmwewf~Z(;(C{#y4Jb9oNXu0+s59_^ zhTizR<)gf9y?rXYqobmtqZD-|D;Qis=7I+F_-q)AKx`WZEk*`D8wMv3M_0a`!HbE3 zL6Of!FH=`uH>z@JRIIcdqa0{*9aJ}fN-z*DH96K=lhX~{a%5w0o7`_LY73e<24!hC z28cL_28n?ZFNh5)3qjo$P~L%H&;*Ygc<9P_@L00S?W4RR%DU=5@Ja8oP6F}V)9;}iIekvS5B7oYvy52 zOi5v2OH4^|%*mO2(JyGSuD|x?IRD3tn_U83nJ3Q+F`xW6#CfuIs6MM}Zb3=q=JwDK zrpd3vcTL_Ap}3hN(u#3%Zj>b-Z*fL`d2oJOiDzCxX~|^q=xdX|Mq5p`iaA_w%h=2; z!zj+hWX0IbEVY2inrSm53&UC_If%ew2(<`8&1A9zE0^GAvSe&#mWYNZms-mtE`wm| zFxh}qF-b&8fbs$(?*s+L2@16h4JllA%GcYhR);Bb;%+P6IbYk3~1L8L_HZ-uD z&}m|1d;sDzH-q>ubXyo18}t|%zvwYCt}$R>WMtx>pwJGMGU#ArY%yeH++aBQYOF$i z7vl+qE#{0T6xJ9so=}(qB0B6CPbe$^5j#M{1rYH9L^Rklo=}(pA~x7Fb~Lb@uxD&) zU|Hb6$aTb+k?VpfBi9ZGMy?5FjGQkl8M~Mmz?f@`BO})tb4Jb|u8dr3+!(ojxG{2` z@L}Zo;ls#vpx&32fr~{>k`V$umx0Xv5zg4yz%n6%k!eK)BUeu(BgY9pMy?k@%$!?- znK&EIUDdKM=Evk#SBmBjbf=M#eAEjEqxaK+~p-`#_RCu?HC$@5C@M@Gz)KF?t;b zIb=yJ$b%1J8M%JMGIl7;iD%^463@tSCZ3VwNj%thFkf>1iD%?mlE5M-K11aJqe=r4 z3kwqq0}CU|e@5|{prpPf2vk-xFzgLtU^oJ0p9^BBhjXqXN!)?5K^h-J6~75$VEBy0 z{uczvr<|bCbm&k!NIj@Xs|77e31ML1fr^8~Kv)8euM#>rDnU*Qv bool; static HOOK: OnceLock = OnceLock::new(); /// Register a handler for egui IME focus (call from `android_main` before eframe). -pub fn set_android_ime_hook(set_allowed: fn(bool)) { +pub fn set_android_ime_hook(set_allowed: fn(bool) -> bool) { let _ = HOOK.set(set_allowed); } /// Returns true when a hook handled the request (skip winit `set_ime_allowed`). pub fn set_allowed(allowed: bool) -> bool { - HOOK.get().is_some_and(|f| { - f(allowed); - true - }) + HOOK.get().is_some_and(|f| f(allowed)) } #[cfg(test)]