/*
* 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.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Tizen.Applications;
using Native = Interop.MediaControllerClient;
using NativePlaylist = Interop.MediaControllerPlaylist;
namespace Tizen.Multimedia.Remoting
{
///
/// Provides a means to send commands to and handle events from media control server.
///
/// 4
public partial class MediaController
{
internal MediaController(MediaControllerManager manager, string serverAppId)
{
Debug.Assert(manager != null);
Debug.Assert(serverAppId != null);
Manager = manager;
ServerAppId = serverAppId;
}
private MediaControllerManager Manager { get; }
///
/// Gets the application id of the server.
///
/// The server application id.
/// 4
public string ServerAppId { get; }
///
/// Gets a value indicating whether the sever has been stopped.
///
/// true if the server has been stopped; otherwise, false.
/// 4
public bool IsStopped
{
get;
private set;
}
private void ThrowIfStopped()
{
if (IsStopped)
{
throw new InvalidOperationException("The server has already been stopped.");
}
}
#region Get information
///
/// Returns the playback state set by the server.
///
/// The playback state.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
/// 4
public MediaControlPlaybackState GetPlaybackState()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
Native.GetPlaybackState(playbackHandle, out var playbackCode).ThrowIfError("Failed to get state.");
return playbackCode.ToPublic();
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle);
}
}
}
///
/// Returns the playback position set by the server.
///
/// The playback position in milliseconds.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
/// 4
public long GetPlaybackPosition()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
Native.GetPlaybackPosition(playbackHandle, out var position).ThrowIfError("Failed to get position.");
return (long)position;
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle);
}
}
}
///
/// Returns the metadata set by the server.
///
/// The metadata.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
/// 4
public MediaControlMetadata GetMetadata()
{
ThrowIfStopped();
IntPtr metadataHandle = IntPtr.Zero;
try
{
NativePlaylist.GetServerMetadata(Manager.Handle, ServerAppId, out metadataHandle).
ThrowIfError("Failed to get metadata.");
return new MediaControlMetadata(metadataHandle);
}
finally
{
if (metadataHandle != IntPtr.Zero)
{
NativePlaylist.DestroyMetadata(metadataHandle);
}
}
}
///
/// Returns the all playlists.
///
/// The set of .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public IEnumerable GetPlaylists()
{
ThrowIfStopped();
var playlists = new List();
Exception caught = null;
NativePlaylist.PlaylistCallback playlistCallback = (handle, _) =>
{
try
{
playlists.Add(new MediaControlPlaylist(handle));
return true;
}
catch (Exception e)
{
caught = e;
return false;
}
};
NativePlaylist.ForeachPlaylist(ServerAppId, playlistCallback, IntPtr.Zero).
ThrowIfError("Failed to get playlist.");
if (caught != null)
{
throw caught;
}
return playlists.AsReadOnly();
}
///
/// Returns the playlist name of current playing media.
///
/// The playlist name.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public MediaControlPlaylist GetPlaylistOfCurrentPlayingMedia()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
// Get the playlist name of current playing media.
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
return GetPlaylists().FirstOrDefault(playlist => playlist.Name == name);
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
}
}
}
///
/// Returns the index of current playing media.
///
/// The index of current playing media.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public string GetIndexOfCurrentPlayingMedia()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
var (name, index) = NativePlaylist.GetPlaylistInfo(playbackHandle);
return index;
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle).ThrowIfError("Failed to destroy playback handle.");
}
}
}
///
/// Returns whether the shuffle mode is enabled.
///
/// A value indicating whether the shuffle mode is enabled.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
/// 4
public bool IsShuffleModeEnabled()
{
ThrowIfStopped();
Native.GetServerShuffleMode(Manager.Handle, ServerAppId, out var shuffleMode).
ThrowIfError("Failed to get shuffle mode state.");
return shuffleMode == MediaControllerNativeShuffleMode.On;
}
///
/// Returns the repeat mode.
///
/// A set by the server.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
/// 4
public MediaControlRepeatMode GetRepeatMode()
{
ThrowIfStopped();
Native.GetServerRepeatMode(Manager.Handle, ServerAppId, out var repeatMode).
ThrowIfError("Failed to get repeat mode state.");
return repeatMode.ToPublic();
}
///
/// Gets the content type of current playing media.
///
/// The .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public MediaControlContentType GetContentTypeOfCurrentPlayingMedia()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
Native.GetPlaybackContentType(playbackHandle, out MediaControlContentType type).
ThrowIfError("Failed to get playback content type");
return type;
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle);
}
}
}
///
/// Gets the icon path.
///
/// The icon path.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public string GetIconPath()
{
ThrowIfStopped();
Native.GetServerIcon(Manager.Handle, ServerAppId, out string uri).
ThrowIfError("Failed to get icon path.");
return uri;
}
///
/// Gets the age rating of current playing media.
///
/// The Age rating of current playing media. The range is 0 to 19, inclusive.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public int GetAgeRatingOfCurrentPlayingMedia()
{
ThrowIfStopped();
IntPtr playbackHandle = IntPtr.Zero;
try
{
Native.GetServerPlaybackHandle(Manager.Handle, ServerAppId, out playbackHandle).ThrowIfError("Failed to get playback.");
Native.GetAgeRating(playbackHandle, out int ageRating).ThrowIfError("Failed to get age rating.");
return ageRating;
}
finally
{
if (playbackHandle != IntPtr.Zero)
{
Native.DestroyPlayback(playbackHandle);
}
}
}
///
/// Gets whether the subtitle mode is enabled or not.
///
/// A value indicating whether the subtitle mode is enabled or not.
/// true if the subtitle mode is enabled; otherwise, false.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public bool IsSubtitleModeEnabled()
{
ThrowIfStopped();
Native.IsSubtitleEnabled(Manager.Handle, ServerAppId, out var isEnabled).
ThrowIfError("Failed to get subtitle mode state.");
return isEnabled;
}
///
/// Gets whether the 360 mode is enabled or not.
///
/// A value indicating whether the 360 mode is enabled or not.
/// true if the 360 mode is enabled; otherwise, false.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public bool IsMode360Enabled()
{
ThrowIfStopped();
Native.IsMode360Enabled(Manager.Handle, ServerAppId, out var isEnabled).
ThrowIfError("Failed to get 360 mode state.");
return isEnabled;
}
///
/// Gets the current display mode.
///
/// The .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
///
/// The has already been disposed.
///
/// 6
public MediaControlDisplayMode GetDisplayMode()
{
ThrowIfStopped();
Native.GetDisplayMode(Manager.Handle, ServerAppId, out var mode).
ThrowIfError("Failed to get display mode state.");
return mode.ToPublic();
}
///
/// Gets the current display rotation.
///
/// The .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
///
/// The has already been disposed.
///
/// 6
public Rotation GetDisplayRotation()
{
ThrowIfStopped();
Native.GetDisplayRotation(Manager.Handle, ServerAppId, out var rotation).
ThrowIfError("Failed to get display rotation state.");
return rotation.ToPublic();
}
#endregion Get information
#region Capability
///
/// Gets the value whether is supported or not.
///
///
/// the set of and .
///
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public Dictionary GetPlaybackCapabilities()
{
ThrowIfStopped();
IntPtr playbackCapaHandle = IntPtr.Zero;
var playbackCapabilities = new Dictionary();
try
{
Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
ThrowIfError("Failed to get playback capability handle.");
foreach (MediaControllerNativePlaybackAction action in Enum.GetValues(typeof(MediaControllerNativePlaybackAction)))
{
Native.GetPlaybackCapability(playbackCapaHandle, action, out MediaControlCapabilitySupport support);
playbackCapabilities.Add(action.ToPublic(), support);
}
return playbackCapabilities;
}
finally
{
if (playbackCapaHandle != IntPtr.Zero)
{
Native.DestroyCapability(playbackCapaHandle);
}
}
}
///
/// Gets the value whether is supported or not.
///
/// A playback command.
/// A .
/// is not valid.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public MediaControlCapabilitySupport GetPlaybackCapability(MediaControlPlaybackCommand action)
{
ThrowIfStopped();
ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), action, nameof(action));
IntPtr playbackCapaHandle = IntPtr.Zero;
try
{
Native.GetPlaybackCapabilityHandle(Manager.Handle, ServerAppId, out playbackCapaHandle).
ThrowIfError("Failed to get playback capability handle.");
Native.GetPlaybackCapability(playbackCapaHandle, action.ToNative(), out MediaControlCapabilitySupport support);
return support;
}
finally
{
if (playbackCapaHandle != IntPtr.Zero)
{
Native.DestroyCapability(playbackCapaHandle);
}
}
}
///
/// Gets the value whether the shuffle mode is supported or not.
///
/// A .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public MediaControlCapabilitySupport GetShuffleModeCapability()
{
ThrowIfStopped();
Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Shuffle, out MediaControlCapabilitySupport support).
ThrowIfError("Failed to get shuffle mode capability");
return support;
}
///
/// Gets the value whether the repeat mode is supported or not.
///
/// A .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 5
public MediaControlCapabilitySupport GetRepeatModeCapability()
{
ThrowIfStopped();
Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Repeat, out MediaControlCapabilitySupport support).
ThrowIfError("Failed to get repeat mode capability");
return support;
}
///
/// Gets the value whether the 360 mode is supported or not.
///
/// A .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public MediaControlCapabilitySupport GetMode360Capability()
{
ThrowIfStopped();
Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Mode360, out MediaControlCapabilitySupport support).
ThrowIfError("Failed to get 360 mode capability");
return support;
}
///
/// Gets the value whether the subtitle mode is supported or not.
///
/// A .
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public MediaControlCapabilitySupport GetSubtitleModeCapability()
{
ThrowIfStopped();
Native.GetSimpleCapability(Manager.Handle, ServerAppId, MediaControlNativeCapabilityCategory.Subtitle, out MediaControlCapabilitySupport support).
ThrowIfError("Failed to get subtitle mode capability");
return support;
}
///
/// Gets the value whether the repeat mode is supported or not.
///
///
/// If there's no supported display mode by server, it will return null.
/// otherwise, it will return the supported list of .
///
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public IEnumerable GetDisplayModeCapability()
{
ThrowIfStopped();
Native.GetDisplayModeCapability(Manager.Handle, ServerAppId, out uint support).
ThrowIfError("Failed to get display mode capability");
return support != 0 ? ((MediaControlNativeDisplayMode)support).ToPublicList() : null;
}
///
/// Gets the value whether the display mode is supported or not.
///
///
/// If there's no supported display rotation by server, it will return null.
/// otherwise, it will return the supported list of .
///
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
/// 6
public IEnumerable GetDisplayRotationCapability()
{
ThrowIfStopped();
Native.GetDisplayRotationCapability(Manager.Handle, ServerAppId, out uint support).
ThrowIfError("Failed to get display mode capability");
return support != 0 ? ((MediaControlNativeDisplayRotation)support).ToPublicList() : null;
}
#endregion Capability
#region Command
///
/// Requests a command to the server and client receives the result of each request(command).
///
/// A class.
///
/// The type of return value is Tuple.
/// First item of Tuple represents the and it represents the extra data from client. It can be null.
/// Second item of Tuple represents the result of each request(command).
///
/// is null.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
///
///
///
///
///
///
///
///
///
///
/// 8
public async Task<(Bundle bundle, int result)> RequestCommandAsync(Command command)
{
if (command == null)
{
throw new ArgumentNullException(nameof(command));
}
ThrowIfStopped();
command.SetRequestInformation(ServerAppId);
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(Manager.Handle);
var result = await tcs.Task;
return (bundle, result);
}
finally
{
CommandCompleted -= eventHandler;
}
}
///
/// Requests command to the server.
///
/// A class.
/// represents the extra data from server and it can be null.
/// is null.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// The has already been disposed.
///
///
///
///
///
///
///
///
///
///
///
/// 5
[Obsolete("Deprecated since API8; Will be removed in API10. Please use RequestCommandAsync(Command command) instead.")]
public async Task RequestAsync(Command command)
{
if (command == null)
{
throw new ArgumentNullException(nameof(command));
}
ThrowIfStopped();
command.SetRequestInformation(ServerAppId);
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(Manager.Handle);
((MediaControllerError)await tcs.Task).ThrowIfError("Failed to request command");
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 void Response(Command command, int result, Bundle bundle)
{
if (command == null)
{
throw new ArgumentNullException(nameof(command));
}
command.Response(Manager.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 void Response(Command command, int result)
{
Response(command, result, null);
}
///
/// Sends the result of each command.
///
/// The command that return to client.
/// The of .
/// The extra data.
/// is null.
///
/// The server is not running .
/// -or-
/// An internal error occurs.
///
/// 8
public void Response(Command command, MediaControlResult result, Bundle bundle)
{
Response(command, (int)result, bundle);
}
///
/// Sends the result of each command.
///
/// The command that return to client.
/// The of .
/// is null.
///
/// The server is not running .
/// -or-
/// An internal error occurs.
///
/// 8
public void Response(Command command, MediaControlResult result)
{
Response(command, (int)result, null);
}
///
/// Sends playback command to the server.
///
/// A playback command.
///
/// The server has already been stopped.
/// -or-
/// An internal error occurs.
///
/// is not valid.
/// The has already been disposed.
///
/// 4
[Obsolete("Please do not use! This will be deprecated. Please use Request instead.")]
public void SendPlaybackCommand(MediaControlPlaybackCommand command)
{
ThrowIfStopped();
ValidationUtil.ValidateEnum(typeof(MediaControlPlaybackCommand), command, nameof(command));
Native.SendPlaybackActionCommandWithoutReqId(Manager.Handle, ServerAppId, command.ToNative()).
ThrowIfError("Failed to send command.");
}
#endregion Command
}
}