Skip to content

CarPlay Support w/ llim1 base#1516

Open
APIUM wants to merge 51 commits intoUnicornsOnLSD:redesignfrom
APIUM:pr-1474-flutter-carplay
Open

CarPlay Support w/ llim1 base#1516
APIUM wants to merge 51 commits intoUnicornsOnLSD:redesignfrom
APIUM:pr-1474-flutter-carplay

Conversation

@APIUM
Copy link
Copy Markdown

@APIUM APIUM commented Jan 17, 2026

Adds CarPlay support using flutter_carplay package.

This PR is based on the work from llim1 #1474 adding the finishing touches to get it ready to merge.

Todo before merging

  • install dependencies & configure iOS project
  • basic now-playing screen
  • library tab: albums list
  • library tab: artists list
  • library tab: genres list
  • library tab: playlists list
  • library tab: tracks list
  • search tab (likely requires changes to flutter_carplay) - Replaced this with Siri support, this can come in a follow up PR (Done in this PR now)
  • recently played tab - follow up PR
  • home tab?
  • support offline mode
  • replace static text with localizations
  • rich 'now-playing' screen inc. queue management + album art (likely requires changes to flutter_carplay) - follow up PR
  • now-playing icon on list entries - follow up PR

Known issues

Will cross off issues here once they are addressed.

  • flutter_carplay loads images from web URIs but file URIs are broken despite file paths being listed as supported
  • 'now-playing' screen has inconsistent behaviour on iOS simulator. Need build to physical hardware to test - Fixed
  • 'now-playing' button in root tab view is missing the icon, despite being clickable
  • Translations
  • Extended CONTRIBUTING.md

Related Issues

Closes #24
Original PR #1474

@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch 2 times, most recently from a7c7134 to b2a397f Compare January 17, 2026 03:58
Copy link
Copy Markdown

@llim1 llim1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, also a few notes:

  • known issue "flutter_carplay loads images from web URIs but file URIs are broken despite file paths being listed as supported" is marked as fixed but issue is still present in my environment. Some artwork shows a '?' image where visible
  • jellyfin api related changes not reviewed
  • siri search not tested

Comment thread lib/services/android_auto_helper.dart Outdated
Comment thread pubspec.lock
Comment thread lib/services/queue_service.dart Outdated
Comment thread ios/Runner/RunnerDebug.entitlements Outdated
@APIUM
Copy link
Copy Markdown
Author

APIUM commented Jan 23, 2026

Thanks for the review @llim1

known issue "flutter_carplay loads images from web URIs but file URIs are broken despite file paths being listed as supported" is marked as fixed but issue is still present in my environment. Some artwork shows a '?' image where visible

I misunderstood this originally, I am now able to recreate it, when there were spaces encoded in the url it broke. I've added a fix for this.

@llim1
Copy link
Copy Markdown

llim1 commented Jan 31, 2026

Thanks for the review @llim1

known issue "flutter_carplay loads images from web URIs but file URIs are broken despite file paths being listed as supported" is marked as fixed but issue is still present in my environment. Some artwork shows a '?' image where visible

I misunderstood this originally, I am now able to recreate it, when there were spaces encoded in the url it broke. I've added a fix for this.

Confirmed, images issue is fixed. No further comments from me 👍

@Chaphasilor
Copy link
Copy Markdown
Collaborator

@APIUM @llim1 do any of you already have access to the entitlements? I think Logan hasn't created the developer account yet, but what about Algy?

@Chaphasilor
Copy link
Copy Markdown
Collaborator

@APIUM you mentioned on Discord that we can go back to using the upstream plugin instead of your fork, right?

@APIUM
Copy link
Copy Markdown
Author

APIUM commented Feb 1, 2026

@APIUM you mentioned on Discord that we can go back to using the upstream plugin instead of your fork, right?

Yep I’ll make this change in here

@APIUM
Copy link
Copy Markdown
Author

APIUM commented Feb 1, 2026

@APIUM @llim1 do any of you already have access to the entitlements? I think Logan hasn't created the developer account yet, but what about Algy?

I’m waiting on Apple to approve my personal account, they look about 3 days to ask for ID, and it’s been over a week since then. They say it’s a 2 day process…. I’m all set up in the team so I’ll be able to test very quickly once Apple approves.

@networkoctopus
Copy link
Copy Markdown

really looking forward to this in Finamp. Thanks for working on it!

Can I cheekily request having an instant mix button available on CarPlay. Would love if it was easy to start a good mix (especially for those who use the new audiomuse-ai backend).

