MediaPlaybackViewModel.cs 29.9 KB
Newer Older
1 2 3 4 5 6 7 8 9
/**********************************************************************
 * VLC for WinRT
 **********************************************************************
 * Copyright © 2013-2014 VideoLAN and Authors
 *
 * Licensed under GPLv2+ and MPLv2
 * Refer to COPYING file of the official project for license
 **********************************************************************/

10
using Windows.Storage.AccessCache;
11
using Windows.Media;
12
using System.Collections.Generic;
13
using System.Diagnostics;
14
using Windows.Storage;
15
using System;
16
using Windows.UI.Core;
17 18 19
using VLC.Helpers;
using VLC.Model;
using VLC.Commands.MediaPlayback;
20
using System.Threading.Tasks;
21
using Windows.Storage.Streams;
22
using Autofac;
23 24
using VLC.Services.RunTime;
using VLC.ViewModels.MusicVM;
Thomas Nigro's avatar
Thomas Nigro committed
25
using libVLCX;
26 27 28
using VLC.Utils;
using VLC.Commands.VideoPlayer;
using VLC.Commands.VideoLibrary;
Thomas Nigro's avatar
Thomas Nigro committed
29
using System.Linq;
30 31 32 33
using VLC.Commands.MusicPlayer;
using VLC.MediaMetaFetcher.Fetchers;
using VLC.Model.Video;
using VLC.Model.Stream;
34
using Windows.UI.Popups;
35
using Windows.UI.Xaml;
36
using System.Collections.ObjectModel;
37
using Windows.UI.ViewManagement;
38
using VLC.Commands;
39
using VLC.Model.Music;
40

