rn-system-bar 3.2.4 → 3.2.5

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.
@@ -6,4 +6,12 @@
6
6
  android:name="android.permission.WRITE_SETTINGS"
7
7
  tools:ignore="ProtectedPermissions" />
8
8
 
9
- </manifest>
9
+ <!-- Required for network state / listener -->
10
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
11
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
12
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
13
+
14
+ <!-- Required for haptic feedback / vibration -->
15
+ <uses-permission android:name="android.permission.VIBRATE" />
16
+
17
+ </manifest>
@@ -10,13 +10,22 @@ import android.content.Context
10
10
  import android.content.Intent
11
11
  import android.content.IntentFilter
12
12
  import android.content.pm.ActivityInfo
13
+ import android.content.res.Configuration
13
14
  import android.graphics.Color
14
15
  import android.hardware.display.DisplayManager
15
16
  import android.media.AudioManager
17
+ import android.net.ConnectivityManager
18
+ import android.net.NetworkCapabilities
19
+ import android.net.NetworkRequest
20
+ import android.os.BatteryManager
16
21
  import android.os.Build
17
22
  import android.os.Handler
18
23
  import android.os.Looper
24
+ import android.os.VibrationEffect
25
+ import android.os.Vibrator
26
+ import android.os.VibratorManager
19
27
  import android.provider.Settings
28
+ import android.util.DisplayMetrics
20
29
  import android.view.View
21
30
  import android.view.WindowInsets
22
31
  import android.view.WindowInsetsController
@@ -503,10 +512,224 @@ class SystemBarModule(
503
512
  displayListener = null
504
513
  }
505
514
 
