2 * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Generic;
19 using System.Threading.Tasks;
20 using Tizen.Applications;
21 using Native = Interop.MediaControllerServer;
23 namespace Tizen.Multimedia.Remoting
26 /// Provides a means to set playback information and metadata and receive commands from clients.
28 /// <seealso cref="MediaControllerManager"/>
29 /// <seealso cref="MediaController"/>
30 /// <since_tizen> 4 </since_tizen>
31 public static partial class MediaControlServer
33 private static IntPtr _handle = IntPtr.Zero;
34 private static bool? _isRunning;
37 /// Gets a value indicating whether the server is running.
39 /// <value>true if the server has started; otherwise, false.</value>
40 /// <seealso cref="Start"/>
41 /// <seealso cref="Stop"/>
42 /// <since_tizen> 4 </since_tizen>
43 public static bool IsRunning
47 if (_isRunning.HasValue == false)
49 _isRunning = GetRunningState();
52 return _isRunning.Value;
56 private static bool GetRunningState()
58 IntPtr handle = IntPtr.Zero;
61 Native.ConnectDb(out handle).ThrowIfError("Failed to retrieve the running state.");
63 Native.CheckServerExist(handle, Applications.Application.Current.ApplicationInfo.ApplicationId,
64 out var value).ThrowIfError("Failed to retrieve the running state.");
70 if (handle != IntPtr.Zero)
72 Native.DisconnectDb(handle);
77 private static void EnsureInitializedIfRunning()
79 if (_handle != IntPtr.Zero)
84 if (IsRunning == false)
86 throw new InvalidOperationException("The server is not running.");
92 private static IntPtr Handle
96 EnsureInitializedIfRunning();
102 private static void Initialize()
104 Native.Create(out _handle).ThrowIfError("Failed to create media controller server.");
108 RegisterPlaybackActionCommandReceivedEvent();
109 RegisterPlaybackPositionCommandReceivedEvent();
110 RegisterPlaylistCommandReceivedEvent();
111 RegisterShuffleModeCommandReceivedEvent();
112 RegisterRepeatModeCommandReceivedEvent();
113 RegisterCustomCommandReceivedEvent();
114 RegisterCommandCompletedEvent();
115 RegisterSearchCommandReceivedEvent();
121 Native.Destroy(_handle);
122 _playbackCommandCallback = null;
123 _handle = IntPtr.Zero;
129 /// Starts the media control server.
132 /// When the server starts, <see cref="MediaControllerManager.ServerStarted"/> will be raised.
134 /// <privilege>http://tizen.org/privilege/mediacontroller.server</privilege>
135 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
136 /// <exception cref="UnauthorizedAccessException">Caller does not have required privilege.</exception>
137 /// <seealso cref="MediaControllerManager.ServerStarted"/>
138 /// <since_tizen> 4 </since_tizen>
139 public static void Start()
145 /// Stops the media control server.
148 /// When the server stops, <see cref="MediaControllerManager.ServerStopped"/> will be raised.
150 /// <exception cref="InvalidOperationException">
151 /// The server is not running .<br/>
153 /// An internal error occurs.
155 /// <seealso cref="MediaControllerManager.ServerStopped"/>
156 /// <since_tizen> 4 </since_tizen>
157 public static void Stop()
159 EnsureInitializedIfRunning();
161 Native.Destroy(_handle).ThrowIfError("Failed to stop the server.");
163 _handle = IntPtr.Zero;
164 _playbackCommandCallback = null;
169 /// Updates playback state and playback position.</summary>
170 /// <param name="state">The playback state.</param>
171 /// <param name="position">The playback position in milliseconds.</param>
172 /// <exception cref="ArgumentException"><paramref name="state"/> is not valid.</exception>
173 /// <exception cref="ArgumentOutOfRangeException"><paramref name="position"/> is less than zero.</exception>
174 /// <exception cref="InvalidOperationException">
175 /// The server is not running .<br/>
177 /// An internal error occurs.
179 /// <since_tizen> 4 </since_tizen>
180 public static void SetPlaybackState(MediaControlPlaybackState state, long position)
182 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackState), state, nameof(state));
186 throw new ArgumentOutOfRangeException(nameof(position), position, "position can't be less than zero.");
189 Native.SetPlaybackState(Handle, state.ToNative()).ThrowIfError("Failed to set playback state.");
191 Native.SetPlaybackPosition(Handle, (ulong)position).ThrowIfError("Failed to set playback position.");
193 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
196 private static void SetMetadata(MediaControllerNativeAttribute attribute, string value)
200 Native.SetMetadata(Handle, attribute, value).ThrowIfError($"Failed to set metadata({attribute}).");
205 /// Updates metadata information.
207 /// <param name="metadata">The metadata to update.</param>
208 /// <exception cref="ArgumentNullException"><paramref name="metadata"/> is null.</exception>
209 /// <exception cref="InvalidOperationException">
210 /// The server is not running .<br/>
212 /// An internal error occurs.
214 /// <since_tizen> 4 </since_tizen>
215 public static void SetMetadata(MediaControlMetadata metadata)
217 if (metadata == null)
219 throw new ArgumentNullException(nameof(metadata));
222 SetMetadata(MediaControllerNativeAttribute.Title, metadata.Title);
223 SetMetadata(MediaControllerNativeAttribute.Artist, metadata.Artist);
224 SetMetadata(MediaControllerNativeAttribute.Album, metadata.Album);
225 SetMetadata(MediaControllerNativeAttribute.Author, metadata.Author);
226 SetMetadata(MediaControllerNativeAttribute.Genre, metadata.Genre);
227 SetMetadata(MediaControllerNativeAttribute.Duration, metadata.Duration);
228 SetMetadata(MediaControllerNativeAttribute.Date, metadata.Date);
229 SetMetadata(MediaControllerNativeAttribute.Copyright, metadata.Copyright);
230 SetMetadata(MediaControllerNativeAttribute.Description, metadata.Description);
231 SetMetadata(MediaControllerNativeAttribute.TrackNumber, metadata.TrackNumber);
232 SetMetadata(MediaControllerNativeAttribute.Picture, metadata.AlbumArtPath);
233 SetMetadata(MediaControllerNativeAttribute.Season, metadata.EncodedSeason);
234 SetMetadata(MediaControllerNativeAttribute.Episode, metadata.EncodedEpisode);
235 SetMetadata(MediaControllerNativeAttribute.Resolution, metadata.EncodedResolution);
237 Native.UpdateMetadata(Handle).ThrowIfError("Failed to set metadata.");
241 /// Updates the shuffle mode.
243 /// <param name="enabled">A value indicating whether the shuffle mode is enabled.</param>
244 /// <exception cref="InvalidOperationException">
245 /// The server is not running .<br/>
247 /// An internal error occurs.
249 /// <since_tizen> 4 </since_tizen>
250 public static void SetShuffleModeEnabled(bool enabled)
252 Native.UpdateShuffleMode(Handle, enabled ? MediaControllerNativeShuffleMode.On : MediaControllerNativeShuffleMode.Off).
253 ThrowIfError("Failed to set shuffle mode.");
257 /// Updates the repeat mode.
259 /// <param name="mode">A value indicating the repeat mode.</param>
260 /// <exception cref="InvalidOperationException">
261 /// The server is not running .<br/>
263 /// An internal error occurs.
265 /// <exception cref="ArgumentException"><paramref name="mode"/> is invalid.</exception>
266 /// <since_tizen> 4 </since_tizen>
267 public static void SetRepeatMode(MediaControlRepeatMode mode)
269 ValidationUtil.ValidateEnum(typeof(MediaControlRepeatMode), mode, nameof(mode));
271 Native.UpdateRepeatMode(Handle, mode.ToNative()).ThrowIfError("Failed to set repeat mode.");
275 /// Sets the index of current playing media.
277 /// <param name="index">The index of current playing media.</param>
278 /// <exception cref="ArgumentNullException"><paramref name="index"/> is null.</exception>
279 /// <exception cref="InvalidOperationException">
280 /// The server is not running .<br/>
282 /// An internal error occurs.
284 /// <since_tizen> 5 </since_tizen>
285 [Obsolete("Please do not use! This will be deprecated. Please use SetInfoOfCurrentPlayingMedia instead.")]
286 public static void SetIndexOfCurrentPlayingMedia(string index)
290 throw new ArgumentNullException(nameof(index));
293 Native.SetIndexOfCurrentPlayingMedia(Handle, index)
294 .ThrowIfError("Failed to set the index of current playing media");
296 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
300 /// Sets the playlist name and index of current playing media.
302 /// <param name="playlistName">The playlist name of current playing media.</param>
303 /// <param name="index">The index of current playing media.</param>
304 /// <exception cref="ArgumentNullException">
305 /// <paramref name="playlistName"/> or <paramref name="index"/> is null.
307 /// <exception cref="InvalidOperationException">
308 /// The server is not running .<br/>
310 /// An internal error occurs.
312 /// <since_tizen> 5 </since_tizen>
313 public static void SetInfoOfCurrentPlayingMedia(string playlistName, string index)
315 if (playlistName == null)
317 throw new ArgumentNullException(nameof(playlistName));
321 throw new ArgumentNullException(nameof(index));
324 Native.SetInfoOfCurrentPlayingMedia(Handle, playlistName, index)
325 .ThrowIfError("Failed to set the playlist name and index of current playing media");
327 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
333 /// <param name="playlist">The name of playlist.</param>
334 /// <exception cref="ArgumentNullException"><paramref name="playlist"/> is null.</exception>
335 /// <exception cref="InvalidOperationException">
336 /// The server is not running .<br/>
338 /// An internal error occurs.
340 /// <since_tizen> 5 </since_tizen>
341 public static void RemovePlaylist(MediaControlPlaylist playlist)
343 if (playlist == null)
345 throw new ArgumentNullException(nameof(playlist));
348 Native.DeletePlaylist(Handle, playlist.Handle);
352 // Saves the playlist to the persistent storage.
353 internal static void SavePlaylist(IntPtr playlistHandle)
355 Native.SavePlaylist(Handle, playlistHandle).ThrowIfError("Failed to save playlist");
358 // Gets the playlist handle by name.
359 internal static IntPtr GetPlaylistHandle(string name)
361 Native.GetPlaylistHandle(Handle, name, out IntPtr playlistHandle)
362 .ThrowIfError("Failed to get playlist handle by name");
364 return playlistHandle;
368 /// Gets the active clients.
370 /// <exception cref="InvalidOperationException">
371 /// The server is not running .<br/>
373 /// An internal error occurs.
375 /// <returns>the activated client ids.</returns>
376 /// <since_tizen> 5 </since_tizen>
377 public static IEnumerable<string> GetActivatedClients()
379 var clientIds = new List<string>();
381 Native.ActivatedClientCallback activatedClientCallback = (name, _) =>
387 Native.ForeachActivatedClient(Handle, activatedClientCallback).
388 ThrowIfError("Failed to get activated client.");
390 return clientIds.AsReadOnly();
394 /// Requests commands to the client.
397 /// The client can request the command to execute <see cref="Command"/>, <br/>
398 /// and then, the server receive the result of each request(command).
400 /// <param name="command">A <see cref="Command"/> class.</param>
401 /// <param name="clientId">The client Id to send command.</param>
402 /// <returns><see cref="Bundle"/> represents the extra data from client and it can be null.</returns>
403 /// <exception cref="ArgumentNullException">
404 /// <paramref name="command"/> or <paramref name="clientId"/> is null.
406 /// <exception cref="InvalidOperationException">
407 /// The server has already been stopped.<br/>
409 /// An internal error occurs.
411 /// <since_tizen> 5 </since_tizen>
412 public static async Task<Bundle> RequestAsync(Command command, string clientId)
416 throw new ArgumentNullException(nameof(command));
418 if (clientId == null)
420 throw new ArgumentNullException(nameof(clientId));
423 command.SetRequestInformation(clientId);
425 var tcs = new TaskCompletionSource<MediaControllerError>();
426 string reqeustId = null;
427 Bundle bundle = null;
429 EventHandler<CommandCompletedEventArgs> eventHandler = (s, e) =>
431 if (e.RequestId == reqeustId)
434 tcs.TrySetResult(e.Result);
440 CommandCompleted += eventHandler;
442 reqeustId = command.Request(Handle);
444 (await tcs.Task).ThrowIfError("Failed to request event.");
450 CommandCompleted -= eventHandler;
455 /// Sends the result of each command.
457 /// <param name="command">The command that return to client.</param>
458 /// <param name="result">The result of <paramref name="command"/>.</param>
459 /// <param name="bundle">The extra data.</param>
460 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
461 /// <exception cref="InvalidOperationException">
462 /// The server is not running .<br/>
464 /// An internal error occurs.
466 /// <since_tizen> 5 </since_tizen>
467 public static void Response(Command command, int result, Bundle bundle)
471 throw new ArgumentNullException(nameof(command));
474 command.Response(Handle, result, bundle);
478 /// Sends the result of each command.
480 /// <param name="command">The command that return to client.</param>
481 /// <param name="result">The result of <paramref name="command"/>.</param>
482 /// <exception cref="ArgumentNullException"><paramref name="command"/> is null.</exception>
483 /// <exception cref="InvalidOperationException">
484 /// The server is not running .<br/>
486 /// An internal error occurs.
488 /// <since_tizen> 5 </since_tizen>
489 public static void Response(Command command, int result)
493 throw new ArgumentNullException(nameof(command));
496 command.Response(Handle, result, null);
501 /// Sets the content type of latest played media.
503 /// <param name="type">A value indicating the content type of the latest played media.</param>
504 /// <exception cref="InvalidOperationException">
505 /// The server is not running .<br/>
507 /// An internal error occurs.
509 /// <exception cref="ArgumentException"><paramref name="type"/> is invalid.</exception>
510 /// <since_tizen> 5 </since_tizen>
511 public static void SetPlaybackContentType(MediaControlContentType type)
513 ValidationUtil.ValidateEnum(typeof(MediaControlContentType), type, nameof(type));
515 Native.SetPlaybackContentType(Handle, type).ThrowIfError("Failed to set playback content type.");
517 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");
521 /// Sets the path of icon.
523 /// <param name="path">The path of icon.</param>
524 /// <exception cref="InvalidOperationException">
525 /// The server is not running .<br/>
527 /// An internal error occurs.
529 /// <exception cref="ArgumentNullException"><paramref name="path"/> is invalid.</exception>
530 /// <since_tizen> 5 </since_tizen>
531 public static void SetIconPath(string path)
535 throw new ArgumentNullException(nameof(path));
538 Native.SetIconPath(Handle, path).ThrowIfError("Failed to set uri path.");
542 /// Sets the capabilities by <see cref="MediaControlPlaybackCommand"/>.
544 /// <param name="capabilities">The set of <see cref="MediaControlPlaybackCommand"/> and <see cref="MediaControlCapabilitySupport"/>.</param>
545 /// <exception cref="InvalidOperationException">
546 /// The server is not running .<br/>
548 /// An internal error occurs.
550 /// <exception cref="ArgumentException"><paramref name="capabilities"/> is invalid.</exception>
551 /// <since_tizen> 5 </since_tizen>
552 public static void SetPlaybackCapabilities(Dictionary<MediaControlPlaybackCommand, MediaControlCapabilitySupport> capabilities)
554 foreach (var pair in capabilities)
556 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), pair.Key, nameof(pair.Key));
557 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), pair.Value, nameof(pair.Value));
559 SetPlaybackCapability(pair.Key, pair.Value);
560 Native.SetPlaybackCapability(Handle, pair.Key.ToNative(), pair.Value).
561 ThrowIfError("Failed to set playback capability.");
564 Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability.");
568 /// Sets the capabilities by <see cref="MediaControlPlaybackCommand"/>.
570 /// <param name="action">A playback command.</param>
571 /// <param name="support">A value indicating whether the <paramref name="action"/> is supported or not.</param>
572 /// <exception cref="InvalidOperationException">
573 /// The server is not running .<br/>
575 /// An internal error occurs.
577 /// <exception cref="ArgumentException"><paramref name="action"/> or <paramref name="support"/> is invalid.</exception>
578 /// <since_tizen> 5 </since_tizen>
579 public static void SetPlaybackCapability(MediaControlPlaybackCommand action, MediaControlCapabilitySupport support)
581 ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), action, nameof(action));
582 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
584 Native.SetPlaybackCapability(Handle, action.ToNative(), support).ThrowIfError("Failed to set playback capability.");
586 Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability.");
590 /// Sets the <see cref="MediaControlCapabilitySupport"/> indicating shuffle mode is supported or not.
592 /// <param name="support">A value indicating whether the shuffle mode is supported or not.</param>
593 /// <exception cref="InvalidOperationException">
594 /// The server is not running .<br/>
596 /// An internal error occurs.
598 /// <exception cref="ArgumentException"><paramref name="support"/> is invalid.</exception>
599 /// <since_tizen> 5 </since_tizen>
600 public static void SetShuffleModeCapability(MediaControlCapabilitySupport support)
602 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
604 Native.SetShuffleModeCapability(Handle, support).ThrowIfError("Failed to set shuffle mode capability.");
608 /// Sets the content type of latest played media.
610 /// <param name="support">A value indicating whether the <see cref="MediaControlRepeatMode"/> is supported or not.</param>
611 /// <exception cref="InvalidOperationException">
612 /// The server is not running .<br/>
614 /// An internal error occurs.
616 /// <exception cref="ArgumentException"><paramref name="support"/> is invalid.</exception>
617 /// <since_tizen> 5 </since_tizen>
618 public static void SetRepeatModeCapability(MediaControlCapabilitySupport support)
620 ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support));
622 Native.SetRepeatModeCapability(Handle, support).ThrowIfError("Failed to set shuffle mode capability.");
624 #endregion Capabilities
627 /// Sets the age rating of latest played media.
629 /// <param name="ageRating">
630 /// The Age rating of latest played media. The valid range is 0 to 19, inclusive.
631 /// Especially, 0 means that media is suitable for all ages.
633 /// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="ageRating"/> is not valid.</exception>
634 /// <exception cref="InvalidOperationException">
635 /// The server is not running .<br/>
637 /// An internal error occurs.
639 /// <since_tizen> 5 </since_tizen>
640 public static void SetAgeRating(int ageRating)
642 if (ageRating < 0 || ageRating > 19)
644 throw new ArgumentOutOfRangeException(nameof(ageRating));
647 Native.SetAgeRating(Handle, ageRating).ThrowIfError("Failed to set age rating.");
649 Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback.");