41
namespace VLC.ViewModels
42
{
43
    public sealed class MediaPlaybackViewModel : BindableBase
44
    {
45
        #region private props
46
        private MouseService _mouseService;
47
        private SystemMediaTransportControls _systemMediaTransportControls;
48
        private TimeSpan _timeTotal;
49

50
        private int _volume = 100;
51
        private int _speedRate = 100;
Thomas Nigro's avatar
Thomas Nigro committed
52
        private int _bufferingProgress;
53
        private Visibility _loadingMedia = Visibility.Collapsed;
54
        #endregion
55

56
        public PlaybackService PlaybackService { get { return Locator.PlaybackService; } }
57 58
        public MouseService MouseService { get { return _mouseService; } }

Thomas Nigro's avatar
Thomas Nigro committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

        #region commands

        public PlayTrackCollCommand PlayTrackCollCommand { get; } = new PlayTrackCollCommand();

        public PlayPauseCommand PlayOrPauseCommand { get; } = new PlayPauseCommand();

        public PlayNextCommand PlayNextCommand { get; } = new PlayNextCommand();

        public PlayPreviousCommand PlayPreviousCommand { get; } = new PlayPreviousCommand();

        public OpenSubtitleCommand OpenSubtitleCommand { get; } = new OpenSubtitleCommand();

        public PickMediaCommand PickMediaCommand { get; } = new PickMediaCommand();

        public StopVideoCommand GoBack { get; } = new StopVideoCommand();

        public ChangePlaybackSpeedRateCommand ChangePlaybackSpeedRateCommand { get; } = new ChangePlaybackSpeedRateCommand();

        public ChangeVolumeCommand ChangeVolumeCommand { get; } = new ChangeVolumeCommand();

        public ChangeAudioDelayCommand ChangeAudioDelayCommand { get; } = new ChangeAudioDelayCommand();

        public ChangeSpuDelayCommand ChangeSpuDelayCommand { get; } = new ChangeSpuDelayCommand();
83 84

        public FastSeekCommand FastSeekCommand { get; } = new FastSeekCommand();
85 86 87 88 89

        public ActionCommand IncreaseVolumeCommand { get; } = new ActionCommand(() => Locator.MediaPlaybackViewModel.ChangeVolumeCommand.Execute("higher"));

        public ActionCommand DecreaseVolumeCommand { get; } = new ActionCommand(() => Locator.MediaPlaybackViewModel.ChangeVolumeCommand.Execute("lower"));

90 91
        #endregion

92
        #region public props
93

94 95
        public bool IsPlaying
        {
96
            get { return PlaybackService.IsPlaying && !PlaybackService.IsPaused; }
97
        }
Thomas Nigro's avatar
Thomas Nigro committed
98

99 100 101
        public bool CanGoNext => Locator.PlaybackService.CanGoNext;
        public bool CanGoPrevious => Locator.PlaybackService.CanGoPrevious;
        public ObservableCollection<IMediaItem> Playlist => Locator.PlaybackService.Playlist;
102 103 104 105 106
        public bool IsShuffled
        {
            get { return Locator.PlaybackService.IsShuffled; }
            set { Locator.PlaybackService.IsShuffled = value; }
        }
107 108 109 110 111
        public bool Repeat
        {
            get { return Locator.PlaybackService.Repeat; }
            set { Locator.PlaybackService.Repeat = value; }
        }
112

Thomas Nigro's avatar
Thomas Nigro committed
113
        public MediaState MediaState => PlaybackService.PlayerState;
114

115 116 117 118 119 120 121 122
        public int Volume
        {
            get
            {
                return _volume;
            }
            set
            {
123
                Debug.WriteLine("new volume set: " + value);
124
                if (value >= VOLUME_MIN && value <= VOLUME_MAX)
125
                {
Thomas Nigro's avatar
Thomas Nigro committed
126
                    PlaybackService.Volume = value;
127 128 129 130
                    SetProperty(ref _volume, value);
                }
            }
        }
Thomas Nigro's avatar
Thomas Nigro committed
131

132 133 134 135 136 137 138 139
        public int SpeedRate
        {
            get
            {
                return _speedRate;
            }
            set
            {
140
                SetProperty(ref _speedRate, value);
141
                float r = (float)value / 100;
Thomas Nigro's avatar
Thomas Nigro committed
142
                PlaybackService.SetSpeedRate(r);
143 144
            }
        }
145

Thomas Nigro's avatar
Thomas Nigro committed
146 147 148 149
        /// <summary>
        /// Gets and Sets the AudioDelay in MilliSeconds
        /// Warning : VLC API needs microseconds, hence the 1000 multiplication
        /// </summary>
150 151
        public long AudioDelay
        {
152
            get { return PlaybackService.AudioDelay; }
153 154
            set
            {
155 156
                PlaybackService.AudioDelay = value;
                OnPropertyChanged(nameof(AudioDelay));
157 158 159
            }
        }

Thomas Nigro's avatar
Thomas Nigro committed
160 161 162 163 164

        /// <summary>
        /// Gets and Sets the SpuDelay in MilliSeconds
        /// Warning : VLC API needs microseconds, hence the 1000 multiplication
        /// </summary>
165 166
        public long SpuDelay
        {
167
            get { return PlaybackService.SpuDelay; }
168 169
            set
            {
170 171
                PlaybackService.SpuDelay = value;
                OnPropertyChanged(nameof(SpuDelay));
172 173
            }
        }
174

175 176 177 178 179 180
        public TimeSpan TimeTotal
        {
            get { return _timeTotal; }
            set { SetProperty(ref _timeTotal, value); }
        }

181
        public int BufferingProgress => _bufferingProgress;
Thomas Nigro's avatar
Thomas Nigro committed
182

183
        public bool IsBuffered => _bufferingProgress == 100;
Thomas Nigro's avatar
Thomas Nigro committed
184

185 186 187
        /**
         * Elasped time in milliseconds
         */
188
        public long Time
189
        {
190 191
            get { return PlaybackService.Time; }
            set { PlaybackService.Time = value; }
192
        }
193
        public float Position
194
        {
195
            get { return PlaybackService.Position; }
Martin Finkel's avatar
Martin Finkel committed
196 197 198 199 200
            set
            {
                if (!SliderBindingEnabled) return; // *ugly* workaround for #212 to bypass two-way databinding madness
                PlaybackService.Position = value;
            }
201
        }
Thomas Nigro's avatar
Thomas Nigro committed
202

203 204

        private DictionaryKeyValue _currentSubtitle;
205 206
        public DictionaryKeyValue CurrentSubtitle
        {
207
            get { return _currentSubtitle; }
208 209
            set
            {
210 211 212 213
                if (value == null)
                    return;
                _currentSubtitle = value;
                PlaybackService.CurrentSubtitleTrack = value.Id;
214 215 216
            }
        }

217
        private DictionaryKeyValue _currentAudioTrack;
218 219
        public DictionaryKeyValue CurrentAudioTrack
        {
220
            get { return _currentAudioTrack; }
221 222
            set
            {
223 224 225 226
                if (value == null)
                    return;
                _currentAudioTrack = value;
                PlaybackService.CurrentAudioTrack = value.Id;
227 228
            }
        }
229 230 231 232 233

        public VLCChapterDescription CurrentChapter
        {
            get
            {
Thomas Nigro's avatar
Thomas Nigro committed
234
                return PlaybackService.GetCurrentChapter();
235 236 237
            }
            set
            {
238 239
                if (value != null)
                    PlaybackService.SetCurrentChapter(value);
240 241
            }
        }
242 243 244 245 246

        public int VOLUME_MAX { get; } = 200;
        public int VOLUME_THRESHOLD { get; } = 100;
        public int VOLUME_MIN { get; } = 0;

Thomas Nigro's avatar
Thomas Nigro committed
247
        #endregion
248

Thomas Nigro's avatar
Thomas Nigro committed
249
        #region public fields
250
        public ObservableCollection<DictionaryKeyValue> AudioTracks { get; } = new ObservableCollection<DictionaryKeyValue>();
251

252
        public ObservableCollection<DictionaryKeyValue> Subtitles { get; } = new ObservableCollection<DictionaryKeyValue>();
253

254
        public List<VLCChapterDescription> Chapters => PlaybackService.GetChapters();
255

256
        public Visibility LoadingMedia { get { return _loadingMedia; } set { SetProperty(ref _loadingMedia, value); } }
Thomas Nigro's avatar
Thomas Nigro committed
257
        #endregion
258

Thomas Nigro's avatar
Thomas Nigro committed
259
        #region constructors
260
        public MediaPlaybackViewModel()
261
        {
Thomas Nigro's avatar
Thomas Nigro committed
262
            _mouseService = App.Container.Resolve<MouseService>();
263 264
            PlaybackService.Playback_MediaPlaying += OnPlaying;
            PlaybackService.Playback_MediaPaused += OnPaused;
265 266 267 268 269 270
            PlaybackService.Playback_MediaTimeChanged += Playback_MediaTimeChanged;
            PlaybackService.Playback_MediaLengthChanged += Playback_MediaLengthChanged;
            PlaybackService.Playback_MediaBuffering += Playback_MediaBuffering;
            PlaybackService.Playback_MediaEndReached += Playback_MediaEndReach;
            PlaybackService.Playback_MediaFailed += Playback_MediaFailed;
            PlaybackService.Playback_MediaStopped += Playback_MediaStopped;
271 272
            PlaybackService.Playback_MediaTracksAdded += OnMediaTrackAdded;
            PlaybackService.Playback_MediaTracksDeleted += OnMediaTrackDeleted;
273 274
            PlaybackService.Playback_MediaParsed += Playback_MediaParsed;

275
            App.Container.Resolve<NetworkListenerService>().InternetConnectionChanged += MediaPlaybackViewModel_InternetConnectionChanged;
276 277 278 279

            Locator.PlaybackService.Playback_MediaSet += OnCurrentMediaChanged;
            Locator.PlaybackService.OnPlaylistChanged += PlaylistService_OnPlaylistChanged;
            Locator.PlaybackService.OnPlaylistEndReached += OnPlaylistEndReached;
280
            Locator.PlaybackService.OnRepeatChanged += OnRepeatChanged;
281
            Locator.PlaybackService.PlayingTypeChanged += OnPlayingTypeChanged;
282 283 284 285 286 287 288 289 290 291

            Window.Current.SizeChanged += OnSizeChanged;
        }

        async void OnSizeChanged(object sender, WindowSizeChangedEventArgs windowSizeChangedEventArgs)
        {
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
            {
                OnPropertyChanged(nameof(MiniPlayerVisibilityMediaCenter));
            });
292 293 294 295
        }

        private async void OnRepeatChanged(bool obj)
        {
296
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
297 298 299 300 301
            {
                OnPropertyChanged(nameof(Repeat));
                OnPropertyChanged(nameof(CanGoNext));
                OnPropertyChanged(nameof(CanGoPrevious));
            });
302 303
        }

