diff --git a/bin/app/Cargo.lock b/bin/app/Cargo.lock
index 87b3e208a..2b391d9f1 100644
--- a/bin/app/Cargo.lock
+++ b/bin/app/Cargo.lock
@@ -3708,7 +3708,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniquad"
version = "0.4.8"
-source = "git+https://github.com/narodnik/miniquad#f9bf3a41e40f8b15032c159e24dec8e40c7adfa5"
+source = "git+https://github.com/not-fl3/miniquad#dd44380a7ace19462bb63e53dce6093d28219aa2"
dependencies = [
"libc",
"ndk-sys",
diff --git a/bin/app/Cargo.toml b/bin/app/Cargo.toml
index bd044e9bf..e3df4953b 100644
--- a/bin/app/Cargo.toml
+++ b/bin/app/Cargo.toml
@@ -9,8 +9,7 @@ homepage = "https://dark.fi"
repository = "https://codeberg.org/darkrenaissance/darkfi"
[dependencies]
-#miniquad = { git = "https://github.com/not-fl3/miniquad" }
-miniquad = { git = "https://github.com/narodnik/miniquad" }
+miniquad = { git = "https://github.com/not-fl3/miniquad" }
# Currently latest version links to freetype-sys 0.19 but we use 0.21
#harfbuzz-sys = "0.6.1"
diff --git a/bin/app/Makefile b/bin/app/Makefile
index 1768931b9..8ec89beae 100644
--- a/bin/app/Makefile
+++ b/bin/app/Makefile
@@ -45,10 +45,10 @@ win-release: $(SRC) fonts
win-debug: $(SRC) fonts
$(CARGO) build $(DEBUG_FEATURES)
-mv target/debug/darkfi-app.exe .
-android-release: $(SRC) fonts assets/forest_720x1280.mp4 gametextinput
+android-release: $(SRC) fonts assets/forest_720x1280.mp4
podman run -v $(shell pwd)/../../:/root/darkfi -w /root/darkfi/bin/app/ -t apk cargo quad-apk build --release $(RELEASE_FEATURES)
-mv $(RELEASE_APK) darkfi-app.apk
-android-debug: $(SRC) fonts assets/forest_720x1280.mp4 gametextinput
+android-debug: $(SRC) fonts assets/forest_720x1280.mp4
podman run -v $(shell pwd)/../../:/root/darkfi -w /root/darkfi/bin/app/ -t apk cargo quad-apk build $(DEBUG_FEATURES)
-mv $(DEBUG_APK) darkfi-app_debug.apk
@@ -69,18 +69,6 @@ ibm-plex-mono-regular.otf:
NotoColorEmoji.ttf:
wget -c https://dark.fi/assets/NotoColorEmoji.ttf
-# Download Android GameTextInput library
-
-GAMETEXTINPUT_INCLUDE = src/android/textinput/include/game-text-input/game-text-input/gametextinput.h
-GAMETEXTINPUT_LIBS = pkg/android/libs/android.arm64-v8a/libgame-text-input.a
-
-gametextinput: $(GAMETEXTINPUT_INCLUDE) $(GAMETEXTINPUT_LIBS)
-
-$(GAMETEXTINPUT_INCLUDE):
- pkg/android/dl-textinput-lib.sh
-$(GAMETEXTINPUT_LIBS):
- pkg/android/dl-textinput-lib.sh
-
# App data
assets/forest_1920x1080.ivf:
@@ -99,7 +87,7 @@ dev: $(SRC) fonts assets/forest_1920x1080.ivf
./darkfi-app
# Users should use the android-release and android-debug targets instead.
-apk: $(SRC) fonts assets/forest_720x1280.mp4 gametextinput
+apk: $(SRC) fonts assets/forest_720x1280.mp4
cargo quad-apk build $(DEV_FEATURES)
$(MAKE) install-apk
@@ -107,6 +95,9 @@ install-apk:
-mv $(DEBUG_APK) .
-adb $(ADB_DEVICE) uninstall darkfi.darkfi_app
adb $(ADB_DEVICE) install -r darkfi-app.apk
+ $(MAKE) log-apk
+
+log-apk:
reset
adb $(ADB_DEVICE) logcat -c
adb $(ADB_DEVICE) shell monkey -p darkfi.darkfi_app -c android.intent.category.LAUNCHER 1
diff --git a/bin/app/java/MainActivity.java b/bin/app/java/MainActivity.java
index 31abbe92b..b57c20644 100644
--- a/bin/app/java/MainActivity.java
+++ b/bin/app/java/MainActivity.java
@@ -4,10 +4,10 @@ import android.view.ViewGroup;
import android.view.WindowInsets.Type;
import android.view.inputmethod.EditorInfo;
import android.text.InputType;
+import android.util.Log;
import java.util.HashMap;
import videodecode.VideoDecoder;
-import textinput.InputConnection;
import textinput.Settings;
import textinput.Listener;
import textinput.State;
@@ -15,6 +15,67 @@ import textinput.GameTextInput;
//% END
+//% QUAD_SURFACE_ON_CREATE_INPUT_CONNECTION
+
+MainActivity main = (MainActivity)getContext();
+// Create InputConnection if it doesn't exist yet
+if (main.inpcon == null) {
+ EditorInfo editorInfo = new EditorInfo();
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
+ editorInfo.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
+
+ main.inpcon = new textinput.InputConnection(
+ getContext(),
+ this,
+ new Settings(editorInfo, true)
+ );
+
+ // Pass the InputConnection to native GameTextInput library
+ main.setInputConnectionNative(main.inpcon);
+}
+
+// Set the listener to receive IME state changes
+main.inpcon.setListener(new Listener() {
+ @Override
+ public void stateChanged(State newState, boolean dismissed) {
+ // Called when the IME sends new text state
+ // Forward to native code which triggers Rust callback
+ Log.d("darkfi", "stateChanged: text=" + newState.toString());
+ main.onTextInputEventNative(newState);
+ }
+
+ @Override
+ public void onImeInsetsChanged(androidx.core.graphics.Insets insets) {
+ // Called when IME insets change (e.g., keyboard height changes)
+ // Optional: can be used for dynamic layout adjustment
+ }
+
+ @Override
+ public void onSoftwareKeyboardVisibilityChanged(boolean visible) {
+ // Called when keyboard is shown or hidden
+ }
+
+ @Override
+ public void onEditorAction(int actionCode) {
+ // Called when user presses action button (Done, Next, etc.)
+ // Optional: handle specific editor actions
+ }
+});
+
+// Copy EditorInfo from GameTextInput to configure IME
+if (outAttrs != null) {
+ GameTextInput.copyEditorInfo(
+ main.inpcon.getEditorInfo(),
+ outAttrs
+ );
+}
+
+// Return the GameTextInput InputConnection to IME
+if (true) return main.inpcon;
+
+//% END
+
//% RESIZING_LAYOUT_BODY
native static void onApplyInsets(
@@ -53,7 +114,7 @@ native void setInputConnectionNative(textinput.InputConnection c);
native void onTextInputEventNative(textinput.State softKeyboardEvent);
// GameTextInput InputConnection reference (public for QuadSurface access)
-public textinput.InputConnection gameTextInputInputConnection;
+public textinput.InputConnection inpcon;
public String getAppDataPath() {
return getApplicationContext().getDataDir().getAbsolutePath();
@@ -95,89 +156,9 @@ public VideoDecoder createVideoDecoder() {
//% MAIN_ACTIVITY_ON_CREATE
-//view.setFocusable(false);
-//view.setFocusableInTouchMode(false);
-//view.clearFocus();
-
// Start a foreground service so the app stays awake
Intent serviceIntent = new Intent(this, ForegroundService.class);
startForegroundService(serviceIntent);
//% END
-
-//% QUAD_SURFACE_ON_CREATE_INPUT_CONNECTION
-
-// Get reference to MainActivity
- if (getContext() == null)
- Log.i("darkfi", "getCTX (on creat) is nulllll!!!!!!!!!!!!!!!!!!");
-MainActivity mainActivity = (MainActivity)getContext();
-
-android.util.Log.d("darkfi", "onCreateInputConnection called");
-
-// Create InputConnection if it doesn't exist yet
-if (mainActivity.gameTextInputInputConnection == null) {
- android.util.Log.d("darkfi", "Creating new InputConnection");
- // Create InputConnection with Context (from QuadSurface)
- android.view.inputmethod.EditorInfo editorInfo = new android.view.inputmethod.EditorInfo();
- editorInfo.inputType = android.text.InputType.TYPE_CLASS_TEXT |
- android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
- editorInfo.imeOptions = android.view.inputmethod.EditorInfo.IME_FLAG_NO_FULLSCREEN;
-
- if (mainActivity == null)
- Log.i("darkfi", "mainact is NULLLL");
- mainActivity.gameTextInputInputConnection = new textinput.InputConnection(
- getContext(),
- this,
- new textinput.Settings(editorInfo, true)
- );
-
- // Pass the InputConnection to native GameTextInput library
- android.util.Log.d("darkfi", "InputConnection created and passed to native");
- mainActivity.setInputConnectionNative(mainActivity.gameTextInputInputConnection);
-} else {
- android.util.Log.d("darkfi", "Reusing existing InputConnection");
-}
-
-// Set the listener to receive IME state changes
-mainActivity.gameTextInputInputConnection.setListener(new textinput.Listener() {
- @Override
- public void stateChanged(textinput.State newState, boolean dismissed) {
- // Called when the IME sends new text state
- // Forward to native code which triggers Rust callback
- android.util.Log.d("darkfi", "stateChanged: text=" + newState.toString());
- mainActivity.onTextInputEventNative(newState);
- }
-
- @Override
- public void onImeInsetsChanged(androidx.core.graphics.Insets insets) {
- // Called when IME insets change (e.g., keyboard height changes)
- // Optional: can be used for dynamic layout adjustment
- }
-
- @Override
- public void onSoftwareKeyboardVisibilityChanged(boolean visible) {
- // Called when keyboard is shown or hidden
- android.util.Log.d("darkfi", "onSoftwareKeyboardVisibilityChanged: " + visible);
- }
-
- @Override
- public void onEditorAction(int actionCode) {
- // Called when user presses action button (Done, Next, etc.)
- // Optional: handle specific editor actions
- }
-});
-
-// Copy EditorInfo from GameTextInput to configure IME
-if (outAttrs != null) {
- textinput.GameTextInput.copyEditorInfo(
- mainActivity.gameTextInputInputConnection.getEditorInfo(),
- outAttrs
- );
-}
-
-// Return the GameTextInput InputConnection to IME
-if (true) return mainActivity.gameTextInputInputConnection;
-return mainActivity.gameTextInputInputConnection;
-
-//% END
diff --git a/bin/app/java/textinput/GameTextInput.java b/bin/app/java/textinput/GameTextInput.java
index a9be73ad9..1008fb356 100644
--- a/bin/app/java/textinput/GameTextInput.java
+++ b/bin/app/java/textinput/GameTextInput.java
@@ -48,5 +48,14 @@ public final class GameTextInput {
to.initialSelEnd = from.initialSelEnd;
}
+ public static final class Pair {
+ int first, second;
+
+ Pair(int f, int s) {
+ first = f;
+ second = s;
+ }
+ }
+
private GameTextInput() {}
}
diff --git a/bin/app/java/textinput/InputConnection.java b/bin/app/java/textinput/InputConnection.java
index 92ec7f854..5f411d870 100644
--- a/bin/app/java/textinput/InputConnection.java
+++ b/bin/app/java/textinput/InputConnection.java
@@ -42,6 +42,8 @@ import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
+import textinput.GameTextInput.Pair;
+
public class InputConnection extends BaseInputConnection implements View.OnKeyListener {
private static final String TAG = "gti.InputConnection";
private final InputMethodManager imm;
diff --git a/bin/app/java/textinput/Pair.java b/bin/app/java/textinput/Pair.java
deleted file mode 100644
index bdbd01b07..000000000
--- a/bin/app/java/textinput/Pair.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package textinput;
-
-public final class Pair {
- public int first, second;
-
- public Pair(int f, int s) {
- first = f;
- second = s;
- }
-}
diff --git a/bin/app/pkg/android/dl-textinput-lib.sh b/bin/app/pkg/android/dl-textinput-lib.sh
deleted file mode 100755
index f35aee597..000000000
--- a/bin/app/pkg/android/dl-textinput-lib.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# Download GameTextInput library from Android Maven repository
-# This script downloads and extracts the GameTextInput headers and libraries
-
-set -e
-
-SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
-PROJECT_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
-
-LIBS_DIR=$SCRIPT_DIR/libs
-INCLUDE_DIR=$PROJECT_ROOT/src/android/textinput/include
-
-VERSION=4.0.0
-AAR=games-text-input-$VERSION.aar
-URL=https://dl.google.com/android/maven2/androidx/games/games-text-input/$VERSION/$AAR
-TMPDIR=/tmp/games-text-input-$VERSION
-
-cleanup() {
- rm -rf $TMPDIR
-}
-trap cleanup EXIT
-
-# Clean existing files
-rm -rf $LIBS_DIR
-rm -rf $INCLUDE_DIR/game-text-input
-
-# Download AAR
-mkdir -p $TMPDIR
-cd $TMPDIR
-wget $URL
-unzip $AAR
-# Copy libs
-mv prefab/modules/game-text-input/libs $LIBS_DIR/
-# Copy headers
-mkdir -p $INCLUDE_DIR/game-text-input
-mv prefab/modules/game-text-input/include/* $INCLUDE_DIR/game-text-input/
-
-echo "GameTextInput ${GAMETEXTINPUT_VERSION} installation complete!"
-echo " Libraries: $LIBS_DIR"
-echo " Headers: $INCLUDE_DIR"
-
diff --git a/bin/app/quad.toml b/bin/app/quad.toml
index 5c11f427f..975118a0f 100644
--- a/bin/app/quad.toml
+++ b/bin/app/quad.toml
@@ -6,8 +6,7 @@ java_files = [
"java/textinput/State.java",
"java/textinput/Listener.java",
"java/textinput/Settings.java",
- "java/textinput/GameTextInput.java",
- "java/textinput/Pair.java"
+ "java/textinput/GameTextInput.java"
]
comptime_jar_files = [
"android-libs/androidx/core-1.9.0.jar"
diff --git a/bin/app/src/android/mod.rs b/bin/app/src/android/mod.rs
index 12a389a9f..19e3760e0 100644
--- a/bin/app/src/android/mod.rs
+++ b/bin/app/src/android/mod.rs
@@ -22,6 +22,7 @@ use std::{collections::HashMap, path::PathBuf, sync::LazyLock};
pub mod insets;
pub mod textinput;
+mod util;
pub mod vid;
macro_rules! call_mainactivity_int_method {
diff --git a/bin/app/src/android/textinput/state.rs b/bin/app/src/android/textinput/gametextinput.rs
similarity index 51%
rename from bin/app/src/android/textinput/state.rs
rename to bin/app/src/android/textinput/gametextinput.rs
index a67a5322f..5ec5f1865 100644
--- a/bin/app/src/android/textinput/state.rs
+++ b/bin/app/src/android/textinput/gametextinput.rs
@@ -16,17 +16,24 @@
* along with this program. If not, see .
*/
-use async_channel::Sender as AsyncSender;
-use miniquad::native::android::{ndk_sys, ndk_utils::*};
-use parking_lot::Mutex as SyncMutex;
-use std::ffi::CString;
+use miniquad::native::android::{self, ndk_sys, ndk_utils::*};
+use parking_lot::{Mutex as SyncMutex, RwLock};
+use std::{
+ ffi::CString,
+ sync::{Arc, OnceLock},
+};
-use super::AndroidTextInputState;
+use super::{AndroidTextInputState, SharedStatePtr};
-const DEFAULT_MAX_STRING_SIZE: usize = 1 << 16;
+macro_rules! w { ($($arg:tt)*) => { warn!(target: "android::textinput::gametextinput", $($arg)*); } }
pub const SPAN_UNDEFINED: i32 = -1;
+/// Global GameTextInput instance for JNI bridge
+///
+/// Single global instance since only ONE editor is active at a time.
+pub static GAME_TEXT_INPUT: OnceLock = OnceLock::new();
+
struct StateClassInfo {
text: ndk_sys::jfieldID,
selection_start: ndk_sys::jfieldID,
@@ -36,19 +43,21 @@ struct StateClassInfo {
}
pub struct GameTextInput {
- env: *mut ndk_sys::JNIEnv,
- state: SyncMutex,
- input_connection: Option,
+ state: SyncMutex