-
Notifications
You must be signed in to change notification settings - Fork 6
RG-T133 Fixes #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RG-T133 Fixes #284
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| const { withAndroidManifest, AndroidConfig } = require('expo/config-plugins'); | ||
|
|
||
| const SERVICE_NAME = 'app.notifee.core.ForegroundService'; | ||
|
|
||
|
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Rename
As per coding guidelines, use camelCase for variables and functions. 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| const withForegroundService = (config) => { | ||
| return withAndroidManifest(config, async (config) => { | ||
| const manifest = config.modResults; | ||
|
|
@@ -11,13 +13,22 @@ const withForegroundService = (config) => { | |
| const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(manifest); | ||
|
|
||
| mainApplication['service'] = mainApplication['service'] || []; | ||
| mainApplication['service'].push({ | ||
|
|
||
| // Idempotent: a prebuild that reuses an existing android/ dir already has this service in | ||
| // the base manifest — and non-clean prebuilds have already accumulated duplicates there — so | ||
| // drop every copy before adding the canonical one. | ||
| const serviceEntry = { | ||
| $: { | ||
| 'android:name': 'app.notifee.core.ForegroundService', | ||
| 'android:foregroundServiceType': 'microphone|connectedDevice', | ||
| 'android:name': SERVICE_NAME, | ||
| // microphone only. connectedDevice is intentionally absent — Bluetooth PTT handsets | ||
| // run on the same microphone session, and Play rejects foreground-service types whose | ||
| // use case cannot be demonstrated in the app. | ||
| 'android:foregroundServiceType': 'microphone', | ||
| 'tools:replace': 'android:foregroundServiceType', | ||
| }, | ||
| }); | ||
| }; | ||
| mainApplication['service'] = mainApplication['service'].filter((service) => service?.$?.['android:name'] !== SERVICE_NAME); | ||
| mainApplication['service'].push(serviceEntry); | ||
|
Comment on lines
+20
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository convention files ---'
find /tmp/coderabbit-repo-knowledge/resgrid-unit-b0349c5f -type f -name '*.md' -print
printf '%s\n' '--- scoped convention and learning headers ---'
for f in /tmp/coderabbit-repo-knowledge/resgrid-unit-b0349c5f/*/*.md; do
printf '\n### %s\n' "$f"
head -5 "$f"
done
printf '%s\n' '--- changed files and relevant source ---'
for f in customManifest.plugin.js app.config.ts src/stores/app/livekit-store.ts; do
if [ -f "$f" ]; then
printf '\n### %s\n' "$f"
case "$f" in
customManifest.plugin.js) sed -n '1,80p' "$f" ;;
app.config.ts) sed -n '90,135p' "$f" ;;
src/stores/app/livekit-store.ts) sed -n '680,790p' "$f" ;;
esac
fi
done
printf '%s\n' '--- Bluetooth PTT and foreground-service references ---'
rg -n -i --glob '!node_modules' --glob '!dist' 'Bluetooth|PTT|connectedDevice|foregroundServiceTypes|FOREGROUND_SERVICE_CONNECTED_DEVICE|SERVICE_NAME|startForeground|AndroidForegroundServiceType' .Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- convention scopes ---'
find /tmp/coderabbit-repo-knowledge/resgrid-unit-b0349c5f -type f -name '*.md' -print0 |
while IFS= read -r -d '' f; do
case "$f" in
*/learnings/*|*/coding-guidelines/*|*/review-guidelines/*)
printf '\n### %s\n' "$f"
head -80 "$f"
;;
esac
done
printf '%s\n' '--- customManifest.plugin.js ---'
cat -n customManifest.plugin.js
printf '%s\n' '--- app.config.ts target ---'
sed -n '1,145p' app.config.ts
printf '%s\n' '--- livekit-store imports and FGS path ---'
rg -n -C 12 --glob 'livekit-store.ts' \
'AndroidForegroundServiceType|foregroundServiceTypes|Bluetooth|PTT|preferredBluetooth|startAudio|connect|publish|microphone|background' src/stores/app/livekit-store.ts
printf '%s\n' '--- package and config references ---'
rg -n -C 3 --glob 'package.json' --glob 'app.config.ts' --glob '*.js' --glob '*.ts' --glob '*.tsx' \
'livekit|foregroundService|customManifest|connectedDevice|BluetoothA2dp|BluetoothHeadset|AudioManager' .Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Bluetooth audio service ---'
fd -i 'bluetooth-audio-service' .
for f in $(fd -i 'bluetooth-audio-service' .); do
printf '\n### %s\n' "$f"
cat -n "$f"
done
printf '%s\n' '--- PTT input and media-button call sites ---'
rg -n -C 8 --glob '*.ts' --glob '*.tsx' --glob '*.js' \
'ensurePttInputMonitoring|setLastButtonAction|MediaButton|media button|BluetoothHeadset|BluetoothA2dp|BLUETOOTH|AudioDevice|onKey|PTT' src plugins customManifest.plugin.js app.config.ts
printf '%s\n' '--- startup Notifee foreground-service registration ---'
rg -n -C 15 --glob '*.ts' --glob '*.tsx' --glob '*.js' \
'registerForegroundService|stopForegroundService|displayNotification|ForegroundServiceType|foregroundServiceTypes' src plugins customManifest.plugin.js app.config.ts
printf '%s\n' '--- exact diff summary and changed hunks ---'
git diff --stat
git diff -- customManifest.plugin.js app.config.ts src/stores/app/livekit-store.tsRepository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Bluetooth service PTT implementation slices ---'
sed -n '1,180p' src/services/bluetooth-audio.service.ts
sed -n '1760,2145p' src/services/bluetooth-audio.service.ts
sed -n '2240,2345p' src/services/bluetooth-audio.service.ts
printf '%s\n' '--- Notifee registration and Android service contract references ---'
rg -n -C 10 --glob '*.ts' --glob '*.tsx' --glob '*.js' \
'registerForegroundService|displayNotification|foregroundServiceTypes|AndroidForegroundServiceType|serviceEntry|app.notifee.core.ForegroundService' \
src app.config.ts customManifest.plugin.js plugins
printf '%s\n' '--- Android permissions and Bluetooth declarations ---'
rg -n -C 5 --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.json' \
'BLUETOOTH|ACCESS_FINE_LOCATION|ACCESS_COARSE_LOCATION|FOREGROUND_SERVICE_CONNECTED_DEVICE|FOREGROUND_SERVICE_MICROPHONE|RECORD_AUDIO' \
app.config.ts customManifest.plugin.js android src pluginsRepository: Resgrid/Unit Length of output: 50368 🌐 Web query:
💡 Result: On Android, the connectedDevice foreground service type is specifically designed for interactions with external hardware—such as Bluetooth devices, USB accessories, or NFC-enabled devices—that require a persistent connection [1][2]. It is not, however, the primary service type for managing microphone audio capture [1][2]. If your application requires ongoing microphone access (e.g., for VoIP calls or voice recording) alongside Bluetooth connectivity, you must use the microphone foreground service type instead [1][2]. Key distinctions and requirements: 1. Foreground Service Types: - connectedDevice: Used for maintaining connections to external devices (Bluetooth, USB, etc.). It requires the FOREGROUND_SERVICE_CONNECTED_DEVICE permission and at least one runtime permission related to the device (e.g., BLUETOOTH_CONNECT) or hardware-specific permission [1][3]. - microphone: Used for background audio capture (e.g., communication apps). It requires the FOREGROUND_SERVICE_MICROPHONE permission and the RECORD_AUDIO runtime permission [1][2]. 2. Audio Routing for Bluetooth: To manage audio routing to a Bluetooth headset, do not rely solely on the connectedDevice service type. Instead, use the Android Audio and Telecom APIs to control routing: - For VoIP/Communication Apps: Use AudioManager.setCommunicationDevice to explicitly route audio to a specific Bluetooth headset [4]. This API is the modern standard for controlling communication audio and is backward-compatible with older Hands-Free Profile (HFP) devices [4]. - Using the Telecom API: For managed calls, use the ConnectionService and Connection classes [5]. The modern approach involves using InCallService.requestCallEndpointChange to switch audio routes to specific endpoints (like Bluetooth) [6][7]. In summary, the connectedDevice service type is for managing the connection to the hardware itself, while the microphone service type is for the audio stream, and the AudioManager or Telecom APIs are used to programmatically direct the audio routing to the desired Bluetooth device [1][4][5]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- BLE operations used for PTT ---'
rg -n -C 6 --glob 'bluetooth-audio.service.ts' \
'BleManager\.(connect|disconnect|startNotification|stopNotification|read|retrieveServices|getConnectedPeripherals)|monitor|startScan|connectToDevice|ensurePttInputMonitoring|setupEventListeners|handle.*Button' \
src/services/bluetooth-audio.service.ts
printf '%s\n' '--- Bluetooth permission handling ---'
rg -n -C 10 --glob 'bluetooth-audio.service.ts' --glob 'app.config.ts' \
'PermissionsAndroid|BLUETOOTH|ACCESS_FINE_LOCATION|ACCESS_COARSE_LOCATION|requestPermissions' \
src/services/bluetooth-audio.service.ts app.config.ts
printf '%s\n' '--- precise foreground-service and BLE declarations ---'
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.xml' --glob '*.json' \
'FOREGROUND_SERVICE_CONNECTED_DEVICE|FOREGROUND_SERVICE_MICROPHONE|android:foregroundServiceType|foregroundServiceTypes|BleManager\.(connect|startNotification|read|retrieveServices)' .Repository: Resgrid/Unit Length of output: 33127 Declare
📍 Affects 3 files
🤖 Prompt for AI Agents |
||
| return config; | ||
| }); | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Resgrid/Unit
Length of output: 11864
🏁 Script executed:
Repository: Resgrid/Unit
Length of output: 50368
🌐 Web query:
Expo SDK 56 expo-secure-store faceIDPermission expo-location motionUsagePermission config plugin documentation💡 Result:
In Expo SDK 56 and later, both expo-secure-store and expo-location provide built-in config plugins to manage required iOS permissions and native settings, which are particularly important when using Continuous Native Generation (CNG) [1][2][3][4]. expo-secure-store: faceIDPermission The expo-secure-store config plugin supports a faceIDPermission property to configure the required NSFaceIDUsageDescription key in Info.plist [1][5]. - Property: faceIDPermission (iOS only) [1][6] - Default: "Allow$(PRODUCT_NAME) to access your Face ID biometric data." [1][6] - Usage: Set this to a string to define your custom permission message, or false to omit the key [5][6]. - Note: This is required when using the requireAuthentication option [1][7]. The feature is not supported in Expo Go due to the missing permission key, and you must use the config plugin (or manual Info.plist entry) in release builds [1][7]. expo-location: motionUsagePermission The expo-location config plugin supports a motionUsagePermission property to configure the required NSMotionUsageDescription key in Info.plist [4]. - Property: motionUsagePermission (iOS only) [4] - Default: "Allow $ (PRODUCT_NAME) to detect your current motion activity" [4] - Usage: Set this to a string to provide a custom message, or false to omit the key [4]. - Warning: Setting this to false will delete the key from your Info.plist, but the app will still link Core Motion, which may cause Apple App Store rejection (ITMS-90683) if the API is used without a proper description [8][9]. Do not combine false with manual entries in ios.infoPlist, as the plugin will remove them [8][9]. Example Configuration (app.json) { "expo": { "plugins": [ [ "expo-secure-store", { "faceIDPermission": "Custom Face ID usage description" } ], [ "expo-location", { "motionUsagePermission": "Custom motion usage description" } ] ] } }
Citations:
🏁 Script executed:
Repository: Resgrid/Unit
Length of output: 23198
🏁 Script executed:
Repository: Resgrid/Unit
Length of output: 22899
Correct the iOS permission disclosures.
The app persists authentication data through
react-native-mmkv, notexpo-secure-store, and usesexpo-locationGPS coordinates without callinggetMotionActivityAsync()or another Core Motion API. Update both strings so they describe the native linkage requirement without claiming Face ID or motion-data use.🤖 Prompt for AI Agents