rn-system-bar 3.1.7 → 3.2.0

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,11 +56,60 @@ class SystemBarModule(
56
56
  // NAVIGATION BAR — 100% native, no expo dep
57
57
  // ═══════════════════════════════════════════════
58
58
 
59
+ // ── Helpers for transparent / translucent bars ──────────────────────────
60
+
61
+ private fun applyEdgeToEdgeFlags() {
62
+ // Required so our colour / transparency actually shows through
63
+ activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
64
+ }
65
+
66
+ private fun setNavBarTransparent() {
67
+ val win = activity()!!.window
68
+ applyEdgeToEdgeFlags()
69
+ win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
70
+ win.navigationBarColor = Color.TRANSPARENT
71
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
72
+ win.setDecorFitsSystemWindows(false)
73
+ } else {
74
+ @Suppress("DEPRECATION")
75
+ win.decorView.systemUiVisibility =
76
+ win.decorView.systemUiVisibility or
77
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
78
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
79
+ }
80
+ }
81
+
82
+ private fun setNavBarTranslucent() {
83
+ val win = activity()!!.window
84
+ win.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
85
+ win.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
86
+ }
87
+
88
+ private fun setNavBarSolid(color: String) {
89
+ val win = activity()!!.window
90
+ applyEdgeToEdgeFlags()
91
+ win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
92
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
93
+ win.setDecorFitsSystemWindows(true)
94
+ } else {
95
+ @Suppress("DEPRECATION")
96
+ win.decorView.systemUiVisibility =
97
+ win.decorView.systemUiVisibility and
98
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.inv() and
99
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE.inv()
100
+ }
101
+ try { win.navigationBarColor = Color.parseColor(color) }
102
+ catch (_: Exception) {}
103
+ }
104
+
59
105
  @ReactMethod
60
106
  fun setNavigationBarColor(color: String) {
61
107
  activity()?.runOnUiThread {
62
- try { activity()!!.window.navigationBarColor = Color.parseColor(color) }
63
- catch (_: Exception) {}
108
+ when (color.trim().lowercase()) {
109
+ "transparent" -> setNavBarTransparent()
110
+ "translucent" -> setNavBarTranslucent()
111
+ else -> setNavBarSolid(color)
112
+ }
64
113
  }
65
114
  }
66
115
 
@@ -147,6 +196,47 @@ class SystemBarModule(
147
196
  }
148
197
  }
149
198
 
199
+ @ReactMethod
200
+ fun setStatusBarColor(color: String) {
201
+ activity()?.runOnUiThread {
202
+ val win = activity()!!.window
203
+ when (color.trim().lowercase()) {
204
+ "transparent" -> {
205
+ applyEdgeToEdgeFlags()
206
+ win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
207
+ win.statusBarColor = Color.TRANSPARENT
208
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
209
+ win.setDecorFitsSystemWindows(false)
210
+ } else {
211
+ @Suppress("DEPRECATION")
212
+ win.decorView.systemUiVisibility =
213
+ win.decorView.systemUiVisibility or
214
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
215
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
216
+ }
217
+ }
218
+ "translucent" -> {
219
+ win.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
220
+ win.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
221
+ }
222
+ else -> {
223
+ applyEdgeToEdgeFlags()
224
+ win.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
225
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
226
+ win.setDecorFitsSystemWindows(true)
227
+ } else {
228
+ @Suppress("DEPRECATION")
229
+ win.decorView.systemUiVisibility =
230
+ win.decorView.systemUiVisibility and
231
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.inv()
232
+ }
233
+ try { win.statusBarColor = Color.parseColor(color) }
234
+ catch (_: Exception) {}
235
+ }
236
+ }
237
+ }
238
+ }
239
+
150
240
  @ReactMethod
