9f7d88d393259e9b3c33630f7524aa7e34579592
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Remoting / MediaController / MediaControlCommand.cs
1 /*
2  * Copyright (c) 2018 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 Tizen.Applications;
18 using System;
19 using System.Collections.Generic;
20 using NativeClient = Interop.MediaControllerClient;
21 using NativeServer = Interop.MediaControllerServer;
22 using NativeClientHandle = Interop.MediaControllerClientHandle;
23
24 namespace Tizen.Multimedia.Remoting
25 {
26     /// <summary>
27     /// Provides a means to send command to media control server.
28     /// </summary>
29     /// <since_tizen> 5 </since_tizen>
30     public abstract class Command
31     {
32         private string _requestId;
33
34         /// <summary>
35         /// The id for command receiver.
36         /// </summary>
37         /// <since_tizen> 5 </since_tizen>
38         protected string ReceiverId { get; private set; }
39
40         /// <summary>
41         /// Initializes a <see cref="Command"/> base class.
42         /// </summary>
43         /// <since_tizen> 5 </since_tizen>
44         protected Command() { }
45
46         /// <summary>
47         /// Sets the server information.
48         /// </summary>
49         /// <param name="receiverId">The receiver Id that receives command.</param>
50         internal void SetRequestInformation(string receiverId)
51         {
52             ReceiverId = receiverId;
53         }
54
55         /// <summary>
56         /// Sets the client information.
57         /// </summary>
58         /// <param name="receiverId">The receiver Id that receives response for command.</param>
59         /// <param name="requestId">The request Id for each command.</param>
60         internal void SetResponseInformation(string receiverId, string requestId)
61         {
62             ReceiverId = receiverId;
63             _requestId = requestId;
64         }
65
66         /// <summary>
67         /// Requests command to server.
68         /// </summary>
69         /// <returns>The request id for each command.</returns>
70         internal abstract string Request(NativeClientHandle clientHandle);
71
72         /// <summary>
73         /// Requests command to client.
74         /// </summary>
75         /// <param name="serverHandle"></param>
76         /// <returns>The request id for each command.</returns>
77         internal virtual string Request(IntPtr serverHandle) => throw new NotImplementedException();
78
79         /// <summary>
80         /// Represents a method that is called when an response command completes.
81         /// </summary>
82         /// <since_tizen> 5 </since_tizen>
83         protected virtual void OnResponseCompleted() { }
84
85         /// <summary>
86         /// Responses command to the client.
87         /// </summary>
88         /// <param name="serverHandle">The server handle.</param>
89         /// <param name="result">The result of each command.</param>
90         /// <param name="bundle">The extra data.</param>
91         internal void Response(IntPtr serverHandle, int result, Bundle bundle)
92         {
93             try
94             {
95                 if (bundle != null)
96                 {
97                     NativeServer.SendCommandReplyBundle(serverHandle, ReceiverId, _requestId, result, bundle.SafeBundleHandle)
98                         .ThrowIfError("Failed to response command.");
99                 }
100                 else
101                 {
102                     NativeServer.SendCommandReply(serverHandle, ReceiverId, _requestId, result, IntPtr.Zero)
103                         .ThrowIfError("Failed to response command.");
104                 }
105             }
106             finally
107             {
108                 OnResponseCompleted();
109             }
110         }
111
112         /// <summary>
113         /// Responses command to the server.
114         /// </summary>
115         /// <param name="clientHandle">The client handle.</param>
116         /// <param name="result">The result of each command.</param>
117         /// <param name="bundle">The extra data.</param>
118         internal void Response(NativeClientHandle clientHandle, int result, Bundle bundle)
119         {
120             try
121             {
122                 if (bundle != null)
123                 {
124                     NativeClient.SendCustomEventReplyBundle(clientHandle, ReceiverId, _requestId, result, bundle.SafeBundleHandle)
125                         .ThrowIfError("Failed to response event.");
126                 }
127                 else
128                 {
129                     NativeClient.SendCustomEventReply(clientHandle, ReceiverId, _requestId, result, IntPtr.Zero)
130                         .ThrowIfError("Failed to repose event.");
131                 }
132             }
133             finally
134             {
135                 OnResponseCompleted();
136             }
137         }
138     }
139
140
141     /// <summary>
142     /// Provides a means to send playback command to media control server.
143     /// </summary>
144     public sealed class PlaybackCommand : Command
145     {
146         /// <summary>
147         /// Initializes a new instance of the <see cref="PlaybackCommand"/> class.
148         /// </summary>
149         /// <param name="action">A <see cref="MediaControlPlaybackCommand"/>.</param>
150         /// <since_tizen> 5 </since_tizen>
151         public PlaybackCommand(MediaControlPlaybackCommand action)
152         {
153             Action = action;
154         }
155
156         /// <summary>
157         /// Gets the playback action.
158         /// </summary>
159         /// <since_tizen> 5 </since_tizen>
160         public MediaControlPlaybackCommand Action { get; }
161
162         internal override string Request(NativeClientHandle clientHandle)
163         {
164             ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), Action, nameof(MediaControlPlaybackCommand));
165
166             NativeClient.SendPlaybackActionCommand(clientHandle, ReceiverId, Action.ToNative(), out string requestId)
167                 .ThrowIfError("Failed to send playback command.");
168
169             return requestId;
170         }
171     }
172
173     /// <summary>
174     /// Provides a means to send playback command to order specific time position.
175     /// </summary>
176     public sealed class PlaybackPositionCommand : Command
177     {
178         /// <summary>
179         /// Initializes a new instance of the <see cref="PlaybackPositionCommand"/> class.
180         /// </summary>
181         /// <param name="position">The playback position in milliseconds.</param>
182         /// <since_tizen> 5 </since_tizen>
183         public PlaybackPositionCommand(ulong position)
184         {
185             Position = position;
186         }
187
188         /// <summary>
189         /// Gets the position to play.
190         /// </summary>
191         /// <since_tizen> 5 </since_tizen>
192         public ulong Position { get; }
193
194         internal override string Request(NativeClientHandle clientHandle)
195         {
196             NativeClient.SendPlaybackPositionCommand(clientHandle, ReceiverId, Position, out string requestId)
197                 .ThrowIfError("Failed to send playback position command.");
198
199             return requestId;
200         }
201     }
202
203     /// <summary>
204     /// Provides a means to send playback command with playlist information.
205     /// </summary>
206     public sealed class PlaylistCommand : Command
207     {
208         /// <summary>
209         /// Initializes a new instance of the <see cref="PlaybackCommand"/> class.
210         /// </summary>
211         /// <param name="action">A <see cref="MediaControlPlaybackCommand"/>.</param>
212         /// <param name="playlistName">The playlist name of the server.</param>
213         /// <param name="index">The index of the media in the playlist.</param>
214         /// <param name="position">The playback position in milliseconds.</param>
215         /// <exception cref="ArgumentException"><paramref name="index"/> cannot be converted to number.</exception>
216         /// <exception cref="ArgumentNullException">
217         /// <paramref name="playlistName"/> or <paramref name="index"/> is not vailed.
218         /// </exception>
219         /// <since_tizen> 5 </since_tizen>
220         public PlaylistCommand(MediaControlPlaybackCommand action, string playlistName, string index, ulong position)
221         {
222             Action = action;
223             Index = index ?? throw new ArgumentNullException(nameof(index));
224             Name = playlistName ?? throw new ArgumentNullException(nameof(playlistName));
225             Position = position;
226         }
227
228         /// <summary>
229         /// Initializes a new instance of the <see cref="PlaybackCommand"/> class.
230         /// </summary>
231         /// <param name="action">A <see cref="MediaControlPlaybackCommand"/>.</param>
232         /// <param name="playlistName">The playlist name of the server.</param>
233         /// <param name="index">The index of the media in the playlist.</param>
234         /// <exception cref="ArgumentException"><paramref name="index"/> cannot be converted to number.</exception>
235         /// <exception cref="ArgumentNullException">
236         /// <paramref name="playlistName"/> or <paramref name="index"/> is null.
237         /// </exception>
238         /// <since_tizen> 5 </since_tizen>
239         public PlaylistCommand(MediaControlPlaybackCommand action, string playlistName, string index)
240             : this(action, playlistName, index, 0)
241         {
242         }
243
244         /// <summary>
245         /// Gets the playback action.
246         /// </summary>
247         /// <since_tizen> 5 </since_tizen>
248         public MediaControlPlaybackCommand Action { get; }
249
250         /// <summary>
251         /// Gets the position to play.
252         /// </summary>
253         /// <since_tizen> 5 </since_tizen>
254         public ulong Position { get; }
255
256         /// <summary>
257         /// Gets the index of playlist.
258         /// </summary>
259         /// <since_tizen> 5 </since_tizen>
260         public string Index { get; }
261
262         /// <summary>
263         /// Gets the name of playlist.
264         /// </summary>
265         /// <since_tizen> 5 </since_tizen>
266         public string Name { get; }
267
268         internal override string Request(NativeClientHandle clientHandle)
269         {
270             ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), Action, nameof(MediaControlPlaybackCommand));
271
272             NativeClient.SendPlaylistCommand(clientHandle, ReceiverId, Name, Index, Action.ToNative(),
273                 Position, out string requestId).ThrowIfError("Failed to send playlist command.");
274
275             return requestId;
276         }
277     }
278
279     /// <summary>
280     /// Provides a means to to send shuffle mode commands.
281     /// </summary>
282     /// <since_tizen> 5 </since_tizen>
283     public sealed class ShuffleModeCommand : Command
284     {
285         /// <summary>
286         /// Initializes a new instance of the <see cref="ShuffleModeCommand"/> class.
287         /// </summary>
288         /// <param name="enabled">A shuffle mode.</param>
289         /// <since_tizen> 5 </since_tizen>
290         public ShuffleModeCommand(bool enabled)
291         {
292             Enabled = enabled;
293         }
294
295         /// <summary>
296         /// Gets a value indicating whether the shuffle mode is enabled.
297         /// </summary>
298         public bool Enabled { get; }
299
300         internal override string Request(NativeClientHandle clientHandle)
301         {
302             var mode = Enabled ? MediaControllerNativeShuffleMode.On : MediaControllerNativeShuffleMode.Off;
303
304             NativeClient.SendShuffleModeCommand(clientHandle, ReceiverId, mode, out string requestId).
305                 ThrowIfError("Failed to send playback shuffle command.");
306
307             return requestId;
308         }
309     }
310
311     /// <summary>
312     /// Provides a means to to send repeat mode commands.
313     /// </summary>
314     /// <since_tizen> 5 </since_tizen>
315     public sealed class RepeatModeCommand : Command
316     {
317         /// <summary>
318         /// Initializes a new instance of the <see cref="RepeatModeCommand"/> class.
319         /// </summary>
320         /// <param name="mode">The <see cref="MediaControlRepeatMode"/>.</param>
321         /// <since_tizen> 5 </since_tizen>
322         public RepeatModeCommand(MediaControlRepeatMode mode)
323         {
324             Mode = mode;
325         }
326
327         /// <summary>
328         /// Gets the repeat mode.
329         /// </summary>
330         /// <since_tizen> 5 </since_tizen>
331         public MediaControlRepeatMode Mode { get; }
332
333         internal override string Request(NativeClientHandle clientHandle)
334         {
335             ValidationUtil.ValidateEnum(typeof(MediaControlRepeatMode), Mode, nameof(MediaControlRepeatMode));
336
337             NativeClient.SendRepeatModeCommand(clientHandle, ReceiverId, Mode.ToNative(), out string requestId).
338                 ThrowIfError("Failed to send playback repeat command.");
339
340             return requestId;
341         }
342     }
343
344     /// <summary>
345     /// Provides a means to to send custom commands.
346     /// </summary>
347     /// <remarks>This command can be used by both client and server to send predefined command or data.</remarks>
348     /// <since_tizen> 5 </since_tizen>
349     public sealed class CustomCommand : Command
350     {
351         /// <summary>
352         /// Initializes a new instance of the <see cref="CustomCommand"/> class.
353         /// </summary>
354         /// <param name="action">A predefined custom command.</param>
355         /// <since_tizen> 5 </since_tizen>
356         public CustomCommand(string action)
357         {
358             Action = action ?? throw new ArgumentNullException(nameof(action));
359         }
360
361         /// <summary>
362         /// Initializes a new instance of the <see cref="CustomCommand"/> class.
363         /// </summary>
364         /// <param name="action">A predefined custom command.</param>
365         /// <param name="bundle">The extra data for custom command.</param>
366         /// <since_tizen> 5 </since_tizen>
367         public CustomCommand(string action, Bundle bundle)
368             : this(action)
369         {
370             Bundle = bundle;
371         }
372
373         ///<summary>
374         /// Gets the custom action.
375         /// </summary>
376         /// <since_tizen> 5 </since_tizen>
377         public string Action { get; }
378
379         /// <summary>
380         /// Gets the extra data.
381         /// </summary>
382         /// <since_tizen> 5 </since_tizen>
383         public Bundle Bundle { get; }
384
385         internal override string Request(NativeClientHandle clientHandle)
386         {
387             string requestId = null;
388
389             if (Bundle != null)
390             {
391                 NativeClient.SendCustomCommandBundle(clientHandle, ReceiverId, Action, Bundle.SafeBundleHandle, out requestId).
392                     ThrowIfError("Failed to send custom command.");
393             }
394             else
395             {
396                 NativeClient.SendCustomCommand(clientHandle, ReceiverId, Action, IntPtr.Zero, out requestId).
397                     ThrowIfError("Failed to send custom command.");
398             }
399
400             return requestId;
401         }
402
403         internal override string Request(IntPtr serverHandle)
404         {
405             string requestId = null;
406
407             if (Bundle != null)
408             {
409                 NativeServer.SendCustomEventBundle(serverHandle, ReceiverId, Action, Bundle.SafeBundleHandle, out requestId)
410                     .ThrowIfError("Failed to send costom event.");
411             }
412             else
413             {
414                 NativeServer.SendCustomEvent(serverHandle, ReceiverId, Action, IntPtr.Zero, out requestId)
415                     .ThrowIfError("Failed to send costom event.");
416             }
417
418             return requestId;
419         }
420     }
421
422     /// <summary>
423     /// Provides a means to to send search commands.
424     /// </summary>
425     /// <since_tizen> 5 </since_tizen>
426     public sealed class SearchCommand : Command
427     {
428         private readonly IntPtr _searchHandle;
429
430         /// <summary>
431         /// Initializes a new instance of the <see cref="SearchCommand"/> class.
432         /// </summary>
433         /// <remarks>User can search maximum 20 items once.</remarks>
434         /// <exception cref="ArgumentNullException"><paramref name="conditions"/> is not set.</exception>
435         /// <exception cref="ArgumentException">
436         ///     <paramref name="conditions.Count"/> is greater than maximum value(20).<br/>
437         ///     -or-<br/>
438         ///     <paramref name="conditions.Count"/> is less than 1.
439         /// </exception>
440         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
441         /// <param name="conditions">The set of <see cref="MediaControlSearchCondition"/>.</param>
442         /// <since_tizen> 5 </since_tizen>
443         public SearchCommand(List<MediaControlSearchCondition> conditions)
444         {
445             if (conditions == null)
446             {
447                 throw new ArgumentNullException(nameof(conditions));
448             }
449             if (conditions.Count <= 0 || conditions.Count > 20)
450             {
451                 var errMessage = $"Invalid number of search conditions. : {conditions.Count}. " +
452                     $"Valid range is 1 ~ 20.";
453                 throw new ArgumentException(errMessage);
454             }
455
456             NativeClient.CreateSearchHandle(out _searchHandle).ThrowIfError("Failed to create search handle.");
457
458             try
459             {
460                 foreach (var condition in conditions)
461                 {
462                     if (condition.Bundle != null)
463                     {
464                         NativeClient.SetSearchConditionBundle(_searchHandle, condition.ContentType, condition.Category,
465                             condition.Keyword, condition.Bundle.SafeBundleHandle).
466                             ThrowIfError("Failed to set search condition.");
467                     }
468                     else
469                     {
470                         NativeClient.SetSearchCondition(_searchHandle, condition.ContentType, condition.Category,
471                             condition.Keyword, IntPtr.Zero).
472                             ThrowIfError("Failed to set search condition.");
473                     }
474                 }
475             }
476             catch
477             {
478                 if (_searchHandle != IntPtr.Zero)
479                 {
480                     NativeClient.DestroySearchHandle(_searchHandle).ThrowIfError("Failed to destroy search handle");
481                 }
482                 throw;
483             }
484         }
485
486         /// <summary>
487         /// Initializes a new instance of the <see cref="SearchCommand"/> class.
488         /// </summary>
489         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
490         /// <param name="condition">The set of <see cref="MediaControlSearchCondition"/>.</param>
491         /// <since_tizen> 5 </since_tizen>
492         public SearchCommand(MediaControlSearchCondition condition)
493         {
494             NativeClient.CreateSearchHandle(out _searchHandle).ThrowIfError("Failed to create search handle.");
495
496             try
497             {   
498                 if (condition.Bundle != null)
499                 {
500                     NativeClient.SetSearchConditionBundle(_searchHandle, condition.ContentType, condition.Category,
501                         condition.Keyword, condition.Bundle.SafeBundleHandle).
502                         ThrowIfError("Failed to set search condition.");
503                 }
504                 else
505                 {
506                     NativeClient.SetSearchCondition(_searchHandle, condition.ContentType, condition.Category,
507                         condition.Keyword, IntPtr.Zero).
508                         ThrowIfError("Failed to set search condition.");
509                 }
510             }
511             catch
512             {
513                 if (_searchHandle != IntPtr.Zero)
514                 {
515                     NativeClient.DestroySearchHandle(_searchHandle).ThrowIfError("Failed to destroy search handle");
516                 }
517                 throw;
518             }
519         }
520
521         internal SearchCommand(List<MediaControlSearchCondition> conditions, IntPtr searchHandle)
522         {
523             _searchHandle = searchHandle;
524
525             try
526             {
527                 foreach (var condition in conditions)
528                 {
529                     if (condition.Bundle != null)
530                     {
531                         NativeClient.SetSearchConditionBundle(_searchHandle, condition.ContentType, condition.Category,
532                             condition.Keyword, condition.Bundle.SafeBundleHandle).
533                             ThrowIfError("Failed to set search condition.");
534                     }
535                     else
536                     {
537                         NativeClient.SetSearchCondition(_searchHandle, condition.ContentType, condition.Category,
538                             condition.Keyword, IntPtr.Zero).
539                             ThrowIfError("Failed to set search condition.");
540                     }
541                 }
542             }
543             catch
544             {
545                 if (_searchHandle != IntPtr.Zero)
546                 {
547                     NativeClient.DestroySearchHandle(_searchHandle).ThrowIfError("Failed to destroy search handle");
548                 }
549                 throw;
550             }
551         }
552
553         internal override string Request(NativeClientHandle clientHandle)
554         {
555             NativeClient.SendSearchCommand(clientHandle, ReceiverId, _searchHandle, out string requestId).
556                 ThrowIfError("Failed to send search command.");
557
558             if (_searchHandle != IntPtr.Zero)
559             {
560                 NativeClient.DestroySearchHandle(_searchHandle).ThrowIfError("Failed to destroy search handle");
561             }
562
563             return requestId;
564         }
565
566         /// <summary>
567         /// Represents a method that is called when an response command completes.
568         /// </summary>
569         /// <since_tizen> 5 </since_tizen>
570         protected override void OnResponseCompleted()
571         {
572             base.OnResponseCompleted();
573
574             if (_searchHandle != IntPtr.Zero)
575             {
576                 NativeClient.DestroySearchHandle(_searchHandle).ThrowIfError("Failed to destroy search handle");
577             }
578         }
579     }
580 }