Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

energy monitor example fails to run on Android 12 #5031

Closed
matthewjrichey opened this issue Apr 8, 2024 · 3 comments · Fixed by #5047
Closed

energy monitor example fails to run on Android 12 #5031

matthewjrichey opened this issue Apr 8, 2024 · 3 comments · Fixed by #5047

Comments

@matthewjrichey
Copy link

Slint release tested: 1.6.0

Command used to build example:
cargo apk build -p energy-monitor --target x86_64-linux-android --lib

Executed on emulated Android 12 (x86_64) and actual Android 12 device (aarch64).

Failure (captured with adb logcat):

04-06 16:02:03.957  2487  2487 W NativeActivity: NativeActivity LoadNativeLibrary("/data/app/~~loKhCe5jYqQSlyBRQqAc3w==/rust.energy_monitor-4pE9Ttt1jr8x3B5BrjazKQ==/lib/x86_64/libenergy_monitor.so") failed: dlopen failed: cannot locate symbol "AMotionEvent_getActionButton" referenced by "/data/app/~~loKhCe5jYqQSlyBRQqAc3w==/rust.energy_monitor-4pE9Ttt1jr8x3B5BrjazKQ==/lib/x86_64/libenergy_monitor.so"...
04-06 16:02:03.958  2487  2487 D AndroidRuntime: Shutting down VM
04-06 16:02:03.959  2487  2487 E AndroidRuntime: FATAL EXCEPTION: main
04-06 16:02:03.959  2487  2487 E AndroidRuntime: Process: rust.energy_monitor, PID: 2487
04-06 16:02:03.959  2487  2487 E AndroidRuntime: java.lang.UnsatisfiedLinkError: Unable to load native library "/data/app/~~loKhCe5jYqQSlyBRQqAc3w==/rust.energy_monitor-4pE9Ttt1jr8x3B5BrjazKQ==/lib/x86_64/libenergy_monitor.so": dlopen failed: cannot locate symbol "AMotionEvent_getActionButton" referenced by "/data/app/~~loKhCe5jYqQSlyBRQqAc3w==/rust.energy_monitor-4pE9Ttt1jr8x3B5BrjazKQ==/lib/x86_64/libenergy_monitor.so"...
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.NativeActivity.onCreate(NativeActivity.java:178)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:7994)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:7978)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:223)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7656)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
04-06 16:02:03.959  2487  2487 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Researched and discovered that AMotionEvent_getActionButton is only available on Android NDK 33 (Android 13) or higher. I came up with a working fix for the problem, which is tested and working on Android 12. I will file the fix as a PR.

@matthewjrichey
Copy link
Author

As a random member of the public, I do not have push access to this public repository (which is understandable), so I could not create a pull request.

Here is a diff that captures my fix for the issue.

diff --git a/internal/backends/android-activity/androidwindowadapter.rs b/internal/backends/android-activity/androidwindowadapter.rs
index 10e98dff8..ff26cd9ea 100644
--- a/internal/backends/android-activity/androidwindowadapter.rs
+++ b/internal/backends/android-activity/androidwindowadapter.rs
@@ -3,7 +3,9 @@
 
 use super::*;
 use crate::javahelper::{print_jni_error, JavaHelper};
-use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction, MotionEvent};
+use android_activity::input::{
+    ButtonState, InputEvent, KeyAction, Keycode, MotionAction, MotionEvent,
+};
 use android_activity::{InputStatus, MainEvent, PollEvent};
 use i_slint_core::api::{LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window};
 use i_slint_core::items::ColorScheme;
@@ -40,6 +42,7 @@ pub struct AndroidWindowAdapter {
     pub(crate) show_cursor_handles: Cell<bool>,
 
     long_press: RefCell<Option<LongPressDetection>>,
+    last_pressed_state: Cell<ButtonState>,
 }
 
 impl WindowAdapter for AndroidWindowAdapter {
@@ -187,6 +190,7 @@ pub fn new(app: AndroidApp) -> Rc<Self> {
             offset: Default::default(),
             show_cursor_handles: Cell::new(false),
             long_press: RefCell::default(),
+            last_pressed_state: Cell::new(ButtonState(0)),
         })
     }
 
@@ -281,7 +285,7 @@ fn process_inputs(&self) -> Result<(), android_activity::error::AppError> {
                         self.window.dispatch_event(WindowEvent::PointerPressed {
                             position: position_for_event(motion_event, self.offset.get())
                                 .to_logical(self.window.scale_factor()),
-                            button: button_for_event(motion_event),
+                            button: button_for_event(motion_event, &self.last_pressed_state),
                         });
                         InputStatus::Handled
                     }
