diff --git a/bin/app/java/MainActivity.java b/bin/app/java/MainActivity.java index df2d65c4b..076a4cab5 100644 --- a/bin/app/java/MainActivity.java +++ b/bin/app/java/MainActivity.java @@ -40,6 +40,9 @@ public void createComposer(final int id) { }); } +private InputMethodManager getIMM() { + return (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); +} public boolean focus(final int id) { final InvisibleInputView iv = editors.get(id); if (iv == null) { @@ -55,8 +58,23 @@ public boolean focus(final int id) { Log.w("darkfi", "error requesting focus for id=" + id + ": " + iv); } - InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(iv, InputMethodManager.SHOW_IMPLICIT); + getIMM().showSoftInput(iv, InputMethodManager.SHOW_IMPLICIT); + } + }); + + return true; +} +public boolean unfocus(final int id) { + final InvisibleInputView iv = editors.get(id); + if (iv == null) { + return false; + } + + runOnUiThread(new Runnable() { + @Override + public void run() { + iv.clearFocus(); + getIMM().hideSoftInputFromWindow(iv.getWindowToken(), 0); } }); diff --git a/bin/app/java/autosuggest/InvisibleInputView.java b/bin/app/java/autosuggest/InvisibleInputView.java index d1892cfa5..90b29b500 100644 --- a/bin/app/java/autosuggest/InvisibleInputView.java +++ b/bin/app/java/autosuggest/InvisibleInputView.java @@ -76,10 +76,11 @@ public class InvisibleInputView extends View { @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { Log.d("darkfi", "Create InputConnection for view=" + this.toString()); - if (inputConnection != null) { - Log.d("darkfi", " -> return existing InputConnection"); - return inputConnection; - } + // Losing focus requires the inputConnection to be destroyed + //if (inputConnection != null) { + // Log.d("darkfi", " -> return existing InputConnection"); + // return inputConnection; + //} outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; diff --git a/bin/app/src/android.rs b/bin/app/src/android.rs index 35b098aef..1aaf247f0 100644 --- a/bin/app/src/android.rs +++ b/bin/app/src/android.rs @@ -184,6 +184,18 @@ pub fn focus(id: usize) -> Option<()> { Some(()) } } +pub fn unfocus(id: usize) -> Option<()> { + let is_success = unsafe { + let env = android::attach_jni_env(); + + ndk_utils::call_bool_method!(env, android::ACTIVITY, "unfocus", "(I)Z", id as i32) + }; + if is_success == 0u8 { + None + } else { + Some(()) + } +} pub fn set_text(id: usize, text: &str) -> Option<()> { let ctext = std::ffi::CString::new(text).unwrap(); diff --git a/bin/app/src/app/node.rs b/bin/app/src/app/node.rs index c4e7d07d1..16fd5cc46 100644 --- a/bin/app/src/app/node.rs +++ b/bin/app/src/app/node.rs @@ -453,6 +453,7 @@ pub fn create_chatedit(name: &str) -> SceneNode { // Used by emoji_picker node.add_method("insert_text", vec![("text", "Text", CallArgType::Str)], None).unwrap(); node.add_method("focus", vec![], None).unwrap(); + node.add_method("unfocus", vec![], None).unwrap(); node } diff --git a/bin/app/src/app/schema/chat.rs b/bin/app/src/app/schema/chat.rs index d764b89ab..5eaf4311b 100644 --- a/bin/app/src/app/schema/chat.rs +++ b/bin/app/src/app/schema/chat.rs @@ -291,12 +291,18 @@ pub async fn make( prop.clone().set_f32(atom, Role::App, 2, EMOJI_BG_W).unwrap(); prop.clone().set_f32(atom, Role::App, 3, CHATEDIT_HEIGHT).unwrap(); + // Menu doesn't exist yet ;) + // So look it up in the callback. let sg_root = app.sg_root.clone(); + let layer_node2 = layer_node.clone(); let chatview_is_visible = PropertyBool::wrap(&layer_node, Role::App, "is_visible", 0).unwrap(); - let goback = move || { + let goback = async move || { info!(target: "app::chat", "clicked back"); let atom = &mut PropertyAtomicGuard::new(); + let editz_node = layer_node2.clone().lookup_node("/content/editz").unwrap(); + editz_node.call_method("unfocus", vec![]).await.unwrap(); + let menu_node = sg_root.clone().lookup_node("/window/menu_layer").unwrap(); menu_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); @@ -306,10 +312,9 @@ pub async fn make( let (slot, recvr) = Slot::new("back_clicked"); node.register("click", slot).unwrap(); let goback2 = goback.clone(); - // Menu doesn't exist yet ;) let listen_click = app.ex.spawn(async move { while let Ok(_) = recvr.recv().await { - goback2(); + goback2().await; } }); app.tasks.lock().unwrap().push(listen_click); diff --git a/bin/app/src/text2/editor/android.rs b/bin/app/src/text2/editor/android.rs index 73c123b4f..cb26413a4 100644 --- a/bin/app/src/text2/editor/android.rs +++ b/bin/app/src/text2/editor/android.rs @@ -128,6 +128,9 @@ impl Editor { } android::focus(self.composer_id).unwrap(); } + pub fn unfocus(&self) { + android::unfocus(self.composer_id).unwrap(); + } pub async fn refresh(&mut self, atom: &mut PropertyAtomicGuard) { let font_size = self.font_size.get(); diff --git a/bin/app/src/ui/chatedit.rs b/bin/app/src/ui/chatedit.rs index 4a8e6bb69..af405fa7a 100644 --- a/bin/app/src/ui/chatedit.rs +++ b/bin/app/src/ui/chatedit.rs @@ -1305,6 +1305,25 @@ impl ChatEdit { editor.focus(); true } + async fn process_unfocus_method(me: &Weak, sub: &MethodCallSub) -> bool { + let Ok(method_call) = sub.receive().await else { + debug!(target: "ui::chatedit", "Event relayer closed"); + return false + }; + + t!("method called: focus({method_call:?})"); + assert!(method_call.send_res.is_none()); + assert!(method_call.data.is_empty()); + + let Some(self_) = me.upgrade() else { + // Should not happen + panic!("self destroyed before insert_text_method_task was stopped!"); + }; + + let mut editor = self_.editor.lock().await; + editor.unfocus(); + true + } async fn handle_android_event(&self, ev: AndroidSuggestEvent) { t!("handle_android_event({ev:?})"); @@ -1370,6 +1389,11 @@ impl UIObject for ChatEdit { let focus_task = ex.spawn(async move { while Self::process_focus_method(&me2, &method_sub).await {} }); + let method_sub = node_ref.subscribe_method_call("unfocus").unwrap(); + let me2 = me.clone(); + let unfocus_task = + ex.spawn(async move { while Self::process_unfocus_method(&me2, &method_sub).await {} }); + let mut on_modify = OnModify::new(ex.clone(), self.node.clone(), me.clone()); on_modify.when_change(self.is_focused.prop(), Self::change_focus); @@ -1454,7 +1478,7 @@ impl UIObject for ChatEdit { } }); - let mut tasks = vec![insert_text_task, focus_task, blinking_cursor_task]; + let mut tasks = vec![insert_text_task, focus_task, unfocus_task, blinking_cursor_task]; tasks.append(&mut on_modify.tasks); #[cfg(target_os = "android")]