@onekeyfe/react-native-device-utils 1.1.14 → 1.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/android/src/main/java/com/margelo/nitro/reactnativedeviceutils/ReactNativeDeviceUtils.kt
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
package com.margelo.nitro.reactnativedeviceutils
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.pm.PackageManager
|
|
4
6
|
import android.graphics.Color
|
|
5
7
|
import android.graphics.Rect
|
|
6
8
|
import android.os.Build
|
|
9
|
+
import android.preference.PreferenceManager
|
|
7
10
|
import androidx.core.content.ContextCompat
|
|
8
11
|
import androidx.core.util.Consumer
|
|
9
12
|
import androidx.window.layout.FoldingFeature
|
|
@@ -25,7 +28,191 @@ data class Listener(
|
|
|
25
28
|
|
|
26
29
|
@DoNotStrip
|
|
27
30
|
class ReactNativeDeviceUtils : HybridReactNativeDeviceUtilsSpec(), LifecycleEventListener {
|
|
28
|
-
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Foldable device model constants for various manufacturers.
|
|
34
|
+
* Reference: https://storage.googleapis.com/play_public/supported_devices.html
|
|
35
|
+
*/
|
|
36
|
+
companion object {
|
|
37
|
+
private const val PREF_KEY_FOLDABLE = "1k_fold"
|
|
38
|
+
|
|
39
|
+
// Xiaomi foldable models
|
|
40
|
+
private val XIAOMI_FOLDABLE_MODELS = setOf(
|
|
41
|
+
"M2011J18C", // Mi MIX FOLD
|
|
42
|
+
"22061218C", // MIX FOLD 2
|
|
43
|
+
"2308CPXD0C", // MIX FOLD 3
|
|
44
|
+
"24072PX77C", // MIX FOLD 4
|
|
45
|
+
"2405CPX3DC", // MIX FLIP
|
|
46
|
+
"2405CPX3DG" // MIX FLIP
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// Huawei foldable models
|
|
50
|
+
private val HUAWEI_FOLDABLE_MODELS = setOf(
|
|
51
|
+
"TAH-AN00", "TAH-AN00m", "TAH-N29m", // Mate X
|
|
52
|
+
"GRL-AL10", // Mate X3
|
|
53
|
+
"TET-AN50", // Mate Xs
|
|
54
|
+
"PAL-AL00", "PAL-LX9", // Mate Xs 2
|
|
55
|
+
"ICL-AL20", "ICL-AL10", // Pocket S
|
|
56
|
+
"BAL-AL00", "BAL-L49", "BAL-AL60", // Pocket 2
|
|
57
|
+
"PSD-AL00", // Mate X5
|
|
58
|
+
"LEM-AL00", // Mate X6
|
|
59
|
+
"ALT-AL10", "ALT-AL00", "ALT-L29", // Pocket
|
|
60
|
+
"TGW-AL00", "TGW-L29", // Mate X5
|
|
61
|
+
"TWH-AL10", // Mate X3
|
|
62
|
+
"DHF-AL00", "DHF-LX9", // Mate Xs 3
|
|
63
|
+
"RHA-AN00m" // Magic V series
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// Huawei foldable device codes
|
|
67
|
+
private val HUAWEI_FOLDABLE_DEVICES = setOf(
|
|
68
|
+
"HWTAH", "HWMRX", "HWTET", "HWPAL", "MateX"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// Vivo foldable models
|
|
72
|
+
private val VIVO_FOLDABLE_MODELS = setOf(
|
|
73
|
+
"V2337A", // X Fold3 Pro
|
|
74
|
+
"V2330", // X Fold3 Pro
|
|
75
|
+
"V2178A", // X Fold
|
|
76
|
+
"V2229A", // X Fold+
|
|
77
|
+
"V2266A", // vivo X Fold2
|
|
78
|
+
"V2303A", // vivo X Fold3
|
|
79
|
+
"V2256A", // X Flip
|
|
80
|
+
"V2436A", // X Fold5
|
|
81
|
+
"V2429" // X Fold5
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
// OPPO foldable models
|
|
85
|
+
private val OPPO_FOLDABLE_MODELS = setOf(
|
|
86
|
+
// Find N5
|
|
87
|
+
"PKH110", "CPH2671",
|
|
88
|
+
// Find N5 卫星通信版
|
|
89
|
+
"PKH120",
|
|
90
|
+
// Find N3
|
|
91
|
+
"PHN110", "CPH2499",
|
|
92
|
+
// Find N3 Flip
|
|
93
|
+
"PHT110", "CPH2519",
|
|
94
|
+
// Find N2 Flip
|
|
95
|
+
"CPH2437", "PGT110",
|
|
96
|
+
// Find N
|
|
97
|
+
"PEUM00"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// Samsung foldable models
|
|
101
|
+
private val SAMSUNG_FOLDABLE_MODELS = setOf(
|
|
102
|
+
// Galaxy Fold
|
|
103
|
+
"SCV44", "SM-F9000", "SM-F900F", "SM-F900U", "SM-F900U1", "SM-F900W",
|
|
104
|
+
// Galaxy Fold 5G
|
|
105
|
+
"SM-F907B", "SM-F907N",
|
|
106
|
+
// Galaxy Z Fold2 5G
|
|
107
|
+
"SM-F9160", "SM-F916B", "SM-F916N", "SM-F916Q", "SM-F916U", "SM-F916U1", "SM-F916W",
|
|
108
|
+
// Galaxy Z Fold3 5G
|
|
109
|
+
"SC-55B", "SCG11", "SM-F9260", "SM-F926B", "SM-F926N", "SM-F926U", "SM-F926U1", "SM-F926W",
|
|
110
|
+
// Galaxy Z Fold4
|
|
111
|
+
"SC-55C", "SCG16", "SM-F9360", "SM-F936B", "SM-F936N", "SM-F936U", "SM-F936U1", "SM-F936W",
|
|
112
|
+
// Galaxy Z Fold5
|
|
113
|
+
"SC-55D", "SCG22", "SM-F9460", "SM-F946B", "SM-F946N", "SM-F946Q", "SM-F946U", "SM-F946U1", "SM-F946W",
|
|
114
|
+
// Galaxy Z Fold6
|
|
115
|
+
"SC-55E", "SCG28", "SM-F9560", "SM-F956B", "SM-F956N", "SM-F956Q", "SM-F956U", "SM-F956U1", "SM-F956W",
|
|
116
|
+
// Galaxy Z Fold7
|
|
117
|
+
"SC-56F", "SCG34", "SM-F9660", "SM-F966B", "SM-F966N", "SM-F966Q", "SM-F966U", "SM-F966U1", "SM-F966W", "SM-F966Z",
|
|
118
|
+
// Galaxy Z Fold Special Edition
|
|
119
|
+
"SM-F958N",
|
|
120
|
+
// Galaxy Z TriFold
|
|
121
|
+
"SM-F9680", "SM-F968B", "SM-F968N", "SM-F968U1",
|
|
122
|
+
// Galaxy Z Flip
|
|
123
|
+
"SCV47", "SM-F7000", "SM-F700F", "SM-F700N", "SM-F700U", "SM-F700U1", "SM-F700W",
|
|
124
|
+
// Galaxy Z Flip 5G
|
|
125
|
+
"SCG04", "SM-F7070", "SM-F707B", "SM-F707N", "SM-F707U", "SM-F707U1", "SM-F707W",
|
|
126
|
+
// Galaxy Z Flip3 5G
|
|
127
|
+
"SC-54B", "SCG12", "SM-F7110", "SM-F711B", "SM-F711N", "SM-F711U", "SM-F711U1", "SM-F711W",
|
|
128
|
+
// Galaxy Z Flip4
|
|
129
|
+
"SC-54C", "SCG17", "SM-F7210", "SM-F721B", "SM-F721C", "SM-F721N", "SM-F721U", "SM-F721U1", "SM-F721W",
|
|
130
|
+
// Galaxy Z Flip5
|
|
131
|
+
"SC-54D", "SCG23", "SM-F7310", "SM-F731B", "SM-F731N", "SM-F731Q", "SM-F731U", "SM-F731U1", "SM-F731W",
|
|
132
|
+
// Galaxy Z Flip6
|
|
133
|
+
"SC-54E", "SCG29", "SM-F7410", "SM-F741B", "SM-F741N", "SM-F741Q", "SM-F741U", "SM-F741U1", "SM-F741W",
|
|
134
|
+
// Galaxy Z Flip7
|
|
135
|
+
"SC-55F", "SCG35", "SM-F7660", "SM-F766B", "SM-F766N", "SM-F766Q", "SM-F766U", "SM-F766U1", "SM-F766W", "SM-F766Z",
|
|
136
|
+
// Galaxy Z Flip7 FE
|
|
137
|
+
"SM-F7610", "SM-F761B", "SM-F761N", "SM-F761U", "SM-F761U1",
|
|
138
|
+
// 心系天下 W23/W25 Flip
|
|
139
|
+
"SM-W7023", "SM-W7025"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Samsung foldable model prefixes
|
|
143
|
+
private val SAMSUNG_FOLDABLE_PREFIXES = listOf(
|
|
144
|
+
"SM-F9", // Galaxy Z Fold series
|
|
145
|
+
"SM-F7", // Galaxy Z Flip series
|
|
146
|
+
"SM-W70" // 心系天下 W series Flip
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
// Google foldable models
|
|
150
|
+
private val GOOGLE_FOLDABLE_MODELS = setOf(
|
|
151
|
+
"Pixel Fold",
|
|
152
|
+
"Pixel 9 Pro Fold",
|
|
153
|
+
"Pixel 10 Pro Fold"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
// Motorola foldable models
|
|
157
|
+
private val MOTOROLA_FOLDABLE_MODELS = setOf(
|
|
158
|
+
// razr 40 series
|
|
159
|
+
"XT2323-3", // moto razr 40
|
|
160
|
+
"XT2321-2", // moto razr 40 Ultra
|
|
161
|
+
// razr 50 series
|
|
162
|
+
"XT2451-4", // moto razr 50
|
|
163
|
+
"XT2453-2", // motorola razr 50
|
|
164
|
+
// razr 60 series
|
|
165
|
+
"XT2551-3", // motorola razr 60 ultra
|
|
166
|
+
// razr 2022
|
|
167
|
+
"XT2251-1",
|
|
168
|
+
// razr Japan models
|
|
169
|
+
"M-51E", // motorola razr 50d
|
|
170
|
+
"M-51F" // motorola razr 60d
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
// ZTE/Nubia foldable models
|
|
174
|
+
private val ZTE_FOLDABLE_MODELS = setOf(
|
|
175
|
+
"NX732J", // nubia Flip 2 5G
|
|
176
|
+
"NX724J", // nubia Flip 5G
|
|
177
|
+
"Z9900S", // nubia Fold
|
|
178
|
+
"A502ZT", // nubia Fold (carrier)
|
|
179
|
+
"A304ZT", // Libero Flip
|
|
180
|
+
"Z8900CA" // nubia Flip 2 5G
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
// Tecno foldable models
|
|
184
|
+
private val TECNO_FOLDABLE_MODELS = setOf(
|
|
185
|
+
"TECNO-AD10", "TECNO AD10", // PHANTOM V Fold
|
|
186
|
+
"TECNO-AE10", "TECNO AE10", // PHANTOM V Fold2 5G
|
|
187
|
+
"TECNO-AD11", "TECNO AD11", // PHANTOM V Flip 5G
|
|
188
|
+
"TECNO-AE11", "TECNO AE11" // PHANTOM V Flip2 5G
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
// Infinix foldable models
|
|
192
|
+
private val INFINIX_FOLDABLE_MODELS = setOf(
|
|
193
|
+
"Infinix-X6962", "Infinix X6962" // ZERO Flip
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
// Royole foldable models
|
|
197
|
+
private val ROYOLE_FOLDABLE_MODELS = setOf(
|
|
198
|
+
"RY1205" // VERTU Ayxta Fold 3
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
// Honor foldable models
|
|
202
|
+
private val HONOR_FOLDABLE_MODELS = setOf(
|
|
203
|
+
"VER-N49", // HONOR Magic V2
|
|
204
|
+
"VER-N49DP", // PORSCHE DESIGN HONOR Magic V2 RSR
|
|
205
|
+
"FCP-N49", // HONOR Magic V3
|
|
206
|
+
"MBH-N49", // HONOR Magic V5
|
|
207
|
+
"FRI-NX9" // HONOR Magic Vs
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
// Vertu foldable models
|
|
211
|
+
private val VERTU_FOLDABLE_MODELS = setOf(
|
|
212
|
+
"VTL-202302" // METAFLIP
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
29
216
|
private var windowLayoutInfo: WindowLayoutInfo? = null
|
|
30
217
|
private var isSpanning = false
|
|
31
218
|
private var layoutInfoConsumer: Consumer<WindowLayoutInfo>? = null
|
|
@@ -53,37 +240,293 @@ class ReactNativeDeviceUtils : HybridReactNativeDeviceUtilsSpec(), LifecycleEven
|
|
|
53
240
|
// MARK: - Dual Screen Detection
|
|
54
241
|
|
|
55
242
|
override fun isDualScreenDevice(): Boolean {
|
|
243
|
+
// Check cached value from PreferenceManager first
|
|
244
|
+
val cached = getCachedFoldableStatus()
|
|
245
|
+
if (cached == true) {
|
|
246
|
+
isDualScreenDeviceDetected = true
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
|
|
56
250
|
if (isDualScreenDeviceDetected != null) {
|
|
57
251
|
return isDualScreenDeviceDetected!!
|
|
58
252
|
}
|
|
253
|
+
|
|
59
254
|
val activity = getCurrentActivity() ?: return false
|
|
60
255
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
61
|
-
|
|
256
|
+
val hasFolding = hasFoldingFeature(activity)
|
|
257
|
+
if (hasFolding) {
|
|
258
|
+
saveFoldableStatus(true)
|
|
259
|
+
}
|
|
260
|
+
isDualScreenDeviceDetected = hasFolding
|
|
62
261
|
return isDualScreenDeviceDetected!!
|
|
63
262
|
}
|
|
64
263
|
isDualScreenDeviceDetected = false
|
|
65
264
|
return isDualScreenDeviceDetected!!
|
|
66
265
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
266
|
+
// MARK: - Manufacturer-specific Foldable Detection
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Check if the device is a Xiaomi foldable
|
|
270
|
+
* Detection: Model list matching + system property check
|
|
271
|
+
*/
|
|
272
|
+
private fun isXiaomiFoldable(): Boolean {
|
|
273
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
274
|
+
if (manufacturer != "XIAOMI") return false
|
|
275
|
+
|
|
276
|
+
val model = Build.MODEL.uppercase()
|
|
277
|
+
if (XIAOMI_FOLDABLE_MODELS.contains(model)) return true
|
|
278
|
+
|
|
279
|
+
// Fallback: Check system property for foldable type
|
|
280
|
+
try {
|
|
281
|
+
val clazz = Class.forName("android.os.SystemProperties")
|
|
282
|
+
val method = clazz.getMethod("get", String::class.java)
|
|
283
|
+
val value = method.invoke(null, "persist.sys.muiltdisplay_type") as? String
|
|
284
|
+
if (value == "2") return true
|
|
285
|
+
} catch (e: Exception) {
|
|
286
|
+
// Ignore reflection errors
|
|
287
|
+
}
|
|
288
|
+
return false
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Check if the device is a Huawei foldable
|
|
293
|
+
* Detection: Model list + device code + system feature check
|
|
294
|
+
*/
|
|
295
|
+
private fun isHuaweiFoldable(): Boolean {
|
|
296
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
297
|
+
if (manufacturer != "HUAWEI") return false
|
|
298
|
+
|
|
299
|
+
val model = Build.MODEL.uppercase()
|
|
300
|
+
val device = Build.DEVICE.uppercase()
|
|
301
|
+
|
|
302
|
+
// Check model list
|
|
303
|
+
if (HUAWEI_FOLDABLE_MODELS.any { model.contains(it.uppercase()) }) return true
|
|
304
|
+
|
|
305
|
+
// Check device codes
|
|
306
|
+
if (HUAWEI_FOLDABLE_DEVICES.any { device.contains(it.uppercase()) }) return true
|
|
307
|
+
|
|
308
|
+
// Fallback: Check system feature for posture sensor
|
|
309
|
+
try {
|
|
310
|
+
val context = NitroModules.applicationContext
|
|
311
|
+
if (context != null) {
|
|
312
|
+
val pm = context.packageManager
|
|
313
|
+
if (pm.hasSystemFeature("com.huawei.hardware.sensor.posture")) {
|
|
314
|
+
return true
|
|
315
|
+
}
|
|
83
316
|
}
|
|
317
|
+
} catch (e: Exception) {
|
|
318
|
+
// Ignore
|
|
84
319
|
}
|
|
85
320
|
return false
|
|
86
321
|
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Check if the device is a Vivo foldable
|
|
325
|
+
* Detection: Model list + private API check
|
|
326
|
+
*/
|
|
327
|
+
private fun isVivoFoldable(): Boolean {
|
|
328
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
329
|
+
if (manufacturer != "VIVO") return false
|
|
330
|
+
|
|
331
|
+
val model = Build.MODEL.uppercase()
|
|
332
|
+
if (VIVO_FOLDABLE_MODELS.contains(model)) return true
|
|
333
|
+
|
|
334
|
+
// Fallback: Check using FtDeviceInfo API
|
|
335
|
+
try {
|
|
336
|
+
val clazz = Class.forName("android.util.FtDeviceInfo")
|
|
337
|
+
val method = clazz.getMethod("getDeviceType")
|
|
338
|
+
val deviceType = method.invoke(null) as? String
|
|
339
|
+
if (deviceType?.lowercase() == "foldable") return true
|
|
340
|
+
} catch (e: Exception) {
|
|
341
|
+
// Ignore reflection errors
|
|
342
|
+
}
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Check if the device is an OPPO foldable
|
|
348
|
+
* Detection: Model list + feature config check
|
|
349
|
+
*/
|
|
350
|
+
private fun isOppoFoldable(): Boolean {
|
|
351
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
352
|
+
if (manufacturer != "OPPO") return false
|
|
353
|
+
|
|
354
|
+
val model = Build.MODEL.uppercase()
|
|
355
|
+
if (OPPO_FOLDABLE_MODELS.contains(model)) return true
|
|
356
|
+
|
|
357
|
+
// Fallback: Check using OplusFeatureConfigManager
|
|
358
|
+
try {
|
|
359
|
+
val clazz = Class.forName("com.oplus.content.OplusFeatureConfigManager")
|
|
360
|
+
val getInstanceMethod = clazz.getMethod("getInstance")
|
|
361
|
+
val instance = getInstanceMethod.invoke(null)
|
|
362
|
+
val hasFeatureMethod = clazz.getMethod("hasFeature", String::class.java)
|
|
363
|
+
val hasFeature = hasFeatureMethod.invoke(instance, "oplus.hardware.type.fold") as? Boolean
|
|
364
|
+
if (hasFeature == true) return true
|
|
365
|
+
} catch (e: Exception) {
|
|
366
|
+
// Ignore reflection errors
|
|
367
|
+
}
|
|
368
|
+
return false
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Check if the device is a Samsung foldable
|
|
373
|
+
* Detection: Model prefix matching + model list
|
|
374
|
+
*/
|
|
375
|
+
private fun isSamsungFoldable(): Boolean {
|
|
376
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
377
|
+
if (manufacturer != "SAMSUNG") return false
|
|
378
|
+
|
|
379
|
+
val model = Build.MODEL.uppercase()
|
|
380
|
+
|
|
381
|
+
// Check model prefixes (SM-F9xxx for Fold, SM-F7xxx for Flip)
|
|
382
|
+
for (prefix in SAMSUNG_FOLDABLE_PREFIXES) {
|
|
383
|
+
if (model.startsWith(prefix.uppercase())) return true
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Check Japan carrier model list
|
|
387
|
+
if (SAMSUNG_FOLDABLE_MODELS.contains(model)) return true
|
|
388
|
+
|
|
389
|
+
return false
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Check if the device is a Google foldable
|
|
394
|
+
*/
|
|
395
|
+
private fun isGoogleFoldable(): Boolean {
|
|
396
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
397
|
+
if (manufacturer != "GOOGLE") return false
|
|
398
|
+
|
|
399
|
+
val model = Build.MODEL.uppercase()
|
|
400
|
+
return GOOGLE_FOLDABLE_MODELS.any { model.contains(it.uppercase()) }
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Check if the device is a Motorola foldable
|
|
405
|
+
*/
|
|
406
|
+
private fun isMotorolaFoldable(): Boolean {
|
|
407
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
408
|
+
if (manufacturer != "MOTOROLA") return false
|
|
409
|
+
|
|
410
|
+
val model = Build.MODEL.uppercase()
|
|
411
|
+
return MOTOROLA_FOLDABLE_MODELS.contains(model)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Check if the device is a ZTE/Nubia foldable
|
|
416
|
+
*/
|
|
417
|
+
private fun isZteFoldable(): Boolean {
|
|
418
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
419
|
+
if (manufacturer != "ZTE" && manufacturer != "NUBIA") return false
|
|
420
|
+
|
|
421
|
+
val model = Build.MODEL.uppercase()
|
|
422
|
+
return ZTE_FOLDABLE_MODELS.contains(model)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Check if the device is a Tecno foldable
|
|
427
|
+
*/
|
|
428
|
+
private fun isTecnoFoldable(): Boolean {
|
|
429
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
430
|
+
if (manufacturer != "TECNO") return false
|
|
431
|
+
|
|
432
|
+
val model = Build.MODEL.uppercase()
|
|
433
|
+
return TECNO_FOLDABLE_MODELS.any { model.contains(it.uppercase()) }
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Check if the device is an Infinix foldable
|
|
438
|
+
*/
|
|
439
|
+
private fun isInfinixFoldable(): Boolean {
|
|
440
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
441
|
+
if (manufacturer != "INFINIX") return false
|
|
442
|
+
|
|
443
|
+
val model = Build.MODEL.uppercase()
|
|
444
|
+
return INFINIX_FOLDABLE_MODELS.any { model.contains(it.uppercase()) }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Check if the device is a Royole foldable
|
|
449
|
+
*/
|
|
450
|
+
private fun isRoyoleFoldable(): Boolean {
|
|
451
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
452
|
+
if (manufacturer != "ROYOLE") return false
|
|
453
|
+
|
|
454
|
+
val model = Build.MODEL.uppercase()
|
|
455
|
+
return ROYOLE_FOLDABLE_MODELS.contains(model)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Check if the device is an Honor foldable
|
|
460
|
+
*/
|
|
461
|
+
private fun isHonorFoldable(): Boolean {
|
|
462
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
463
|
+
if (manufacturer != "HONOR") return false
|
|
464
|
+
|
|
465
|
+
val model = Build.MODEL.uppercase()
|
|
466
|
+
return HONOR_FOLDABLE_MODELS.contains(model)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Check if the device is a Vertu foldable
|
|
471
|
+
*/
|
|
472
|
+
private fun isVertuFoldable(): Boolean {
|
|
473
|
+
val manufacturer = Build.MANUFACTURER.uppercase()
|
|
474
|
+
if (manufacturer != "VERTU") return false
|
|
475
|
+
|
|
476
|
+
val model = Build.MODEL.uppercase()
|
|
477
|
+
return VERTU_FOLDABLE_MODELS.contains(model)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Get cached foldable status from PreferenceManager
|
|
482
|
+
*/
|
|
483
|
+
private fun getCachedFoldableStatus(): Boolean? {
|
|
484
|
+
try {
|
|
485
|
+
val context = NitroModules.applicationContext ?: return null
|
|
486
|
+
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
487
|
+
if (!prefs.contains(PREF_KEY_FOLDABLE)) return null
|
|
488
|
+
return prefs.getBoolean(PREF_KEY_FOLDABLE, false)
|
|
489
|
+
} catch (e: Exception) {
|
|
490
|
+
return null
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Save foldable status to PreferenceManager
|
|
496
|
+
*/
|
|
497
|
+
private fun saveFoldableStatus(isFoldable: Boolean) {
|
|
498
|
+
try {
|
|
499
|
+
val context = NitroModules.applicationContext ?: return
|
|
500
|
+
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
501
|
+
prefs.edit().putBoolean(PREF_KEY_FOLDABLE, isFoldable).apply()
|
|
502
|
+
} catch (e: Exception) {
|
|
503
|
+
// Ignore save errors
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Detect if device is foldable by checking manufacturer-specific methods
|
|
509
|
+
* Results are cached in PreferenceManager with key "1k_fold"
|
|
510
|
+
*/
|
|
511
|
+
private fun isFoldableDeviceByName(): Boolean {
|
|
512
|
+
// Check each manufacturer
|
|
513
|
+
val isFoldable = isXiaomiFoldable() ||
|
|
514
|
+
isHuaweiFoldable() ||
|
|
515
|
+
isVivoFoldable() ||
|
|
516
|
+
isOppoFoldable() ||
|
|
517
|
+
isSamsungFoldable() ||
|
|
518
|
+
isGoogleFoldable() ||
|
|
519
|
+
isMotorolaFoldable() ||
|
|
520
|
+
isZteFoldable() ||
|
|
521
|
+
isTecnoFoldable() ||
|
|
522
|
+
isInfinixFoldable() ||
|
|
523
|
+
isRoyoleFoldable() ||
|
|
524
|
+
isHonorFoldable() ||
|
|
525
|
+
isVertuFoldable()
|
|
526
|
+
|
|
527
|
+
// Cache the result
|
|
528
|
+
return isFoldable
|
|
529
|
+
}
|
|
87
530
|
|
|
88
531
|
private fun hasFoldingFeature(activity: Activity): Boolean {
|
|
89
532
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
|
@@ -229,7 +672,8 @@ class ReactNativeDeviceUtils : HybridReactNativeDeviceUtilsSpec(), LifecycleEven
|
|
|
229
672
|
}
|
|
230
673
|
|
|
231
674
|
fun callSpanningChangedListeners(isSpanning: Boolean) {
|
|
232
|
-
|
|
675
|
+
// Create a snapshot to avoid ConcurrentModificationException when listeners modify the list during callbacks
|
|
676
|
+
for (listener in spanningChangedListeners.toList()) {
|
|
233
677
|
listener.callback(isSpanning)
|
|
234
678
|
}
|
|
235
679
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-device-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "react-native-device-utils",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"react": "19.2.0",
|
|
83
83
|
"react-native": "0.83.0",
|
|
84
84
|
"react-native-builder-bob": "^0.40.13",
|
|
85
|
-
"react-native-nitro-modules": "0.
|
|
85
|
+
"react-native-nitro-modules": "0.33.2",
|
|
86
86
|
"release-it": "^19.0.4",
|
|
87
87
|
"turbo": "^2.5.6",
|
|
88
88
|
"typescript": "^5.9.2"
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"peerDependencies": {
|
|
91
91
|
"react": "*",
|
|
92
92
|
"react-native": "*",
|
|
93
|
-
"react-native-nitro-modules": "
|
|
93
|
+
"react-native-nitro-modules": "0.33.2"
|
|
94
94
|
},
|
|
95
95
|
"react-native-builder-bob": {
|
|
96
96
|
"source": "src",
|