/*
* 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
}
}