Skip to content

Commit 735b4b6

Browse files
committed
feat: android native support
1 parent 2972c0e commit 735b4b6

3 files changed

Lines changed: 95 additions & 1 deletion

File tree

maa-framework/src/common.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,30 @@ bitflags::bitflags! {
308308
// ADB Controller Methods
309309
// ============================================================================
310310

311+
/// Raw screen resolution used by Android native control units.
312+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
313+
pub struct AndroidScreenResolution {
314+
/// Raw screenshot width reported by the control unit.
315+
pub width: i32,
316+
/// Raw screenshot height reported by the control unit.
317+
pub height: i32,
318+
}
319+
320+
/// Configuration for [`crate::controller::Controller::new_android_native`].
321+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
322+
pub struct AndroidNativeControllerConfig {
323+
/// Path to the Android native control unit shared library.
324+
pub library_path: String,
325+
/// Raw screenshot/touch coordinate resolution exposed by the control unit.
326+
pub screen_resolution: AndroidScreenResolution,
327+
/// Target Android display id. Defaults to `0` when omitted by MaaFramework.
328+
#[serde(default, skip_serializing_if = "Option::is_none")]
329+
pub display_id: Option<u32>,
330+
/// Whether to force-stop before `start_app`. Defaults to `false` when omitted.
331+
#[serde(default, skip_serializing_if = "Option::is_none")]
332+
pub force_stop: Option<bool>,
333+
}
334+
311335
bitflags::bitflags! {
312336
/// ADB screencap method flags.
313337
///
@@ -903,3 +927,37 @@ pub struct CustomRecognitionResult {
903927
pub box_rect: (i32, i32, i32, i32),
904928
pub detail: serde_json::Value,
905929
}
930+
931+
#[cfg(test)]
932+
mod tests {
933+
use super::{AndroidNativeControllerConfig, AndroidScreenResolution};
934+
use serde_json::json;
935+
936+
#[test]
937+
fn android_native_controller_config_serializes_expected_shape() {
938+
let config = AndroidNativeControllerConfig {
939+
library_path: "/data/local/tmp/libmaa_unit.so".to_string(),
940+
screen_resolution: AndroidScreenResolution {
941+
width: 1920,
942+
height: 1080,
943+
},
944+
display_id: Some(1),
945+
force_stop: Some(true),
946+
};
947+
948+
let value = serde_json::to_value(config).unwrap();
949+
950+
assert_eq!(
951+
value,
952+
json!({
953+
"library_path": "/data/local/tmp/libmaa_unit.so",
954+
"screen_resolution": {
955+
"width": 1920,
956+
"height": 1080
957+
},
958+
"display_id": 1,
959+
"force_stop": true
960+
})
961+
);
962+
}
963+
}

maa-framework/src/controller.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
//! Device controller for input, screen capture, and app management.
22
33
use crate::{MaaError, MaaResult, common, sys};
4+
use serde::Serialize;
45
use std::collections::HashMap;
56
use std::ffi::CString;
67
use std::os::raw::c_void;
8+
#[cfg(feature = "dynamic")]
9+
use std::panic::AssertUnwindSafe;
710
use std::ptr::NonNull;
811
use std::sync::{Arc, Mutex};
912

@@ -151,6 +154,37 @@ impl Controller {
151154
Self::from_handle(handle)
152155
}
153156

157+
/// Create a new Android native controller.
158+
///
159+
/// The config is serialized to JSON and passed to
160+
/// `MaaAndroidNativeControllerCreate`. You can pass either a
161+
/// [`common::AndroidNativeControllerConfig`] value or any other serializable
162+
/// type that matches the expected JSON schema.
163+
pub fn new_android_native<T: Serialize>(config: &T) -> MaaResult<Self> {
164+
let config_json = serde_json::to_string(config).map_err(|e| {
165+
MaaError::InvalidConfig(format!(
166+
"Failed to serialize Android native controller config: {}",
167+
e
168+
))
169+
})?;
170+
let c_config = CString::new(config_json)?;
171+
172+
#[cfg(feature = "dynamic")]
173+
let handle = std::panic::catch_unwind(AssertUnwindSafe(|| unsafe {
174+
sys::MaaAndroidNativeControllerCreate(c_config.as_ptr())
175+
}))
176+
.map_err(|_| {
177+
MaaError::InvalidArgument(
178+
"Android native controller is not available in this MaaFramework build".to_string(),
179+
)
180+
})?;
181+
182+
#[cfg(not(feature = "dynamic"))]
183+
let handle = unsafe { sys::MaaAndroidNativeControllerCreate(c_config.as_ptr()) };
184+
185+
Self::from_handle(handle)
186+
}
187+
154188
/// Create a new PlayCover controller for iOS app control on macOS.
155189
156190
pub fn new_playcover(address: &str, uuid: &str) -> MaaResult<Self> {

maa-framework/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
//! |--------|-------------|
4646
//! | [`tasker`] | Task execution and pipeline management |
4747
//! | [`resource`] | Resource loading (images, models, pipelines) |
48-
//! | [`controller`] | Device control (ADB, Win32, macOS, PlayCover, WlRoots) |
48+
//! | [`controller`] | Device control (ADB, Win32, macOS, Android Native, PlayCover, WlRoots) |
4949
//! | [`context`] | Task execution context for custom components |
5050
//! | [`toolkit`] | Device discovery utilities |
5151
//! | [`pipeline`] | Pipeline configuration types |
@@ -92,6 +92,8 @@ pub mod tasker;
9292
pub mod toolkit;
9393
pub mod util;
9494

95+
pub use common::AndroidNativeControllerConfig;
96+
pub use common::AndroidScreenResolution;
9597
pub use common::ControllerFeature;
9698
pub use common::MaaStatus;
9799
pub use error::{MaaError, MaaResult};

0 commit comments

Comments
 (0)