[MediaController] Add APIs to create playlist (#484)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Remoting / MediaController / MediaController.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Linq;
19 using System.Collections.Generic;
20 using System.Diagnostics;
21 using System.Threading.Tasks;
22 using Tizen.Applications;
23 using Native = Interop.MediaControllerClient;
24 using NativePlaylist = Interop.MediaControllerPlaylist;
25
26 namespace Tizen.Multimedia.Remoting
27 {
28     /// <summary>
29     /// Provides a means to send commands to and handle events from media control server.
30     /// </summary>
31     /// <since_tizen> 4 </since_tizen>
32     public partial class MediaController
33     {
34         internal MediaController(MediaControllerManager manager, string serverAppId)
35         {
36             Debug.Assert(manager != null);
37             Debug.Assert(serverAppId != null);
38
39             Manager = manager;
40             ServerAppId = serverAppId;
41         }
42
43         private MediaControllerManager Manager { get; }
44
45         /// <summary>
46         /// Gets the application id of the server.
47         /// </summary>
48         /// <value>The server application id.</value>
49         /// <since_tizen> 4 </since_tizen>
50         public string ServerAppId { get; }
51
52         /// <summary>
53         /// Gets a value indicating whether the sever has been stopped.
54         /// </summary>
55         /// <value>true if the server has been stopped; otherwise, false.</value>
56         /// <since_tizen> 4 </since_tizen>
57         public bool IsStopped
58         {
59             get;
60             private set;
61         }
62
63         private void ThrowIfStopped()
64         {
65             if (IsStopped)
66             {
67                 throw new InvalidOperationException("The server has already been stopped.");
68             }
69         }
70
71         /// <summary>
72         /// Returns the playback state set by the server.
73         /// </summary>
74         /// <returns>The playback state.</returns>
75         /// <exception cref="InvalidOperationException">
76         ///     The server has already been stopped.<br/>
77         ///     -or-<br/>
78         ///     An internal error occurs.
79         /// </exception>
80         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
81         /// <seealso cref="MediaControlServer.SetPlaybackState(MediaControlPlaybackState, long)"/>
82         /// <since_tizen> 4 </since_tizen>
83         public MediaControlPlaybackState GetPlaybackState()
84         {
85             ThrowIfStopped();
86
87             IntPtr playbackHandle = IntPtr.Zero;
88
89             try
90             {
91                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
92
93                 Native.GetPlaybackState(playbackHandle, out var playbackCode).ThrowIfError("Failed to get state.");
94
95                 return playbackCode.ToPublic();
96             }
97             finally
98             {
99                 if (playbackHandle != IntPtr.Zero)
100                 {
101                     Native.DestroyPlayback(playbackHandle);
102                 }
103             }
104         }
105
106         /// <summary>
107         /// Returns the playback position set by the server.
108         /// </summary>
109         /// <returns>The playback position in milliseconds.</returns>
110         /// <exception cref="InvalidOperationException">
111         ///     The server has already been stopped.<br/>
112         ///     -or-<br/>
113         ///     An internal error occurs.
114         /// </exception>
115         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
116         /// <seealso cref="MediaControlServer.SetPlaybackState(MediaControlPlaybackState, long)"/>
117         /// <since_tizen> 4 </since_tizen>
118         public long GetPlaybackPosition()
119         {
120             ThrowIfStopped();
121
122             IntPtr playbackHandle = IntPtr.Zero;
123
124             try
125             {
126                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
127
128                 Native.GetPlaybackPosition(playbackHandle, out var position).ThrowIfError("Failed to get position.");
129
130                 return (long)position;
131             }
132             finally
133             {
134                 if (playbackHandle != IntPtr.Zero)
135                 {
136                     Native.DestroyPlayback(playbackHandle);
137                 }
138             }
139         }
140
141         /// <summary>
142         /// Returns the metadata set by the server.
143         /// </summary>
144         /// <returns>The metadata.</returns>
145         /// <exception cref="InvalidOperationException">
146         ///     The server has already been stopped.<br/>
147         ///     -or-<br/>
148         ///     An internal error occurs.
149         /// </exception>
150         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
151         /// <seealso cref="MediaControlServer.SetMetadata(MediaControlMetadata)"/>
152         /// <since_tizen> 4 </since_tizen>
153         public MediaControlMetadata GetMetadata()
154         {
155             ThrowIfStopped();
156
157             IntPtr metadataHandle = IntPtr.Zero;
158
159             try
160             {
161                 NativePlaylist.GetServerMetadata(Manager.Handle, ServerAppId, out metadataHandle).
162                     ThrowIfError("Failed to get metadata.");
163
164                 return new MediaControlMetadata(metadataHandle);
165             }
166             finally
167             {
168                 if (metadataHandle != IntPtr.Zero)
169                 {
170                     NativePlaylist.DestroyMetadata(metadataHandle);
171                 }
172             }
173         }
174
175         /// <summary>
176         /// Returns the all playlists.
177         /// </summary>
178         /// <returns>The set of <see cref="MediaControlPlaylist"/>.</returns>
179         /// <exception cref="InvalidOperationException">
180         ///     The server has already been stopped.<br/>
181         ///     -or-<br/>
182         ///     An internal error occurs.
183         /// </exception>
184         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
185         /// <since_tizen> 5 </since_tizen>
186         public IEnumerable<MediaControlPlaylist> GetPlaylists()
187         {
188             ThrowIfStopped();
189
190             var playlists = new List<MediaControlPlaylist>();
191
192             NativePlaylist.PlaylistCallback playlistCallback = (handle, _) =>
193             {
194                 playlists.Add(new MediaControlPlaylist(handle));
195             };
196             NativePlaylist.ForeachServerPlaylist(Manager.Handle, ServerAppId, playlistCallback, IntPtr.Zero)
197                 .ThrowIfError("Failed to get playlist.");
198
199             return playlists.AsReadOnly();
200         }
201
202         /// <summary>
203         /// Returns the playlist name of current playing media.
204         /// </summary>
205         /// <returns>The playlist name.</returns>
206         /// <exception cref="InvalidOperationException">
207         ///     The server has already been stopped.<br/>
208         ///     -or-<br/>
209         ///     An internal error occurs.
210         /// </exception>
211         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
212         /// <since_tizen> 5 </since_tizen>
213         public MediaControlPlaylist GetPlaylistOfCurrentPlayingMedia()
214         {
215             ThrowIfStopped();
216
217             IntPtr playbackHandle = IntPtr.Zero;
218
219             // Get the playlist name of current playing media.
220             try
221             {
222                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
223
224                 var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
225
226                 return GetPlaylists().FirstOrDefault(playlist => playlist.Name == name);
227             }
228             finally
229             {
230                 if (playbackHandle != IntPtr.Zero)
231                 {
232                     Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
233                 }
234             }
235         }
236
237         /// <summary>
238         /// Returns the index of current playing media.
239         /// </summary>
240         /// <returns>The index of current playing media.</returns>
241         /// <exception cref="InvalidOperationException">
242         ///     The server has already been stopped.<br/>
243         ///     -or-<br/>
244         ///     An internal error occurs.
245         /// </exception>
246         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
247         /// <since_tizen> 5 </since_tizen>
248         public string GetIndexOfCurrentPlayingMedia()
249         {
250             ThrowIfStopped();
251
252             IntPtr playbackHandle = IntPtr.Zero;
253
254             try
255             {
256                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
257
258                 var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
259                 return index;
260             }
261             finally
262             {
263                 if (playbackHandle != IntPtr.Zero)
264                 {
265                     Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
266                 }
267             }
268         }
269
270         /// <summary>
271         /// Returns whether the shuffle mode is enabled.
272         /// </summary>
273         /// <returns>A value indicating whether the shuffle mode is enabled.</returns>
274         /// <exception cref="InvalidOperationException">
275         ///     The server has already been stopped.<br/>
276         ///     -or-<br/>
277         ///     An internal error occurs.
278         /// </exception>
279         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
280         /// <seealso cref="MediaControlServer.SetShuffleModeEnabled(bool)"/>
281         /// <since_tizen> 4 </since_tizen>
282         public bool IsShuffleModeEnabled()
283         {
284             ThrowIfStopped();
285
286             Native.GetServerShuffleMode(Manager.Handle, ServerAppId, out var shuffleMode).
287                 ThrowIfError("Failed to get shuffle mode state.");
288
289             return shuffleMode == MediaControllerNativeShuffleMode.On;
290         }
291
292         /// <summary>
293         /// Returns the repeat mode.
294         /// </summary>
295         /// <returns>A <see cref="MediaControlRepeatMode"/> set by the server.</returns>
296         /// <exception cref="InvalidOperationException">
297         ///     The server has already been stopped.<br/>
298         ///     -or-<br/>
299         ///     An internal error occurs.
300         /// </exception>
301         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
302         /// <seealso cref="MediaControlServer.SetRepeatMode(MediaControlRepeatMode)"/>
303         /// <since_tizen> 4 </since_tizen>
304         public MediaControlRepeatMode GetRepeatMode()
305         {
306             ThrowIfStopped();
307
308             Native.GetServerRepeatMode(Manager.Handle, ServerAppId, out var repeatMode).
309                 ThrowIfError("Failed to get repeat mode state.");
310
311             return repeatMode.ToPublic();
312         }
313
314         /// <summary>
315         /// Requests command to the server.
316         /// </summary>
317         /// <remarks>
318         /// The client can request the server to execute <see cref="PlaybackCommand"/> or <see cref="ShuffleModeCommand"/> or
319         /// <see cref="RepeatModeCommand"/> or <see cref="CustomCommand"/>, <br/>
320         /// and then, the client receive the result of each request(command).
321         /// </remarks>
322         /// <param name="command">A <see cref="Command"/> class.</param>
323         /// <returns>A task that represents the asynchronous operation.</returns>
324         /// <exception cref="InvalidOperationException">
325         ///     The server has already been stopped.<br/>
326         ///     -or-<br/>
327         ///     An internal error occurs.
328         /// </exception>
329         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
330         /// <since_tizen> 5 </since_tizen>
331         public async Task RequestAsync(Command command)
332         {
333             ThrowIfStopped();
334
335             command.SetRequestInformation(ServerAppId);
336
337             var tcs = new TaskCompletionSource<MediaControllerError>();
338             string reqeustId = null;
339
340             EventHandler<CommandCompletedEventArgs> eventHandler = (s, e) =>
341             {
342                 if (e.RequestId == reqeustId)
343                 {
344                     tcs.TrySetResult(e.Result);
345                 }
346             };
347
348             try
349             {
350                 CommandCompleted += eventHandler;
351
352                 reqeustId = command.Request(Manager.Handle);
353
354                 (await tcs.Task).ThrowIfError("Failed to request command");
355             }
356             finally
357             {
358                 CommandCompleted -= eventHandler;
359             }
360         }
361
362         /// <summary>
363         /// Sends the result of each command.
364         /// </summary>
365         /// <param name="command">The command that return to client.</param>
366         /// <param name="result">The result of <paramref name="command"/>.</param>
367         /// <param name="bundle">The extra data.</param>
368         /// <exception cref="InvalidOperationException">
369         ///     The server is not running .<br/>
370         ///     -or-<br/>
371         ///     An internal error occurs.
372         /// </exception>
373         /// <since_tizen> 5 </since_tizen>
374         public void Response(Command command, int result, Bundle bundle)
375         {
376             command.Response(Manager.Handle, result, bundle);
377         }
378
379         /// <summary>
380         /// Sends the result of each command.
381         /// </summary>
382         /// <param name="command">The command that return to client.</param>
383         /// <param name="result">The result of <paramref name="command"/>.</param>
384         /// <exception cref="InvalidOperationException">
385         ///     The server is not running .<br/>
386         ///     -or-<br/>
387         ///     An internal error occurs.
388         /// </exception>
389         /// <since_tizen> 5 </since_tizen>
390         public void Response(Command command, int result)
391         {
392             command.Response(Manager.Handle, result, null);
393         }
394
395         /// <summary>
396         /// Sends playback command to the server.
397         /// </summary>
398         /// <param name="command">A playback command.</param>
399         /// <exception cref="InvalidOperationException">
400         ///     The server has already been stopped.<br/>
401         ///     -or-<br/>
402         ///     An internal error occurs.
403         /// </exception>
404         /// <exception cref="ArgumentException"><paramref name="command"/> is not valid.</exception>
405         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
406         /// <seealso cref="MediaControlServer.PlaybackCommandReceived"/>
407         /// <since_tizen> 4 </since_tizen>
408         [Obsolete("Please do not use! This will be deprecated. Please use Request instead.")]
409         public void SendPlaybackCommand(MediaControlPlaybackCommand command)
410         {
411             ThrowIfStopped();
412
413             ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), command, nameof(command));
414
415             Native.SendPlaybackStateCommand(Manager.Handle, ServerAppId, command.ToNative()).
416                 ThrowIfError("Failed to send command.");
417         }
418
419         #region Capabilities
420         /// <summary>
421         /// Gets the content type of current playing media.
422         /// </summary>
423         /// <returns>The <see cref="MediaControlContentType"/>.</returns>
424         /// <exception cref="InvalidOperationException">
425         ///     The server has already been stopped.<br/>
426         ///     -or-<br/>
427         ///     An internal error occurs.
428         /// </exception>
429         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
430         /// <since_tizen> 5 </since_tizen>
431         public MediaControlContentType GetContentTypeOfCurrentPlayingMedia()
432         {
433             ThrowIfStopped();
434
435             IntPtr playbackHandle = IntPtr.Zero;
436
437             try
438             {
439                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
440
441                 Native.GetPlaybackContentType(playbackHandle, out MediaControlContentType type).
442                     ThrowIfError("Failed to get playback content type");
443
444                 return type;
445             }
446             finally
447             {
448                 if (playbackHandle != IntPtr.Zero)
449                 {
450                     Native.DestroyPlayback(playbackHandle);
451                 }
452             }
453         }
454
455         /// <summary>
456         /// Gets the icon path.
457         /// </summary>
458         /// <returns>The icon path.</returns>
459         /// <exception cref="InvalidOperationException">
460         ///     The server has already been stopped.<br/>
461         ///     -or-<br/>
462         ///     An internal error occurs.
463         /// </exception>
464         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
465         /// <since_tizen> 5 </since_tizen>
466         public string GetIconPath()
467         {
468             ThrowIfStopped();
469
470             Native.GetServerIcon(Manager.Handle, ServerAppId, out string uri).
471                 ThrowIfError("Failed to get icon path.");
472
473             return uri;
474         }
475
476         /// <summary>
477         /// Gets the age rating of current playing media.
478         /// </summary>
479         /// <returns>The Age rating of current playing media. The range is 0 to 19, inclusive.</returns>
480         /// <exception cref="InvalidOperationException">
481         ///     The server has already been stopped.<br/>
482         ///     -or-<br/>
483         ///     An internal error occurs.
484         /// </exception>
485         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
486         /// <since_tizen> 5 </since_tizen>
487         public int GetAgeRatingOfCurrentPlayingMedia()
488         {
489             ThrowIfStopped();
490
491             IntPtr playbackHandle = IntPtr.Zero;
492
493             try
494             {
495                 Native.GetServerPlayback(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
496
497                 Native.GetAgeRating(playbackHandle, out int ageRating).ThrowIfError("Failed to get age rating.");
498
499                 return ageRating;
500             }
501             finally
502             {
503                 if (playbackHandle != IntPtr.Zero)
504                 {
505                     Native.DestroyPlayback(playbackHandle);
506                 }
507             }
508         }
509
510         /// <summary>
511         /// Gets the value whether <see cref="MediaControlPlaybackCommand"/> is supported or not.
512         /// </summary>
513         /// <returns>
514         /// the set of <see cref="MediaControlPlaybackCommand"/> and <see cref="MediaControlCapabilitySupport"/>.
515         /// </returns>
516         /// <exception cref="InvalidOperationException">
517         ///     The server has already been stopped.<br/>
518         ///     -or-<br/>
519         ///     An internal error occurs.
520         /// </exception>
521         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
522         /// <since_tizen> 5 </since_tizen>
523         public Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport> GetPlaybackCapabilities()
524         {
525             ThrowIfStopped();
526
527             IntPtr playbackCapaHandle = IntPtr.Zero;
528
529             var playbackCapabilities = new Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport>();
530
531             try
532             {
533                 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
534                     ThrowIfError("Failed to get playback capability handle.");
535
536                 foreach (MediaControllerNativePlaybackAction action in Enum.GetValues(typeof(MediaControllerNativePlaybackAction)))
537                 {
538                     Native.IsCapabilitySupported(playbackCapaHandle, action, out MediaControlCapabilitySupport support);
539                     playbackCapabilities.Add(action.ToPublic(), support);
540                 }
541
542                 return playbackCapabilities;
543             }
544             finally
545             {
546                 if (playbackCapaHandle != IntPtr.Zero)
547                 {
548                     Native.DestroyCapability(playbackCapaHandle);
549                 }
550             }
551         }
552
553         /// <summary>
554         /// Gets the value whether <paramref name="action"/> is supported or not.
555         /// </summary>
556         /// <param name="action">A playback command.</param>
557         /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
558         /// <exception cref="InvalidOperationException">
559         ///     The server has already been stopped.<br/>
560         ///     -or-<br/>
561         ///     An internal error occurs.
562         /// </exception>
563         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
564         /// <since_tizen> 5 </since_tizen>
565         public MediaControlCapabilitySupport GetPlaybackCapability(MediaControlPlaybackCommand action)
566         {
567             ThrowIfStopped();
568
569             IntPtr playbackCapaHandle = IntPtr.Zero;
570
571             try
572             {
573                 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
574                     ThrowIfError("Failed to get playback capability handle.");
575
576                 Native.IsCapabilitySupported(playbackCapaHandle, action.ToNative(), out MediaControlCapabilitySupport support);
577
578                 return support;
579             }
580             finally
581             {
582                 if (playbackCapaHandle != IntPtr.Zero)
583                 {
584                     Native.DestroyCapability(playbackCapaHandle);
585                 }
586             }
587         }
588
589         /// <summary>
590         /// Gets the value whether the shuffle mode is supported or not.
591         /// </summary>
592         /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
593         /// <exception cref="InvalidOperationException">
594         ///     The server has already been stopped.<br/>
595         ///     -or-<br/>
596         ///     An internal error occurs.
597         /// </exception>
598         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
599         /// <since_tizen> 5 </since_tizen>
600         public MediaControlCapabilitySupport GetShuffleModeCapability()
601         {
602             ThrowIfStopped();
603
604             IntPtr playbackCapaHandle = IntPtr.Zero;
605
606             try
607             {
608                 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
609                     ThrowIfError("Failed to get playback capability handle.");
610
611                 Native.GetShuffleCapability(Manager.Handle, ServerAppId, out MediaControlCapabilitySupport support);
612
613                 return support;
614             }
615             finally
616             {
617                 if (playbackCapaHandle != IntPtr.Zero)
618                 {
619                     Native.DestroyCapability(playbackCapaHandle);
620                 }
621             }
622         }
623
624         /// <summary>
625         /// Gets the value whether the repeat mode is supported or not.
626         /// </summary>
627         /// <returns>A <see cref="MediaControlCapabilitySupport"/>.</returns>
628         /// <exception cref="InvalidOperationException">
629         ///     The server has already been stopped.<br/>
630         ///     -or-<br/>
631         ///     An internal error occurs.
632         /// </exception>
633         /// <exception cref="ObjectDisposedException">The <see cref="MediaControllerManager"/> has already been disposed of.</exception>
634         /// <since_tizen> 5 </since_tizen>
635         public MediaControlCapabilitySupport GetRepeatModeCapability()
636         {
637             ThrowIfStopped();
638
639             IntPtr playbackCapaHandle = IntPtr.Zero;
640
641             try
642             {
643                 Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
644                     ThrowIfError("Failed to get playback capability handle.");
645
646                 Native.GetRepeatCapability(Manager.Handle, ServerAppId, out MediaControlCapabilitySupport support);
647
648                 return support;
649             }
650             finally
651             {
652                 if (playbackCapaHandle != IntPtr.Zero)
653                 {
654                     Native.DestroyCapability(playbackCapaHandle);
655                 }
656             }
657         }
658         #endregion Capabilities
659     }
660 }