#![deny(clippy::manual_let_else)]
use std::hash::Hash;
use std::sync::Arc;
use std::sync::RwLock;
use std::time::Duration;
use input::KeyCharacterMap;
use libc::c_void;
use ndk::asset::AssetManager;
use ndk::native_window::NativeWindow;
use bitflags::bitflags;
#[cfg(not(target_os = "android"))]
compile_error!("android-activity only supports compiling for Android");
#[cfg(all(feature = "game-activity", feature = "native-activity"))]
compile_error!(
r#"The "game-activity" and "native-activity" features cannot be enabled at the same time"#
);
#[cfg(all(
not(any(feature = "game-activity", feature = "native-activity")),
not(doc)
))]
compile_error!(
r#"Either "game-activity" or "native-activity" must be enabled as features
If you have set one of these features then this error indicates that Cargo is trying to
link together multiple implementations of android-activity (with incompatible versions)
which is not supported.
Since android-activity is responsible for the `android_main` entrypoint of your application
then there can only be a single implementation of android-activity linked with your application.
You can use `cargo tree` (e.g. via `cargo ndk -t arm64-v8a tree`) to identify why multiple
versions have been resolved.
You may need to add a `[patch]` into your Cargo.toml to ensure a specific version of
android-activity is used across all of your application's crates."#
);
#[cfg_attr(any(feature = "native-activity", doc), path = "native_activity/mod.rs")]
#[cfg_attr(any(feature = "game-activity", doc), path = "game_activity/mod.rs")]
pub(crate) mod activity_impl;
pub mod error;
use error::Result;
pub mod input;
mod config;
pub use config::ConfigurationRef;
mod util;
mod jni_utils;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Rect {
pub left: i32,
pub top: i32,
pub right: i32,
pub bottom: i32,
}
impl Rect {
pub fn empty() -> Self {
Self {
left: 0,
top: 0,
right: 0,
bottom: 0,
}
}
}
impl From<Rect> for ndk_sys::ARect {
fn from(rect: Rect) -> Self {
Self {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom,
}
}
}
impl From<ndk_sys::ARect> for Rect {
fn from(arect: ndk_sys::ARect) -> Self {
Self {
left: arect.left,
right: arect.right,
top: arect.top,
bottom: arect.bottom,
}
}
}
pub use activity_impl::StateLoader;
pub use activity_impl::StateSaver;
#[non_exhaustive]
#[derive(Debug)]
pub enum MainEvent<'a> {
InputAvailable,
#[non_exhaustive]
InitWindow {},
#[non_exhaustive]
TerminateWindow {},
#[non_exhaustive]
WindowResized {},
#[non_exhaustive]
RedrawNeeded {},
#[non_exhaustive]
ContentRectChanged {},
GainedFocus,
LostFocus,
#[non_exhaustive]
ConfigChanged {},
LowMemory,
Start,
#[non_exhaustive]
Resume { loader: StateLoader<'a> },
#[non_exhaustive]
SaveState { saver: StateSaver<'a> },
Pause,
Stop,
Destroy,
#[non_exhaustive]
InsetsChanged {},
}
#[derive(Debug)]
#[non_exhaustive]
pub enum PollEvent<'a> {
Wake,
Timeout,
Main(MainEvent<'a>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputStatus {
Handled,
Unhandled,
}
use activity_impl::AndroidAppInner;
pub use activity_impl::AndroidAppWaker;
bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct WindowManagerFlags: u32 {
const ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
const DIM_BEHIND = 0x00000002;
#[deprecated = "Blurring is no longer supported"]
const BLUR_BEHIND = 0x00000004;
const NOT_FOCUSABLE = 0x00000008;
const NOT_TOUCHABLE = 0x00000010;
const NOT_TOUCH_MODAL = 0x00000020;
#[deprecated]
const TOUCHABLE_WHEN_WAKING = 0x00000040;
const KEEP_SCREEN_ON = 0x00000080;
const LAYOUT_IN_SCREEN = 0x00000100;
const LAYOUT_NO_LIMITS = 0x00000200;
const FULLSCREEN = 0x00000400;
const FORCE_NOT_FULLSCREEN = 0x00000800;
#[deprecated="This flag is no longer used"]
const DITHER = 0x00001000;
const SECURE = 0x00002000;
const SCALED = 0x00004000;
const IGNORE_CHEEK_PRESSES = 0x00008000;
const LAYOUT_INSET_DECOR = 0x00010000;
const ALT_FOCUSABLE_IM = 0x00020000;
const WATCH_OUTSIDE_TOUCH = 0x00040000;
const SHOW_WHEN_LOCKED = 0x00080000;
const SHOW_WALLPAPER = 0x00100000;
const TURN_SCREEN_ON = 0x00200000;
const DISMISS_KEYGUARD = 0x00400000;
}
}
#[derive(Debug, Clone)]
pub struct AndroidApp {
pub(crate) inner: Arc<RwLock<AndroidAppInner>>,
}
impl PartialEq for AndroidApp {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl Eq for AndroidApp {}
impl Hash for AndroidApp {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.inner).hash(state);
}
}
impl AndroidApp {
pub fn native_window(&self) -> Option<NativeWindow> {
self.inner.read().unwrap().native_window()
}
pub fn vm_as_ptr(&self) -> *mut c_void {
self.inner.read().unwrap().vm_as_ptr()
}
pub fn activity_as_ptr(&self) -> *mut c_void {
self.inner.read().unwrap().activity_as_ptr()
}
pub fn poll_events<F>(&self, timeout: Option<Duration>, callback: F)
where
F: FnMut(PollEvent<'_>),
{
self.inner.read().unwrap().poll_events(timeout, callback);
}
pub fn create_waker(&self) -> AndroidAppWaker {
self.inner.read().unwrap().create_waker()
}
pub fn config(&self) -> ConfigurationRef {
self.inner.read().unwrap().config()
}
pub fn content_rect(&self) -> Rect {
self.inner.read().unwrap().content_rect()
}
pub fn asset_manager(&self) -> AssetManager {
self.inner.read().unwrap().asset_manager()
}
pub fn set_window_flags(
&self,
add_flags: WindowManagerFlags,
remove_flags: WindowManagerFlags,
) {
self.inner
.write()
.unwrap()
.set_window_flags(add_flags, remove_flags);
}
pub fn enable_motion_axis(&self, axis: input::Axis) {
self.inner.write().unwrap().enable_motion_axis(axis);
}
pub fn disable_motion_axis(&self, axis: input::Axis) {
self.inner.write().unwrap().disable_motion_axis(axis);
}
pub fn show_soft_input(&self, show_implicit: bool) {
self.inner.read().unwrap().show_soft_input(show_implicit);
}
pub fn hide_soft_input(&self, hide_implicit_only: bool) {
self.inner
.read()
.unwrap()
.hide_soft_input(hide_implicit_only);
}
pub fn text_input_state(&self) -> input::TextInputState {
self.inner.read().unwrap().text_input_state()
}
pub fn set_text_input_state(&self, state: input::TextInputState) {
self.inner.read().unwrap().set_text_input_state(state);
}
pub fn input_events_iter(&self) -> Result<input::InputIterator> {
let receiver = {
let guard = self.inner.read().unwrap();
guard.input_events_receiver()?
};
Ok(input::InputIterator {
inner: receiver.into(),
})
}
pub fn device_key_character_map(&self, device_id: i32) -> Result<KeyCharacterMap> {
Ok(self
.inner
.read()
.unwrap()
.device_key_character_map(device_id)?)
}
pub fn sdk_version() -> i32 {
let mut prop = android_properties::getprop("ro.build.version.sdk");
if let Some(val) = prop.value() {
val.parse::<i32>()
.expect("Failed to parse ro.build.version.sdk property")
} else {
panic!("Couldn't read ro.build.version.sdk system property");
}
}
pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
self.inner.read().unwrap().internal_data_path()
}
pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
self.inner.read().unwrap().external_data_path()
}
pub fn obb_path(&self) -> Option<std::path::PathBuf> {
self.inner.read().unwrap().obb_path()
}
}
#[test]
fn test_app_is_send_sync() {
fn needs_send_sync<T: Send + Sync>() {}
needs_send_sync::<AndroidApp>();
}