Skip to content

Commit

Permalink
Delete from queue (#639)
Browse files Browse the repository at this point in the history
* iOS - swipe implementation

* Android - swipe implementation

* Handling queue modified

* Delete from queue toast

* Cleanup

* iOS build fix

* Delete from queue when Shuffle enabled fix
  • Loading branch information
akrol95 authored Dec 10, 2024
1 parent 8c836f5 commit 5db94ed
Show file tree
Hide file tree
Showing 46 changed files with 2,363 additions and 106 deletions.
2 changes: 2 additions & 0 deletions BMM.Core/Constants/ViewConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ public class ViewConstants
public const float LongAnimationDuration = 0.6f;
public const float DefaultAnimationDuration = 0.3f;
public const float QuickAnimationDuration = DefaultAnimationDuration / 2;
public const float SwiftAnimationDuration = 0.1f;
public const int SwiftAnimationDurationInMilliseconds = (int)(SwiftAnimationDuration * 1000);
public const int DefaultAnimationDurationInMilliseconds = (int)(DefaultAnimationDuration * 1000);
public const int LongAnimationDurationInMilliseconds = (int)(LongAnimationDuration * 1000);
public const int QuickAnimationDurationInMilliseconds = (int)(QuickAnimationDuration * 1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,12 @@ await _mvxNavigationService.Navigate<AlbumViewModel, Album>(new Album
{
await _showTrackInfoAction.ExecuteGuarded(track);
})));

options.AddIf(() => sourceVM is QueueViewModel,
new StandardIconOptionPO(
_bmmLanguageBinder[Translations.QueueViewModel_RemoveFromQueueOption],
ImageResourceNames.IconRemove,
new MvxAsyncCommand(() => _mediaPlayer.DeleteFromQueue(track))));

return options;
}
Expand Down
24 changes: 22 additions & 2 deletions BMM.Core/Helpers/ListHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,32 @@ public static int FindIndex<T>(this IList<T> source, Predicate<T> match, int sta
for (int i = startIndex; i < source.Count; i++)
{
if (match(source[i]))
{
return i;
}
}

return NumericConstants.Undefined;
}

public static T FindNextAfter<T>(this IList<T> source, T item)
{
int nextIndex = source
.IndexOf(item) + 1;

if (nextIndex >= source.Count)
return default;

return source[nextIndex];
}