@Haar
Copy link
Copy Markdown

Haar commented Feb 22, 2026

image

Just been running this PR locally on a Simulator before trying it out on actual Hardware. Quick question - what's the difference between Shuffle all and Instant Mix?

@APIUM
Copy link
Copy Markdown
Author

APIUM commented Feb 22, 2026

Just been running this PR locally on a Simulator before trying it out on actual Hardware. Quick question - what's the difference between Shuffle all and Instant Mix?

Hi @Haar thanks for having a look.

Shuffle all is just a standard shuffle of all the music in your library.
Instant Mix seeds a Jellyfin 'Instant Mix' with a random album from a list of 10. That list is made from 5 random albums, and 5 frequently played albums. I think this behavior will change as I think Chaphasilor is working on some Instant Mix functionality in the home screen redesign.

@networkoctopus see above for the current Instant Mix situation

Harr please let me know if you find a way to test on real hardware, I am having trouble with profiles and certificates. Still working this out and it's why this PR has stalled.

@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch from 525c5d6 to e053219 Compare February 25, 2026 00:48
@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch 2 times, most recently from c9842c1 to 660c558 Compare March 5, 2026 10:47
@Chaphasilor Chaphasilor mentioned this pull request Mar 14, 2026
25 tasks
Comment thread ios/Runner/AppDelegate.swift Outdated
Comment thread ios/Runner/AppDelegate.swift
Comment thread ios/Runner/Info-Debug.plist Outdated
Comment thread lib/services/audio_service_smtc.dart Outdated
Comment thread lib/services/carplay_helper.dart Outdated
Comment thread lib/services/ios_helpers.dart Outdated
Comment thread lib/services/ios_helpers.dart Outdated
@Chaphasilor
Copy link
Copy Markdown
Collaborator

@APIUM seems like this is coming along nicely! If you resolve the merge conflicts you'll also get CI runs again :)

@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch from 62bc78d to 622ef6a Compare March 22, 2026 10:24
@APIUM
Copy link
Copy Markdown
Author

APIUM commented Mar 22, 2026

@APIUM seems like this is coming along nicely! If you resolve the merge conflicts you'll also get CI runs again :)

Done! Needs approval to run

@Chaphasilor
Copy link
Copy Markdown
Collaborator

also, can you update the todo list at the top to reflect the current status?
oh and don't worry if the code gen - splash screen check fails, that's not your fault

@APIUM
Copy link
Copy Markdown
Author

APIUM commented Mar 25, 2026

also, can you update the todo list at the top to reflect the current status? oh and don't worry if the code gen - splash screen check fails, that's not your fault

Sorry, didn't see this. Have just pushed the formatting fix.

Copy link
Copy Markdown
Collaborator

@Chaphasilor Chaphasilor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, found some things that should probably be updated, but most things look good from my perspective!

Comment thread ios/Runner/AppDelegate.swift Outdated
Comment thread ios/Podfile.lock
Comment thread ios/Podfile.lock
static const androidStopForegroundOnPause = true;
static const onlyShowFavorites = false;
static const trackShuffleItemCount = 250;
static const quickShuffleItemCount = 30;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a description of what this is used for? as in, why do we need this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread lib/services/jellyfin_api.dart
return;
}