151
241
  fun setStatusBarVisibility(visible: Boolean) {
152
242
  activity()?.runOnUiThread {
@@ -379,12 +469,13 @@ class SystemBarModule(
379
469
  }
380
470
 
381
471
  // ═══════════════════════════════════════════════
382
- // SCREENCAST
472
+ // SYSTEM SCREENCAST (external display / HDMI / Miracast)
473
+ // Detects OS-level external displays via DisplayManager.
383
474
  // ═══════════════════════════════════════════════
384
475
 
385
476
  private var displayListener: DisplayManager.DisplayListener? = null
386
477
 
387
- private fun buildDisplayListMap(): WritableMap {
478
+ private fun buildSystemDisplayMap(): WritableMap {
388
479
  val dm = displayManager()
389
480
  val map = Arguments.createMap()
390
481
  val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
@@ -404,33 +495,217 @@ class SystemBarModule(
404
495
  }
405
496
 
406
497
  @ReactMethod
407
- fun getScreencastInfo(promise: Promise) {
408
- try { promise.resolve(buildDisplayListMap()) }
498
+ fun getSystemScreencastInfo(promise: Promise) {
499
+ try { promise.resolve(buildSystemDisplayMap()) }
409
500
  catch (e: Exception) { promise.reject("SCREENCAST_ERROR", e.message, e) }
410
501
  }
411
502
 
412
503
  @ReactMethod
413
- fun startScreencastListener() {
504
+ fun startSystemScreencastListener() {
414
505
  if (displayListener != null) return
415
506
  val listener = object : DisplayManager.DisplayListener {
416
- override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
417
- override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
418
- override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
507
+ override fun onDisplayAdded(id: Int) { emit("SystemBar_SystemScreencastChange", buildSystemDisplayMap()) }
508
+ override fun onDisplayRemoved(id: Int) { emit("SystemBar_SystemScreencastChange", buildSystemDisplayMap()) }
509
+ override fun onDisplayChanged(id: Int) { emit("SystemBar_SystemScreencastChange", buildSystemDisplayMap()) }
419
510
  }
420
511
  displayListener = listener
421
512
  displayManager().registerDisplayListener(listener, null)
422
513
  }
423
514
 
424
515
  @ReactMethod
425
- fun stopScreencastListener() {
516
+ fun stopSystemScreencastListener() {
426
517
  displayListener?.let { displayManager().unregisterDisplayListener(it) }
427
518
  displayListener = null
428
519
  }
429
520
 
521
+ // ═══════════════════════════════════════════════
522
+ // 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
+ // ═══════════════════════════════════════════════
532
+
533
+ private val mediaRouter: android.media.MediaRouter by lazy {
534
+ reactContext.getSystemService(Context.MEDIA_ROUTER_SERVICE) as android.media.MediaRouter
535
+ }
536
+
537
+ // Current state
538
+ private var appCastState = "idle"
539
+ private var connectedRoute: android.media.MediaRouter.RouteInfo? = null
540
+ private var appCastCallback: android.media.MediaRouter.Callback? = null
541
+
542
+ // All routes discovered during the current scan (id → route)
543
+ private val discoveredRoutes = mutableMapOf<String, android.media.MediaRouter.RouteInfo>()
544
+
545
+ // ── Serialisation helpers ──────────────────
546
+
547
+ private fun routeId(r: android.media.MediaRouter.RouteInfo): String =
548
+ r.name?.toString()?.replace(" ", "_")?.plus("_${r.hashCode()}") ?: r.hashCode().toString()
549
+
550
+ private fun routeToMap(r: android.media.MediaRouter.RouteInfo): WritableMap {
551
+ val m = Arguments.createMap()
552
+ m.putString("id", routeId(r))
553
+ m.putString("name", r.name?.toString() ?: "Unknown")
554
+ val desc = r.description?.toString()
555
+ if (desc != null) m.putString("description", desc) else m.putNull("description")
556
+ // MediaRouter doesn't expose signal strength — set null
557
+ m.putNull("signalStrength")
558
+ // Consider a device to require pairing when its status matches "connecting"
559
+ m.putBoolean("requiresPairing", false)
560
+ return m
561
+ }
562
+
563
+ private fun buildAppCastMap(): WritableMap {
564
+ val map = Arguments.createMap()
565
+ map.putString("state", appCastState)
566
+ map.putNull("error")
567
+
568
+ val devArr = Arguments.createArray()
569
+ for (r in discoveredRoutes.values) devArr.pushMap(routeToMap(r))
570
+ map.putArray("devices", devArr)
571
+
572
+ val connected = connectedRoute
573
+ if (connected != null) map.putMap("connectedDevice", routeToMap(connected))
574
+ else map.putNull("connectedDevice")
575
+
576
+ return map
577
+ }
578
+
579
+ private fun emitAppCastChange(error: String? = null) {
580
+ val map = buildAppCastMap()
581
+ if (error != null) map.putString("error", error)
582
+ emit("SystemBar_AppCastChange", map)
583
+ }
584
+
585
+ // ── Scan ──────────────────────────────────
586
+
587
+ @ReactMethod
588
+ fun startAppCastScan() {
589
+ if (appCastCallback != null) return // already scanning
590
+ appCastState = "scanning"
591
+ discoveredRoutes.clear()
592
+
593
+ val cb = object : android.media.MediaRouter.Callback() {
594
+ override fun onRouteAdded(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
595
+ // Skip the default phone speaker / earpiece
596
+ if (route.isDefault) return
597
+ val id = routeId(route)
598
+ discoveredRoutes[id] = route
599
+ emitAppCastChange()
600
+ }
601
+ override fun onRouteRemoved(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
602
+ discoveredRoutes.remove(routeId(route))
603
+ if (connectedRoute?.let { routeId(it) } == routeId(route)) {
604
+ connectedRoute = null
605
+ appCastState = "idle"
606
+ }
607
+ emitAppCastChange()
608
+ }
609
+ override fun onRouteChanged(router: android.media.MediaRouter, route: android.media.MediaRouter.RouteInfo) {
610
+ val id = routeId(route)
611
+ if (!route.isDefault) discoveredRoutes[id] = route
612
+ emitAppCastChange()
613
+ }
614
+ override fun onRouteSelected(router: android.media.MediaRouter, type: Int, route: android.media.MediaRouter.RouteInfo) {
615
+ if (route.isDefault) return
616
+ connectedRoute = route
617
+ appCastState = "connected"
618
+ emitAppCastChange()
619
+ }
620
+ override fun onRouteUnselected(router: android.media.MediaRouter, type: Int, route: android.media.MediaRouter.RouteInfo) {
621
+ if (appCastState != "idle") {
622
+ connectedRoute = null
623
+ appCastState = "idle"
624
+ emitAppCastChange()
625
+ }
626
+ }
627
+ }
628
+
629
+ appCastCallback = cb
630
+ mainHandler.post {
631
+ mediaRouter.addCallback(
632
+ android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO,
633
+ cb,
634
+ android.media.MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY or
635
+ android.media.MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
636
+ )
637
+ }
638
+ emitAppCastChange()
639
+ }
640
+
641
+ @ReactMethod
642
+ fun stopAppCastScan() {
643
+ appCastCallback?.let { mainHandler.post { mediaRouter.removeCallback(it) } }
644
+ appCastCallback = null
645
+ if (appCastState == "scanning") {
646
+ appCastState = "idle"
647
+ emitAppCastChange()
648
+ }
649
+ }
650
+
651
+ // ── Connect / Disconnect ──────────────────
652
+
653
+ @ReactMethod
654
+ fun connectAppCast(deviceId: String, pairingPin: String?) {
655
+ val route = discoveredRoutes[deviceId]
656
+ if (route == null) {
657
+ val err = Arguments.createMap()
658
+ err.putString("state", appCastState)
659
+ err.putString("error", "Device not found: $deviceId")
660
+ err.putNull("connectedDevice")
661
+ err.putArray("devices", Arguments.createArray())
662
+ emit("SystemBar_AppCastChange", err)
663
+ return
664
+ }
665
+ appCastState = "connecting"
666
+ emitAppCastChange()
667
+ mainHandler.post {
668
+ try {
669
+ mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, route)
670
+ // onRouteSelected callback will set state = "connected" and re-emit
671
+ } catch (e: Exception) {
672
+ appCastState = "idle"
673
+ emitAppCastChange(error = e.message ?: "Connection failed")
674
+ }
675
+ }
676
+ }
677
+
678
+ @ReactMethod
679
+ fun disconnectAppCast() {
680
+ if (connectedRoute == null) return
681
+ appCastState = "disconnecting"
682
+ emitAppCastChange()
683
+ mainHandler.post {
684
+ try {
685
+ // Select the default (phone) route to stop remote casting
686
+ val defaultRoute = mediaRouter.getDefaultRoute()
687
+ mediaRouter.selectRoute(android.media.MediaRouter.ROUTE_TYPE_LIVE_VIDEO, defaultRoute)
688
+ connectedRoute = null
689
+ appCastState = "idle"
690
+ emitAppCastChange()
691
+ } catch (e: Exception) {
692
+ appCastState = "idle"
693
+ emitAppCastChange(error = e.message ?: "Disconnect failed")
694
+ }
695
+ }
696
+ }
697
+
698
+ @ReactMethod
699
+ fun getAppCastInfo(promise: Promise) {
700
+ try { promise.resolve(buildAppCastMap()) }
701
+ catch (e: Exception) { promise.reject("APP_CAST_ERROR", e.message, e) }
702
+ }
703
+
430
704
  // ── Cleanup ──────────────────────────────────
431
705
  override fun onCatalystInstanceDestroy() {
432
706
  stopBrightnessListener()
433
707
  stopVolumeListener()
434
- stopScreencastListener()
708
+ stopSystemScreencastListener()
709
+ stopAppCastScan()
435
710
  }
436
- }
711
+ }
package/index.ts CHANGED
@@ -2,7 +2,28 @@
2
2
  // rn-system-bar · index.ts
3
3
  // ─────────────────────────────────────────────
4
4
 
5
+ // All imperative JS/TS APIs
5
6
  export * from "./src/SystemBar";
7
+
8
+ // All TypeScript types
6
9
  export * from "./src/types";
7
- export { useSystemBar, useScreencast } from "./src/useSystemBar";
8
- export type { SystemBarConfig } from "./src/useSystemBar";
10
+
11
+ // Theme hook (standalone access)
12
+ export { setGlobalThemeMode, useTheme } from "./src/useTheme";
13
+
14
+ // React hooks
15
+ export {
16
+ useAppCast,
17
+ useScreencast,
18
+ useSystemBar,
19
+ useSystemScreencast,
20
+ useTheme as useThemeHook,
21
+ useThemeSystemBar,
22
+ } from "./src/useSystemBar";
23
+
24
+ // Types from hooks
25
+ export type {
26
+ SystemBarConfig,
27
+ ThemedSystemBarConfig,
28
+ UseAppCastReturn,
29
+ } from "./src/useSystemBar";
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────
2
- // rn-system-bar · SystemBarModule.m v5
2
+ // rn-system-bar · SystemBarModule.m v6
3
3
  // Objective-C bridge — exposes Swift to React Native
4
4
  // ─────────────────────────────────────────────
5
5
 
@@ -36,10 +36,17 @@ RCT_EXTERN_METHOD(stopBatteryListener)
36
36
  // Haptics
37
37
  RCT_EXTERN_METHOD(haptic:(NSString *)pattern)
38
38
 
39
- // Screencast
40
- RCT_EXTERN_METHOD(getScreencastInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
41
- RCT_EXTERN_METHOD(startScreencastListener)
42
- RCT_EXTERN_METHOD(stopScreencastListener)
39
+ // System Screencast (external display / AirPlay mirror)
40
+ RCT_EXTERN_METHOD(getSystemScreencastInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
41
+ RCT_EXTERN_METHOD(startSystemScreencastListener)
42
+ RCT_EXTERN_METHOD(stopSystemScreencastListener)
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)
43
50
 
44
51
  // Font Scale
45
52
  RCT_EXTERN_METHOD(getFontScaleInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@@ -58,4 +65,4 @@ RCT_EXTERN_METHOD(setStatusBarVisibility:(BOOL)visible)
58
65
  RCT_EXTERN_METHOD(setVolumeHUDVisible:(BOOL)visible)
59
66
  RCT_EXTERN_METHOD(immersiveMode:(BOOL)enable)
60
67
 
61
- @end
68
+ @end
@@ -226,36 +226,70 @@ class SystemBarModule: RCTEventEmitter {
226
226
  }
227
227
 
228
228
  // ═══════════════════════════════════════════════
229
- // 🆕 SCREENCAST
229
+ // 🆕 SYSTEM SCREENCAST (external display / AirPlay mirror)
230
+ // Detects OS-level external screens via UIScreen.
230
231
  // ═══════════════════════════════════════════════
231
232
 
232
- private func currentScreencastMap() -> [String: Any] {
233
+ private func currentSystemScreencastMap() -> [String: Any] {
233
234
  let isCasting = UIScreen.screens.count > 1
234
235
  return [
235
236
  "isCasting": isCasting,
236
237
  "displayName": isCasting ? "External Display" : NSNull(),
238
+ "displays": UIScreen.screens.dropFirst().enumerated().map { idx, _ in
239
+ ["id": idx + 1, "name": "External Display \(idx + 1)", "isValid": true]
240
+ },
237
241
  ]
238
242
  }
239
243
 
240
244
  @objc
241
- func getScreencastInfo(_ resolve: @escaping RCTPromiseResolveBlock,
242
- rejecter reject: RCTPromiseRejectBlock) {
243
- resolve(currentScreencastMap())
245
+ func getSystemScreencastInfo(_ resolve: @escaping RCTPromiseResolveBlock,
246
+ rejecter reject: RCTPromiseRejectBlock) {
247
+ resolve(currentSystemScreencastMap())
244
248
  }
245
249
 
246
- @objc func startScreencastListener() {
250
+ @objc func startSystemScreencastListener() {
247
251
  screencastObserver = NotificationCenter.default.addObserver(
248
252
  forName: UIScreen.didConnectNotification, object: nil, queue: .main
249
253
  ) { [weak self] _ in
250
- self?.emit("SystemBar_ScreencastChange", body: self?.currentScreencastMap() ?? [:])
254
+ self?.emit("SystemBar_SystemScreencastChange", body: self?.currentSystemScreencastMap() ?? [:])
251
255
  }
252
256
  }
253
257
 
254
- @objc func stopScreencastListener() {
258
+ @objc func stopSystemScreencastListener() {
255
259
  if let obs = screencastObserver { NotificationCenter.default.removeObserver(obs) }
256
260
  screencastObserver = nil
257
261
  }
258
262
 
263
+ // ═══════════════════════════════════════════════
264
+ // APP-ONLY CAST (Android MediaRouter — iOS stubs)
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
+
259
293
  // setSecureScreen — iOS doesn't support this via public API (no-op)
260
294
  @objc func setSecureScreen(_ enable: Bool) {}
261
295
 
@@ -295,9 +329,10 @@ class SystemBarModule: RCTEventEmitter {
295
329
  @objc func setNavigationBarButtonStyle(_ style: String) {}
296
330
  @objc func setNavigationBarStyle(_ style: String) {}
297
331
  @objc func setNavigationBarBehavior(_ behavior: String) {}
332
+ // color: hex | "transparent" | "translucent" — Android only
298
333
  @objc func setStatusBarColor(_ color: String) {}
299
334
  @objc func setVolumeHUDVisible(_ visible: Bool) {}
300
335
  @objc func immersiveMode(_ enable: Bool) {}
301
336
  @objc func setStatusBarStyle(_ style: String) {}
302
337
  @objc func setStatusBarVisibility(_ visible: Bool) {}
303
- }
338
+ }
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./src/SystemBar";
2
2
  export * from "./src/types";
3
- export { useSystemBar, useScreencast } from "./src/useSystemBar";
4
- export type { SystemBarConfig } from "./src/useSystemBar";
3
+ export { setGlobalThemeMode, useTheme } from "./src/useTheme";
4
+ export { useAppCast, useScreencast, useSystemBar, useSystemScreencast, useTheme as useThemeHook, useThemeSystemBar, } from "./src/useSystemBar";
5
+ export type { SystemBarConfig, ThemedSystemBarConfig, UseAppCastReturn, } from "./src/useSystemBar";
package/lib/index.js CHANGED
@@ -17,9 +17,20 @@ 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.useScreencast = exports.useSystemBar = void 0;
20
+ exports.useThemeSystemBar = exports.useThemeHook = exports.useSystemScreencast = exports.useSystemBar = exports.useScreencast = exports.useAppCast = exports.useTheme = exports.setGlobalThemeMode = void 0;
21
+ // All imperative JS/TS APIs
21
22
  __exportStar(require("./src/SystemBar"), exports);
23
+ // All TypeScript types
22
24
  __exportStar(require("./src/types"), exports);
25
+ // Theme hook (standalone access)
26
+ var useTheme_1 = require("./src/useTheme");
27
+ Object.defineProperty(exports, "setGlobalThemeMode", { enumerable: true, get: function () { return useTheme_1.setGlobalThemeMode; } });
28
+ Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return useTheme_1.useTheme; } });
29
+ // React hooks
23
30
  var useSystemBar_1 = require("./src/useSystemBar");
24
- Object.defineProperty(exports, "useSystemBar", { enumerable: true, get: function () { return useSystemBar_1.useSystemBar; } });
31
+ Object.defineProperty(exports, "useAppCast", { enumerable: true, get: function () { return useSystemBar_1.useAppCast; } });
25
32
  Object.defineProperty(exports, "useScreencast", { enumerable: true, get: function () { return useSystemBar_1.useScreencast; } });
33
+ Object.defineProperty(exports, "useSystemBar", { enumerable: true, get: function () { return useSystemBar_1.useSystemBar; } });
34
+ Object.defineProperty(exports, "useSystemScreencast", { enumerable: true, get: function () { return useSystemBar_1.useSystemScreencast; } });
35
+ Object.defineProperty(exports, "useThemeHook", { enumerable: true, get: function () { return useSystemBar_1.useTheme; } });
36
+ Object.defineProperty(exports, "useThemeSystemBar", { enumerable: true, get: function () { return useSystemBar_1.useThemeSystemBar; } });
@@ -15,7 +15,16 @@ export interface Spec extends TurboModule {
15
15
  setVolumeHUDVisible(visible: boolean): void;
16
16
  keepScreenOn(enable: boolean): void;
17
17
  immersiveMode(enable: boolean): void;
18
+ setSecureScreen(enable: boolean): void;
18
19
  setOrientation(mode: string): void;
20
+ getSystemScreencastInfo(): Promise<Object>;
21
+ startSystemScreencastListener(): void;
22
+ stopSystemScreencastListener(): void;
23
+ startAppCastScan(): void;
24
+ stopAppCastScan(): void;
25
+ connectAppCast(deviceId: string, pairingPin: string | null): void;
26
+ disconnectAppCast(): void;
27
+ getAppCastInfo(): Promise<Object>;
19
28
  }
20
29
  declare const _default: Spec;
21
30
  export default _default;
@@ -1,15 +1,42 @@
1
- import type { NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarStyle, NavigationBarVisibility, Orientation, ScreencastInfo, StatusBarStyle, VolumeStream } from "./types";
2
- export declare const setNavigationBarColor: (color: string) => void;
1
+ import type { AppCastInfo, NavigationBarBehavior, NavigationBarButtonStyle, NavigationBarColorValue, NavigationBarStyle, NavigationBarVisibility, Orientation, StatusBarColorValue, StatusBarStyle, SystemScreencastInfo, VolumeStream } from "./types";
2
+ /**
3
+ * Set the navigation bar background colour.
4
+ *
5
+ * @param color
6
+ * - Any hex string → solid colour e.g. `"#1a1a2e"`
7
+ * - `"transparent"` → fully transparent (content draws behind bar)
8
+ * - `"translucent"` → semi-transparent (system scrim over content)
9
+ *
10
+ * @example
11
+ * setNavigationBarColor("#000000"); // solid black
12
+ * setNavigationBarColor("transparent"); // glass / edge-to-edge
13
+ * setNavigationBarColor("translucent"); // frosted glass
14
+ */
15
+ export declare const setNavigationBarColor: (color: NavigationBarColorValue) => void;
16
+ /**
17
+ * Hide or show the navigation bar.
18
+ *
19
+ * @param mode `"visible"` | `"hidden"`
20
+ */
3
21
  export declare const setNavigationBarVisibility: (mode: NavigationBarVisibility) => void;
4
22
  export declare const setNavigationBarButtonStyle: (style: NavigationBarButtonStyle) => void;
5
23
  export declare const setNavigationBarStyle: (style: NavigationBarStyle) => void;
6
24
  export declare const setNavigationBarBehavior: (behavior: NavigationBarBehavior) => void;
25
+ /**
26
+ * Set the status bar background colour (Android only).
27
+ *
28
+ * @param color
29
+ * - Any hex string → solid colour
30
+ * - `"transparent"` → fully transparent
31
+ * - `"translucent"` → semi-transparent
32
+ */
33
+ export declare const setStatusBarColor: (color: StatusBarColorValue) => void;
7
34
  export declare const setStatusBarStyle: (style: StatusBarStyle) => void;
8
35
  export declare const setStatusBarVisibility: (visible: boolean) => void;
9
36
  export declare const setBrightness: (level: number) => void;
10
37
  export declare const getBrightness: () => Promise<number>;
11
38
  /**
12
- * Subscribe to system brightness changes (polls every 500ms on Android).
39
+ * Subscribe to system brightness changes (polls every 500 ms on Android).
13
40
  * @returns unsubscribe function
14
41
  */
15
42
  export declare const onBrightnessChange: (callback: (brightness: number) => void) => (() => void);
@@ -25,5 +52,56 @@ export declare const keepScreenOn: (enable: boolean) => void;
25
52
  export declare const immersiveMode: (enable: boolean) => void;
26
53
  export declare const setSecureScreen: (enable: boolean) => void;
27
54
  export declare const setOrientation: (mode: Orientation) => void;
28
- export declare const getScreencastInfo: () => Promise<ScreencastInfo>;
29
- export declare const onScreencastChange: (callback: (info: ScreencastInfo) => void) => (() => void);
55
+ /**
56
+ * One-shot snapshot of system-level external display state.
57
+ * Works on both Android (DisplayManager) and iOS (UIScreen.screens).
58
+ */
59
+ export declare const getSystemScreencastInfo: () => Promise<SystemScreencastInfo>;
60
+ /**
61
+ * Subscribe to system external-display changes.
62
+ * Fires when an HDMI / Miracast / AirPlay display connects or disconnects.
63
+ * @returns unsubscribe function
64
+ */
65
+ export declare const onSystemScreencastChange: (callback: (info: SystemScreencastInfo) => void) => (() => void);
66
+ /** @deprecated Use getSystemScreencastInfo() */
67
+ export declare const getScreencastInfo: () => Promise<SystemScreencastInfo>;
68
+ /** @deprecated Use onSystemScreencastChange() */
69
+ export declare const onScreencastChange: (callback: (info: SystemScreencastInfo) => void) => (() => void);
70
+ /**
71
+ * Start scanning for nearby castable devices (Chromecast, TV, etc.).
72
+ * Listen for results via `onAppCastChange`.
73
+ * Android: uses MediaRouter. iOS: no-op (AirPlay is system-only).
74
+ */
75
+ export declare const startAppCastScan: () => void;
76
+ /**
77
+ * Stop the device discovery scan.
78
+ */
79
+ export declare const stopAppCastScan: () => void;
80
+ /**
81
+ * Connect to a discovered device and begin casting this app's screen.
82
+ * @param deviceId The `id` field from `AppCastDevice` (MediaRouter route ID).
83
+ * @param pairingPin Optional PIN string if `requiresPairing` is true.
84
+ */
85
+ export declare const connectAppCast: (deviceId: string, pairingPin?: string) => void;
86
+ /**
87
+ * Disconnect the active in-app cast session.
88
+ */
89
+ export declare const disconnectAppCast: () => void;
90
+ /**
91
+ * Get the current in-app cast snapshot (state + device list).
92
+ */
93
+ export declare const getAppCastInfo: () => Promise<AppCastInfo>;
94
+ /**
95
+ * Subscribe to in-app cast state changes.
96
+ * Fires on: device discovered/lost, state changes (scanning → connecting → connected),
97
+ * pairing requests, errors.
98
+ *
99
+ * @example
100
+ * const unsub = onAppCastChange((info) => {
101
+ * if (info.state === "connected") console.log("Casting to", info.connectedDevice?.name);
102
+ * if (info.error) console.warn("Cast error:", info.error);
103
+ * });
104
+ *
105
+ * @returns unsubscribe function
106
+ */
107
+ export declare const onAppCastChange: (callback: (info: AppCastInfo) => void) => (() => void);