public static T FindPreviousBefore<T>(this IList<T> source, T item)
{
int previousIndex = source
.IndexOf(item) - 1;

if (previousIndex < NumericConstants.Zero)
return default;

return source[previousIndex];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
using BMM.Core.Extensions;
using BMM.Core.Implementations.Device;
using BMM.Core.Implementations.LiveRadio;
using BMM.Core.Implementations.Localization.Interfaces;
using BMM.Core.Implementations.UI;
using BMM.Core.Messages.MediaPlayer;
using BMM.Core.NewMediaPlayer;
using BMM.Core.NewMediaPlayer.Abstractions;
using BMM.Core.NewMediaPlayer.Constants;
using BMM.Core.Translation;
using BMM.Core.ViewModels;
using MvvmCross.Navigation;
using MvvmCross.Plugin.Messenger;
Expand All @@ -31,6 +34,8 @@ public class ViewModelHandlingMediaPlayerDecorator : IMediaPlayer

private readonly ILiveTime _liveTime;
private readonly IMvxMessenger _mvxMessenger;
private readonly IToastDisplayer _toastDisplayer;
private readonly IBMMLanguageBinder _bmmLanguageBinder;

private bool _isViewmodelShown;

Expand All @@ -42,7 +47,9 @@ public ViewModelHandlingMediaPlayerDecorator(
IMediaPlayerInitializer mediaPlayerInitializer,
IPlayerErrorHandler playerErrorHandler,
ILiveTime liveTime,
IMvxMessenger mvxMessenger)
IMvxMessenger mvxMessenger,
IToastDisplayer toastDisplayer,
IBMMLanguageBinder bmmLanguageBinder)
{
_deviceInfo = deviceInfo;
_navigationService = navigationService;
Expand All @@ -52,6 +59,8 @@ public ViewModelHandlingMediaPlayerDecorator(
_playerErrorHandler = playerErrorHandler;
_liveTime = liveTime;
_mvxMessenger = mvxMessenger;
_toastDisplayer = toastDisplayer;
_bmmLanguageBinder = bmmLanguageBinder;

_mediaPlayer.ContinuingPreviousSession = () => { ShowViewmodelIfNecessary(); };
}
Expand Down Expand Up @@ -195,6 +204,18 @@ public void Stop()

public decimal CurrentPlaybackSpeed => _mediaPlayer.CurrentPlaybackSpeed;

public async Task DeleteFromQueue(IMediaTrack track)
{
if (track.Id == CurrentTrack?.Id)
{
await _toastDisplayer.WarnAsync(_bmmLanguageBinder[Translations.QueueViewModel_CannotRemoveFromQueue]);
return;
}

await _mediaPlayer.DeleteFromQueue(track);
await _toastDisplayer.Success(_bmmLanguageBinder[Translations.QueueViewModel_TrackRemovedFromQueue]);
}

public Task<bool> AddToEndOfQueue(IMediaTrack track, string playbackOrigin, bool ignoreIfAlreadyAdded = false)
{
if (ignoreIfAlreadyAdded && _queue.Tracks.Any(t => t.Equals(track)))
Expand Down
10 changes: 10 additions & 0 deletions BMM.Core/Messages/MediaPlayer/QueueChangedMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using MvvmCross.Plugin.Messenger;

namespace BMM.Core.Messages.MediaPlayer;

public class QueueChangedMessage : MvxMessage
{
public QueueChangedMessage(object sender) : base(sender)
{
}
}
7 changes: 7 additions & 0 deletions BMM.Core/Models/Enums/SwipePlacement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace BMM.Core.Models.Enums;

public enum SwipePlacement
{
Left,
Right
}
7 changes: 7 additions & 0 deletions BMM.Core/Models/POs/Tracks/TrackPO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using BMM.Core.Constants;
using BMM.Core.Extensions;
using BMM.Core.GuardedActions.Tracks.Interfaces;
using BMM.Core.Helpers;
using BMM.Core.Implementations.Downloading.DownloadQueue;
using BMM.Core.Implementations.FileStorage;
using BMM.Core.Implementations.FirebaseRemoteConfig;
Expand Down Expand Up @@ -65,13 +66,19 @@ public TrackPO(
{
await optionsClickedCommand.ExecuteAsync(Track);
});

DeleteFromQueueCommand = new ExceptionHandlingCommand(async () =>
{
await mediaPlayer.DeleteFromQueue(track);
});

RefreshState().FireAndForget();
SetTrackInformation();
}

public IMvxAsyncCommand ShowTrackInfoCommand { get; }
public IMvxAsyncCommand OptionButtonClickedCommand { get; }
public IMvxAsyncCommand DeleteFromQueueCommand { get; }

private void SetTrackInformation()
{
Expand Down
2 changes: 2 additions & 0 deletions BMM.Core/NewMediaPlayer/Abstractions/IMediaPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public interface ICommonMediaPlayer

decimal CurrentPlaybackSpeed { get; }

Task DeleteFromQueue(IMediaTrack track);

Task<bool> AddToEndOfQueue(IMediaTrack track, string playbackOrigin, bool ignoreIfAlreadyAdded = false);

Task<bool> QueueToPlayNext(IMediaTrack track, string playbackOrigin);
Expand Down
7 changes: 4 additions & 3 deletions BMM.Core/NewMediaPlayer/Abstractions/IMediaQueue.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using BMM.Api.Abstraction;
using BMM.Api.Abstraction;

namespace BMM.Core.NewMediaPlayer.Abstractions
{
Expand All @@ -13,8 +11,11 @@ public interface IMediaQueue
Task<bool> PlayNext(IMediaTrack track, IMediaTrack currentPlayedTrack);

Task<bool> Append(IMediaTrack track);

void Delete(IMediaTrack track);

IList<IMediaTrack> Tracks { get; }
bool HasPendingChanges { get; set; }

bool IsSameQueue(IList<IMediaTrack> newMediaTracks);

Expand Down
22 changes: 19 additions & 3 deletions BMM.Core/NewMediaPlayer/MediaQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
using BMM.Core.Implementations.Localization.Interfaces;
using BMM.Core.Implementations.Media;
using BMM.Core.Implementations.UI;
using BMM.Core.Messages.MediaPlayer;
using BMM.Core.NewMediaPlayer.Abstractions;
using BMM.Core.Translation;
using BMM.Core.ViewModels.MyContent;
using MvvmCross;
using MvvmCross.Base;
using MvvmCross.Localization;
using MvvmCross.Plugin.Messenger;

namespace BMM.Core.NewMediaPlayer
{
Expand All @@ -26,19 +28,23 @@ public class MediaQueue : IMediaQueue
private readonly MediaFileUrlSetter _mediaFileUrlSetter;
private readonly IToastDisplayer _toastDisplayer;
private readonly IBMMLanguageBinder _bmmLanguageBinder;

public IList<IMediaTrack> Tracks { get; private set; }
private readonly IMvxMessenger _mvxMessenger;

public MediaQueue(
MediaFileUrlSetter mediaFileUrlSetter,
IToastDisplayer toastDisplayer,
IBMMLanguageBinder bmmLanguageBinder)
IBMMLanguageBinder bmmLanguageBinder,
IMvxMessenger mvxMessenger)
{
_mediaFileUrlSetter = mediaFileUrlSetter;
_toastDisplayer = toastDisplayer;
_bmmLanguageBinder = bmmLanguageBinder;
_mvxMessenger = mvxMessenger;
Tracks = new List<IMediaTrack>();
}

public IList<IMediaTrack> Tracks { get; private set; }
public bool HasPendingChanges { get; set; }

public void Replace(IMediaTrack track)
{
Expand Down Expand Up @@ -119,6 +125,16 @@ public async Task<bool> PlayNext(IMediaTrack track, IMediaTrack currentPlayedTra

return true;
}

public void Delete(IMediaTrack track)
{
lock (_lock)
{
Tracks.Remove(track);
_mvxMessenger.Publish(new QueueChangedMessage(this));
HasPendingChanges = true;
}
}

// todo this should be handled without checking the UI!
private async Task<bool> FileNotDownloadedButInOfflineViewModel(IMediaTrack track)
Expand Down
14 changes: 14 additions & 0 deletions BMM.Core/NewMediaPlayer/ShuffleableQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ public Task<bool> Append(IMediaTrack track)
return _queue.Append(track);
}

public void Delete(IMediaTrack track)
{
if (IsShuffleEnabled)
_shuffledTracks.Remove(track);

_queue.Delete(track);
}

public Task<bool> PlayNext(IMediaTrack track, IMediaTrack currentPlayedTrack)
{
if (IsShuffleEnabled)
Expand All @@ -61,6 +69,12 @@ public Task<bool> PlayNext(IMediaTrack track, IMediaTrack currentPlayedTrack)

public IList<IMediaTrack> Tracks => IsShuffleEnabled ? _shuffledTracks : _queue.Tracks;

public bool HasPendingChanges
{
get => _queue.HasPendingChanges;
set => _queue.HasPendingChanges = value;
}

public bool IsSameQueue(IList<IMediaTrack> newMediaTracks)
{
return _queue.IsSameQueue(newMediaTracks);
Expand Down
4 changes: 4 additions & 0 deletions BMM.Core/Translation/en/Translations.designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion BMM.Core/Translation/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,11 @@
"Read": "Read"
},
"QueueViewModel": {
"Title": "Queue"
"Title": "Queue",
"Delete": "Delete",
"CannotRemoveFromQueue": "Cannot remove currently playing track from the queue",
"TrackRemovedFromQueue": "Track has been removed from the queue",
"RemoveFromQueueOption": "Remove from queue"
},
"SearchViewModel": {
"Title": "Search",
Expand Down
3 changes: 3 additions & 0 deletions BMM.Core/ViewModels/OptionsListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ namespace BMM.Core.ViewModels
{
public class OptionsListViewModel : BaseViewModel<IOptionsListParameter>, ITrackOptionsViewModel
{
private const int TimeForDialogCloseInMillis = 250;

public OptionsListViewModel()
{
CloseOptionsCommand = new MvxCommand(() => CloseInteraction.Raise());
OptionSelectedCommand = new MvxAsyncCommand<StandardIconOptionPO>(async option =>
{
await CloseCommand.ExecuteAsync();
await Task.Delay(TimeForDialogCloseInMillis);
option.ClickCommand.Execute();
});
}
Expand Down
31 changes: 29 additions & 2 deletions BMM.Core/ViewModels/QueueViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,60 @@
using System.Threading.Tasks;
using BMM.Api.Abstraction;
using BMM.Api.Implementation.Models;
using BMM.Core.Extensions;
using BMM.Core.Implementations.Factories.Tracks;
using BMM.Core.Implementations.UI;
using BMM.Core.Messages.MediaPlayer;
using BMM.Core.Models.POs.Base;
using BMM.Core.Models.POs.Base.Interfaces;
using BMM.Core.Models.POs.Tracks;
using BMM.Core.NewMediaPlayer.Abstractions;
using BMM.Core.ViewModels.Base;
using MvvmCross;
using MvvmCross.Plugin.Messenger;

namespace BMM.Core.ViewModels
{
public class QueueViewModel : DocumentsViewModel
{
private readonly IMediaQueue _mediaQueue;
private readonly ITrackPOFactory _trackPOFactory;
private readonly IMediaPlayer _mediaPlayer;
private readonly IMvxMessenger _mvxMessenger;
private MvxSubscriptionToken _token;

public QueueViewModel(
IMediaQueue mediaQueue,
ITrackPOFactory trackPOFactory)
ITrackPOFactory trackPOFactory,
IMediaPlayer mediaPlayer,
IMvxMessenger mvxMessenger)
{
_mediaQueue = mediaQueue;
_trackPOFactory = trackPOFactory;
_mediaPlayer = mediaPlayer;
_mvxMessenger = mvxMessenger;
}

public override void ViewAppeared()
{
base.ViewAppeared();
_token = _mvxMessenger.Subscribe<QueueChangedMessage>(QueueChangedAction);
}

public override void ViewDisappeared()
{
base.ViewDisappeared();
_mvxMessenger.UnsubscribeSafe<QueueChangedMessage>(_token);
}

private async void QueueChangedAction(QueueChangedMessage queueChangedMessage)
{
await Refresh();
}

protected override async Task DocumentAction(IDocumentPO item, IList<Track> list)
{
await Mvx.IoCProvider.Resolve<IMediaPlayer>().Play(_mediaQueue.Tracks, ((TrackPO)item).Track);
await _mediaPlayer.Play(_mediaQueue.Tracks, ((TrackPO)item).Track);
}

public async override Task<IEnumerable<IDocumentPO>> LoadItems(CachePolicy policy = CachePolicy.UseCacheAndRefreshOutdated)
Expand Down
Loading

0 comments on commit 5db94ed

Please sign in to comment.