304
        private async void OnCurrentMediaChanged(IMediaItem media)
305
        {
306
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
307 308 309 310 311 312 313 314
            {
                OnPropertyChanged(nameof(CanGoNext));
                SystemMediaTransportControlsNextPossible(CanGoNext);
                OnPropertyChanged(nameof(CanGoPrevious));
                SystemMediaTransportControlsBackPossible(CanGoPrevious);
            });
        }

315
        private async void OnPlayingTypeChanged(PlayingType pType)
316
        {
317
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
318
            {
319
                OnPropertyChanged(nameof(MiniPlayerVisibility));
320
                OnPropertyChanged(nameof(MiniPlayerVisibilityMediaCenter));
321 322 323
            });
        }

324
        public Visibility MiniPlayerVisibility
325 326 327
        {
            get
            {
328
                if (Locator.MediaPlaybackViewModel.PlaybackService.PlayingType == PlayingType.Music)
329 330 331 332 333
                {
                    return Visibility.Visible;
                }
                return Visibility.Collapsed;
            }
334 335
        }

336 337 338 339
        public Visibility MiniPlayerVisibilityMediaCenter
        {
            get
            {
340 341
                // hide option on xbox and tablet mode
                if (DeviceHelper.IsMediaCenterModeCompliant || IsTabletMode)
342 343 344 345 346
                    return Visibility.Collapsed;
                return MiniPlayerVisibility;
            }
        }

