rn-system-bar 3.2.2 → 3.2.4
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/README.md +180 -650
- package/android/src/main/java/com/systembar/SystemBarModule.kt +0 -222
- package/index.ts +3 -2
- package/ios/SystemBarModule.m +1 -8
- package/ios/SystemBarModule.swift +55 -68
- package/lib/index.d.ts +2 -2
- package/lib/index.js +4 -2
- package/lib/src/SystemBar.d.ts +33 -64
- package/lib/src/SystemBar.js +95 -131
- package/lib/src/types.d.ts +43 -43
- package/lib/src/useSystemBar.d.ts +25 -24
- package/lib/src/useSystemBar.js +101 -69
- package/package.json +1 -1
- package/src/SystemBar.ts +111 -128
- package/src/types.ts +68 -51
- package/src/useSystemBar.ts +106 -135
|
@@ -503,232 +503,10 @@ class SystemBarModule(
|
|
|
503
503
|
displayListener = null
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
-
// ═══════════════════════════════════════════════
|
|
507
|
-
// APP-ONLY CAST (MediaRouter — Chromecast / TV)
|
|
508
|
-
// ═══════════════════════════════════════════════
|
|
509
|
-
|
|
510
|
-
private val mediaRouter: android.media.MediaRouter by lazy {
|
|
511
|
-
reactContext.getSystemService(Context.MEDIA_ROUTER_SERVICE) as android.media.MediaRouter
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
private var appCastState = "idle"
|
|
515
|
-
private var connectedRoute: android.media.MediaRouter.RouteInfo? = null
|
|
516
|
-
private var appCastCallback: android.media.MediaRouter.Callback? = null
|
|
517
|
-
private val discoveredRoutes = mutableMapOf<String, android.media.MediaRouter.RouteInfo>()
|
|
518
|
-
|
|
519
|
-
// ── Helpers ───────────────────────────────────
|
|
520
|
-
|
|
521
|
-
private fun routeId(r: android.media.MediaRouter.RouteInfo): String =
|
|
522
|
-
r.name?.toString()?.replace(" ", "_")?.plus("_${r.hashCode()}") ?: r.hashCode().toString()
|
|
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
|
-
|
|
536
|
-
private fun routeToMap(r: android.media.MediaRouter.RouteInfo): WritableMap {
|
|
537
|
-
val m = Arguments.createMap()
|
|
538
|
-
m.putString("id", routeId(r))
|
|
539
|
-
m.putString("name", r.name?.toString() ?: "Unknown")
|
|
540
|
-
val desc = r.description?.toString()
|
|
541
|
-
if (desc != null) m.putString("description", desc) else m.putNull("description")
|
|
542
|
-
m.putNull("signalStrength")
|
|
543
|
-
m.putBoolean("requiresPairing", false)
|
|
544
|
-
return m
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
private fun buildAppCastMap(): WritableMap {
|
|
548
|
-
val map = Arguments.createMap()
|
|
549
|
-
map.putString("state", appCastState)
|
|
550
|
-
map.putNull("error")
|
|
551
|
-
|
|
552
|
-
val devArr = Arguments.createArray()
|
|
553
|
-
for (r in discoveredRoutes.values) devArr.pushMap(routeToMap(r))
|
|
554
|
-
map.putArray("devices", devArr)
|
|
555
|
-
|
|
556
|
-
val connected = connectedRoute
|
|
557
|
-
if (connected != null) map.putMap("connectedDevice", routeToMap(connected))
|
|
558
|
-
else map.putNull("connectedDevice")
|
|
559
|
-
|
|
560
|
-
return map
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
private fun emitAppCastChange(error: String? = null) {
|
|
564
|
-
val map = buildAppCastMap()
|
|
565
|
-
if (error != null) map.putString("error", error)
|
|
566
|
-
emit("SystemBar_AppCastChange", map)
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// ── Scan ──────────────────────────────────────
|
|
570
|
-
|
|
571
|
-
@ReactMethod
|
|
572
|
-
fun startAppCastScan() {
|
|
573
|
-
if (appCastCallback != null) return
|
|
574
|
-
appCastState = "scanning"
|
|
575
|
-
discoveredRoutes.clear()
|
|
576
|
-
|
|
577
|
-
val cb = object : android.media.MediaRouter.Callback() {
|
|
578
|
-
|
|
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
|
-
}
|
|
588
|
-
|
|
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
|
-
}
|
|
600
|
-
|
|
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
|
-
}
|
|
609
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
) {}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
appCastCallback = cb
|
|
654
|
-
mainHandler.post {
|
|
655
|
-
mediaRouter.addCallback(
|
|
656
|
-
android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO,
|
|
657
|
-
cb,
|
|
658
|
-
// CALLBACK_FLAG_REQUEST_DISCOVERY is typed as BigInteger in newer SDK bindings
|
|
659
|
-
android.media.MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY.toInt() or
|
|
660
|
-
android.media.MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN.toInt()
|
|
661
|
-
)
|
|
662
|
-
}
|
|
663
|
-
emitAppCastChange()
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
@ReactMethod
|
|
667
|
-
fun stopAppCastScan() {
|
|
668
|
-
appCastCallback?.let { mainHandler.post { mediaRouter.removeCallback(it) } }
|
|
669
|
-
appCastCallback = null
|
|
670
|
-
if (appCastState == "scanning") {
|
|
671
|
-
appCastState = "idle"
|
|
672
|
-
emitAppCastChange()
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// ── Connect / Disconnect ──────────────────────
|
|
677
|
-
|
|
678
|
-
@ReactMethod
|
|
679
|
-
fun connectAppCast(deviceId: String, pairingPin: String?) {
|
|
680
|
-
val route = discoveredRoutes[deviceId]
|
|
681
|
-
if (route == null) {
|
|
682
|
-
val err = Arguments.createMap()
|
|
683
|
-
err.putString("state", appCastState)
|
|
684
|
-
err.putString("error", "Device not found: $deviceId")
|
|
685
|
-
err.putNull("connectedDevice")
|
|
686
|
-
err.putArray("devices", Arguments.createArray())
|
|
687
|
-
emit("SystemBar_AppCastChange", err)
|
|
688
|
-
return
|
|
689
|
-
}
|
|
690
|
-
appCastState = "connecting"
|
|
691
|
-
emitAppCastChange()
|
|
692
|
-
mainHandler.post {
|
|
693
|
-
try {
|
|
694
|
-
mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, route)
|
|
695
|
-
} catch (e: Exception) {
|
|
696
|
-
appCastState = "idle"
|
|
697
|
-
emitAppCastChange(error = e.message ?: "Connection failed")
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
@ReactMethod
|
|
703
|
-
fun disconnectAppCast() {
|
|
704
|
-
if (connectedRoute == null) return
|
|
705
|
-
appCastState = "disconnecting"
|
|
706
|
-
emitAppCastChange()
|
|
707
|
-
mainHandler.post {
|
|
708
|
-
try {
|
|
709
|
-
val defaultRoute = mediaRouter.getDefaultRoute()
|
|
710
|
-
mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, defaultRoute)
|
|
711
|
-
connectedRoute = null
|
|
712
|
-
appCastState = "idle"
|
|
713
|
-
emitAppCastChange()
|
|
714
|
-
} catch (e: Exception) {
|
|
715
|
-
appCastState = "idle"
|
|
716
|
-
emitAppCastChange(error = e.message ?: "Disconnect failed")
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
@ReactMethod
|
|
722
|
-
fun getAppCastInfo(promise: Promise) {
|
|
723
|
-
try { promise.resolve(buildAppCastMap()) }
|
|
724
|
-
catch (e: Exception) { promise.reject("APP_CAST_ERROR", e.message, e) }
|
|
725
|
-
}
|
|
726
|
-
|
|
727
506
|
// ── Cleanup ───────────────────────────────────
|
|
728
507
|
override fun onCatalystInstanceDestroy() {
|
|
729
508
|
stopBrightnessListener()
|
|
730
509
|
stopVolumeListener()
|
|
731
510
|
stopSystemScreencastListener()
|
|
732
|
-
stopAppCastScan()
|
|
733
511
|
}
|
|
734
512
|
}
|
package/index.ts
CHANGED
|
@@ -13,7 +13,9 @@ export { setGlobalThemeMode, useTheme } from "./src/useTheme";
|
|
|
13
13
|
|
|
14
14
|
// React hooks
|
|
15
15
|
export {
|
|
16
|
-
|
|
16
|
+
useBattery,
|
|
17
|
+
useFontScale,
|
|
18
|
+
useNetwork,
|
|
17
19
|
useScreencast,
|
|
18
20
|
useSystemBar,
|
|
19
21
|
useSystemScreencast,
|
|
@@ -25,5 +27,4 @@ export {
|
|
|
25
27
|
export type {
|
|
26
28
|
SystemBarConfig,
|
|
27
29
|
ThemedSystemBarConfig,
|
|
28
|
-
UseAppCastReturn,
|
|
29
30
|
} from "./src/useSystemBar";
|
package/ios/SystemBarModule.m
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · SystemBarModule.m
|
|
2
|
+
// rn-system-bar · SystemBarModule.m
|
|
3
3
|
// Objective-C bridge — exposes Swift to React Native
|
|
4
4
|
// ─────────────────────────────────────────────
|
|
5
5
|
|
|
@@ -41,13 +41,6 @@ RCT_EXTERN_METHOD(getSystemScreencastInfo:(RCTPromiseResolveBlock)resolve reject
|
|
|
41
41
|
RCT_EXTERN_METHOD(startSystemScreencastListener)
|
|
42
42
|
RCT_EXTERN_METHOD(stopSystemScreencastListener)
|
|
43
43
|
|
|
44
|
-
// App-only Cast (Android MediaRouter — stubs on iOS)
|
|
45
|
-
RCT_EXTERN_METHOD(startAppCastScan)
|
|
46
|
-
RCT_EXTERN_METHOD(stopAppCastScan)
|
|
47
|
-
RCT_EXTERN_METHOD(connectAppCast:(NSString *)deviceId pairingPin:(NSString *)pairingPin)
|
|
48
|
-
RCT_EXTERN_METHOD(disconnectAppCast)
|
|
49
|
-
RCT_EXTERN_METHOD(getAppCastInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
50
|
-
|
|
51
44
|
// Font Scale
|
|
52
45
|
RCT_EXTERN_METHOD(getFontScaleInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
53
46
|
RCT_EXTERN_METHOD(startFontScaleListener)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────
|
|
2
|
-
// rn-system-bar · SystemBarModule.swift
|
|
3
|
-
// New: Network, Battery, Haptics, Screencast, FontScale
|
|
2
|
+
// rn-system-bar · SystemBarModule.swift
|
|
4
3
|
// ─────────────────────────────────────────────
|
|
5
4
|
|
|
6
5
|
import Foundation
|
|
@@ -18,7 +17,7 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
18
17
|
return [
|
|
19
18
|
"SystemBar_NetworkChange",
|
|
20
19
|
"SystemBar_BatteryChange",
|
|
21
|
-
"
|
|
20
|
+
"SystemBar_SystemScreencastChange",
|
|
22
21
|
"SystemBar_FontScaleChange",
|
|
23
22
|
]
|
|
24
23
|
}
|
|
@@ -77,6 +76,9 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
77
76
|
DispatchQueue.main.async { UIApplication.shared.isIdleTimerDisabled = enable }
|
|
78
77
|
}
|
|
79
78
|
|
|
79
|
+
// setSecureScreen — iOS doesn't support this via public API (no-op)
|
|
80
|
+
@objc func setSecureScreen(_ enable: Bool) {}
|
|
81
|
+
|
|
80
82
|
// ═══════════════════════════════════════════════
|
|
81
83
|
// ORIENTATION
|
|
82
84
|
// ═══════════════════════════════════════════════
|
|
@@ -85,11 +87,11 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
85
87
|
DispatchQueue.main.async {
|
|
86
88
|
var mask: UIInterfaceOrientationMask
|
|
87
89
|
switch mode {
|
|
88
|
-
case "portrait":
|
|
89
|
-
case "landscape":
|
|
90
|
-
case "landscape-left":
|
|
91
|
-
case "landscape-right":mask = .landscapeRight
|
|
92
|
-
default:
|
|
90
|
+
case "portrait": mask = .portrait
|
|
91
|
+
case "landscape": mask = .landscape
|
|
92
|
+
case "landscape-left": mask = .landscapeLeft
|
|
93
|
+
case "landscape-right": mask = .landscapeRight
|
|
94
|
+
default: mask = .all
|
|
93
95
|
}
|
|
94
96
|
if #available(iOS 16.0, *) {
|
|
95
97
|
(UIApplication.shared.connectedScenes.first as? UIWindowScene)?
|
|
@@ -101,14 +103,13 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
// ═══════════════════════════════════════════════
|
|
104
|
-
//
|
|
106
|
+
// NETWORK INFO
|
|
105
107
|
// ═══════════════════════════════════════════════
|
|
106
108
|
|
|
107
109
|
private func currentNetworkMap() -> [String: Any] {
|
|
108
110
|
var isConnected = false
|
|
109
111
|
var type = "unknown"
|
|
110
112
|
|
|
111
|
-
// Use NWPathMonitor snapshot via reachability
|
|
112
113
|
if let reachability = SCNetworkReachabilityCreateWithName(nil, "apple.com") {
|
|
113
114
|
var flags: SCNetworkReachabilityFlags = []
|
|
114
115
|
SCNetworkReachabilityGetFlags(reachability, &flags)
|
|
@@ -118,13 +119,12 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
118
119
|
else { type = "none" }
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
let airplaneMode = false // iOS doesn't expose this to apps
|
|
122
122
|
return [
|
|
123
|
-
"type":
|
|
124
|
-
"isConnected":
|
|
125
|
-
"isAirplaneMode":
|
|
126
|
-
"ssid":
|
|
127
|
-
"cellularGeneration":
|
|
123
|
+
"type": type,
|
|
124
|
+
"isConnected": isConnected,
|
|
125
|
+
"isAirplaneMode": false, // iOS does not expose this to apps
|
|
126
|
+
"ssid": NSNull(),
|
|
127
|
+
"cellularGeneration": NSNull(),
|
|
128
128
|
]
|
|
129
129
|
}
|
|
130
130
|
|
|
@@ -145,23 +145,24 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
@objc func stopNetworkListener() {
|
|
148
|
-
networkMonitor?.cancel()
|
|
148
|
+
networkMonitor?.cancel()
|
|
149
|
+
networkMonitor = nil
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
// ═══════════════════════════════════════════════
|
|
152
|
-
//
|
|
153
|
+
// BATTERY
|
|
153
154
|
// ═══════════════════════════════════════════════
|
|
154
155
|
|
|
155
156
|
private func currentBatteryMap() -> [String: Any] {
|
|
156
157
|
UIDevice.current.isBatteryMonitoringEnabled = true
|
|
157
|
-
let raw = UIDevice.current.batteryLevel
|
|
158
|
+
let raw = UIDevice.current.batteryLevel // -1 if unknown
|
|
158
159
|
let level = raw >= 0 ? Int(raw * 100) : -1
|
|
159
160
|
let state: String
|
|
160
161
|
switch UIDevice.current.batteryState {
|
|
161
|
-
case .charging:
|
|
162
|
-
case .full:
|
|
163
|
-
case .unplugged:
|
|
164
|
-
default:
|
|
162
|
+
case .charging: state = "charging"
|
|
163
|
+
case .full: state = "full"
|
|
164
|
+
case .unplugged: state = "discharging"
|
|
165
|
+
default: state = "unknown"
|
|
165
166
|
}
|
|
166
167
|
let isCharging = state == "charging" || state == "full"
|
|
167
168
|
return [
|
|
@@ -183,10 +184,12 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
183
184
|
UIDevice.current.isBatteryMonitoringEnabled = true
|
|
184
185
|
let nc = NotificationCenter.default
|
|
185
186
|
batteryObservers = [
|
|
186
|
-
nc.addObserver(forName: UIDevice.batteryLevelDidChangeNotification,
|
|
187
|
+
nc.addObserver(forName: UIDevice.batteryLevelDidChangeNotification,
|
|
188
|
+
object: nil, queue: .main) { [weak self] _ in
|
|
187
189
|
self?.emit("SystemBar_BatteryChange", body: self?.currentBatteryMap() ?? [:])
|
|
188
190
|
},
|
|
189
|
-
nc.addObserver(forName: UIDevice.batteryStateDidChangeNotification,
|
|
191
|
+
nc.addObserver(forName: UIDevice.batteryStateDidChangeNotification,
|
|
192
|
+
object: nil, queue: .main) { [weak self] _ in
|
|
190
193
|
self?.emit("SystemBar_BatteryChange", body: self?.currentBatteryMap() ?? [:])
|
|
191
194
|
},
|
|
192
195
|
]
|
|
@@ -199,7 +202,7 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
// ═══════════════════════════════════════════════
|
|
202
|
-
//
|
|
205
|
+
// HAPTIC FEEDBACK
|
|
203
206
|
// ═══════════════════════════════════════════════
|
|
204
207
|
|
|
205
208
|
@objc func haptic(_ pattern: String) {
|
|
@@ -226,8 +229,7 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
226
229
|
}
|
|
227
230
|
|
|
228
231
|
// ═══════════════════════════════════════════════
|
|
229
|
-
//
|
|
230
|
-
// Detects OS-level external screens via UIScreen.
|
|
232
|
+
// SYSTEM SCREENCAST (external display / AirPlay mirror)
|
|
231
233
|
// ═══════════════════════════════════════════════
|
|
232
234
|
|
|
233
235
|
private func currentSystemScreencastMap() -> [String: Any] {
|
|
@@ -251,7 +253,8 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
251
253
|
screencastObserver = NotificationCenter.default.addObserver(
|
|
252
254
|
forName: UIScreen.didConnectNotification, object: nil, queue: .main
|
|
253
255
|
) { [weak self] _ in
|
|
254
|
-
self?.emit("SystemBar_SystemScreencastChange",
|
|
256
|
+
self?.emit("SystemBar_SystemScreencastChange",
|
|
257
|
+
body: self?.currentSystemScreencastMap() ?? [:])
|
|
255
258
|
}
|
|
256
259
|
}
|
|
257
260
|
|
|
@@ -261,45 +264,30 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
// ═══════════════════════════════════════════════
|
|
264
|
-
//
|
|
265
|
-
// AirPlay is system-managed on iOS; no public API for app-only casting.
|
|
266
|
-
// ═══════════════════════════════════════════════
|
|
267
|
-
|
|
268
|
-
@objc func startAppCastScan() {
|
|
269
|
-
// iOS: AirPlay is system-managed — no-op. Emit idle state.
|
|
270
|
-
emit("SystemBar_AppCastChange", body: [
|
|
271
|
-
"state": "idle", "devices": [], "connectedDevice": NSNull(), "error": NSNull()
|
|
272
|
-
])
|
|
273
|
-
}
|
|
274
|
-
@objc func stopAppCastScan() {}
|
|
275
|
-
|
|
276
|
-
@objc func connectAppCast(_ deviceId: String, pairingPin: String?) {
|
|
277
|
-
emit("SystemBar_AppCastChange", body: [
|
|
278
|
-
"state": "idle", "devices": [], "connectedDevice": NSNull(),
|
|
279
|
-
"error": "App-only cast not supported on iOS"
|
|
280
|
-
])
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
@objc func disconnectAppCast() {}
|
|
284
|
-
|
|
285
|
-
@objc
|
|
286
|
-
func getAppCastInfo(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
287
|
-
rejecter reject: RCTPromiseRejectBlock) {
|
|
288
|
-
resolve([
|
|
289
|
-
"state": "idle", "devices": [], "connectedDevice": NSNull(), "error": NSNull()
|
|
290
|
-
])
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// setSecureScreen — iOS doesn't support this via public API (no-op)
|
|
294
|
-
@objc func setSecureScreen(_ enable: Bool) {}
|
|
295
|
-
|
|
296
|
-
// ═══════════════════════════════════════════════
|
|
297
|
-
// 🆕 FONT SCALE
|
|
267
|
+
// FONT SCALE
|
|
298
268
|
// ═══════════════════════════════════════════════
|
|
299
269
|
|
|
300
270
|
private func currentFontScaleMap() -> [String: Any] {
|
|
271
|
+
let category = UIApplication.shared.preferredContentSizeCategory
|
|
272
|
+
// Map Apple's content size category to a numeric scale factor
|
|
273
|
+
let scale: Double
|
|
274
|
+
switch category {
|
|
275
|
+
case .extraSmall: scale = 0.82
|
|
276
|
+
case .small: scale = 0.88
|
|
277
|
+
case .medium: scale = 0.94
|
|
278
|
+
case .large: scale = 1.00 // default
|
|
279
|
+
case .extraLarge: scale = 1.12
|
|
280
|
+
case .extraExtraLarge: scale = 1.24
|
|
281
|
+
case .extraExtraExtraLarge: scale = 1.35
|
|
282
|
+
case .accessibilityMedium: scale = 1.60
|
|
283
|
+
case .accessibilityLarge: scale = 1.90
|
|
284
|
+
case .accessibilityExtraLarge: scale = 2.35
|
|
285
|
+
case .accessibilityExtraExtraLarge: scale = 2.75
|
|
286
|
+
case .accessibilityExtraExtraExtraLarge: scale = 3.12
|
|
287
|
+
default: scale = 1.00
|
|
288
|
+
}
|
|
301
289
|
return [
|
|
302
|
-
"fontScale":
|
|
290
|
+
"fontScale": scale,
|
|
303
291
|
"density": Double(UIScreen.main.scale),
|
|
304
292
|
]
|
|
305
293
|
}
|
|
@@ -323,16 +311,15 @@ class SystemBarModule: RCTEventEmitter {
|
|
|
323
311
|
fontScaleObserver = nil
|
|
324
312
|
}
|
|
325
313
|
|
|
326
|
-
// ── Android-only stubs
|
|
314
|
+
// ── Android-only stubs (no-op on iOS) ─────────
|
|
327
315
|
@objc func setNavigationBarColor(_ color: String) {}
|
|
328
316
|
@objc func setNavigationBarVisibility(_ mode: String) {}
|
|
329
317
|
@objc func setNavigationBarButtonStyle(_ style: String) {}
|
|
330
318
|
@objc func setNavigationBarStyle(_ style: String) {}
|
|
331
319
|
@objc func setNavigationBarBehavior(_ behavior: String) {}
|
|
332
|
-
// color: hex | "transparent" | "translucent" — Android only
|
|
333
320
|
@objc func setStatusBarColor(_ color: String) {}
|
|
334
|
-
@objc func setVolumeHUDVisible(_ visible: Bool) {}
|
|
335
|
-
@objc func immersiveMode(_ enable: Bool) {}
|
|
336
321
|
@objc func setStatusBarStyle(_ style: String) {}
|
|
337
322
|
@objc func setStatusBarVisibility(_ visible: Bool) {}
|
|
323
|
+
@objc func setVolumeHUDVisible(_ visible: Bool) {}
|
|
324
|
+
@objc func immersiveMode(_ enable: Bool) {}
|
|
338
325
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./src/SystemBar";
|
|
2
2
|
export * from "./src/types";
|
|
3
3
|
export { setGlobalThemeMode, useTheme } from "./src/useTheme";
|
|
4
|
-
export {
|
|
5
|
-
export type { SystemBarConfig, ThemedSystemBarConfig,
|
|
4
|
+
export { useBattery, useFontScale, useNetwork, useScreencast, useSystemBar, useSystemScreencast, useTheme as useThemeHook, useThemeSystemBar, } from "./src/useSystemBar";
|
|
5
|
+
export type { SystemBarConfig, ThemedSystemBarConfig, } from "./src/useSystemBar";
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,7 @@ 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.useThemeSystemBar = exports.useThemeHook = exports.useSystemScreencast = exports.useSystemBar = exports.useScreencast = exports.
|
|
20
|
+
exports.useThemeSystemBar = exports.useThemeHook = exports.useSystemScreencast = exports.useSystemBar = exports.useScreencast = exports.useNetwork = exports.useFontScale = exports.useBattery = exports.useTheme = exports.setGlobalThemeMode = void 0;
|
|
21
21
|
// All imperative JS/TS APIs
|
|
22
22
|
__exportStar(require("./src/SystemBar"), exports);
|
|
23
23
|
// All TypeScript types
|
|
@@ -28,7 +28,9 @@ Object.defineProperty(exports, "setGlobalThemeMode", { enumerable: true, get: fu
|
|
|
28
28
|
Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return useTheme_1.useTheme; } });
|
|
29
29
|
// React hooks
|
|
30
30
|
var useSystemBar_1 = require("./src/useSystemBar");
|
|
31
|
-
Object.defineProperty(exports, "
|
|
31
|
+
Object.defineProperty(exports, "useBattery", { enumerable: true, get: function () { return useSystemBar_1.useBattery; } });
|
|
32
|
+
Object.defineProperty(exports, "useFontScale", { enumerable: true, get: function () { return useSystemBar_1.useFontScale; } });
|
|
33
|
+
Object.defineProperty(exports, "useNetwork", { enumerable: true, get: function () { return useSystemBar_1.useNetwork; } });
|
|
32
34
|
Object.defineProperty(exports, "useScreencast", { enumerable: true, get: function () { return useSystemBar_1.useScreencast; } });
|
|
33
35
|
Object.defineProperty(exports, "useSystemBar", { enumerable: true, get: function () { return useSystemBar_1.useSystemBar; } });
|
|
34
36
|
Object.defineProperty(exports, "useSystemScreencast", { enumerable: true, get: function () { return useSystemBar_1.useSystemScreencast; } });
|