--- /dev/null
+/*
+ * Copyright (c) 2016 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.CompilerServices;
+using Tizen;
+
+internal static partial class Interop
+{
+ internal enum ErrorCode
+ {
+ None = Tizen.Internals.Errors.ErrorCode.None,
+ OutOfMemory = Tizen.Internals.Errors.ErrorCode.OutOfMemory,
+ InvalidParameter = Tizen.Internals.Errors.ErrorCode.InvalidParameter,
+ InvalidOperation = Tizen.Internals.Errors.ErrorCode.InvalidOperation,
+ PermissionDenied = Tizen.Internals.Errors.ErrorCode.PermissionDenied,
+ NotSupported = Tizen.Internals.Errors.ErrorCode.NotSupported,
+
+ InvalidState = -0x019A0000 | 0x01, // RADIO_ERROR_INVALID_STATE
+ SoundPolicy = -0x019A0000 | 0x02, // RADIO_ERROR_SOUND_POLICY
+ NoAntenna = -0x019A0000 | 0x03, // RADIO_ERROR_NO_ANTENNA
+ }
+}
+
+internal static class ErrorCodeExtensions
+{
+ private const string LogTag = "Tizen.Multimedia";
+
+ internal static bool IsSuccess(this Interop.ErrorCode err)
+ {
+ return err == Interop.ErrorCode.None;
+ }
+
+ internal static bool IsFailed(this Interop.ErrorCode err)
+ {
+ return !err.IsSuccess();
+ }
+
+ /// <summary>
+ /// Utility method to check for error, returns false if failed and print warning messages
+ /// </summary>
+ /// <returns>true in case of no error, false otherwise</returns>
+ internal static bool WarnIfFailed(this Interop.ErrorCode err, string msg, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ if (err.IsFailed())
+ {
+ Log.Debug(LogTag, $"{msg}, err: {err.ToString()}", file, func, line);
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Utility method to check for error, returns false if failed and throw exception
+ /// </summary>
+ /// <returns>true in case of no error</returns>
+ internal static bool ThrowIfFailed(this Interop.ErrorCode err, string msg, [CallerFilePath] string file = "", [CallerMemberName] string func = "", [CallerLineNumber] int line = 0)
+ {
+ if (err.IsFailed())
+ {
+ Log.Error(LogTag, $"{msg}, err: {err.ToString()}", file, func, line);
+ throw err.GetException(msg);
+ }
+ return true;
+ }
+
+ internal static Exception GetException(this Interop.ErrorCode err, string message)
+ {
+ string errMessage = $"{message}, err: {err.ToString()}";
+ switch (err)
+ {
+ //case ErrorCode.None:
+ case Interop.ErrorCode.PermissionDenied: return new UnauthorizedAccessException(errMessage);
+ case Interop.ErrorCode.InvalidParameter: return new ArgumentException(errMessage);
+ case Interop.ErrorCode.OutOfMemory: return new OutOfMemoryException(errMessage);
+ case Interop.ErrorCode.NotSupported: return new NotSupportedException(errMessage);
+ case Interop.ErrorCode.NoAntenna: return new NotSupportedException(errMessage);
+ case Interop.ErrorCode.InvalidOperation:
+ case Interop.ErrorCode.InvalidState:
+ case Interop.ErrorCode.SoundPolicy:
+ default: return new InvalidOperationException(errMessage);
+ }
+ }
+}
\ No newline at end of file
public const string Libc = "libc.so.6";
public const string Camera = "libcapi-media-camera.so.0";
public const string StreamRecorder = "libcapi-media-streamrecorder.so.0";
+ public const string Radio = "libcapi-media-radio.so.0";
public const string ThumbnailExtractor = "libcapi-media-thumbnail-util.so";
public const string WavPlayer = "libcapi-media-wav-player.so.0";
public const string TonePlayer = "libcapi-media-tone-player.so.0";
--- /dev/null
+/*
+ * Copyright (c) 2016 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;
+
+internal static partial class Interop
+{
+ internal enum RadioState
+ {
+ Ready, // RADIO_STATE_READY
+ Playing, // RADIO_STATE_PLAYING
+ Scanning, // RADIO_STATE_SCANNING
+ }
+
+ internal enum RadioInterruptedReason
+ {
+ Completed, // RADIO_INTERRUPTED_COMPLETED
+ Media, // RADIO_INTERRUPTED_BY_MEDIA
+ Call, // RADIO_INTERRUPTED_BY_CALL
+ EarjackUnplug, // RADIO_INTERRUPTED_BY_EARJACK_UNPLUG
+ ResourceConflict, // RADIO_INTERRUPTED_BY_RESOURCE_CONFLICT
+ Alarm, // RADIO_INTERRUPTED_BY_ALARM
+ Emergency, // RADIO_INTERRUPTED_BY_EMERGENCY
+ ResumableMedia, // RADIO_INTERRUPTED_BY_RESUMABLE_MEDIA
+ Notification, // RADIO_INTERRUPTED_BY_NOTIFICATION
+ }
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_state")]
+ internal static extern ErrorCode GetState(this RadioHandle /* radio_h */ radio, out RadioState /* radio_state_e */ state);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_start")]
+ internal static extern ErrorCode Start(this RadioHandle /* radio_h */ radio);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_stop")]
+ internal static extern ErrorCode Stop(this RadioHandle /* radio_h */ radio);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_seek_up")]
+ internal static extern ErrorCode SeekUp(this RadioHandle /* radio_h */ radio, RadioHandle.SeekCompletedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_seek_down")]
+ internal static extern ErrorCode SeekDown(this RadioHandle /* radio_h */ radio, RadioHandle.SeekCompletedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_set_frequency")]
+ internal static extern ErrorCode SetFrequency(this RadioHandle /* radio_h */ radio, int frequency);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_frequency")]
+ internal static extern ErrorCode GetFrequency(this RadioHandle /* radio_h */ radio, out int frequency);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_signal_strength")]
+ internal static extern ErrorCode GetSignalStrength(this RadioHandle /* radio_h */ radio, out int strength);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_scan_start")]
+ internal static extern ErrorCode ScanStart(this RadioHandle /* radio_h */ radio, RadioHandle.ScanUpdatedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_scan_stop")]
+ internal static extern ErrorCode ScanStop(this RadioHandle /* radio_h */ radio, RadioHandle.ScanStoppedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_set_mute")]
+ internal static extern ErrorCode SetMute(this RadioHandle /* radio_h */ radio, bool muted);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_is_muted")]
+ internal static extern ErrorCode GetMuted(this RadioHandle /* radio_h */ radio, out bool muted);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_set_scan_completed_cb")]
+ internal static extern ErrorCode SetScanCompletedCb(this RadioHandle /* radio_h */ radio, RadioHandle.ScanCompletedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_unset_scan_completed_cb")]
+ internal static extern ErrorCode UnsetScanCompletedCb(this RadioHandle /* radio_h */ radio);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_set_interrupted_cb")]
+ internal static extern ErrorCode SetInterruptedCb(this RadioHandle /* radio_h */ radio, RadioHandle.InterruptedCallback callback, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_unset_interrupted_cb")]
+ internal static extern ErrorCode UnsetInterruptedCb(this RadioHandle /* radio_h */ radio);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_frequency_range")]
+ internal static extern ErrorCode GetFrequencyRange(this RadioHandle /* radio_h */ radio, out int minFreq, out int maxFreq);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_channel_spacing")]
+ internal static extern ErrorCode GetChannelSpacing(this RadioHandle /* radio_h */ radio, out int channelSpacing);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_set_volume")]
+ internal static extern ErrorCode SetVolume(this RadioHandle /* radio_h */ radio, float volume);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_get_volume")]
+ internal static extern ErrorCode GetVolume(this RadioHandle /* radio_h */ radio, out float volume);
+
+ internal struct RadioFrequencyRange
+ {
+ public int minFreq;
+ public int maxFreq;
+ }
+
+ internal class RadioHandle : SafeMultimediaHandle
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void SeekCompletedCallback(int frequency, IntPtr /* void */ userData);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ScanUpdatedCallback(int frequency, IntPtr /* void */ userData);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ScanStoppedCallback(IntPtr /* void */ userData);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ScanCompletedCallback(IntPtr /* void */ userData);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void InterruptedCallback(RadioInterruptedReason /* radio_interrupted_code_e */ code, IntPtr /* void */ userData);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_create")]
+ internal static extern ErrorCode Create(out IntPtr /* radio_h */ radio);
+
+ [DllImport(Libraries.Radio, EntryPoint = "radio_destroy")]
+ internal static extern ErrorCode Destroy(IntPtr /* radio_h */ radio);
+
+ internal RadioHandle(IntPtr handle, bool needToRelease) : base(handle, needToRelease)
+ {
+ }
+
+ internal RadioHandle() : this(CreateNativeHandle(), true)
+ {
+ }
+
+ internal static IntPtr CreateNativeHandle()
+ {
+ IntPtr handle;
+ Create(out handle).ThrowIfFailed("Failed to create native handle");
+ return handle;
+ }
+
+ internal override ErrorCode DisposeNativeHandle()
+ {
+ ScanCompleteCb = null;
+ InteruptedCb = null;
+ return Destroy(handle);
+ }
+
+ internal RadioState State
+ {
+ get { return NativeGet<RadioState>(this.GetState); }
+ }
+
+ internal int Frequency
+ {
+ get { return NativeGet<int>(this.GetFrequency); }
+ set { NativeSet(this.SetFrequency, value); }
+ }
+
+ internal int SignalStrength
+ {
+ get { return NativeGet<int>(this.GetSignalStrength); }
+ }
+
+ internal bool IsMuted
+ {
+ get { return NativeGet<bool>(this.GetMuted); }
+ set { NativeSet(this.SetMute, value); }
+ }
+
+ internal int ChannelSpacing
+ {
+ get { return NativeGet<int>(this.GetChannelSpacing); }
+ }
+
+ internal float Volume
+ {
+ get { return NativeGet<float>(this.GetVolume); }
+ set { NativeSet(this.SetVolume, value); }
+ }
+
+ internal int MinimumFrequency
+ {
+ get
+ {
+ RadioFrequencyRange range;
+ this.GetFrequencyRange(out range.minFreq, out range.maxFreq);
+ return range.minFreq;
+ }
+ }
+
+ internal int MaximumFrequency
+ {
+ get
+ {
+ RadioFrequencyRange range;
+ this.GetFrequencyRange(out range.minFreq, out range.maxFreq);
+ return range.maxFreq;
+ }
+ }
+
+ internal ScanCompletedCallback ScanCompleteCb
+ {
+ set
+ {
+ var err = (value != null) ? this.SetScanCompletedCb(value, IntPtr.Zero) : this.UnsetScanCompletedCb();
+ err.ThrowIfFailed("Failed to set/ unset scan complete callback");
+ }
+ }
+
+ internal InterruptedCallback InteruptedCb
+ {
+ set
+ {
+ var err = (value != null) ? this.SetInterruptedCb(value, IntPtr.Zero) : this.UnsetInterruptedCb();
+ err.ThrowIfFailed("Failed to set/ unset interrupted callback");
+ }
+ }
+
+ internal void StartPlayback()
+ {
+ this.Start().ThrowIfFailed("Failed to start radio");
+ }
+
+ internal void StopPlayback()
+ {
+ this.Stop().ThrowIfFailed("Failed to stop radio");
+ }
+
+ internal void StartScan(ScanUpdatedCallback callback)
+ {
+ this.ScanStart(callback, IntPtr.Zero).ThrowIfFailed("Failed to start radio");
+ }
+
+ internal void StopScan(ScanStoppedCallback callback)
+ {
+ this.ScanStop(callback, IntPtr.Zero).ThrowIfFailed("Failed to stop radio");
+ }
+
+ internal void SeekUp(SeekCompletedCallback callback)
+ {
+ this.SeekUp(callback, IntPtr.Zero).ThrowIfFailed("Failed to start radio");
+ }
+
+ internal void SeekDown(SeekCompletedCallback callback)
+ {
+ this.SeekDown(callback, IntPtr.Zero).ThrowIfFailed("Failed to stop radio");
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+internal static partial class Interop
+{
+ internal static Task<T> PinnedTask<T>(TaskCompletionSource<T> tcs)
+ {
+ var gch = GCHandle.Alloc(tcs);
+ return tcs.Task.ContinueWith(
+ t => { gch.Free(); return t; },
+ TaskContinuationOptions.ExecuteSynchronously).Unwrap();
+ }
+
+ internal abstract class SafeMultimediaHandle : SafeHandle
+ {
+ internal delegate ErrorCode GetterMethod<TProp>(out TProp value);
+ internal delegate ErrorCode SetterMethod<TProp>(TProp value);
+
+ protected SafeMultimediaHandle(IntPtr handle, bool needToRelease) : base(handle, true)
+ {
+ Debug.Assert(handle != IntPtr.Zero);
+ HasOwnership = needToRelease;
+ }
+
+ internal bool HasOwnership { get; set; }
+ public override bool IsInvalid { get { return handle == IntPtr.Zero; } }
+
+ internal abstract ErrorCode DisposeNativeHandle();
+
+ internal TProp NativeGet<TProp>(GetterMethod<TProp> getter, [CallerMemberName] string propertyName = "")
+ {
+ TProp value; getter(out value).ThrowIfFailed($"Failed to get {propertyName}");
+ return value;
+ }
+
+ internal string NativeGet(GetterMethod<string> getter, [CallerMemberName] string propertyName = "")
+ {
+ string value; getter(out value).ThrowIfFailed($"Failed to get {propertyName}");
+ return value;
+ }
+
+ internal void NativeSet<TProp>(SetterMethod<TProp> setter, TProp value, [CallerMemberName] string propertyName = "")
+ {
+ setter(value).ThrowIfFailed($"Failed to set {propertyName}");
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ var err = ErrorCode.None;
+ if (HasOwnership)
+ {
+ err = DisposeNativeHandle();
+ err.WarnIfFailed($"Failed to delete native {GetType()} handle");
+ }
+
+ SetHandle(IntPtr.Zero);
+ return err.IsSuccess();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 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.CompilerServices;
+using System.Threading.Tasks;
+
+namespace Tizen.Multimedia
+{
+ /// <summary>
+ /// Radio class, provides support for using the Radio feature
+ /// </summary>
+ public class Radio : IDisposable
+ {
+ internal Interop.RadioHandle _handle;
+
+ /// <summary>
+ /// Radio constructor
+ /// </summary>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="NotSupportedException">This is thrown if Radio feature is not supported</exception>
+ /// <exception cref="InvalidOperationException"></exception>
+ public Radio()
+ {
+ _handle = new Interop.RadioHandle();
+ _handle.ScanCompleteCb = ScanCompleteCallback;
+ _handle.InteruptedCb = PlaybackIntruptedCallback;
+ }
+
+ /// <summary>
+ /// Scan update event, to be triggered when radio scan information is updated
+ /// </summary>
+ public event EventHandler<ScanUpdatedEventArgs> ScanInformationUpdated;
+
+ /// <summary>
+ /// Scan stopped event, to be triggered when radio scanning stops
+ /// </summary>
+ public event EventHandler ScanStopped;
+
+ /// <summary>
+ /// Scan complete event, to be triggered when radio scan is completed
+ /// </summary>
+ public event EventHandler ScanCompleted;
+
+ /// <summary>
+ /// Playback interrupted event, to be triggered when radio playback is interrupted
+ /// </summary>
+ public event EventHandler<RadioInterruptedEventArgs> PlaybackInterrupted;
+
+ /// <summary>
+ /// Current state for the radio
+ /// </summary>
+ public RadioState State
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return (RadioState)_handle.State;
+ }
+ }
+
+ /// <summary>
+ /// Current radio frequency, in [87500 ~ 108000] (kHz) range
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">This is thrown if value passed to setter in not in valid range</exception>
+ public int Frequency
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.Frequency;
+ }
+ set
+ {
+ ValidateObjectNotDisposed();
+ ValidateInputRangeForPropertySetter(value, 87500, 108000);
+ _handle.Frequency = value;
+ }
+ }
+
+ /// <summary>
+ /// Current signal strength, in [-128 ~ 128] (dBm) range
+ /// </summary>
+ public int SignalStrength
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.SignalStrength;
+ }
+ }
+
+ /// <summary>
+ /// Indicates if radio is muted
+ /// </summary>
+ public bool IsMuted
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.IsMuted;
+ }
+ set
+ {
+ ValidateObjectNotDisposed();
+ _handle.IsMuted = value;
+ }
+ }
+
+ /// <summary>
+ /// Channel spacing for current region
+ /// </summary>
+ public int ChannelSpacing
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.ChannelSpacing;
+ }
+ }
+
+ /// <summary>
+ /// Current radio volume level, in [0.0 ~ 1.0](1.0 = 100%) range
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">This is thrown if value passed to setter in not in valid range</exception>
+ public float Volume
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.Volume;
+ }
+ set
+ {
+ ValidateObjectNotDisposed();
+ ValidateInputRangeForPropertySetter(value, 0.0, 1.0);
+ _handle.Volume = value;
+ }
+ }
+
+ /// <summary>
+ /// Minimum frequency for the region, in [87500 ~ 108000] (kHz) range
+ /// </summary>
+ public int MinimumFrequency
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.MinimumFrequency;
+ }
+ }
+
+ /// <summary>
+ /// Maximum frequency for the region, in [87500 ~ 108000] (kHz) range
+ /// </summary>
+ public int MaximumFrequency
+ {
+ get
+ {
+ ValidateObjectNotDisposed();
+ return _handle.MaximumFrequency;
+ }
+ }
+
+ /// <summary>
+ /// Starts radio playback
+ /// </summary>
+ /// <remarks>This method can be called if Radio is in Ready state. This method will move Radio to Playing state</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Ready state</exception>
+ public void StartPlayback()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State == RadioState.Ready);
+ _handle.StartPlayback();
+ }
+
+ /// <summary>
+ /// Stops radio playback
+ /// </summary>
+ /// <remarks>This method can be called if Radio is in Playing state. This method will move Radio to Ready state</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+ public void StopPlayback()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State == RadioState.Playing);
+ _handle.StopPlayback();
+ }
+
+ /// <summary>
+ /// Starts radio scan, will trigger ScanInformationUpdated event, when scan information is updated
+ /// </summary>
+ /// <remarks>This method should not be called if Radio is in Scanning state. This method will move Radio to Scanning state</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is already in Scanning state</exception>
+ public void StartScan()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State != RadioState.Scanning);
+ _handle.StartScan(ScanUpdateCallback);
+ }
+
+ /// <summary>
+ /// Stops radio scan, will trigger ScanStopped event, once complete
+ /// </summary>
+ /// <remarks>This method should be called only if Radio is in Scanning state</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Scanning state</exception>
+ public void StopScan()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State == RadioState.Scanning);
+ _handle.StopScan(ScanStoppedCallback);
+ }
+
+ /// <summary>
+ /// Seeks up the effective frequency of the radio
+ /// </summary>
+ /// <returns>Current frequency, in range [87500 ~ 108000] (kHz)</returns>
+ /// <remarks>Radio must be in Playing state to use this API</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+ public Task<int> SeekUpAsync()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State == RadioState.Playing);
+
+ TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+ Interop.RadioHandle.SeekCompletedCallback callback = (currentFrequency, userData) =>
+ {
+ tcs.TrySetResult(currentFrequency);
+ };
+
+ _handle.SeekUp(callback);
+ return Interop.PinnedTask(tcs);
+ }
+
+ /// <summary>
+ /// Seeks down the effective frequency of the radio
+ /// </summary>
+ /// <returns>Current frequency, in range [87500 ~ 108000] (kHz)</returns>
+ /// <remarks>Radio must be in Playing state to use this API</remarks>
+ /// <exception cref="InvalidOperationException">This is thrown if Radio is not in Playing state</exception>
+ public Task<int> SeekDownAsync()
+ {
+ ValidateObjectNotDisposed();
+ ValidateRadioState(() => State == RadioState.Playing);
+
+ TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
+ Interop.RadioHandle.SeekCompletedCallback callback = (currentFrequency, userData) =>
+ {
+ tcs.TrySetResult(currentFrequency);
+ };
+
+ _handle.SeekDown(callback);
+ return Interop.PinnedTask(tcs);
+ }
+
+ private void ScanUpdateCallback(int frequency, IntPtr data)
+ {
+ ScanInformationUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency));
+ }
+
+ private void ScanStoppedCallback(IntPtr data)
+ {
+ ScanStopped?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void ScanCompleteCallback(IntPtr data)
+ {
+ ScanCompleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void PlaybackIntruptedCallback(Interop.RadioInterruptedReason reason, IntPtr data)
+ {
+ PlaybackInterrupted?.Invoke(this, new RadioInterruptedEventArgs((RadioInterruptedReason)reason));
+ }
+
+ private void ValidateInputRangeForPropertySetter<T>(T input, T min, T max, [CallerMemberName] string member = "") where T : IComparable<T>
+ {
+ if (min.CompareTo(input) == 1 || max.CompareTo(input) == -1)
+ {
+ throw new ArgumentOutOfRangeException(member, input, $"Valid Range [{min} - {max}]");
+ }
+ }
+
+ private void ValidateRadioState(Func<bool> stateVerifier, [CallerMemberName] string member = "")
+ {
+ if (stateVerifier() == false)
+ {
+ throw new InvalidOperationException($"{State} is not valid state to call {member}. Please check API documentation");
+ }
+ }
+
+ private void ValidateObjectNotDisposed()
+ {
+ if (_disposedValue)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+ }
+
+ #region IDisposable Support
+ private bool _disposedValue = false;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ _handle.Dispose();
+ _disposedValue = true;
+ }
+ }
+
+ ~Radio()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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;
+
+namespace Tizen.Multimedia
+{
+ /// <summary>
+ /// Arguments for radio Playback Interrupted events
+ /// </summary>
+ public class RadioInterruptedEventArgs : EventArgs
+ {
+ internal RadioInterruptedEventArgs(RadioInterruptedReason reason)
+ {
+ Reason = reason;
+ }
+
+ /// <summary>
+ /// Tuned radio frequency, in range [87500 ~ 108000] (kHz)
+ /// </summary>
+ public RadioInterruptedReason Reason { get; }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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.
+ */
+
+namespace Tizen.Multimedia
+{
+ /// <summary>
+ /// Radio Interrupted Reason
+ /// </summary>
+ public enum RadioInterruptedReason
+ {
+ /// <summary>
+ /// Playback interrupted by a resource conflict
+ /// </summary>
+ ResourceConflict = Interop.RadioInterruptedReason.ResourceConflict,
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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.
+ */
+
+namespace Tizen.Multimedia
+{
+ /// <summary>
+ /// Radio State
+ /// </summary>
+ public enum RadioState
+ {
+ /// <summary>
+ /// Ready to play or scan
+ /// </summary>
+ Ready = Interop.RadioState.Ready,
+ /// <summary>
+ /// Playing audio from the tuner
+ /// </summary>
+ Playing = Interop.RadioState.Playing,
+ /// <summary>
+ /// Scanning/ searching for the next station for signal
+ /// </summary>
+ Scanning = Interop.RadioState.Scanning,
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 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;
+
+namespace Tizen.Multimedia
+{
+ /// <summary>
+ /// Arguments for radio scan update events
+ /// </summary>
+ public class ScanUpdatedEventArgs : EventArgs
+ {
+ internal ScanUpdatedEventArgs(int tunedFrequency)
+ {
+ Frequency = tunedFrequency;
+ }
+
+ /// <summary>
+ /// Tuned radio frequency, in range [87500 ~ 108000] (kHz)
+ /// </summary>
+ public int Frequency { get; }
+ }
+}
\ No newline at end of file