Martin Finkel's avatar
Martin Finkel committed
347
        public bool SliderBindingEnabled { get; set; }
348 349
        bool IsTabletMode => UIViewSettings.GetForCurrentView().UserInteractionMode == UserInteractionMode.Touch &&
                             DeviceHelper.GetDeviceType() != DeviceTypeEnum.Phone;
Martin Finkel's avatar
Martin Finkel committed
350

351

352
        private async void PlaylistService_OnPlaylistChanged()
353
        {
354
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
355 356 357 358 359 360
            {
                OnPropertyChanged(nameof(CanGoNext));
                SystemMediaTransportControlsNextPossible(CanGoNext);
                OnPropertyChanged(nameof(CanGoPrevious));
                SystemMediaTransportControlsBackPossible(CanGoPrevious);
                OnPropertyChanged(nameof(Playlist));
361
                OnPropertyChanged(nameof(IsShuffled));
362
            });
363 364 365 366
        }

        private async void MediaPlaybackViewModel_InternetConnectionChanged(object sender, Model.Events.InternetConnectionChangedEventArgs e)
        {
367
            if (!e.IsConnected && IsPlaying && Locator.PlaybackService.CurrentPlaybackMedia is StreamMedia)
368
            {
369
                await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, async () =>
370 371 372 373 374 375
                {
                    GoBack.Execute(null);
                    var dialog = new MessageDialog(Strings.MediaCantBeRead.ToUpperFirstChar(), Strings.NoInternetConnection.ToUpperFirstChar());
                    await dialog.ShowAsync();
                });
            }
376
        }
377

Thomas Nigro's avatar
Thomas Nigro committed
378
        #endregion
379

Thomas Nigro's avatar
Thomas Nigro committed
380
        #region methods
381 382 383
        public async Task OpenFile(StorageFile file)
        {
            if (file == null) return;
384 385 386
            if (string.IsNullOrEmpty(file.Path))
            {
                // It's definitely a stream since it doesn't add a proper path but a FolderRelativeId
Thomas Nigro's avatar
Thomas Nigro committed
387
                // WARNING : Apps should use vlc://openstream/?from=url&url= for this matter
388
                var mrl = file.FolderRelativeId;
389
                var lastIndex = mrl.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase);
390
                if (lastIndex != -1)
391
                    mrl = mrl.Remove(0, lastIndex + "\\".Length);
392 393 394 395
                await PlayStream(mrl);
                return;
            }

