Skip to content

Commit

Permalink
Fix brave/brave-ios#7541: Fixing CarPlay Next and Previous steering w…
Browse files Browse the repository at this point in the history
…heel buttons (brave/brave-ios#7542)

Fixing Next and Previous steering wheel buttons.
Fixing Playback with AsyncAwait issues.
Fixing NowPlaying
Fix last played in CarPlay
Fix previous and next buttons not working properly
Fix playing indicator not updating properly
Fix playing previous and next UI updating so it doesn't pop back and forth.
Made code more efficient when updating the UI instead of generating a whole new UI all over again and reconfiguring it.
Fix view view not updating the title bar when the car changes the item playing.
Fix navigation between phone and car to sync only when the folder changes and the item being played changes.
  • Loading branch information
Brandon-T authored Jun 1, 2023
1 parent b9ddc84 commit d31e645
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,6 @@ class PlaylistCarplayController: NSObject {
}

func observeFolderStates() {
PlaylistCarplayManager.shared.onCarplayUIChangedToRoot.eraseToAnyPublisher()
.receive(on: DispatchQueue.main)
.sink { [weak self] in
guard let self = self else { return }
self.interfaceController.popToRootTemplate(animated: true) { success, error in
if !success, let error = error {
Logger.module.error("\(error.localizedDescription)")
}
}
}.store(in: &playlistObservers)

PlaylistManager.shared.onCurrentFolderDidChange
.receive(on: DispatchQueue.main)
.sink { [weak self] in
Expand Down Expand Up @@ -124,26 +113,8 @@ class PlaylistCarplayController: NSObject {
guard let tabTemplate = self.interfaceController.rootTemplate as? CPTabBarTemplate else {
return
}

// The folder is already showing all its items
var currentTemplate = self.interfaceController.topTemplate as? CPListTemplate

// If the folder's items isn't showing, then we're either on the folder view or settings view
// Therefore we need to push the folder view onto the stack
if currentTemplate == nil || (currentTemplate?.userInfo as? [String: String])?["id"] != PlaylistCarPlayTemplateID.itemsList.rawValue {

// Fetch the root playlistTabTemplate
currentTemplate = tabTemplate.templates.compactMap({ $0 as? CPListTemplate }).first(where: {
($0.userInfo as? [String: String])?["id"] == PlaylistCarPlayTemplateID.itemsList.rawValue
})
}

// We need to ensure the template that is showing is the list of playlist items
guard (currentTemplate?.userInfo as? [String: String])?["id"] == PlaylistCarPlayTemplateID.itemsList.rawValue else {
return
}

if let playlistTabTemplate = currentTemplate {

if let playlistTabTemplate = self.getItemListTemplate() {
let items = playlistTabTemplate.sections.flatMap({ $0.items }).compactMap({ $0 as? CPListItem })

items.forEach({
Expand All @@ -162,7 +133,7 @@ class PlaylistCarplayController: NSObject {
}
})
}

tabTemplate.updateTemplates(tabTemplate.templates)

if self.interfaceController.topTemplate != CPNowPlayingTemplate.shared {
Expand Down Expand Up @@ -239,6 +210,29 @@ class PlaylistCarplayController: NSObject {
reloadData()
}.store(in: &playlistObservers)
}

private func getFolderTemplateList() -> CPListTemplate? {
// Retrieve the tab-bar
guard let tabTemplate = self.interfaceController.rootTemplate as? CPTabBarTemplate else {
return nil
}

// Retrieve the folder list item
let folderTemplate = tabTemplate.templates.compactMap({ $0 as? CPListTemplate }).first(where: {
($0.userInfo as? [String: String])?["id"] == PlaylistCarPlayTemplateID.folders.rawValue
})

return folderTemplate
}

private func getItemListTemplate() -> CPListTemplate? {
// Retrieve the item-list
let listTemplate = interfaceController.templates.compactMap({ $0 as? CPListTemplate }).first(where: {
($0.userInfo as? [String: String])?["id"] == PlaylistCarPlayTemplateID.itemsList.rawValue
})

return listTemplate
}

private func doLayout() {
do {
Expand Down Expand Up @@ -273,48 +267,19 @@ class PlaylistCarplayController: NSObject {
}

private func reloadData() {
guard let tabBarTemplate = interfaceController.rootTemplate as? CPTabBarTemplate else {
return
}

// Need to unwind the navigation stack to the root before attempting any modifications
// Some cars will crash if we replace or modify the RootTemplate while
// an alert of CPNowPlayingTemplate.shared is being displayed
// interfaceController.pop(to: tabBarTemplate, animated: true)

// Update Currently Playing Index before layout (so we can show the playing indicator)
if let itemId = PlaylistCarplayManager.shared.currentPlaylistItem?.tagId {
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = PlaylistManager.shared.index(of: itemId) ?? -1
}

do {
try foldersFRC.performFetch()
try sharedFoldersFRC.performFetch()
} catch {
Logger.module.error("\(error.localizedDescription)")
}


// FOLDERS TEMPLATE
let foldersTemplate = generatePlaylistFolderListTemplate()

// SETTINGS TEMPLATE
let settingsTemplate = generateSettingsTemplate()

// ALL TEMPLATES
let tabTemplates: [CPTemplate] = [
foldersTemplate,
settingsTemplate,
]

// If we have any controllers presented, we need to remove them.
interfaceController.popToRootTemplate(animated: true) { success, error in
if !success, let error = error {
Logger.module.error("\(error.localizedDescription)")
}
guard let foldersTemplate = generatePlaylistFolderListTemplate() as? CPListTemplate else {
Logger.module.error("Invalid Folders Template - NOT A CPListTemplate!")
return
}

// Reload the templates instead of replacing the RootTemplate
tabBarTemplate.updateTemplates(tabTemplates)
let oldFoldersTemplate = getFolderTemplateList()
oldFoldersTemplate?.updateSections(foldersTemplate.sections)
}

private func generatePlaylistFolderListTemplate() -> CPTemplate {
Expand Down Expand Up @@ -413,7 +378,7 @@ class PlaylistCarplayController: NSObject {
let listItem = selectableItem as? CPListItem ?? listItem
listItem?.accessoryType = .none

Task { [listItems] in
Task { @MainActor in
do {
try await self.initiatePlaybackOfItem(itemId: itemId)
} catch {
Expand All @@ -429,12 +394,6 @@ class PlaylistCarplayController: NSObject {

listItem.accessoryType = PlaylistManager.shared.state(for: itemId) != .downloaded ? .cloud : .none

let isPlaying = self.player.isPlaying || self.player.isWaitingToPlay
for item in listItems.enumerated() {
let userInfo = item.element.userInfo as? [String: Any] ?? [:]
item.element.isPlaying = isPlaying && (PlaylistCarplayManager.shared.currentPlaylistItem?.src == userInfo["id"] as? String)
}

let userInfo = listItem.userInfo as? [String: Any]
PlaylistMediaStreamer.setNowPlayingMediaArtwork(image: userInfo?["icon"] as? UIImage)
PlaylistMediaStreamer.updateNowPlayingInfo(self.player)
Expand Down Expand Up @@ -558,13 +517,6 @@ class PlaylistCarplayController: NSObject {
extension PlaylistCarplayController: CPInterfaceControllerDelegate {
func templateWillAppear(_ aTemplate: CPTemplate, animated: Bool) {
Logger.module.debug("Template \(aTemplate.classForCoder) will appear.")

if interfaceController.topTemplate != CPNowPlayingTemplate.shared,
(aTemplate.userInfo as? [String: String])?["id"] == PlaylistCarPlayTemplateID.folders.rawValue {
self.stop()

PlaylistCarplayManager.shared.onCarplayUIChangedToRoot.send()
}
}

func templateDidAppear(_ aTemplate: CPTemplate, animated: Bool) {
Expand Down Expand Up @@ -611,6 +563,7 @@ extension PlaylistCarplayController {
return thumbnailRenderer
}

@MainActor
func initiatePlaybackOfItem(itemId: String) async throws {
guard let index = PlaylistManager.shared.index(of: itemId),
let item = PlaylistManager.shared.itemAtIndex(index)
Expand All @@ -634,16 +587,17 @@ extension PlaylistCarplayController {

// Reset Now Playing when playback is starting.
PlaylistMediaStreamer.clearNowPlayingInfo()

PlaylistCarplayManager.shared.currentPlaylistItem = item
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = index

try await playItem(item: item)

if player.isPlaying {
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = index
do {
PlaylistCarplayManager.shared.currentPlaylistItem = item
} else {
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = index

try await playItem(item: item)

if let item = PlaylistCarplayManager.shared.currentPlaylistItem {
updateLastPlayedItem(item: item)
}
} catch {
PlaylistCarplayManager.shared.currentPlaylistItem = nil
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = -1
}
Expand Down Expand Up @@ -750,6 +704,15 @@ extension PlaylistCarplayController {
PlaylistCarplayManager.shared.currentPlaylistItem = nil
player.stop()

PlaylistManager.shared.playbackTask?.cancel()
PlaylistManager.shared.playbackTask = nil
}

private func clear() {
PlaylistMediaStreamer.clearNowPlayingInfo()
player.clear()

PlaylistManager.shared.playbackTask?.cancel()
PlaylistManager.shared.playbackTask = nil
}

Expand All @@ -772,12 +735,14 @@ extension PlaylistCarplayController {
}
}

@MainActor
func load(url: URL, autoPlayEnabled: Bool) async throws {
try await load(asset: AVURLAsset(url: url, options: AVAsset.defaultOptions), autoPlayEnabled: autoPlayEnabled)
}

@MainActor
func load(asset: AVURLAsset, autoPlayEnabled: Bool) async throws {
player.stop()
self.clear()

let isNewItem = try await player.load(asset: asset)

Expand All @@ -795,6 +760,7 @@ extension PlaylistCarplayController {
}
}

@MainActor
func playItem(item: PlaylistInfo) async throws {
let isPlaying = player.isPlaying

Expand Down Expand Up @@ -828,6 +794,7 @@ extension PlaylistCarplayController {
return try await streamItem(item: item)
}

@MainActor
func streamItem(item: PlaylistInfo) async throws {
let isPlaying = player.isPlaying
var item = item
Expand Down Expand Up @@ -864,8 +831,7 @@ extension PlaylistCarplayController {
return
}

let lastPlayedTime = Preferences.Playlist.playbackLeftOff.value ? playTime.seconds : 0.0
PlaylistItem.updateLastPlayed(itemId: item.tagId, pageSrc: item.pageSrc, lastPlayedOffset: lastPlayedTime)
PlaylistManager.shared.updateLastPlayed(item: item, playTime: playTime.seconds)
}

func displayExpiredResourceError(item: PlaylistInfo?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,7 @@ class PlaylistListViewController: UIViewController {
// Store the last played item's time-offset
if let playTime = delegate?.currentPlaylistItem?.currentTime(),
let item = PlaylistCarplayManager.shared.currentPlaylistItem {
let lastPlayedTime = Preferences.Playlist.playbackLeftOff.value ? playTime.seconds : 0.0
PlaylistItem.updateLastPlayed(itemId: item.tagId, pageSrc: item.pageSrc, lastPlayedOffset: lastPlayedTime)
PlaylistManager.shared.updateLastPlayed(item: item, playTime: playTime.seconds)
}

onCancelEditingItems()
Expand All @@ -206,9 +205,15 @@ class PlaylistListViewController: UIViewController {

folderObserver = nil
if isMovingFromParent || isBeingDismissed {
delegate?.stopPlaying()
if !PlaylistCarplayManager.shared.isCarPlayAvailable {
delegate?.stopPlaying()
}

stopLoadingSharedPlaylist()
PlaylistCarplayManager.shared.onCarplayUIChangedToRoot.send()

if !PlaylistCarplayManager.shared.isCarPlayAvailable {
PlaylistCarplayManager.shared.onCarplayUIChangedToRoot.send()
}
}
}

Expand Down Expand Up @@ -327,9 +332,11 @@ class PlaylistListViewController: UIViewController {

PlaylistManager.shared.playbackTask = Task { @MainActor in
do {
let item = try await delegate.playItem(item: item)
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = indexPath.row
PlaylistCarplayManager.shared.currentPlaylistItem = item

let item = try await delegate.playItem(item: item)

self.commitPlayerItemTransaction(
at: indexPath,
isExpired: false)
Expand All @@ -345,6 +352,8 @@ class PlaylistListViewController: UIViewController {
delegate.updateLastPlayedItem(item: item)
} catch {
PlaylistCarplayManager.shared.currentPlaylistItem = nil
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = -1

self.commitPlayerItemTransaction(
at: indexPath,
isExpired: false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,14 +590,11 @@ extension PlaylistViewController: PlaylistViewControllerDelegate {
}

func updateLastPlayedItem(item: PlaylistInfo) {
Preferences.Playlist.lastPlayedItemUrl.value = item.pageSrc

guard let playTime = player.currentItem?.currentTime() else {
return
}

let lastPlayedTime = Preferences.Playlist.playbackLeftOff.value ? playTime.seconds : 0.0
PlaylistItem.updateLastPlayed(itemId: item.tagId, pageSrc: item.pageSrc, lastPlayedOffset: lastPlayedTime)
PlaylistManager.shared.updateLastPlayed(item: item, playTime: playTime.seconds)
}

func displayLoadingResourceError() {
Expand Down Expand Up @@ -736,6 +733,7 @@ extension PlaylistViewController: VideoViewDelegate {
self.updateLastPlayedItem(item: item)
} catch {
PlaylistCarplayManager.shared.currentPlaylistItem = nil
PlaylistCarplayManager.shared.currentlyPlayingItemIndex = -1
Logger.module.error("Playlist Error Playing Item: \(error)")

if isUserInitiated || self.repeatMode == .repeatOne || assetCount <= 1 {
Expand Down Expand Up @@ -845,13 +843,21 @@ extension PlaylistViewController: VideoViewDelegate {
func toggleRepeatMode(_ videoView: VideoView) {
player.toggleRepeatMode()
}

private func clear() {
PlaylistMediaStreamer.clearNowPlayingInfo()
player.clear()

PlaylistManager.shared.playbackTask?.cancel()
PlaylistManager.shared.playbackTask = nil
}

func load(_ videoView: VideoView, url: URL, autoPlayEnabled: Bool) async throws {
try await load(videoView, asset: AVURLAsset(url: url, options: AVAsset.defaultOptions), autoPlayEnabled: autoPlayEnabled)
}

func load(_ videoView: VideoView, asset: AVURLAsset, autoPlayEnabled: Bool) async throws /*`MediaPlaybackError`*/ {
player.stop()
self.clear()

let isNewItem = try await player.load(asset: asset)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ public class PlaylistManager: NSObject {
var fetchedObjects: [PlaylistItem] {
frc.fetchedObjects ?? []
}

func updateLastPlayed(item: PlaylistInfo, playTime: Double) {
let lastPlayedTime = Preferences.Playlist.playbackLeftOff.value ? playTime : 0.0
Preferences.Playlist.lastPlayedItemUrl.value = item.pageSrc
PlaylistItem.updateLastPlayed(itemId: item.tagId, pageSrc: item.pageSrc, lastPlayedOffset: lastPlayedTime)
}

func itemAtIndex(_ index: Int) -> PlaylistInfo? {
if index >= 0 && index < numberOfAssets {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ class MediaPlayer: NSObject {
// Stop receiving remote commands
UIApplication.shared.endReceivingRemoteControlEvents()
}

func clear() {
player.replaceCurrentItem(with: nil)
pendingMediaItem = nil
}

func load(url: URL) async throws -> Bool {
try await load(asset: AVURLAsset(url: url, options: AVAsset.defaultOptions))
Expand Down

0 comments on commit d31e645

Please sign in to comment.