expo-beacon 0.10.3 → 0.10.4

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.
package/README.md CHANGED
@@ -1844,8 +1844,8 @@ type NotificationConfig = {
1844
1844
  ```ts
1845
1845
  type BeaconNotificationSettings = {
1846
1846
  events?: BeaconNotificationConfig;
1847
- foregroundService?: ForegroundServiceConfig; // Android only
1848
- channel?: NotificationChannelConfig; // Android only
1847
+ foregroundService?: ForegroundServiceConfig; // Android only, title/text/icon only
1848
+ channel?: NotificationChannelConfig; // Android only, beacon alert channel
1849
1849
  };
1850
1850
  ```
1851
1851
 
@@ -1854,8 +1854,8 @@ type BeaconNotificationSettings = {
1854
1854
  ```ts
1855
1855
  type CarPlayNotificationSettings = {
1856
1856
  events?: CarPlayNotificationConfig;
1857
- foregroundService?: ForegroundServiceConfig; // Android only, CarPlay-only mode
1858
- channel?: CarPlayChannelConfig; // Android only
1857
+ foregroundService?: ForegroundServiceConfig; // Android only, CarPlay-only title/text/icon
1858
+ channel?: CarPlayChannelConfig; // Android only, CarPlay alert channel
1859
1859
  };
1860
1860
  ```
1861
1861
 
@@ -1884,6 +1884,8 @@ type ForegroundServiceConfig = {
1884
1884
  };
1885
1885
  ```
1886
1886
 
1887
+ Foreground service notifications always use the dedicated Android channel `expo_beacon_foreground_channel`, created with low importance and no sound/vibration. This config only changes the notification content.
1888
+
1887
1889
  ### `NotificationChannelConfig`
1888
1890
 
1889
1891
  ```ts
@@ -2222,6 +2224,8 @@ Local notifications are posted automatically for beacon enter/exit/timeout event
2222
2224
  | Foreground service text | `"Monitoring for iBeacons in the background"` |
2223
2225
  | CarPlay-only foreground title | `"Connected device monitoring active"` |
2224
2226
  | CarPlay-only foreground text | `"Monitoring connected vehicle (CarPlay/Android Auto)"` |
2227
+ | Foreground channel name (Android) | `"Beacon Foreground Service"` |
2228
+ | Foreground channel importance (Android) | `"low"` (no sound/vibration) |
2225
2229
  | Channel name (Android) | `"Beacon Monitoring"` |
2226
2230
  | Channel importance (Android) | `"low"` |
2227
2231
  | CarPlay channel name (Android) | `"CarPlay / Android Auto"` |
@@ -2229,7 +2233,7 @@ Local notifications are posted automatically for beacon enter/exit/timeout event
2229
2233
 
2230
2234
  ### Android Channels
2231
2235
 
2232
- Beacon foreground-service and event notifications use the channel ID `expo_beacon_channel`. CarPlay / Android Auto event notifications use `expo_beacon_carplay_channel`; when Android runs in CarPlay-only mode, the foreground-service notification also uses the CarPlay channel and `carPlay.foregroundService` config.
2236
+ Foreground-service notifications use their own quiet channel ID, `expo_beacon_foreground_channel`, so the persistent status notification does not make sound or vibrate. Beacon event notifications use `expo_beacon_channel`; CarPlay / Android Auto event notifications use `expo_beacon_carplay_channel`. In CarPlay-only mode, the foreground-service notification still uses the quiet foreground channel but reads title/text/icon from `carPlay.foregroundService`.
2233
2237
 
2234
2238
  > **Android channel importance note**: Android prevents decreasing channel importance after the first notification. Increasing works; decreasing has no effect until the user clears notification settings or reinstalls the app.
2235
2239
 
@@ -19,6 +19,7 @@ import org.altbeacon.beacon.*
19
19
  import org.json.JSONArray
20
20
 
21
21
  private const val CHANNEL_ID = "expo_beacon_channel"
22
+ private const val FOREGROUND_CHANNEL_ID = "expo_beacon_foreground_channel"
22
23
  private const val CARPLAY_CHANNEL_ID = "expo_beacon_carplay_channel"
23
24
  internal const val FOREGROUND_NOTIF_ID = 1001
24
25
  /**
@@ -102,6 +103,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
102
103
  override fun onCreate() {
103
104
  super.onCreate()
104
105
  activeService = this
106
+ ensureForegroundNotificationChannel(this)
105
107
  ensureNotificationChannel(this)
106
108
  ensureCarPlayNotificationChannel(this)
107
109
  apiForwarder = BeaconApiForwarder(this)
@@ -1239,6 +1241,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
1239
1241
  .edit()
1240
1242
  .putBoolean(MONITORING_ACTIVE_KEY, true)
1241
1243
  .apply()
1244
+ ensureForegroundNotificationChannel(context)
1242
1245
  ensureNotificationChannel(context)
1243
1246
  ensureCarPlayNotificationChannel(context)
1244
1247
  val intent = Intent(context, BeaconForegroundService::class.java)
@@ -1307,6 +1310,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
1307
1310
  */
1308
1311
  fun enableCarPlay(context: Context) {
1309
1312
  setCarPlayEnabled(context, true)
1313
+ ensureForegroundNotificationChannel(context)
1310
1314
  ensureNotificationChannel(context)
1311
1315
  ensureCarPlayNotificationChannel(context)
1312
1316
  // Arm the WorkManager watchdog (15-min periodic safety net) and the
@@ -1570,12 +1574,38 @@ class BeaconForegroundService : Service(), BeaconConsumer {
1570
1574
  NotificationManager.IMPORTANCE_DEFAULT
1571
1575
  )
1572
1576
 
1577
+ /**
1578
+ * Ensure the persistent foreground-service notification has a quiet channel of its own.
1579
+ * Event channels can be default/high importance, but the always-on service status should
1580
+ * never make sound or vibrate.
1581
+ */
1582
+ fun ensureForegroundNotificationChannel(context: Context) {
1583
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
1584
+ val notifMgr = context.getSystemService(NotificationManager::class.java)
1585
+ if (notifMgr?.getNotificationChannel(FOREGROUND_CHANNEL_ID) == null) {
1586
+ val channel =
1587
+ NotificationChannel(
1588
+ FOREGROUND_CHANNEL_ID,
1589
+ "Beacon Foreground Service",
1590
+ NotificationManager.IMPORTANCE_LOW
1591
+ )
1592
+ .apply {
1593
+ description =
1594
+ "Persistent status for beacon and CarPlay background monitoring"
1595
+ setSound(null, null)
1596
+ enableVibration(false)
1597
+ }
1598
+ notifMgr?.createNotificationChannel(channel)
1599
+ }
1600
+ }
1601
+
1573
1602
  /**
1574
1603
  * Build the persistent foreground-service notification from any Context. Static so
1575
1604
  * cold-start paths that run before a service instance exists (e.g. [BootReceiver]) read the
1576
1605
  * same persisted config.
1577
1606
  */
1578
1607
  fun buildForegroundNotification(context: Context): Notification {
1608
+ ensureForegroundNotificationChannel(context)
1579
1609
  val config = readNotificationConfig(context)
1580
1610
 
1581
1611
  // If beacon monitoring is not active and the service is alive only
@@ -1605,16 +1635,15 @@ class BeaconForegroundService : Service(), BeaconConsumer {
1605
1635
  "foregroundService"
1606
1636
  )
1607
1637
  }
1608
- val channelId = if (carPlayOnly) CARPLAY_CHANNEL_ID else CHANNEL_ID
1609
-
1610
1638
  val title = fgConfig?.optString("title")?.takeIf { it.isNotEmpty() } ?: defaultTitle
1611
1639
  val text = fgConfig?.optString("text")?.takeIf { it.isNotEmpty() } ?: defaultText
1612
1640
 
1613
- return NotificationCompat.Builder(context, channelId)
1641
+ return NotificationCompat.Builder(context, FOREGROUND_CHANNEL_ID)
1614
1642
  .setSmallIcon(resolveIconRes(context, fgConfig))
1615
1643
  .setContentTitle(title)
1616
1644
  .setContentText(text)
1617
1645
  .setPriority(NotificationCompat.PRIORITY_LOW)
1646
+ .setSilent(true)
1618
1647
  .setOngoing(true)
1619
1648
  .build()
1620
1649
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "description": "Expo module for scanning, pairing, and monitoring iBeacons on Android and iOS",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",