396
            var token = StorageApplicationPermissions.FutureAccessList.Add(file);
397 398
            var fileType = VLCFileExtensions.FileTypeHelper(file.FileType);
            if (fileType == VLCFileExtensions.VLCFileType.Video)
399 400 401
            {
                await PlayVideoFile(file, token);
            }
402
            else if (fileType == VLCFileExtensions.VLCFileType.Audio)
403
            {
404
                await PlayAudioFile(file, token);
405
            }
406 407
            else if (fileType == VLCFileExtensions.VLCFileType.Subtitle)
            {
Thomas Nigro's avatar
Thomas Nigro committed
408
                if (IsPlaying && PlaybackService.PlayingType == PlayingType.Video)
409 410
                    OpenSubtitleCommand.Execute(file);
            }
411 412
        }

413 414 415 416 417
        /// <summary>
        /// Navigates to the Video Player with the request MRL as parameter
        /// </summary>
        /// <param name="mrl">The stream MRL to be played</param>
        /// <returns></returns>
418
        public async Task PlayStream(string streamMrl)
419
        {
420
            Uri uri;
421 422
            try
            {
423 424
                uri = new Uri(streamMrl);
            }
Thomas Nigro's avatar
Thomas Nigro committed
425
            catch (UriFormatException ex)
426 427 428 429 430 431 432
            {
                var md = new MessageDialog(string.Format("{0} is invalid ({1})", streamMrl, ex.Message), "Invalid URI");
                await md.ShowAsync();
                return;
            }
            try
            {
433
                var stream = Locator.MediaLibrary.LoadStreamFromDatabaseOrCreateOne(uri.ToString());
434
                LoadingMedia = Visibility.Visible;
435
                await Locator.PlaybackService.SetPlaylist(new List<IMediaItem> { stream });
436
            }
437
            catch (Exception e)
438
            {
439
                LogHelper.Log(StringsHelper.ExceptionToString(e));
440 441 442 443
                return;
            }
        }

444 445 446 447
        /// <summary>
        /// Navigates to the Audio Player screen with the requested file a parameter.
        /// </summary>
        /// <param name="file">The file to be played.</param>
448 449
        /// <param name="token">Token is for files that are NOT in the sandbox, such as files taken from the filepicker from a sd card but not in the Video/Music folder.</param>
        public async Task PlayAudioFile(StorageFile file, string token = null)
450
        {
451
            var trackItem = await Locator.MediaLibrary.GetTrackItemFromFile(file, token);
452

453
            await Locator.PlaybackService.SetPlaylist(new List<IMediaItem> { trackItem });
454 455 456 457 458 459 460 461 462
        }

        /// <summary>
        /// Navigates to the Video Player screen with the requested file a parameter.
        /// </summary>
        /// <param name="file">The file to be played.</param>
        /// <param name="token">Token is for files that are NOT in the sandbox, such as files taken from the filepicker from a sd card but not in the Video/Music folder.</param>
        public async Task PlayVideoFile(StorageFile file, string token = null)
        {
463
            var video = await MediaLibraryHelper.GetVideoItem(file);
464
            video.Id = -1;
465
            if (token != null)
466
                video.Token = token;
467

468
            await Locator.PlaybackService.SetPlaylist(new List<IMediaItem> { video });
469
        }
470

471
        public void UpdatePosition()
472 473 474
        {
            if (Locator.VideoPlayerVm.CurrentVideo != null)
            {
475
                Locator.VideoPlayerVm.CurrentVideo.TimeWatchedSeconds = (int)((double)Time / 1000);
476
                Locator.MediaLibrary.UpdateVideo(Locator.VideoPlayerVm.CurrentVideo);
477 478 479 480 481 482 483
            }
        }


        #endregion

        #region Events
484
        private async void OnPlaying()
485
        {
486
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Low, () =>
487
            {
488
                LoadingMedia = Visibility.Collapsed;
489
                OnPropertyChanged(nameof(IsPlaying));
490
            });
491 492 493
            if (_systemMediaTransportControls != null)
                _systemMediaTransportControls.PlaybackStatus = MediaPlaybackStatus.Playing;
        }
494
        private async void OnPaused()
495
        {
496
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Low, () =>
497 498 499
            {
                OnPropertyChanged(nameof(IsPlaying));
            });
500 501
            if (_systemMediaTransportControls != null)
                _systemMediaTransportControls.PlaybackStatus = MediaPlaybackStatus.Paused;
502
        }
503

504
        private async void Playback_MediaTimeChanged(long time)
505
        {
506
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
507
            {
Thomas Nigro's avatar
Thomas Nigro committed
508
                OnPropertyChanged(nameof(Time));
509 510 511
                // Assume position also changes when time does.
                // We could/should also watch OnPositionChanged event, but let's save us
                // the cost of another dispatched call.
Thomas Nigro's avatar
Thomas Nigro committed
512
                OnPropertyChanged(nameof(Position));
513 514 515
            });
        }

516
        private async void Playback_MediaLengthChanged(long length)
517
        {
518
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
519
            {
520 521
                if (length < 0)
                    return;
522 523 524 525
                TimeTotal = TimeSpan.FromMilliseconds(length);
            });
        }

526
        private async void Playback_MediaStopped()
527
        {
528
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, async () =>
529
            {
530 531
                AudioTracks.Clear();
                Subtitles.Clear();
Thomas Nigro's avatar
Thomas Nigro committed
532 533 534 535
                OnPropertyChanged(nameof(AudioTracks));
                OnPropertyChanged(nameof(Subtitles));
                OnPropertyChanged(nameof(CurrentAudioTrack));
                OnPropertyChanged(nameof(CurrentSubtitle));
536
                OnPropertyChanged(nameof(IsPlaying));
537
                await ClearMediaTransportControls();
538
            });
539 540
        }

541
        private async void Playback_MediaBuffering(float f)
Thomas Nigro's avatar
Thomas Nigro committed
542
        {
543
            _bufferingProgress = (int)f;
544
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
Thomas Nigro's avatar
Thomas Nigro committed
545
            {
546
                OnPropertyChanged(nameof(BufferingProgress));
547
                OnPropertyChanged(nameof(IsBuffered));
Thomas Nigro's avatar
Thomas Nigro committed
548 549 550
            });
        }

551
        private async void Playback_MediaFailed()
552
        {
553
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, async () =>
554
            {
555
                if (Locator.PlaybackService.CurrentPlaybackMedia is StreamMedia)
556
                {
557
                    await Locator.MediaLibrary.RemoveStreamFromCollectionAndDatabase(Locator.PlaybackService.CurrentPlaybackMedia as StreamMedia);
558
                }
559
                GoBack.Execute(null);
560
            });
561 562
        }

563
        private async void Playback_MediaEndReach()
564
        {
Thomas Nigro's avatar
Thomas Nigro committed
565
            switch (PlaybackService.PlayingType)
566 567 568 569
            {
                case PlayingType.Music:
                    break;
                case PlayingType.Video:
570
                    await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Low, () =>
571 572 573 574 575
                    {
                        if (Locator.VideoPlayerVm.CurrentVideo != null)
                            Locator.VideoPlayerVm.CurrentVideo.TimeWatchedSeconds = 0;
                    });
                    if (Locator.VideoPlayerVm.CurrentVideo != null)
576
                        Locator.MediaLibrary.UpdateVideo(Locator.VideoPlayerVm.CurrentVideo);
577 578 579 580 581 582
                    break;
                case PlayingType.NotPlaying:
                    break;
                default:
                    break;
            }
583 584 585 586
        }
        
        private async void OnPlaylistEndReached()
        {
587 588 589
            var isAppCurrentlyBackgrounded = ApplicationSettingsHelper.ReadSettingsValue("AppBackgrounded") as bool?;
            if (isAppCurrentlyBackgrounded.HasValue && isAppCurrentlyBackgrounded.Value) return;

590
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Low, () =>
591
            {
592
                if (PlaybackService.PlayingType == PlayingType.Video)
593
                {
594 595
                    App.RootPage.StopCompositionAnimationOnSwapChain();
                }
596
                Locator.NavigationService.GoBack_Default();
597
            });
