android_ndk/
configuration.rs

1//! Bindings for `AConfiguration`.
2//!
3//! See also the [NDK docs](https://developer.android.com/ndk/reference/group/configuration) for
4//! `AConfiguration`, as well as the [docs for providing
5//! resources](https://developer.android.com/guide/topics/resources/providing-resources.html),
6//! which explain many of the configuration values.  The [`android.content.res.Configuration`
7//! javadoc](https://developer.android.com/reference/android/content/res/Configuration.html) may
8//! also have useful information.
9
10use num_enum::{IntoPrimitive, TryFromPrimitive};
11use std::convert::TryInto;
12use std::fmt;
13use std::ptr::NonNull;
14
15/// A native `AConfiguration *`.
16///
17/// This stores information about configuration.  See [the NDK
18/// docs](https://developer.android.com/ndk/reference/group/configuration)
19pub struct Configuration {
20    ptr: NonNull<ffi::AConfiguration>,
21}
22
23unsafe impl Send for Configuration {}
24unsafe impl Sync for Configuration {}
25
26impl Drop for Configuration {
27    fn drop(&mut self) {
28        unsafe { ffi::AConfiguration_delete(self.ptr.as_ptr()) }
29    }
30}
31
32impl Clone for Configuration {
33    fn clone(&self) -> Self {
34        let mut new = Self::new();
35        new.copy(self);
36        new
37    }
38}
39
40impl PartialEq for Configuration {
41    fn eq(&self, other: &Self) -> bool {
42        self.diff(other).0 == 0
43    }
44}
45impl Eq for Configuration {}
46
47impl fmt::Debug for Configuration {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        f.debug_struct("Configuration")
50            .field("mcc", &self.mcc())
51            .field("mnc", &self.mnc())
52            .field("lang", &self.language())
53            .field("country", &self.country())
54            .field("orientation", &self.orientation())
55            .field("touchscreen", &self.touchscreen())
56            .field("density", &self.density())
57            .field("keyboard", &self.keyboard())
58            .field("navigation", &self.navigation())
59            .field("keys_hidden", &self.keys_hidden())
60            .field("nav_hidden", &self.nav_hidden())
61            .field("sdk_version", &self.sdk_version())
62            .field("screen_size", &self.screen_size())
63            .field("screen_long", &self.screen_long())
64            .field("ui_mode_type", &self.ui_mode_type())
65            .field("ui_mode_night", &self.ui_mode_night())
66            .finish()
67    }
68}
69
70impl Configuration {
71    /// Construct a `Configuration` from a pointer.
72    ///
73    /// By calling this function, you assert that it is a valid pointer to a native
74    /// `AConfiguration`, and give ownership of it to the `Configuration` instance.
75    pub unsafe fn from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
76        Self { ptr }
77    }
78
79    /// Create a new `Configuration`, with the same contents as the `AConfiguration` referenced by
80    /// the pointer.
81    ///
82    /// This is useful if you have a pointer, but not ownership of it.
83    pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
84        let conf = Self::new();
85        ffi::AConfiguration_copy(conf.ptr.as_ptr(), ptr.as_ptr());
86        conf
87    }
88
89    /// The pointer to the native `AConfiguration`.  Keep in mind that the `Configuration` object
90    /// still has ownership, and will free it when dropped.
91    pub fn ptr(&self) -> NonNull<ffi::AConfiguration> {
92        self.ptr
93    }
94
95    /// Create a new `Configuration`, with none of the values set.
96    pub fn new() -> Self {
97        unsafe {
98            Self {
99                ptr: NonNull::new(ffi::AConfiguration_new()).unwrap(),
100            }
101        }
102    }
103
104    /// `dest.copy(&src)` copies the contents of `src` to `dest`
105    pub fn copy(&mut self, other: &Self) {
106        unsafe { ffi::AConfiguration_copy(self.ptr.as_ptr(), other.ptr.as_ptr()) }
107    }
108
109    /// Information about what fields differ between the two configurations
110    pub fn diff(&self, other: &Self) -> DiffResult {
111        unsafe {
112            DiffResult(ffi::AConfiguration_diff(self.ptr.as_ptr(), other.ptr.as_ptr()) as u32)
113        }
114    }
115
116    /// Returns false if anything in `self` conflicts with `requested`
117    pub fn matches(&self, requested: &Self) -> bool {
118        unsafe { ffi::AConfiguration_match(self.ptr.as_ptr(), requested.ptr.as_ptr()) != 0 }
119    }
120
121    /// Returns the country code. It will always be two letters.
122    pub fn country(&self) -> String {
123        let mut result = "  ".to_owned();
124        unsafe {
125            ffi::AConfiguration_getCountry(self.ptr.as_ptr(), result.as_mut_ptr() as *mut _);
126        }
127        result
128    }
129
130    /// Returns the screen density class.
131    pub fn density(&self) -> Density {
132        unsafe {
133            (ffi::AConfiguration_getDensity(self.ptr.as_ptr()) as u32)
134                .try_into()
135                .unwrap()
136        }
137    }
138
139    /// Returns the keyboard type.
140    pub fn keyboard(&self) -> Keyboard {
141        unsafe {
142            (ffi::AConfiguration_getKeyboard(self.ptr.as_ptr()) as u32)
143                .try_into()
144                .unwrap()
145        }
146    }
147
148    /// Returns keyboard visibility/availability.
149    pub fn keys_hidden(&self) -> KeysHidden {
150        unsafe {
151            (ffi::AConfiguration_getKeysHidden(self.ptr.as_ptr()) as u32)
152                .try_into()
153                .unwrap()
154        }
155    }
156
157    /// Returns the language, as a `String` of two characters, if a language is set
158    pub fn language(&self) -> Option<String> {
159        let mut chars = [0u8; 2];
160        unsafe {
161            ffi::AConfiguration_getLanguage(self.ptr.as_ptr(), chars[..].as_mut_ptr() as *mut _);
162        }
163        if chars[0] == 0 {
164            None
165        } else {
166            Some(std::str::from_utf8(&chars[..]).unwrap().to_owned())
167        }
168    }
169
170    /// Returns the layout direction
171    pub fn layout_direction(&self) -> LayoutDir {
172        unsafe {
173            (ffi::AConfiguration_getLayoutDirection(self.ptr.as_ptr()) as u32)
174                .try_into()
175                .unwrap()
176        }
177    }
178
179    /// Returns the mobile country code.
180    pub fn mcc(&self) -> i32 {
181        unsafe { ffi::AConfiguration_getMcc(self.ptr.as_ptr()) }
182    }
183
184    /// Returns the mobile network code, if one is defined
185    pub fn mnc(&self) -> Option<i32> {
186        unsafe {
187            match ffi::AConfiguration_getMnc(self.ptr.as_ptr()) {
188                0 => None,
189                x if x == ffi::ACONFIGURATION_MNC_ZERO as i32 => Some(0),
190                x => Some(x),
191            }
192        }
193    }
194
195    pub fn nav_hidden(&self) -> NavHidden {
196        unsafe {
197            (ffi::AConfiguration_getNavHidden(self.ptr.as_ptr()) as u32)
198                .try_into()
199                .unwrap()
200        }
201    }
202
203    pub fn navigation(&self) -> Navigation {
204        unsafe {
205            (ffi::AConfiguration_getNavigation(self.ptr.as_ptr()) as u32)
206                .try_into()
207                .unwrap()
208        }
209    }
210
211    pub fn orientation(&self) -> Orientation {
212        unsafe {
213            (ffi::AConfiguration_getOrientation(self.ptr.as_ptr()) as u32)
214                .try_into()
215                .unwrap()
216        }
217    }
218
219    pub fn screen_height_dp(&self) -> Option<i32> {
220        unsafe {
221            let height = ffi::AConfiguration_getScreenHeightDp(self.ptr.as_ptr());
222            if height == ffi::ACONFIGURATION_SCREEN_HEIGHT_DP_ANY as i32 {
223                None
224            } else {
225                Some(height)
226            }
227        }
228    }
229
230    pub fn screen_width_dp(&self) -> Option<i32> {
231        unsafe {
232            let width = ffi::AConfiguration_getScreenWidthDp(self.ptr.as_ptr());
233            if width == ffi::ACONFIGURATION_SCREEN_WIDTH_DP_ANY as i32 {
234                None
235            } else {
236                Some(width)
237            }
238        }
239    }
240
241    pub fn screen_long(&self) -> ScreenLong {
242        unsafe {
243            (ffi::AConfiguration_getScreenLong(self.ptr.as_ptr()) as u32)
244                .try_into()
245                .unwrap()
246        }
247    }
248
249    pub fn screen_round(&self) -> ScreenRound {
250        unsafe {
251            (ffi::AConfiguration_getScreenRound(self.ptr.as_ptr()) as u32)
252                .try_into()
253                .unwrap()
254        }
255    }
256
257    pub fn screen_size(&self) -> ScreenSize {
258        unsafe {
259            (ffi::AConfiguration_getScreenSize(self.ptr.as_ptr()) as u32)
260                .try_into()
261                .unwrap()
262        }
263    }
264
265    pub fn sdk_version(&self) -> i32 {
266        unsafe { ffi::AConfiguration_getSdkVersion(self.ptr.as_ptr()) }
267    }
268
269    pub fn smallest_screen_width_dp(&self) -> Option<i32> {
270        unsafe {
271            let width = ffi::AConfiguration_getSmallestScreenWidthDp(self.ptr.as_ptr());
272            if width == ffi::ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY as i32 {
273                None
274            } else {
275                Some(width)
276            }
277        }
278    }
279
280    pub fn touchscreen(&self) -> Touchscreen {
281        unsafe {
282            (ffi::AConfiguration_getTouchscreen(self.ptr.as_ptr()) as u32)
283                .try_into()
284                .unwrap()
285        }
286    }
287
288    pub fn ui_mode_night(&self) -> UiModeNight {
289        unsafe {
290            (ffi::AConfiguration_getUiModeNight(self.ptr.as_ptr()) as u32)
291                .try_into()
292                .unwrap()
293        }
294    }
295
296    pub fn ui_mode_type(&self) -> UiModeType {
297        unsafe {
298            (ffi::AConfiguration_getUiModeType(self.ptr.as_ptr()) as u32)
299                .try_into()
300                .unwrap()
301        }
302    }
303}
304
305/// A bitfield representing the differences between two `Configuration`s
306#[derive(Copy, Clone, Debug, PartialEq, Eq)]
307pub struct DiffResult(pub u32);
308
309impl DiffResult {
310    pub fn mcc(self) -> bool {
311        self.0 & ffi::ACONFIGURATION_MCC != 0
312    }
313    pub fn mnc(self) -> bool {
314        self.0 & ffi::ACONFIGURATION_MNC != 0
315    }
316    pub fn locale(self) -> bool {
317        self.0 & ffi::ACONFIGURATION_LOCALE != 0
318    }
319    pub fn touchscreen(self) -> bool {
320        self.0 & ffi::ACONFIGURATION_TOUCHSCREEN != 0
321    }
322    pub fn keyboard(self) -> bool {
323        self.0 & ffi::ACONFIGURATION_KEYBOARD != 0
324    }
325    pub fn keyboard_hidden(self) -> bool {
326        self.0 & ffi::ACONFIGURATION_KEYBOARD_HIDDEN != 0
327    }
328    pub fn navigation(self) -> bool {
329        self.0 & ffi::ACONFIGURATION_NAVIGATION != 0
330    }
331    pub fn orientation(self) -> bool {
332        self.0 & ffi::ACONFIGURATION_ORIENTATION != 0
333    }
334    pub fn density(self) -> bool {
335        self.0 & ffi::ACONFIGURATION_DENSITY != 0
336    }
337    pub fn screen_size(self) -> bool {
338        self.0 & ffi::ACONFIGURATION_SCREEN_SIZE != 0
339    }
340    pub fn version(self) -> bool {
341        self.0 & ffi::ACONFIGURATION_VERSION != 0
342    }
343    pub fn screen_layout(self) -> bool {
344        self.0 & ffi::ACONFIGURATION_SCREEN_LAYOUT != 0
345    }
346    pub fn ui_mode(self) -> bool {
347        self.0 & ffi::ACONFIGURATION_UI_MODE != 0
348    }
349    pub fn smallest_screen_size(self) -> bool {
350        self.0 & ffi::ACONFIGURATION_SMALLEST_SCREEN_SIZE != 0
351    }
352    pub fn layout_dir(self) -> bool {
353        self.0 & ffi::ACONFIGURATION_LAYOUTDIR != 0
354    }
355    pub fn screen_round(self) -> bool {
356        self.0 & ffi::ACONFIGURATION_SCREEN_ROUND != 0
357    }
358    pub fn color_mode(self) -> bool {
359        self.0 & ffi::ACONFIGURATION_COLOR_MODE != 0
360    }
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
364#[repr(u32)]
365pub enum Orientation {
366    Any = ffi::ACONFIGURATION_ORIENTATION_ANY,
367    Port = ffi::ACONFIGURATION_ORIENTATION_PORT,
368    Land = ffi::ACONFIGURATION_ORIENTATION_LAND,
369    Square = ffi::ACONFIGURATION_ORIENTATION_SQUARE,
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
373#[repr(u32)]
374pub enum Touchscreen {
375    Any = ffi::ACONFIGURATION_TOUCHSCREEN_ANY,
376    NoTouch = ffi::ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
377    Stylus = ffi::ACONFIGURATION_TOUCHSCREEN_STYLUS,
378    Finger = ffi::ACONFIGURATION_TOUCHSCREEN_FINGER,
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
382#[repr(u32)]
383pub enum Density {
384    Default = ffi::ACONFIGURATION_DENSITY_DEFAULT,
385    Low = ffi::ACONFIGURATION_DENSITY_LOW,
386    Medium = ffi::ACONFIGURATION_DENSITY_MEDIUM,
387    TV = ffi::ACONFIGURATION_DENSITY_TV,
388    High = ffi::ACONFIGURATION_DENSITY_HIGH,
389    XHigh = ffi::ACONFIGURATION_DENSITY_XHIGH,
390    XXHigh = ffi::ACONFIGURATION_DENSITY_XXHIGH,
391    XXXHigh = ffi::ACONFIGURATION_DENSITY_XXXHIGH,
392    Any = ffi::ACONFIGURATION_DENSITY_ANY,
393    None = ffi::ACONFIGURATION_DENSITY_NONE,
394}
395
396impl Density {
397    /// The DPI associated with the density class.
398    /// See [the Android screen density
399    /// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
400    ///
401    /// There are some `Density` values that have no associated DPI; these values return `None`.
402    pub fn approx_dpi(self) -> Option<u32> {
403        match self {
404            Self::Default => Some(160), // Or should it be None?
405            Self::Low => Some(120),
406            Self::Medium => Some(160),
407            Self::High => Some(240),
408            Self::XHigh => Some(320),
409            Self::XXHigh => Some(480),
410            Self::XXXHigh => Some(640),
411            Self::TV => Some(213),
412            Self::Any => None,
413            Self::None => None,
414        }
415    }
416
417    /// The Hi-DPI factor associated with the density class.  This is the factor by which an
418    /// image/resource should be scaled to match its size across devices.  The baseline is a 160dpi
419    /// screen (i.e., Hi-DPI factor = DPI / 160).
420    /// See [the Android screen density
421    /// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
422    ///
423    /// There are some `Density` values that have no associated DPI; these values return `None`.
424    pub fn approx_hidpi_factor(self) -> Option<f64> {
425        match self {
426            Self::Default => Some(1.), // Or should it be None?
427            Self::Low => Some(0.75),
428            Self::Medium => Some(1.),
429            Self::High => Some(1.5),
430            Self::XHigh => Some(2.),
431            Self::XXHigh => Some(3.),
432            Self::XXXHigh => Some(4.),
433            Self::TV => Some(4. / 3.),
434            Self::Any => None,
435            Self::None => None,
436        }
437    }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
441#[repr(u32)]
442pub enum Keyboard {
443    Any = ffi::ACONFIGURATION_KEYBOARD_ANY,
444    NoKeys = ffi::ACONFIGURATION_KEYBOARD_NOKEYS,
445    Qwerty = ffi::ACONFIGURATION_KEYBOARD_QWERTY,
446    TwelveKey = ffi::ACONFIGURATION_KEYBOARD_12KEY,
447}
448
449// FIXME is it a bitmask?
450// FIXME are they all bitmasks?
451#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
452#[repr(u32)]
453pub enum Navigation {
454    Any = ffi::ACONFIGURATION_NAVIGATION_ANY,
455    NoNav = ffi::ACONFIGURATION_NAVIGATION_NONAV,
456    DPad = ffi::ACONFIGURATION_NAVIGATION_DPAD,
457    Trackball = ffi::ACONFIGURATION_NAVIGATION_TRACKBALL,
458    Wheel = ffi::ACONFIGURATION_NAVIGATION_WHEEL,
459}
460
461#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
462#[repr(u32)]
463pub enum KeysHidden {
464    Any = ffi::ACONFIGURATION_KEYSHIDDEN_ANY,
465    No = ffi::ACONFIGURATION_KEYSHIDDEN_NO,
466    Yes = ffi::ACONFIGURATION_KEYSHIDDEN_YES,
467    Soft = ffi::ACONFIGURATION_KEYSHIDDEN_SOFT,
468}
469
470#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
471#[repr(u32)]
472pub enum NavHidden {
473    Any = ffi::ACONFIGURATION_NAVHIDDEN_ANY,
474    No = ffi::ACONFIGURATION_NAVHIDDEN_NO,
475    Yes = ffi::ACONFIGURATION_NAVHIDDEN_YES,
476}
477
478#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
479#[repr(u32)]
480pub enum ScreenSize {
481    Any = ffi::ACONFIGURATION_SCREENSIZE_ANY,
482    Small = ffi::ACONFIGURATION_SCREENSIZE_SMALL,
483    Normal = ffi::ACONFIGURATION_SCREENSIZE_NORMAL,
484    Large = ffi::ACONFIGURATION_SCREENSIZE_LARGE,
485    XLarge = ffi::ACONFIGURATION_SCREENSIZE_XLARGE,
486}
487
488#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
489#[repr(u32)]
490pub enum ScreenLong {
491    Any = ffi::ACONFIGURATION_SCREENLONG_ANY,
492    No = ffi::ACONFIGURATION_SCREENLONG_NO,
493    Yes = ffi::ACONFIGURATION_SCREENLONG_YES,
494}
495
496#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
497#[repr(u32)]
498pub enum ScreenRound {
499    Any = ffi::ACONFIGURATION_SCREENROUND_ANY,
500    No = ffi::ACONFIGURATION_SCREENROUND_NO,
501    Yes = ffi::ACONFIGURATION_SCREENROUND_YES,
502}
503
504#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
505#[repr(u32)]
506pub enum WideColorGamut {
507    Any = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_ANY,
508    No = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_NO,
509    Yes = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_YES,
510}
511
512#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
513#[repr(u32)]
514pub enum HDR {
515    Any = ffi::ACONFIGURATION_HDR_ANY,
516    No = ffi::ACONFIGURATION_HDR_NO,
517    Yes = ffi::ACONFIGURATION_HDR_YES,
518}
519
520#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
521#[repr(u32)]
522pub enum LayoutDir {
523    Any = ffi::ACONFIGURATION_LAYOUTDIR_ANY,
524    Ltr = ffi::ACONFIGURATION_LAYOUTDIR_LTR,
525    Rtl = ffi::ACONFIGURATION_LAYOUTDIR_RTL,
526}
527
528#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
529#[repr(u32)]
530pub enum UiModeType {
531    Any = ffi::ACONFIGURATION_UI_MODE_TYPE_ANY,
532    Normal = ffi::ACONFIGURATION_UI_MODE_TYPE_NORMAL,
533    Desk = ffi::ACONFIGURATION_UI_MODE_TYPE_DESK,
534    Car = ffi::ACONFIGURATION_UI_MODE_TYPE_CAR,
535    Television = ffi::ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
536    Applicance = ffi::ACONFIGURATION_UI_MODE_TYPE_APPLIANCE,
537    Watch = ffi::ACONFIGURATION_UI_MODE_TYPE_WATCH,
538    VrHeadset = ffi::ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET,
539}
540
541#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
542#[repr(u32)]
543pub enum UiModeNight {
544    Any = ffi::ACONFIGURATION_UI_MODE_NIGHT_ANY,
545    No = ffi::ACONFIGURATION_UI_MODE_NIGHT_NO,
546    Yes = ffi::ACONFIGURATION_UI_MODE_NIGHT_YES,
547}