diff --git a/Opus/Code/Api/Services/Downloader.cs b/Opus/Code/Api/Services/Downloader.cs index 7fa17bc..9a612ae 100644 --- a/Opus/Code/Api/Services/Downloader.cs +++ b/Opus/Code/Api/Services/Downloader.cs @@ -229,7 +229,7 @@ namespace Opus.Api.Services if (queue.Count == 1) SetNotificationProgress(100, true); - SetMetaData(position, video.Title, video.Author, new string[] { video.Thumbnails.MaxResUrl, video.Thumbnails.StandardResUrl, video.Thumbnails.HighResUrl }); + SetMetaData(position, video.Title, video.Author, video.Thumbnails); files.Remove(filePath); downloadCount--; @@ -256,7 +256,7 @@ namespace Opus.Api.Services } } - private async void SetMetaData(int position, string title, string artist, string[] thumbnails) + private async void SetMetaData(int position, string title, string artist, ThumbnailSet thumbnails) { string filePath = queue[position].Path; diff --git a/Opus/Code/Api/SongParser.cs b/Opus/Code/Api/SongParser.cs index 9f3021c..291abf7 100644 --- a/Opus/Code/Api/SongParser.cs +++ b/Opus/Code/Api/SongParser.cs @@ -160,7 +160,7 @@ namespace Opus.Code.Api Video video = await client.GetVideoAsync(song.YoutubeID); song.Title = video.Title; song.Artist = video.Author; - song.Album = await YoutubeManager.GetBestThumb(new string[] { video.Thumbnails.MaxResUrl, video.Thumbnails.StandardResUrl, video.Thumbnails.HighResUrl }); + song.Album = await YoutubeManager.GetBestThumb(video.Thumbnails); song.Duration = (int)video.Duration.TotalMilliseconds; if (queuePosition == MusicPlayer.CurrentID()) diff --git a/Opus/Code/Api/YoutubeManager.cs b/Opus/Code/Api/YoutubeManager.cs index 39db79c..471958e 100644 --- a/Opus/Code/Api/YoutubeManager.cs +++ b/Opus/Code/Api/YoutubeManager.cs @@ -407,7 +407,7 @@ namespace Opus.Api foreach (var video in searchReponse.Items) { - Song song = new Song(System.Net.WebUtility.HtmlDecode(video.Snippet.Title), video.Snippet.ChannelTitle, video.Snippet.Thumbnails.High.Url, video.Id.VideoId, -1, -1, null, true, false); + Song song = new Song(WebUtility.HtmlDecode(video.Snippet.Title), video.Snippet.ChannelTitle, video.Snippet.Thumbnails.High.Url, video.Id.VideoId, -1, -1, null, true, false); songs.Add(song); } } @@ -432,10 +432,11 @@ namespace Opus.Api /// /// Return the thumbnail with the greatest quality. /// - /// This array should contains the urls of the thumbnails in this order: MaxResUrl, StandardResUrl, HighResUrl + /// This will create an array of the thumbnails in this order: MaxResUrl, StandardResUrl, HighResUrl /// - public async static Task GetBestThumb(string[] thumbnails) + public async static Task GetBestThumb(ThumbnailSet Thumbnails) { + string[] thumbnails = new string[] { Thumbnails.MaxResUrl, Thumbnails.StandardResUrl, Thumbnails.HighResUrl }; foreach (string thumb in thumbnails) { HttpWebRequest request = new HttpWebRequest(new Uri(thumb)) diff --git a/Opus/Code/DataStructure/HomeSection.cs b/Opus/Code/DataStructure/HomeSection.cs index 97c47f6..1ee3adb 100644 --- a/Opus/Code/DataStructure/HomeSection.cs +++ b/Opus/Code/DataStructure/HomeSection.cs @@ -4,33 +4,41 @@ using System.Collections.Generic; namespace Opus.DataStructure { [System.Serializable] - public class HomeSection + public class Section { public string SectionTitle; public SectionType contentType; public List contentValue; public List playlistContent; + public List channelContent; public RecyclerView recycler; - public HomeSection(string sectionTitle, SectionType contentType) + public Section(string sectionTitle, SectionType contentType) { SectionTitle = sectionTitle; this.contentType = contentType; } - public HomeSection(string sectionTitle, SectionType contentType, List contentValue) + public Section(string sectionTitle, SectionType contentType, List contentValue) { SectionTitle = sectionTitle; this.contentType = contentType; this.contentValue = contentValue; } - public HomeSection(string sectionTitle, SectionType contentType, List playlistContent) + public Section(string sectionTitle, SectionType contentType, List playlistContent) { SectionTitle = sectionTitle; this.contentType = contentType; this.playlistContent = playlistContent; } + + public Section(string sectionTitle, SectionType contentType, List channelContent) + { + SectionTitle = sectionTitle; + this.contentType = contentType; + this.channelContent = channelContent; + } } public enum SectionType diff --git a/Opus/Code/DataStructure/Song.cs b/Opus/Code/DataStructure/Song.cs index 7a23209..e1433d1 100644 --- a/Opus/Code/DataStructure/Song.cs +++ b/Opus/Code/DataStructure/Song.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json; using Opus.Fragments; using SQLite; using System; +using System.Collections.Generic; namespace Opus.DataStructure { @@ -78,6 +79,21 @@ namespace Opus.DataStructure return new Song(Title, playOrder ?? Artist, Album, null, AlbumArt, id, path); } + public static explicit operator Song(YoutubeExplode.Models.Video video) + { + return new Song(video.Title, video.Author, video.Thumbnails.HighResUrl, video.Id, -1, -1, null, true, false); + } + + public static List FromVideoArray(IReadOnlyList videos) + { + List songs = new List(); + for (int i = 0; i < videos.Count; i++) + { + songs.Add((Song)videos[i]); + } + return songs; + } + public static explicit operator Song(string v) { diff --git a/Opus/Code/MainActivity.cs b/Opus/Code/MainActivity.cs index b38870e..5681ac3 100644 --- a/Opus/Code/MainActivity.cs +++ b/Opus/Code/MainActivity.cs @@ -50,7 +50,6 @@ namespace Opus public class MainActivity : AppCompatActivity, GoogleApiClient.IOnConnectionFailedListener, IResultCallback, IMenuItemOnActionExpandListener, View.IOnFocusChangeListener, ISessionManagerListener { public static MainActivity instance; - //public new static int Theme = 1; public static int dialogTheme; public bool NoToolbarMenu = false; diff --git a/Opus/Code/UI/Adapter/PlaylistAdapter.cs b/Opus/Code/UI/Adapter/PlaylistAdapter.cs index a008ea9..3a3dd32 100644 --- a/Opus/Code/UI/Adapter/PlaylistAdapter.cs +++ b/Opus/Code/UI/Adapter/PlaylistAdapter.cs @@ -150,7 +150,7 @@ namespace Opus.Adapter Color color; TypedValue value = new TypedValue(); - if (MainActivity.instance.Theme.ResolveAttribute(Resource.Styleable.TextAppearance_android_textColor, value, true)) + if (MainActivity.instance.Theme.ResolveAttribute(Android.Resource.Attribute.TextColor, value, true)) color = Color.ParseColor("#" + Java.Lang.Integer.ToHexString(value.Data)); else color = Color.Black; diff --git a/Opus/Code/UI/Adapter/HomeAdapter.cs b/Opus/Code/UI/Adapter/SectionAdapter.cs similarity index 92% rename from Opus/Code/UI/Adapter/HomeAdapter.cs rename to Opus/Code/UI/Adapter/SectionAdapter.cs index aa9a34b..55579c0 100644 --- a/Opus/Code/UI/Adapter/HomeAdapter.cs +++ b/Opus/Code/UI/Adapter/SectionAdapter.cs @@ -11,16 +11,16 @@ using System.Collections.Generic; namespace Opus.Adapter { - public class HomeAdapter : RecyclerView.Adapter + public class SectionAdapter : RecyclerView.Adapter { - public List items; + public List
items; private bool refreshDisabled = true; public event EventHandler ItemClick; public event EventHandler ItemLongClick; - public HomeAdapter(List items) + public SectionAdapter(List
items) { this.items = items; } @@ -73,6 +73,11 @@ namespace Opus.Adapter if(MusicPlayer.CurrentID() != -1 && MusicPlayer.CurrentID() <= MusicPlayer.queue.Count) holder.recycler.ScrollToPosition(MusicPlayer.CurrentID()); } + else if(items[position].SectionTitle == null) + { + //The playlist is loading + holder.recycler.SetAdapter(new LineAdapter(new List(), holder.recycler)); + } else { holder.title.Text = items[position].SectionTitle; @@ -80,8 +85,8 @@ namespace Opus.Adapter holder.more.Click += (sender, e) => { position = holder.AdapterPosition; - MainActivity.instance.contentRefresh.Refresh -= Home.instance.OnRefresh; - Home.instance = null; + //MainActivity.instance.contentRefresh.Refresh -= Home.instance.OnRefresh; + //Home.instance = null; MainActivity.instance.SupportFragmentManager.BeginTransaction().Replace(Resource.Id.contentView, PlaylistTracks.NewInstance(items[position].contentValue, items[position].SectionTitle)).AddToBackStack(null).Commit(); }; } @@ -139,13 +144,6 @@ namespace Opus.Adapter // }; //} - //else if (items[position].contentType == SectionType.Shuffle) - //{ - // if (MainActivity.Theme == 1) - // ((CardView)viewHolder.ItemView).SetCardBackgroundColor(Color.ParseColor("#212121")); - // else - // ((CardView)viewHolder.ItemView).SetCardBackgroundColor(Color.White); - //} } void OnClick(int position) diff --git a/Opus/Code/UI/Fragments/ChannelDetails.cs b/Opus/Code/UI/Fragments/ChannelDetails.cs new file mode 100644 index 0000000..d4602a0 --- /dev/null +++ b/Opus/Code/UI/Fragments/ChannelDetails.cs @@ -0,0 +1,317 @@ +using Android.Content; +using Android.Database; +using Android.Net; +using Android.OS; +using Android.Support.Design.Widget; +using Android.Support.V4.App; +using Android.Views; +using Android.Widget; +using Java.Lang; +using Opus.Adapter; +using Opus.Api; +using Opus.DataStructure; +using Opus.Others; +using Square.Picasso; +using System.Collections.Generic; +using System.Threading.Tasks; +using YoutubeExplode; +using static Android.Provider.MediaStore.Audio; +using CursorLoader = Android.Support.V4.Content.CursorLoader; +using PlaylistItem = Opus.DataStructure.PlaylistItem; +using PopupMenu = Android.Support.V7.Widget.PopupMenu; +using RecyclerView = Android.Support.V7.Widget.RecyclerView; +using SearchView = Android.Support.V7.Widget.SearchView; +using Toolbar = Android.Support.V7.Widget.Toolbar; + +namespace Opus.Fragments +{ + public class ChannelDetails : Fragment, /*PopupMenu.IOnMenuItemClickListener,*/ AppBarLayout.IOnOffsetChangedListener + { + public static ChannelDetails instance; + private Channel item; + + private TextView EmptyView; + private RecyclerView ListView; + private SectionAdapter adapter; + private List
sections = new List
(); + + public override void OnActivityCreated(Bundle savedInstanceState) + { + base.OnActivityCreated(savedInstanceState); + if (item == null) + { + MainActivity.instance.SupportFragmentManager.PopBackStack(); + return; + } + + MainActivity.instance.contentRefresh.Refresh += OnRefresh; + MainActivity.instance.DisplaySearch(); + + MainActivity.instance.SupportActionBar.SetHomeButtonEnabled(true); + MainActivity.instance.SupportActionBar.SetDisplayHomeAsUpEnabled(true); + MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(true); + MainActivity.instance.FindViewById(Resource.Id.toolbarLogo).Visibility = ViewStates.Gone; + MainActivity.instance.SupportActionBar.Title = item.Name; + } + + public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) + { + View view = inflater.Inflate(Resource.Layout.LonelyRecycler, container, false); + ListView = view.FindViewById(Resource.Id.recycler); + ListView.SetLayoutManager(new Android.Support.V7.Widget.LinearLayoutManager(MainActivity.instance)); + if(item != null) + CreateHeader(); + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + PopulateList(); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + return view; + } + + void CreateHeader() + { + Activity.FindViewById(Resource.Id.playlistHeader).Visibility = ViewStates.Visible; + ((AppBarLayout.LayoutParams)Activity.FindViewById(Resource.Id.collapsingToolbar).LayoutParameters).ScrollFlags = AppBarLayout.LayoutParams.ScrollFlagScroll | AppBarLayout.LayoutParams.ScrollFlagExitUntilCollapsed; + Activity.FindViewById(Resource.Id.appbar).AddOnOffsetChangedListener(this); + Activity.FindViewById(Resource.Id.headerTitle).Text = item.Name; + Activity.FindViewById(Resource.Id.headerAuthor).Text = null; + Activity.FindViewById(Resource.Id.headerNumber).Text = null; + Activity.FindViewById(Resource.Id.playlistButtons).Visibility = ViewStates.Gone; + Picasso.With(Android.App.Application.Context).Load(item.ImageURL).Placeholder(Resource.Drawable.noAlbum).Transform(new RemoveBlackBorder(true)).Into(Activity.FindViewById(Resource.Id.headerArt)); + Activity.FindViewById(Resource.Id.collapsingToolbar).RequestLayout(); + } + + public override void OnDestroyView() + { + //MainActivity.instance.RemoveFilterListener(Search); + if (!MainActivity.instance.Paused) + { + Activity.FindViewById(Resource.Id.playlistButtons).Visibility = ViewStates.Visible; + Activity.FindViewById(Resource.Id.playlistHeader).Visibility = ViewStates.Gone; + + MainActivity.instance.HideSearch(); + MainActivity.instance.SupportActionBar.SetHomeButtonEnabled(false); + MainActivity.instance.SupportActionBar.SetDisplayHomeAsUpEnabled(false); + MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(true); + MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(false); + + MainActivity.instance.contentRefresh.Refresh -= OnRefresh; + Activity.FindViewById(Resource.Id.appbar).RemoveOnOffsetChangedListener(this); + + + if (YoutubeSearch.instances != null) + { + MainActivity.instance.FindViewById(Resource.Id.tabs).Visibility = ViewStates.Visible; + SearchView searchView = (SearchView)MainActivity.instance.menu.FindItem(Resource.Id.search).ActionView; + searchView.Focusable = false; + MainActivity.instance.menu.FindItem(Resource.Id.search).ExpandActionView(); + searchView.SetQuery(YoutubeSearch.instances[0].Query, false); + searchView.ClearFocus(); + + int selectedTab = 0; + for (int i = 0; i < YoutubeSearch.instances.Length; i++) + { + if (YoutubeSearch.instances[i].IsFocused) + selectedTab = i; + } + } + instance = null; + } + base.OnDestroyView(); + } + + public static Fragment NewInstance(Channel item) + { + instance = new ChannelDetails { Arguments = new Bundle() }; + instance.item = item; + return instance; + } + + async Task PopulateList() + { + sections.Clear(); + try + { + var request = YoutubeManager.YoutubeService.ChannelSections.List("snippet,contentDetails"); + request.ChannelId = item.YoutubeID; + + var response = await request.ExecuteAsync(); + + for (int i = 0; i < response.Items.Count; i++) + { + if (response.Items[i].ContentDetails?.Playlists.Count == 1) + { + System.Console.WriteLine("&Single playlsit"); + sections.Add(new Section(null, SectionType.SinglePlaylist)); + LoadPlaylist(sections.Count - 1, response.Items[i].ContentDetails.Playlists[0]); + } + + //else if (item.ContentDetails.Playlists.Count > 1) + // sections.Add(new Section(item.Snippet.Title, SectionType.PlaylistList, new List())); + + //else if (item.ContentDetails.Channels.Count > 1) + // sections.Add(new Section(item.Snippet.Title, SectionType.ChannelList, new List())); + } + + adapter = new SectionAdapter(sections); + ListView.SetAdapter(adapter); + } + catch (System.Net.Http.HttpRequestException) { System.Console.WriteLine("&Channel section list time out"); } + } + + async void LoadPlaylist(int slot, string playlistID) + { + System.Console.WriteLine("&Loading playlist: " + playlistID + " slot: " + slot + " sections count " + sections.Count); + var pl = await new YoutubeClient().GetPlaylistAsync(playlistID, 1); + sections[slot] = new Section(pl.Title, SectionType.SinglePlaylist, Song.FromVideoArray(pl.Videos)); + adapter.NotifyItemChanged(slot); + } + + public async void OnRefresh(object sender, System.EventArgs e) + { + await PopulateList(); + MainActivity.instance.contentRefresh.Refreshing = false; + } + + //public void Search(object sender, SearchView.QueryTextChangeEventArgs e) + //{ + // if (e.NewText == "") + // query = null; + // else + // query = e.NewText.ToLower(); + + // if (item.LocalID != -1) + // LoaderManager.GetInstance(this).RestartLoader(0, null, this); + // else + // { + // if (query == null) + // adapter.tracks.SetFilter(x => true); + // else + // adapter.tracks.SetFilter(x => x.Title.ToLower().Contains(query) || x.Artist.ToLower().Contains(query)); + + // adapter.NotifyDataSetChanged(); + // } + //} + + public void More(Song song, int position) + { + //BottomSheetDialog bottomSheet = new BottomSheetDialog(MainActivity.instance); + //View bottomView = LayoutInflater.Inflate(Resource.Layout.BottomSheet, null); + //bottomView.FindViewById(Resource.Id.bsTitle).Text = song.Title; + //bottomView.FindViewById(Resource.Id.bsArtist).Text = song.Artist; + //if (song.AlbumArt == -1 || song.IsYt) + //{ + // Picasso.With(MainActivity.instance).Load(song.Album).Placeholder(Resource.Drawable.noAlbum).Transform(new RemoveBlackBorder(true)).Into(bottomView.FindViewById(Resource.Id.bsArt)); + //} + //else + //{ + // var songCover = Uri.Parse("content://media/external/audio/albumart"); + // var songAlbumArtUri = ContentUris.WithAppendedId(songCover, song.AlbumArt); + + // Picasso.With(MainActivity.instance).Load(songAlbumArtUri).Placeholder(Resource.Drawable.noAlbum).Resize(400, 400).CenterCrop().Into(bottomView.FindViewById(Resource.Id.bsArt)); + //} + //bottomSheet.SetContentView(bottomView); + + //List actions = new List + //{ + // new BottomSheetAction(Resource.Drawable.Play, Resources.GetString(Resource.String.play), (sender, eventArg) => + // { + // if(useHeader) + // PlaylistManager.PlayInOrder(item, position); + // else + // SongManager.Play(song); + // bottomSheet.Dismiss(); + // }), + // new BottomSheetAction(Resource.Drawable.PlaylistPlay, Resources.GetString(Resource.String.play_next), (sender, eventArg) => + // { + // SongManager.PlayNext(song); + // bottomSheet.Dismiss(); + // }), + // new BottomSheetAction(Resource.Drawable.Queue, Resources.GetString(Resource.String.play_last), (sender, eventArg) => + // { + // SongManager.PlayLast(song); + // bottomSheet.Dismiss(); + // }), + // new BottomSheetAction(Resource.Drawable.PlaylistAdd, Resources.GetString(Resource.String.add_to_playlist), (sender, eventArg) => + // { + // PlaylistManager.AddSongToPlaylistDialog(song); + // bottomSheet.Dismiss(); + // }) + //}; + + //if (item.HasWritePermission) + //{ + // actions.Add(new BottomSheetAction(Resource.Drawable.Close, Resources.GetString(Resource.String.remove_track_from_playlist), (sender, eventArg) => + // { + // RemoveFromPlaylist(song, position); + // bottomSheet.Dismiss(); + // })); + //} + + //if (!song.IsYt) + //{ + // actions.Add(new BottomSheetAction(Resource.Drawable.Edit, Resources.GetString(Resource.String.edit_metadata), (sender, eventArg) => + // { + // LocalManager.EditMetadata(song); + // bottomSheet.Dismiss(); + // })); + //} + //else + //{ + // actions.AddRange(new BottomSheetAction[] + // { + // new BottomSheetAction(Resource.Drawable.PlayCircle, Resources.GetString(Resource.String.create_mix_from_song), (sender, eventArg) => + // { + // YoutubeManager.CreateMixFromSong(song); + // bottomSheet.Dismiss(); + // }), + // new BottomSheetAction(Resource.Drawable.Download, Resources.GetString(Resource.String.download), (sender, eventArg) => + // { + // YoutubeManager.Download(new[] { song }); + // bottomSheet.Dismiss(); + // }) + // }); + //} + + //bottomSheet.FindViewById(Resource.Id.bsItems).Adapter = new BottomSheetAdapter(MainActivity.instance, Resource.Layout.BottomSheetText, actions); + //bottomSheet.Show(); + } + + + public override void OnResume() + { + base.OnResume(); + instance = this; + + //if (!Activity.FindViewById(Resource.Id.headerPlay).HasOnClickListeners) + // Activity.FindViewById(Resource.Id.headerPlay).Click += (sender, e0) => { PlaylistManager.PlayInOrder(item); }; + //if (!Activity.FindViewById(Resource.Id.headerShuffle).HasOnClickListeners) + // Activity.FindViewById(Resource.Id.headerShuffle).Click += (sender, e0) => { PlaylistManager.Shuffle(item); }; + //if (!Activity.FindViewById(Resource.Id.headerMore).HasOnClickListeners) + // Activity.FindViewById(Resource.Id.headerMore).Click += PlaylistMore; + } + + public void OnOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) + { + if (instance == null) + return; + + if (System.Math.Abs(verticalOffset) <= appBarLayout.TotalScrollRange - MainActivity.instance.FindViewById(Resource.Id.toolbar).Height) + { + Activity.FindViewById(Resource.Id.playlistHeader).Visibility = ViewStates.Visible; + MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(false); + } + else + { + Activity.FindViewById(Resource.Id.playlistHeader).Visibility = ViewStates.Invisible; + MainActivity.instance.SupportActionBar.SetDisplayShowTitleEnabled(true); + } + } + + public override void OnDestroy() + { + base.OnDestroy(); + MainActivity.instance.FindViewById(Resource.Id.toolbarLogo).Visibility = ViewStates.Visible; + } + } +} diff --git a/Opus/Code/UI/Fragments/EditMetaData.cs b/Opus/Code/UI/Fragments/EditMetaData.cs index d129e2c..396127e 100644 --- a/Opus/Code/UI/Fragments/EditMetaData.cs +++ b/Opus/Code/UI/Fragments/EditMetaData.cs @@ -346,7 +346,7 @@ namespace Opus.Fragments album.Text = video.Title + " - " + video.Author; } - ytThumbUri = await YoutubeManager.GetBestThumb(new string[] { video.Thumbnails.MaxResUrl, video.Thumbnails.StandardResUrl, video.Thumbnails.HighResUrl }); + ytThumbUri = await YoutubeManager.GetBestThumb(video.Thumbnails); Picasso.With(this).Load(ytThumbUri).Placeholder(Resource.Drawable.noAlbum).Transform(new RemoveBlackBorder(true)).MemoryPolicy(MemoryPolicy.NoCache, MemoryPolicy.NoStore).Into(albumArt); } diff --git a/Opus/Code/UI/Fragments/Home.cs b/Opus/Code/UI/Fragments/Home.cs index 24a63c3..14b6e0f 100644 --- a/Opus/Code/UI/Fragments/Home.cs +++ b/Opus/Code/UI/Fragments/Home.cs @@ -22,10 +22,10 @@ namespace Opus.Fragments { public static Home instance; public RecyclerView ListView; - public HomeAdapter adapter; + public SectionAdapter adapter; public LineAdapter QueueAdapter; public ItemTouchHelper itemTouchHelper; - public static List adapterItems = new List(); + public static List
adapterItems = new List
(); public List selectedTopics = new List(); public List selectedTopicsID = new List(); public View view; @@ -69,15 +69,15 @@ namespace Opus.Fragments if (!populating) { populating = true; - adapterItems = new List(); + adapterItems = new List
(); if (MusicPlayer.UseCastPlayer || (MusicPlayer.queue != null && MusicPlayer.queue?.Count > 0)) { - HomeSection queue = new HomeSection("Queue", SectionType.SinglePlaylist, MusicPlayer.queue); + Section queue = new Section("Queue", SectionType.SinglePlaylist, MusicPlayer.queue); adapterItems.Add(queue); } - HomeSection shuffle = new HomeSection(Resources.GetString(Resource.String.shuffle), SectionType.Shuffle); + Section shuffle = new Section(Resources.GetString(Resource.String.shuffle), SectionType.Shuffle); adapterItems.Add(shuffle); await Task.Run(() => @@ -126,14 +126,14 @@ namespace Opus.Fragments if (songList.Count > 0) { - HomeSection featured = new HomeSection(Resources.GetString(Resource.String.featured), SectionType.SinglePlaylist, songList.GetRange(0, songList.Count > 50 ? 50 : songList.Count)); + Section featured = new Section(Resources.GetString(Resource.String.featured), SectionType.SinglePlaylist, songList.GetRange(0, songList.Count > 50 ? 50 : songList.Count)); adapterItems.Add(featured); } } }); view.FindViewById(Resource.Id.loading).Visibility = ViewStates.Gone; - adapter = new HomeAdapter(adapterItems); + adapter = new SectionAdapter(adapterItems); ListView.SetAdapter(adapter); adapter.ItemClick += ListView_ItemClick; ListView.SetItemAnimator(new DefaultItemAnimator()); @@ -146,7 +146,7 @@ namespace Opus.Fragments sp.AddRange(saved); sp.AddRange(pl); - adapterItems.Add(new HomeSection(GetString(Resource.String.playlists), SectionType.PlaylistList, sp)); + adapterItems.Add(new Section(GetString(Resource.String.playlists), SectionType.PlaylistList, sp)); adapter.NotifyItemInserted(adapterItems.Count - 1); } else @@ -155,7 +155,7 @@ namespace Opus.Fragments if(saved != null && saved.Count > 0) { - adapterItems.Add(new HomeSection(GetString(Resource.String.playlists), SectionType.PlaylistList, saved)); + adapterItems.Add(new Section(GetString(Resource.String.playlists), SectionType.PlaylistList, saved)); adapter.NotifyItemInserted(adapterItems.Count - 1); } } @@ -168,7 +168,7 @@ namespace Opus.Fragments { if (adapterItems[0].SectionTitle != "Queue") { - HomeSection queue = new HomeSection("Queue", SectionType.SinglePlaylist, MusicPlayer.queue); + Section queue = new Section("Queue", SectionType.SinglePlaylist, MusicPlayer.queue); adapterItems.Insert(0, queue); adapter?.NotifyItemInserted(0); } diff --git a/Opus/Code/UI/Fragments/PlaylistTracks.cs b/Opus/Code/UI/Fragments/PlaylistTracks.cs index 3817fd6..b8e6253 100644 --- a/Opus/Code/UI/Fragments/PlaylistTracks.cs +++ b/Opus/Code/UI/Fragments/PlaylistTracks.cs @@ -157,7 +157,7 @@ namespace Opus.Fragments if (YoutubeSearch.instances != null) { MainActivity.instance.FindViewById(Resource.Id.tabs).Visibility = ViewStates.Visible; - Android.Support.V7.Widget.SearchView searchView = (Android.Support.V7.Widget.SearchView)MainActivity.instance.menu.FindItem(Resource.Id.search).ActionView; + SearchView searchView = (SearchView)MainActivity.instance.menu.FindItem(Resource.Id.search).ActionView; searchView.Focusable = false; MainActivity.instance.menu.FindItem(Resource.Id.search).ExpandActionView(); searchView.SetQuery(YoutubeSearch.instances[0].Query, false); @@ -272,7 +272,7 @@ namespace Opus.Fragments instance = new PlaylistTracks { Arguments = new Bundle() }; instance.item = item; instance.useHeader = true; - instance.isInEditMode = true; + instance.isInEditMode = item.HasWritePermission && item.LocalID != 0; instance.fullyLoadded = item.LocalID != 0 && item.LocalID != -1; Task.Run(async () => @@ -356,15 +356,9 @@ namespace Opus.Fragments Uri musicUri = Playlists.Members.GetContentUri("external", item.LocalID); string selection; if (query != null) - { selection = Media.InterfaceConsts.Title + " LIKE \"%" + query + "%\" OR " + Media.InterfaceConsts.Artist + " LIKE \"%" + query + "%\""; - isInEditMode = false; - } else - { selection = null; - isInEditMode = true; - } return new CursorLoader(Android.App.Application.Context, musicUri, null, selection, null, Playlists.Members.PlayOrder); } @@ -451,11 +445,17 @@ namespace Opus.Fragments public void Search(object sender, SearchView.QueryTextChangeEventArgs e) { if (e.NewText == "") + { query = null; + isInEditMode = item.HasWritePermission && item.LocalID != 0; + } else + { query = e.NewText.ToLower(); + isInEditMode = false; + } - if(item.LocalID != -1) + if (item.LocalID != -1) LoaderManager.GetInstance(this).RestartLoader(0, null, this); else { diff --git a/Opus/Code/UI/Fragments/YoutubeSearch.cs b/Opus/Code/UI/Fragments/YoutubeSearch.cs index 1ecfe1a..747ceaf 100644 --- a/Opus/Code/UI/Fragments/YoutubeSearch.cs +++ b/Opus/Code/UI/Fragments/YoutubeSearch.cs @@ -293,11 +293,8 @@ namespace Opus.Fragments catch(Exception ex) { Console.WriteLine("&Exception catched in the youtube search: " + ex.Source + " - " + ex.Message); - if(ex is System.Net.Http.HttpRequestException) - { - EmptyView.Text = GetString(Resource.String.timout); - EmptyView.Visibility = ViewStates.Visible; - } + EmptyView.Text = GetString(Resource.String.timout); + EmptyView.Visibility = ViewStates.Visible; } } @@ -340,6 +337,11 @@ namespace Opus.Fragments MainActivity.instance.FindViewById(Resource.Id.tabs).Visibility = ViewStates.Gone; MainActivity.instance.SupportFragmentManager.BeginTransaction().Replace(Resource.Id.contentView, PlaylistTracks.NewInstance(result[position].playlist)).AddToBackStack("Playlist Track").Commit(); break; + case YtKind.Channel: + MainActivity.instance.menu.FindItem(Resource.Id.search).CollapseActionView(); + MainActivity.instance.FindViewById(Resource.Id.tabs).Visibility = ViewStates.Gone; + MainActivity.instance.SupportFragmentManager.BeginTransaction().Replace(Resource.Id.contentView, ChannelDetails.NewInstance(result[position].channel)).AddToBackStack("Channel Details").Commit(); + break; default: break; } diff --git a/Opus/Code/UI/Views/CollapsingToolbar.cs b/Opus/Code/UI/Views/CollapsingToolbar.cs index e22f9cc..293e1d8 100644 --- a/Opus/Code/UI/Views/CollapsingToolbar.cs +++ b/Opus/Code/UI/Views/CollapsingToolbar.cs @@ -15,7 +15,7 @@ public class CollapsingToolbar : CollapsingToolbarLayout protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (PlaylistTracks.instance != null && PlaylistTracks.instance.useHeader) + if ((PlaylistTracks.instance != null && PlaylistTracks.instance.useHeader) ||ChannelDetails.instance != null) heightMeasureSpec = widthMeasureSpec; diff --git a/Opus/Opus.csproj b/Opus/Opus.csproj index 6e9b52a..c4b4666 100644 --- a/Opus/Opus.csproj +++ b/Opus/Opus.csproj @@ -331,6 +331,7 @@ + @@ -364,7 +365,7 @@ - + diff --git a/Opus/Resources/Resource.Designer.cs b/Opus/Resources/Resource.Designer.cs index 0fc63a4..a2a525c 100644 --- a/Opus/Resources/Resource.Designer.cs +++ b/Opus/Resources/Resource.Designer.cs @@ -4997,8 +4997,8 @@ namespace Opus // aapt resource value: 0x7f0b0067 public const int META = 2131427431; - // aapt resource value: 0x7f0b01b5 - public const int PreferenceScreen = 2131427765; + // aapt resource value: 0x7f0b01b6 + public const int PreferenceScreen = 2131427766; // aapt resource value: 0x7f0b0068 public const int SHIFT = 2131427432; @@ -5006,8 +5006,8 @@ namespace Opus // aapt resource value: 0x7f0b0069 public const int SYM = 2131427433; - // aapt resource value: 0x7f0b01b7 - public const int accountPreference = 2131427767; + // aapt resource value: 0x7f0b01b8 + public const int accountPreference = 2131427768; // aapt resource value: 0x7f0b00ff public const int action = 2131427583; @@ -5093,8 +5093,8 @@ namespace Opus // aapt resource value: 0x7f0b0059 public const int add = 2131427417; - // aapt resource value: 0x7f0b01d4 - public const int addToQueue = 2131427796; + // aapt resource value: 0x7f0b01d5 + public const int addToQueue = 2131427797; // aapt resource value: 0x7f0b00ce public const int added = 2131427534; @@ -5117,17 +5117,17 @@ namespace Opus // aapt resource value: 0x7f0b006a public const int always = 2131427434; - // aapt resource value: 0x7f0b01bf - public const int apAlbum = 2131427775; + // aapt resource value: 0x7f0b01c0 + public const int apAlbum = 2131427776; - // aapt resource value: 0x7f0b01c1 - public const int apTitle = 2131427777; + // aapt resource value: 0x7f0b01c2 + public const int apTitle = 2131427778; // aapt resource value: 0x7f0b011b public const int appbar = 2131427611; - // aapt resource value: 0x7f0b01ad - public const int artist = 2131427757; + // aapt resource value: 0x7f0b01ae + public const int artist = 2131427758; // aapt resource value: 0x7f0b008f public const int async = 2131427471; @@ -5138,8 +5138,8 @@ namespace Opus // aapt resource value: 0x7f0b004d public const int auto = 2131427405; - // aapt resource value: 0x7f0b01be - public const int autoplay = 2131427774; + // aapt resource value: 0x7f0b01bf + public const int autoplay = 2131427775; // aapt resource value: 0x7f0b011f public const int backToolbar = 2131427615; @@ -5153,8 +5153,8 @@ namespace Opus // aapt resource value: 0x7f0b0061 public const int beginning = 2131427425; - // aapt resource value: 0x7f0b01b6 - public const int behavior = 2131427766; + // aapt resource value: 0x7f0b01b7 + public const int behavior = 2131427767; // aapt resource value: 0x7f0b0090 public const int blocking = 2131427472; @@ -5165,14 +5165,14 @@ namespace Opus // aapt resource value: 0x7f0b006f public const int bottom = 2131427439; - // aapt resource value: 0x7f0b01bc - public const int bottomDivider = 2131427772; + // aapt resource value: 0x7f0b01bd + public const int bottomDivider = 2131427773; // aapt resource value: 0x7f0b013a public const int bottomView = 2131427642; - // aapt resource value: 0x7f0b01cf - public const int browseLayout = 2131427791; + // aapt resource value: 0x7f0b01d0 + public const int browseLayout = 2131427792; // aapt resource value: 0x7f0b00d9 public const int browseList = 2131427545; @@ -5336,8 +5336,8 @@ namespace Opus // aapt resource value: 0x7f0b00a0 public const int default_activity_button = 2131427488; - // aapt resource value: 0x7f0b01d1 - public const int delete = 2131427793; + // aapt resource value: 0x7f0b01d2 + public const int delete = 2131427794; // aapt resource value: 0x7f0b0110 public const int design_bottom_sheet = 2131427600; @@ -5366,14 +5366,14 @@ namespace Opus // aapt resource value: 0x7f0b0045 public const int download = 2131427397; - // aapt resource value: 0x7f0b01d2 - public const int downloadMDfromYT = 2131427794; + // aapt resource value: 0x7f0b01d3 + public const int downloadMDfromYT = 2131427795; // aapt resource value: 0x7f0b0118 public const int downloadStatus = 2131427608; - // aapt resource value: 0x7f0b01ae - public const int edit = 2131427758; + // aapt resource value: 0x7f0b01af + public const int edit = 2131427759; // aapt resource value: 0x7f0b00c0 public const int edit_query = 2131427520; @@ -5495,8 +5495,8 @@ namespace Opus // aapt resource value: 0x7f0b0089 public const int filled = 2131427465; - // aapt resource value: 0x7f0b01d6 - public const int filter = 2131427798; + // aapt resource value: 0x7f0b01d7 + public const int filter = 2131427799; // aapt resource value: 0x7f0b0094 public const int fit = 2131427476; @@ -5537,17 +5537,17 @@ namespace Opus // aapt resource value: 0x7f0b01a7 public const int headerAuthor = 2131427751; - // aapt resource value: 0x7f0b01ab - public const int headerMore = 2131427755; + // aapt resource value: 0x7f0b01ac + public const int headerMore = 2131427756; // aapt resource value: 0x7f0b01a8 public const int headerNumber = 2131427752; - // aapt resource value: 0x7f0b01a9 - public const int headerPlay = 2131427753; - // aapt resource value: 0x7f0b01aa - public const int headerShuffle = 2131427754; + public const int headerPlay = 2131427754; + + // aapt resource value: 0x7f0b01ab + public const int headerShuffle = 2131427755; // aapt resource value: 0x7f0b01a6 public const int headerTitle = 2131427750; @@ -5561,8 +5561,8 @@ namespace Opus // aapt resource value: 0x7f0b00a2 public const int icon = 2131427490; - // aapt resource value: 0x7f0b01c8 - public const int icon1 = 2131427784; + // aapt resource value: 0x7f0b01c9 + public const int icon1 = 2131427785; // aapt resource value: 0x7f0b0128 public const int icon_frame = 2131427624; @@ -5588,8 +5588,8 @@ namespace Opus // aapt resource value: 0x7f0b01a4 public const int infoPanel = 2131427748; - // aapt resource value: 0x7f0b01ba - public const int isLive = 2131427770; + // aapt resource value: 0x7f0b01bb + public const int isLive = 2131427771; // aapt resource value: 0x7f0b0092 public const int italic = 2131427474; @@ -5618,8 +5618,8 @@ namespace Opus // aapt resource value: 0x7f0b0029 public const int line1 = 2131427369; - // aapt resource value: 0x7f0b01cb - public const int line2 = 2131427787; + // aapt resource value: 0x7f0b01cc + public const int line2 = 2131427788; // aapt resource value: 0x7f0b002a public const int line3 = 2131427370; @@ -5654,17 +5654,17 @@ namespace Opus // aapt resource value: 0x7f0b0102 public const int logo = 2131427586; - // aapt resource value: 0x7f0b01cd - public const int masked = 2131427789; + // aapt resource value: 0x7f0b01ce + public const int masked = 2131427790; - // aapt resource value: 0x7f0b01c6 - public const int maxValue = 2131427782; + // aapt resource value: 0x7f0b01c7 + public const int maxValue = 2131427783; // aapt resource value: 0x7f0b0181 public const int media_actions = 2131427713; - // aapt resource value: 0x7f0b01d7 - public const int media_route_menu_item = 2131427799; + // aapt resource value: 0x7f0b01d8 + public const int media_route_menu_item = 2131427800; // aapt resource value: 0x7f0b00cd public const int message = 2131427533; @@ -5693,8 +5693,8 @@ namespace Opus // aapt resource value: 0x7f0b0063 public const int middle = 2131427427; - // aapt resource value: 0x7f0b01c5 - public const int minValue = 2131427781; + // aapt resource value: 0x7f0b01c6 + public const int minValue = 2131427782; // aapt resource value: 0x7f0b0083 public const int mini = 2131427459; @@ -5861,8 +5861,8 @@ namespace Opus // aapt resource value: 0x7f0b005a public const int multiply = 2131427418; - // aapt resource value: 0x7f0b01ce - public const int musicLayout = 2131427790; + // aapt resource value: 0x7f0b01cf + public const int musicLayout = 2131427791; // aapt resource value: 0x7f0b0101 public const int name = 2131427585; @@ -5903,8 +5903,8 @@ namespace Opus // aapt resource value: 0x7f0b008a public const int outline = 2131427466; - // aapt resource value: 0x7f0b01ca - public const int pager = 2131427786; + // aapt resource value: 0x7f0b01cb + public const int pager = 2131427787; // aapt resource value: 0x7f0b0081 public const int parallax = 2131427457; @@ -5948,11 +5948,14 @@ namespace Opus // aapt resource value: 0x7f0b013c public const int playersHolder = 2131427644; + // aapt resource value: 0x7f0b01a9 + public const int playlistButtons = 2131427753; + // aapt resource value: 0x7f0b0134 public const int playlistHeader = 2131427636; - // aapt resource value: 0x7f0b01d0 - public const int playlistLayout = 2131427792; + // aapt resource value: 0x7f0b01d1 + public const int playlistLayout = 2131427793; // aapt resource value: 0x7f0b010a public const int playlistLocation = 2131427594; @@ -5960,8 +5963,8 @@ namespace Opus // aapt resource value: 0x7f0b0109 public const int playlistName = 2131427593; - // aapt resource value: 0x7f0b01c2 - public const int playlistURL = 2131427778; + // aapt resource value: 0x7f0b01c3 + public const int playlistURL = 2131427779; // aapt resource value: 0x7f0b011a public const int progress = 2131427610; @@ -5987,8 +5990,8 @@ namespace Opus // aapt resource value: 0x7f0b01a1 public const int queueParent = 2131427745; - // aapt resource value: 0x7f0b01bd - public const int queueSwitch = 2131427773; + // aapt resource value: 0x7f0b01be + public const int queueSwitch = 2131427774; // aapt resource value: 0x7f0b00b8 public const int radio = 2131427512; @@ -5996,14 +5999,14 @@ namespace Opus // aapt resource value: 0x7f0b00d1 public const int recycler = 2131427537; - // aapt resource value: 0x7f0b01b1 - public const int recycler_view = 2131427761; + // aapt resource value: 0x7f0b01b2 + public const int recycler_view = 2131427762; - // aapt resource value: 0x7f0b01c9 - public const int refine = 2131427785; + // aapt resource value: 0x7f0b01ca + public const int refine = 2131427786; - // aapt resource value: 0x7f0b01c7 - public const int reorder = 2131427783; + // aapt resource value: 0x7f0b01c8 + public const int reorder = 2131427784; // aapt resource value: 0x7f0b0197 public const int repeat = 2131427735; @@ -6014,8 +6017,8 @@ namespace Opus // aapt resource value: 0x7f0b012f public const int rightButtons = 2131427631; - // aapt resource value: 0x7f0b01c0 - public const int rightIcon = 2131427776; + // aapt resource value: 0x7f0b01c1 + public const int rightIcon = 2131427777; // aapt resource value: 0x7f0b0189 public const int right_icon = 2131427721; @@ -6023,8 +6026,8 @@ namespace Opus // aapt resource value: 0x7f0b0185 public const int right_side = 2131427717; - // aapt resource value: 0x7f0b01d5 - public const int saveAsPlaylist = 2131427797; + // aapt resource value: 0x7f0b01d6 + public const int saveAsPlaylist = 2131427798; // aapt resource value: 0x7f0b0019 public const int save_image_matrix = 2131427353; @@ -6053,11 +6056,11 @@ namespace Opus // aapt resource value: 0x7f0b0087 public const int scrollable = 2131427463; - // aapt resource value: 0x7f0b01c3 - public const int search = 2131427779; - // aapt resource value: 0x7f0b01c4 - public const int searchSuggestions = 2131427780; + public const int search = 2131427780; + + // aapt resource value: 0x7f0b01c5 + public const int searchSuggestions = 2131427781; // aapt resource value: 0x7f0b00c2 public const int search_badge = 2131427522; @@ -6095,11 +6098,11 @@ namespace Opus // aapt resource value: 0x7f0b00e8 public const int seek_bar_controls = 2131427560; - // aapt resource value: 0x7f0b01b2 - public const int seekbar = 2131427762; - // aapt resource value: 0x7f0b01b3 - public const int seekbar_value = 2131427763; + public const int seekbar = 2131427763; + + // aapt resource value: 0x7f0b01b4 + public const int seekbar_value = 2131427764; // aapt resource value: 0x7f0b00cc public const int select_dialog_listview = 2131427532; @@ -6107,8 +6110,8 @@ namespace Opus // aapt resource value: 0x7f0b0079 public const int selected = 2131427449; - // aapt resource value: 0x7f0b01d8 - public const int settings = 2131427800; + // aapt resource value: 0x7f0b01d9 + public const int settings = 2131427801; // aapt resource value: 0x7f0b00b4 public const int shortcut = 2131427508; @@ -6152,8 +6155,8 @@ namespace Opus // aapt resource value: 0x7f0b019c public const int songTimer = 2131427740; - // aapt resource value: 0x7f0b01b9 - public const int songView = 2131427769; + // aapt resource value: 0x7f0b01ba + public const int songView = 2131427770; // aapt resource value: 0x7f0b0140 public const int spArt = 2131427648; @@ -6188,8 +6191,8 @@ namespace Opus // aapt resource value: 0x7f0b00a4 public const int spacer = 2131427492; - // aapt resource value: 0x7f0b01b0 - public const int spinner = 2131427760; + // aapt resource value: 0x7f0b01b1 + public const int spinner = 2131427761; // aapt resource value: 0x7f0b0015 public const int split_action_bar = 2131427349; @@ -6236,11 +6239,11 @@ namespace Opus // aapt resource value: 0x7f0b0098 public const int surface_view = 2131427480; - // aapt resource value: 0x7f0b01b4 - public const int switchWidget = 2131427764; + // aapt resource value: 0x7f0b01b5 + public const int switchWidget = 2131427765; - // aapt resource value: 0x7f0b01af - public const int sync = 2131427759; + // aapt resource value: 0x7f0b01b0 + public const int sync = 2131427760; // aapt resource value: 0x7f0b00d0 public const int syncLoading = 2131427536; @@ -6272,8 +6275,8 @@ namespace Opus // aapt resource value: 0x7f0b002f public const int text2 = 2131427375; - // aapt resource value: 0x7f0b01ac - public const int textLayout = 2131427756; + // aapt resource value: 0x7f0b01ad + public const int textLayout = 2131427757; // aapt resource value: 0x7f0b00aa public const int textSpacerNoButtons = 2131427498; @@ -6332,8 +6335,8 @@ namespace Opus // aapt resource value: 0x7f0b0070 public const int top = 2131427440; - // aapt resource value: 0x7f0b01b8 - public const int topDivider = 2131427768; + // aapt resource value: 0x7f0b01b9 + public const int topDivider = 2131427769; // aapt resource value: 0x7f0b00ae public const int topPanel = 2131427502; @@ -6356,8 +6359,8 @@ namespace Opus // aapt resource value: 0x7f0b0020 public const int transition_transform = 2131427360; - // aapt resource value: 0x7f0b01d3 - public const int undoChange = 2131427795; + // aapt resource value: 0x7f0b01d4 + public const int undoChange = 2131427796; // aapt resource value: 0x7f0b005f public const int uniform = 2131427423; @@ -6377,8 +6380,8 @@ namespace Opus // aapt resource value: 0x7f0b0028 public const int view_offset_helper = 2131427368; - // aapt resource value: 0x7f0b01cc - public const int visible = 2131427788; + // aapt resource value: 0x7f0b01cd + public const int visible = 2131427789; // aapt resource value: 0x7f0b016c public const int volume_item_container = 2131427692; @@ -6392,8 +6395,8 @@ namespace Opus // aapt resource value: 0x7f0b0060 public const int wrap_content = 2131427424; - // aapt resource value: 0x7f0b01bb - public const int youtubeIcon = 2131427771; + // aapt resource value: 0x7f0b01bc + public const int youtubeIcon = 2131427772; // aapt resource value: 0x7f0b0137 public const int ytProgress = 2131427639; diff --git a/Opus/Resources/layout/PlaylistHeader.xml b/Opus/Resources/layout/PlaylistHeader.xml index f088e6a..e337b92 100644 --- a/Opus/Resources/layout/PlaylistHeader.xml +++ b/Opus/Resources/layout/PlaylistHeader.xml @@ -56,6 +56,7 @@ android:layout_width="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" + android:id="@+id/playlistButtons" android:background="@drawable/semiDarkLinear" >