/* * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using Tizen.Applications; using Native = Interop.MediaControllerServer; using NativePlaylist = Interop.MediaControllerPlaylist; namespace Tizen.Multimedia.Remoting { /// /// Provides a means to set playback information and metadata and receive commands from clients. /// /// /// /// 4 public static partial class MediaControlServer { private static IntPtr _handle; private static bool? _isRunning; private static string _serverName; /// /// Gets a value indicating whether the server is running. /// /// true if the server has started; otherwise, false. /// /// /// 4 public static bool IsRunning { get { if (_isRunning.HasValue == false) { _isRunning = GetRunningState(); } return _isRunning.Value; } } private static bool GetRunningState() { IntPtr handle = IntPtr.Zero; try { Native.ConnectDb(out handle).ThrowIfError("Failed to retrieve the running state."); Native.CheckServerExist(handle, Applications.Application.Current.ApplicationInfo.ApplicationId, out var value).ThrowIfError("Failed to retrieve the running state."); return value; } finally { if (handle != IntPtr.Zero) { Native.DisconnectDb(handle); } } } private static void EnsureInitializedIfRunning() { if (_handle != IntPtr.Zero) { return; } if (IsRunning == false) { throw new InvalidOperationException("The server is not running."); } Initialize(); } private static IntPtr Handle { get { EnsureInitializedIfRunning(); return _handle; } } private static void Initialize() { Native.Create(out _handle).ThrowIfError("Failed to create media controller server."); try { RegisterPlaybackActionCommandReceivedEvent(); RegisterPlaybackPositionCommandReceivedEvent(); RegisterPlaylistCommandReceivedEvent(); RegisterShuffleModeCommandReceivedEvent(); RegisterRepeatModeCommandReceivedEvent(); RegisterSubtitleModeCommandReceivedEvent(); RegisterMode360CommandReceivedEvent(); RegisterDisplayModeCommandReceivedEvent(); RegisterDisplayRotationCommandReceivedEvent(); RegisterCustomCommandReceivedEvent(); RegisterCommandCompletedEvent(); RegisterSearchCommandReceivedEvent(); _serverName = Application.Current.ApplicationInfo.ApplicationId; _isRunning = true; } catch { Native.Destroy(_handle); _playbackCommandCallback = null; _handle = IntPtr.Zero; _serverName = null; throw; } } /// /// Starts the media control server. /// /// /// When the server starts, will be raised. /// /// http://tizen.org/privilege/mediacontroller.server /// An internal error occurs. /// Caller does not have required privilege. /// /// 4 public static void Start() { Initialize(); } /// /// Stops the media control server. /// /// /// When the server stops, will be raised. /// /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// /// 4 public static void Stop() { EnsureInitializedIfRunning(); Native.Destroy(_handle).ThrowIfError("Failed to stop the server."); _handle = IntPtr.Zero; _playbackCommandCallback = null; _isRunning = false; } /// /// Gets the active clients. /// /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// the activated client ids. /// 5 public static IEnumerable GetActivatedClients() { var clientIds = new List(); Native.ActivatedClientCallback activatedClientCallback = (name, _) => { clientIds.Add(name); return true; }; Native.ForeachActivatedClient(Handle, activatedClientCallback). ThrowIfError("Failed to get activated client."); return clientIds.AsReadOnly(); } #region Set information /// /// Updates playback state and playback position. /// The playback state. /// The playback position in milliseconds. /// is not valid. /// is less than zero. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 4 public static void SetPlaybackState(MediaControlPlaybackState state, long position) { ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackState), state, nameof(state)); if (position < 0) { throw new ArgumentOutOfRangeException(nameof(position), position, "position can't be less than zero."); } Native.SetPlaybackState(Handle, state.ToNative()).ThrowIfError("Failed to set playback state."); Native.SetPlaybackPosition(Handle, (ulong)position).ThrowIfError("Failed to set playback position."); Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback."); } private static void SetMetadata(MediaControllerNativeAttribute attribute, string value) { if (value != null) { Native.SetMetadata(Handle, attribute, value).ThrowIfError($"Failed to set metadata({attribute})."); } } /// /// Updates metadata information. /// /// The metadata to update. /// is null. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 4 public static void SetMetadata(MediaControlMetadata metadata) { if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } SetMetadata(MediaControllerNativeAttribute.Title, metadata.Title); SetMetadata(MediaControllerNativeAttribute.Artist, metadata.Artist); SetMetadata(MediaControllerNativeAttribute.Album, metadata.Album); SetMetadata(MediaControllerNativeAttribute.Author, metadata.Author); SetMetadata(MediaControllerNativeAttribute.Genre, metadata.Genre); SetMetadata(MediaControllerNativeAttribute.Duration, metadata.Duration); SetMetadata(MediaControllerNativeAttribute.Date, metadata.Date); SetMetadata(MediaControllerNativeAttribute.Copyright, metadata.Copyright); SetMetadata(MediaControllerNativeAttribute.Description, metadata.Description); SetMetadata(MediaControllerNativeAttribute.TrackNumber, metadata.TrackNumber); SetMetadata(MediaControllerNativeAttribute.Picture, metadata.AlbumArtPath); SetMetadata(MediaControllerNativeAttribute.Season, metadata.EncodedSeason); SetMetadata(MediaControllerNativeAttribute.Episode, metadata.EncodedEpisode); SetMetadata(MediaControllerNativeAttribute.Resolution, metadata.EncodedResolution); Native.UpdateMetadata(Handle).ThrowIfError("Failed to set metadata."); } /// /// Updates the shuffle mode. /// /// A value indicating whether the shuffle mode is enabled. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 4 public static void SetShuffleModeEnabled(bool enabled) { Native.UpdateShuffleMode(Handle, enabled ? MediaControllerNativeShuffleMode.On : MediaControllerNativeShuffleMode.Off). ThrowIfError("Failed to set shuffle mode."); } /// /// Updates the repeat mode. /// /// A value indicating the repeat mode. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 4 public static void SetRepeatMode(MediaControlRepeatMode mode) { ValidationUtil.ValidateEnum(typeof(MediaControlRepeatMode), mode, nameof(mode)); Native.UpdateRepeatMode(Handle, mode.ToNative()).ThrowIfError("Failed to set repeat mode."); } /// /// Sets the playlist name and index of current playing media. /// /// The playlist name of current playing media. /// The index of current playing media. /// /// or is null. /// /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 public static void SetInfoOfCurrentPlayingMedia(string playlistName, string index) { if (playlistName == null) { throw new ArgumentNullException(nameof(playlistName)); } if (index == null) { throw new ArgumentNullException(nameof(index)); } Native.SetInfoOfCurrentPlayingMedia(Handle, playlistName, index) .ThrowIfError("Failed to set the playlist name and index of current playing media"); Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback."); } /// /// Sets the age rating of latest played media. /// /// /// The Age rating of latest played media. The valid range is 0 to 19, inclusive. /// Especially, 0 means that media is suitable for all ages. /// /// The specified is not valid. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 public static void SetAgeRating(int ageRating) { if (ageRating < 0 || ageRating > 19) { throw new ArgumentOutOfRangeException(nameof(ageRating)); } Native.SetAgeRating(Handle, ageRating).ThrowIfError("Failed to set age rating."); Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback."); } /// /// Sets the subtitle mode. /// /// A value indicating whether the subtitle mode is enabled. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 6 public static void SetSubtitleMode(bool isEnabled) { Native.UpdateSubtitleMode(Handle, isEnabled).ThrowIfError("Failed to set subtitle mode."); } /// /// Sets the 360 mode. /// /// A value indicating whether the 360 mode is enabled. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 6 public static void SetMode360(bool isEnabled) { Native.UpdateMode360(Handle, isEnabled).ThrowIfError("Failed to set 360 mode."); } /// /// Sets the display mode. /// /// A value indicating the . /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 6 public static void SetDisplayMode(MediaControlDisplayMode mode) { Native.UpdateDisplayMode(Handle, mode.ToNative()).ThrowIfError("Failed to set display mode."); } /// /// Sets the display rotation. /// /// A value indicating the . /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 6 public static void SetDisplayRotation(Rotation rotation) { Native.UpdateDisplayRotaton(Handle, rotation.ToNative()).ThrowIfError("Failed to set display rotation."); } /// /// Sets the index of current playing media. /// /// The index of current playing media. /// is null. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 [Obsolete("Please do not use! This will be deprecated. Please use SetInfoOfCurrentPlayingMedia instead.")] public static void SetIndexOfCurrentPlayingMedia(string index) { if (index == null) { throw new ArgumentNullException(nameof(index)); } Native.SetIndexOfCurrentPlayingMedia(Handle, index) .ThrowIfError("Failed to set the index of current playing media"); Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback."); } #endregion Set information #region Playlist /// /// Delete playlist. /// /// Currently, only server can remove the playlist. /// The name of playlist. /// is null. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 public static void RemovePlaylist(MediaControlPlaylist playlist) { if (playlist == null) { throw new ArgumentNullException(nameof(playlist)); } Native.DeletePlaylist(Handle, playlist.Handle); playlist.Dispose(); } // Saves the playlist to the persistent storage. internal static void SavePlaylist(IntPtr playlistHandle) { Native.SavePlaylist(Handle, playlistHandle).ThrowIfError("Failed to save playlist"); } // Gets the playlist handle by name. internal static IntPtr GetPlaylistHandle(string name) { NativePlaylist.GetPlaylistHandle(_serverName, name, out IntPtr playlistHandle). ThrowIfError("Failed to get playlist handle by name"); return playlistHandle; } #endregion Playlist #region Capability /// /// Sets the content type of latest played media. /// /// A value indicating the content type of the latest played media. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 5 public static void SetPlaybackContentType(MediaControlContentType type) { ValidationUtil.ValidateEnum(typeof(MediaControlContentType), type, nameof(type)); Native.SetPlaybackContentType(Handle, type).ThrowIfError("Failed to set playback content type."); Native.UpdatePlayback(Handle).ThrowIfError("Failed to set playback."); } /// /// Sets the path of icon. /// /// The path of icon. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 5 public static void SetIconPath(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } Native.SetIconPath(Handle, path).ThrowIfError("Failed to set uri path."); } /// /// Sets the capabilities by . /// /// The set of and . /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 5 public static void SetPlaybackCapabilities(Dictionary capabilities) { foreach (var pair in capabilities) { ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), pair.Key, nameof(pair.Key)); ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), pair.Value, nameof(pair.Value)); SetPlaybackCapability(pair.Key, pair.Value); Native.SetPlaybackCapability(Handle, pair.Key.ToNative(), pair.Value). ThrowIfError("Failed to set playback capability."); } Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability."); } /// /// Sets the capabilities by . /// /// A playback command. /// A value indicating whether the is supported or not. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// or is invalid. /// 5 public static void SetPlaybackCapability(MediaControlPlaybackCommand action, MediaControlCapabilitySupport support) { ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), action, nameof(action)); ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support)); Native.SetPlaybackCapability(Handle, action.ToNative(), support).ThrowIfError("Failed to set playback capability."); Native.SaveAndNotifyPlaybackCapabilityUpdated(Handle).ThrowIfError("Failed to update playback capability."); } /// /// Sets the indicating shuffle mode is supported or not. /// /// A value indicating whether the shuffle mode is supported or not. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 5 public static void SetShuffleModeCapability(MediaControlCapabilitySupport support) { ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support)); Native.SetSimpleCapability(Handle, MediaControlNativeCapabilityCategory.Shuffle, support). ThrowIfError("Failed to set shuffle mode capability."); } /// /// Sets the indicating repeat mode is supported or not. /// /// A value indicating whether the is supported or not. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 5 public static void SetRepeatModeCapability(MediaControlCapabilitySupport support) { ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support)); Native.SetSimpleCapability(Handle, MediaControlNativeCapabilityCategory.Repeat, support). ThrowIfError("Failed to set repeat mode capability."); } /// /// Sets the supported list of . /// /// /// is not allowed in display mode capability. /// The default value of each is not supported. /// /// The supported list of . /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 6 public static void SetDisplayModeCapabilities(IDictionary capabilities) { foreach (var pair in capabilities) { SetDisplayModeCapability(pair.Key, pair.Value); } } /// /// Sets the is supported or not. /// /// /// is not allowed in display mode capability.
/// The default value of each is not supported. ///
/// The . /// A value indicating whether the is supported or not. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// or is invalid. /// 6 public static void SetDisplayModeCapability(MediaControlDisplayMode mode, MediaControlCapabilitySupport support) { ValidationUtil.ValidateEnum(typeof(MediaControlDisplayMode), mode, nameof(mode)); ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support)); if (support == MediaControlCapabilitySupport.NotDecided) { throw new ArgumentException($"NotDecided is not allowed in {mode} capability."); } Native.SetDisplayModeCapability(Handle, (uint)mode.ToNative(), support). ThrowIfError("Failed to set display mode capability."); } /// /// Sets the supported list of . /// /// /// is not allowed in display rotation capability.
/// The default value of each is not supported. ///
/// The supported list of . /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// is invalid. /// 6 public static void SetDisplayRotationCapabilities(IDictionary capabilities) { foreach (var pair in capabilities) { SetDisplayRotationCapability(pair.Key, pair.Value); } } /// /// Sets the is supported or not. /// /// /// is not allowed in display rotation capability.
/// The default value of each is not supported. ///
/// The . /// A value indicating whether the is supported or not.. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// or is invalid. /// 6 public static void SetDisplayRotationCapability(Rotation rotation, MediaControlCapabilitySupport support) { ValidationUtil.ValidateEnum(typeof(Rotation), rotation, nameof(rotation)); ValidationUtil.ValidateEnum(typeof(MediaControlCapabilitySupport), support, nameof(support)); if (support == MediaControlCapabilitySupport.NotDecided) { throw new ArgumentException($"NotDecided is not allowed in {rotation} capability."); } Native.SetDisplayRotationCapability(Handle, (uint)rotation.ToNative(), support). ThrowIfError("Failed to set display rotation capability."); } #endregion Capability #region Command /// /// Requests commands to the client. /// /// /// The client can request the command to execute ,
/// and then, the server receive the result of each request(command). ///
/// A class. /// The client Id to send command. /// represents the extra data from client and it can be null. /// /// or is null. /// /// /// The server has already been stopped.
/// -or-
/// An internal error occurs. ///
/// 5 public static async Task RequestAsync(Command command, string clientId) { if (command == null) { throw new ArgumentNullException(nameof(command)); } if (clientId == null) { throw new ArgumentNullException(nameof(clientId)); } command.SetRequestInformation(clientId); var tcs = new TaskCompletionSource(); string reqeustId = null; Bundle bundle = null; EventHandler eventHandler = (s, e) => { if (e.RequestId == reqeustId) { bundle = e.Bundle; tcs.TrySetResult(e.Result); } }; try { CommandCompleted += eventHandler; reqeustId = command.Request(Handle); (await tcs.Task).ThrowIfError("Failed to request event."); return bundle; } finally { CommandCompleted -= eventHandler; } } /// /// Sends the result of each command. /// /// The command that return to client. /// The result of . /// The extra data. /// is null. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 public static void Response(Command command, int result, Bundle bundle) { if (command == null) { throw new ArgumentNullException(nameof(command)); } command.Response(Handle, result, bundle); } /// /// Sends the result of each command. /// /// The command that return to client. /// The result of . /// is null. /// /// The server is not running .
/// -or-
/// An internal error occurs. ///
/// 5 public static void Response(Command command, int result) { if (command == null) { throw new ArgumentNullException(nameof(command)); } command.Response(Handle, result, null); } #endregion Command } }