From 785be4b208afe2e43d80f0c8a64d9a2090071e3b Mon Sep 17 00:00:00 2001
From: Tristan Roux
Date: Thu, 11 Apr 2019 04:45:40 +0200
Subject: [PATCH] Totally reworking the get playlist part of the app. It's now
cleaner. Reseting the player loadbar when switching to a song witch is not
available anymore.
---
Opus/MainActivity.cs | 2 +-
Opus/Resources/Portable Class/Home.cs | 6 +-
Opus/Resources/Portable Class/MusicPlayer.cs | 9 +
Opus/Resources/Portable Class/Playlist.cs | 563 +++++++++++--------
4 files changed, 329 insertions(+), 251 deletions(-)
diff --git a/Opus/MainActivity.cs b/Opus/MainActivity.cs
index ef516c6..bc9c941 100644
--- a/Opus/MainActivity.cs
+++ b/Opus/MainActivity.cs
@@ -964,7 +964,7 @@ namespace Opus
public void NotStreamable(string title)
{
- Snackbar snackBar = Snackbar.Make(FindViewById(Resource.Id.snackBar), title + Resource.String.not_streamable, Snackbar.LengthLong);
+ Snackbar snackBar = Snackbar.Make(FindViewById(Resource.Id.snackBar), title + GetString(Resource.String.not_streamable), Snackbar.LengthLong);
snackBar.View.FindViewById(Resource.Id.snackbar_text).SetTextColor(Color.White);
snackBar.Show();
}
diff --git a/Opus/Resources/Portable Class/Home.cs b/Opus/Resources/Portable Class/Home.cs
index ba64a17..75733ca 100644
--- a/Opus/Resources/Portable Class/Home.cs
+++ b/Opus/Resources/Portable Class/Home.cs
@@ -56,12 +56,12 @@ namespace Opus.Resources.Portable_Class
if (adapter != null)
ListView.SetAdapter(adapter);
else
- PopulateSongs();
+ PopulateView();
return view;
}
#pragma warning restore CS4014
- private /*async Task */ void PopulateSongs()
+ private /*async Task */ void PopulateView()
{
if (!populating)
{
@@ -398,7 +398,7 @@ namespace Opus.Resources.Portable_Class
public async Task Refresh()
{
- /*await*/ PopulateSongs();
+ /*await*/ PopulateView();
await Task.Delay(0); //WE DONT NEED TO TALK ABOUT THIS
}
diff --git a/Opus/Resources/Portable Class/MusicPlayer.cs b/Opus/Resources/Portable Class/MusicPlayer.cs
index 3a6a3ce..968e2b4 100644
--- a/Opus/Resources/Portable Class/MusicPlayer.cs
+++ b/Opus/Resources/Portable Class/MusicPlayer.cs
@@ -554,6 +554,9 @@ namespace Opus.Resources.Portable_Class
if (MainActivity.instance != null)
MainActivity.instance.FindViewById(Resource.Id.ytProgress).Visibility = ViewStates.Gone;
song.IsParsed = false;
+
+ if (startPlaybackWhenPosible)
+ Player.instance?.Ready();
return null;
}
catch(YoutubeExplode.Exceptions.VideoUnplayableException ex)
@@ -566,6 +569,9 @@ namespace Opus.Resources.Portable_Class
song.IsParsed = false;
if (position != -1)
RemoveFromQueue(position); //Remove the song from the queue since it can't be played.
+
+ if(startPlaybackWhenPosible)
+ Player.instance?.Ready();
return null;
}
catch(YoutubeExplode.Exceptions.VideoUnavailableException)
@@ -577,6 +583,9 @@ namespace Opus.Resources.Portable_Class
song.IsParsed = false;
if (position != -1)
RemoveFromQueue(position); //Remove the song from the queue since it can't be played.
+
+ if (startPlaybackWhenPosible)
+ Player.instance?.Ready();
return null;
}
return song;
diff --git a/Opus/Resources/Portable Class/Playlist.cs b/Opus/Resources/Portable Class/Playlist.cs
index ba45afd..f8acc86 100644
--- a/Opus/Resources/Portable Class/Playlist.cs
+++ b/Opus/Resources/Portable Class/Playlist.cs
@@ -18,6 +18,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using static Android.Provider.MediaStore.Audio;
using CursorLoader = Android.Support.V4.Content.CursorLoader;
+using Application = Android.App.Application;
namespace Opus.Resources.Portable_Class
{
@@ -64,70 +65,34 @@ namespace Opus.Resources.Portable_Class
{
populating = true;
- //Synced playlists
- List SyncedPlaylists = new List();
- await Task.Run(() =>
- {
- SQLiteConnection db = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
- db.CreateTable();
-
- SyncedPlaylists = db.Table().ToList();
- });
-
//Initialisation
LocalPlaylists.Clear();
YoutubePlaylists.Clear();
LocalPlaylists.Add(new PlaylistItem("Header", -1));
YoutubePlaylists.Add(new PlaylistItem("Header", null));
PlaylistItem Loading = new PlaylistItem("Loading", null);
+
+ //Get all local playlist and display an error message if we have an error.
+ (List locPlaylists, string error) = await GetLocalPlaylists();
- if (await MainActivity.instance.GetReadPermission())
- {
- //Local playlists
- Android.Net.Uri uri = Playlists.ExternalContentUri;
- CursorLoader loader = new CursorLoader(Android.App.Application.Context, uri, null, null, null, null);
- ICursor cursor = (ICursor)loader.LoadInBackground();
+ if (instance == null)
+ return;
- if (cursor != null && cursor.MoveToFirst())
- {
- int nameID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Name);
- int listID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Id);
- do
- {
- string name = cursor.GetString(nameID);
- long id = cursor.GetLong(listID);
+ if (locPlaylists == null) //an error has occured
+ LocalPlaylists.Add(new PlaylistItem("EMPTY", -1) { Owner = error });
- PlaylistItem ytPlaylist = SyncedPlaylists.Find(x => x.LocalID == id);
- if (ytPlaylist == null)
- {
- Android.Net.Uri musicUri = Playlists.Members.GetContentUri("external", id);
- CursorLoader cursorLoader = new CursorLoader(Android.App.Application.Context, musicUri, null, null, null, null);
- ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
- LocalPlaylists.Add(new PlaylistItem(name, id, musicCursor.Count));
- }
- else
- {
- if (ytPlaylist.YoutubeID == null)
- ytPlaylist.SyncState = SyncState.Loading;
- else
- ytPlaylist.SyncState = SyncState.True;
+ //Handle synced playlist from the local playlist array we had before.
+ (List loc, List SyncedPlaylists) = await ProcessSyncedPlaylists(locPlaylists);
- YoutubePlaylists.Add(ytPlaylist);
- }
- }
- while (cursor.MoveToNext());
- cursor.Close();
- }
+ if (instance == null)
+ return;
- if (LocalPlaylists.Count == 1)
- LocalPlaylists.Add(new PlaylistItem("EMPTY", -1) { Owner = Resources.GetString(Resource.String.local_playlist_empty) });
- }
- else
- {
- LocalPlaylists.Add(new PlaylistItem("EMPTY", -1) { Owner = Resources.GetString(Resource.String.no_permission) });
- }
+ LocalPlaylists.AddRange(loc);
+ YoutubePlaylists.AddRange(SyncedPlaylists);
+
+ //Display this for now, we'll load non synced youtube playlist in the background.
YoutubePlaylists.Add(Loading);
adapter = new PlaylistAdapter(LocalPlaylists, YoutubePlaylists);
ListView.SetAdapter(adapter);
@@ -135,220 +100,324 @@ namespace Opus.Resources.Portable_Class
adapter.ItemLongCLick += ListView_ItemLongClick;
ListView.SetItemAnimator(new DefaultItemAnimator());
- //Youtube playlists
- if (!await MainActivity.instance.WaitForYoutube())
+ //Youtube owned playlists
+ (List yt, string err) = await GetOwnedYoutubePlaylists(SyncedPlaylists);
+
+ if (instance == null)
+ return;
+
+ if (yt == null)
{
YoutubePlaylists.Remove(Loading);
adapter.NotifyItemRemoved(LocalPlaylists.Count + YoutubePlaylists.Count);
-
- for (int i = 1; i < YoutubePlaylists.Count; i++)
- {
- YoutubePlaylists[i].SyncState = SyncState.Error;
- PlaylistHolder holder = (PlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(LocalPlaylists.Count + i));
- holder.sync.SetImageResource(Resource.Drawable.SyncError);
- holder.sync.Visibility = ViewStates.Visible;
- holder.SyncLoading.Visibility = ViewStates.Gone;
- if (MainActivity.Theme == 1)
- holder.sync.SetColorFilter(Color.White);
- }
-
- YoutubePlaylists.Add(new PlaylistItem("Error", null));
+ YoutubePlaylists.Add(new PlaylistItem("Error", null)); //Should use the "err" var here
adapter.NotifyItemInserted(LocalPlaylists.Count + YoutubePlaylists.Count);
populating = false;
+ SyncError();
return;
}
- int YtCount = YoutubePlaylists.Count;
-
- try
+ else
{
- YouTubeService youtube = YoutubeEngine.youtubeService;
-
- PlaylistsResource.ListRequest request = youtube.Playlists.List("snippet,contentDetails");
- request.Mine = true;
- request.MaxResults = 25;
- PlaylistListResponse response = await request.ExecuteAsync();
-
- if (instance == null)
- return;
-
- for (int i = 0; i < response.Items.Count; i++)
- {
- Google.Apis.YouTube.v3.Data.Playlist playlist = response.Items[i];
- PlaylistItem item = new PlaylistItem(playlist.Snippet.Title, playlist.Id, playlist, (int)playlist.ContentDetails.ItemCount)
- {
- Owner = playlist.Snippet.ChannelTitle,
- ImageURL = playlist.Snippet.Thumbnails.High.Url,
- HasWritePermission = true
- };
-
- PlaylistItem syncedItem = SyncedPlaylists.Find(x => x.YoutubeID == item.YoutubeID);
- if (SyncedPlaylists.Find(x => x.YoutubeID == item.YoutubeID) != null)
- {
- int position = YoutubePlaylists.FindIndex(x => x.Name == item.Name);
- YoutubePlaylists[position].Snippet = item.Snippet;
- YoutubePlaylists[position].Count = item.Count;
- SyncedPlaylists.RemoveAll(x => x.YoutubeID == item.YoutubeID);
- }
- else if (SyncedPlaylists.Find(x => x.Name == item.Name) != null)
- {
- item.LocalID = SyncedPlaylists.Find(x => x.Name == item.Name).LocalID;
- int position = YoutubePlaylists.FindIndex(x => x.Name == item.Name);
- YoutubePlaylists[position] = item;
- YoutubePlaylists[position].SyncState = SyncState.True;
-
- PlaylistHolder holder = (PlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(LocalPlaylists.Count + position));
- holder.Owner.Text = item.Owner;
- Picasso.With(Android.App.Application.Context).Load(item.ImageURL).Placeholder(Resource.Color.background_material_dark).Resize(400, 400).CenterCrop().Into(holder.AlbumArt);
- holder.edit.Visibility = ViewStates.Visible;
- if (MainActivity.Theme == 1)
- holder.edit.SetColorFilter(Color.White);
- holder.sync.SetImageResource(Resource.Drawable.Sync);
- holder.sync.Visibility = ViewStates.Visible;
- holder.SyncLoading.Visibility = ViewStates.Gone;
- if (MainActivity.Theme == 1)
- holder.sync.SetColorFilter(Color.White);
-
- Task.Run(() =>
- {
- SQLiteConnection db = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
- db.CreateTable();
- db.InsertOrReplace(item);
- });
- SyncedPlaylists.RemoveAll(x => x.LocalID == item.LocalID);
- }
- else
- YoutubePlaylists.Add(item);
- }
-
- if(YtCount != YoutubePlaylists.Count)
- {
- YoutubePlaylists.Remove(Loading);
- YoutubePlaylists.Add(Loading);
- adapter.NotifyItemMoved(LocalPlaylists.Count + YoutubePlaylists.IndexOf(Loading), LocalPlaylists.Count + YoutubePlaylists.Count);
- }
- adapter.NotifyItemRangeInserted(LocalPlaylists.Count + YoutubePlaylists.Count + 1 - YtCount, YoutubePlaylists.Count - YtCount);
- YtCount = YoutubePlaylists.Count;
-
- //Saved playlists
- ChannelSectionsResource.ListRequest forkedRequest = youtube.ChannelSections.List("snippet,contentDetails");
- forkedRequest.Mine = true;
- ChannelSectionListResponse forkedResponse = await forkedRequest.ExecuteAsync();
- if (instance == null)
- return;
-
- foreach (ChannelSection section in forkedResponse.Items)
- {
- if (section.Snippet.Title == "Saved Playlists")
- {
- for (int i = 0; i < section.ContentDetails.Playlists.Count; i++)
- {
- PlaylistsResource.ListRequest plRequest = youtube.Playlists.List("snippet, contentDetails");
- plRequest.Id = section.ContentDetails.Playlists[i];
- PlaylistListResponse plResponse = await plRequest.ExecuteAsync();
-
- if (instance == null)
- return;
-
- Google.Apis.YouTube.v3.Data.Playlist playlist = plResponse.Items[i];
- playlist.Kind = "youtube#saved";
- PlaylistItem item = new PlaylistItem(playlist.Snippet.Title, playlist.Id, playlist, (int)playlist.ContentDetails.ItemCount)
- {
- Owner = playlist.Snippet.ChannelTitle,
- ImageURL = playlist.Snippet.Thumbnails.High.Url,
- HasWritePermission = false
- };
-
- if (SyncedPlaylists.Find(x => x.YoutubeID == item.YoutubeID) != null)
- {
- int position = YoutubePlaylists.FindIndex(x => x.Name == item.Name);
- YoutubePlaylists[position].Snippet = item.Snippet;
- YoutubePlaylists[position].Count = item.Count;
- YoutubePlaylists[position].SyncState = SyncState.True;
- SyncedPlaylists.RemoveAll(x => x.YoutubeID == item.YoutubeID);
- }
- else if (SyncedPlaylists.Find(x => x.Name == item.Name) != null)
- {
- item.LocalID = SyncedPlaylists.Find(x => x.Name == item.Name).LocalID;
- int position = YoutubePlaylists.FindIndex(x => x.Name == item.Name);
- YoutubePlaylists[position] = item;
- YoutubePlaylists[position].SyncState = SyncState.True;
-
- PlaylistHolder holder = (PlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(LocalPlaylists.Count + position));
- holder.Owner.Text = item.Owner;
- Picasso.With(Android.App.Application.Context).Load(item.ImageURL).Placeholder(Resource.Color.background_material_dark).Resize(400, 400).CenterCrop().Into(holder.AlbumArt);
- holder.edit.Visibility = ViewStates.Gone;
- holder.sync.SetImageResource(Resource.Drawable.Sync);
- holder.sync.Visibility = ViewStates.Visible;
- holder.SyncLoading.Visibility = ViewStates.Gone;
- if (MainActivity.Theme == 1)
- holder.sync.SetColorFilter(Color.White);
-
- Task.Run(() =>
- {
- SQLiteConnection db = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
- db.CreateTable();
- db.InsertOrReplace(item);
- });
- SyncedPlaylists.RemoveAll(x => x.LocalID == item.LocalID);
- }
- else
- YoutubePlaylists.Add(item);
- }
- }
- }
-
- YoutubePlaylists.Remove(Loading);
- adapter.NotifyItemRemoved(LocalPlaylists.Count + YtCount);
-
- if (YoutubePlaylists.Count == 1)
- {
- YoutubePlaylists.Add(new PlaylistItem("EMPTY", null) { Owner = Resources.GetString(Resource.String.youtube_playlist_empty) });
- }
- adapter.NotifyItemRangeInserted(LocalPlaylists.Count + YoutubePlaylists.Count + 1 - YtCount, YoutubePlaylists.Count - YtCount);
- adapter.forkSaved = true;
-
- if (SyncedPlaylists.Count > 0)
- {
- List BadSync = SyncedPlaylists.FindAll(x => x.SyncState == SyncState.Loading);
- LocalPlaylists.AddRange(BadSync);
-
- for (int i = 0; i < SyncedPlaylists.Count; i++)
- SyncedPlaylists[i].SyncState = SyncState.Error;
-
- if(BadSync.Count > 0)
- {
- if (LocalPlaylists[1].Name == "EMPTY")
- {
- LocalPlaylists.RemoveAt(1);
- adapter.NotifyItemRemoved(1);
- }
- adapter.NotifyItemRangeInserted(LocalPlaylists.Count - SyncedPlaylists.Count, BadSync.Count);
- }
- }
+ int startPos = YoutubePlaylists.Count - 1;
+ YoutubePlaylists.InsertRange(startPos, yt);
+ adapter.NotifyItemRangeInserted(LocalPlaylists.Count + startPos, yt.Count);
}
- catch (System.Net.Http.HttpRequestException)
+
+ //Youtube saved playlists
+ (yt, error) = await GetSavedYoutubePlaylists(SyncedPlaylists);
+
+ if (instance == null)
+ return;
+
+ if (yt == null)
{
YoutubePlaylists.Remove(Loading);
adapter.NotifyItemRemoved(LocalPlaylists.Count + YoutubePlaylists.Count);
-
- for (int i = 1; i < YoutubePlaylists.Count; i++)
- {
- YoutubePlaylists[i].SyncState = SyncState.Error;
- PlaylistHolder holder = (PlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(LocalPlaylists.Count + i));
- holder.sync.SetImageResource(Resource.Drawable.SyncError);
- holder.sync.Visibility = ViewStates.Visible;
- holder.SyncLoading.Visibility = ViewStates.Gone;
- if (MainActivity.Theme == 1)
- holder.sync.SetColorFilter(Color.White);
- }
-
- YoutubePlaylists.Add(new PlaylistItem("Error", null));
+ YoutubePlaylists.Add(new PlaylistItem("Error", null)); //Should use the "error" var here
adapter.NotifyItemInserted(LocalPlaylists.Count + YoutubePlaylists.Count);
+ populating = false;
+ SyncError();
+ return;
}
+ else
+ {
+ YoutubePlaylists.Remove(Loading);
+ int loadPos = LocalPlaylists.Count + YoutubePlaylists.Count;
+ YoutubePlaylists.AddRange(yt);
+ adapter.NotifyItemChanged(loadPos);
+ adapter.NotifyItemRangeInserted(loadPos + 1, yt.Count - 1);
+ adapter.forkSaved = true;
+ }
+
+ if (SyncedPlaylists.Count > 0)
+ {
+ List BadSync = SyncedPlaylists.FindAll(x => x.SyncState == SyncState.Loading);
+ for (int i = 0; i < SyncedPlaylists.Count; i++)
+ SyncedPlaylists[i].SyncState = SyncState.Error;
+
+ LocalPlaylists.AddRange(BadSync);
+
+ if (BadSync.Count > 0)
+ {
+ if (LocalPlaylists[1].Name == "EMPTY")
+ {
+ LocalPlaylists.RemoveAt(1);
+ adapter.NotifyItemRemoved(1);
+ }
+ adapter.NotifyItemRangeInserted(LocalPlaylists.Count - SyncedPlaylists.Count, BadSync.Count);
+ }
+ }
+
populating = false;
}
}
+ private void SyncError()
+ {
+ for (int i = 1; i < YoutubePlaylists.Count; i++)
+ {
+ if(YoutubePlaylists[i].SyncState == SyncState.Loading)
+ {
+ YoutubePlaylists[i].SyncState = SyncState.Error;
+ PlaylistHolder holder = (PlaylistHolder)ListView.GetChildViewHolder(ListView.GetChildAt(LocalPlaylists.Count + i));
+ holder.sync.SetImageResource(Resource.Drawable.SyncError);
+ holder.sync.Visibility = ViewStates.Visible;
+ holder.SyncLoading.Visibility = ViewStates.Gone;
+ if (MainActivity.Theme == 1)
+ holder.sync.SetColorFilter(Color.White);
+ }
+ }
+ }
+
+ /* This method will return all playlists available on the local storage in the array "playlists".
+ * If there is an error, the Task will return an error message to display to the user. */
+ public static async Task<(List, string)> GetLocalPlaylists()
+ {
+ if (!await MainActivity.instance.GetReadPermission())
+ return (null, Application.Context.GetString(Resource.String.no_permission));
+
+ List playlists = new List();
+
+ Android.Net.Uri uri = Playlists.ExternalContentUri;
+ CursorLoader loader = new CursorLoader(Application.Context, uri, null, null, null, null);
+ ICursor cursor = (ICursor)loader.LoadInBackground();
+
+ if (cursor != null && cursor.MoveToFirst())
+ {
+ int nameID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Name);
+ int listID = cursor.GetColumnIndex(Playlists.InterfaceConsts.Id);
+ do
+ {
+ string name = cursor.GetString(nameID);
+ long id = cursor.GetLong(listID);
+
+ Android.Net.Uri musicUri = Playlists.Members.GetContentUri("external", id);
+ CursorLoader cursorLoader = new CursorLoader(Application.Context, musicUri, null, null, null, null);
+ ICursor musicCursor = (ICursor)cursorLoader.LoadInBackground();
+
+ playlists.Add(new PlaylistItem(name, id, musicCursor.Count));
+ }
+ while (cursor.MoveToNext());
+ cursor.Close();
+ }
+
+ if (playlists.Count == 1)
+ return (null, Application.Context.GetString(Resource.String.local_playlist_empty));
+ else
+ return (playlists, null);
+ }
+
+ /* This method will proceed the local playlists and remove synced one from the imput.
+ * It will return as first ouput an array containing the true local playlist and as second output the synced youtube playlists.
+ * The outputed youtube playlists will already have the right sync state set (loading, synced...) */
+ public static async Task<(List, List)> ProcessSyncedPlaylists(List localPlaylists)
+ {
+ List syncedPlaylists = new List();
+ await Task.Run(() =>
+ {
+ SQLiteConnection db = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
+ db.CreateTable();
+
+ syncedPlaylists = db.Table().ToList();
+ });
+
+ foreach (PlaylistItem synced in syncedPlaylists)
+ {
+ PlaylistItem local = localPlaylists?.Find(x => x.LocalID == synced.LocalID);
+ if (local != null)
+ {
+ localPlaylists.Remove(local); //This playlist is a synced one, we don't want to display it in the "local" collumn but in the youtube one.
+
+ //Set sync state of the playlist (SyncState can't be false since we take the playlist in the synced database)
+ if (synced.YoutubeID == null)
+ synced.SyncState = SyncState.Loading;
+ else
+ synced.SyncState = SyncState.True;
+ }
+ else
+ {
+ //If local is null, we had an error loading local playlists or another thing (can be read permission denied for example)
+ synced.SyncState = SyncState.Error;
+ }
+ }
+
+ return (localPlaylists, syncedPlaylists);
+ }
+
+
+ /* This method return all youtube playlists owned by the user and process synced playlist if you give an array of know youtube synced playlists
+ * The YoutubePlaylists array should contains the synced playlist availables. Warning, this will return your initial array (proceded if there is synced playlist) + owned playlists
+ * The second outputed var (the string) is the error message that should be displayed to the user (the list will be null if there is an error)*/
+ public static async Task<(List, string)> GetOwnedYoutubePlaylists(List SyncedPlaylists)
+ {
+ if (!await MainActivity.instance.WaitForYoutube())
+ return (null, "Error"); //Should have a better error handling
+
+ List YoutubePlaylists = new List();
+
+ try
+ {
+ YouTubeService youtube = YoutubeEngine.youtubeService;
+
+ PlaylistsResource.ListRequest request = youtube.Playlists.List("snippet,contentDetails");
+ request.Mine = true;
+ request.MaxResults = 25;
+ PlaylistListResponse response = await request.ExecuteAsync();
+
+ for (int i = 0; i < response.Items.Count; i++)
+ {
+ Google.Apis.YouTube.v3.Data.Playlist playlist = response.Items[i];
+ PlaylistItem item = new PlaylistItem(playlist.Snippet.Title, playlist.Id, playlist, (int)playlist.ContentDetails.ItemCount)
+ {
+ Owner = playlist.Snippet.ChannelTitle,
+ ImageURL = playlist.Snippet.Thumbnails.High.Url,
+ HasWritePermission = true
+ };
+
+ AddItemWithSyncCheck(item, SyncedPlaylists, YoutubePlaylists);
+ }
+
+ return (YoutubePlaylists, null);
+ }
+ catch (System.Net.Http.HttpRequestException)
+ {
+ return (null, "Error"); //Should handle precise error here
+ }
+ }
+
+ /* This method return all youtube playlists saved by the user and process synced playlist if you give an array of know youtube synced playlists
+ * The YoutubePlaylists array should contains the synced playlist availables. Warning, this will return your initial array (proceded if there is synced playlist) + saved playlists
+ * The second outputed var (the string) is the error message that should be displayed to the user (the list will be null if there is an error)*/
+ public static async Task<(List, string)> GetSavedYoutubePlaylists(List SyncedPlaylists)
+ {
+ if (!await MainActivity.instance.WaitForYoutube())
+ return (null, "Error"); //Should have a better error handling
+
+ List YoutubePlaylists = new List();
+
+ try
+ {
+ YouTubeService youtube = YoutubeEngine.youtubeService;
+
+ ChannelSectionsResource.ListRequest forkedRequest = youtube.ChannelSections.List("snippet,contentDetails");
+ forkedRequest.Mine = true;
+ ChannelSectionListResponse forkedResponse = await forkedRequest.ExecuteAsync();
+
+ foreach (ChannelSection section in forkedResponse.Items)
+ {
+ if (section.Snippet.Title == "Saved Playlists")
+ {
+ for (int i = 0; i < section.ContentDetails.Playlists.Count; i++)
+ {
+ PlaylistsResource.ListRequest plRequest = youtube.Playlists.List("snippet, contentDetails");
+ plRequest.Id = section.ContentDetails.Playlists[i];
+ PlaylistListResponse plResponse = await plRequest.ExecuteAsync();
+
+ Google.Apis.YouTube.v3.Data.Playlist playlist = plResponse.Items[i];
+ playlist.Kind = "youtube#saved";
+ PlaylistItem item = new PlaylistItem(playlist.Snippet.Title, playlist.Id, playlist, (int)playlist.ContentDetails.ItemCount)
+ {
+ Owner = playlist.Snippet.ChannelTitle,
+ ImageURL = playlist.Snippet.Thumbnails.High.Url,
+ HasWritePermission = false
+ };
+
+ AddItemWithSyncCheck(item, SyncedPlaylists, YoutubePlaylists);
+ }
+ }
+ }
+
+ return (YoutubePlaylists, null);
+ }
+ catch (System.Net.Http.HttpRequestException)
+ {
+ return (null, "Error"); //Should handle precise error here
+ }
+ }
+
+ //All change are made here because PlaylistItem and List are classes witch mean that they will be updated automatically on the others methods
+ public static void AddItemWithSyncCheck(PlaylistItem item, List SyncedPlaylists, List YoutubePlaylists)
+ {
+ PlaylistItem syncedItem = SyncedPlaylists?.Find(x => x.YoutubeID == item.YoutubeID);
+ if (syncedItem != null)
+ {
+ syncedItem.Snippet = item.Snippet;
+ syncedItem.Count = item.Count;
+ }
+ else if (SyncedPlaylists?.Find(x => x.Name == item.Name) != null)
+ {
+ /*We couldn't find a match of a synced playlist with the exact youtube id but we found a synced playlist with the exact same name as this one (item).
+ * We bind them and complete the database for future calls. */
+ syncedItem = SyncedPlaylists.Find(x => x.Name == item.Name);
+ int syncIndex = SyncedPlaylists.IndexOf(syncedItem);
+ item.LocalID = syncedItem.LocalID;
+ item.SyncState = SyncState.True;
+
+ SyncedPlaylists[syncIndex] = item;
+
+ if(instance != null)
+ MainActivity.instance.RunOnUiThread(() => { instance.YoutubeItemSynced(item, syncIndex); });
+
+ Task.Run(() =>
+ {
+ SQLiteConnection db = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "SyncedPlaylists.sqlite"));
+ db.CreateTable();
+ db.InsertOrReplace(item);
+ });
+ }
+ else
+ YoutubePlaylists.Add(item);
+ }
+
+ //We give the item and the index since the public "YoutubePlaylists" array is not updated yet.
+ private void YoutubeItemSynced(PlaylistItem item, int syncedPlaylistIndex)
+ {
+ /*The display order is
+ * - Local Header
+ * - Local Playlists
+ * - Youtube Header
+ * - Synced Playlists
+ * Since local header and local playlists are both contained in the "LocalPlaylists" array, to get the position of the syncedPlaylist,
+ * we need to sum the LocalPlaylists count (this sum get the position of the youtube header) and then we add the syncedPlaylistIndex.
+ * We need to add one for the youtube header (witch is not in the syncedplaylists array)*/
+ PlaylistHolder holder = (PlaylistHolder)ListView.FindViewHolderForAdapterPosition(LocalPlaylists.Count + syncedPlaylistIndex + 1);
+ holder.Owner.Text = item.Owner;
+ Picasso.With(Application.Context).Load(item.ImageURL).Placeholder(Resource.Color.background_material_dark).Resize(400, 400).CenterCrop().Into(holder.AlbumArt);
+
+ if(item.HasWritePermission)
+ {
+ holder.edit.Visibility = ViewStates.Visible;
+ if (MainActivity.Theme == 1)
+ holder.edit.SetColorFilter(Color.White);
+ }
+
+ holder.sync.SetImageResource(Resource.Drawable.Sync);
+ holder.sync.Visibility = ViewStates.Visible;
+ holder.SyncLoading.Visibility = ViewStates.Gone;
+ if (MainActivity.Theme == 1)
+ holder.sync.SetColorFilter(Color.White);
+ }
+
public static Fragment NewInstance()
{
if(instance == null)