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
|
-
|
|
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