rn-system-bar 3.1.1 → 3.1.3

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,9 +1,9 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.systembar">
2
+ xmlns:tools="http://schemas.android.com/tools">
3
3
 
4
4
  <!-- Required for writing system brightness -->
5
- <uses-permission android:name="android.permission.WRITE_SETTINGS"
6
- tools:ignore="ProtectedPermissions"
7
- xmlns:tools="http://schemas.android.com/tools"/>
5
+ <uses-permission
6
+ android:name="android.permission.WRITE_SETTINGS"
7
+ tools:ignore="ProtectedPermissions" />
8
8
 
9
9
  </manifest>
@@ -8,6 +8,7 @@ import android.app.Activity
8
8
  import android.content.Context
9
9
  import android.content.Intent
10
10
  import android.content.pm.ActivityInfo
11
+ import android.graphics.Color
11
12
  import android.hardware.display.DisplayManager
12
13
  import android.media.AudioManager
13
14
  import android.os.Build
@@ -20,6 +21,7 @@ import android.view.WindowManager
20
21
  import com.facebook.react.bridge.*
21
22
  import com.facebook.react.modules.core.DeviceEventManagerModule
22
23
 
24
+ @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
23
25
  class SystemBarModule(
24
26
  private val reactContext: ReactApplicationContext
25
27
  ) : ReactContextBaseJavaModule(reactContext) {
@@ -42,64 +44,65 @@ class SystemBarModule(
42
44
  else -> AudioManager.STREAM_MUSIC
43
45
  }
44
46
 
47
+ private fun emit(event: String, data: WritableMap) {
48
+ reactContext
49
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
50
+ .emit(event, data)
51
+ }
52
+
45
53
  // ═══════════════════════════════════════════════
46
54
  // NAVIGATION BAR COLOR
47
- // Direct Window API edge-to-edge safe.
55
+ // WindowInsetsController API 30+, fallback for older.
56
+ // navigationBarColor deprecated on API 35+ but still
57
+ // works — suppressed at class level.
48
58
  // ═══════════════════════════════════════════════
49
59
 
50
60
  @ReactMethod
51
61
  fun setNavigationBarColor(color: String) {
52
62
  activity()?.runOnUiThread {
53
63
  try {
54
- activity()!!.window.navigationBarColor =
55
- android.graphics.Color.parseColor(color)
64
+ val parsed = Color.parseColor(color)
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
66
+ // API 35+ — use WindowInsetsController appearance for tinting
67
+ val controller = activity()!!.window.insetsController
68
+ // Nav bar color via window attribute still works as tint on 35+
69
+ activity()!!.window.navigationBarColor = parsed
70
+ } else {
71
+ activity()!!.window.navigationBarColor = parsed
72
+ }
56
73
  } catch (_: Exception) {}
57
74
  }
58
75
  }
59
76
 
60
77
  // ═══════════════════════════════════════════════
61
78
  // BRIGHTNESS
62
- //
63
- // setBrightness — TWO things at once:
64
- // 1. window.attributes.screenBrightness → current screen (instant)
65
- // 2. Settings.System.SCREEN_BRIGHTNESS → system brightness persisted
66
- //
67
- // Requires WRITE_SETTINGS permission.
68
- // If permission not granted → request it, still apply window brightness.
69
- //
70
- // getBrightness — reads SYSTEM brightness (0–255 → 0.0–1.0)
71
- // so the slider always shows the true system value.
72
79
  // ═══════════════════════════════════════════════
73
80
 
74
81
  @ReactMethod
