rn-system-bar 3.2.1 → 3.2.2

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.
@@ -56,10 +56,7 @@ class SystemBarModule(
56
56
  // NAVIGATION BAR — 100% native, no expo dep
57
57
  // ═══════════════════════════════════════════════
58
58
 
59
- // ── Helpers for transparent / translucent bars ──────────────────────────
60
-
61
59
  private fun applyEdgeToEdgeFlags() {
62
- // Required so our colour / transparency actually shows through
63
60
  activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
64
61
  }
65
62
 
@@ -154,7 +151,6 @@ class SystemBarModule(
154
151
 
155
152
  @ReactMethod
156
153
  fun setNavigationBarStyle(style: String) {
157
- // Maps to button style — "dark"/"auto" → dark icons, "light"/"inverted" → light icons
158
154
  setNavigationBarButtonStyle(if (style == "dark" || style == "auto") "dark" else "light")
159
155
  }
160
156
 
@@ -165,11 +161,9 @@ class SystemBarModule(
165
161
  val c = activity()!!.window.insetsController ?: return@runOnUiThread
166
162
  c.systemBarsBehavior = when (behavior) {
167
163
  "overlay-swipe" -> WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
168
- // inset modes — default persistent behavior
169
164
  else -> WindowInsetsController.BEHAVIOR_DEFAULT
170
165
  }
171
166
  }
172
- // API < 30: IMMERSIVE_STICKY is closest — no-op otherwise
173
167
  }
174
168
  }
175
169
 
@@ -288,14 +282,12 @@ class SystemBarModule(
288
282
  }
289
283
 
290
284
  // ── Realtime brightness listener ─────────────
291
- private var brightnessReceiver: BroadcastReceiver? = null
292
285
  private var brightnessPollingRunnable: Runnable? = null
293
286
  private var lastBrightness = -1
294
287
 
295
288
  @ReactMethod
296
289
  fun startBrightnessListener() {
297
290
  if (brightnessPollingRunnable != null) return
298
- // Settings.System doesn't broadcast changes — poll every 500ms
299
291
  val runnable = object : Runnable {
300
292
  override fun run() {
301
293
  try {
@@ -430,20 +422,15 @@ class SystemBarModule(
430
422
 
431
423
  @ReactMethod
432
424
  fun setOrientation(mode: String) {
433
- // "auto" → always unspecified (follow system auto-rotate toggle unconditionally)
434
425
  if (mode == "auto") {
435
426
  activity()?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
436
427
  return
437
428
  }
438
429
 
439
- // For a specific orientation: check if the device's auto-rotate toggle is ON.
440
- // auto-rotate ON → use sensor-based variant so the device can still rotate
441
- // within the requested axis (portrait ↔ reverse-portrait, etc.)
442
- // auto-rotate OFF → hard-lock to the exact orientation requested
443
430
  val autoRotateOn = Settings.System.getInt(
444
431
  reactContext.contentResolver,
445
432
  Settings.System.ACCELEROMETER_ROTATION,
446
- 0 // default = locked (off)
433
+ 0
447
434
  ) == 1
448
435
 
449
436
  activity()?.requestedOrientation = when (mode) {
@@ -456,7 +443,6 @@ class SystemBarModule(
456
443
  else ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
457
444
 
458
445
  "landscape-left" ->
459
- // Reverse landscape — no sensor variant; honour toggle by using full sensor landscape
460
446
  if (autoRotateOn) ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
461
447
  else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
462
448
 
@@ -470,7 +456,6 @@ class SystemBarModule(
470
456
 
471
457
  // ═══════════════════════════════════════════════
472
458
  // SYSTEM SCREENCAST (external display / HDMI / Miracast)
473
- // Detects OS-level external displays via DisplayManager.
474
459
  // ═══════════════════════════════════════════════
475
460
 
476
461
  private var displayListener: DisplayManager.DisplayListener? = null
@@ -520,42 +505,41 @@ class SystemBarModule(
520
505
 
521
506
  // ═══════════════════════════════════════════════
522
507
  // APP-ONLY CAST (MediaRouter — Chromecast / TV)
523
- //
524
- // Casts ONLY this app's screen — not the whole system.
525
- // Uses android.media.MediaRouter (API 16+, no Play Services required).
526
- //
527
- // Flow:
528
- // startAppCastScan() → devices arrive via SystemBar_AppCastChange
529
- // connectAppCast(deviceId) → state = "connecting" then "connected"
530
- // disconnectAppCast() → state = "disconnecting" then "idle"
531
508
  // ═══════════════════════════════════════════════
532
509
 
533
510
  private val mediaRouter: android.media.MediaRouter by lazy {
534
511
  reactContext.getSystemService(Context.MEDIA_ROUTER_SERVICE) as android.media.MediaRouter
535
512
  }
536
513
 
537
- // Current state
538
514
  private var appCastState = "idle"
539
515
  private var connectedRoute: android.media.MediaRouter.RouteInfo? = null
540
516
  private var appCastCallback: android.media.MediaRouter.Callback? = null
541
-
542
- // All routes discovered during the current scan (id → route)
543
517
  private val discoveredRoutes = mutableMapOf<String, android.media.MediaRouter.RouteInfo>()
544
518
 
545
- // ── Serialisation helpers ──────────────────
519
+ // ── Helpers ───────────────────────────────────
546
520
 
547
521
  private fun routeId(r: android.media.MediaRouter.RouteInfo): String =
548
522
  r.name?.toString()?.replace(" ", "_")?.plus("_${r.hashCode()}") ?: r.hashCode().toString()
549
523
 
524
+ /**
525
+ * Replaces the removed RouteInfo.isDefault property (removed in API 34+).
526
+ * The default route is the built-in phone speaker / earpiece.
527
+ * We identify it by checking that it only supports audio (not video/live-video)
528
+ * and that it carries no presentation display.
529
+ */
530
+ private fun android.media.MediaRouter.RouteInfo.isDefaultRoute(): Boolean {
531
+ val videoMask = android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO or
532
+ android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY
533
+ return (supportedTypes and videoMask) == 0
534
+ }
535
+
550
536
  private fun routeToMap(r: android.media.MediaRouter.RouteInfo): WritableMap {
551
537
  val m = Arguments.createMap()
552
538
  m.putString("id", routeId(r))
553
539
  m.putString("name", r.name?.toString() ?: "Unknown")
554
540
  val desc = r.description?.toString()
555
541
  if (desc != null) m.putString("description", desc) else m.putNull("description")
556
- // MediaRouter doesn't expose signal strength — set null
557
542
  m.putNull("signalStrength")
558
- // Consider a device to require pairing when its status matches "connecting"
559
543
  m.putBoolean("requiresPairing", false)
560
544
  return m
561
545
  }
@@ -582,81 +566,96 @@ class SystemBarModule(
582
566
  emit("SystemBar_AppCastChange", map)
583
567
  }
584
568
 
585
- // ── Scan ──────────────────────────────────
569
+ // ── Scan ──────────────────────────────────────
586
570
 
587
571
  @ReactMethod
588
572
  fun startAppCastScan() {
589
- if (appCastCallback != null) return // already scanning
573
+ if (appCastCallback != null) return
590
574
  appCastState = "scanning"
591
575
  discoveredRoutes.clear()
592
576
 
593
- val cb = object : android.media.MediaRouter.Callback() {
577
+ val cb = object : android.media.MediaRouter.Callback() {
594
578
 
595
- private fun android.media.MediaRouter.RouteInfo.isDefaultRoute(): Boolean =
596
- (supportedTypes and android.media.MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0 &&
597
- name?.toString()?.lowercase()?.contains("phone") == true
579
+ override fun onRouteAdded(
580
+ router: android.media.MediaRouter,
581
+ route: android.media.MediaRouter.RouteInfo
582
+ ) {
583
+ if (route.isDefaultRoute()) return
584
+ val id = routeId(route)
585
+ discoveredRoutes[id] = route
586
+ emitAppCastChange()
587
+ }
598
588
 
599
- override fun onRouteAdded(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
600
- if (route.isDefaultRoute()) return
601
- val id = routeId(route)
602
- discoveredRoutes[id] = route
603
- emitAppCastChange()
604
- }
589
+ override fun onRouteRemoved(
590
+ router: android.media.MediaRouter,
591
+ route: android.media.MediaRouter.RouteInfo
592
+ ) {
593
+ discoveredRoutes.remove(routeId(route))
594
+ if (connectedRoute?.let { routeId(it) } == routeId(route)) {
595
+ connectedRoute = null
596
+ appCastState = "idle"
597
+ }
598
+ emitAppCastChange()
599
+ }
605
600
 
606
- override fun onRouteRemoved(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
607
- discoveredRoutes.remove(routeId(route))
608
- if (connectedRoute?.let { routeId(it) } == routeId(route)) {
609
- connectedRoute = null
610
- appCastState = "idle"
611
- }
612
- emitAppCastChange()
613
- }
601
+ override fun onRouteChanged(
602
+ router: android.media.MediaRouter,
603
+ route: android.media.MediaRouter.RouteInfo
604
+ ) {
605
+ val id = routeId(route)
606
+ if (!route.isDefaultRoute()) discoveredRoutes[id] = route
607
+ emitAppCastChange()
608
+ }
614
609
 
615
- override fun onRouteChanged(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
616
- val id = routeId(route)
617
- if (!route.isDefaultRoute()) discoveredRoutes[id] = route
618
- emitAppCastChange()
619
- }
610
+ override fun onRouteSelected(
611
+ router: android.media.MediaRouter,
612
+ type: Int,
613
+ route: android.media.MediaRouter.RouteInfo
614
+ ) {
615
+ if (route.isDefaultRoute()) return
616
+ connectedRoute = route
617
+ appCastState = "connected"
618
+ emitAppCastChange()
619
+ }
620
620
 
621
- override fun onRouteSelected(router: android.media.MediaRouter, type: Int, route: android.media.MediaRouter.RouteInfo) {
622
- if (route.isDefaultRoute()) return
623
- connectedRoute = route
624
- appCastState = "connected"
625
- emitAppCastChange()
626
- }
621
+ override fun onRouteUnselected(
622
+ router: android.media.MediaRouter,
623
+ type: Int,
624
+ route: android.media.MediaRouter.RouteInfo
625
+ ) {
626
+ if (appCastState != "idle") {
627
+ connectedRoute = null
628
+ appCastState = "idle"
629
+ emitAppCastChange()
630
+ }
631
+ }
627
632
 
628
- override fun onRouteUnselected(router: android.media.MediaRouter, type: Int, route: android.media.MediaRouter.RouteInfo) {
629
- if (appCastState != "idle") {
630
- connectedRoute = null
631
- appCastState = "idle"
632
- emitAppCastChange()
633
+ // ── Required abstract stubs (no-op) ───────
634
+ override fun onRouteGrouped(
635
+ router: android.media.MediaRouter,
636
+ route: android.media.MediaRouter.RouteInfo,
637
+ group: android.media.MediaRouter.RouteGroup,
638
+ index: Int
639
+ ) {}
640
+
641
+ override fun onRouteUngrouped(
642
+ router: android.media.MediaRouter,
643
+ route: android.media.MediaRouter.RouteInfo,
644
+ group: android.media.MediaRouter.RouteGroup
645
+ ) {}
646
+
647
+ override fun onRouteVolumeChanged(
648
+ router: android.media.MediaRouter,
649
+ route: android.media.MediaRouter.RouteInfo
650
+ ) {}
633
651
  }
634
- }
635
652
 
636
- // ── Required abstract stubs ──────────────────────────────────────────
637
- override fun onRouteGrouped(
638
- router: android.media.MediaRouter,
639
- route: android.media.MediaRouter.RouteInfo,
640
- group: android.media.MediaRouter.RouteGroup,
641
- index: Int
642
- ) {}
643
-
644
- override fun onRouteUngrouped(
645
- router: android.media.MediaRouter,
646
- route: android.media.MediaRouter.RouteInfo,
647
- group: android.media.MediaRouter.RouteGroup
648
- ) {}
649
-
650
- override fun onRouteVolumeChanged(
651
- router: android.media.MediaRouter,
652
- route: android.media.MediaRouter.RouteInfo
653
- ) {}
654
- }
655
653
  appCastCallback = cb
656
654
  mainHandler.post {
657
655
  mediaRouter.addCallback(
658
656
  android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO,
659
657
  cb,
658
+ // CALLBACK_FLAG_REQUEST_DISCOVERY is typed as BigInteger in newer SDK bindings
660
659
  android.media.MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY.toInt() or
661
660
  android.media.MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN.toInt()
662
661
  )
@@ -674,7 +673,7 @@ class SystemBarModule(
674
673
  }
675
674
  }
676
675
 
677
- // ── Connect / Disconnect ──────────────────
676
+ // ── Connect / Disconnect ──────────────────────
678
677
 
679
678
  @ReactMethod
680
679
  fun connectAppCast(deviceId: String, pairingPin: String?) {
@@ -693,7 +692,6 @@ class SystemBarModule(
693
692
  mainHandler.post {
694
693
  try {
695
694
  mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, route)
696
- // onRouteSelected callback will set state = "connected" and re-emit
697
695
  } catch (e: Exception) {
698
696
  appCastState = "idle"
699
697
  emitAppCastChange(error = e.message ?: "Connection failed")
@@ -708,7 +706,6 @@ class SystemBarModule(
708
706
  emitAppCastChange()
709
707
  mainHandler.post {
710
708
  try {
711
- // Select the default (phone) route to stop remote casting
712
709
  val defaultRoute = mediaRouter.getDefaultRoute()
713
710
  mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, defaultRoute)
714
711
  connectedRoute = null
@@ -727,7 +724,7 @@ class SystemBarModule(
727
724
  catch (e: Exception) { promise.reject("APP_CAST_ERROR", e.message, e) }
728
725
  }
729
726
 
730
- // ── Cleanup ──────────────────────────────────
727
+ // ── Cleanup ───────────────────────────────────
731
728
  override fun onCatalystInstanceDestroy() {
732
729
  stopBrightnessListener()
733
730
  stopVolumeListener()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-system-bar",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "description": "Control Android & iOS system bars, brightness, volume, orientation and screen flags from React Native.",
5
5
  "main": "lib/index.js",
6
6
  "react-native": "lib/index.js",