rn-system-bar 3.2.1 → 3.2.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.
|
@@ -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
|
|
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
|
-
// ──
|
|
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,83 +566,98 @@ 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
|
|
573
|
+
if (appCastCallback != null) return
|
|
590
574
|
appCastState = "scanning"
|
|
591
575
|
discoveredRoutes.clear()
|
|
592
576
|
|
|
593
|
-
|
|
577
|
+
val cb = object : android.media.MediaRouter.Callback() {
|
|
594
578
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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,
|
|
660
|
-
|
|
661
|
-
|
|
658
|
+
// CALLBACK_FLAG_REQUEST_DISCOVERY (0x4) and CALLBACK_FLAG_PERFORM_ACTIVE_SCAN (0x2)
|
|
659
|
+
// were removed from the Android SDK — use their raw int values directly.
|
|
660
|
+
0x4 or 0x2
|
|
662
661
|
)
|
|
663
662
|
}
|
|
664
663
|
emitAppCastChange()
|
|
@@ -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