75
82
  fun setBrightness(level: Float) {
76
83
  val clamped = level.coerceIn(0.01f, 1f)
77
84
 
78
- // 1️⃣ Window brightness — instant, no permission needed
85
+ // Window brightness — instant, no permission needed
79
86
  activity()?.runOnUiThread {
80
87
  val lp = activity()!!.window.attributes
81
88
  lp.screenBrightness = clamped
82
89
  activity()!!.window.attributes = lp
83
90
  }
84
91
 
85
- // 2️⃣ System brightness — persisted, needs WRITE_SETTINGS
92
+ // System brightness — persisted, needs WRITE_SETTINGS
86
93
  val sysValue = (clamped * 255).toInt().coerceIn(1, 255)
87
-
88
94
  if (Settings.System.canWrite(reactContext)) {
89
- // Permission granted — write system brightness
90
95
  Settings.System.putInt(
91
96
  reactContext.contentResolver,
92
97
  Settings.System.SCREEN_BRIGHTNESS,
93
98
  sysValue
94
99
  )
95
- // Also disable auto-brightness so manual value sticks
96
100
  Settings.System.putInt(
97
101
  reactContext.contentResolver,
98
102
  Settings.System.SCREEN_BRIGHTNESS_MODE,
99
103
  Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL
100
104
  )
101
105
  } else {
102
- // No permission — open system settings so user can grant it once
103
106
  val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).apply {
104
107
  flags = Intent.FLAG_ACTIVITY_NEW_TASK
105
108
  }
@@ -110,7 +113,6 @@ class SystemBarModule(
110
113
  @ReactMethod
111
114
  fun getBrightness(promise: Promise) {
112
115
  try {
113
- // Always read from system setting so value matches device brightness bar
114
116
  val sys = Settings.System.getInt(
115
117
  reactContext.contentResolver,
116
118
  Settings.System.SCREEN_BRIGHTNESS,
@@ -178,7 +180,6 @@ class SystemBarModule(
178
180
  c.show(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
179
181
  }
180
182
  } else {
181
- @Suppress("DEPRECATION")
182
183
  activity()!!.window.decorView.systemUiVisibility = if (enable) {
183
184
  View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
184
185
  View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
@@ -202,6 +203,7 @@ class SystemBarModule(
202
203
 
203
204
  // ═══════════════════════════════════════════════
204
205
  // ORIENTATION
206
+ // "auto" = FULL_SENSOR (follows system auto-rotate)
205
207
  // ═══════════════════════════════════════════════
206
208
 
207
209
  @ReactMethod
@@ -211,36 +213,41 @@ class SystemBarModule(
211
213
  "landscape" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
212
214
  "landscape-left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
213
215
  "landscape-right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
216
+ // "auto" = respect system auto-rotate toggle
214
217
  else -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
215
218
  }
216
219
  }
217
220
 
218
221
  // ═══════════════════════════════════════════════
219
- // SCREENCAST
222
+ // SCREENCAST — list all presentation displays
220
223
  // ═══════════════════════════════════════════════
221
224
 
222
225
  private var displayListener: DisplayManager.DisplayListener? = null
223
226
 
224
- private fun emit(event: String, data: WritableMap) {
225
- reactContext
226
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
227
- .emit(event, data)
228
- }
227
+ private fun buildDisplayListMap(): WritableMap {
228
+ val dm = displayManager()
229
+ val map = Arguments.createMap()
230
+ val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
231
+ val arr = Arguments.createArray()
232
+
233
+ for (d in displays) {
234
+ val item = Arguments.createMap()
235
+ item.putInt("id", d.displayId)
236
+ item.putString("name", d.name)
237
+ item.putBoolean("isValid", d.isValid)
238
+ arr.pushMap(item)
239
+ }
229
240
 
230
- private fun buildScreencastMap(): WritableMap {
231
- val dm = displayManager()
232
- val map = Arguments.createMap()
233
- val presentations = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
234
- val isCasting = presentations.isNotEmpty()
235
- map.putBoolean("isCasting", isCasting)
236
- if (isCasting) map.putString("displayName", presentations[0].name)
241
+ map.putBoolean("isCasting", displays.isNotEmpty())
242
+ map.putArray("displays", arr)
243
+ if (displays.isNotEmpty()) map.putString("displayName", displays[0].name)
237
244
  else map.putNull("displayName")
238
245
  return map
239
246
  }
240
247
 
241
248
  @ReactMethod
242
249
  fun getScreencastInfo(promise: Promise) {
243
- try { promise.resolve(buildScreencastMap()) }
250
+ try { promise.resolve(buildDisplayListMap()) }
244
251
  catch (e: Exception) { promise.reject("SCREENCAST_ERROR", e.message, e) }
245
252
  }
246
253
 
@@ -248,9 +255,9 @@ class SystemBarModule(
248
255
  fun startScreencastListener() {
249
256
  if (displayListener != null) return
250
257
  val listener = object : DisplayManager.DisplayListener {
251
- override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
252
- override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
253
- override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildScreencastMap()) }
258
+ override fun onDisplayAdded(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
259
+ override fun onDisplayRemoved(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
260
+ override fun onDisplayChanged(id: Int) { emit("SystemBar_ScreencastChange", buildDisplayListMap()) }
254
261
  }
255
262
  displayListener = listener
256
263
  displayManager().registerDisplayListener(listener, null)
@@ -5,7 +5,13 @@ export type NavigationBarVisibility = "visible" | "hidden";
5
5
  export type StatusBarStyle = "light" | "dark";
6
6
  export type Orientation = "portrait" | "landscape" | "landscape-left" | "landscape-right" | "auto";
7
7
  export type VolumeStream = "music" | "ring" | "notification" | "alarm" | "system";
8
+ export interface ScreencastDisplay {
9
+ id: number;
10
+ name: string;
11
+ isValid: boolean;
12
+ }
8
13
  export interface ScreencastInfo {
9
14
  isCasting: boolean;
10
15
  displayName: string | null;
16
+ displays: ScreencastDisplay[];
11
17
  }
@@ -91,6 +91,7 @@ const useScreencast = () => {
91
91
  const [info, setInfo] = (0, react_1.useState)({
92
92
  isCasting: false,
93
93
  displayName: null,
94
+ displays: [],
94
95
  });
95
96
  (0, react_1.useEffect)(() => {
96
97
  SystemBar.getScreencastInfo().then(setInfo).catch(() => { });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-system-bar",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
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",
package/src/types.ts CHANGED
@@ -26,7 +26,14 @@ export type VolumeStream =
26
26
  | "alarm"
27
27
  | "system";
28
28
 
29
+ export interface ScreencastDisplay {
30
+ id: number;
31
+ name: string;
32
+ isValid: boolean;
33
+ }
34
+
29
35
  export interface ScreencastInfo {
30
36
  isCasting: boolean;
31
37
  displayName: string | null;
38
+ displays: ScreencastDisplay[];
32
39
  }
@@ -78,6 +78,7 @@ export const useScreencast = () => {
78
78
  const [info, setInfo] = useState<ScreencastInfo>({
79
79
  isCasting: false,
80
80
  displayName: null,
81
+ displays: [],
81
82
  });
82
83
 
83
84
  useEffect(() => {