Skip to content

Instantly share code, notes, and snippets.

@Clancey
Last active June 3, 2021 22:29
Show Gist options
  • Save Clancey/fa271da174de95d0a83eef3def46eaea to your computer and use it in GitHub Desktop.
Save Clancey/fa271da174de95d0a83eef3def46eaea to your computer and use it in GitHub Desktop.
gMusic CarPlay
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 ?? "";
}
}
}
}
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);
};
});
}
}
}
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);
}
}
}
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);
}
}
}
}
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