From 48993acf582a0c1ffc27304d1bd1809efa300cc0 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 12 Sep 2022 18:26:38 +0100 Subject: [PATCH] Support changing window manager params This enables support for changing the various WindowManager parameters documented here: https://developer.android.com/reference/android/view/WindowManager.LayoutParams either via NativeActivity_setWindowFlags or GameActivity_setWindowFlags Fixes: #25 --- android-activity/src/game_activity/mod.rs | 13 +- android-activity/src/lib.rs | 198 +++++++++++++++++++- android-activity/src/native_activity/mod.rs | 18 +- 3 files changed, 226 insertions(+), 3 deletions(-) diff --git a/android-activity/src/game_activity/mod.rs b/android-activity/src/game_activity/mod.rs index c551dae..2974d1b 100644 --- a/android-activity/src/game_activity/mod.rs +++ b/android-activity/src/game_activity/mod.rs @@ -23,7 +23,7 @@ use ndk::asset::AssetManager; use ndk::configuration::Configuration; use ndk::native_window::NativeWindow; -use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect}; +use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect, WindowManagerFlags}; mod ffi; @@ -309,6 +309,17 @@ impl AndroidAppInner { } } + pub fn set_window_flags( + &self, + add_flags: WindowManagerFlags, + remove_flags: WindowManagerFlags, + ) { + unsafe { + let activity = (*self.native_app.as_ptr()).activity; + ffi::GameActivity_setWindowFlags(activity, add_flags.bits(), remove_flags.bits()) + } + } + pub fn enable_motion_axis(&mut self, axis: Axis) { unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) } } diff --git a/android-activity/src/lib.rs b/android-activity/src/lib.rs index 6a4db70..da1c628 100644 --- a/android-activity/src/lib.rs +++ b/android-activity/src/lib.rs @@ -6,6 +6,8 @@ use std::time::Duration; 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"); @@ -170,9 +172,174 @@ pub enum PollEvent<'a> { } use activity_impl::AndroidAppInner; - pub use activity_impl::AndroidAppWaker; +bitflags! { + /// Flags for [`AndroidApp::set_window_flags`] + /// as per the [android.view.WindowManager.LayoutParams Java API](https://developer.android.com/reference/android/view/WindowManager.LayoutParams) + pub struct WindowManagerFlags: u32 { + /// As long as this window is visible to the user, allow the lock + /// screen to activate while the screen is on. This can be used + /// independently, or in combination with + /// [`Self::KEEP_SCREEN_ON`] and/or [`Self::SHOW_WHEN_LOCKED`] + const ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; + + /// Everything behind this window will be dimmed. */ + const DIM_BEHIND = 0x00000002; + + /// Blur everything behind this window. + #[deprecated = "Blurring is no longer supported"] + const BLUR_BEHIND = 0x00000004; + + /// This window won't ever get key input focus, so the + /// user can not send key or other button events to it. Those will + /// instead go to whatever focusable window is behind it. This flag + /// will also enable [`Self::NOT_TOUCH_MODAL`] whether or not + /// that is explicitly set. + /// + /// Setting this flag also implies that the window will not need to + /// interact with + /// a soft input method, so it will be Z-ordered and positioned + /// independently of any active input method (typically this means it + /// gets Z-ordered on top of the input method, so it can use the full + /// screen for its content and cover the input method if needed. You + /// can use [`Self::ALT_FOCUSABLE_IM`] to modify this + /// behavior. + const NOT_FOCUSABLE = 0x00000008; + + /// This window can never receive touch events. + const NOT_TOUCHABLE = 0x00000010; + + /// Even when this window is focusable (if + /// [`Self::NOT_FOCUSABLE`] is not set), allow any pointer + /// events outside of the window to be sent to the windows behind it. + /// Otherwise it will consume all pointer events itself, regardless of + /// whether they are inside of the window. + const NOT_TOUCH_MODAL = 0x00000020; + + /// When set, if the device is asleep when the touch + /// screen is pressed, you will receive this first touch event. Usually + /// the first touch event is consumed by the system since the user can + /// not see what they are pressing on. + #[deprecated] + const TOUCHABLE_WHEN_WAKING = 0x00000040; + + /// As long as this window is visible to the user, keep + /// the device's screen turned on and bright. + const KEEP_SCREEN_ON = 0x00000080; + + /// Place the window within the entire screen, ignoring + /// decorations around the border (such as the status bar). The + /// window must correctly position its contents to take the screen + /// decoration into account. + const LAYOUT_IN_SCREEN = 0x00000100; + + /// Allows the window to extend outside of the screen. + const LAYOUT_NO_LIMITS = 0x00000200; + + /// Hide all screen decorations (such as the status + /// bar) while this window is displayed. This allows the window to + /// use the entire display space for itself -- the status bar will + /// be hidden when an app window with this flag set is on the top + /// layer. A fullscreen window will ignore a value of + /// [`Self::SOFT_INPUT_ADJUST_RESIZE`] the window will stay + /// fullscreen and will not resize. + const FULLSCREEN = 0x00000400; + + /// Override [`Self::FULLSCREEN`] and force the + /// screen decorations (such as the status bar) to be shown. + const FORCE_NOT_FULLSCREEN = 0x00000800; + /// Turn on dithering when compositing this window to + /// the screen. + #[deprecated="This flag is no longer used"] + const DITHER = 0x00001000; + + /// Treat the content of the window as secure, preventing + /// it from appearing in screenshots or from being viewed on non-secure + /// displays. + const SECURE = 0x00002000; + + /// A special mode where the layout parameters are used + /// to perform scaling of the surface when it is composited to the + /// screen. + const SCALED = 0x00004000; + + /// Intended for windows that will often be used when the user is + /// holding the screen against their face, it will aggressively + /// filter the event stream to prevent unintended presses in this + /// situation that may not be desired for a particular window, when + /// such an event stream is detected, the application will receive + /// a `AMOTION_EVENT_ACTION_CANCEL` to indicate this so + /// applications can handle this accordingly by taking no action on + /// the event until the finger is released. + const IGNORE_CHEEK_PRESSES = 0x00008000; + + /// A special option only for use in combination with + /// [`Self::LAYOUT_IN_SCREEN`]. When requesting layout in + /// the screen your window may appear on top of or behind screen decorations + /// such as the status bar. By also including this flag, the window + /// manager will report the inset rectangle needed to ensure your + /// content is not covered by screen decorations. + const LAYOUT_INSET_DECOR = 0x00010000; + + /// Invert the state of [`Self::NOT_FOCUSABLE`] with + /// respect to how this window interacts with the current method. + /// That is, if [`Self::NOT_FOCUSABLE`] is set and this flag is set, + /// then the window will behave as if it needs to interact with the + /// input method and thus be placed behind/away from it; if + /// [`Self::NOT_FOCUSABLE`] is not set and this flag is set, + /// then the window will behave as if it doesn't need to interact + /// with the input method and can be placed to use more space and + /// cover the input method. + const ALT_FOCUSABLE_IM = 0x00020000; + + /// If you have set [`Self::NOT_TOUCH_MODAL`], you + /// can set this flag to receive a single special MotionEvent with + /// the action + /// `AMOTION_EVENT_ACTION_OUTSIDE` for + /// touches that occur outside of your window. Note that you will not + /// receive the full down/move/up gesture, only the location of the + /// first down as an `AMOTION_EVENT_ACTION_OUTSIDE`. + const WATCH_OUTSIDE_TOUCH = 0x00040000; + + /// Special flag to let windows be shown when the screen + /// is locked. This will let application windows take precedence over + /// key guard or any other lock screens. Can be used with + /// [`Self::KEEP_SCREEN_ON`] to turn screen on and display + /// windows directly before showing the key guard window. Can be used with + /// [`Self::DISMISS_KEYGUARD`] to automatically fully + /// dismiss non-secure key guards. This flag only applies to the top-most + /// full-screen window. + const SHOW_WHEN_LOCKED = 0x00080000; + + /// Ask that the system wallpaper be shown behind + /// your window. The window surface must be translucent to be able + /// to actually see the wallpaper behind it; this flag just ensures + /// that the wallpaper surface will be there if this window actually + /// has translucent regions. + const SHOW_WALLPAPER = 0x00100000; + + /// When set as a window is being added or made + /// visible, once the window has been shown then the system will + /// poke the power manager's user activity (as if the user had woken + /// up the device) to turn the screen on. + const TURN_SCREEN_ON = 0x00200000; + + /// When set the window will cause the key guard to + /// be dismissed, only if it is not a secure lock key guard. Because such + /// a key guard is not needed for security, it will never re-appear if + /// the user navigates to another window (in contrast to + /// [`Self::SHOW_WHEN_LOCKED`], which will only temporarily + /// hide both secure and non-secure key guards but ensure they reappear + /// when the user moves to another UI that doesn't hide them). + /// If the key guard is currently active and is secure (requires an + /// unlock pattern) then the user will still need to confirm it before + /// seeing this window, unless [`Self::SHOW_WHEN_LOCKED`] has + /// also been set. + const DISMISS_KEYGUARD = 0x00400000; + } +} + #[derive(Debug, Clone)] pub struct AndroidApp { pub(crate) inner: Arc>, @@ -251,6 +418,26 @@ impl AndroidApp { self.inner.read().unwrap().asset_manager() } + /// Change the window flags of the given activity. + /// + /// Note that some flags must be set before the window decoration is created, + /// see + /// ``. + pub fn set_window_flags( + &self, + add_flags: WindowManagerFlags, + remove_flags: WindowManagerFlags, + ) { + self.inner + .write() + .unwrap() + .set_window_flags(add_flags, remove_flags); + } + + /// Enable additional input axis + /// + /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled + /// and other axis should be enabled explicitly. pub fn enable_motion_axis(&self, axis: input::Axis) { self.inner.write().unwrap().enable_motion_axis(axis); } @@ -259,6 +446,15 @@ impl AndroidApp { self.inner.write().unwrap().disable_motion_axis(axis); } + /// Query and process all out-standing input event + /// + /// Applications are generally either expected to call this in-sync with their rendering or + /// in response to a [`MainEvent::InputAvailable`] event being delivered. _Note though that your + /// application is will only be delivered a single [`MainEvent::InputAvailable`] event between calls + /// to this API._ + /// + /// To reduce overhead, by default only [`input::Axis::X`] and [`input::Axis::Y`] are enabled + /// and other axis should be enabled explicitly via [`Self::enable_motion_axis`]. pub fn input_events<'b, F>(&self, callback: F) where F: FnMut(&input::InputEvent), diff --git a/android-activity/src/native_activity/mod.rs b/android-activity/src/native_activity/mod.rs index 552dd34..99cd9c9 100644 --- a/android-activity/src/native_activity/mod.rs +++ b/android-activity/src/native_activity/mod.rs @@ -21,7 +21,7 @@ use ndk::configuration::Configuration; use ndk::input_queue::InputQueue; use ndk::native_window::NativeWindow; -use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect}; +use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect, WindowManagerFlags}; mod ffi; @@ -353,6 +353,22 @@ impl AndroidAppInner { } } + pub fn set_window_flags( + &self, + add_flags: WindowManagerFlags, + remove_flags: WindowManagerFlags, + ) { + let na = self.native_activity(); + let na_mut = na as *mut ndk_sys::ANativeActivity; + unsafe { + ffi::ANativeActivity_setWindowFlags( + na_mut.cast(), + add_flags.bits(), + remove_flags.bits(), + ); + } + } + pub fn enable_motion_axis(&self, _axis: input::Axis) { // NOP - The InputQueue API doesn't let us optimize which axis values are read }