515
+
516
+ // ═══════════════════════════════════════════════
517
+ // NETWORK
518
+ // ═══════════════════════════════════════════════
519
+
520
+ private fun connectivityManager() =
521
+ reactContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
522
+
523
+ private fun buildNetworkMap(): WritableMap {
524
+ val cm = connectivityManager()
525
+ val map = Arguments.createMap()
526
+
527
+ val net = cm.activeNetwork
528
+ val caps = if (net != null) cm.getNetworkCapabilities(net) else null
529
+
530
+ val isConnected = caps != null &&
531
+ (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ||
532
+ caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED))
533
+
534
+ val type = when {
535
+ caps == null -> "none"
536
+ caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "wifi"
537
+ caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "cellular"
538
+ caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "ethernet"
539
+ else -> "unknown"
540
+ }
541
+
542
+ map.putString("type", type)
543
+ map.putBoolean("isConnected", isConnected)
544
+ val airplaneMode = Settings.Global.getInt(
545
+ reactContext.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) == 1
546
+ map.putBoolean("isAirplaneMode", airplaneMode)
547
+ map.putNull("ssid")
548
+ map.putNull("cellularGeneration")
549
+ return map
550
+ }
551
+
552
+ @ReactMethod
553
+ fun getNetworkInfo(promise: Promise) {
554
+ try { promise.resolve(buildNetworkMap()) }
555
+ catch (e: Exception) { promise.reject("NETWORK_ERROR", e.message, e) }
556
+ }
557
+
558
+ private var networkCallback: ConnectivityManager.NetworkCallback? = null
559
+
560
+ @ReactMethod
561
+ fun startNetworkListener() {
562
+ if (networkCallback != null) return
563
+ val cb = object : ConnectivityManager.NetworkCallback() {
564
+ override fun onAvailable(network: android.net.Network) { emit("SystemBar_NetworkChange", buildNetworkMap()) }
565
+ override fun onLost(network: android.net.Network) { emit("SystemBar_NetworkChange", buildNetworkMap()) }
566
+ override fun onCapabilitiesChanged(
567
+ network: android.net.Network,
568
+ caps: NetworkCapabilities
569
+ ) { emit("SystemBar_NetworkChange", buildNetworkMap()) }
570
+ }
571
+ networkCallback = cb
572
+ val request = NetworkRequest.Builder().build()
573
+ connectivityManager().registerNetworkCallback(request, cb)
574
+ }
575
+
576
+ @ReactMethod
577
+ fun stopNetworkListener() {
578
+ networkCallback?.let {
579
+ try { connectivityManager().unregisterNetworkCallback(it) } catch (_: Exception) {}
580
+ }
581
+ networkCallback = null
582
+ }
583
+
584
+ // ═══════════════════════════════════════════════
585
+ // BATTERY
586
+ // ═══════════════════════════════════════════════
587
+
588
+ private fun buildBatteryMap(): WritableMap {
589
+ val map = Arguments.createMap()
590
+ val intent = reactContext.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
591
+
592
+ val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
593
+ val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, 100) ?: 100
594
+ val pct = if (scale > 0) (level * 100 / scale) else -1
595
+
596
+ val status = intent?.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN)
597
+ val state = when (status) {
598
+ BatteryManager.BATTERY_STATUS_CHARGING -> "charging"
599
+ BatteryManager.BATTERY_STATUS_FULL -> "full"
600
+ BatteryManager.BATTERY_STATUS_DISCHARGING,
601
+ BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "discharging"
602
+ else -> "unknown"
603
+ }
604
+ val isCharging = state == "charging" || state == "full"
605
+
606
+ map.putInt("level", pct)
607
+ map.putString("state", state)
608
+ map.putBoolean("isCharging", isCharging)
609
+ map.putBoolean("isLow", pct in 0..20 && !isCharging)
610
+ return map
611
+ }
612
+
613
+ @ReactMethod
614
+ fun getBatteryInfo(promise: Promise) {
615
+ try { promise.resolve(buildBatteryMap()) }
616
+ catch (e: Exception) { promise.reject("BATTERY_ERROR", e.message, e) }
617
+ }
618
+
619
+ private var batteryReceiver: BroadcastReceiver? = null
620
+
621
+ @ReactMethod
622
+ fun startBatteryListener() {
623
+ if (batteryReceiver != null) return
624
+ val receiver = object : BroadcastReceiver() {
625
+ override fun onReceive(ctx: Context?, intent: Intent?) {
626
+ emit("SystemBar_BatteryChange", buildBatteryMap())
627
+ }
628
+ }
629
+ batteryReceiver = receiver
630
+ val filter = IntentFilter().apply {
631
+ addAction(Intent.ACTION_BATTERY_CHANGED)
632
+ addAction(Intent.ACTION_BATTERY_LOW)
633
+ addAction(Intent.ACTION_BATTERY_OKAY)
634
+ addAction(Intent.ACTION_POWER_CONNECTED)
635
+ addAction(Intent.ACTION_POWER_DISCONNECTED)
636
+ }
637
+ reactContext.registerReceiver(receiver, filter)
638
+ }
639
+
640
+ @ReactMethod
641
+ fun stopBatteryListener() {
642
+ batteryReceiver?.let { try { reactContext.unregisterReceiver(it) } catch (_: Exception) {} }
643
+ batteryReceiver = null
644
+ }
645
+
646
+ // ═══════════════════════════════════════════════
647
+ // HAPTICS
648
+ // ═══════════════════════════════════════════════
649
+
650
+ private fun vibrator(): Vibrator =
651
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
652
+ (reactContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager)
653
+ .defaultVibrator
654
+ } else {
655
+ @Suppress("DEPRECATION")
656
+ reactContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
657
+ }
658
+
659
+ @ReactMethod
660
+ fun haptic(pattern: String) {
661
+ try {
662
+ val vib = vibrator()
663
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
664
+ val effect = when (pattern) {
665
+ "light" -> VibrationEffect.createOneShot(30, 80)
666
+ "medium" -> VibrationEffect.createOneShot(50, 150)
667
+ "heavy" -> VibrationEffect.createOneShot(80, 255)
668
+ "success" -> VibrationEffect.createWaveform(longArrayOf(0, 40, 60, 40), intArrayOf(0, 180, 0, 255), -1)
669
+ "warning" -> VibrationEffect.createWaveform(longArrayOf(0, 60, 40, 60), intArrayOf(0, 200, 0, 200), -1)
670
+ "error" -> VibrationEffect.createWaveform(longArrayOf(0, 80, 40, 80, 40, 80), intArrayOf(0, 255, 0, 255, 0, 255), -1)
671
+ "selection" -> VibrationEffect.createOneShot(20, 60)
672
+ else -> VibrationEffect.createOneShot(50, 150)
673
+ }
674
+ vib.vibrate(effect)
675
+ } else {
676
+ @Suppress("DEPRECATION")
677
+ vib.vibrate(when (pattern) {
678
+ "light" -> 30L
679
+ "heavy" -> 80L
680
+ "selection" -> 20L
681
+ else -> 50L
682
+ })
683
+ }
684
+ } catch (_: Exception) {}
685
+ }
686
+
687
+ // ═══════════════════════════════════════════════
688
+ // FONT SCALE
689
+ // ═══════════════════════════════════════════════
690
+
691
+ private fun buildFontScaleMap(): WritableMap {
692
+ val map = Arguments.createMap()
693
+ val config = reactContext.resources.configuration
694
+ val metrics = reactContext.resources.displayMetrics
695
+ map.putDouble("fontScale", config.fontScale.toDouble())
696
+ map.putDouble("density", metrics.density.toDouble())
697
+ return map
698
+ }
699
+
700
+ @ReactMethod
701
+ fun getFontScaleInfo(promise: Promise) {
702
+ try { promise.resolve(buildFontScaleMap()) }
703
+ catch (e: Exception) { promise.reject("FONTSCALE_ERROR", e.message, e) }
704
+ }
705
+
706
+ private var fontScaleReceiver: BroadcastReceiver? = null
707
+
708
+ @ReactMethod
709
+ fun startFontScaleListener() {
710
+ if (fontScaleReceiver != null) return
711
+ val receiver = object : BroadcastReceiver() {
712
+ override fun onReceive(ctx: Context?, intent: Intent?) {
713
+ emit("SystemBar_FontScaleChange", buildFontScaleMap())
714
+ }
715
+ }
716
+ fontScaleReceiver = receiver
717
+ reactContext.registerReceiver(receiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
718
+ }
719
+
720
+ @ReactMethod
721
+ fun stopFontScaleListener() {
722
+ fontScaleReceiver?.let { try { reactContext.unregisterReceiver(it) } catch (_: Exception) {} }
723
+ fontScaleReceiver = null
724
+ }
725
+
506
726
  // ── Cleanup ───────────────────────────────────
507
727
  override fun onCatalystInstanceDestroy() {
508
728
  stopBrightnessListener()
509
729
  stopVolumeListener()
510
730
  stopSystemScreencastListener()
731
+ stopNetworkListener()
732
+ stopBatteryListener()
733
+ stopFontScaleListener()
511
734
  }
512
735
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-system-bar",
3
- "version": "3.2.4",
3
+ "version": "3.2.5",
4
4
  "description": "Control Android & iOS system bars, brightness, volume, orientation and screen flags from React Native.",
5
5
  "main": "lib/index.js",
6
6
  "react-native": "lib/index.js",