598
        }
599 600


601
        private async void OnMediaTrackAdded(TrackType type, int trackId, string name)
602
        {
603 604 605 606 607
            var item = new DictionaryKeyValue
            {
                Id = trackId,
                Name = name
            };
608
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
609
            {
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
                if (type == TrackType.Audio)
                {
                    if (AudioTracks.Count == 0)
                        AudioTracks.Add(new DictionaryKeyValue { Id = -1, Name = "Disabled" });
                    AudioTracks.Add(item);
                    if (_currentAudioTrack == null)
                        _currentAudioTrack = item;
                    OnPropertyChanged(nameof(AudioTracks));
                    OnPropertyChanged(nameof(CurrentAudioTrack));
                }
                else if (type == TrackType.Subtitle)
                {
                    if (Subtitles.Count == 0)
                        Subtitles.Add(new DictionaryKeyValue { Id = -1, Name = "Disabled" });
                    Subtitles.Add(item);
                    if (_currentSubtitle == null)
                        _currentSubtitle = item;
                    OnPropertyChanged(nameof(Subtitles));
                    OnPropertyChanged(nameof(CurrentSubtitle));
                }
                else
                {
                    Locator.NavigationService.Go(VLCPage.VideoPlayerPage);
                }
            });
        }
636

637 638 639 640
        private async void OnMediaTrackDeleted(TrackType type, int trackId)
        {
            if (type == TrackType.Video)
                return;
641
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
642 643 644
            {
                ObservableCollection<DictionaryKeyValue> target;
                if (type == TrackType.Audio)
645
                {
646 647 648 649 650 651 652
                    target = AudioTracks;
                    OnPropertyChanged(nameof(CurrentAudioTrack));
                }
                else
                {
                    target = Subtitles;
                    OnPropertyChanged(nameof(CurrentSubtitle));
653
                }
654 655 656
                var res = target.FirstOrDefault((item) => item.Id == trackId);
                if (res != null)
                    target.Remove(res);
Thomas Nigro's avatar
Thomas Nigro committed
657
            });
658
        }
659

660
        private async void Playback_MediaParsed(ParsedStatus parsedStatus)
661
        {
662
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
663 664 665
            {
                OnPropertyChanged(nameof(Chapters));
                OnPropertyChanged(nameof(CurrentChapter));
666
            });
667 668
        }

Thomas Nigro's avatar
Thomas Nigro committed
669
        #endregion
670

Thomas Nigro's avatar
Thomas Nigro committed
671
        #region MediaTransportControls
672 673 674

        public void SetMediaTransportControls(SystemMediaTransportControls systemMediaTransportControls)
        {
675 676
            _systemMediaTransportControls = systemMediaTransportControls;
            if (_systemMediaTransportControls != null)
677
            {
678 679
                _systemMediaTransportControls.PlaybackStatus = MediaPlaybackStatus.Closed;
                _systemMediaTransportControls.ButtonPressed += SystemMediaTransportControlsOnButtonPressed;
680
                _systemMediaTransportControls.IsEnabled = true;
681
            }
682 683
        }

684 685 686 687 688 689 690
        private SystemMediaTransportControlsDisplayUpdater CommonTransportControlInit()
        {
            _systemMediaTransportControls.PlaybackStatus = MediaPlaybackStatus.Playing;
            _systemMediaTransportControls.IsEnabled = true;
            _systemMediaTransportControls.IsPauseEnabled = true;
            _systemMediaTransportControls.IsPlayEnabled = true;
            _systemMediaTransportControls.PlaybackRate = 1;
691
            _systemMediaTransportControls.IsStopEnabled = true;
692 693
            _systemMediaTransportControls.IsFastForwardEnabled = true;
            _systemMediaTransportControls.IsRewindEnabled = true;
694 695 696 697 698 699 700

            var updater = _systemMediaTransportControls.DisplayUpdater;
            updater.ClearAll();
            updater.AppMediaId = "VLC Media Player";
            return updater;
        }

