rn-system-bar 3.0.3 → 3.1.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.
@@ -1,25 +1,36 @@
1
1
  // ─────────────────────────────────────────────
2
- // rn-system-bar · SystemBarModule.kt
3
- // v4 Navigation bar removed (handled by expo-navigation-bar).
4
- // Status bar removed (handled by RN StatusBar).
5
- // Only: Brightness, Volume, Screen flags, Orientation.
6
- // Zero deprecated APIs.
2
+ // rn-system-bar · SystemBarModule.kt v5
3
+ // New: Network, Battery, Haptics, Screencast, FontScale
7
4
  // ─────────────────────────────────────────────
8
5
 
9
6
  package com.systembar
10
7
 
11
8
  import android.app.Activity
9
+ import android.content.BroadcastReceiver
12
10
  import android.content.Context
11
+ import android.content.Intent
12
+ import android.content.IntentFilter
13
13
  import android.content.pm.ActivityInfo
14
+ import android.hardware.display.DisplayManager
14
15
  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
15
21
  import android.os.Build
22
+ import android.os.VibrationEffect
23
+ import android.os.Vibrator
24
+ import android.os.VibratorManager
16
25
  import android.provider.Settings
26
+ import android.view.Display
17
27
  import android.view.View
18
28
  import android.view.WindowInsets
19
29
  import android.view.WindowInsetsController
20
30
  import android.view.WindowManager
21
31
 
22
32
  import com.facebook.react.bridge.*
33
+ import com.facebook.react.modules.core.DeviceEventManagerModule
23
34
 
24
35
  class SystemBarModule(
25
36
  private val reactContext: ReactApplicationContext
@@ -29,8 +40,23 @@ class SystemBarModule(
29
40
 
30
41
  private fun activity(): Activity? = reactContext.currentActivity
31
42
 
32
- private fun audioManager(): AudioManager =
33
- reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
43
+ private fun emit(event: String, data: WritableMap) {
44
+ reactContext
45
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
46
+ .emit(event, data)
47
+ }
48
+
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
+ }
34
60
 
35
61
  private fun streamType(stream: String): Int = when (stream) {
36
62
  "ring" -> AudioManager.STREAM_RING
@@ -46,34 +72,23 @@ class SystemBarModule(
46
72
 
47
73
  @ReactMethod
48
74
  fun setBrightness(level: Float) {
49
- val act = activity() ?: return
50
- act.runOnUiThread {
51
- val lp = act.window.attributes
75
+ activity()?.runOnUiThread {
76
+ val lp = activity()!!.window.attributes
52
77
  lp.screenBrightness = level.coerceIn(0f, 1f)
53
- act.window.attributes = lp
78
+ activity()!!.window.attributes = lp
54
79
  }
55
80
  }
56
81
 
57
82
  @ReactMethod
58
83
  fun getBrightness(promise: Promise) {
59
84
  try {
60
- val act = activity()
61
- if (act != null) {
62
- val lp = act.window.attributes
63
- if (lp.screenBrightness >= 0f) {
64
- promise.resolve(lp.screenBrightness.toDouble())
65
- return
66
- }
85
+ val lp = activity()?.window?.attributes
86
+ if (lp != null && lp.screenBrightness >= 0f) {
87
+ promise.resolve(lp.screenBrightness.toDouble()); return
67
88
  }
68
- val sysBrightness = Settings.System.getInt(
69
- reactContext.contentResolver,
70
- Settings.System.SCREEN_BRIGHTNESS,
71
- 128
72
- )
73
- promise.resolve(sysBrightness / 255.0)
74
- } catch (e: Exception) {
75
- promise.reject("BRIGHTNESS_ERROR", e.message, e)
76
- }
89
+ val sys = Settings.System.getInt(reactContext.contentResolver, Settings.System.SCREEN_BRIGHTNESS, 128)
90
+ promise.resolve(sys / 255.0)
91
+ } catch (e: Exception) { promise.reject("BRIGHTNESS_ERROR", e.message, e) }
77
92
  }
78
93
 
79
94
  // ═══════════════════════════════════════════════
@@ -82,34 +97,24 @@ class SystemBarModule(
82
97
 
83
98
  private var suppressVolumeHUD = false
84
99
 
85
- @ReactMethod
86
- fun setVolumeHUDVisible(visible: Boolean) {
87
- suppressVolumeHUD = !visible
88
- }
100
+ @ReactMethod fun setVolumeHUDVisible(visible: Boolean) { suppressVolumeHUD = !visible }
89
101
 
90
102
  @ReactMethod
91
103
  fun setVolume(level: Float, stream: String) {
92
104
  try {
93
- val am = audioManager()
94
- val type = streamType(stream)
95
- val max = am.getStreamMaxVolume(type)
96
- val vol = (level.coerceIn(0f, 1f) * max).toInt()
97
- val flags = if (suppressVolumeHUD) 0 else AudioManager.FLAG_SHOW_UI
98
- am.setStreamVolume(type, vol, flags)
105
+ val am = audioManager(); val type = streamType(stream)
106
+ val vol = (level.coerceIn(0f, 1f) * am.getStreamMaxVolume(type)).toInt()
107
+ am.setStreamVolume(type, vol, if (suppressVolumeHUD) 0 else AudioManager.FLAG_SHOW_UI)
99
108
  } catch (_: Exception) {}
100
109
  }
101
110
 
102
111
  @ReactMethod
103
112
  fun getVolume(stream: String, promise: Promise) {
104
113
  try {
105
- val am = audioManager()
106
- val type = streamType(stream)
107
- val current = am.getStreamVolume(type)
114
+ val am = audioManager(); val type = streamType(stream)
108
115
  val max = am.getStreamMaxVolume(type)
109
- promise.resolve(if (max > 0) current.toDouble() / max else 0.0)
110
- } catch (e: Exception) {
111
- promise.reject("VOLUME_ERROR", e.message, e)
112
- }
116
+ promise.resolve(if (max > 0) am.getStreamVolume(type).toDouble() / max else 0.0)
117
+ } catch (e: Exception) { promise.reject("VOLUME_ERROR", e.message, e) }
113
118
  }
114
119
 
115
120
  // ═══════════════════════════════════════════════
@@ -118,46 +123,30 @@ class SystemBarModule(
118
123
 
119
124
  @ReactMethod
120
125
  fun keepScreenOn(enable: Boolean) {
121
- val act = activity() ?: return
122
- act.runOnUiThread {
123
- if (enable) {
124
- act.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
125
- } else {
126
- act.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
127
- }
126
+ activity()?.runOnUiThread {
127
+ if (enable) activity()!!.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
128
+ else activity()!!.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
128
129
  }
129
130
  }
130
131
 
131
132
  @ReactMethod
132
133
  fun immersiveMode(enable: Boolean) {
133
- val act = activity() ?: return
134
- act.runOnUiThread {
134
+ activity()?.runOnUiThread {
135
135
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
136
- // API 30+ — WindowInsetsController (modern, no deprecated flags)
137
- val controller = act.window.insetsController ?: return@runOnUiThread
136
+ val c = activity()!!.window.insetsController ?: return@runOnUiThread
138
137
  if (enable) {
139
- controller.hide(
140
- WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
141
- )
142
- controller.systemBarsBehavior =
143
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
138
+ c.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
139
+ c.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
144
140
  } else {
145
- controller.show(
146
- WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
147
- )
141
+ c.show(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
148
142
  }
149
143
  } else {
150
- // API 21–29: only path available; suppressed intentionally
151
144
  @Suppress("DEPRECATION")
152
- act.window.decorView.systemUiVisibility = if (enable) {
153
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
154
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
155
- View.SYSTEM_UI_FLAG_FULLSCREEN or
156
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
145
+ 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
157
148
  View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
158
- } else {
159
- View.SYSTEM_UI_FLAG_VISIBLE
160
- }
149
+ } else { View.SYSTEM_UI_FLAG_VISIBLE }
161
150
  }
162
151
  }
163
152
  }
@@ -168,8 +157,7 @@ class SystemBarModule(
168
157
 
169
158
  @ReactMethod
170
159
  fun setOrientation(mode: String) {
171
- val act = activity() ?: return
172
- act.requestedOrientation = when (mode) {
160
+ activity()?.requestedOrientation = when (mode) {
173
161
  "portrait" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
174
162
  "landscape" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
175
163
  "landscape-left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
@@ -177,4 +165,286 @@ class SystemBarModule(
177
165
  else -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
178
166
  }
179
167
  }
180
- }
168
+
169
+ // ═══════════════════════════════════════════════
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
261
+ // ═══════════════════════════════════════════════
262
+
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
+ // ═══════════════════════════════════════════════
322
+
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
+ }
352
+ }
353
+ }
354
+
355
+ // ═══════════════════════════════════════════════
356
+ // 🆕 SCREENCAST
357
+ // ═══════════════════════════════════════════════
358
+
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)
368
+ else map.putNull("displayName")
369
+ return map
370
+ }
371
+
372
+ @ReactMethod
373
+ fun getScreencastInfo(promise: Promise) {
374
+ try { promise.resolve(buildScreencastMap()) }
375
+ catch (e: Exception) { promise.reject("SCREENCAST_ERROR", e.message, e) }
376
+ }
377
+
378
+ @ReactMethod
379
+ fun startScreencastListener() {
380
+ if (displayListener != null) return
381
+ 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()) }
385
+ }
386
+ displayListener = listener
387
+ displayManager().registerDisplayListener(listener, null)
388
+ }
389
+
390
+ @ReactMethod
391
+ fun stopScreencastListener() {
392
+ displayListener?.let { displayManager().unregisterDisplayListener(it) }
393
+ displayListener = null
394
+ }
395
+
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 ──────────────────
444
+ override fun onCatalystInstanceDestroy() {
445
+ stopNetworkListener()
446
+ stopBatteryListener()
447
+ stopScreencastListener()
448
+ stopFontScaleListener()
449
+ }
450
+ }
@@ -9,13 +9,14 @@ import com.facebook.react.bridge.NativeModule
9
9
  import com.facebook.react.bridge.ReactApplicationContext
10
10
  import com.facebook.react.uimanager.ViewManager
11
11
 
12
+ // @Suppress at class level — covers all overrides of deprecated ReactPackage members
13
+ @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
12
14
  class SystemBarPackage : ReactPackage {
13
15
 
14
16
  override fun createNativeModules(
15
17
  reactContext: ReactApplicationContext
16
18
  ): List<NativeModule> = listOf(SystemBarModule(reactContext))
17
19
 
18
- @Suppress("DEPRECATION")
19
20
  override fun createViewManagers(
20
21
  reactContext: ReactApplicationContext
21
22
  ): List<ViewManager<*, *>> = emptyList()
package/index.ts CHANGED
@@ -4,3 +4,12 @@
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";
15
+ export type { SystemBarConfig } from "./src/useSystemBar";
@@ -1,40 +1,60 @@
1
1
  // ─────────────────────────────────────────────
2
- // rn-system-bar · SystemBarModule.m
2
+ // rn-system-bar · SystemBarModule.m v5
3
3
  // Objective-C bridge — exposes Swift to React Native
4
4
  // ─────────────────────────────────────────────
5
5
 
6
6
  #import <React/RCTBridgeModule.h>
7
+ #import <React/RCTEventEmitter.h>
7
8
 
8
- @interface RCT_EXTERN_MODULE(SystemBar, NSObject)
9
-
10
- // Status Bar
11
- RCT_EXTERN_METHOD(setStatusBarStyle:(NSString *)style)
12
- RCT_EXTERN_METHOD(setStatusBarVisibility:(BOOL)visible)
9
+ @interface RCT_EXTERN_MODULE(SystemBar, RCTEventEmitter)
13
10
 
14
11
  // Brightness
15
12
  RCT_EXTERN_METHOD(setBrightness:(float)level)
16
- RCT_EXTERN_METHOD(getBrightness:(RCTPromiseResolveBlock)resolve
17
- rejecter:(RCTPromiseRejectBlock)reject)
13
+ RCT_EXTERN_METHOD(getBrightness:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
18
14
 
19
15
  // Volume
20
16
  RCT_EXTERN_METHOD(setVolume:(float)level stream:(NSString *)stream)
21
- RCT_EXTERN_METHOD(getVolume:(NSString *)stream
22
- resolver:(RCTPromiseResolveBlock)resolve
23
- rejecter:(RCTPromiseRejectBlock)reject)
17
+ RCT_EXTERN_METHOD(getVolume:(NSString *)stream resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
24
18
 
25
19
  // Screen
26
20
  RCT_EXTERN_METHOD(keepScreenOn:(BOOL)enable)
21
+ RCT_EXTERN_METHOD(setSecureScreen:(BOOL)enable)
27
22
 
28
23
  // Orientation
29
24
  RCT_EXTERN_METHOD(setOrientation:(NSString *)mode)
30
25
 
31
- // Android-only stubs (no-ops on iOS)
26
+ // Network
27
+ RCT_EXTERN_METHOD(getNetworkInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
28
+ RCT_EXTERN_METHOD(startNetworkListener)
29
+ RCT_EXTERN_METHOD(stopNetworkListener)
30
+
31
+ // Battery
32
+ RCT_EXTERN_METHOD(getBatteryInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
33
+ RCT_EXTERN_METHOD(startBatteryListener)
34
+ RCT_EXTERN_METHOD(stopBatteryListener)
35
+
36
+ // Haptics
37
+ RCT_EXTERN_METHOD(haptic:(NSString *)pattern)
38
+
39
+ // Screencast
40
+ RCT_EXTERN_METHOD(getScreencastInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
41
+ RCT_EXTERN_METHOD(startScreencastListener)
42
+ RCT_EXTERN_METHOD(stopScreencastListener)
43
+
44
+ // Font Scale
45
+ RCT_EXTERN_METHOD(getFontScaleInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
46
+ RCT_EXTERN_METHOD(startFontScaleListener)
47
+ RCT_EXTERN_METHOD(stopFontScaleListener)
48
+
49
+ // Android-only stubs
32
50
  RCT_EXTERN_METHOD(setNavigationBarColor:(NSString *)color)
33
51
  RCT_EXTERN_METHOD(setNavigationBarVisibility:(NSString *)mode)
34
52
  RCT_EXTERN_METHOD(setNavigationBarButtonStyle:(NSString *)style)
35
53
  RCT_EXTERN_METHOD(setNavigationBarStyle:(NSString *)style)
36
54
  RCT_EXTERN_METHOD(setNavigationBarBehavior:(NSString *)behavior)
37
55
  RCT_EXTERN_METHOD(setStatusBarColor:(NSString *)color)
56
+ RCT_EXTERN_METHOD(setStatusBarStyle:(NSString *)style)
57
+ RCT_EXTERN_METHOD(setStatusBarVisibility:(BOOL)visible)
38
58
  RCT_EXTERN_METHOD(setVolumeHUDVisible:(BOOL)visible)
39
59
  RCT_EXTERN_METHOD(immersiveMode:(BOOL)enable)
40
60