From d6259fa2ce51b8868310b97a41ba41a49d2ec6f0 Mon Sep 17 00:00:00 2001 From: Sudipto Bal Date: Tue, 30 Jun 2020 18:03:02 +0900 Subject: [PATCH] [Bluetooth][TCSACR-323] Add new Avrcp Control API (#1666) Signed-off by: Sudipto Bal Signed-off-by: Wootak Jung --- .../Interop/Interop.Bluetooth.cs | 95 ++++ .../BluetoothAvrcpControl.cs | 543 +++++++++++++++++++++ .../BluetoothAvrcpControlImpl.cs | 474 ++++++++++++++++++ .../BluetoothEnumerations.cs | 36 ++ .../Tizen.Network.Bluetooth/BluetoothEventArgs.cs | 123 ++++- .../Tizen.Network.Bluetooth/BluetoothStructs.cs | 15 + 6 files changed, 1279 insertions(+), 7 deletions(-) create mode 100644 src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs create mode 100644 src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs diff --git a/src/Tizen.Network.Bluetooth/Interop/Interop.Bluetooth.cs b/src/Tizen.Network.Bluetooth/Interop/Interop.Bluetooth.cs index 1c9cfad..2a80d0f 100644 --- a/src/Tizen.Network.Bluetooth/Interop/Interop.Bluetooth.cs +++ b/src/Tizen.Network.Bluetooth/Interop/Interop.Bluetooth.cs @@ -90,6 +90,15 @@ internal static partial class Interop internal delegate void ScanModeChangedCallback(int scan, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void AvrcpControlConnectionChangedCallback(bool connected, string remoteAddress, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void PositionChangedCallback(uint position, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void PlayStatusChangedCallback(int play_status, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void TrackInfoChangedCallback([MarshalAs(UnmanagedType.Struct)]ref TrackInfoStruct track, IntPtr userData); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void ConnectionChangedCallback(int result, bool connected, string deviceAddress, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void ClientCharacteristicValueChangedCallback(IntPtr characteristicHandle, string value, int len, IntPtr userData); @@ -512,6 +521,92 @@ internal static partial class Interop [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_scan_mode_changed_cb")] internal static extern int UnsetScanModeChangedCallback(); + // Bluetooth AVRCP Control + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_initialize")] + internal static extern int AvrcpControlInitialize(AvrcpControlConnectionChangedCallback callback, IntPtr userData); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_deinitialize")] + internal static extern int AvrcpControlDeinitialize(); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_connect")] + internal static extern int AvrcpControlConnect(string address); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_disconnect")] + internal static extern int AvrcpControlDisconnect(string address); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_equalizer_state")] + internal static extern int SetEqualizerState(EqualizerState state); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_equalizer_state")] + internal static extern int GetEqualizerState(out EqualizerState state); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_repeat_mode")] + internal static extern int SetRepeatMode(RepeatMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_repeat_mode")] + internal static extern int GetRepeatMode(out RepeatMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_shuffle_mode")] + internal static extern int SetShuffleMode(ShuffleMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_shuffle_mode")] + internal static extern int GetShuffleMode(out ShuffleMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_scan_mode")] + internal static extern int SetScanMode(ScanMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_scan_mode")] + internal static extern int GetScanMode(out ScanMode mode); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_position")] + internal static extern int GetPosition(out uint position); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_play_status")] + internal static extern int GetPlayStatus(out PlayerState state); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_get_track_info")] + internal static extern int GetTrackInfo(out IntPtr infoptr); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_free_track_info")] + internal static extern int FreeTrackInfo(IntPtr infoptr); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_player_command")] + internal static extern int SendPlayerCommand(PlayerCommand command); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_player_command_to")] + internal static extern int SendPlayerCommandTo(PlayerCommand command, string remoteAddress); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_set_absolute_volume")] + internal static extern int SetAbsoluteVolume(uint volume); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_increase_volume")] + internal static extern int IncreaseVolume(); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_decrease_volume")] + internal static extern int DecreaseVolume(); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_control_send_delay_report")] + internal static extern int SendDelayReport(uint value); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_position_changed_cb")] + internal static extern int SetPositionChangedCallback(PositionChangedCallback PositionChangedCb, IntPtr userData); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_position_changed_cb")] + internal static extern int UnsetPositionChangedCallback(); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_play_status_changed_cb")] + internal static extern int SetPlayStatusChangedCallback(PlayStatusChangedCallback PlayStatusChangedCb, IntPtr userData); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_play_status_changed_cb")] + internal static extern int UnsetPlayStatusChangedCallback(); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_set_track_info_changed_cb")] + internal static extern int SetTrackInfoChangedCallback(TrackInfoChangedCallback TrackInfoChangedCb, IntPtr userData); + + [DllImport(Libraries.Bluetooth, EntryPoint = "bt_avrcp_unset_track_info_changed_cb")] + internal static extern int UnsetTrackInfoChangedCallback(); + // Bluetooth GATT [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs new file mode 100644 index 0000000..d1f7df8 --- /dev/null +++ b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControl.cs @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2020 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.ComponentModel; +using System.Threading.Tasks; + +namespace Tizen.Network.Bluetooth +{ + /// + /// This class is used to send commands from the control device (For example, headset) to the target device (For example, media player). + /// + /// + /// This class can be obtained from BluetoothDevice.GetProfile method. + /// + /// http://tizen.org/privilege/bluetooth + /// http://tizen.org/feature/network.bluetooth.audio.controller + /// 8 + public class BluetoothAvrcpControl : BluetoothProfile + { + private TaskCompletionSource _taskForConnection; + private TaskCompletionSource _taskForDisconnection; + private bool disposed = false; + private bool _isConnected = false; + + internal BluetoothAvrcpControl() + { + BluetoothAvrcpControlImpl.Instance.ConnectionChanged += OnConnectionChanged; + BluetoothAvrcpControlImpl.Instance.PositionChanged += OnPositionChanged; + BluetoothAvrcpControlImpl.Instance.PlayStateChanged += OnPlayStateChanged; + BluetoothAvrcpControlImpl.Instance.TrackInfoChanged += OnTrackInfoChanged; + } + + private void OnConnectionChanged(object s, AvrcpControlConnectionChangedEventArgs e) + { + if (e.RemoteAddress != RemoteAddress) + { + return; + } + + _isConnected = e.IsConnected; + + if (_taskForConnection != null && !_taskForConnection.Task.IsCompleted) + { + if (e.IsConnected == true) + { + _taskForConnection.SetResult(true); + ConnectionStateChanged?.Invoke(this, e); + } + else + { + _taskForConnection.SetException(BluetoothErrorFactory.CreateBluetoothException((int)BluetoothError.OperationFailed)); + } + _taskForConnection = null; + return; + } + if (_taskForDisconnection != null && !_taskForDisconnection.Task.IsCompleted) + { + if (e.IsConnected == false) + { + _taskForDisconnection.SetResult(true); + ConnectionStateChanged?.Invoke(this, e); + } + else + { + _taskForDisconnection.SetException(BluetoothErrorFactory.CreateBluetoothException((int)BluetoothError.OperationFailed)); + } + _taskForDisconnection = null; + } + } + + private void OnPositionChanged(object s, PositionChangedEventArgs e) + { + PositionChanged?.Invoke(this, e); + } + + private void OnPlayStateChanged(object s, PlayStateChangedEventArgs e) + { + PlayStateChanged?.Invoke(this, e); + } + + private void OnTrackInfoChanged(object s, TrackInfoChangedEventArgs e) + { + TrackInfoChanged?.Invoke(this, e); + } + + /// + /// The AvrcpControlConnectionChangedEventArgs event is invoked when the connection status of device is changed. + /// + /// 8 + public event EventHandler ConnectionStateChanged; + + /// + /// The PositionChangedEventArgs event is invoked when the play position of a track is changed. + /// + /// 8 + public event EventHandler PositionChanged; + + /// + /// The PlayStateChangedEventArgs event is invoked when the play state of a track gets changed. + /// + /// 8 + public event EventHandler PlayStateChanged; + + /// + /// The TrackInfoChangedEventArgs event is invoked when info of a track gets changed. + /// + /// 8 + public event EventHandler TrackInfoChanged; + + /// + /// Asynchronously connects the remote device + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the method fails + /// 8 + public Task ConnectAsync() + { + if (_taskForConnection != null && !_taskForConnection.Task.IsCompleted) + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress); + } + + _taskForConnection = new TaskCompletionSource(); + BluetoothAvrcpControlImpl.Instance.Connect(RemoteAddress); + return _taskForConnection.Task; + } + + /// + /// Asynchronously disconnects the remote device + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the method fails + /// 8 + public Task DisconnectAsync() + { + if (_taskForDisconnection != null && !_taskForDisconnection.Task.IsCompleted) + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress); + } + _taskForDisconnection = new TaskCompletionSource(); + BluetoothAvrcpControlImpl.Instance.Disconnect(RemoteAddress); + return _taskForDisconnection.Task; + } + + /// + /// A property for the equalizer mode of target device. + /// + /// + /// The remote device must be connected. + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or when notifying the repeat mode state to the remote device fails. + /// + /// 8 + public EqualizerState EqualizerState + { + get + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetEqualizerState(); + } + else + { + return EqualizerState.Off; + } + } + set + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SetEqualizerState(value); + } + } + } + + /// + /// A property for the repeat mode of target device. + /// + /// + /// The remote device must be connected. + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or when notifying the repeat mode state to the remote device fails. + /// + /// 8 + public RepeatMode RepeatMode + { + get + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetRepeatMode(); + } + else + { + return RepeatMode.Off; + } + } + set + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SetRepeatMode(value); + } + } + } + + /// + /// A property for the suffle mode of target device. + /// + /// + /// The remote device must be connected. + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or if retrieving scan mode of the remote device fails. + /// + /// 8 + public ShuffleMode ShuffleMode + { + get + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetShuffleMode(); + } + else + { + return ShuffleMode.Off; + } + } + set + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SetShuffleMode(value); + } + } + } + + /// + /// A property for the scan mode of target device. + /// + /// + /// The remote device must be connected. + /// + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or if retrieving scan mode of the remote device fails. + /// + /// 8 + public ScanMode ScanMode + { + get + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetScanMode(); + } + else + { + return ScanMode.Off; + } + } + set + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SetScanMode(value); + } + } + } + + /// + /// Gets position of the track being played on the target device. + /// + /// + /// The remote device must be connected. + /// + /// Play position of the track being played on the target device + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or if there is an error retrieving the position of the track that is currently being played. + /// + /// 8 + public uint GetPosition() + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetPosition(); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + return 0; + } + } + + /// + /// Gets player state of the target device. + /// + /// + /// The remote device must be connected. + /// + /// Play status of the target device + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or if there happens to be an error while retrieving the player state of the target device. + /// + /// 8 + public PlayerState GetPlayStatus() + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetPlayStatus(); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + return PlayerState.Stopped; + } + } + + /// + /// Gets info of the track being played on the target device. + /// + /// + /// The remote device must be connected. + /// + /// Info of the track being played on the target device + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or if there happens to be an error while retrieving info of the track being played on the target. + /// + /// 8 + public Track GetTrackInfo() + { + if (_isConnected == true) + { + return BluetoothAvrcpControlImpl.Instance.GetTrackInfo(); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + return null; + } + } + + /// + /// Sends a particular play command to the target device + /// + /// + /// The remote device must be connected. + /// + /// Command to be sent + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or when sending command to the target device fails. + /// + /// 8 + public void SendPlayerCommand(PlayerCommand command) + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SendPlayerCommand(command); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + /// + /// Sends a play command to a particular target device + /// + /// + /// The remote device must be connected. + /// + /// Command to be sent + /// Address of the device to send command + /// Thrown when the Bluetooth is not supported. + /// Thrown when the Bluetooth is not enabled + /// or when sending command to the target device fails. + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendPlayerCommandTo(PlayerCommand command, string remoteAddress) + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SendPlayerCommandTo(command, remoteAddress); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + /// + /// Sets absolute volume of target device + /// + /// + /// The remote device must be connected + /// + /// The volume level to be set + /// Thrown when the Bluetooth is not supported + /// Thrown when the Bluetooth is not enabled + /// or when setting absolute volume of the target device fails + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void SetAbsoluteVolume(uint volume) + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SetAbsoluteVolume(volume); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + /// + /// Increases volume of target device + /// + /// + /// The remote device must be connected + /// + /// Thrown when the Bluetooth is not supported + /// Thrown when the Bluetooth is not enabled + /// or when increasing volume of the target device fails + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void IncreaseVolume() + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.IncreaseVolume(); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + /// + /// Decreases volume of target device + /// + /// + /// The remote device must be connected + /// + /// Thrown when the Bluetooth is not supported + /// Thrown when the Bluetooth is not enabled + /// or when decreasing volume of the target device fails + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void DecreaseVolume() + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.DecreaseVolume(); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + /// + /// Sends delay report to the target device + /// + /// + /// The remote device must be connected + /// + /// Delay to be sent to target + /// Thrown when the Bluetooth is not supported + /// Thrown when the Bluetooth is not enabled + /// or when sending delay to the target device fails + /// + /// 8 + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendDelayReport(uint delay) + { + if (_isConnected == true) + { + BluetoothAvrcpControlImpl.Instance.SendDelayReport(delay); + } + else + { + BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected); + } + } + + ~BluetoothAvrcpControl() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing) + { + // Free managed objects. + BluetoothAvrcpControlImpl.Instance.ConnectionChanged -= OnConnectionChanged; + BluetoothAvrcpControlImpl.Instance.PositionChanged -= OnPositionChanged; + BluetoothAvrcpControlImpl.Instance.PlayStateChanged -= OnPlayStateChanged; + BluetoothAvrcpControlImpl.Instance.TrackInfoChanged -= OnTrackInfoChanged; + } + //Free unmanaged objects. + disposed = true; + } + } +} diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs new file mode 100644 index 0000000..3606378 --- /dev/null +++ b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothAvrcpControlImpl.cs @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2020 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.Runtime.InteropServices; + +namespace Tizen.Network.Bluetooth +{ + internal class BluetoothAvrcpControlImpl :IDisposable + { + private event EventHandler _positionChanged; + private event EventHandler _playStateChanged; + private event EventHandler _trackInfoChanged; + + private Interop.Bluetooth.PositionChangedCallback _positionChangedCallback; + private Interop.Bluetooth.PlayStatusChangedCallback _playStateChangedCallback; + private Interop.Bluetooth.TrackInfoChangedCallback _trackInfoChangedCallback; + private Interop.Bluetooth.AvrcpControlConnectionChangedCallback _connStateChangedCallback; + + private static BluetoothAvrcpControlImpl _instance = new BluetoothAvrcpControlImpl(); + private bool disposed = false; + + internal event EventHandler ConnectionChanged; + + internal event EventHandler PositionChanged + { + add + { + if (_positionChanged == null) + { + RegisterPositionChangedEvent(); + } + _positionChanged += value; + } + remove + { + _positionChanged -= value; + if (_positionChanged == null) + { + UnregisterPositionChangedEvent(); + } + } + } + + internal event EventHandler PlayStateChanged + { + add + { + if (_playStateChanged == null) + { + RegisterPlayStateChangedEvent(); + } + _playStateChanged += value; + } + remove + { + _playStateChanged -= value; + if (_playStateChanged == null) + { + UnregisterPlayStateChangedEvent(); + } + } + } + + internal event EventHandler TrackInfoChanged + { + add + { + if (_trackInfoChanged == null) + { + RegisterTrackInfoChangedEvent(); + } + _trackInfoChanged += value; + } + remove + { + _trackInfoChanged -= value; + if (_trackInfoChanged == null) + { + UnregisterTrackInfoChangedEvent(); + } + } + } + + private void RegisterPositionChangedEvent() + { + _positionChangedCallback = (uint position, IntPtr userData) => + { + _positionChanged?.Invoke(null, new PositionChangedEventArgs(position)); + }; + int ret = Interop.Bluetooth.SetPositionChangedCallback(_positionChangedCallback, IntPtr.Zero); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set position changed callback, Error - " + (BluetoothError)ret); + } + } + + private void UnregisterPositionChangedEvent() + { + int ret = Interop.Bluetooth.UnsetPositionChangedCallback(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to unset position changed callback, Error - " + (BluetoothError)ret); + } + } + + private void RegisterPlayStateChangedEvent() + { + _playStateChangedCallback = (int state, IntPtr userdata) => + { + _playStateChanged?.Invoke(null, new PlayStateChangedEventArgs((PlayerState)state)); + }; + int ret = Interop.Bluetooth.SetPlayStatusChangedCallback(_playStateChangedCallback, IntPtr.Zero); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set play status changed callback, Error - " + (BluetoothError)ret); + } + } + + private void UnregisterPlayStateChangedEvent() + { + int ret = Interop.Bluetooth.UnsetPlayStatusChangedCallback(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to unset play status changed callback, Error - " + (BluetoothError)ret); + } + } + + private void RegisterTrackInfoChangedEvent() + { + _trackInfoChangedCallback = (ref TrackInfoStruct track_info, IntPtr userdata) => + { + _trackInfoChanged?.Invoke(null, new TrackInfoChangedEventArgs(new Track() + { + Album = track_info.Album, + Artist = track_info.Artist, + Genre = track_info.Genre, + Title = track_info.Title, + TotalTracks = track_info.total_tracks, + TrackNum = track_info.number, + Duration = track_info.duration + })); + int ret = Interop.Bluetooth.SetTrackInfoChangedCallback(_trackInfoChangedCallback, IntPtr.Zero); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set track info changed callback, Error - " + (BluetoothError)ret); + } + }; + + } + + private void UnregisterTrackInfoChangedEvent() + { + int ret = Interop.Bluetooth.UnsetTrackInfoChangedCallback(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to unset track info changed callback, Error - " + (BluetoothError)ret); + } + } + + internal void Connect(string address) + { + int ret = Interop.Bluetooth.AvrcpControlConnect(address); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to connect " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal void Disconnect(string address) + { + int ret = Interop.Bluetooth.AvrcpControlDisconnect(address); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to disconnect " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal EqualizerState GetEqualizerState() + { + EqualizerState state; + int ret = Interop.Bluetooth.GetEqualizerState(out state); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get equalizer state " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return state; + } + + internal void SetEqualizerState(EqualizerState state) + { + int ret = Interop.Bluetooth.SetEqualizerState(state); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set equalizer state to " + state + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal RepeatMode GetRepeatMode() + { + RepeatMode mode; + int ret = Interop.Bluetooth.GetRepeatMode(out mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get repeat mode" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return mode; + } + + internal void SetRepeatMode(RepeatMode mode) + { + int ret = Interop.Bluetooth.SetRepeatMode(mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set repeat mode to " + mode + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal ShuffleMode GetShuffleMode() + { + ShuffleMode mode; + int ret = Interop.Bluetooth.GetShuffleMode(out mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get shuffle mode" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return mode; + } + + internal void SetShuffleMode(ShuffleMode mode) + { + int ret = Interop.Bluetooth.SetShuffleMode(mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set shuffle mode to " + mode + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal ScanMode GetScanMode() + { + ScanMode mode; + int ret = Interop.Bluetooth.GetScanMode(out mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get scan mode" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return mode; + } + + internal void SetScanMode(ScanMode mode) + { + int ret = Interop.Bluetooth.SetScanMode(mode); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set scan mode to " + mode + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal uint GetPosition() + { + uint position; + int ret = Interop.Bluetooth.GetPosition(out position); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get play position" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return position; + } + + internal PlayerState GetPlayStatus() + { + PlayerState state; + int ret = Interop.Bluetooth.GetPlayStatus(out state); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get play status" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + return state; + } + + internal Track GetTrackInfo() + { + Track trackdata = new Track(); + TrackInfoStruct trackinfo; + IntPtr infoptr; + + int ret = Interop.Bluetooth.GetTrackInfo(out infoptr); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to get track data" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + + trackinfo = (TrackInfoStruct)Marshal.PtrToStructure(infoptr, typeof(TrackInfoStruct)); + trackdata.Album = trackinfo.Album; + trackdata.Artist = trackinfo.Artist; + trackdata.Genre = trackinfo.Genre; + trackdata.Title = trackinfo.Title; + trackdata.TotalTracks = trackinfo.total_tracks; + trackdata.TrackNum = trackinfo.number; + trackdata.Duration = trackinfo.duration; + + ret = Interop.Bluetooth.FreeTrackInfo(infoptr); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to free track data" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + + return trackdata; + } + + internal void SendPlayerCommand(PlayerCommand command) + { + int ret = Interop.Bluetooth.SendPlayerCommand(command); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to send player command " + command + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + internal void SendPlayerCommandTo(PlayerCommand command, string remoteAddress) + { + int ret = Interop.Bluetooth.SendPlayerCommandTo(command, remoteAddress); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to send player command " + command + " to remote address " + remoteAddress + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal void SetAbsoluteVolume(uint volume) + { + int ret = Interop.Bluetooth.SetAbsoluteVolume(volume); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to set absolute volume to level " + volume + " - " + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal void IncreaseVolume() + { + int ret = Interop.Bluetooth.IncreaseVolume(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to increase volume" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal void DecreaseVolume() + { + int ret = Interop.Bluetooth.DecreaseVolume(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to decrease volume" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal void SendDelayReport(uint delay) + { + int ret = Interop.Bluetooth.SendDelayReport(delay); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to send delay report" + (BluetoothError)ret); + BluetoothErrorFactory.ThrowBluetoothException(ret); + } + } + + internal static BluetoothAvrcpControlImpl Instance + { + get + { + return _instance; + } + } + + private BluetoothAvrcpControlImpl () + { + ControlInitialize(); + } + + private void ControlInitialize() + { + _connStateChangedCallback = (bool connected, string remoteAddress, IntPtr userData) => + { + ConnectionChanged?.Invoke(null, new AvrcpControlConnectionChangedEventArgs(connected, remoteAddress)); + }; + int ret = Interop.Bluetooth.AvrcpControlInitialize(_connStateChangedCallback, IntPtr.Zero); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to initialize AVRCP Control, Error - " + (BluetoothError)ret); + } + } + + private void ControlDeinitialize() + { + int ret = Interop.Bluetooth.AvrcpControlDeinitialize(); + if (ret != (int)BluetoothError.None) + { + Log.Error(Globals.LogTag, "Failed to deinitialize AVRCP Control, Error - " + (BluetoothError)ret); + } + } + + private void UnregisterAllEvents() + { + if (_playStateChanged == null) + { + UnregisterPlayStateChangedEvent(); + } + if (_positionChanged == null) + { + UnregisterPositionChangedEvent(); + } + if (_trackInfoChanged == null) + { + UnregisterTrackInfoChangedEvent(); + } + } + + ~BluetoothAvrcpControlImpl() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing) + { + // Free managed objects. + } + //Free unmanaged objects + ControlDeinitialize(); + UnregisterAllEvents(); + disposed = true; + } + } +} diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEnumerations.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEnumerations.cs index a57f495..cf3c788 100644 --- a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEnumerations.cs +++ b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEnumerations.cs @@ -977,6 +977,42 @@ namespace Tizen.Network.Bluetooth } /// + /// Enumeration for the player command. + /// + /// 8 + public enum PlayerCommand + { + /// + /// Play current track + /// + Play = 1, + /// + /// Pause current track + /// + Pause, + /// + /// Stop playing track + /// + Stop, + /// + /// Go to the next track + /// + Next, + /// + /// Go to the previous track + /// + Previous, + /// + /// Fast-forward current track + /// + FastForward, + /// + /// Rewind current track + /// + Rewind + } + + /// /// Enumeration for the Bluetooth LE device address types. /// /// 3 diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEventArgs.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEventArgs.cs index 114de7b..2db5636 100644 --- a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEventArgs.cs +++ b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothEventArgs.cs @@ -392,13 +392,7 @@ namespace Tizen.Network.Bluetooth /// A value indicating whether the device is connected. /// /// 3 - public bool IsConnected - { - get - { - return _isConnected; - } - } + public bool IsConnected => _isConnected; /// /// The device connection data. @@ -956,6 +950,121 @@ namespace Tizen.Network.Bluetooth } /// + /// An extended EventArgs class contains the connection state and the remote device address. + /// + /// 8 + public class AvrcpControlConnectionChangedEventArgs : EventArgs + { + private bool _isConnected; + string _remoteAddress; + // Setting Values when Event is invoked + internal AvrcpControlConnectionChangedEventArgs(bool conn, string address) + { + _isConnected = conn; + _remoteAddress = address; + } + + /// + /// A value indicating whether this instance is connected. + /// + /// 8 + public bool IsConnected + { + get + { + return _isConnected; + } + } + + /// + /// The remote address. + /// + /// 8 + public string RemoteAddress + { + get + { + return _remoteAddress; + } + } + } + + /// + /// An extended EventArgs class contains the position. + /// + /// 8 + public class PositionChangedEventArgs : EventArgs + { + private uint _pos; + internal PositionChangedEventArgs(uint pos) + { + _pos = pos; + } + + /// + /// The current position in milliseconds. + /// + /// 8 + public uint Position + { + get + { + return _pos; + } + } + } + + /// + /// An extended EventArgs class contains the play state. + /// + /// 8 + public class PlayStateChangedEventArgs : EventArgs + { + private PlayerState _playState; + internal PlayStateChangedEventArgs(PlayerState playState) + { + _playState = playState; + } + + /// + /// The current play state. + /// + /// 8 + public PlayerState PlayState + { + get + { + return _playState; + } + } + } + + /// + /// An extended EventArgs class contains the play state. + /// + /// 8 + public class TrackInfoChangedEventArgs : EventArgs + { + private Track _track = new Track(); + internal TrackInfoChangedEventArgs(Track Data) + { + _track = Data; + } + + /// + /// The current track data + /// + /// 8 + public Track TrackData + { + get + { + return _track; + } + } + } + + /// /// An extended EventArgs class contains the changed Bluetooth LE advertising state changed information. /// /// 3 diff --git a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothStructs.cs b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothStructs.cs index ad5f49d..1370707 100644 --- a/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothStructs.cs +++ b/src/Tizen.Network.Bluetooth/Tizen.Network.Bluetooth/BluetoothStructs.cs @@ -225,6 +225,21 @@ namespace Tizen.Network.Bluetooth internal IntPtr data; } + [StructLayout(LayoutKind.Sequential)] + internal struct TrackInfoStruct + { + [MarshalAsAttribute(UnmanagedType.LPStr)] + internal string Title; + [MarshalAsAttribute(UnmanagedType.LPStr)] + internal string Artist; + [MarshalAsAttribute(UnmanagedType.LPStr)] + internal string Album; + [MarshalAsAttribute(UnmanagedType.LPStr)] + internal string Genre; + internal uint total_tracks; + internal uint number; + internal uint duration; + } internal static class BluetoothUtils { internal static BluetoothDevice ConvertStructToDeviceClass(BluetoothDeviceStruct device) -- 2.7.4