@@ -308,7 +312,7 @@ fn process_inputs(&self) -> Result<(), android_activity::error::AppError> {
                         self.window.dispatch_event(WindowEvent::PointerReleased {
                             position: position_for_event(motion_event, self.offset.get())
                                 .to_logical(self.window.scale_factor()),
-                            button: button_for_event(motion_event),
+                            button: button_for_event(motion_event, &self.last_pressed_state),
                         });
                         InputStatus::Handled
                     }
@@ -456,13 +460,51 @@ fn position_for_event(motion_event: &MotionEvent, offset: PhysicalPosition) -> P
     })
 }
 
-fn button_for_event(motion_event: &MotionEvent) -> PointerEventButton {
-    match motion_event.action_button() {
-        android_activity::input::Button::Primary => PointerEventButton::Left,
-        android_activity::input::Button::Secondary => PointerEventButton::Right,
-        android_activity::input::Button::Tertiary => PointerEventButton::Middle,
-        _ => PointerEventButton::Other,
+fn button_for_event(
+    motion_event: &MotionEvent,
+    last_pressed_cell: &Cell<ButtonState>,
+) -> PointerEventButton {
+    //
+    // The motion_event API has a method called action_button() which can be used to directly
+    // determine the button associated with the event. However, the disadvantage of using the
+    // action_button() API is that it relies on NDK 33 or higher, which implies that the output
+    // application will only run on Android 13 or higher.
+    //
+    // This functionally equivalent method of computing the action button relies on the
+    // button_state() call from the motion event, rather than action_button(). It is a bit more
+    // complex than using action_button() directly, since the previous button state must be
+    // tracked and used in the calculation for computing which button was toggled. However, this
+    // will run on Android 12 (and possibly lower).
+    //
+    // See here for further discussion:
+    //
+    // https://stackoverflow.com/questions/75718566/amotionevent-getbuttonstate-returns-0-for-every-button-during-mouse-button-relea
+    //
+    let cur_pressed_state = motion_event.button_state();
+    let last_pressed_state = last_pressed_cell.get();
+    let toggled = match motion_event.action() {
+        MotionAction::ButtonPress => {
+            last_pressed_cell.set(cur_pressed_state);
+            ButtonState((last_pressed_state.0 ^ cur_pressed_state.0) & cur_pressed_state.0)
+        }
+        MotionAction::ButtonRelease => {
+            last_pressed_cell.set(cur_pressed_state);
+            ButtonState((last_pressed_state.0 ^ cur_pressed_state.0) & last_pressed_state.0)
+        }
+        _ => ButtonState(0),
+    };
+
+    // if multiple buttons toggled, primary takes precedence, then secondary, etc.
+    if toggled.primary() {
+        return PointerEventButton::Left;
+    }
+    if toggled.secondary() {
+        return PointerEventButton::Right;
+    }
+    if toggled.teriary() {
+        return PointerEventButton::Middle;
     }
+    return PointerEventButton::Other;
 }
 
 fn map_key_event(key_event: &android_activity::input::KeyEvent) -> Option<WindowEvent> {

hunger added a commit to hunger/slint that referenced this issue Apr 12, 2024
Make the code by @matthewjrichey found in issue slint-ui#5031 into a PR:

> [...] discovered that `AMotionEvent_getActionButton` is only available on
> Android NDK 33 (Android 13) or higher. I came up with a working fix for the
> problem, which is tested and working on Android 12.

Done-by: @matthewjrichey
@hunger
Copy link
Member

hunger commented Apr 12, 2024

I took the liberty to turn this diff into a PR here: #5047 for your review-conveninece.

The code looks sensible to me, but I have no Android setup here (nor a Android 12 device) to test this with.

@matthewjrichey: You can clone the repo, push your code into your own clone and then create a PR from that. This approach is not limited to Slint, it works for any project on github, just in case you run into the situation again.

@hunger
Copy link
Member

hunger commented Apr 12, 2024

Thank you for your time and contribution. I think this is small enough to count as a "simple fix" and not need the full CLA dance, which my PR goes around...

ogoffart pushed a commit that referenced this issue Apr 12, 2024
Make the code by @matthewjrichey found in issue #5031 into a PR:

> [...] discovered that `AMotionEvent_getActionButton` is only available on
> Android NDK 33 (Android 13) or higher. I came up with a working fix for the
> problem, which is tested and working on Android 12.

Done-by: @matthewjrichey
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants