@@ -44,11 +44,10 @@ import kotlinx.coroutines.suspendCancellableCoroutine
4444import java.net.Inet4Address
4545import java.net.NetworkInterface
4646import java.security.KeyStore
47+ import java.util.Locale
4748import kotlin.coroutines.resume
4849
49- /* *
50- * Build information cache to avoid repeated Build.* lookups
51- */
50+ /* * Build information cache to avoid repeated Build.* lookups */
5251private data class BuildInfoCache (
5352 val serialNumber : String ,
5453 val androidId : String ,
@@ -115,16 +114,15 @@ class DeviceInfo : HybridDeviceInfoSpec() {
115114 }
116115
117116 /* *
118- * Get serial number with permission check
119- * Requires READ_PHONE_STATE permission on Android 8.0+
117+ * Get serial number with permission check Requires READ_PHONE_STATE permission on Android 8.0+
120118 */
121119 @SuppressLint(" MissingPermission" )
122120 private fun getSerialNumberInternal (): String {
123121 return try {
124122 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
125123 // Android 8.0+ requires READ_PHONE_STATE permission
126- if (context.checkSelfPermission(android.Manifest .permission.READ_PHONE_STATE )
127- == PackageManager .PERMISSION_GRANTED
124+ if (context.checkSelfPermission(android.Manifest .permission.READ_PHONE_STATE ) ==
125+ PackageManager .PERMISSION_GRANTED
128126 ) {
129127 Build .getSerial()
130128 } else {
@@ -147,7 +145,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
147145 Settings .Secure .getString(
148146 context.contentResolver,
149147 Settings .Secure .ANDROID_ID ,
150- ) ? : " unknown" ,
148+ )
149+ ? : " unknown" ,
151150 securityPatch = Build .VERSION .SECURITY_PATCH ,
152151 bootloader = Build .BOOTLOADER ,
153152 codename = Build .VERSION .CODENAME ,
@@ -168,9 +167,7 @@ class DeviceInfo : HybridDeviceInfoSpec() {
168167
169168 /* * Cached system features list */
170169 private val systemFeatures: List <String > by lazy {
171- context.packageManager.systemAvailableFeatures
172- .mapNotNull { it.name }
173- .sorted()
170+ context.packageManager.systemAvailableFeatures.mapNotNull { it.name }.sorted()
174171 }
175172
176173 /* * Cached supported media types */
@@ -179,9 +176,7 @@ class DeviceInfo : HybridDeviceInfoSpec() {
179176 val codecList = MediaCodecList (MediaCodecList .ALL_CODECS )
180177 val types = mutableSetOf<String >()
181178 codecList.codecInfos?.forEach { codecInfo ->
182- codecInfo.supportedTypes.forEach { type ->
183- types.add(type)
184- }
179+ codecInfo.supportedTypes.forEach { type -> types.add(type) }
185180 }
186181
187182 types.sorted()
@@ -259,7 +254,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
259254 get() = false
260255
261256 /* *
262- * Check if device has Dynamic Island Android devices don't have Dynamic Island (iOS-only feature)
257+ * Check if device has Dynamic Island Android devices don't have Dynamic Island (iOS-only
258+ * feature)
263259 */
264260 override val hasDynamicIsland: Boolean
265261 get() = false
@@ -329,7 +325,6 @@ class DeviceInfo : HybridDeviceInfoSpec() {
329325 when {
330326 batteryManager.getIntProperty(BatteryManager .BATTERY_PROPERTY_STATUS ) ==
331327 BatteryManager .BATTERY_STATUS_FULL -> BatteryState .FULL
332-
333328 isCharging -> BatteryState .CHARGING
334329 else -> BatteryState .UNPLUGGED
335330 }
@@ -502,7 +497,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
502497 override val hasGms: Boolean
503498 get() {
504499 return try {
505- val result = GoogleApiAvailability .getInstance().isGooglePlayServicesAvailable(context)
500+ val result =
501+ GoogleApiAvailability .getInstance().isGooglePlayServicesAvailable(context)
506502 result == ConnectionResult .SUCCESS
507503 } catch (e: Exception ) {
508504 Log .w(NAME , " GMS not available or GMS library not found" , e)
@@ -600,7 +596,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
600596 get() {
601597 return try {
602598 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
603- context.packageManager.getInstallSourceInfo(context.packageName).installingPackageName
599+ context.packageManager.getInstallSourceInfo(context.packageName)
600+ .installingPackageName
604601 ? : " unknown"
605602 } else {
606603 @Suppress(" DEPRECATION" )
@@ -644,7 +641,6 @@ class DeviceInfo : HybridDeviceInfoSpec() {
644641 continuation.resume(" unknown" )
645642 }
646643 }
647-
648644 else -> {
649645 referrerClient.endConnection()
650646 continuation.resume(" unknown" )
@@ -787,7 +783,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
787783 val locationManager =
788784 context.getSystemService(Context .LOCATION_SERVICE ) as LocationManager
789785
790- locationManager.allProviders
786+ locationManager
787+ .allProviders
791788 .filter { locationManager.isProviderEnabled(it) }
792789 .toTypedArray()
793790 } catch (e: Exception ) {
@@ -835,9 +832,7 @@ class DeviceInfo : HybridDeviceInfoSpec() {
835832
836833 /* * Get device token (iOS-specific, throws error on Android) */
837834 override fun getDeviceToken (): Promise <String > {
838- return Promise .async {
839- throw Exception (" getDeviceToken() is only available on iOS" )
840- }
835+ return Promise .async { throw Exception (" getDeviceToken() is only available on iOS" ) }
841836 }
842837
843838 /* * Get IP address with 5-second cache */
@@ -938,8 +933,8 @@ class DeviceInfo : HybridDeviceInfoSpec() {
938933 override val isLiquidGlassAvailable: Boolean = false
939934
940935 /* *
941- * Check if hardware-backed key storage is available
942- * Android KeyStore is always hardware-backed on API 23+ (TEE or StrongBox)
936+ * Check if hardware-backed key storage is available Android KeyStore is always hardware-backed
937+ * on API 23+ (TEE or StrongBox)
943938 */
944939 override val isHardwareKeyStoreAvailable: Boolean
945940 get() {
@@ -1007,6 +1002,32 @@ class DeviceInfo : HybridDeviceInfoSpec() {
10071002 }
10081003 }
10091004
1005+ // MARK: - Localization & Navigation
1006+
1007+ /* * Get device system language in BCP 47 format */
1008+ override val systemLanguage: String
1009+ get() = Locale .getDefault().toLanguageTag()
1010+
1011+ /* * Get Android navigation mode Values: 0 = 3-button, 1 = 2-button, 2 = gesture */
1012+ override val navigationMode: NavigationMode
1013+ get() {
1014+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .Q ) {
1015+ // Pre-Android 10 only has 3-button navigation
1016+ return NavigationMode .BUTTONS
1017+ }
1018+ return try {
1019+ when (Settings .Secure .getInt(context.contentResolver, " navigation_mode" , 0 )) {
1020+ 0 -> NavigationMode .BUTTONS // 3-button navigation
1021+ 1 -> NavigationMode .TWOBUTTONS // 2-button navigation
1022+ 2 -> NavigationMode .GESTURE // Gesture navigation
1023+ else -> NavigationMode .UNKNOWN
1024+ }
1025+ } catch (e: Exception ) {
1026+ Log .w(NAME , " Failed to get navigation mode" , e)
1027+ NavigationMode .UNKNOWN
1028+ }
1029+ }
1030+
10101031 companion object {
10111032 const val NAME = " NitroDeviceInfo"
10121033 }
0 commit comments