// Fetch 1 random track and start continuous radio from it.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to enable radio, then please rename the method. "Instant Mix" is a specific Jellyfin feature and pre-fetches similar tracks in advance. Radio fetches tracks just-in-time, and the Continuous radio mode does not yield the same results as an instant mix. For that, you should use the "Similar" mode instead.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, if this is for the "Instant Mix" quick action, then you should just fetch the items from the correct API instead, or add an itemCount parameter to the existing instant mix method of the audio service helper.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's fair. It was for 'Instant Mix' on the UI. I would like to avoid the queue size issue again so I have renamed this Radio Mix

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no "Radio Mix" - we have "Instant Mix" and "Start Radio" in the UI.
I'm guessing you can't access the translation tokens from CarPlay? If you can, just use the same token that's used in the track or album menu ^^

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread lib/services/carplay_helper.dart
Comment on lines +377 to +379
List<FinampQueueItem> getRecentPlays({int limit = 5}) {
return _queueService.peekQueue(previous: limit);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this used? If the queue is replaced, this might be an empty list. The playback history service could be another source.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right. It's used on the CarPlay home screen. I have changed it to that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really pick up on this, the CarPlay home screen is only built once and there aren't any tracks in there, so it doesn't update. I think I'd rather just make it better in the updated Home Screen so I'm going to leave this as-is (still changing to the playback history service)

Comment thread lib/services/carplay_helper.dart Outdated
Comment on lines +495 to +498
case TabContentType.genres:
showAlbumsTemplate(tabType: parentId.contentType);
case TabContentType.artists:
showArtistsTemplate();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traditionally genres in Finamp were closer to artists than to albums. What made you use the albums template for them here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean you'd get a list of tracks rather than albums? I picked albums as it gives the user a lot more choice with the constrained list length on CarPlay

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So am I wrong in assuming that these templates refer to a screen? So that an album template shows the content of a specific album, and an artist template that of a specific artists?
Because in that case, an album would contain tracks, and an artist could contain a mix of tracks and albums, with an emphasis on albums. That's what I meant by "traditionally": before the redesign, the artist screen in Finamp was just a list of albums, and so was the genre screen.

But if this template refers to how list items are rendered, then of course the genres tab should could show genres that look like "albums" when rendered.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it was a confusing name (since renamed) - yes it's just the list template, not a specific screen. I'd previously only used it for Albums but now it's 'showBrowsableListTemplate' instead.

@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch from 29acc9d to f4d8177 Compare April 8, 2026 12:49
Comment thread lib/services/carplay_helper.dart Outdated
/// Returns a map of imageId -> local file URI string.
/// Items sharing the same imageId are downloaded only once.
Future<Map<String, String>> _preloadCarPlayImages(List<BaseItemDto> items) async {
final Map<String, String> imageUriMap = {};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to resolve the URI with something more like _providers.read(albumImageProvider(AlbumImageRequest(item: item, maxHeight: 200, maxWidth: 200))).uri, which is what android auto does. This covers things like the player image cache, which you missed with this logic. By the way, have we verified that carplay doesn't have an internal cache if we give identical URIs repeatedly?

try mutableDir.setResourceValues(values)
}

// TODO: This is a workaround because audio_service doesn't set MPNowPlayingInfoCenter.playbackState on iOS.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there's already a PR fixing this ryanheise/audio_service#1140, so it might be easier to strip this code out and just switch to that fork until it's merged?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried it won't get merged, it's 7 months old. I'm happy to make a ticket for this and we can update and remove the workaround when it's merged?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is not a ton of messy code that could be avoided by this, it's probably better to keep tracking the regular audio_service repo. I'd like to stay up to date with any other fixes there.

Comment thread lib/services/ios_helpers.dart Outdated
Comment thread lib/services/ios_helpers.dart
Comment thread lib/services/carplay_helper.dart Outdated
Comment thread lib/services/carplay_helper.dart Outdated
Comment thread lib/services/carplay_helper.dart Outdated
Comment thread lib/services/carplay_helper.dart Outdated

/// Maximum items to fetch from server for CarPlay lists.
/// Keeps UI responsive and avoids memory issues on car displays.
const _carPlayOnlineLimit = 250;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice a lot of the lists seem to just cutoff at this limit, and 250 isn't crazy high. Is there a way to add pagination, or at least an indicator that we've cutoff the list?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another I'd like to improve later. CarPlay starts to get laggy above around this - AA is the same. I want to be able to load as we go with pagination.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@APIUM FYI there's an open PR that tries to add pagination to AA, although it's a workaround: #1579
Might be worth looking into at least, since the native capabilities of AA (and CarPlay from what you've told me) are really limited.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was the core concept I was planning to use too - when that's merged then we should have the functions I was missing when I was trying to get this in that made me push it out to a follow up PR. I'll put a link to it in my issue.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't see they were using NameStartsWith - I was having trouble for this use, I've now forgotten as I think there were a couple of issues, but one is that in CarPlay we display by SortName, but that API call doesn't give SortName it gives the regular name so things would show up in the wrong spot. I think there was a bigger showstopper though, it might have been that this causes issues with special characters?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be surprised. Also haven't tested that PR yet.

Comment thread lib/services/carplay_helper.dart Outdated
@Chaphasilor
Copy link
Copy Markdown
Collaborator

@UnicornsOnLSD is anything needed on the App Store side if we release this? I'd like to avoid being rejected because we forgot to change some configuration here or in the store settings 😅

@UnicornsOnLSD
Copy link
Copy Markdown
Owner

There shouldn't be, main thing is getting the entitlement, which we do have (and seems to work in TestFlight builds, which is promising :) )

@APIUM APIUM force-pushed the pr-1474-flutter-carplay branch from 8b4d3a8 to b5ebb17 Compare April 21, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants