rn-system-bar 3.1.0 → 3.1.1
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/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/systembar/SystemBarModule.kt +105 -286
- package/index.ts +1 -8
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -5
- package/lib/src/SystemBar.d.ts +3 -56
- package/lib/src/SystemBar.js +24 -140
- package/lib/src/types.d.ts +0 -33
- package/lib/src/types.js +1 -1
- package/lib/src/useSystemBar.d.ts +1 -31
- package/lib/src/useSystemBar.js +6 -98
- package/package.json +3 -2
- package/rn-system-bar.podspec +1 -1
- package/src/SystemBar.ts +24 -154
- package/src/types.ts +1 -66
- package/src/useSystemBar.ts +10 -111
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
package="com.systembar">
|
|
3
|
+
|
|
4
|
+
<!-- Required for writing system brightness -->
|
|
5
|
+
<uses-permission android:name="android.permission.WRITE_SETTINGS"
|
|
6
|
+
tools:ignore="ProtectedPermissions"
|
|
7
|
+
xmlns:tools="http://schemas.android.com/tools"/>
|
|
8
|
+
|
|
9
|
+
</manifest>
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · SystemBarModule.kt
|
|
3
|
-
// New: Network, Battery, Haptics, Screencast, FontScale
|
|
2
|
+
// rn-system-bar · SystemBarModule.kt
|
|
4
3
|
// ─────────────────────────────────────────────
|
|
5
4
|
|
|
6
5
|
package com.systembar
|
|
7
6
|
|
|
8
7
|
import android.app.Activity
|
|
9
|
-
import android.content.BroadcastReceiver
|
|
10
8
|
import android.content.Context
|
|
11
9
|
import android.content.Intent
|
|
12
|
-
import android.content.IntentFilter
|
|
13
10
|
import android.content.pm.ActivityInfo
|
|
14
11
|
import android.hardware.display.DisplayManager
|
|
15
12
|
import android.media.AudioManager
|
|
16
|
-
import android.net.ConnectivityManager
|
|
17
|
-
import android.net.Network
|
|
18
|
-
import android.net.NetworkCapabilities
|
|
19
|
-
import android.net.NetworkRequest
|
|
20
|
-
import android.os.BatteryManager
|
|
21
13
|
import android.os.Build
|
|
22
|
-
import android.os.VibrationEffect
|
|
23
|
-
import android.os.Vibrator
|
|
24
|
-
import android.os.VibratorManager
|
|
25
14
|
import android.provider.Settings
|
|
26
|
-
import android.view.Display
|
|
27
15
|
import android.view.View
|
|
28
16
|
import android.view.WindowInsets
|
|
29
17
|
import android.view.WindowInsetsController
|
|
@@ -40,23 +28,11 @@ class SystemBarModule(
|
|
|
40
28
|
|
|
41
29
|
private fun activity(): Activity? = reactContext.currentActivity
|
|
42
30
|
|
|
43
|
-
private fun
|
|
44
|
-
reactContext
|
|
45
|
-
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
46
|
-
.emit(event, data)
|
|
47
|
-
}
|
|
31
|
+
private fun audioManager() =
|
|
32
|
+
reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
48
33
|
|
|
49
|
-
private fun
|
|
50
|
-
|
|
51
|
-
private fun batteryManager() = reactContext.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
|
|
52
|
-
private fun displayManager() = reactContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
53
|
-
|
|
54
|
-
@Suppress("DEPRECATION")
|
|
55
|
-
private fun vibrator(): Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
56
|
-
(reactContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager).defaultVibrator
|
|
57
|
-
} else {
|
|
58
|
-
reactContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
|
59
|
-
}
|
|
34
|
+
private fun displayManager() =
|
|
35
|
+
reactContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
60
36
|
|
|
61
37
|
private fun streamType(stream: String): Int = when (stream) {
|
|
62
38
|
"ring" -> AudioManager.STREAM_RING
|
|
@@ -66,29 +42,84 @@ class SystemBarModule(
|
|
|
66
42
|
else -> AudioManager.STREAM_MUSIC
|
|
67
43
|
}
|
|
68
44
|
|
|
45
|
+
// ═══════════════════════════════════════════════
|
|
46
|
+
// NAVIGATION BAR COLOR
|
|
47
|
+
// Direct Window API — edge-to-edge safe.
|
|
48
|
+
// ═══════════════════════════════════════════════
|
|
49
|
+
|
|
50
|
+
@ReactMethod
|
|
51
|
+
fun setNavigationBarColor(color: String) {
|
|
52
|
+
activity()?.runOnUiThread {
|
|
53
|
+
try {
|
|
54
|
+
activity()!!.window.navigationBarColor =
|
|
55
|
+
android.graphics.Color.parseColor(color)
|
|
56
|
+
} catch (_: Exception) {}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
69
60
|
// ═══════════════════════════════════════════════
|
|
70
61
|
// BRIGHTNESS
|
|
62
|
+
//
|
|
63
|
+
// setBrightness — TWO things at once:
|
|
64
|
+
// 1. window.attributes.screenBrightness → current screen (instant)
|
|
65
|
+
// 2. Settings.System.SCREEN_BRIGHTNESS → system brightness persisted
|
|
66
|
+
//
|
|
67
|
+
// Requires WRITE_SETTINGS permission.
|
|
68
|
+
// If permission not granted → request it, still apply window brightness.
|
|
69
|
+
//
|
|
70
|
+
// getBrightness — reads SYSTEM brightness (0–255 → 0.0–1.0)
|
|
71
|
+
// so the slider always shows the true system value.
|
|
71
72
|
// ═══════════════════════════════════════════════
|
|
72
73
|
|
|
73
74
|
@ReactMethod
|
|
74
75
|
fun setBrightness(level: Float) {
|
|
76
|
+
val clamped = level.coerceIn(0.01f, 1f)
|
|
77
|
+
|
|
78
|
+
// 1️⃣ Window brightness — instant, no permission needed
|
|
75
79
|
activity()?.runOnUiThread {
|
|
76
80
|
val lp = activity()!!.window.attributes
|
|
77
|
-
lp.screenBrightness =
|
|
81
|
+
lp.screenBrightness = clamped
|
|
78
82
|
activity()!!.window.attributes = lp
|
|
79
83
|
}
|
|
84
|
+
|
|
85
|
+
// 2️⃣ System brightness — persisted, needs WRITE_SETTINGS
|
|
86
|
+
val sysValue = (clamped * 255).toInt().coerceIn(1, 255)
|
|
87
|
+
|
|
88
|
+
if (Settings.System.canWrite(reactContext)) {
|
|
89
|
+
// Permission granted — write system brightness
|
|
90
|
+
Settings.System.putInt(
|
|
91
|
+
reactContext.contentResolver,
|
|
92
|
+
Settings.System.SCREEN_BRIGHTNESS,
|
|
93
|
+
sysValue
|
|
94
|
+
)
|
|
95
|
+
// Also disable auto-brightness so manual value sticks
|
|
96
|
+
Settings.System.putInt(
|
|
97
|
+
reactContext.contentResolver,
|
|
98
|
+
Settings.System.SCREEN_BRIGHTNESS_MODE,
|
|
99
|
+
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL
|
|
100
|
+
)
|
|
101
|
+
} else {
|
|
102
|
+
// No permission — open system settings so user can grant it once
|
|
103
|
+
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).apply {
|
|
104
|
+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
105
|
+
}
|
|
106
|
+
try { reactContext.startActivity(intent) } catch (_: Exception) {}
|
|
107
|
+
}
|
|
80
108
|
}
|
|
81
109
|
|
|
82
110
|
@ReactMethod
|
|
83
111
|
fun getBrightness(promise: Promise) {
|
|
84
112
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
// Always read from system setting so value matches device brightness bar
|
|
114
|
+
val sys = Settings.System.getInt(
|
|
115
|
+
reactContext.contentResolver,
|
|
116
|
+
Settings.System.SCREEN_BRIGHTNESS,
|
|
117
|
+
128
|
|
118
|
+
)
|
|
90
119
|
promise.resolve(sys / 255.0)
|
|
91
|
-
} catch (e: Exception) {
|
|
120
|
+
} catch (e: Exception) {
|
|
121
|
+
promise.reject("BRIGHTNESS_ERROR", e.message, e)
|
|
122
|
+
}
|
|
92
123
|
}
|
|
93
124
|
|
|
94
125
|
// ═══════════════════════════════════════════════
|
|
@@ -97,13 +128,16 @@ class SystemBarModule(
|
|
|
97
128
|
|
|
98
129
|
private var suppressVolumeHUD = false
|
|
99
130
|
|
|
100
|
-
@ReactMethod
|
|
131
|
+
@ReactMethod
|
|
132
|
+
fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
|
|
101
133
|
|
|
102
134
|
@ReactMethod
|
|
103
135
|
fun setVolume(level: Float, stream: String) {
|
|
104
136
|
try {
|
|
105
|
-
val am
|
|
106
|
-
val
|
|
137
|
+
val am = audioManager()
|
|
138
|
+
val type = streamType(stream)
|
|
139
|
+
val max = am.getStreamMaxVolume(type)
|
|
140
|
+
val vol = (level.coerceIn(0f, 1f) * max).toInt()
|
|
107
141
|
am.setStreamVolume(type, vol, if (suppressVolumeHUD) 0 else AudioManager.FLAG_SHOW_UI)
|
|
108
142
|
} catch (_: Exception) {}
|
|
109
143
|
}
|
|
@@ -111,10 +145,13 @@ class SystemBarModule(
|
|
|
111
145
|
@ReactMethod
|
|
112
146
|
fun getVolume(stream: String, promise: Promise) {
|
|
113
147
|
try {
|
|
114
|
-
val am
|
|
115
|
-
val
|
|
148
|
+
val am = audioManager()
|
|
149
|
+
val type = streamType(stream)
|
|
150
|
+
val max = am.getStreamMaxVolume(type)
|
|
116
151
|
promise.resolve(if (max > 0) am.getStreamVolume(type).toDouble() / max else 0.0)
|
|
117
|
-
} catch (e: Exception) {
|
|
152
|
+
} catch (e: Exception) {
|
|
153
|
+
promise.reject("VOLUME_ERROR", e.message, e)
|
|
154
|
+
}
|
|
118
155
|
}
|
|
119
156
|
|
|
120
157
|
// ═══════════════════════════════════════════════
|
|
@@ -143,14 +180,26 @@ class SystemBarModule(
|
|
|
143
180
|
} else {
|
|
144
181
|
@Suppress("DEPRECATION")
|
|
145
182
|
activity()!!.window.decorView.systemUiVisibility = if (enable) {
|
|
146
|
-
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
|
147
|
-
View.
|
|
183
|
+
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
|
184
|
+
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
|
185
|
+
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
|
186
|
+
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
|
148
187
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
149
|
-
} else {
|
|
188
|
+
} else {
|
|
189
|
+
View.SYSTEM_UI_FLAG_VISIBLE
|
|
190
|
+
}
|
|
150
191
|
}
|
|
151
192
|
}
|
|
152
193
|
}
|
|
153
194
|
|
|
195
|
+
@ReactMethod
|
|
196
|
+
fun setSecureScreen(enable: Boolean) {
|
|
197
|
+
activity()?.runOnUiThread {
|
|
198
|
+
if (enable) activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
199
|
+
else activity()!!.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
154
203
|
// ═══════════════════════════════════════════════
|
|
155
204
|
// ORIENTATION
|
|
156
205
|
// ═══════════════════════════════════════════════
|
|
@@ -167,199 +216,19 @@ class SystemBarModule(
|
|
|
167
216
|
}
|
|
168
217
|
|
|
169
218
|
// ═══════════════════════════════════════════════
|
|
170
|
-
//
|
|
219
|
+
// SCREENCAST
|
|
171
220
|
// ═══════════════════════════════════════════════
|
|
172
221
|
|
|
173
|
-
private var
|
|
174
|
-
|
|
175
|
-
private fun buildNetworkMap(): WritableMap {
|
|
176
|
-
val cm = connectivityManager()
|
|
177
|
-
val map = Arguments.createMap()
|
|
178
|
-
|
|
179
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
180
|
-
val net = cm.activeNetwork
|
|
181
|
-
val caps = cm.getNetworkCapabilities(net)
|
|
182
|
-
val connected = caps != null
|
|
183
|
-
|
|
184
|
-
map.putBoolean("isConnected", connected)
|
|
185
|
-
|
|
186
|
-
val type = when {
|
|
187
|
-
caps == null -> "none"
|
|
188
|
-
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "wifi"
|
|
189
|
-
caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "cellular"
|
|
190
|
-
caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "ethernet"
|
|
191
|
-
else -> "unknown"
|
|
192
|
-
}
|
|
193
|
-
map.putString("type", type)
|
|
194
|
-
|
|
195
|
-
// Cellular generation
|
|
196
|
-
val cellGen: String? = if (type == "cellular") {
|
|
197
|
-
val am = audioManager() // not audio — reuse context trick; use TelephonyManager instead
|
|
198
|
-
null // placeholder; TelephonyManager requires READ_PHONE_STATE permission
|
|
199
|
-
} else null
|
|
200
|
-
if (cellGen != null) map.putString("cellularGeneration", cellGen)
|
|
201
|
-
else map.putNull("cellularGeneration")
|
|
202
|
-
|
|
203
|
-
// SSID (requires ACCESS_FINE_LOCATION on Android 10+)
|
|
204
|
-
map.putNull("ssid")
|
|
205
|
-
|
|
206
|
-
} else {
|
|
207
|
-
@Suppress("DEPRECATION")
|
|
208
|
-
val info = cm.activeNetworkInfo
|
|
209
|
-
map.putBoolean("isConnected", info?.isConnected == true)
|
|
210
|
-
map.putString("type", when (info?.type) {
|
|
211
|
-
ConnectivityManager.TYPE_WIFI -> "wifi"
|
|
212
|
-
ConnectivityManager.TYPE_MOBILE -> "cellular"
|
|
213
|
-
ConnectivityManager.TYPE_ETHERNET -> "ethernet"
|
|
214
|
-
else -> if (info?.isConnected == true) "unknown" else "none"
|
|
215
|
-
})
|
|
216
|
-
map.putNull("cellularGeneration")
|
|
217
|
-
map.putNull("ssid")
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Airplane mode
|
|
221
|
-
val airplaneMode = Settings.Global.getInt(
|
|
222
|
-
reactContext.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0
|
|
223
|
-
) != 0
|
|
224
|
-
map.putBoolean("isAirplaneMode", airplaneMode)
|
|
225
|
-
|
|
226
|
-
return map
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
@ReactMethod
|
|
230
|
-
fun getNetworkInfo(promise: Promise) {
|
|
231
|
-
try { promise.resolve(buildNetworkMap()) }
|
|
232
|
-
catch (e: Exception) { promise.reject("NETWORK_ERROR", e.message, e) }
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
@ReactMethod
|
|
236
|
-
fun startNetworkListener() {
|
|
237
|
-
if (networkCallback != null) return
|
|
238
|
-
val cb = object : ConnectivityManager.NetworkCallback() {
|
|
239
|
-
override fun onAvailable(network: Network) { emit("SystemBar_NetworkChange", buildNetworkMap()) }
|
|
240
|
-
override fun onLost(network: Network) { emit("SystemBar_NetworkChange", buildNetworkMap()) }
|
|
241
|
-
override fun onCapabilitiesChanged(n: Network, c: NetworkCapabilities) {
|
|
242
|
-
emit("SystemBar_NetworkChange", buildNetworkMap())
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
networkCallback = cb
|
|
246
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
247
|
-
connectivityManager().registerDefaultNetworkCallback(cb)
|
|
248
|
-
} else {
|
|
249
|
-
connectivityManager().registerNetworkCallback(NetworkRequest.Builder().build(), cb)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
@ReactMethod
|
|
254
|
-
fun stopNetworkListener() {
|
|
255
|
-
networkCallback?.let { connectivityManager().unregisterNetworkCallback(it) }
|
|
256
|
-
networkCallback = null
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// ═══════════════════════════════════════════════
|
|
260
|
-
// 🆕 BATTERY
|
|
261
|
-
// ═══════════════════════════════════════════════
|
|
262
|
-
|
|
263
|
-
private var batteryReceiver: BroadcastReceiver? = null
|
|
264
|
-
|
|
265
|
-
private fun buildBatteryMap(): WritableMap {
|
|
266
|
-
val bm = batteryManager()
|
|
267
|
-
val map = Arguments.createMap()
|
|
268
|
-
val level = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
|
|
269
|
-
val status = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS)
|
|
270
|
-
|
|
271
|
-
val state = when (status) {
|
|
272
|
-
BatteryManager.BATTERY_STATUS_CHARGING -> "charging"
|
|
273
|
-
BatteryManager.BATTERY_STATUS_FULL -> "full"
|
|
274
|
-
BatteryManager.BATTERY_STATUS_DISCHARGING -> "discharging"
|
|
275
|
-
BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "discharging"
|
|
276
|
-
else -> "unknown"
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
val isCharging = state == "charging" || state == "full"
|
|
280
|
-
|
|
281
|
-
map.putInt("level", level)
|
|
282
|
-
map.putString("state", state)
|
|
283
|
-
map.putBoolean("isCharging", isCharging)
|
|
284
|
-
map.putBoolean("isLow", level <= 20 && !isCharging)
|
|
285
|
-
return map
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
@ReactMethod
|
|
289
|
-
fun getBatteryInfo(promise: Promise) {
|
|
290
|
-
try { promise.resolve(buildBatteryMap()) }
|
|
291
|
-
catch (e: Exception) { promise.reject("BATTERY_ERROR", e.message, e) }
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
@ReactMethod
|
|
295
|
-
fun startBatteryListener() {
|
|
296
|
-
if (batteryReceiver != null) return
|
|
297
|
-
val receiver = object : BroadcastReceiver() {
|
|
298
|
-
override fun onReceive(ctx: Context?, intent: Intent?) {
|
|
299
|
-
emit("SystemBar_BatteryChange", buildBatteryMap())
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
batteryReceiver = receiver
|
|
303
|
-
val filter = IntentFilter().apply {
|
|
304
|
-
addAction(Intent.ACTION_BATTERY_CHANGED)
|
|
305
|
-
addAction(Intent.ACTION_BATTERY_LOW)
|
|
306
|
-
addAction(Intent.ACTION_BATTERY_OKAY)
|
|
307
|
-
addAction(Intent.ACTION_POWER_CONNECTED)
|
|
308
|
-
addAction(Intent.ACTION_POWER_DISCONNECTED)
|
|
309
|
-
}
|
|
310
|
-
reactContext.registerReceiver(receiver, filter)
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
@ReactMethod
|
|
314
|
-
fun stopBatteryListener() {
|
|
315
|
-
batteryReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
316
|
-
batteryReceiver = null
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ═══════════════════════════════════════════════
|
|
320
|
-
// 🆕 HAPTIC FEEDBACK
|
|
321
|
-
// ═══════════════════════════════════════════════
|
|
222
|
+
private var displayListener: DisplayManager.DisplayListener? = null
|
|
322
223
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
329
|
-
val effect = when (pattern) {
|
|
330
|
-
"light" -> VibrationEffect.createOneShot(20, 80)
|
|
331
|
-
"medium" -> VibrationEffect.createOneShot(40, 128)
|
|
332
|
-
"heavy" -> VibrationEffect.createOneShot(60, 255)
|
|
333
|
-
"success" -> VibrationEffect.createWaveform(longArrayOf(0, 30, 60, 30), intArrayOf(0, 180, 0, 255), -1)
|
|
334
|
-
"warning" -> VibrationEffect.createWaveform(longArrayOf(0, 50, 80, 50), intArrayOf(0, 200, 0, 200), -1)
|
|
335
|
-
"error" -> VibrationEffect.createWaveform(longArrayOf(0, 40, 40, 40, 40, 40), intArrayOf(0, 255, 0, 255, 0, 255), -1)
|
|
336
|
-
"selection" -> VibrationEffect.createOneShot(10, 60)
|
|
337
|
-
else -> VibrationEffect.createOneShot(30, 128)
|
|
338
|
-
}
|
|
339
|
-
vib.vibrate(effect)
|
|
340
|
-
} else {
|
|
341
|
-
@Suppress("DEPRECATION")
|
|
342
|
-
when (pattern) {
|
|
343
|
-
"light" -> vib.vibrate(20)
|
|
344
|
-
"medium" -> vib.vibrate(40)
|
|
345
|
-
"heavy" -> vib.vibrate(60)
|
|
346
|
-
"success" -> vib.vibrate(longArrayOf(0, 30, 60, 30), -1)
|
|
347
|
-
"warning" -> vib.vibrate(longArrayOf(0, 50, 80, 50), -1)
|
|
348
|
-
"error" -> vib.vibrate(longArrayOf(0, 40, 40, 40, 40, 40), -1)
|
|
349
|
-
"selection" -> vib.vibrate(10)
|
|
350
|
-
else -> vib.vibrate(30)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
224
|
+
private fun emit(event: String, data: WritableMap) {
|
|
225
|
+
reactContext
|
|
226
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
227
|
+
.emit(event, data)
|
|
353
228
|
}
|
|
354
229
|
|
|
355
|
-
// ═══════════════════════════════════════════════
|
|
356
|
-
// 🆕 SCREENCAST
|
|
357
|
-
// ═══════════════════════════════════════════════
|
|
358
|
-
|
|
359
|
-
private var displayListener: DisplayManager.DisplayListener? = null
|
|
360
|
-
|
|
361
230
|
private fun buildScreencastMap(): WritableMap {
|
|
362
|
-
val dm
|
|
231
|
+
val dm = displayManager()
|
|
363
232
|
val map = Arguments.createMap()
|
|
364
233
|
val presentations = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
|
365
234
|
val isCasting = presentations.isNotEmpty()
|
|
@@ -379,9 +248,9 @@ class SystemBarModule(
|
|
|
379
248
|
fun startScreencastListener() {
|
|
380
249
|
if (displayListener != null) return
|
|
381
250
|
val listener = object : DisplayManager.DisplayListener {
|
|
382
|
-
override fun onDisplayAdded(id: Int)
|
|
383
|
-
override fun onDisplayRemoved(id: Int)
|
|
384
|
-
override fun onDisplayChanged(id: Int)
|
|
251
|
+
override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
|
|
252
|
+
override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
|
|
253
|
+
override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
|
|
385
254
|
}
|
|
386
255
|
displayListener = listener
|
|
387
256
|
displayManager().registerDisplayListener(listener, null)
|
|
@@ -393,58 +262,8 @@ class SystemBarModule(
|
|
|
393
262
|
displayListener = null
|
|
394
263
|
}
|
|
395
264
|
|
|
396
|
-
|
|
397
|
-
fun setSecureScreen(enable: Boolean) {
|
|
398
|
-
activity()?.runOnUiThread {
|
|
399
|
-
if (enable) activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
400
|
-
else activity()!!.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// ═══════════════════════════════════════════════
|
|
405
|
-
// 🆕 FONT SCALE
|
|
406
|
-
// ═══════════════════════════════════════════════
|
|
407
|
-
|
|
408
|
-
private var fontScaleReceiver: BroadcastReceiver? = null
|
|
409
|
-
|
|
410
|
-
private fun buildFontScaleMap(): WritableMap {
|
|
411
|
-
val map = Arguments.createMap()
|
|
412
|
-
val cfg = reactContext.resources.configuration
|
|
413
|
-
val dm = reactContext.resources.displayMetrics
|
|
414
|
-
map.putDouble("fontScale", cfg.fontScale.toDouble())
|
|
415
|
-
map.putDouble("density", dm.density.toDouble())
|
|
416
|
-
return map
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
@ReactMethod
|
|
420
|
-
fun getFontScaleInfo(promise: Promise) {
|
|
421
|
-
try { promise.resolve(buildFontScaleMap()) }
|
|
422
|
-
catch (e: Exception) { promise.reject("FONT_SCALE_ERROR", e.message, e) }
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
@ReactMethod
|
|
426
|
-
fun startFontScaleListener() {
|
|
427
|
-
if (fontScaleReceiver != null) return
|
|
428
|
-
val receiver = object : BroadcastReceiver() {
|
|
429
|
-
override fun onReceive(ctx: Context?, intent: Intent?) {
|
|
430
|
-
emit("SystemBar_FontScaleChange", buildFontScaleMap())
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
fontScaleReceiver = receiver
|
|
434
|
-
reactContext.registerReceiver(receiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
@ReactMethod
|
|
438
|
-
fun stopFontScaleListener() {
|
|
439
|
-
fontScaleReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
440
|
-
fontScaleReceiver = null
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// ── Cleanup on host destroy ──────────────────
|
|
265
|
+
// ── Cleanup ──────────────────────────────────
|
|
444
266
|
override fun onCatalystInstanceDestroy() {
|
|
445
|
-
stopNetworkListener()
|
|
446
|
-
stopBatteryListener()
|
|
447
267
|
stopScreencastListener()
|
|
448
|
-
stopFontScaleListener()
|
|
449
268
|
}
|
|
450
269
|
}
|
package/index.ts
CHANGED
|
@@ -4,12 +4,5 @@
|
|
|
4
4
|
|
|
5
5
|
export * from "./src/SystemBar";
|
|
6
6
|
export * from "./src/types";
|
|
7
|
-
export {
|
|
8
|
-
useSystemBar,
|
|
9
|
-
useBattery,
|
|
10
|
-
useNetwork,
|
|
11
|
-
useScreencast,
|
|
12
|
-
useFontScale,
|
|
13
|
-
useHaptic,
|
|
14
|
-
} from "./src/useSystemBar";
|
|
7
|
+
export { useSystemBar, useScreencast } from "./src/useSystemBar";
|
|
15
8
|
export type { SystemBarConfig } from "./src/useSystemBar";
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "./src/SystemBar";
|
|
2
2
|
export * from "./src/types";
|
|
3
|
-
export { useSystemBar,
|
|
3
|
+
export { useSystemBar, useScreencast } from "./src/useSystemBar";
|
|
4
4
|
export type { SystemBarConfig } from "./src/useSystemBar";
|
package/lib/index.js
CHANGED
|
@@ -17,13 +17,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
17
17
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.
|
|
20
|
+
exports.useScreencast = exports.useSystemBar = void 0;
|
|
21
21
|
__exportStar(require("./src/SystemBar"), exports);
|
|
22
22
|
__exportStar(require("./src/types"), exports);
|
|
23
23
|
var useSystemBar_1 = require("./src/useSystemBar");
|
|
24
24
|
Object.defineProperty(exports, "useSystemBar", { enumerable: true, get: function () { return useSystemBar_1.useSystemBar; } });
|
|
25
|
-
Object.defineProperty(exports, "useBattery", { enumerable: true, get: function () { return useSystemBar_1.useBattery; } });
|
|
26
|
-
Object.defineProperty(exports, "useNetwork", { enumerable: true, get: function () { return useSystemBar_1.useNetwork; } });
|
|
27
25
|
Object.defineProperty(exports, "useScreencast", { enumerable: true, get: function () { return useSystemBar_1.useScreencast; } });
|
|
28
|
-
Object.defineProperty(exports, "useFontScale", { enumerable: true, get: function () { return useSystemBar_1.useFontScale; } });
|
|
29
|
-
Object.defineProperty(exports, "useHaptic", { enumerable: true, get: function () { return useSystemBar_1.useHaptic; } });
|
package/lib/src/SystemBar.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const setNavigationBarColor: (color: string) =>
|
|
1
|
+
import type { NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarStyle, VolumeStream } from "./types";
|
|
2
|
+
export declare const setNavigationBarColor: (color: string) => void;
|
|
3
3
|
export declare const setNavigationBarVisibility: (mode: NavigationBarVisibility) => Promise<void>;
|
|
4
4
|
export declare const setNavigationBarButtonStyle: (style: NavigationBarButtonStyle) => Promise<void>;
|
|
5
5
|
export declare const setNavigationBarStyle: (style: NavigationBarStyle) => Promise<void>;
|
|
@@ -14,60 +14,7 @@ export declare const getVolume: (stream?: VolumeStream) => Promise<number>;
|
|
|
14
14
|
export declare const setVolumeHUDVisible: (visible: boolean) => void;
|
|
15
15
|
export declare const keepScreenOn: (enable: boolean) => void;
|
|
16
16
|
export declare const immersiveMode: (enable: boolean) => void;
|
|
17
|
+
export declare const setSecureScreen: (enable: boolean) => void;
|
|
17
18
|
export declare const setOrientation: (mode: Orientation) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Get current network connection info.
|
|
20
|
-
* Includes: type, isConnected, isAirplaneMode, ssid, cellularGeneration
|
|
21
|
-
*/
|
|
22
|
-
export declare const getNetworkInfo: () => Promise<NetworkInfo>;
|
|
23
|
-
/**
|
|
24
|
-
* Subscribe to network changes.
|
|
25
|
-
* @returns unsubscribe function — call it in useEffect cleanup
|
|
26
|
-
* @example
|
|
27
|
-
* const unsub = onNetworkChange((info) => console.log(info));
|
|
28
|
-
* return () => unsub();
|
|
29
|
-
*/
|
|
30
|
-
export declare const onNetworkChange: (callback: (info: NetworkInfo) => void) => (() => void);
|
|
31
|
-
/**
|
|
32
|
-
* Get current battery info: level (0–100), state, isCharging, isLow.
|
|
33
|
-
*/
|
|
34
|
-
export declare const getBatteryInfo: () => Promise<BatteryInfo>;
|
|
35
|
-
/**
|
|
36
|
-
* Subscribe to battery level / state changes.
|
|
37
|
-
* @returns unsubscribe function
|
|
38
|
-
*/
|
|
39
|
-
export declare const onBatteryChange: (callback: (info: BatteryInfo) => void) => (() => void);
|
|
40
|
-
/**
|
|
41
|
-
* Trigger haptic feedback.
|
|
42
|
-
*
|
|
43
|
-
* iOS: Uses UIImpactFeedbackGenerator / UINotificationFeedbackGenerator.
|
|
44
|
-
* Android: Uses Vibrator / VibrationEffect (API 26+).
|
|
45
|
-
*
|
|
46
|
-
* @param pattern "light" | "medium" | "heavy" | "success" | "warning" | "error" | "selection"
|
|
47
|
-
*/
|
|
48
|
-
export declare const haptic: (pattern: HapticPattern) => void;
|
|
49
|
-
/**
|
|
50
|
-
* Check if screen is currently being cast or mirrored.
|
|
51
|
-
*/
|
|
52
19
|
export declare const getScreencastInfo: () => Promise<ScreencastInfo>;
|
|
53
|
-
/**
|
|
54
|
-
* Subscribe to screencast state changes (started / stopped).
|
|
55
|
-
* @returns unsubscribe function
|
|
56
|
-
*/
|
|
57
20
|
export declare const onScreencastChange: (callback: (info: ScreencastInfo) => void) => (() => void);
|
|
58
|
-
/**
|
|
59
|
-
* Prevent screen content from appearing in screenshots / recordings.
|
|
60
|
-
* Useful for sensitive screens (payments, passwords).
|
|
61
|
-
* @platform android
|
|
62
|
-
*/
|
|
63
|
-
export declare const setSecureScreen: (enable: boolean) => void;
|
|
64
|
-
/**
|
|
65
|
-
* Get current system font scale and display density.
|
|
66
|
-
* Useful for adapting layout to accessibility settings.
|
|
67
|
-
*/
|
|
68
|
-
export declare const getFontScaleInfo: () => Promise<FontScaleInfo>;
|
|
69
|
-
/**
|
|
70
|
-
* Subscribe to font scale changes (user changes system text size).
|
|
71
|
-
* @returns unsubscribe function
|
|
72
|
-
*/
|
|
73
|
-
export declare const onFontScaleChange: (callback: (info: FontScaleInfo) => void) => (() => void);
|