rn-system-bar 3.1.3 → 3.1.5
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/systembar/SystemBarModule.kt +234 -74
- package/lib/src/SystemBar.d.ts +16 -7
- package/lib/src/SystemBar.js +53 -43
- package/lib/src/useSystemBar.js +7 -10
- package/package.json +1 -1
- package/src/SystemBar.ts +67 -39
- package/src/useSystemBar.ts +12 -17
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
package com.systembar
|
|
6
6
|
|
|
7
7
|
import android.app.Activity
|
|
8
|
+
import android.content.BroadcastReceiver
|
|
8
9
|
import android.content.Context
|
|
9
10
|
import android.content.Intent
|
|
11
|
+
import android.content.IntentFilter
|
|
10
12
|
import android.content.pm.ActivityInfo
|
|
11
13
|
import android.graphics.Color
|
|
12
14
|
import android.hardware.display.DisplayManager
|
|
13
15
|
import android.media.AudioManager
|
|
14
16
|
import android.os.Build
|
|
17
|
+
import android.os.Handler
|
|
18
|
+
import android.os.Looper
|
|
15
19
|
import android.provider.Settings
|
|
16
20
|
import android.view.View
|
|
17
21
|
import android.view.WindowInsets
|
|
@@ -29,12 +33,10 @@ class SystemBarModule(
|
|
|
29
33
|
override fun getName(): String = "SystemBar"
|
|
30
34
|
|
|
31
35
|
private fun activity(): Activity? = reactContext.currentActivity
|
|
36
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
32
37
|
|
|
33
|
-
private fun audioManager()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
private fun displayManager() =
|
|
37
|
-
reactContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
38
|
+
private fun audioManager() = reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
39
|
+
private fun displayManager() = reactContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
|
38
40
|
|
|
39
41
|
private fun streamType(stream: String): Int = when (stream) {
|
|
40
42
|
"ring" -> AudioManager.STREAM_RING
|
|
@@ -51,26 +53,114 @@ class SystemBarModule(
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
// ═══════════════════════════════════════════════
|
|
54
|
-
// NAVIGATION BAR
|
|
55
|
-
// WindowInsetsController API 30+, fallback for older.
|
|
56
|
-
// navigationBarColor deprecated on API 35+ but still
|
|
57
|
-
// works — suppressed at class level.
|
|
56
|
+
// NAVIGATION BAR — 100% native, no expo dep
|
|
58
57
|
// ═══════════════════════════════════════════════
|
|
59
58
|
|
|
60
59
|
@ReactMethod
|
|
61
60
|
fun setNavigationBarColor(color: String) {
|
|
62
61
|
activity()?.runOnUiThread {
|
|
63
|
-
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
try { activity()!!.window.navigationBarColor = Color.parseColor(color) }
|
|
63
|
+
catch (_: Exception) {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@ReactMethod
|
|
68
|
+
fun setNavigationBarVisibility(mode: String) {
|
|
69
|
+
activity()?.runOnUiThread {
|
|
70
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
71
|
+
val c = activity()!!.window.insetsController ?: return@runOnUiThread
|
|
72
|
+
if (mode == "hidden") c.hide(WindowInsets.Type.navigationBars())
|
|
73
|
+
else c.show(WindowInsets.Type.navigationBars())
|
|
74
|
+
} else {
|
|
75
|
+
val dv = activity()!!.window.decorView
|
|
76
|
+
dv.systemUiVisibility = if (mode == "hidden") {
|
|
77
|
+
dv.systemUiVisibility or
|
|
78
|
+
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
|
79
|
+
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
70
80
|
} else {
|
|
71
|
-
|
|
81
|
+
dv.systemUiVisibility and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION.inv()
|
|
72
82
|
}
|
|
73
|
-
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@ReactMethod
|
|
88
|
+
fun setNavigationBarButtonStyle(style: String) {
|
|
89
|
+
activity()?.runOnUiThread {
|
|
90
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
91
|
+
val c = activity()!!.window.insetsController ?: return@runOnUiThread
|
|
92
|
+
c.setSystemBarsAppearance(
|
|
93
|
+
if (style == "dark") WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS else 0,
|
|
94
|
+
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
|
|
95
|
+
)
|
|
96
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
97
|
+
val dv = activity()!!.window.decorView
|
|
98
|
+
dv.systemUiVisibility = if (style == "dark")
|
|
99
|
+
dv.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
|
100
|
+
else
|
|
101
|
+
dv.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@ReactMethod
|
|
107
|
+
fun setNavigationBarStyle(style: String) {
|
|
108
|
+
// Maps to button style — "dark"/"auto" → dark icons, "light"/"inverted" → light icons
|
|
109
|
+
setNavigationBarButtonStyle(if (style == "dark" || style == "auto") "dark" else "light")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@ReactMethod
|
|
113
|
+
fun setNavigationBarBehavior(behavior: String) {
|
|
114
|
+
activity()?.runOnUiThread {
|
|
115
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
116
|
+
val c = activity()!!.window.insetsController ?: return@runOnUiThread
|
|
117
|
+
c.systemBarsBehavior = when (behavior) {
|
|
118
|
+
"overlay-swipe" -> WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
|
119
|
+
// inset modes — default persistent behavior
|
|
120
|
+
else -> WindowInsetsController.BEHAVIOR_DEFAULT
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// API < 30: IMMERSIVE_STICKY is closest — no-op otherwise
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ═══════════════════════════════════════════════
|
|
128
|
+
// STATUS BAR — 100% native
|
|
129
|
+
// ═══════════════════════════════════════════════
|
|
130
|
+
|
|
131
|
+
@ReactMethod
|
|
132
|
+
fun setStatusBarStyle(style: String) {
|
|
133
|
+
activity()?.runOnUiThread {
|
|
134
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
135
|
+
val c = activity()!!.window.insetsController ?: return@runOnUiThread
|
|
136
|
+
c.setSystemBarsAppearance(
|
|
137
|
+
if (style == "dark") WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS else 0,
|
|
138
|
+
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
|
139
|
+
)
|
|
140
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
141
|
+
val dv = activity()!!.window.decorView
|
|
142
|
+
dv.systemUiVisibility = if (style == "dark")
|
|
143
|
+
dv.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
|
144
|
+
else
|
|
145
|
+
dv.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@ReactMethod
|
|
151
|
+
fun setStatusBarVisibility(visible: Boolean) {
|
|
152
|
+
activity()?.runOnUiThread {
|
|
153
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
154
|
+
val c = activity()!!.window.insetsController ?: return@runOnUiThread
|
|
155
|
+
if (visible) c.show(WindowInsets.Type.statusBars())
|
|
156
|
+
else c.hide(WindowInsets.Type.statusBars())
|
|
157
|
+
} else {
|
|
158
|
+
val dv = activity()!!.window.decorView
|
|
159
|
+
dv.systemUiVisibility = if (!visible)
|
|
160
|
+
dv.systemUiVisibility or View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
161
|
+
else
|
|
162
|
+
dv.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN.inv()
|
|
163
|
+
}
|
|
74
164
|
}
|
|
75
165
|
}
|
|
76
166
|
|
|
@@ -81,47 +171,64 @@ class SystemBarModule(
|
|
|
81
171
|
@ReactMethod
|
|
82
172
|
fun setBrightness(level: Float) {
|
|
83
173
|
val clamped = level.coerceIn(0.01f, 1f)
|
|
84
|
-
|
|
85
|
-
// Window brightness — instant, no permission needed
|
|
86
174
|
activity()?.runOnUiThread {
|
|
87
175
|
val lp = activity()!!.window.attributes
|
|
88
176
|
lp.screenBrightness = clamped
|
|
89
177
|
activity()!!.window.attributes = lp
|
|
90
178
|
}
|
|
91
|
-
|
|
92
|
-
// System brightness — persisted, needs WRITE_SETTINGS
|
|
93
179
|
val sysValue = (clamped * 255).toInt().coerceIn(1, 255)
|
|
94
180
|
if (Settings.System.canWrite(reactContext)) {
|
|
95
|
-
Settings.System.putInt(
|
|
96
|
-
|
|
97
|
-
Settings.System.SCREEN_BRIGHTNESS,
|
|
98
|
-
sysValue
|
|
99
|
-
)
|
|
100
|
-
Settings.System.putInt(
|
|
101
|
-
reactContext.contentResolver,
|
|
102
|
-
Settings.System.SCREEN_BRIGHTNESS_MODE,
|
|
103
|
-
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL
|
|
104
|
-
)
|
|
181
|
+
Settings.System.putInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, sysValue)
|
|
182
|
+
Settings.System.putInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
|
|
105
183
|
} else {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
try {
|
|
185
|
+
reactContext.startActivity(Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).apply {
|
|
186
|
+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
187
|
+
})
|
|
188
|
+
} catch (_: Exception) {}
|
|
110
189
|
}
|
|
111
190
|
}
|
|
112
191
|
|
|
113
192
|
@ReactMethod
|
|
114
193
|
fun getBrightness(promise: Promise) {
|
|
115
194
|
try {
|
|
116
|
-
val sys = Settings.System.getInt(
|
|
117
|
-
reactContext.contentResolver,
|
|
118
|
-
Settings.System.SCREEN_BRIGHTNESS,
|
|
119
|
-
128
|
|
120
|
-
)
|
|
195
|
+
val sys = Settings.System.getInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, 128)
|
|
121
196
|
promise.resolve(sys / 255.0)
|
|
122
|
-
} catch (e: Exception) {
|
|
123
|
-
|
|
197
|
+
} catch (e: Exception) { promise.reject("BRIGHTNESS_ERROR", e.message, e) }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ── Realtime brightness listener ─────────────
|
|
201
|
+
private var brightnessReceiver: BroadcastReceiver? = null
|
|
202
|
+
private var brightnessPollingRunnable: Runnable? = null
|
|
203
|
+
private var lastBrightness = -1
|
|
204
|
+
|
|
205
|
+
@ReactMethod
|
|
206
|
+
fun startBrightnessListener() {
|
|
207
|
+
if (brightnessPollingRunnable != null) return
|
|
208
|
+
// Settings.System doesn't broadcast changes — poll every 500ms
|
|
209
|
+
val runnable = object : Runnable {
|
|
210
|
+
override fun run() {
|
|
211
|
+
try {
|
|
212
|
+
val cur = Settings.System.getInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, 128)
|
|
213
|
+
if (cur != lastBrightness) {
|
|
214
|
+
lastBrightness = cur
|
|
215
|
+
val map = Arguments.createMap()
|
|
216
|
+
map.putDouble("brightness", cur / 255.0)
|
|
217
|
+
emit("SystemBar_BrightnessChange", map)
|
|
218
|
+
}
|
|
219
|
+
} catch (_: Exception) {}
|
|
220
|
+
brightnessPollingRunnable?.let { mainHandler.postDelayed(it, 500) }
|
|
221
|
+
}
|
|
124
222
|
}
|
|
223
|
+
brightnessPollingRunnable = runnable
|
|
224
|
+
mainHandler.post(runnable)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@ReactMethod
|
|
228
|
+
fun stopBrightnessListener() {
|
|
229
|
+
brightnessPollingRunnable?.let { mainHandler.removeCallbacks(it) }
|
|
230
|
+
brightnessPollingRunnable = null
|
|
231
|
+
lastBrightness = -1
|
|
125
232
|
}
|
|
126
233
|
|
|
127
234
|
// ═══════════════════════════════════════════════
|
|
@@ -130,16 +237,13 @@ class SystemBarModule(
|
|
|
130
237
|
|
|
131
238
|
private var suppressVolumeHUD = false
|
|
132
239
|
|
|
133
|
-
@ReactMethod
|
|
134
|
-
fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
|
|
240
|
+
@ReactMethod fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
|
|
135
241
|
|
|
136
242
|
@ReactMethod
|
|
137
243
|
fun setVolume(level: Float, stream: String) {
|
|
138
244
|
try {
|
|
139
|
-
val am
|
|
140
|
-
val
|
|
141
|
-
val max = am.getStreamMaxVolume(type)
|
|
142
|
-
val vol = (level.coerceIn(0f, 1f) * max).toInt()
|
|
245
|
+
val am = audioManager(); val type = streamType(stream)
|
|
246
|
+
val vol = (level.coerceIn(0f, 1f) * am.getStreamMaxVolume(type)).toInt()
|
|
143
247
|
am.setStreamVolume(type, vol, if (suppressVolumeHUD) 0 else AudioManager.FLAG_SHOW_UI)
|
|
144
248
|
} catch (_: Exception) {}
|
|
145
249
|
}
|
|
@@ -147,13 +251,46 @@ class SystemBarModule(
|
|
|
147
251
|
@ReactMethod
|
|
148
252
|
fun getVolume(stream: String, promise: Promise) {
|
|
149
253
|
try {
|
|
150
|
-
val am
|
|
151
|
-
val
|
|
152
|
-
val max = am.getStreamMaxVolume(type)
|
|
254
|
+
val am = audioManager(); val type = streamType(stream)
|
|
255
|
+
val max = am.getStreamMaxVolume(type)
|
|
153
256
|
promise.resolve(if (max > 0) am.getStreamVolume(type).toDouble() / max else 0.0)
|
|
154
|
-
} catch (e: Exception) {
|
|
155
|
-
|
|
257
|
+
} catch (e: Exception) { promise.reject("VOLUME_ERROR", e.message, e) }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ── Realtime volume listener ─────────────────
|
|
261
|
+
private var volumeReceiver: BroadcastReceiver? = null
|
|
262
|
+
|
|
263
|
+
@ReactMethod
|
|
264
|
+
fun startVolumeListener() {
|
|
265
|
+
if (volumeReceiver != null) return
|
|
266
|
+
val receiver = object : BroadcastReceiver() {
|
|
267
|
+
override fun onReceive(ctx: Context?, intent: Intent?) {
|
|
268
|
+
if (intent?.action != "android.media.VOLUME_CHANGED_ACTION") return
|
|
269
|
+
val streamType = intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_TYPE", AudioManager.STREAM_MUSIC)
|
|
270
|
+
val am = audioManager()
|
|
271
|
+
val max = am.getStreamMaxVolume(streamType)
|
|
272
|
+
val cur = am.getStreamVolume(streamType)
|
|
273
|
+
val streamName = when (streamType) {
|
|
274
|
+
AudioManager.STREAM_RING -> "ring"
|
|
275
|
+
AudioManager.STREAM_NOTIFICATION -> "notification"
|
|
276
|
+
AudioManager.STREAM_ALARM -> "alarm"
|
|
277
|
+
AudioManager.STREAM_SYSTEM -> "system"
|
|
278
|
+
else -> "music"
|
|
279
|
+
}
|
|
280
|
+
val map = Arguments.createMap()
|
|
281
|
+
map.putDouble("volume", if (max > 0) cur.toDouble() / max else 0.0)
|
|
282
|
+
map.putString("stream", streamName)
|
|
283
|
+
emit("SystemBar_VolumeChange", map)
|
|
284
|
+
}
|
|
156
285
|
}
|
|
286
|
+
volumeReceiver = receiver
|
|
287
|
+
reactContext.registerReceiver(receiver, IntentFilter("android.media.VOLUME_CHANGED_ACTION"))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@ReactMethod
|
|
291
|
+
fun stopVolumeListener() {
|
|
292
|
+
volumeReceiver?.let { try { reactContext.unregisterReceiver(it) } catch (_: Exception) {} }
|
|
293
|
+
volumeReceiver = null
|
|
157
294
|
}
|
|
158
295
|
|
|
159
296
|
// ═══════════════════════════════════════════════
|
|
@@ -181,14 +318,10 @@ class SystemBarModule(
|
|
|
181
318
|
}
|
|
182
319
|
} else {
|
|
183
320
|
activity()!!.window.decorView.systemUiVisibility = if (enable) {
|
|
184
|
-
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
|
185
|
-
View.
|
|
186
|
-
View.SYSTEM_UI_FLAG_FULLSCREEN or
|
|
187
|
-
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
|
321
|
+
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
|
322
|
+
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
|
188
323
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
189
|
-
} else {
|
|
190
|
-
View.SYSTEM_UI_FLAG_VISIBLE
|
|
191
|
-
}
|
|
324
|
+
} else { View.SYSTEM_UI_FLAG_VISIBLE }
|
|
192
325
|
}
|
|
193
326
|
}
|
|
194
327
|
}
|
|
@@ -203,33 +336,59 @@ class SystemBarModule(
|
|
|
203
336
|
|
|
204
337
|
// ═══════════════════════════════════════════════
|
|
205
338
|
// ORIENTATION
|
|
206
|
-
// "auto" = FULL_SENSOR (follows system auto-rotate)
|
|
207
339
|
// ═══════════════════════════════════════════════
|
|
208
340
|
|
|
209
341
|
@ReactMethod
|
|
210
342
|
fun setOrientation(mode: String) {
|
|
343
|
+
// "auto" → always unspecified (follow system auto-rotate toggle unconditionally)
|
|
344
|
+
if (mode == "auto") {
|
|
345
|
+
activity()?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// For a specific orientation: check if the device's auto-rotate toggle is ON.
|
|
350
|
+
// auto-rotate ON → use sensor-based variant so the device can still rotate
|
|
351
|
+
// within the requested axis (portrait ↔ reverse-portrait, etc.)
|
|
352
|
+
// auto-rotate OFF → hard-lock to the exact orientation requested
|
|
353
|
+
val autoRotateOn = Settings.System.getInt(
|
|
354
|
+
reactContext.contentResolver,
|
|
355
|
+
Settings.System.ACCELEROMETER_ROTATION,
|
|
356
|
+
0 // default = locked (off)
|
|
357
|
+
) == 1
|
|
358
|
+
|
|
211
359
|
activity()?.requestedOrientation = when (mode) {
|
|
212
|
-
"portrait"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
360
|
+
"portrait" ->
|
|
361
|
+
if (autoRotateOn) ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
|
|
362
|
+
else ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
|
363
|
+
|
|
364
|
+
"landscape" ->
|
|
365
|
+
if (autoRotateOn) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
|
366
|
+
else ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
367
|
+
|
|
368
|
+
"landscape-left" ->
|
|
369
|
+
// Reverse landscape — no sensor variant; honour toggle by using full sensor landscape
|
|
370
|
+
if (autoRotateOn) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
|
371
|
+
else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
|
372
|
+
|
|
373
|
+
"landscape-right" ->
|
|
374
|
+
if (autoRotateOn) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
|
375
|
+
else ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
376
|
+
|
|
377
|
+
else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
|
218
378
|
}
|
|
219
379
|
}
|
|
220
380
|
|
|
221
381
|
// ═══════════════════════════════════════════════
|
|
222
|
-
// SCREENCAST
|
|
382
|
+
// SCREENCAST
|
|
223
383
|
// ═══════════════════════════════════════════════
|
|
224
384
|
|
|
225
385
|
private var displayListener: DisplayManager.DisplayListener? = null
|
|
226
386
|
|
|
227
387
|
private fun buildDisplayListMap(): WritableMap {
|
|
228
|
-
val dm
|
|
229
|
-
val map
|
|
388
|
+
val dm = displayManager()
|
|
389
|
+
val map = Arguments.createMap()
|
|
230
390
|
val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
|
|
231
|
-
val arr
|
|
232
|
-
|
|
391
|
+
val arr = Arguments.createArray()
|
|
233
392
|
for (d in displays) {
|
|
234
393
|
val item = Arguments.createMap()
|
|
235
394
|
item.putInt("id", d.displayId)
|
|
@@ -237,7 +396,6 @@ class SystemBarModule(
|
|
|
237
396
|
item.putBoolean("isValid", d.isValid)
|
|
238
397
|
arr.pushMap(item)
|
|
239
398
|
}
|
|
240
|
-
|
|
241
399
|
map.putBoolean("isCasting", displays.isNotEmpty())
|
|
242
400
|
map.putArray("displays", arr)
|
|
243
401
|
if (displays.isNotEmpty()) map.putString("displayName", displays[0].name)
|
|
@@ -271,6 +429,8 @@ class SystemBarModule(
|
|
|
271
429
|
|
|
272
430
|
// ── Cleanup ──────────────────────────────────
|
|
273
431
|
override fun onCatalystInstanceDestroy() {
|
|
432
|
+
stopBrightnessListener()
|
|
433
|
+
stopVolumeListener()
|
|
274
434
|
stopScreencastListener()
|
|
275
435
|
}
|
|
276
436
|
}
|
package/lib/src/SystemBar.d.ts
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import type { NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarStyle, VolumeStream } from "./types";
|
|
2
2
|
export declare const setNavigationBarColor: (color: string) => void;
|
|
3
|
-
export declare const setNavigationBarVisibility: (mode: NavigationBarVisibility) =>
|
|
4
|
-
export declare const setNavigationBarButtonStyle: (style: NavigationBarButtonStyle) =>
|
|
5
|
-
export declare const setNavigationBarStyle: (style: NavigationBarStyle) =>
|
|
6
|
-
export declare const setNavigationBarBehavior: (behavior: NavigationBarBehavior) =>
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const
|
|
9
|
-
export declare const setStatusBarVisibility: (visible: boolean, animated?: boolean) => void;
|
|
3
|
+
export declare const setNavigationBarVisibility: (mode: NavigationBarVisibility) => void;
|
|
4
|
+
export declare const setNavigationBarButtonStyle: (style: NavigationBarButtonStyle) => void;
|
|
5
|
+
export declare const setNavigationBarStyle: (style: NavigationBarStyle) => void;
|
|
6
|
+
export declare const setNavigationBarBehavior: (behavior: NavigationBarBehavior) => void;
|
|
7
|
+
export declare const setStatusBarStyle: (style: StatusBarStyle) => void;
|
|
8
|
+
export declare const setStatusBarVisibility: (visible: boolean) => void;
|
|
10
9
|
export declare const setBrightness: (level: number) => void;
|
|
11
10
|
export declare const getBrightness: () => Promise<number>;
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to system brightness changes (polls every 500ms on Android).
|
|
13
|
+
* @returns unsubscribe function
|
|
14
|
+
*/
|
|
15
|
+
export declare const onBrightnessChange: (callback: (brightness: number) => void) => (() => void);
|
|
12
16
|
export declare const setVolume: (level: number, stream?: VolumeStream) => void;
|
|
13
17
|
export declare const getVolume: (stream?: VolumeStream) => Promise<number>;
|
|
14
18
|
export declare const setVolumeHUDVisible: (visible: boolean) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Subscribe to system volume changes (hardware buttons, other apps).
|
|
21
|
+
* @returns unsubscribe function
|
|
22
|
+
*/
|
|
23
|
+
export declare const onVolumeChange: (callback: (volume: number, stream: VolumeStream) => void) => (() => void);
|
|
15
24
|
export declare const keepScreenOn: (enable: boolean) => void;
|
|
16
25
|
export declare const immersiveMode: (enable: boolean) => void;
|
|
17
26
|
export declare const setSecureScreen: (enable: boolean) => void;
|
package/lib/src/SystemBar.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─────────────────────────────────────────────
|
|
3
3
|
// rn-system-bar · SystemBar.ts
|
|
4
|
+
// Zero expo-navigation-bar dependency.
|
|
5
|
+
// All APIs → native Kotlin / iOS Swift.
|
|
4
6
|
// ─────────────────────────────────────────────
|
|
5
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.onScreencastChange = exports.getScreencastInfo = exports.setOrientation = exports.setSecureScreen = exports.immersiveMode = exports.keepScreenOn = exports.setVolumeHUDVisible = exports.getVolume = exports.setVolume = exports.
|
|
8
|
+
exports.onScreencastChange = exports.getScreencastInfo = exports.setOrientation = exports.setSecureScreen = exports.immersiveMode = exports.keepScreenOn = exports.onVolumeChange = exports.setVolumeHUDVisible = exports.getVolume = exports.setVolume = exports.onBrightnessChange = exports.getBrightness = exports.setBrightness = exports.setStatusBarVisibility = exports.setStatusBarStyle = exports.setNavigationBarBehavior = exports.setNavigationBarStyle = exports.setNavigationBarButtonStyle = exports.setNavigationBarVisibility = exports.setNavigationBarColor = void 0;
|
|
7
9
|
const react_native_1 = require("react-native");
|
|
8
|
-
const NavBar = (() => {
|
|
9
|
-
try {
|
|
10
|
-
return require("expo-navigation-bar");
|
|
11
|
-
}
|
|
12
|
-
catch (_a) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
})();
|
|
16
10
|
const { SystemBar: Native } = react_native_1.NativeModules;
|
|
17
11
|
const isAndroid = react_native_1.Platform.OS === "android";
|
|
18
12
|
const androidOnly = (name) => {
|
|
@@ -28,9 +22,7 @@ const checkNative = () => {
|
|
|
28
22
|
throw new Error("[rn-system-bar] Native module not found. Rebuild your project.");
|
|
29
23
|
};
|
|
30
24
|
// ═══════════════════════════════════════════════
|
|
31
|
-
// NAVIGATION BAR (Android
|
|
32
|
-
// Color → native Window API (edge-to-edge safe)
|
|
33
|
-
// Others → expo-navigation-bar (work in edge-to-edge)
|
|
25
|
+
// NAVIGATION BAR (Android — 100% native)
|
|
34
26
|
// ═══════════════════════════════════════════════
|
|
35
27
|
const setNavigationBarColor = (color) => {
|
|
36
28
|
if (!androidOnly("setNavigationBarColor"))
|
|
@@ -40,60 +32,48 @@ const setNavigationBarColor = (color) => {
|
|
|
40
32
|
};
|
|
41
33
|
exports.setNavigationBarColor = setNavigationBarColor;
|
|
42
34
|
const setNavigationBarVisibility = (mode) => {
|
|
43
|
-
var _a;
|
|
44
35
|
if (!androidOnly("setNavigationBarVisibility"))
|
|
45
|
-
return
|
|
46
|
-
|
|
36
|
+
return;
|
|
37
|
+
checkNative();
|
|
38
|
+
Native.setNavigationBarVisibility(mode);
|
|
47
39
|
};
|
|
48
40
|
exports.setNavigationBarVisibility = setNavigationBarVisibility;
|
|
49
41
|
const setNavigationBarButtonStyle = (style) => {
|
|
50
|
-
var _a;
|
|
51
42
|
if (!androidOnly("setNavigationBarButtonStyle"))
|
|
52
|
-
return
|
|
53
|
-
|
|
43
|
+
return;
|
|
44
|
+
checkNative();
|
|
45
|
+
Native.setNavigationBarButtonStyle(style);
|
|
54
46
|
};
|
|
55
47
|
exports.setNavigationBarButtonStyle = setNavigationBarButtonStyle;
|
|
56
48
|
const setNavigationBarStyle = (style) => {
|
|
57
|
-
var _a;
|
|
58
49
|
if (!androidOnly("setNavigationBarStyle"))
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
return;
|
|
51
|
+
checkNative();
|
|
52
|
+
Native.setNavigationBarStyle(style);
|
|
62
53
|
};
|
|
63
54
|
exports.setNavigationBarStyle = setNavigationBarStyle;
|
|
64
55
|
const setNavigationBarBehavior = (behavior) => {
|
|
65
|
-
var _a;
|
|
66
56
|
if (!androidOnly("setNavigationBarBehavior"))
|
|
67
|
-
return
|
|
68
|
-
|
|
57
|
+
return;
|
|
58
|
+
checkNative();
|
|
59
|
+
Native.setNavigationBarBehavior(behavior);
|
|
69
60
|
};
|
|
70
61
|
exports.setNavigationBarBehavior = setNavigationBarBehavior;
|
|
71
62
|
// ═══════════════════════════════════════════════
|
|
72
|
-
// STATUS BAR
|
|
63
|
+
// STATUS BAR (native — no RN StatusBar)
|
|
73
64
|
// ═══════════════════════════════════════════════
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
react_native_1.StatusBar.setBackgroundColor(color, animated);
|
|
78
|
-
};
|
|
79
|
-
exports.setStatusBarColor = setStatusBarColor;
|
|
80
|
-
const setStatusBarStyle = (style, animated = false) => {
|
|
81
|
-
react_native_1.StatusBar.setBarStyle(style === "light" ? "light-content" : "dark-content", animated);
|
|
65
|
+
const setStatusBarStyle = (style) => {
|
|
66
|
+
checkNative();
|
|
67
|
+
Native.setStatusBarStyle(style);
|
|
82
68
|
};
|
|
83
69
|
exports.setStatusBarStyle = setStatusBarStyle;
|
|
84
|
-
const setStatusBarVisibility = (visible
|
|
85
|
-
|
|
70
|
+
const setStatusBarVisibility = (visible) => {
|
|
71
|
+
checkNative();
|
|
72
|
+
Native.setStatusBarVisibility(visible);
|
|
86
73
|
};
|
|
87
74
|
exports.setStatusBarVisibility = setStatusBarVisibility;
|
|
88
75
|
// ═══════════════════════════════════════════════
|
|
89
76
|
// BRIGHTNESS
|
|
90
|
-
//
|
|
91
|
-
// setBrightness → updates BOTH window brightness (instant)
|
|
92
|
-
// AND system brightness (persisted).
|
|
93
|
-
// On first call: opens WRITE_SETTINGS if not granted.
|
|
94
|
-
//
|
|
95
|
-
// getBrightness → reads SYSTEM brightness so slider always
|
|
96
|
-
// matches the device brightness bar.
|
|
97
77
|
// ═══════════════════════════════════════════════
|
|
98
78
|
const setBrightness = (level) => {
|
|
99
79
|
checkNative();
|
|
@@ -105,6 +85,21 @@ const getBrightness = () => {
|
|
|
105
85
|
return Native.getBrightness();
|
|
106
86
|
};
|
|
107
87
|
exports.getBrightness = getBrightness;
|
|
88
|
+
/**
|
|
89
|
+
* Subscribe to system brightness changes (polls every 500ms on Android).
|
|
90
|
+
* @returns unsubscribe function
|
|
91
|
+
*/
|
|
92
|
+
const onBrightnessChange = (callback) => {
|
|
93
|
+
checkNative();
|
|
94
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
95
|
+
Native.startBrightnessListener();
|
|
96
|
+
const sub = DeviceEventEmitter.addListener("SystemBar_BrightnessChange", (e) => callback(e.brightness));
|
|
97
|
+
return () => {
|
|
98
|
+
sub.remove();
|
|
99
|
+
Native.stopBrightnessListener();
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
exports.onBrightnessChange = onBrightnessChange;
|
|
108
103
|
// ═══════════════════════════════════════════════
|
|
109
104
|
// VOLUME
|
|
110
105
|
// ═══════════════════════════════════════════════
|
|
@@ -125,6 +120,21 @@ const setVolumeHUDVisible = (visible) => {
|
|
|
125
120
|
Native.setVolumeHUDVisible(visible);
|
|
126
121
|
};
|
|
127
122
|
exports.setVolumeHUDVisible = setVolumeHUDVisible;
|
|
123
|
+
/**
|
|
124
|
+
* Subscribe to system volume changes (hardware buttons, other apps).
|
|
125
|
+
* @returns unsubscribe function
|
|
126
|
+
*/
|
|
127
|
+
const onVolumeChange = (callback) => {
|
|
128
|
+
checkNative();
|
|
129
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
130
|
+
Native.startVolumeListener();
|
|
131
|
+
const sub = DeviceEventEmitter.addListener("SystemBar_VolumeChange", (e) => callback(e.volume, e.stream));
|
|
132
|
+
return () => {
|
|
133
|
+
sub.remove();
|
|
134
|
+
Native.stopVolumeListener();
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
exports.onVolumeChange = onVolumeChange;
|
|
128
138
|
// ═══════════════════════════════════════════════
|
|
129
139
|
// SCREEN
|
|
130
140
|
// ═══════════════════════════════════════════════
|
package/lib/src/useSystemBar.js
CHANGED
|
@@ -47,24 +47,21 @@ const useSystemBar = (config) => {
|
|
|
47
47
|
return;
|
|
48
48
|
configRef.current = configStr;
|
|
49
49
|
const apply = async () => {
|
|
50
|
-
|
|
50
|
+
// All nav bar calls are now sync (no expo-navigation-bar)
|
|
51
51
|
if (config.navigationBarColor !== undefined)
|
|
52
52
|
SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
53
53
|
if (config.navigationBarVisibility !== undefined)
|
|
54
|
-
|
|
54
|
+
SystemBar.setNavigationBarVisibility(config.navigationBarVisibility);
|
|
55
55
|
if (config.navigationBarButtonStyle !== undefined)
|
|
56
|
-
|
|
56
|
+
SystemBar.setNavigationBarButtonStyle(config.navigationBarButtonStyle);
|
|
57
57
|
if (config.navigationBarStyle !== undefined)
|
|
58
|
-
|
|
58
|
+
SystemBar.setNavigationBarStyle(config.navigationBarStyle);
|
|
59
59
|
if (config.navigationBarBehavior !== undefined)
|
|
60
|
-
|
|
61
|
-
await Promise.allSettled(p);
|
|
62
|
-
if (config.statusBarColor !== undefined)
|
|
63
|
-
SystemBar.setStatusBarColor(config.statusBarColor, config.statusBarAnimated);
|
|
60
|
+
SystemBar.setNavigationBarBehavior(config.navigationBarBehavior);
|
|
64
61
|
if (config.statusBarStyle !== undefined)
|
|
65
|
-
SystemBar.setStatusBarStyle(config.statusBarStyle
|
|
62
|
+
SystemBar.setStatusBarStyle(config.statusBarStyle);
|
|
66
63
|
if (config.statusBarVisible !== undefined)
|
|
67
|
-
SystemBar.setStatusBarVisibility(config.statusBarVisible
|
|
64
|
+
SystemBar.setStatusBarVisibility(config.statusBarVisible);
|
|
68
65
|
if (config.keepScreenOn !== undefined)
|
|
69
66
|
SystemBar.keepScreenOn(config.keepScreenOn);
|
|
70
67
|
if (config.immersiveMode !== undefined)
|
package/package.json
CHANGED
package/src/SystemBar.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
2
|
// rn-system-bar · SystemBar.ts
|
|
3
|
+
// Zero expo-navigation-bar dependency.
|
|
4
|
+
// All APIs → native Kotlin / iOS Swift.
|
|
3
5
|
// ─────────────────────────────────────────────
|
|
4
6
|
|
|
5
|
-
import { NativeModules, Platform
|
|
7
|
+
import { NativeModules, Platform } from "react-native";
|
|
6
8
|
|
|
7
9
|
import type {
|
|
8
10
|
NavigationBarBehavior,
|
|
@@ -15,11 +17,6 @@ import type {
|
|
|
15
17
|
VolumeStream,
|
|
16
18
|
} from "./types";
|
|
17
19
|
|
|
18
|
-
const NavBar = (() => {
|
|
19
|
-
try { return require("expo-navigation-bar"); }
|
|
20
|
-
catch { return null; }
|
|
21
|
-
})();
|
|
22
|
-
|
|
23
20
|
const { SystemBar: Native } = NativeModules;
|
|
24
21
|
|
|
25
22
|
const isAndroid = Platform.OS === "android";
|
|
@@ -37,9 +34,7 @@ const checkNative = () => {
|
|
|
37
34
|
};
|
|
38
35
|
|
|
39
36
|
// ═══════════════════════════════════════════════
|
|
40
|
-
// NAVIGATION BAR (Android
|
|
41
|
-
// Color → native Window API (edge-to-edge safe)
|
|
42
|
-
// Others → expo-navigation-bar (work in edge-to-edge)
|
|
37
|
+
// NAVIGATION BAR (Android — 100% native)
|
|
43
38
|
// ═══════════════════════════════════════════════
|
|
44
39
|
|
|
45
40
|
export const setNavigationBarColor = (color: string): void => {
|
|
@@ -48,53 +43,46 @@ export const setNavigationBarColor = (color: string): void => {
|
|
|
48
43
|
Native.setNavigationBarColor(color);
|
|
49
44
|
};
|
|
50
45
|
|
|
51
|
-
export const setNavigationBarVisibility = (mode: NavigationBarVisibility):
|
|
52
|
-
if (!androidOnly("setNavigationBarVisibility")) return
|
|
53
|
-
|
|
46
|
+
export const setNavigationBarVisibility = (mode: NavigationBarVisibility): void => {
|
|
47
|
+
if (!androidOnly("setNavigationBarVisibility")) return;
|
|
48
|
+
checkNative();
|
|
49
|
+
Native.setNavigationBarVisibility(mode);
|
|
54
50
|
};
|
|
55
51
|
|
|
56
|
-
export const setNavigationBarButtonStyle = (style: NavigationBarButtonStyle):
|
|
57
|
-
if (!androidOnly("setNavigationBarButtonStyle")) return
|
|
58
|
-
|
|
52
|
+
export const setNavigationBarButtonStyle = (style: NavigationBarButtonStyle): void => {
|
|
53
|
+
if (!androidOnly("setNavigationBarButtonStyle")) return;
|
|
54
|
+
checkNative();
|
|
55
|
+
Native.setNavigationBarButtonStyle(style);
|
|
59
56
|
};
|
|
60
57
|
|
|
61
|
-
export const setNavigationBarStyle = (style: NavigationBarStyle):
|
|
62
|
-
if (!androidOnly("setNavigationBarStyle")) return
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
export const setNavigationBarStyle = (style: NavigationBarStyle): void => {
|
|
59
|
+
if (!androidOnly("setNavigationBarStyle")) return;
|
|
60
|
+
checkNative();
|
|
61
|
+
Native.setNavigationBarStyle(style);
|
|
65
62
|
};
|
|
66
63
|
|
|
67
|
-
export const setNavigationBarBehavior = (behavior: NavigationBarBehavior):
|
|
68
|
-
if (!androidOnly("setNavigationBarBehavior")) return
|
|
69
|
-
|
|
64
|
+
export const setNavigationBarBehavior = (behavior: NavigationBarBehavior): void => {
|
|
65
|
+
if (!androidOnly("setNavigationBarBehavior")) return;
|
|
66
|
+
checkNative();
|
|
67
|
+
Native.setNavigationBarBehavior(behavior);
|
|
70
68
|
};
|
|
71
69
|
|
|
72
70
|
// ═══════════════════════════════════════════════
|
|
73
|
-
// STATUS BAR
|
|
71
|
+
// STATUS BAR (native — no RN StatusBar)
|
|
74
72
|
// ═══════════════════════════════════════════════
|
|
75
73
|
|
|
76
|
-
export const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export const setStatusBarStyle = (style: StatusBarStyle, animated = false): void => {
|
|
82
|
-
StatusBar.setBarStyle(style === "light" ? "light-content" : "dark-content", animated);
|
|
74
|
+
export const setStatusBarStyle = (style: StatusBarStyle): void => {
|
|
75
|
+
checkNative();
|
|
76
|
+
Native.setStatusBarStyle(style);
|
|
83
77
|
};
|
|
84
78
|
|
|
85
|
-
export const setStatusBarVisibility = (visible: boolean
|
|
86
|
-
|
|
79
|
+
export const setStatusBarVisibility = (visible: boolean): void => {
|
|
80
|
+
checkNative();
|
|
81
|
+
Native.setStatusBarVisibility(visible);
|
|
87
82
|
};
|
|
88
83
|
|
|
89
84
|
// ═══════════════════════════════════════════════
|
|
90
85
|
// BRIGHTNESS
|
|
91
|
-
//
|
|
92
|
-
// setBrightness → updates BOTH window brightness (instant)
|
|
93
|
-
// AND system brightness (persisted).
|
|
94
|
-
// On first call: opens WRITE_SETTINGS if not granted.
|
|
95
|
-
//
|
|
96
|
-
// getBrightness → reads SYSTEM brightness so slider always
|
|
97
|
-
// matches the device brightness bar.
|
|
98
86
|
// ═══════════════════════════════════════════════
|
|
99
87
|
|
|
100
88
|
export const setBrightness = (level: number): void => {
|
|
@@ -107,6 +95,26 @@ export const getBrightness = (): Promise<number> => {
|
|
|
107
95
|
return Native.getBrightness();
|
|
108
96
|
};
|
|
109
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Subscribe to system brightness changes (polls every 500ms on Android).
|
|
100
|
+
* @returns unsubscribe function
|
|
101
|
+
*/
|
|
102
|
+
export const onBrightnessChange = (
|
|
103
|
+
callback: (brightness: number) => void
|
|
104
|
+
): (() => void) => {
|
|
105
|
+
checkNative();
|
|
106
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
107
|
+
Native.startBrightnessListener();
|
|
108
|
+
const sub = DeviceEventEmitter.addListener(
|
|
109
|
+
"SystemBar_BrightnessChange",
|
|
110
|
+
(e: { brightness: number }) => callback(e.brightness)
|
|
111
|
+
);
|
|
112
|
+
return () => {
|
|
113
|
+
sub.remove();
|
|
114
|
+
Native.stopBrightnessListener();
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
110
118
|
// ═══════════════════════════════════════════════
|
|
111
119
|
// VOLUME
|
|
112
120
|
// ═══════════════════════════════════════════════
|
|
@@ -127,6 +135,26 @@ export const setVolumeHUDVisible = (visible: boolean): void => {
|
|
|
127
135
|
Native.setVolumeHUDVisible(visible);
|
|
128
136
|
};
|
|
129
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe to system volume changes (hardware buttons, other apps).
|
|
140
|
+
* @returns unsubscribe function
|
|
141
|
+
*/
|
|
142
|
+
export const onVolumeChange = (
|
|
143
|
+
callback: (volume: number, stream: VolumeStream) => void
|
|
144
|
+
): (() => void) => {
|
|
145
|
+
checkNative();
|
|
146
|
+
const { DeviceEventEmitter } = require("react-native");
|
|
147
|
+
Native.startVolumeListener();
|
|
148
|
+
const sub = DeviceEventEmitter.addListener(
|
|
149
|
+
"SystemBar_VolumeChange",
|
|
150
|
+
(e: { volume: number; stream: VolumeStream }) => callback(e.volume, e.stream)
|
|
151
|
+
);
|
|
152
|
+
return () => {
|
|
153
|
+
sub.remove();
|
|
154
|
+
Native.stopVolumeListener();
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
|
|
130
158
|
// ═══════════════════════════════════════════════
|
|
131
159
|
// SCREEN
|
|
132
160
|
// ═══════════════════════════════════════════════
|
package/src/useSystemBar.ts
CHANGED
|
@@ -44,24 +44,19 @@ export const useSystemBar = (config: SystemBarConfig) => {
|
|
|
44
44
|
configRef.current = configStr;
|
|
45
45
|
|
|
46
46
|
const apply = async () => {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// All nav bar calls are now sync (no expo-navigation-bar)
|
|
49
48
|
if (config.navigationBarColor !== undefined) SystemBar.setNavigationBarColor(config.navigationBarColor);
|
|
50
|
-
if (config.navigationBarVisibility !== undefined)
|
|
51
|
-
if (config.navigationBarButtonStyle !== undefined)
|
|
52
|
-
if (config.navigationBarStyle !== undefined)
|
|
53
|
-
if (config.navigationBarBehavior !== undefined)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (config.
|
|
58
|
-
if (config.
|
|
59
|
-
if (config.
|
|
60
|
-
if (config.
|
|
61
|
-
if (config.immersiveMode !== undefined) SystemBar.immersiveMode(config.immersiveMode);
|
|
62
|
-
if (config.brightness !== undefined) SystemBar.setBrightness(config.brightness);
|
|
63
|
-
if (config.orientation !== undefined) SystemBar.setOrientation(config.orientation);
|
|
64
|
-
if (config.secureScreen !== undefined) SystemBar.setSecureScreen(config.secureScreen);
|
|
49
|
+
if (config.navigationBarVisibility !== undefined) SystemBar.setNavigationBarVisibility(config.navigationBarVisibility);
|
|
50
|
+
if (config.navigationBarButtonStyle !== undefined) SystemBar.setNavigationBarButtonStyle(config.navigationBarButtonStyle);
|
|
51
|
+
if (config.navigationBarStyle !== undefined) SystemBar.setNavigationBarStyle(config.navigationBarStyle);
|
|
52
|
+
if (config.navigationBarBehavior !== undefined) SystemBar.setNavigationBarBehavior(config.navigationBarBehavior);
|
|
53
|
+
if (config.statusBarStyle !== undefined) SystemBar.setStatusBarStyle(config.statusBarStyle);
|
|
54
|
+
if (config.statusBarVisible !== undefined) SystemBar.setStatusBarVisibility(config.statusBarVisible);
|
|
55
|
+
if (config.keepScreenOn !== undefined) SystemBar.keepScreenOn(config.keepScreenOn);
|
|
56
|
+
if (config.immersiveMode !== undefined) SystemBar.immersiveMode(config.immersiveMode);
|
|
57
|
+
if (config.brightness !== undefined) SystemBar.setBrightness(config.brightness);
|
|
58
|
+
if (config.orientation !== undefined) SystemBar.setOrientation(config.orientation);
|
|
59
|
+
if (config.secureScreen !== undefined) SystemBar.setSecureScreen(config.secureScreen);
|
|
65
60
|
};
|
|
66
61
|
|
|
67
62
|
apply().catch(() => {
|