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