Migrate Android Auto support to audio_service#223
Conversation
📝 WalkthroughWalkthroughAndroid Auto integration is migrated from custom native and Flutter bridge services to ChangesAudio service Android Auto migration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant AndroidAuto
participant MuslyAudioHandler
participant LibraryProvider
participant PlayerProvider
AndroidAuto->>MuslyAudioHandler: Browse, search, or playback request
MuslyAudioHandler->>LibraryProvider: Request library data
LibraryProvider-->>MuslyAudioHandler: Return media items
MuslyAudioHandler->>PlayerProvider: Invoke playback callback
PlayerProvider-->>MuslyAudioHandler: Update playback or remote state
MuslyAudioHandler-->>AndroidAuto: Publish media-session state
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/providers/library_provider.dart`:
- Around line 474-479: Update the server-offline handling in the song, album,
artist, and playlist branches of the relevant provider method: assign each
downloaded-only filtered collection unconditionally, including empty results,
instead of preserving the full catalog. For playlists, retain only playlists
containing at least one downloaded song, using the existing downloaded ID set
and playlist song data.
In `@lib/providers/player_provider.dart`:
- Line 2755: Update _onCastStateChanged() to initialize setRemotePlayback’s
volume from CastService.mediaState.volume instead of the fixed 50, and keep the
Android Auto remote volume synchronized when subsequent Cast-side volume changes
are reported.
In `@lib/services/audio_handler.dart`:
- Around line 528-538: Update the AudioServiceConfig configuration to set
androidStopForegroundOnPause to false, keeping the audio service foregrounded
during pauses and preserving Android Auto cold-start/resume behavior. Do not add
battery-optimization handling or unrelated configuration changes.
- Around line 342-357: Update setRemotePlayback so the isRemote == false branch
emits a local PlaybackState after adding LocalAndroidPlaybackInfo, replacing the
remote playback state immediately; preserve the existing remote-mode update
behavior and construct the local state using the established playback-state
flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1ae1f6f8-f0f4-488b-a1f3-034764a64229
⛔ Files ignored due to path filters (2)
packages/flutter_chrome_cast/pubspec.lockis excluded by!**/*.lockpubspec.lockis excluded by!**/*.lock
📒 Files selected for processing (16)
android/app/src/main/AndroidManifest.xmlandroid/app/src/main/kotlin/com/musly/musly/AndroidAutoPlugin.ktandroid/app/src/main/kotlin/com/musly/musly/AndroidSystemPlugin.ktandroid/app/src/main/kotlin/com/musly/musly/BluetoothMediaHelper.ktandroid/app/src/main/kotlin/com/musly/musly/LyricsPlugin.ktandroid/app/src/main/kotlin/com/musly/musly/MainActivity.ktandroid/app/src/main/kotlin/com/musly/musly/MusicService.ktlib/main.dartlib/providers/auth_provider.dartlib/providers/library_provider.dartlib/providers/player_provider.dartlib/services/android_auto_service.dartlib/services/android_system_service.dartlib/services/audio_handler.dartlib/services/services.dartpubspec.yaml
💤 Files with no reviewable changes (7)
- lib/services/services.dart
- android/app/src/main/kotlin/com/musly/musly/AndroidAutoPlugin.kt
- lib/services/android_auto_service.dart
- android/app/src/main/kotlin/com/musly/musly/MusicService.kt
- lib/services/android_system_service.dart
- android/app/src/main/kotlin/com/musly/musly/BluetoothMediaHelper.kt
- lib/providers/auth_provider.dart
… for offline mode
…s and better request management
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/l10n/app_localizations_ar.dart`:
- Around line 1848-1851: The audioFocusDenied overrides return an English
fallback instead of locale-specific text. Update audioFocusDenied in
lib/l10n/app_localizations_ar.dart (lines 1848-1851) with the approved Arabic
translation and in lib/l10n/app_localizations_az.dart (lines 1848-1851) with the
approved Azerbaijani translation.
In `@lib/l10n/app_localizations_bn.dart`:
- Around line 1848-1851: Translate the audioFocusDenied getter instead of
leaving the English fallback in each affected locale:
lib/l10n/app_localizations_bn.dart lines 1848-1851,
lib/l10n/app_localizations_de.dart lines 1859-1862,
lib/l10n/app_localizations_el.dart lines 1848-1851, and
lib/l10n/app_localizations_fi.dart lines 1848-1851. Preserve the existing getter
and message meaning while providing Bengali, German, Greek, and Finnish
translations respectively.
In `@lib/l10n/app_localizations_fr.dart`:
- Around line 1868-1870: Replace the English fallback returned by
audioFocusDenied in each locale override with the appropriate locale-specific
translation: lib/l10n/app_localizations_fr.dart lines 1868-1870 (French),
lib/l10n/app_localizations_ga.dart lines 1849-1851 (Irish),
lib/l10n/app_localizations_pt.dart lines 1857-1859 (Portuguese),
lib/l10n/app_localizations_ro.dart lines 1850-1852 (Romanian),
lib/l10n/app_localizations_ru.dart lines 1862-1864 (Russian),
lib/l10n/app_localizations_tr.dart lines 1849-1851 (Turkish),
lib/l10n/app_localizations_uk.dart lines 1849-1851 (Ukrainian), and
lib/l10n/app_localizations_vi.dart lines 1849-1851 (Vietnamese).
In `@lib/l10n/app_localizations_hi.dart`:
- Around line 1848-1851: Replace the English value returned by audioFocusDenied
in lib/l10n/app_localizations_hi.dart (1848-1851),
lib/l10n/app_localizations_id.dart (1848-1851), and
lib/l10n/app_localizations_it.dart (1859-1862) with accurate Hindi, Indonesian,
and Italian translations respectively, preserving the getter and override
structure.
In `@lib/providers/player_provider.dart`:
- Around line 1331-1373: Update _ensureAudioFocus so the try/catch only handles
failures from _androidSystemService.requestAudioFocus(). In the
AudioFocusResult.granted branch, complete the focus-state updates, then invoke
onGranted() outside that guarded request flow so exceptions from playback
propagate to the caller without setting _audioFocusDenied.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 72e0507f-cbd3-43d4-b888-4707b6d0e693
📒 Files selected for processing (34)
android/app/src/main/kotlin/com/musly/musly/AndroidSystemPlugin.ktlib/l10n/app_en.arblib/l10n/app_localizations.dartlib/l10n/app_localizations_ar.dartlib/l10n/app_localizations_az.dartlib/l10n/app_localizations_bn.dartlib/l10n/app_localizations_da.dartlib/l10n/app_localizations_de.dartlib/l10n/app_localizations_el.dartlib/l10n/app_localizations_en.dartlib/l10n/app_localizations_es.dartlib/l10n/app_localizations_fi.dartlib/l10n/app_localizations_fr.dartlib/l10n/app_localizations_ga.dartlib/l10n/app_localizations_hi.dartlib/l10n/app_localizations_id.dartlib/l10n/app_localizations_it.dartlib/l10n/app_localizations_nl.dartlib/l10n/app_localizations_no.dartlib/l10n/app_localizations_pl.dartlib/l10n/app_localizations_pt.dartlib/l10n/app_localizations_ro.dartlib/l10n/app_localizations_ru.dartlib/l10n/app_localizations_sq.dartlib/l10n/app_localizations_sv.dartlib/l10n/app_localizations_te.dartlib/l10n/app_localizations_tr.dartlib/l10n/app_localizations_uk.dartlib/l10n/app_localizations_vi.dartlib/l10n/app_localizations_zh.dartlib/providers/player_provider.dartlib/screens/main_screen.dartlib/services/android_system_service.dartlib/services/audio_handler.dart
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/services/audio_handler.dart
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/l10n/app_az.arb`:
- Line 55: Update the audioFocusDenied translation value to use standard
Azerbaijani passive wording, replacing “başladıla bilmədi” with the appropriate
“başlana bilmədi” phrasing while preserving the rest of the message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7ef6a49a-6138-4fe2-9f71-d696a0177db9
📒 Files selected for processing (37)
android/app/src/main/AndroidManifest.xmlandroid/app/src/main/kotlin/com/musly/musly/MainActivity.ktlib/l10n/app_ar.arblib/l10n/app_az.arblib/l10n/app_bn.arblib/l10n/app_de.arblib/l10n/app_el.arblib/l10n/app_fi.arblib/l10n/app_fr.arblib/l10n/app_ga.arblib/l10n/app_hi.arblib/l10n/app_id.arblib/l10n/app_it.arblib/l10n/app_localizations_ar.dartlib/l10n/app_localizations_az.dartlib/l10n/app_localizations_bn.dartlib/l10n/app_localizations_de.dartlib/l10n/app_localizations_el.dartlib/l10n/app_localizations_fi.dartlib/l10n/app_localizations_fr.dartlib/l10n/app_localizations_ga.dartlib/l10n/app_localizations_hi.dartlib/l10n/app_localizations_id.dartlib/l10n/app_localizations_it.dartlib/l10n/app_localizations_pt.dartlib/l10n/app_localizations_ro.dartlib/l10n/app_localizations_ru.dartlib/l10n/app_localizations_tr.dartlib/l10n/app_localizations_uk.dartlib/l10n/app_localizations_vi.dartlib/l10n/app_pt.arblib/l10n/app_ro.arblib/l10n/app_ru.arblib/l10n/app_tr.arblib/l10n/app_uk.arblib/l10n/app_vi.arblib/providers/player_provider.dart
🚧 Files skipped from review as they are similar to previous changes (18)
- lib/l10n/app_localizations_it.dart
- lib/l10n/app_localizations_fr.dart
- lib/l10n/app_localizations_pt.dart
- lib/l10n/app_localizations_ar.dart
- lib/l10n/app_localizations_vi.dart
- lib/l10n/app_localizations_id.dart
- lib/l10n/app_localizations_uk.dart
- lib/l10n/app_localizations_ga.dart
- lib/l10n/app_localizations_hi.dart
- lib/l10n/app_localizations_de.dart
- lib/l10n/app_localizations_bn.dart
- lib/l10n/app_localizations_ru.dart
- lib/l10n/app_localizations_tr.dart
- lib/l10n/app_localizations_az.dart
- lib/l10n/app_localizations_fi.dart
- lib/l10n/app_localizations_ro.dart
- android/app/src/main/AndroidManifest.xml
- lib/providers/player_provider.dart
| "wordSyncClearCacheSubtitle": "Removes locally stored word timestamps", | ||
| "wordSyncNotSupported": "Not supported on this platform" | ||
| "wordSyncNotSupported": "Not supported on this platform", | ||
| "audioFocusDenied": "Oxutma başladıla bilmədi — audio fokusu başqa tətbiqdədir" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Azerbaijani passive tense "-la/-li "-ıla/-ili" verb "başlamaq" past passive "başlanıla" "başlatıla"
💡 Result:
In Azerbaijani, the correct passive form of the verb başlamaq (to start) is başlanmaq. The specific past passive tense form would be başlanıldı (it was started). The forms başlanıla and başlatıla are incorrect regarding the passive voice of this verb [1][2]. Here is the grammatical breakdown: 1. Passive Formation Rule: Azerbaijani verbs ending in a vowel (like başla-) typically form the passive by adding the suffix -n [1][3][2]. Therefore, başla- + -n- + -maq results in başlanmaq (to be started) [4]. 2. Analysis of Incorrect Forms: - başlanıla: This appears to be a confusion with other verb conjugations or incorrect suffix application. In Azerbaijani, -ıl/-il is the passive suffix for consonants (not vowels), and -ıla is not a standard passive marker for this verb [1][2]. - başlatıla: The suffix -t is used in Azerbaijani to form causative verbs (e.g., başlamaq -> başlatmaq, to cause to start) [5]. Therefore, başlatmaq is the causative form, not the passive form. Attempting to add further suffixes to this root does not create a standard passive [5]. Summary of Forms: - Root: başla- (to start) - Passive: başlan- (to be started) [4] - Causative: başlat- (to cause to start) [5] To express the past passive, you would conjugate the passive stem: başlanıldı (it was started) [1][2].
Citations:
- 1: https://sublearn.com/learn/az/grammar/passive-voice-formation-il-il-ul-ul
- 2: https://talkpal.ai/culture/how-do-you-use-the-passive-voice-in-azerbaijani/
- 3: https://doi.org/10.11647/obp.0516.08
- 4: https://www.turkishtextbook.com/passive-and-reflexive-verbs/
- 5: http://hdl.handle.net/20.500.12323/3191
🌐 Web query:
"başlanıla bilmədi" Azerbaijani
💡 Result:
"Başlanıla bilmədi" Azərbaycan dilində "could not be started" və ya "was not able to be started" mənasına gəlir. Bu ifadə adətən bir prosesin, proqramın, xidmətin və ya işin icrasına başlamaq cəhdinin uğursuzluqla nəticələndiyini bildirən texniki bir xəbər mesajıdır [1][2][3]. Bu ifadənin tərkib hissələri aşağıdakılardır: - Başlanıla: "Başlamaq" (to start) felinin məchul növü (passive voice) və imkan/bacarıq (potential) formasıdır. - Bilmədi: "Bilə bilmədi" (could not) felinin keçmiş zaman formasıdır. Texniki kontekstlərdə, məsələn, kompüter sistemlərində və ya proqram təminatında bir xəta baş verdikdə və sistem işə düşmədikdə, istifadəçilərə bu cür xəbərdarlıqlar göstərilə bilər. Bu, müvafiq icra prosesinin müəyyən səbəblərdən (məsələn, sistem resurslarının çatışmazlığı, faylın zədələnməsi və ya icazə problemləri) yerinə yetirilə bilmədiyini göstərir [4][5].
Citations:
- 1: https://unec.edu.az/application/uploads/2021/02/Mustafazad-Nail.pdf
- 2: https://axar.az/news/toplum/925145.html
- 3: https://cebheinfo.az/dunya/erdogan-terefleri-tecili-dialoqa-cagirib_593
- 4: https://glosbe.com/en/az/error
- 5: https://context.reverso.net/translation/english-turkish/error+occurred
🌐 Web query:
"başlatıla bilmədi" Azerbaijani
💡 Result:
"Başlatıla bilmədi" ifadesi, Azerbaycan Türkçesinde "başlatılamadı" (could not be started) anlamına gelen bir hata mesajı veya durum bildirisidir [1][2]. Bu ifade genellikle bilgisayar yazılımları, oyunlar veya işletim sistemi servislerinin teknik bir nedenden dolayı çalıştırılamadığı durumlarda kullanıcıya gösterilen bir sistem uyarısıdır [1][3][4]. Dilbilgisi açısından analizi şu şekildedir: - Başla-: Kök fiil (start). - -t-: Ettirgenlik eki (make start/launch). - -ıla-: Edilgenlik eki (be started/be launched). - -bil-: Yeterlilik fiili (can/able to). - -mə-: Olumsuzluk eki (not). - -di: Geçmiş zaman kipi (past tense). Bu hata mesajıyla karşılaşıldığında, sorunun kaynağı genellikle eksik veya bozuk sistem dosyaları (örneğin DLL dosyaları), güncel olmayan sürücüler, antivirüs yazılımlarının engellemeleri veya gerekli izinlerin (yönetici yetkisi gibi) verilmemiş olması olabilir [1][3][4][2]. Çözüm için genellikle ilgili yazılımın yönetici olarak çalıştırılması, dosyaların bütünlüğünün doğrulanması (dosya onarma) veya eksik sistem bileşenlerinin (örneğin Microsoft Visual C++ paketleri) yeniden yüklenmesi önerilir [1][3][4][2].
Citations:
- 1: https://www.technopat.net/sosyal/konu/genshin-impact-launcher-baslatilamiyor-hatasi.2155993/
- 2: https://gamerpapers.com/valorant-couldnt-start-hatasi-2024/
- 3: https://technotoday.com.tr/valorant-couldnt-start-hatasi-cozum/
- 4: https://www.lagofast.com/tr/blog/valorant-hata-kodu-57/
🌐 Web query:
"başlandıla bilmədi" Azerbaijani
💡 Result:
"Başlandıla bilmədi" is grammatically incorrect in Azerbaijani [1][2]. The verb "başlamaq" (to start) is typically used in the following ways depending on what you intend to express: 1. Başlana bilmədi: This is the correct passive construction, meaning "could not be started" [3]. It combines the verb root "başla-" with the passive suffix "-n" and the ability/possibility marker "bilmədi" (could not) [3]. 2. Başlaya bilmədi: This is the active construction, meaning "[he/she/it] could not start" [4]. It is used when the subject of the sentence failed to perform the action of starting something [4]. In summary, if you are referring to an action that failed to be initiated (passive voice), use "başlana bilmədi". If you are referring to a person or entity that failed to initiate an action (active voice), use "başlaya bilmədi" [4].
Citations:
- 1: https://home.cydlabs.com/kiwix/content/wiktionary_en_all_maxi_2024-05/A/ba%C5%9Flamaq
- 2: https://sublearn.com/learn/az/grammar/simple-past-tense-formation-di-di-du-du
- 3: https://sublearn.com/learn/az/grammar/passive-with-modal-verbs
- 4: https://dia-az.info/8/print:page,1,434931-starlink-azerbaycanda-fealiyyete-bashlaya-bilmir.html
Fix the Azerbaijani wording.
başladıla is not a standard Azerbaijani form for this context. Use the passive potential form that conveys “could not be started,” such as Oxutma başlana bilmədi — audio fokusu başqa tətbiqdədir, or reconcile the wording with a native speaker.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/l10n/app_az.arb` at line 55, Update the audioFocusDenied translation
value to use standard Azerbaijani passive wording, replacing “başladıla bilmədi”
with the appropriate “başlana bilmədi” phrasing while preserving the rest of the
message.
Problem
Android Auto showed no recent items, no search, and playback couldn't be started from the car. Root cause: the custom MusicService (MediaBrowserServiceCompat) held no data of its own — it relied entirely on the Flutter engine being alive to push library data and answer search/playback requests over a MethodChannel/EventChannel bridge. When Android Auto starts the service directly (app never opened on the phone), MainActivity never runs, so the plugin's event sink stays null and every request from the car is silently dropped. On top of that, a callback registration bug (PlayerProvider was overwriting LibraryProvider's working onRequestLibraryData handler with a no-op) meant the browse tree could stay empty even with the app open.
Fix
Replaced the custom native MediaBrowserServiceCompat + MethodChannel bridge with audio_service, which already backed the iOS lock-screen integration. audio_service hosts its own MediaBrowserService and, when Android Auto connects with the app closed, spawns a headless Flutter engine and runs main() — so the browse tree, search, and playback commands are served by Dart code directly, with no dependency on the UI having been opened first.
Changes
Dart
Android (Kotlin)
Testing
Summary by CodeRabbit