//! Bindings for `AConfiguration`. //! //! See also the [NDK docs](https://developer.android.com/ndk/reference/group/configuration) for //! `AConfiguration`, as well as the [docs for providing //! resources](https://developer.android.com/guide/topics/resources/providing-resources.html), //! which explain many of the configuration values. The [`android.content.res.Configuration` //! javadoc](https://developer.android.com/reference/android/content/res/Configuration.html) may //! also have useful information. use crate::asset::AssetManager; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::convert::TryInto; use std::fmt; use std::ptr::NonNull; /// A native `AConfiguration *`. /// /// This stores information about configuration. See [the NDK /// docs](https://developer.android.com/ndk/reference/group/configuration) pub struct Configuration { ptr: NonNull, } unsafe impl Send for Configuration {} unsafe impl Sync for Configuration {} impl Drop for Configuration { fn drop(&mut self) { unsafe { ffi::AConfiguration_delete(self.ptr.as_ptr()) } } } impl Clone for Configuration { fn clone(&self) -> Self { let mut new = Self::new(); new.copy(self); new } } impl PartialEq for Configuration { fn eq(&self, other: &Self) -> bool { self.diff(other).0 == 0 } } impl Eq for Configuration {} impl fmt::Debug for Configuration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Configuration") .field("mcc", &self.mcc()) .field("mnc", &self.mnc()) .field("lang", &self.language()) .field("country", &self.country()) .field("orientation", &self.orientation()) .field("touchscreen", &self.touchscreen()) .field("density", &self.density()) .field("keyboard", &self.keyboard()) .field("navigation", &self.navigation()) .field("keys_hidden", &self.keys_hidden()) .field("nav_hidden", &self.nav_hidden()) .field("sdk_version", &self.sdk_version()) .field("screen_size", &self.screen_size()) .field("screen_long", &self.screen_long()) .field("ui_mode_type", &self.ui_mode_type()) .field("ui_mode_night", &self.ui_mode_night()) .finish() } } impl Configuration { /// Construct a `Configuration` from a pointer. /// /// By calling this function, you assert that it is a valid pointer to a native /// `AConfiguration`, and give ownership of it to the `Configuration` instance. pub unsafe fn from_ptr(ptr: NonNull) -> Self { Self { ptr } } /// Create a new `Configuration`, with the same contents as the `AConfiguration` referenced by /// the pointer. /// /// This is useful if you have a pointer, but not ownership of it. pub unsafe fn clone_from_ptr(ptr: NonNull) -> Self { let conf = Self::new(); ffi::AConfiguration_copy(conf.ptr.as_ptr(), ptr.as_ptr()); conf } /// The pointer to the native `AConfiguration`. Keep in mind that the `Configuration` object /// still has ownership, and will free it when dropped. pub fn ptr(&self) -> NonNull { self.ptr } pub fn from_asset_manager(am: &AssetManager) -> Self { let config = Self::new(); unsafe { ffi::AConfiguration_fromAssetManager(config.ptr().as_mut(), am.ptr().as_mut()); } config } /// Create a new `Configuration`, with none of the values set. pub fn new() -> Self { unsafe { Self { ptr: NonNull::new(ffi::AConfiguration_new()).unwrap(), } } } /// `dest.copy(&src)` copies the contents of `src` to `dest` pub fn copy(&mut self, other: &Self) { unsafe { ffi::AConfiguration_copy(self.ptr.as_ptr(), other.ptr.as_ptr()) } } /// Information about what fields differ between the two configurations pub fn diff(&self, other: &Self) -> DiffResult { unsafe { DiffResult(ffi::AConfiguration_diff(self.ptr.as_ptr(), other.ptr.as_ptr()) as u32) } } /// Returns false if anything in `self` conflicts with `requested` pub fn matches(&self, requested: &Self) -> bool { unsafe { ffi::AConfiguration_match(self.ptr.as_ptr(), requested.ptr.as_ptr()) != 0 } } /// Returns the country code. It will always be two letters. pub fn country(&self) -> String { let mut result = " ".to_owned(); unsafe { ffi::AConfiguration_getCountry(self.ptr.as_ptr(), result.as_mut_ptr() as *mut _); } result } /// Returns the screen density in dpi. /// /// On some devices it can return values outside of the density enum. pub fn density(&self) -> Option { let density = unsafe { ffi::AConfiguration_getDensity(self.ptr.as_ptr()) as u32 }; match density { ffi::ACONFIGURATION_DENSITY_DEFAULT => Some(160), ffi::ACONFIGURATION_DENSITY_ANY => None, ffi::ACONFIGURATION_DENSITY_NONE => None, density => Some(density), } } /// Returns the keyboard type. pub fn keyboard(&self) -> Keyboard { unsafe { (ffi::AConfiguration_getKeyboard(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } /// Returns keyboard visibility/availability. pub fn keys_hidden(&self) -> KeysHidden { unsafe { (ffi::AConfiguration_getKeysHidden(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } /// Returns the language, as a `String` of two characters, if a language is set pub fn language(&self) -> Option { let mut chars = [0u8; 2]; unsafe { ffi::AConfiguration_getLanguage(self.ptr.as_ptr(), chars[..].as_mut_ptr() as *mut _); } if chars[0] == 0 { None } else { Some(std::str::from_utf8(&chars[..]).unwrap().to_owned()) } } /// Returns the layout direction pub fn layout_direction(&self) -> LayoutDir { unsafe { (ffi::AConfiguration_getLayoutDirection(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } /// Returns the mobile country code. pub fn mcc(&self) -> i32 { unsafe { ffi::AConfiguration_getMcc(self.ptr.as_ptr()) } } /// Returns the mobile network code, if one is defined pub fn mnc(&self) -> Option { unsafe { match ffi::AConfiguration_getMnc(self.ptr.as_ptr()) { 0 => None, x if x == ffi::ACONFIGURATION_MNC_ZERO as i32 => Some(0), x => Some(x), } } } pub fn nav_hidden(&self) -> NavHidden { unsafe { (ffi::AConfiguration_getNavHidden(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn navigation(&self) -> Navigation { unsafe { (ffi::AConfiguration_getNavigation(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn orientation(&self) -> Orientation { unsafe { (ffi::AConfiguration_getOrientation(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn screen_height_dp(&self) -> Option { unsafe { let height = ffi::AConfiguration_getScreenHeightDp(self.ptr.as_ptr()); if height == ffi::ACONFIGURATION_SCREEN_HEIGHT_DP_ANY as i32 { None } else { Some(height) } } } pub fn screen_width_dp(&self) -> Option { unsafe { let width = ffi::AConfiguration_getScreenWidthDp(self.ptr.as_ptr()); if width == ffi::ACONFIGURATION_SCREEN_WIDTH_DP_ANY as i32 { None } else { Some(width) } } } pub fn screen_long(&self) -> ScreenLong { unsafe { (ffi::AConfiguration_getScreenLong(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn screen_round(&self) -> ScreenRound { unsafe { (ffi::AConfiguration_getScreenRound(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn screen_size(&self) -> ScreenSize { unsafe { (ffi::AConfiguration_getScreenSize(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn sdk_version(&self) -> i32 { unsafe { ffi::AConfiguration_getSdkVersion(self.ptr.as_ptr()) } } pub fn smallest_screen_width_dp(&self) -> Option { unsafe { let width = ffi::AConfiguration_getSmallestScreenWidthDp(self.ptr.as_ptr()); if width == ffi::ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY as i32 { None } else { Some(width) } } } pub fn touchscreen(&self) -> Touchscreen { unsafe { (ffi::AConfiguration_getTouchscreen(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn ui_mode_night(&self) -> UiModeNight { unsafe { (ffi::AConfiguration_getUiModeNight(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } pub fn ui_mode_type(&self) -> UiModeType { unsafe { (ffi::AConfiguration_getUiModeType(self.ptr.as_ptr()) as u32) .try_into() .unwrap() } } } /// A bitfield representing the differences between two `Configuration`s #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct DiffResult(pub u32); impl DiffResult { pub fn mcc(self) -> bool { self.0 & ffi::ACONFIGURATION_MCC != 0 } pub fn mnc(self) -> bool { self.0 & ffi::ACONFIGURATION_MNC != 0 } pub fn locale(self) -> bool { self.0 & ffi::ACONFIGURATION_LOCALE != 0 } pub fn touchscreen(self) -> bool { self.0 & ffi::ACONFIGURATION_TOUCHSCREEN != 0 } pub fn keyboard(self) -> bool { self.0 & ffi::ACONFIGURATION_KEYBOARD != 0 } pub fn keyboard_hidden(self) -> bool { self.0 & ffi::ACONFIGURATION_KEYBOARD_HIDDEN != 0 } pub fn navigation(self) -> bool { self.0 & ffi::ACONFIGURATION_NAVIGATION != 0 } pub fn orientation(self) -> bool { self.0 & ffi::ACONFIGURATION_ORIENTATION != 0 } pub fn density(self) -> bool { self.0 & ffi::ACONFIGURATION_DENSITY != 0 } pub fn screen_size(self) -> bool { self.0 & ffi::ACONFIGURATION_SCREEN_SIZE != 0 } pub fn version(self) -> bool { self.0 & ffi::ACONFIGURATION_VERSION != 0 } pub fn screen_layout(self) -> bool { self.0 & ffi::ACONFIGURATION_SCREEN_LAYOUT != 0 } pub fn ui_mode(self) -> bool { self.0 & ffi::ACONFIGURATION_UI_MODE != 0 } pub fn smallest_screen_size(self) -> bool { self.0 & ffi::ACONFIGURATION_SMALLEST_SCREEN_SIZE != 0 } pub fn layout_dir(self) -> bool { self.0 & ffi::ACONFIGURATION_LAYOUTDIR != 0 } pub fn screen_round(self) -> bool { self.0 & ffi::ACONFIGURATION_SCREEN_ROUND != 0 } pub fn color_mode(self) -> bool { self.0 & ffi::ACONFIGURATION_COLOR_MODE != 0 } } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum Orientation { Any = ffi::ACONFIGURATION_ORIENTATION_ANY, Port = ffi::ACONFIGURATION_ORIENTATION_PORT, Land = ffi::ACONFIGURATION_ORIENTATION_LAND, Square = ffi::ACONFIGURATION_ORIENTATION_SQUARE, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum Touchscreen { Any = ffi::ACONFIGURATION_TOUCHSCREEN_ANY, NoTouch = ffi::ACONFIGURATION_TOUCHSCREEN_NOTOUCH, Stylus = ffi::ACONFIGURATION_TOUCHSCREEN_STYLUS, Finger = ffi::ACONFIGURATION_TOUCHSCREEN_FINGER, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum Density { Default = ffi::ACONFIGURATION_DENSITY_DEFAULT, Low = ffi::ACONFIGURATION_DENSITY_LOW, Medium = ffi::ACONFIGURATION_DENSITY_MEDIUM, TV = ffi::ACONFIGURATION_DENSITY_TV, High = ffi::ACONFIGURATION_DENSITY_HIGH, XHigh = ffi::ACONFIGURATION_DENSITY_XHIGH, XXHigh = ffi::ACONFIGURATION_DENSITY_XXHIGH, XXXHigh = ffi::ACONFIGURATION_DENSITY_XXXHIGH, Any = ffi::ACONFIGURATION_DENSITY_ANY, None = ffi::ACONFIGURATION_DENSITY_NONE, } impl Density { /// The DPI associated with the density class. /// See [the Android screen density /// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp) /// /// There are some `Density` values that have no associated DPI; these values return `None`. pub fn dpi(self) -> Option { match self { Self::Default => Some(160), // Or should it be None? Self::Low => Some(120), Self::Medium => Some(160), Self::High => Some(240), Self::XHigh => Some(320), Self::XXHigh => Some(480), Self::XXXHigh => Some(640), Self::TV => Some(213), Self::Any => None, Self::None => None, } } /// The Hi-DPI factor associated with the density class. This is the factor by which an /// image/resource should be scaled to match its size across devices. The baseline is a 160dpi /// screen (i.e., Hi-DPI factor = DPI / 160). /// See [the Android screen density /// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp) /// /// There are some `Density` values that have no associated DPI; these values return `None`. pub fn approx_hidpi_factor(self) -> Option { match self { Self::Default => Some(1.), // Or should it be None? Self::Low => Some(0.75), Self::Medium => Some(1.), Self::High => Some(1.5), Self::XHigh => Some(2.), Self::XXHigh => Some(3.), Self::XXXHigh => Some(4.), Self::TV => Some(4. / 3.), Self::Any => None, Self::None => None, } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum Keyboard { Any = ffi::ACONFIGURATION_KEYBOARD_ANY, NoKeys = ffi::ACONFIGURATION_KEYBOARD_NOKEYS, Qwerty = ffi::ACONFIGURATION_KEYBOARD_QWERTY, TwelveKey = ffi::ACONFIGURATION_KEYBOARD_12KEY, } // FIXME is it a bitmask? // FIXME are they all bitmasks? #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum Navigation { Any = ffi::ACONFIGURATION_NAVIGATION_ANY, NoNav = ffi::ACONFIGURATION_NAVIGATION_NONAV, DPad = ffi::ACONFIGURATION_NAVIGATION_DPAD, Trackball = ffi::ACONFIGURATION_NAVIGATION_TRACKBALL, Wheel = ffi::ACONFIGURATION_NAVIGATION_WHEEL, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum KeysHidden { Any = ffi::ACONFIGURATION_KEYSHIDDEN_ANY, No = ffi::ACONFIGURATION_KEYSHIDDEN_NO, Yes = ffi::ACONFIGURATION_KEYSHIDDEN_YES, Soft = ffi::ACONFIGURATION_KEYSHIDDEN_SOFT, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum NavHidden { Any = ffi::ACONFIGURATION_NAVHIDDEN_ANY, No = ffi::ACONFIGURATION_NAVHIDDEN_NO, Yes = ffi::ACONFIGURATION_NAVHIDDEN_YES, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum ScreenSize { Any = ffi::ACONFIGURATION_SCREENSIZE_ANY, Small = ffi::ACONFIGURATION_SCREENSIZE_SMALL, Normal = ffi::ACONFIGURATION_SCREENSIZE_NORMAL, Large = ffi::ACONFIGURATION_SCREENSIZE_LARGE, XLarge = ffi::ACONFIGURATION_SCREENSIZE_XLARGE, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum ScreenLong { Any = ffi::ACONFIGURATION_SCREENLONG_ANY, No = ffi::ACONFIGURATION_SCREENLONG_NO, Yes = ffi::ACONFIGURATION_SCREENLONG_YES, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum ScreenRound { Any = ffi::ACONFIGURATION_SCREENROUND_ANY, No = ffi::ACONFIGURATION_SCREENROUND_NO, Yes = ffi::ACONFIGURATION_SCREENROUND_YES, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum WideColorGamut { Any = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_ANY, No = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_NO, Yes = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_YES, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum HDR { Any = ffi::ACONFIGURATION_HDR_ANY, No = ffi::ACONFIGURATION_HDR_NO, Yes = ffi::ACONFIGURATION_HDR_YES, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum LayoutDir { Any = ffi::ACONFIGURATION_LAYOUTDIR_ANY, Ltr = ffi::ACONFIGURATION_LAYOUTDIR_LTR, Rtl = ffi::ACONFIGURATION_LAYOUTDIR_RTL, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum UiModeType { Any = ffi::ACONFIGURATION_UI_MODE_TYPE_ANY, Normal = ffi::ACONFIGURATION_UI_MODE_TYPE_NORMAL, Desk = ffi::ACONFIGURATION_UI_MODE_TYPE_DESK, Car = ffi::ACONFIGURATION_UI_MODE_TYPE_CAR, Television = ffi::ACONFIGURATION_UI_MODE_TYPE_TELEVISION, Applicance = ffi::ACONFIGURATION_UI_MODE_TYPE_APPLIANCE, Watch = ffi::ACONFIGURATION_UI_MODE_TYPE_WATCH, VrHeadset = ffi::ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET, } #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[repr(u32)] pub enum UiModeNight { Any = ffi::ACONFIGURATION_UI_MODE_NIGHT_ANY, No = ffi::ACONFIGURATION_UI_MODE_NIGHT_NO, Yes = ffi::ACONFIGURATION_UI_MODE_NIGHT_YES, }