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);
[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)]
--- /dev/null
+/*
+ * 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
+{
+ /// <summary>
+ /// This class is used to send commands from the control device (For example, headset) to the target device (For example, media player).
+ /// </summary>
+ /// <remarks>
+ /// This class can be obtained from BluetoothDevice.GetProfile method.
+ /// </remarks>
+ /// <privilege> http://tizen.org/privilege/bluetooth </privilege>
+ /// <feature> http://tizen.org/feature/network.bluetooth.audio.controller </feature>
+ /// <since_tizen> 8 </since_tizen>
+ public class BluetoothAvrcpControl : BluetoothProfile
+ {
+ private TaskCompletionSource<bool> _taskForConnection;
+ private TaskCompletionSource<bool> _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);
+ }
+
+ /// <summary>
+ /// The AvrcpControlConnectionChangedEventArgs event is invoked when the connection status of device is changed.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public event EventHandler<AvrcpControlConnectionChangedEventArgs> ConnectionStateChanged;
+
+ /// <summary>
+ /// The PositionChangedEventArgs event is invoked when the play position of a track is changed.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public event EventHandler<PositionChangedEventArgs> PositionChanged;
+
+ /// <summary>
+ /// The PlayStateChangedEventArgs event is invoked when the play state of a track gets changed.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public event EventHandler<PlayStateChangedEventArgs> PlayStateChanged;
+
+ /// <summary>
+ /// The TrackInfoChangedEventArgs event is invoked when info of a track gets changed.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public event EventHandler<TrackInfoChangedEventArgs> TrackInfoChanged;
+
+ /// <summary>
+ /// Asynchronously connects the remote device
+ /// </summary>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the method fails</exception>
+ /// <since_tizen> 8 </since_tizen>
+ public Task ConnectAsync()
+ {
+ if (_taskForConnection != null && !_taskForConnection.Task.IsCompleted)
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress);
+ }
+
+ _taskForConnection = new TaskCompletionSource<bool>();
+ BluetoothAvrcpControlImpl.Instance.Connect(RemoteAddress);
+ return _taskForConnection.Task;
+ }
+
+ /// <summary>
+ /// Asynchronously disconnects the remote device
+ /// </summary>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the method fails</exception>
+ /// <since_tizen> 8 </since_tizen>
+ public Task DisconnectAsync()
+ {
+ if (_taskForDisconnection != null && !_taskForDisconnection.Task.IsCompleted)
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NowInProgress);
+ }
+ _taskForDisconnection = new TaskCompletionSource<bool>();
+ BluetoothAvrcpControlImpl.Instance.Disconnect(RemoteAddress);
+ return _taskForDisconnection.Task;
+ }
+
+ /// <summary>
+ /// A property for the equalizer mode of target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when notifying the repeat mode state to the remote device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public EqualizerState EqualizerState
+ {
+ get
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetEqualizerState();
+ }
+ else
+ {
+ return EqualizerState.Off;
+ }
+ }
+ set
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SetEqualizerState(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// A property for the repeat mode of target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when notifying the repeat mode state to the remote device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public RepeatMode RepeatMode
+ {
+ get
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetRepeatMode();
+ }
+ else
+ {
+ return RepeatMode.Off;
+ }
+ }
+ set
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SetRepeatMode(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// A property for the suffle mode of target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or if retrieving scan mode of the remote device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public ShuffleMode ShuffleMode
+ {
+ get
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetShuffleMode();
+ }
+ else
+ {
+ return ShuffleMode.Off;
+ }
+ }
+ set
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SetShuffleMode(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// A property for the scan mode of target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or if retrieving scan mode of the remote device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public ScanMode ScanMode
+ {
+ get
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetScanMode();
+ }
+ else
+ {
+ return ScanMode.Off;
+ }
+ }
+ set
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SetScanMode(value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets position of the track being played on the target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <returns> Play position of the track being played on the target device </returns>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or if there is an error retrieving the position of the track that is currently being played.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public uint GetPosition()
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetPosition();
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ return 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets player state of the target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <returns> Play status of the target device </returns>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or if there happens to be an error while retrieving the player state of the target device.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public PlayerState GetPlayStatus()
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetPlayStatus();
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ return PlayerState.Stopped;
+ }
+ }
+
+ /// <summary>
+ /// Gets info of the track being played on the target device.
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <returns> Info of the track being played on the target device </returns>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">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.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public Track GetTrackInfo()
+ {
+ if (_isConnected == true)
+ {
+ return BluetoothAvrcpControlImpl.Instance.GetTrackInfo();
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Sends a particular play command to the target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <param name="command">Command to be sent</param>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when sending command to the target device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ public void SendPlayerCommand(PlayerCommand command)
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SendPlayerCommand(command);
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ }
+ }
+
+ /// <summary>
+ /// Sends a play command to a particular target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected.
+ /// </remarks>
+ /// <param name="command">Command to be sent</param>
+ /// <param name="remoteAddress">Address of the device to send command</param>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported.</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when sending command to the target device fails.
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SendPlayerCommandTo(PlayerCommand command, string remoteAddress)
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SendPlayerCommandTo(command, remoteAddress);
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ }
+ }
+
+ /// <summary>
+ /// Sets absolute volume of target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected
+ /// </remarks>
+ /// <param name="volume">The volume level to be set</param>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when setting absolute volume of the target device fails
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SetAbsoluteVolume(uint volume)
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.SetAbsoluteVolume(volume);
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ }
+ }
+
+ /// <summary>
+ /// Increases volume of target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when increasing volume of the target device fails
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void IncreaseVolume()
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.IncreaseVolume();
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ }
+ }
+
+ /// <summary>
+ /// Decreases volume of target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected
+ /// </remarks>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when decreasing volume of the target device fails
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void DecreaseVolume()
+ {
+ if (_isConnected == true)
+ {
+ BluetoothAvrcpControlImpl.Instance.DecreaseVolume();
+ }
+ else
+ {
+ BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.RemoteDeviceNotConnected);
+ }
+ }
+
+ /// <summary>
+ /// Sends delay report to the target device
+ /// </summary>
+ /// <remarks>
+ /// The remote device must be connected
+ /// </remarks>
+ /// <param name="delay">Delay to be sent to target</param>
+ /// <exception cref="NotSupportedException">Thrown when the Bluetooth is not supported</exception>
+ /// <exception cref="InvalidOperationException">Thrown when the Bluetooth is not enabled
+ /// or when sending delay to the target device fails
+ /// </exception>
+ /// <since_tizen> 8 </since_tizen>
+ [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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<PositionChangedEventArgs> _positionChanged;
+ private event EventHandler<PlayStateChangedEventArgs> _playStateChanged;
+ private event EventHandler<TrackInfoChangedEventArgs> _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<AvrcpControlConnectionChangedEventArgs> ConnectionChanged;
+
+ internal event EventHandler<PositionChangedEventArgs> PositionChanged
+ {
+ add
+ {
+ if (_positionChanged == null)
+ {
+ RegisterPositionChangedEvent();
+ }
+ _positionChanged += value;
+ }
+ remove
+ {
+ _positionChanged -= value;
+ if (_positionChanged == null)
+ {
+ UnregisterPositionChangedEvent();
+ }
+ }
+ }
+
+ internal event EventHandler<PlayStateChangedEventArgs> PlayStateChanged
+ {
+ add
+ {
+ if (_playStateChanged == null)
+ {
+ RegisterPlayStateChangedEvent();
+ }
+ _playStateChanged += value;
+ }
+ remove
+ {
+ _playStateChanged -= value;
+ if (_playStateChanged == null)
+ {
+ UnregisterPlayStateChangedEvent();
+ }
+ }
+ }
+
+ internal event EventHandler<TrackInfoChangedEventArgs> 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;
+ }
+ }
+}
}
/// <summary>
+ /// Enumeration for the player command.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public enum PlayerCommand
+ {
+ /// <summary>
+ /// Play current track
+ /// </summary>
+ Play = 1,
+ /// <summary>
+ /// Pause current track
+ /// </summary>
+ Pause,
+ /// <summary>
+ /// Stop playing track
+ /// </summary>
+ Stop,
+ /// <summary>
+ /// Go to the next track
+ /// </summary>
+ Next,
+ /// <summary>
+ /// Go to the previous track
+ /// </summary>
+ Previous,
+ /// <summary>
+ /// Fast-forward current track
+ /// </summary>
+ FastForward,
+ /// <summary>
+ /// Rewind current track
+ /// </summary>
+ Rewind
+ }
+
+ /// <summary>
/// Enumeration for the Bluetooth LE device address types.
/// </summary>
/// <since_tizen> 3 </since_tizen>
/// A value indicating whether the device is connected.
/// </summary>
/// <since_tizen> 3 </since_tizen>
- public bool IsConnected
- {
- get
- {
- return _isConnected;
- }
- }
+ public bool IsConnected => _isConnected;
/// <summary>
/// The device connection data.
}
/// <summary>
+ /// An extended EventArgs class contains the connection state and the remote device address.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ 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;
+ }
+
+ /// <summary>
+ /// A value indicating whether this instance is connected.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public bool IsConnected
+ {
+ get
+ {
+ return _isConnected;
+ }
+ }
+
+ /// <summary>
+ /// The remote address.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public string RemoteAddress
+ {
+ get
+ {
+ return _remoteAddress;
+ }
+ }
+ }
+
+ /// <summary>
+ /// An extended EventArgs class contains the position.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public class PositionChangedEventArgs : EventArgs
+ {
+ private uint _pos;
+ internal PositionChangedEventArgs(uint pos)
+ {
+ _pos = pos;
+ }
+
+ /// <summary>
+ /// The current position in milliseconds.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public uint Position
+ {
+ get
+ {
+ return _pos;
+ }
+ }
+ }
+
+ /// <summary>
+ /// An extended EventArgs class contains the play state.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public class PlayStateChangedEventArgs : EventArgs
+ {
+ private PlayerState _playState;
+ internal PlayStateChangedEventArgs(PlayerState playState)
+ {
+ _playState = playState;
+ }
+
+ /// <summary>
+ /// The current play state.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public PlayerState PlayState
+ {
+ get
+ {
+ return _playState;
+ }
+ }
+ }
+
+ /// <summary>
+ /// An extended EventArgs class contains the play state.
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public class TrackInfoChangedEventArgs : EventArgs
+ {
+ private Track _track = new Track();
+ internal TrackInfoChangedEventArgs(Track Data)
+ {
+ _track = Data;
+ }
+
+ /// <summary>
+ /// The current track data
+ /// </summary>
+ /// <since_tizen> 8 </since_tizen>
+ public Track TrackData
+ {
+ get
+ {
+ return _track;
+ }
+ }
+ }
+
+ /// <summary>
/// An extended EventArgs class contains the changed Bluetooth LE advertising state changed information.
/// </summary>
/// <since_tizen> 3 </since_tizen>
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)