Release 4.0.0-preview1-00201
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Radio / Radio / Radio.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Linq;
19 using System.Threading.Tasks;
20 using Tizen.System;
21 using static Tizen.Multimedia.Interop.Radio;
22
23 namespace Tizen.Multimedia
24 {
25     /// <summary>
26     /// Provides a means for using the radio feature.
27     /// </summary>
28     public class Radio : IDisposable
29     {
30         private Interop.RadioHandle _handle;
31
32         private const string FeatureFmRadio = "http://tizen.org/feature/fmradio";
33
34         /// <summary>
35         /// Initializes a new instance of the Radio class.
36         /// </summary>
37         /// <exception cref="NotSupportedException">The radio feature is not supported.</exception>
38         public Radio()
39         {
40             ValidateFeatureSupported(FeatureFmRadio);
41
42             Create(out _handle);
43
44             try
45             {
46                 SetScanCompletedCb(_handle, ScanCompleteCallback).ThrowIfFailed("Failed to initialize radio");
47                 SetInterruptedCb(_handle, InterruptedCallback).ThrowIfFailed("Failed to initialize radio");
48             }
49             catch (Exception)
50             {
51                 _handle.Dispose();
52                 throw;
53             }
54         }
55
56         private Interop.RadioHandle Handle
57         {
58             get
59             {
60                 if (_disposed)
61                 {
62                     throw new ObjectDisposedException(GetType().Name);
63                 }
64                 return _handle;
65             }
66         }
67
68         /// <summary>
69         /// Occurs when the radio scanning information is updated.
70         /// </summary>
71         public event EventHandler<ScanUpdatedEventArgs> ScanUpdated;
72
73         /// <summary>
74         /// Occurs when the radio scanning stops.
75         /// </summary>
76         public event EventHandler ScanStopped;
77
78         /// <summary>
79         /// Occurs when the radio scanning is completed.
80         /// </summary>
81         public event EventHandler ScanCompleted;
82
83         /// <summary>
84         /// Occurs when the radio is interrupted.
85         /// </summary>
86         public event EventHandler<RadioInterruptedEventArgs> Interrupted;
87
88         /// <summary>
89         /// Gets the current state of the radio.
90         /// </summary>
91         public RadioState State
92         {
93             get
94             {
95                 RadioState state;
96                 GetState(Handle, out state);
97                 return state;
98             }
99         }
100
101         /// <summary>
102         /// Gets or sets the radio frequency in the range of 87500 ~ 108000 kHz.
103         /// </summary>
104         /// <exception cref="ArgumentOutOfRangeException">
105         ///     <paramref name="value"/> is less than <see cref="Range.Min"/> of <see cref="FrequencyRange"/>.\n
106         ///     - or - \n
107         ///     <paramref name="value"/> is greater than <see cref="Range.Max"/> of <see cref="FrequencyRange"/>.\n
108         /// </exception>
109         public int Frequency
110         {
111             get
112             {
113                 int value = 0;
114                 GetFrequency(Handle, out value).ThrowIfFailed("Failed to get frequency");
115                 return value;
116             }
117             set
118             {
119                 if (value < FrequencyRange.Min || value > FrequencyRange.Max)
120                 {
121                     throw new ArgumentOutOfRangeException(nameof(Frequency), value, "Frequency must be within FrequencyRange.");
122                 }
123
124                 SetFrequency(Handle, value).ThrowIfFailed("Failed to set frequency");
125             }
126         }
127
128         /// <summary>
129         /// Gets the current signal strength in the range of -128 ~ 128 dBm.
130         /// </summary>
131         public int SignalStrength
132         {
133             get
134             {
135                 int value = 0;
136                 GetSignalStrength(Handle, out value).ThrowIfFailed("Failed to get signal strength");
137                 return value;
138             }
139         }
140
141         /// <summary>
142         /// Gets the value indicating if the radio is muted.
143         /// </summary>
144         /// <value>
145         /// true if the radio is muted; otherwise, false.
146         /// The default is false.
147         /// </value>
148         public bool IsMuted
149         {
150             get
151             {
152                 bool value;
153                 GetMuted(Handle, out value).ThrowIfFailed("Failed to get the mute state");
154                 return value;
155             }
156             set
157             {
158                 SetMute(Handle, value).ThrowIfFailed("Failed to set the mute state");
159             }
160         }
161
162         /// <summary>
163         /// Gets the channel spacing for the current region.
164         /// </summary>
165         public int ChannelSpacing
166         {
167             get
168             {
169                 int value;
170                 GetChannelSpacing(Handle, out value).ThrowIfFailed("Failed to get channel spacing");
171                 return value;
172             }
173         }
174
175         /// <summary>
176         /// Gets or sets the radio volume level.
177         /// </summary>
178         /// <remarks>Valid volume range is from 0 to 1.0(100%), inclusive.</remarks>
179         /// <value>The default is 1.0.</value>
180         /// <exception cref="ArgumentOutOfRangeException">
181         ///     <paramref name="value"/> is less than zero.\n
182         ///     - or -\n
183         ///     <paramref name="value"/> is greater than 1.0.
184         /// </exception>
185         public float Volume
186         {
187             get
188             {
189                 float value;
190                 GetVolume(Handle, out value).ThrowIfFailed("Failed to get volume level.");
191                 return value;
192             }
193             set
194             {
195                 if (value < 0F || 1.0F < value)
196                 {
197                     throw new ArgumentOutOfRangeException(nameof(value), value,
198                         $"Valid volume range is 0 <= value <= 1.0, but got { value }.");
199                 }
200
201                 SetVolume(Handle, value).ThrowIfFailed("Failed to set volume level");
202             }
203         }
204
205         /// <summary>
206         /// Gets the frequency for the region in the range of 87500 ~ 108000 kHz.
207         /// </summary>
208         public Range FrequencyRange
209         {
210             get
211             {
212                 int min, max;
213
214                 GetFrequencyRange(Handle, out min, out max).ThrowIfFailed("Failed to get frequency range");
215
216                 return new Range(min, max);
217             }
218         }
219
220         /// <summary>
221         /// Starts the radio.
222         /// </summary>
223         /// <remarks>The radio must be in the <see cref="RadioState.Ready"/> state.</remarks>
224         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
225         public void Start()
226         {
227             ValidateRadioState(RadioState.Ready);
228
229             Interop.Radio.Start(Handle).ThrowIfFailed("Failed to start radio");
230         }
231
232         /// <summary>
233         /// Stops the radio.
234         /// </summary>
235         /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
236         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
237         public void Stop()
238         {
239             ValidateRadioState(RadioState.Playing);
240
241             Interop.Radio.Stop(Handle).ThrowIfFailed("Failed to stop radio");
242         }
243
244         /// <summary>
245         /// Starts the radio scanning and triggers the ScanInformationUpdated event when the scan information is updated.
246         /// </summary>
247         /// <remarks>The radio must be in the <see cref="RadioState.Ready"/> or <see cref="RadioState.Playing"/> state.</remarks>
248         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
249         /// <seealso cref="ScanUpdated"/>
250         /// <seealso cref="ScanCompleted"/>
251         public void StartScan()
252         {
253             ValidateRadioState(RadioState.Ready, RadioState.Playing);
254
255             ScanStart(Handle, ScanUpdatedCallback);
256         }
257
258         /// <summary>
259         /// Stops the radio scanning.
260         /// </summary>
261         /// <remarks>The radio must be in the <see cref="RadioState.Scanning"/> state.</remarks>
262         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
263         /// <seealso cref="ScanStopped"/>
264         public void StopScan()
265         {
266             ValidateRadioState(RadioState.Scanning);
267
268             ScanStop(Handle, ScanStoppedCallback);
269         }
270
271         /// <summary>
272         /// Seeks up the effective frequency of the radio.
273         /// </summary>
274         /// <returns>
275         /// A task that represents the asynchronous seeking operation.
276         /// The result value is the current frequency in the range of 87500 ~ 108000 kHz.
277         /// It can be -1 if the seeking operation has failed.
278         /// </returns>
279         /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
280         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
281         public async Task<int> SeekUpAsync()
282         {
283             ValidateRadioState(RadioState.Playing);
284
285             TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
286             SeekCompletedCallback callback = (currentFrequency, _) =>
287             {
288                 tcs.TrySetResult(currentFrequency);
289             };
290
291             SeekUp(Handle, callback);
292             return await tcs.Task;
293         }
294
295         /// <summary>
296         /// Seeks down the effective frequency of the radio.
297         /// </summary>
298         /// <returns>
299         /// A task that represents the asynchronous seeking operation.
300         /// The result value is the current frequency in the range of 87500 ~ 108000 kHz.
301         /// It can be -1 if the seeking operation has failed.
302         /// </returns>
303         /// <remarks>The radio must be in the <see cref="RadioState.Playing"/> state.</remarks>
304         /// <exception cref="InvalidOperationException">The radio is not in the valid state.</exception>
305         public async Task<int> SeekDownAsync()
306         {
307             ValidateRadioState(RadioState.Playing);
308
309             TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
310             SeekCompletedCallback callback = (currentFrequency, _) =>
311             {
312                 tcs.TrySetResult(currentFrequency);
313             };
314
315             SeekDown(Handle, callback);
316             return await tcs.Task;
317         }
318
319         private void ValidateFeatureSupported(string featurePath)
320         {
321             bool supported = false;
322             Information.TryGetValue(featurePath, out supported);
323
324             if (supported == false)
325             {
326                 throw new NotSupportedException($"The feature({featurePath}) is not supported.");
327             }
328
329         }
330
331         private void ScanUpdatedCallback(int frequency, IntPtr data)
332         {
333             ScanUpdated?.Invoke(this, new ScanUpdatedEventArgs(frequency));
334         }
335
336         private void ScanStoppedCallback(IntPtr data)
337         {
338             ScanStopped?.Invoke(this, EventArgs.Empty);
339         }
340
341         private void ScanCompleteCallback(IntPtr data)
342         {
343             ScanCompleted?.Invoke(this, EventArgs.Empty);
344         }
345
346         private void InterruptedCallback(RadioInterruptedReason reason, IntPtr data)
347         {
348             Interrupted?.Invoke(this, new RadioInterruptedEventArgs(reason));
349         }
350
351         private void ValidateRadioState(params RadioState[] required)
352         {
353             RadioState curState = State;
354
355             if (required.Contains(curState) == false)
356             {
357                 throw new InvalidOperationException($"{curState} is not valid state.");
358             }
359         }
360
361         #region IDisposable Support
362         private bool _disposed = false;
363
364         /// <summary>
365         /// Releases the resources used by the Radio.
366         /// </summary>
367         /// <param name="disposing">
368         /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
369         /// </param>
370         protected virtual void Dispose(bool disposing)
371         {
372             if (!_disposed)
373             {
374                 if (_handle != null)
375                 {
376                     _handle.Dispose();
377                 }
378                 _disposed = true;
379             }
380         }
381
382         /// <summary>
383         /// Releases all resources used by the <see cref="Radio"/> object.
384         /// </summary>
385         public void Dispose()
386         {
387             Dispose(true);
388         }
389         #endregion
390     }
391 }