From 444ffaf2188cfac0658bfce49ac5f091e68633c4 Mon Sep 17 00:00:00 2001 From: Anonymous Raccoon <32224410+AnonymusRaccoon@users.noreply.github.com> Date: Thu, 10 May 2018 14:45:28 +0200 Subject: [PATCH] Adding headphones control for play/pause. Solving bugs. --- MusicApp/MainActivity.cs | 38 ++++++++++--------- MusicApp/MusicApp.csproj | 2 + .../Resources/Portable Class/AudioStopper.cs | 20 ++++++++++ .../Resources/Portable Class/FolderBrowse.cs | 2 + .../Resources/Portable Class/FolderTracks.cs | 22 ++++++++++- .../Portable Class/HeadphonesActions.cs | 27 +++++++++++++ .../Resources/Portable Class/MusicPlayer.cs | 17 ++++++--- MusicApp/Resources/Portable Class/Player.cs | 32 ++++++++-------- MusicApp/Resources/Portable Class/Playlist.cs | 2 + .../Portable Class/PlaylistTracks.cs | 31 +++++++++++++-- MusicApp/Resources/Portable Class/Sleeper.cs | 2 +- .../Resources/Portable Class/YoutubeEngine.cs | 18 +++++++-- 12 files changed, 166 insertions(+), 47 deletions(-) create mode 100644 MusicApp/Resources/Portable Class/AudioStopper.cs create mode 100644 MusicApp/Resources/Portable Class/HeadphonesActions.cs diff --git a/MusicApp/MainActivity.cs b/MusicApp/MainActivity.cs index f90f018..0845109 100644 --- a/MusicApp/MainActivity.cs +++ b/MusicApp/MainActivity.cs @@ -346,7 +346,6 @@ namespace MusicApp await Task.Delay(100); HideTabs(); HideSearch(); - SaveInstance(); SupportFragmentManager.BeginTransaction().Replace(Resource.Id.contentView, Player.NewInstance()).Commit(); } @@ -444,9 +443,7 @@ namespace MusicApp SetYtTabs(YoutubeEngine.searchKeyWorld, selectedTab); YoutubeEngine.instances[selectedTab].focused = true; YoutubeEngine.instances[selectedTab].OnFocus(); - //YoutubeEngine.instances[selectedTab].ListView.GetLayoutManager().OnRestoreInstanceState(youtubeParcel); - youtubeInstanceSave = null; - youtubeParcel = null; + YoutubeEngine.instances[selectedTab].ResumeListView(); } else { @@ -953,8 +950,11 @@ namespace MusicApp { YoutubeEngine instance = null; foreach (YoutubeEngine inst in YoutubeEngine.instances) - if (instance.focused) + { + Console.WriteLine(inst); + if (inst.focused) instance = inst; + } youtubeParcel = instance.ListView.GetLayoutManager().OnSaveInstanceState(); MainActivity.instance.youtubeInstanceSave = "YoutubeEngine" + "-" + instance.querryType; @@ -1316,11 +1316,11 @@ namespace MusicApp parcelableSender = "Home"; parcelable = Home.instance.ListView.GetLayoutManager().OnSaveInstanceState(); } - else if (Queue.instance != null) - { - parcelableSender = "Queue"; - parcelable = Queue.instance.ListView.GetLayoutManager().OnSaveInstanceState(); - } + //else if (Queue.instance != null) + //{ + // parcelableSender = "Queue"; + // parcelable = Queue.instance.ListView.GetLayoutManager().OnSaveInstanceState(); + //} else if (Browse.instance != null && Browse.instance.focused) { parcelableSender = "Browse"; @@ -1357,21 +1357,25 @@ namespace MusicApp switch (parcelableSender) { case "Home": - Navigate(Resource.Id.musicLayout, true); - break; - case "Queue": - Transition(Resource.Id.contentView, Queue.instance, false, true); + //Navigate(Resource.Id.musicLayout, true); + FindViewById(Resource.Id.bottomView).SelectedItemId = Resource.Id.musicLayout; break; + //case "Queue": + // Transition(Resource.Id.contentView, Queue.instance, false, true); + // break; case "Browse": - Navigate(Resource.Id.browseLayout, true); + //Navigate(Resource.Id.browseLayout, true); + FindViewById(Resource.Id.bottomView).SelectedItemId = Resource.Id.browseLayout; break; case "FolderBrowse": - Navigate(Resource.Id.browseLayout, true); + //Navigate(Resource.Id.browseLayout, true); + FindViewById(Resource.Id.bottomView).SelectedItemId = Resource.Id.browseLayout; FindViewById(Resource.Id.pager).CurrentItem = 1; FindViewById(Resource.Id.tabs).SetScrollPosition(1, 0f, true); break; case "Playlist": - Navigate(Resource.Id.playlistLayout, true); + //Navigate(Resource.Id.playlistLayout, true); + FindViewById(Resource.Id.bottomView).SelectedItemId = Resource.Id.playlistLayout; break; case "YoutubeEngine-All": SetYtTabs(YoutubeEngine.searchKeyWorld, 0); diff --git a/MusicApp/MusicApp.csproj b/MusicApp/MusicApp.csproj index 36acbc9..7449108 100644 --- a/MusicApp/MusicApp.csproj +++ b/MusicApp/MusicApp.csproj @@ -227,6 +227,7 @@ + @@ -236,6 +237,7 @@ + diff --git a/MusicApp/Resources/Portable Class/AudioStopper.cs b/MusicApp/Resources/Portable Class/AudioStopper.cs new file mode 100644 index 0000000..b81b4bf --- /dev/null +++ b/MusicApp/Resources/Portable Class/AudioStopper.cs @@ -0,0 +1,20 @@ +using Android.App; +using Android.Content; +using Android.Media; + +namespace MusicApp.Resources.Portable_Class +{ + [IntentFilter(new[] { AudioManager.ActionAudioBecomingNoisy })] + public class AudioStopper : BroadcastReceiver + { + public override void OnReceive(Context context, Intent intent) + { + if (intent.Action != AudioManager.ActionAudioBecomingNoisy) + return; + + Intent musicIntent = new Intent(Application.Context, typeof(MusicPlayer)); + musicIntent.SetAction("ForcePause"); + Application.Context.StartService(musicIntent); + } + } +} \ No newline at end of file diff --git a/MusicApp/Resources/Portable Class/FolderBrowse.cs b/MusicApp/Resources/Portable Class/FolderBrowse.cs index c8e6fd9..bb08fc4 100644 --- a/MusicApp/Resources/Portable Class/FolderBrowse.cs +++ b/MusicApp/Resources/Portable Class/FolderBrowse.cs @@ -292,6 +292,8 @@ namespace MusicApp.Resources.Portable_Class { MusicPlayer.instance.AddToQueue(song); } + + Player.instance.UpdateNext(); } } diff --git a/MusicApp/Resources/Portable Class/FolderTracks.cs b/MusicApp/Resources/Portable Class/FolderTracks.cs index 6508a34..dde2e4f 100644 --- a/MusicApp/Resources/Portable Class/FolderTracks.cs +++ b/MusicApp/Resources/Portable Class/FolderTracks.cs @@ -225,6 +225,7 @@ namespace MusicApp.Resources.Portable_Class { MusicPlayer.instance.AddToQueue(song); } + Player.instance.UpdateNext(); } private void ListView_ItemLongClick(object sender, AdapterView.ItemLongClickEventArgs e) @@ -243,12 +244,31 @@ namespace MusicApp.Resources.Portable_Class AlertDialog.Builder builder = new AlertDialog.Builder(Activity, MainActivity.dialogTheme); builder.SetTitle("Pick an action"); - builder.SetItems(actions, (senderAlert, args) => + builder.SetItems(actions, async (senderAlert, args) => { switch (args.Which) { case 0: + Browse.act = Activity; + int Position = tracks.IndexOf(item); + + List queue = tracks.GetRange(Position + 1, tracks.Count - Position - 1); + if (result != null) + { + queue = result.GetRange(Position + 1, result.Count - Position - 1); + } + queue.Reverse(); + Browse.Play(item); + + while (MusicPlayer.instance == null) + await Task.Delay(10); + + foreach (Song song in queue) + { + MusicPlayer.instance.AddToQueue(song); + } + Player.instance.UpdateNext(); break; case 1: Browse.PlayNext(item); diff --git a/MusicApp/Resources/Portable Class/HeadphonesActions.cs b/MusicApp/Resources/Portable Class/HeadphonesActions.cs new file mode 100644 index 0000000..4e3fca4 --- /dev/null +++ b/MusicApp/Resources/Portable Class/HeadphonesActions.cs @@ -0,0 +1,27 @@ +using Android.Content; +using Android.Support.V4.Media.Session; + +namespace MusicApp.Resources.Portable_Class +{ + public class HeadphonesActions : MediaSessionCompat.Callback + { + public override void OnPlay() + { + base.OnPlay(); + PlayPause(); + } + + public override void OnPause() + { + base.OnPause(); + PlayPause(); + } + + void PlayPause() + { + Intent intent = new Intent(Android.App.Application.Context, typeof(MusicPlayer)); + intent.SetAction("Pause"); + Android.App.Application.Context.StartService(intent); + } + } +} \ No newline at end of file diff --git a/MusicApp/Resources/Portable Class/MusicPlayer.cs b/MusicApp/Resources/Portable Class/MusicPlayer.cs index d7e185d..e98a773 100644 --- a/MusicApp/Resources/Portable Class/MusicPlayer.cs +++ b/MusicApp/Resources/Portable Class/MusicPlayer.cs @@ -76,6 +76,11 @@ namespace MusicApp.Resources.Portable_Class Resume(); break; + case "ForcePause": + if (isRunning) + Pause(); + break; + case "Next": PlayNext(); break; @@ -165,6 +170,7 @@ namespace MusicApp.Resources.Portable_Class mediaSession.SetFlags(MediaSessionCompat.FlagHandlesMediaButtons | MediaSessionCompat.FlagHandlesTransportControls); PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder().SetActions(PlaybackStateCompat.ActionPlay | PlaybackStateCompat.ActionPause); mediaSession.SetPlaybackState(builder.Build()); + mediaSession.SetCallback(new HeadphonesActions()); } DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(Application.Context, "MusicApp"); @@ -241,6 +247,7 @@ namespace MusicApp.Resources.Portable_Class mediaSession.SetFlags(MediaSessionCompat.FlagHandlesMediaButtons | MediaSessionCompat.FlagHandlesTransportControls); PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder().SetActions(PlaybackStateCompat.ActionPlay | PlaybackStateCompat.ActionPause); mediaSession.SetPlaybackState(builder.Build()); + mediaSession.SetCallback(new HeadphonesActions()); } DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(Application.Context, "MusicApp"); @@ -595,9 +602,9 @@ namespace MusicApp.Resources.Portable_Class tmpDefaultIntent.SetAction("Player"); PendingIntent defaultIntent = PendingIntent.GetActivity(Application.Context, 0, tmpDefaultIntent, PendingIntentFlags.UpdateCurrent); - Intent tmpCancelIntent = new Intent(Application.Context, typeof(MainActivity)); - tmpCancelIntent.SetAction("Stop"); - PendingIntent cancelIntent = PendingIntent.GetActivity(Application.Context, 0, tmpCancelIntent, PendingIntentFlags.UpdateCurrent); + //Intent tmpCancelIntent = new Intent(Application.Context, typeof(MainActivity)); + //tmpCancelIntent.SetAction("Stop"); + //PendingIntent cancelIntent = PendingIntent.GetActivity(Application.Context, 0, tmpCancelIntent, PendingIntentFlags.UpdateCurrent); notification = new NotificationCompat.Builder(Application.Context, "MusicApp.Channel") .SetVisibility(NotificationCompat.VisibilityPublic) @@ -610,14 +617,14 @@ namespace MusicApp.Resources.Portable_Class .SetStyle(new MediaStyle() .SetShowActionsInCompactView(1) .SetShowCancelButton(true) - .SetCancelButtonIntent(cancelIntent) + //.SetCancelButtonIntent(cancelIntent) .SetMediaSession(mediaSession.SessionToken)) .SetColor(ContextCompat.GetColor(Application.Context, Resource.Color.notification_icon_bg_color)) .SetContentTitle(title) .SetContentText(artist) .SetLargeIcon(icon) .SetContentIntent(defaultIntent) - .SetDeleteIntent(cancelIntent) + //.SetDeleteIntent(cancelIntent) .Build(); ContextCompat.StartForegroundService(Application.Context, new Intent(Application.Context, typeof(MusicPlayer))); StartForeground(notificationID, notification); diff --git a/MusicApp/Resources/Portable Class/Player.cs b/MusicApp/Resources/Portable Class/Player.cs index d28d483..feae8e4 100644 --- a/MusicApp/Resources/Portable Class/Player.cs +++ b/MusicApp/Resources/Portable Class/Player.cs @@ -1,21 +1,16 @@ using Android.Content; +using Android.Graphics; +using Android.Graphics.Drawables; using Android.OS; +using Android.Support.Design.Widget; +using Android.Support.V4.App; +using Android.Support.V4.Widget; using Android.Views; using Android.Widget; using MusicApp.Resources.values; -using System; using Square.Picasso; -using Android.Support.Design.Widget; +using System; using System.Threading.Tasks; -using Android.Support.V4.App; -using Java.Util; - -using AlarmManager = Android.App.AlarmManager; -using PendingIntent = Android.App.PendingIntent; -using Android.Graphics; -using System.Threading; -using Android.Support.V4.Widget; -using Android.Graphics.Drawables; namespace MusicApp.Resources.Portable_Class { @@ -27,8 +22,8 @@ namespace MusicApp.Resources.Portable_Class private SeekBar bar; private ImageView imgView; - private int[] timers = new int[] { 0, 1, 10, 30, 60, 120 }; - private string[] items = new string[] { "Off", "1 minute", "10 minutes", "30 minutes", "1 hour", "2 hours" }; + private readonly int[] timers = new int[] { 0, 1, 10, 30, 60, 120 }; + private readonly string[] items = new string[] { "Off", "1 minute", "10 minutes", "30 minutes", "1 hour", "2 hours" }; private int checkedItem = 0; @@ -164,12 +159,15 @@ namespace MusicApp.Resources.Portable_Class Picasso.With(Android.App.Application.Context).Load(Resource.Drawable.noAlbum).Placeholder(Resource.Drawable.MusicIcon).Resize(400, 400).CenterCrop().Into(nextArt); } - while (MusicPlayer.player == null) + while (MusicPlayer.player == null || MusicPlayer.player.Duration == 0) await Task.Delay(100); bar = playerView.FindViewById(Resource.Id.songTimer); MusicPlayer.SetSeekBar(bar); handler.PostDelayed(UpdateSeekBar, 1000); + + await Task.Delay(1000); + MusicPlayer.SetSeekBar(bar); } public async void RefreshPlayer() @@ -296,7 +294,7 @@ namespace MusicApp.Resources.Portable_Class handler.PostDelayed(UpdateSeekBar, 1000); } - private void Fab_Click(object sender, EventArgs e) + private async void Fab_Click(object sender, EventArgs e) { MainActivity.instance.SupportFragmentManager.PopBackStack(); if (MainActivity.instance.youtubeInstanceSave != null) @@ -319,10 +317,11 @@ namespace MusicApp.Resources.Portable_Class default: break; } + await Task.Delay(750); MainActivity.instance.SetYtTabs(YoutubeEngine.searchKeyWorld, selectedTab); YoutubeEngine.instances[selectedTab].focused = true; YoutubeEngine.instances[selectedTab].OnFocus(); - YoutubeEngine.instances[selectedTab].ListView.GetLayoutManager().OnRestoreInstanceState(MainActivity.instance.youtubeParcel); + YoutubeEngine.instances[selectedTab].ResumeListView(); } else { @@ -369,6 +368,7 @@ namespace MusicApp.Resources.Portable_Class void Sleep(int time) { + Console.WriteLine("&Going to sleep in " + time + ", slected item is the " + checkedItem + " one."); Intent intent = new Intent(Activity, typeof(Sleeper)); intent.PutExtra("time", time); Activity.StartService(intent); diff --git a/MusicApp/Resources/Portable Class/Playlist.cs b/MusicApp/Resources/Portable Class/Playlist.cs index 4a83206..0aa8050 100644 --- a/MusicApp/Resources/Portable Class/Playlist.cs +++ b/MusicApp/Resources/Portable Class/Playlist.cs @@ -322,6 +322,7 @@ namespace MusicApp.Resources.Portable_Class { MusicPlayer.instance.AddToQueue(song); } + Player.instance.UpdateNext(); } } @@ -357,6 +358,7 @@ namespace MusicApp.Resources.Portable_Class { MusicPlayer.instance.AddToQueue(song); } + Player.instance.UpdateNext(); } public static void RandomPlay(long playlistID, Context context) diff --git a/MusicApp/Resources/Portable Class/PlaylistTracks.cs b/MusicApp/Resources/Portable Class/PlaylistTracks.cs index 3e0331e..8e7a2f2 100644 --- a/MusicApp/Resources/Portable Class/PlaylistTracks.cs +++ b/MusicApp/Resources/Portable Class/PlaylistTracks.cs @@ -84,7 +84,6 @@ namespace MusicApp.Resources.Portable_Class async void PopulateList() { - System.Console.WriteLine("&Populating playlist tracks with ydID = " + ytID); if (playlistId == 0 && ytID == "") return; @@ -308,12 +307,13 @@ namespace MusicApp.Resources.Portable_Class { MusicPlayer.instance.AddToQueue(song); } + Player.instance.UpdateNext(); } private void ListView_ItemLongClick(object sender, AdapterView.ItemLongClickEventArgs e) { Song item = tracks[e.Position]; - if (result != null) + if (result != null && result.Count > e.Position) item = result[e.Position]; More(item, e.Position); @@ -336,15 +336,38 @@ namespace MusicApp.Resources.Portable_Class AlertDialog.Builder builder = new AlertDialog.Builder(Activity, MainActivity.dialogTheme); builder.SetTitle("Pick an action"); - builder.SetItems(action.ToArray(), (senderAlert, args) => + builder.SetItems(action.ToArray(), async (senderAlert, args) => { switch (args.Which) { case 0: + int Position = tracks.IndexOf(item); + List queue = tracks.GetRange(Position + 1, tracks.Count - Position - 1); + if (result != null && result.Count - 1 >= Position) + { + item = result[Position]; + queue = result.GetRange(Position + 1, result.Count - Position - 1); + } + queue.Reverse(); + if (!item.IsYt) + { + Browse.act = Activity; Browse.Play(item); + } else - YoutubeEngine.Play(item.GetPath(), item.GetName(), item.GetArtist(), item.GetAlbum()); + { + YoutubeEngine.Play(item.youtubeID, item.GetName(), item.GetArtist(), item.GetAlbum()); + } + + while (MusicPlayer.instance == null) + await Task.Delay(10); + + foreach (Song song in queue) + { + MusicPlayer.instance.AddToQueue(song); + } + Player.instance.UpdateNext(); break; case 1: diff --git a/MusicApp/Resources/Portable Class/Sleeper.cs b/MusicApp/Resources/Portable Class/Sleeper.cs index 77a50bc..f98b4f1 100644 --- a/MusicApp/Resources/Portable Class/Sleeper.cs +++ b/MusicApp/Resources/Portable Class/Sleeper.cs @@ -23,7 +23,6 @@ namespace MusicApp.Resources.Portable_Class public override void OnCreate() { base.OnCreate(); - instance = this; } public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) @@ -74,6 +73,7 @@ namespace MusicApp.Resources.Portable_Class musicIntent.SetAction("SleepPause"); MainActivity.instance.StartService(musicIntent); notificationManager.Cancel(1001); + instance = null; } } } \ No newline at end of file diff --git a/MusicApp/Resources/Portable Class/YoutubeEngine.cs b/MusicApp/Resources/Portable Class/YoutubeEngine.cs index fa0c442..6d2c39a 100644 --- a/MusicApp/Resources/Portable Class/YoutubeEngine.cs +++ b/MusicApp/Resources/Portable Class/YoutubeEngine.cs @@ -37,7 +37,7 @@ namespace MusicApp.Resources.Portable_Class public View emptyView; public static View loadingView; private bool searching; - private string[] actions = new string[] { "Play", "Play Next", "Play Last", "Add To Playlist", "Download" }; + private readonly string[] actions = new string[] { "Play", "Play Next", "Play Last", "Add To Playlist", "Download" }; public override void OnActivityCreated(Bundle savedInstanceState) { @@ -76,6 +76,16 @@ namespace MusicApp.Resources.Portable_Class } } + public async void ResumeListView() + { + while (ListView == null || ListView.GetLayoutManager() == null) + await Task.Delay(10); + + ListView.GetLayoutManager().OnRestoreInstanceState(MainActivity.instance.youtubeParcel); + MainActivity.instance.youtubeInstanceSave = null; + MainActivity.instance.youtubeParcel = null; + } + public static Fragment[] NewInstances(string searchQuery) { searchKeyWorld = searchQuery; @@ -151,11 +161,13 @@ namespace MusicApp.Resources.Portable_Class } searchResult.MaxResults = 20; + System.Console.WriteLine("&Search created"); var searchReponse = await searchResult.ExecuteAsync(); + System.Console.WriteLine("&Search waited"); result = new List(); - foreach(var video in searchReponse.Items) + foreach (var video in searchReponse.Items) { Song videoInfo = new Song(video.Snippet.Title, video.Snippet.ChannelTitle, video.Snippet.Thumbnails.Default__.Url, video.Id.VideoId ?? video.Id.PlaylistId, -1, -1, video.Id.VideoId ?? video.Id.PlaylistId, true); YtKind kind = YtKind.Null; @@ -245,7 +257,7 @@ namespace MusicApp.Resources.Portable_Class MainActivity.instance.SupportActionBar.Title = item.GetName(); MainActivity.instance.HideTabs(); instances = null; - //MainActivity.instance.youtubeParcel = ListView.GetLayoutManager().OnSaveInstanceState(); + MainActivity.instance.youtubeParcel = ListView.GetLayoutManager().OnSaveInstanceState(); MainActivity.instance.youtubeInstanceSave = "YoutubeEngine" + "-" + querryType; MainActivity.instance.Transition(Resource.Id.contentView, PlaylistTracks.NewInstance(item.youtubeID, item.GetName()), true); break;