701 702
        public async Task SetMediaTransportControlsInfo(string artistName, string albumName, string trackName, string albumUri)
        {
703
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
704
            {
705
                var updater = CommonTransportControlInit();
706 707
                updater.Type = MediaPlaybackType.Music;
                // Music metadata.
708 709 710
                updater.MusicProperties.AlbumArtist = albumName ?? string.Empty;
                updater.MusicProperties.Artist = artistName ?? string.Empty;
                updater.MusicProperties.Title = trackName ?? string.Empty;
711 712 713

                // Set the album art thumbnail.
                // RandomAccessStreamReference is defined in Windows.Storage.Streams
714 715
                
                var thumbnailUri = !string.IsNullOrEmpty(albumUri) ? new Uri(albumUri) : new Uri("ms-appx:///Assets/Icons/music.png");
716
                updater.Thumbnail = RandomAccessStreamReference.CreateFromUri(thumbnailUri);
717 718 719

                // Update the system media transport controls.
                updater.Update();
720 721 722
            });
        }

723
        public async Task SetMediaTransportControlsInfo(string title, string pictureUri = null)
724
        {
725
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
726
            {
727
                LogHelper.Log("PLAYVIDEO: Updating SystemMediaTransportControls");
728
                var updater = CommonTransportControlInit();
729
                updater.Type = MediaPlaybackType.Video;
730
                updater.VideoProperties.Title = title ?? string.Empty;
731 732 733 734
                if (!string.IsNullOrEmpty(pictureUri))
                {
                    updater.Thumbnail = RandomAccessStreamReference.CreateFromUri(new Uri(pictureUri));
                }
735
                updater.Update();
736 737 738
            });
        }

739 740
        public async Task ClearMediaTransportControls()
        {
741
            await DispatchHelper.InvokeInUIThread(CoreDispatcherPriority.Normal, () =>
742
            {
743 744 745 746
                if (_systemMediaTransportControls == null) return;
                LogHelper.Log("PLAYVIDEO: Updating SystemMediaTransportControls");
                SystemMediaTransportControlsDisplayUpdater updater = _systemMediaTransportControls.DisplayUpdater;
                updater.ClearAll();
747 748 749
            });
        }

750
        private void SystemMediaTransportControlsOnButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
751 752 753 754
        {
            switch (args.Button)
            {
                case SystemMediaTransportControlsButton.Pause:
755 756
                    PlaybackService.Pause();
                    break;
757
                case SystemMediaTransportControlsButton.Play:
Thomas Nigro's avatar
Thomas Nigro committed
758
                    PlaybackService.Play();
759 760
                    break;
                case SystemMediaTransportControlsButton.Stop:
Thomas Nigro's avatar
Thomas Nigro committed
761
                    PlaybackService.Stop();
762 763
                    break;
                case SystemMediaTransportControlsButton.Previous:
764
                    Locator.PlaybackService.Previous();
765 766
                    break;
                case SystemMediaTransportControlsButton.Next:
767
                    Locator.PlaybackService.Next();
768
                    break;
769
                case SystemMediaTransportControlsButton.FastForward:
770
                    FastSeekCommand.Execute(30000);
771 772
                    break;
                case SystemMediaTransportControlsButton.Rewind:
773
                    FastSeekCommand.Execute(-30000);
774
                    break;
775 776 777
            }
        }

778 779
        public void SystemMediaTransportControlsBackPossible(bool backPossible)
        {
780 781
            if (_systemMediaTransportControls != null)
                _systemMediaTransportControls.IsPreviousEnabled = backPossible;
782 783 784 785
        }

        public void SystemMediaTransportControlsNextPossible(bool nextPossible)
        {
786 787
            if (_systemMediaTransportControls != null)
                _systemMediaTransportControls.IsNextEnabled = nextPossible;
788
        }
Thomas Nigro's avatar
Thomas Nigro committed
789
        #endregion
790 791
    }
}