Last active
June 3, 2021 22:29
-
-
Save Clancey/fa271da174de95d0a83eef3def46eaea to your computer and use it in GitHub Desktop.
gMusic CarPlay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using System.Linq; | |
namespace gMusic | |
{ | |
public class ArtistPlayableGroup : PlayableGroup | |
{ | |
ArtistViewModel model; | |
ArtistViewModel Model {get{ return model?? (model = new ArtistViewModel()); }} | |
int section = -1; | |
public ArtistPlayableGroup(ArtistViewModel model, int section) | |
{ | |
this.section = section; | |
this.model = model; | |
} | |
public ArtistPlayableGroup () | |
{ | |
Title = "Artists"; | |
Playable = false; | |
} | |
public override int NumberOfItems () | |
{ | |
return section >= 0 ? Model.RowsInSection (section) : Model.NumberOfSections (); | |
} | |
public override IPlayableItem ItemForIndex (int index) | |
{ | |
if (section == -1) | |
return new ArtistPlayableGroup(Model,index){Title = Model.HeaderForSection (index)}; | |
var artist = Model.ItemFor (section, index); | |
return new ArtistPlayable (artist); | |
} | |
public override async Task Load () | |
{ | |
if (section == -1) | |
return; | |
await Task.Run (() => { | |
foreach(var row in Enumerable.Range(0, Math.Min(20,Model.RowsInSection(section)))) | |
{ | |
Model.ItemFor(section,row); | |
} | |
}); | |
Console.WriteLine ("done loading"); | |
} | |
class ArtistPlayable : PlayableGroup | |
{ | |
Artist artist; | |
public ArtistPlayable(Artist artist) | |
{ | |
this.artist = artist; | |
this.Title = artist.Name ?? ""; | |
} | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using MonoTouch.MediaPlayer; | |
using System.Linq; | |
namespace gMusic | |
{ | |
public abstract class LazyLoadingPlayableGroup : PlayableGroup | |
{ | |
public LazyLoadingPlayableGroup () | |
{ | |
} | |
protected abstract int RealNumberOfRows {get;} | |
protected abstract void LoadItem ( int row); | |
public override int NumberOfItems () | |
{ | |
if (realCount == -1) { | |
realCount = RealNumberOfRows; | |
count = Math.Min (50, realCount); | |
} | |
Console.WriteLine ("Requested Count {0}", count); | |
return count; | |
} | |
protected int count = -1; | |
protected int realCount = -1; | |
protected bool finished; | |
protected bool loaded = false; | |
protected Task loading; | |
public override async Task Load () | |
{ | |
if (loaded) | |
return; | |
if (loading != null && !loading.IsCompleted) { | |
await loading; | |
return; | |
} | |
NumberOfItems (); | |
loading = actualLoad (); | |
await loading; | |
if (realCount != count) | |
MPPlayableContentManager.Shared.BeginInvokeOnMainThread (() => loadMore ()); | |
loaded = true; | |
Console.WriteLine ("loading songs complete"); | |
} | |
bool everyOther = true; | |
async Task loadMore() | |
{ | |
if (finished) | |
return; | |
//MPPlayableContentManager.Shared.BeginUpdates (); | |
var oldCount = count; | |
count = Math.Min (realCount, count + 150); | |
await actualLoad (oldCount); | |
if (count == realCount) { | |
finished = true; | |
} | |
everyOther = !everyOther; | |
var isMain = MonoTouch.Foundation.NSThread.IsMain; | |
//MPPlayableContentManager.Shared.EndUpdates (); | |
if (finished || everyOther) | |
MPPlayableContentManager.Shared.ReloadData (); | |
Console.WriteLine ("Reloading--- Count = {0}/{1}", count, realCount); | |
if(!finished) | |
Task.Run (async () => { | |
await Task.Delay(2000); | |
MPPlayableContentManager.Shared.BeginInvokeOnMainThread(()=>{ | |
loadMore(); | |
}); | |
}); | |
} | |
Task actualLoad(int oldCount = 0) | |
{ | |
return Task.Run (() => { | |
foreach(var row in Enumerable.Range(oldCount, count - oldCount)) | |
{ | |
LoadItem(row); | |
}; | |
}); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using MonoTouch.MediaPlayer; | |
using MonoTouch.Foundation; | |
using System.Collections; | |
using MonoTouch.UIKit; | |
using System.Collections.Generic; | |
using SimpleTables; | |
using System.Linq; | |
using System.Threading.Tasks; | |
namespace gMusic | |
{ | |
public interface IPlayableItem | |
{ | |
string Id { get; } | |
string Title {get;} | |
string SubTitle { get; } | |
float PlaybackProgress { get; } | |
bool Playable {get;} | |
UIImage Image { get; set; } | |
MPContentItem GetItem (); | |
void Play(); | |
} | |
public class PlayableItem : IPlayableItem | |
{ | |
public PlayableItem(string id) | |
{ | |
Id = id; | |
Playable = true; | |
} | |
string id; | |
public virtual string Id { | |
get { | |
return id; | |
} | |
set { | |
id = value; | |
} | |
} | |
string title; | |
public virtual string Title { | |
get { | |
return title; | |
} | |
set { | |
title = value; | |
if (item != null) | |
item.Title = title; | |
} | |
} | |
float playbackProgress; | |
public virtual float PlaybackProgress { | |
get { | |
return playbackProgress; | |
} | |
set { | |
playbackProgress = value; | |
if (item != null) | |
item.PlaybackProgress = playbackProgress; | |
} | |
} | |
string subTitle; | |
public string SubTitle { | |
get { | |
return subTitle; | |
} | |
set { | |
subTitle = value; | |
if (item != null) | |
item.Subtitle = subTitle; | |
} | |
} | |
UIImage image; | |
public UIImage Image { | |
get { | |
return image; | |
} | |
set { | |
image = value; | |
if (item == null) | |
return; | |
MPPlayableContentManager.Shared.BeginUpdates(); | |
item.Artwork = image == null ? new MPMediaItemArtwork () : new MPMediaItemArtwork (image); | |
MPPlayableContentManager.Shared.EndUpdates(); | |
} | |
} | |
public virtual bool Playable {get;set;} | |
public virtual bool ChildrenPlayable { | |
get { return false; } | |
} | |
public virtual void Play() | |
{ | |
throw new NotImplementedException (); | |
} | |
protected MPContentItem item; | |
public virtual MPContentItem GetItem () | |
{ | |
if (Id == null) | |
Id = Guid.NewGuid ().ToString (); | |
if (item == null) { | |
item = new MPContentItem (Id) { | |
PlaybackProgress = PlaybackProgress, | |
Title = Title ?? "", | |
Subtitle = subTitle ?? "", | |
Container = false, | |
Playable = Playable, | |
}; | |
if (Image != null) | |
item.Artwork = new MPMediaItemArtwork (image); | |
} | |
return item; | |
} | |
} | |
public class PlayableGroup : PlayableItem, IEnumerable | |
{ | |
public List<IPlayableItem> Items = new List<IPlayableItem> (); | |
public PlayableGroup() : base (Guid.NewGuid ().ToString ()) | |
{ | |
Playable = false; | |
} | |
public string Id {get;set;} | |
public UIImage Image { get; set; } | |
public virtual int NumberOfItems () | |
{ | |
return Items.Count; | |
} | |
public override MPContentItem GetItem () | |
{ | |
var content = base.GetItem (); | |
content.Container = true; | |
return content; | |
} | |
public void Add(IPlayableItem item) | |
{ | |
Items.Add (item); | |
} | |
#region IEnumerable implementation | |
public IEnumerator GetEnumerator () | |
{ | |
foreach (var i in Items) | |
yield return i; | |
} | |
public virtual IPlayableItem ItemForIndex(int index) | |
{ | |
return Items[index]; | |
} | |
public virtual async Task Load() | |
{ | |
} | |
#endregion | |
} | |
public class PlayableItemDelegate : MPPlayableContentDelegate | |
{ | |
public PlayableItemDelegate() | |
{ | |
} | |
public override void PlayableContentManager(MPPlayableContentManager contentManager, NSIndexPath indexPath, Action<NSError> completionHandler) | |
{ | |
NSError error = null; | |
try{ | |
var source = contentManager.DataSource as PlayableGroupSource; | |
var item = source.ItemAt (indexPath); | |
if(item != null) { | |
item.Play (); | |
completionHandler(null); | |
} | |
} | |
catch(Exception ex) { | |
error = new NSError ((NSString)ex.Message, ex.HResult, NSDictionary.FromObjectsAndKeys(ex.Data.Values.Cast<object>().ToArray(),ex.Data.Keys.Cast<object>().ToArray())); | |
} | |
finally{ | |
completionHandler(error); | |
} | |
} | |
} | |
[Register("PlayableGroupSource")] | |
public class PlayableGroupSource : MPPlayableContentDataSource , IMPPlayableContentDelegate, IEnumerable | |
{ | |
public List<IPlayableItem> Items = new List<IPlayableItem> (); | |
public PlayableGroupSource () | |
{ | |
} | |
public override bool ChildItemsDisplayPlaybackProgress (MonoTouch.Foundation.NSIndexPath indexPath) | |
{ | |
return true; | |
} | |
public override int NumberOfChildItems (MonoTouch.Foundation.NSIndexPath indexPath) | |
{ | |
if (indexPath.Length == 0) | |
return Items.Count; | |
var playableGroup = ItemAt (indexPath) as PlayableGroup; | |
return playableGroup != null ? playableGroup.NumberOfItems() : 0; | |
} | |
public override MPContentItem ContentItem (MonoTouch.Foundation.NSIndexPath indexPath) | |
{ | |
var item = ItemAt (indexPath); | |
return item.GetItem (); | |
} | |
Dictionary<NSIndexPath,PlayableGroup> groups = new Dictionary<NSIndexPath, PlayableGroup>(); | |
public IPlayableItem ItemAt(NSIndexPath indexPath) | |
{ | |
if (indexPath.Length == 0) | |
return null; | |
var parent = indexPath.IndexPathByRemovingLastIndex (); | |
var ui = indexPath.GetIndexes (); | |
var indexes = ui.Select(x=> (int)x).ToArray(); | |
PlayableGroup parentGroup; | |
if(groups.TryGetValue(parent,out parentGroup)) | |
{ | |
return parentGroup.ItemForIndex (indexes.Last ()); | |
} | |
var root = Items [indexes [0]]; | |
if (indexes.Length == 1) | |
return root; | |
parentGroup = root as PlayableGroup; | |
if (root == null) { | |
return parentGroup; | |
} | |
for (var i = 1; i < indexes.Length - 1; i++) { | |
var item = parentGroup.ItemForIndex (indexes[i]); | |
parentGroup = item as PlayableGroup; | |
} | |
groups [parent] = parentGroup; | |
return parentGroup.ItemForIndex(indexes.Last ()); | |
} | |
public void Add(IPlayableItem item) | |
{ | |
Items.Add (item); | |
} | |
public override async void BeginLoadingChildItems (NSIndexPath indexPath, Action<NSError> completionHandler) | |
{ | |
if (indexPath.Length > 0) { | |
try{ | |
var item = ItemAt(indexPath) as PlayableGroup; | |
Console.WriteLine("Loading Group: {0}",item.Title); | |
if(item != null) | |
await item.Load(); | |
} | |
catch(Exception ex) { | |
Console.WriteLine (ex); | |
} | |
} | |
completionHandler (null); | |
} | |
public IEnumerator GetEnumerator () | |
{ | |
foreach (var i in Items) | |
yield return i; | |
} | |
[Export ("playableContentManager:initiatePlaybackOfContentItemAtIndexPath:completionHandler:")] | |
public void PlayableContentManager (MPPlayableContentManager contentManager, NSIndexPath indexPath, Action<NSError> completionHandler) | |
{ | |
Console.WriteLine ("Bam"); | |
completionHandler (null); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using System.Linq; | |
using Xamarin.Data; | |
using System.Collections.Generic; | |
using MonoTouch.MediaPlayer; | |
namespace gMusic | |
{ | |
public class SongsPlayableGroup: LazyLoadingPlayableGroup | |
{ | |
SongViewModel model; | |
SongViewModel Model {get{ return model?? (model = new SongViewModel()); }} | |
int section = -1; | |
public SongsPlayableGroup(SongViewModel model, int section) | |
{ | |
this.section = section; | |
this.model = model; | |
} | |
public SongsPlayableGroup () | |
{ | |
Title = "Songs"; | |
} | |
Dictionary<int,IPlayableItem> items = new Dictionary<int, IPlayableItem>(); | |
public override IPlayableItem ItemForIndex (int index) | |
{ | |
IPlayableItem item; | |
if (items.TryGetValue (index, out item)) | |
return item; | |
if (section == -1) { | |
item = new SongsPlayableGroup (Model, index){ Title = Model.HeaderForSection (index) }; | |
} else { | |
var artist = Model.ItemFor (section, index); | |
item = new SongPlayable (artist){ info = model.GroupInfo }; | |
} | |
items [index] = item; | |
return item; | |
} | |
protected override void LoadItem (int row) | |
{ | |
if (section == -1) | |
Model.HeaderForSection (row); | |
else | |
model.ItemFor (section, row); | |
} | |
protected override int RealNumberOfRows | |
{ | |
get{ return section >= 0 ? Model.RowsInSection (section) : Model.NumberOfSections (); } | |
} | |
class SongPlayable : PlayableItem | |
{ | |
Song song; | |
public GroupInfo info; | |
public SongPlayable(Song song) : base (song.Id) | |
{ | |
this.song = song; | |
this.Title = song.Title ?? ""; | |
} | |
public override void Play () | |
{ | |
Playback.PlayAllSongs (song,info,false); | |
} | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mainDataSource = CreateCarplaySource (); | |
MPPlayableContentManager.Shared.DataSource = mainDataSource; | |
//MPPlayableContentManager.Shared.WeakDelegate = mainDataSource; | |
MPPlayableContentManager.Shared.Delegate = playableDel = new PlayableItemDelegate(); | |
public PlayableGroupSource CreateCarplaySource() | |
{ | |
return new PlayableGroupSource{ | |
new PlayableGroup{ | |
Title = "Current Playlist", | |
}, | |
new PlayableGroup{ | |
Title = "Playlists", | |
}, | |
new PlayableGroup{ | |
Title = "Radio", | |
}, | |
new ArtistPlayableGroup(), | |
new PlayableGroup { | |
Title = "Albums", | |
}, | |
new PlayableGroup { | |
Title = "Genres", | |
}, | |
new SongsPlayableGroup() | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment