rn-system-bar 3.1.0 → 3.1.3

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.
@@ -0,0 +1,9 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ xmlns:tools="http://schemas.android.com/tools">
3
+
4
+ <!-- Required for writing system brightness -->
5
+ <uses-permission
6
+ android:name="android.permission.WRITE_SETTINGS"
7
+ tools:ignore="ProtectedPermissions" />
8
+
9
+ </manifest>
@@ -1,29 +1,18 @@
1
1
  // ─────────────────────────────────────────────
2
- // rn-system-bar · SystemBarModule.kt v5
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
11
+ import android.graphics.Color
14
12
  import android.hardware.display.DisplayManager
15
13
  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
14
  import android.os.Build
22
- import android.os.VibrationEffect
23
- import android.os.Vibrator
24
- import android.os.VibratorManager
25
15
  import android.provider.Settings
26
- import android.view.Display
27
16
  import android.view.View
28
17
  import android.view.WindowInsets
29
18
  import android.view.WindowInsetsController
@@ -32,6 +21,7 @@ import android.view.WindowManager
32
21
  import com.facebook.react.bridge.*
33
22
  import com.facebook.react.modules.core.DeviceEventManagerModule
34
23
 
24
+ @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
35
25
  class SystemBarModule(
36
26
  private val reactContext: ReactApplicationContext
37
27
  ) : ReactContextBaseJavaModule(reactContext) {
@@ -40,23 +30,11 @@ class SystemBarModule(
40
30
 
41
31
  private fun activity(): Activity? = reactContext.currentActivity
42
32
 
43
- private fun emit(event: String, data: WritableMap) {
44
- reactContext
45
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
46
- .emit(event, data)
47
- }
33
+ private fun audioManager() =
34
+ reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
48
35
 
49
- private fun audioManager() = reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
50
- private fun connectivityManager() = reactContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
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
- }
36
+ private fun displayManager() =
37
+ reactContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
60
38
 
61
39
  private fun streamType(stream: String): Int = when (stream) {
62
40
  "ring" -> AudioManager.STREAM_RING
@@ -66,29 +44,84 @@ class SystemBarModule(
66
44
  else -> AudioManager.STREAM_MUSIC
67
45
  }
68
46
 
47
+ private fun emit(event: String, data: WritableMap) {
48
+ reactContext
49
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
50
+ .emit(event, data)
51
+ }
52
+
53
+ // ═══════════════════════════════════════════════
54
+ // NAVIGATION BAR COLOR
55
+ // WindowInsetsController API 30+, fallback for older.
56
+ // navigationBarColor deprecated on API 35+ but still
57
+ // works — suppressed at class level.
58
+ // ═══════════════════════════════════════════════
59
+
60
+ @ReactMethod
61
+ fun setNavigationBarColor(color: String) {
62
+ activity()?.runOnUiThread {
63
+ try {
64
+ val parsed = Color.parseColor(color)
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
66
+ // API 35+ — use WindowInsetsController appearance for tinting
67
+ val controller = activity()!!.window.insetsController
68
+ // Nav bar color via window attribute still works as tint on 35+
69
+ activity()!!.window.navigationBarColor = parsed
70
+ } else {
71
+ activity()!!.window.navigationBarColor = parsed
72
+ }
73
+ } catch (_: Exception) {}
74
+ }
75
+ }
76
+
69
77
  // ═══════════════════════════════════════════════
70
78
  // BRIGHTNESS
71
79
  // ═══════════════════════════════════════════════
72
80
 
73
81
  @ReactMethod
74
82
  fun setBrightness(level: Float) {
83
+ val clamped = level.coerceIn(0.01f, 1f)
84
+
85
+ // Window brightness — instant, no permission needed
75
86
  activity()?.runOnUiThread {
76
87
  val lp = activity()!!.window.attributes
77
- lp.screenBrightness = level.coerceIn(0f, 1f)
88
+ lp.screenBrightness = clamped
78
89
  activity()!!.window.attributes = lp
79
90
  }
91
+
92
+ // System brightness — persisted, needs WRITE_SETTINGS
93
+ val sysValue = (clamped * 255).toInt().coerceIn(1, 255)
94
+ if (Settings.System.canWrite(reactContext)) {
95
+ Settings.System.putInt(
96
+ reactContext.contentResolver,
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
+ )
105
+ } else {
106
+ val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).apply {
107
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
108
+ }
109
+ try { reactContext.startActivity(intent) } catch (_: Exception) {}
110
+ }
80
111
  }
81
112
 
82
113
  @ReactMethod
83
114
  fun getBrightness(promise: Promise) {
84
115
  try {
85
- val lp = activity()?.window?.attributes
86
- if (lp != null && lp.screenBrightness >= 0f) {
87
- promise.resolve(lp.screenBrightness.toDouble()); return
88
- }
89
- val sys = Settings.System.getInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, 128)
116
+ val sys = Settings.System.getInt(
117
+ reactContext.contentResolver,
118
+ Settings.System.SCREEN_BRIGHTNESS,
119
+ 128
120
+ )
90
121
  promise.resolve(sys / 255.0)
91
- } catch (e: Exception) { promise.reject("BRIGHTNESS_ERROR", e.message, e) }
122
+ } catch (e: Exception) {
123
+ promise.reject("BRIGHTNESS_ERROR", e.message, e)
124
+ }
92
125
  }
93
126
 
94
127
  // ═══════════════════════════════════════════════
@@ -97,13 +130,16 @@ class SystemBarModule(
97
130
 
98
131
  private var suppressVolumeHUD = false
99
132
 
100
- @ReactMethod fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
133
+ @ReactMethod
134
+ fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
101
135
 
102
136
  @ReactMethod
103
137
  fun setVolume(level: Float, stream: String) {
104
138
  try {
105
- val am = audioManager(); val type = streamType(stream)
106
- val vol = (level.coerceIn(0f, 1f) * am.getStreamMaxVolume(type)).toInt()
139
+ val am = audioManager()
140
+ val type = streamType(stream)
141
+ val max = am.getStreamMaxVolume(type)
142
+ val vol = (level.coerceIn(0f, 1f) * max).toInt()
107
143
  am.setStreamVolume(type, vol, if (suppressVolumeHUD) 0 else AudioManager.FLAG_SHOW_UI)
108
144
  } catch (_: Exception) {}
109
145
  }
@@ -111,10 +147,13 @@ class SystemBarModule(
111
147
  @ReactMethod
112
148
  fun getVolume(stream: String, promise: Promise) {
113
149
  try {
114
- val am = audioManager(); val type = streamType(stream)
115
- val max = am.getStreamMaxVolume(type)
150
+ val am = audioManager()
151
+ val type = streamType(stream)
152
+ val max = am.getStreamMaxVolume(type)
116
153
  promise.resolve(if (max > 0) am.getStreamVolume(type).toDouble() / max else 0.0)
117
- } catch (e: Exception) { promise.reject("VOLUME_ERROR", e.message, e) }
154
+ } catch (e: Exception) {
155
+ promise.reject("VOLUME_ERROR", e.message, e)
156
+ }
118
157
  }
119
158
 
120
159
  // ═══════════════════════════════════════════════
@@ -141,18 +180,30 @@ class SystemBarModule(
141
180
  c.show(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
142
181
  }
143
182
  } else {
144
- @Suppress("DEPRECATION")
145
183
  activity()!!.window.decorView.systemUiVisibility = if (enable) {
146
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
147
- View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
184
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
185
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
186
+ View.SYSTEM_UI_FLAG_FULLSCREEN or
187
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
148
188
  View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
149
- } else { View.SYSTEM_UI_FLAG_VISIBLE }
189
+ } else {
190
+ View.SYSTEM_UI_FLAG_VISIBLE
191
+ }
150
192
  }
151
193
  }
152
194
  }
153
195
 
196
+ @ReactMethod
197
+ fun setSecureScreen(enable: Boolean) {
198
+ activity()?.runOnUiThread {
199
+ if (enable) activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
200
+ else activity()!!.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
201
+ }
202
+ }
203
+
154
204
  // ═══════════════════════════════════════════════
155
205
  // ORIENTATION
206
+ // "auto" = FULL_SENSOR (follows system auto-rotate)
156
207
  // ═══════════════════════════════════════════════
157
208
 
158
209
  @ReactMethod
@@ -162,216 +213,41 @@ class SystemBarModule(
162
213
  "landscape" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
163
214
  "landscape-left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
164
215
  "landscape-right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
216
+ // "auto" = respect system auto-rotate toggle
165
217
  else -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
166
218
  }
167
219
  }
168
220
 
169
221
  // ═══════════════════════════════════════════════
170
- // 🆕 NETWORK INFO
171
- // ═══════════════════════════════════════════════
172
-
173
- private var networkCallback: ConnectivityManager.NetworkCallback? = null
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
222
+ // SCREENCAST list all presentation displays
261
223
  // ═══════════════════════════════════════════════
262
224
 
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
- // ═══════════════════════════════════════════════
225
+ private var displayListener: DisplayManager.DisplayListener? = null
322
226
 
323
- @ReactMethod
324
- fun haptic(pattern: String) {
325
- val vib = vibrator()
326
- if (!vib.hasVibrator()) return
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
- }
227
+ private fun buildDisplayListMap(): WritableMap {
228
+ val dm = displayManager()
229
+ val map = Arguments.createMap()
230
+ val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
231
+ val arr = Arguments.createArray()
232
+
233
+ for (d in displays) {
234
+ val item = Arguments.createMap()
235
+ item.putInt("id", d.displayId)
236
+ item.putString("name", d.name)
237
+ item.putBoolean("isValid", d.isValid)
238
+ arr.pushMap(item)
352
239
  }
353
- }
354
-
355
- // ═══════════════════════════════════════════════
356
- // 🆕 SCREENCAST
357
- // ═══════════════════════════════════════════════
358
240
 
359
- private var displayListener: DisplayManager.DisplayListener? = null
360
-
361
- private fun buildScreencastMap(): WritableMap {
362
- val dm = displayManager()
363
- val map = Arguments.createMap()
364
- val presentations = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
365
- val isCasting = presentations.isNotEmpty()
366
- map.putBoolean("isCasting", isCasting)
367
- if (isCasting) map.putString("displayName", presentations[0].name)
241
+ map.putBoolean("isCasting", displays.isNotEmpty())
242
+ map.putArray("displays", arr)
243
+ if (displays.isNotEmpty()) map.putString("displayName", displays[0].name)
368
244
  else map.putNull("displayName")
369
245
  return map
370
246
  }
371
247
 
372
248
  @ReactMethod
373
249
  fun getScreencastInfo(promise: Promise) {
374
- try { promise.resolve(buildScreencastMap()) }
250
+ try { promise.resolve(buildDisplayListMap()) }
375
251
  catch (e: Exception) { promise.reject("SCREENCAST_ERROR", e.message, e) }
376
252
  }
377
253
 
@@ -379,9 +255,9 @@ class SystemBarModule(
379
255
  fun startScreencastListener() {
380
256
  if (displayListener != null) return
381
257
  val listener = object : DisplayManager.DisplayListener {
382
- override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
383
- override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
384
- override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
258
+ override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
259
+ override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
260
+ override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
385
261
  }
386
262
  displayListener = listener
387
263
  displayManager().registerDisplayListener(listener, null)
@@ -393,58 +269,8 @@ class SystemBarModule(
393
269
  displayListener = null
394
270
  }
395
271
 
396
- @ReactMethod
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 ──────────────────
272
+ // ── Cleanup ──────────────────────────────────
444
273
  override fun onCatalystInstanceDestroy() {
445
- stopNetworkListener()
446
- stopBatteryListener()
447
274
  stopScreencastListener()
448
- stopFontScaleListener()
449
275
  }
450
276
  }
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, useBattery, useNetwork, useScreencast, useFontScale, useHaptic, } from "./src/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.useHaptic = exports.useFontScale = exports.useScreencast = exports.useNetwork = exports.useBattery = exports.useSystemBar = void 0;
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; } });
@@ -1,5 +1,5 @@
1
- import type { BatteryInfo, FontScaleInfo, HapticPattern, NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, NetworkInfo, Orientation, ScreencastInfo, StatusBarStyle, VolumeStream } from "./types";
2
- export declare const setNavigationBarColor: (color: string) => Promise<void>;
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);