private const string LogTag = "Tizen.Applications";
private Window _win;
private int _resId;
+ private bool _disposed = false;
/// <summary>
/// Initializes the EFLWindow class.
}
}
+ /// <summary>
+ /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ _win.Unrealize();
+ _win = null;
+ }
+ _disposed = true;
+ }
+
+ /// <summary>
+ /// Dispose the window resources
+ /// </summary>
+ /// <returns></returns>
+ /// <since_tizen> 6 </since_tizen>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
}
{
private Interop.CBApplication.FrameLifecycleCallbacks _callbacks;
private const string LogTag = "Tizen.Applications";
+ private IDictionary<string, IWindowInfo> _winDic = new Dictionary<string, IWindowInfo>();
internal FrameComponentStateManager(Type ctype, string id, ComponentBasedApplication parent) : base(ctype, id, parent)
{
if (win == null)
return IntPtr.Zero;
+ _winDic.Add(id, win);
if (!fc.OnCreate())
{
Log.Error(LogTag, "OnCreate fail");
if (fc.Handle == context)
{
fc.OnDestroy();
+ _winDic[fc.Id].Dispose();
RemoveComponent(fc);
break;
}
/// Interface for window information
/// </summary>
/// <since_tizen> 6 </since_tizen>
- public interface IWindowInfo
+ public interface IWindowInfo : IDisposable
{
/// <summary>
/// Gets window resource ID
*/
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace Tizen.Messaging.Email
/// <since_tizen> 3 </since_tizen>
public static class EmailSender
{
+ static private Dictionary<int, Interop.Email.EmailSentCallback> _sendCbMap = new Dictionary<int, Interop.Email.EmailSentCallback>();
+ static private int _callbackId = 0;
+
/// <summary>
/// Sends the email message.
/// </summary>
email.FillHandle();
email.Save();
- Interop.Email.EmailSentCallback _emailSendingCallback = (IntPtr handle, int result, IntPtr userData) =>
+ int id = _callbackId++;
+ _sendCbMap[id] = (IntPtr handle, int result, IntPtr userData) =>
{
- task.SetResult((EmailSendResult)result);
+ task?.SetResult((EmailSendResult)result);
+ _sendCbMap.Remove((int)userData);
};
- ret = Interop.Email.SetCb(email._emailHandle, _emailSendingCallback, IntPtr.Zero);
+ ret = Interop.Email.SetCb(email._emailHandle, _sendCbMap[id], (IntPtr)id);
if (ret != (int)EmailError.None)
{
Log.Error(EmailErrorFactory.LogTag, "Failed to set email incoming callback, Error code: " + (EmailError)ret);
+ _sendCbMap.Remove(id);
throw EmailErrorFactory.GetException(ret);
}
if (ret != (int)EmailError.None)
{
Log.Error(EmailErrorFactory.LogTag, "Failed to send email, Error code: " + (EmailError)ret);
+ _sendCbMap.Remove(id);
throw EmailErrorFactory.GetException(ret);
}
- var sendResult = await task.Task;
-
+ var sendResult = await task.Task.ConfigureAwait(false);
ret = Interop.Email.UnsetCb(email._emailHandle);
+
if (ret != (int)EmailError.None)
{
Log.Error(EmailErrorFactory.LogTag, "Failed to set email incoming callback, Error code: " + (EmailError)ret);
throw EmailErrorFactory.GetException(ret);
}
-
return sendResult;
}
+
}
}
[DllImport(Libraries.Player, EntryPoint = "player_foreach_media_stream_supported_format")]
internal static extern PlayerErrorCode SupportedMediaStreamFormat(IntPtr player, SupportedMediaFormatCallback callback, IntPtr userData);
+
+ [DllImport(Libraries.Player, EntryPoint = "player_set_audio_codec_type")]
+ internal static extern PlayerErrorCode SetAudioCodecType(IntPtr player, CodecType type);
+
+ [DllImport(Libraries.Player, EntryPoint = "player_get_audio_codec_type")]
+ internal static extern PlayerErrorCode GetAudioCodecType(IntPtr player, out CodecType type);
}
internal class PlayerHandle : SafeHandle
{
Player = owner;
- if (IsAvailable== false)
+ if (IsAvailable == false)
{
return;
}
/// <exception cref="InvalidOperationException">
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException"><see cref="IsAvailable"/> returns false. (Since tizen 6.0)</exception>
+ /// <seealso cref="IsAvailable"/>
/// <since_tizen> 3 </since_tizen>
public void Clear()
{
Player.ValidateNotDisposed();
Player.AudioOffload.CheckDisabled();
+ if (IsAvailable == false)
+ throw new NotAvailableException("The function is not available.");
+
Native.EqualizerClear(Player.Handle).
ThrowIfFailed(Player, "Failed to clear equalizer effect");
}
/// <exception cref="InvalidOperationException">
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException"><see cref="IsAvailable"/> returns false. (Since tizen 6.0)</exception>
+ /// <seealso cref="IsAvailable"/>
/// <since_tizen> 3 </since_tizen>
public int Count
{
{
Player.AudioOffload.CheckDisabled();
+ if (IsAvailable == false)
+ throw new NotAvailableException("The function is not available.");
+
Native.GetEqualizerBandsCount(Player.Handle, out var count).
ThrowIfFailed(Player, "Failed to initialize the AudioEffect");
/// <exception cref="InvalidOperationException">
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException"><seealso cref="IsAvailable"/> returns false. (Since tizen 6.0)</exception>
+ /// <seealso cref="IsAvailable"/>
/// <since_tizen> 3 </since_tizen>
public Range BandLevelRange
{
{
Player.AudioOffload.CheckDisabled();
+ if (IsAvailable == false)
+ throw new NotAvailableException("The function is not available.");
+
Native.GetEqualizerLevelRange(Player.Handle, out var min, out var max).
ThrowIfFailed(Player, "Failed to initialize the AudioEffect");
/// <summary>
/// Gets the value whether the AudioEffect is available or not.
/// </summary>
+ /// <remarks>This function returns the availability of the audio effect function group and
+ /// it could be unavailable depending on the platform capabilities.</remarks>
/// <exception cref="InvalidOperationException">
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
+ /// <seealso cref="Player.AudioCodecType"/>
/// <since_tizen> 3 </since_tizen>
public bool IsAvailable
{
/// </summary>
/// <value>If the replaygain status is true, replaygain is applied (if contents has a replaygain tag);
/// otherwise, the replaygain is not affected by tag and properties.</value>
+ /// <remarks>This function could be unavailable depending on the audio codec type.</remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="InvalidOperationException">
/// The player is not in the valid state.
/// -or-<br/>
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
+ /// <seealso cref="AudioCodecType"/>
/// <since_tizen> 5 </since_tizen>
public bool ReplayGain
{
/// </summary>
/// <value>The value indicating whether or not AudioPitch is enabled. The default is false.</value>
/// <remarks>This function is used for audio content only.
- /// To set, the player must be in the <see cref="PlayerState.Idle"/> state.</remarks>
+ /// To set, the player must be in the <see cref="PlayerState.Idle"/> state.
+ /// This function could be unavailable depending on the audio codec type.</remarks>
/// <exception cref="InvalidOperationException">
/// The player is not in the valid state.
/// -or-<br/>
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
/// <seealso cref="AudioPitch"/>
+ /// <seealso cref="AudioCodecType"/>
/// <since_tizen> 6 </since_tizen>
public bool AudioPitchEnabled
{
/// </summary>
/// <value>The audio stream pitch value. The default is 1.</value>
/// <remarks>Enabling pitch control could increase the CPU usage on some devices.
- /// This function is used for audio content only.</remarks>
+ /// This function is used for audio content only.
+ /// This function could be unavailable depending on the audio codec type.</remarks>
/// <exception cref="InvalidOperationException">
/// A pitch is not enabled.
/// -or-<br/>
/// -or-<br/>
/// value is greater than 2.0.<br/>
/// </exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
/// <seealso cref="AudioPitchEnabled"/>
+ /// <seealso cref="AudioCodecType"/>
/// <since_tizen> 6 </since_tizen>
public float AudioPitch
{
}
}
+ /// <summary>
+ /// Gets or sets the default codec type of the audio decoder.
+ /// </summary>
+ /// <value>A <see cref="CodecType"/> that specifies the type.
+ /// The default codec type could be different depending on the device capability.</value>
+ /// <remarks>
+ /// <para>To set, the player must be in the <see cref="PlayerState.Idle"/> state.</para>
+ /// <para>If H/W audio codec type is not supported in some cases, S/W audio codec type could be used instead.</para>
+ /// <para>The availability could be changed depending on the codec capability.
+ /// If an application wants to use the H/W audio codec type as default,
+ /// following functions should be called after the codec type is set. :<br/>
+ /// <see cref="AudioEffect.IsAvailable"/><br/>
+ /// <see cref="EnableExportingAudioData"/><br/>
+ /// <see cref="DisableExportingAudioData"/><br/>
+ /// <see cref="ReplayGain"/><br/>
+ /// <see cref="AudioPitch"/><br/>
+ /// <see cref="AudioPitchEnabled"/><br/></para>
+ /// </remarks>
+ /// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
+ /// <exception cref="ArgumentException">The value is not valid.</exception>
+ /// <exception cref="InvalidOperationException">
+ /// The player is not in the valid state.
+ /// -or-<br/>
+ /// Operation failed; internal error.
+ /// </exception>
+ /// <exception cref="CodecNotSupportedException">The selected codec is not supported.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public CodecType AudioCodecType
+ {
+ get
+ {
+ ValidateNotDisposed();
+
+ NativePlayer.GetAudioCodecType(Handle, out var value).
+ ThrowIfFailed(this, "Failed to get the type of the audio codec");
+
+ return value;
+ }
+ set
+ {
+ ValidateNotDisposed();
+ ValidatePlayerState(PlayerState.Idle);
+
+ ValidationUtil.ValidateEnum(typeof(CodecType), value, nameof(value));
+
+ NativePlayer.SetAudioCodecType(Handle, value).
+ ThrowIfFailed(this, "Failed to set the type of the audio codec");
+ }
+ }
+
private SphericalVideo _sphericalVideo;
/// <summary>
/// <para>The audio PCM data can be retrieved using a <see cref="AudioDataDecoded"/> event as a media packet
/// and it is available until it's destroyed by <see cref="MediaPacket.Dispose()"/>.
/// The packet has to be destroyed as quickly as possible after rendering the data
- /// and all the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
+ /// and all the packets have to be destroyed before <see cref="Unprepare"/> is called.</para>
+ /// <para>This function could be unavailable depending on the audio codec type.</para></remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="ArgumentException">The value is not valid.</exception>
/// <exception cref="InvalidOperationException">
/// -or-<br/>
/// If audio offload is enabled by calling <see cref="AudioOffload.IsEnabled"/>. (Since tizen 6.0)
/// </exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
/// <seealso cref="PlayerAudioExtractOption"/>
/// <seealso cref="DisableExportingAudioData"/>
+ /// <seealso cref="AudioCodecType"/>
/// <since_tizen> 6 </since_tizen>
public void EnableExportingAudioData(AudioMediaFormat format, PlayerAudioExtractOption option)
{
/// Disable to decode an audio data.
/// </summary>
/// <remarks>The player must be in the <see cref="PlayerState.Idle"/> or <see cref="PlayerState.Ready"/>
- /// state.</remarks>
+ /// state.
+ /// This function could be unavailable depending on the audio codec type.</remarks>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// <exception cref="InvalidOperationException">The player is not in the valid state.</exception>
+ /// <exception cref="NotAvailableException">The function is not available depending on the audio codec type. (Since tizen 6.0)</exception>
/// <seealso cref="EnableExportingAudioData"/>
+ /// <seealso cref="AudioCodecType"/>
/// <since_tizen> 6 </since_tizen>
public void DisableExportingAudioData()
{
/// <summary>
/// Enables to decode a video data for every frame.
/// </summary>
- /// <remarks><para>The player must be in the <see cref="PlayerState.Idle"/> state.
- /// And, <see cref="Multimedia.Display"/> must not be set.</para>
- /// <para>A <see cref="VideoFrameDecoded"/> event is called in a separate thread(not in the main loop).</para>
- /// <para>The video frame can be retrieved using a <see cref="VideoFrameDecoded"/> event as a media packet.
- /// So if you change the media packet in the <see cref="VideoFrameDecoded"/> event, it will be displayed on the device.
- /// The callback function holds the same buffer that will be drawn on the display device.
- /// and the <see cref="MediaPacket"/> is available until it's destroyed by <see cref="MediaPacket.Dispose()"/>.
- /// The packet has to be destroyed as quickly as possible after rendering the data
- /// and all the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
+ /// <remarks><para>The player must be in the <see cref="PlayerState.Idle"/> state,
+ /// but <see cref="Multimedia.Display"/> must not be set.</para>
+ /// <para>A <see cref="VideoFrameDecoded"/> event is called in a separate thread, not called in the main loop.</para>
+ /// <para>The video frame can be retrieved using a <see cref="VideoFrameDecoded"/> event with a media packet parameter.
+ /// If you change the media packet in the <see cref="VideoFrameDecoded"/> event, it will be displayed on the device.
+ /// The callback function holds the same buffer that is drawn on the display device.
+ /// and the <see cref="MediaPacket"/> is available until it is destroyed by <see cref="MediaPacket.Dispose()"/>.
+ /// It is recommended to destroy the packet as quickly as possible after the decoded data is rendered on the display.
+ /// All the packets have to be destroyed before <see cref="Unprepare"/> is called.</para></remarks>
/// <feature>http://tizen.org/feature/multimedia.raw_video</feature>
/// <exception cref="NotSupportedException">The required feature is not supported.</exception>
/// <exception cref="ObjectDisposedException">The player has already been disposed of.</exception>
/// </summary>
NoSyncAndDeinterleave = 0x03,
}
+
+
+ /// <summary>
+ /// Specifies the types of a codec for <see cref="Player"/>.
+ /// </summary>
+ /// <seealso cref="Player.AudioCodecType"/>
+ /// <since_tizen> 6 </since_tizen>
+ public enum CodecType
+ {
+ /// <summary>
+ /// An optional flag for using the H/W codec.
+ /// </summary>
+ Hardware,
+
+ /// <summary>
+ /// An optional flag for using the S/W codec
+ /// </summary>
+ Software,
+ }
}
ServiceDisconnected = PlayerErrorClass | 0x0d,
NotSupportedAudioCodec = PlayerErrorClass | 0x0e,
NotSupportedVideoCodec = PlayerErrorClass | 0x0f,
- NotSupportedSubtitle = PlayerErrorClass | 0x10
+ NotSupportedSubtitle = PlayerErrorClass | 0x10,
+ NotAvailable = PlayerErrorClass | 0x12
}
internal static class PlayerErrorCodeExtensions
case PlayerErrorCode.NotSupportedVideoCodec:
throw new CodecNotSupportedException(CodecKind.Video);
+
+ case PlayerErrorCode.NotAvailable:
+ throw new NotAvailableException(msg);
}
return null;
{
}
}
+
+ /// <summary>
+ /// The exception that is thrown when there it is not available.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class NotAvailableException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the NotAvailableException class with a specified error message.
+ /// </summary>
+ /// <param name="message">Error description.</param>
+ /// <since_tizen> 6 </since_tizen>
+ public NotAvailableException(string message) : base(message)
+ {
+ }
+ }
}
};
}
- internal static Tizen.Multimedia.Rectangle[] ToApiStruct(MediaVision.Rectangle[] rects)
+ internal static Tizen.Multimedia.Rectangle[] ToApiStruct(this MediaVision.Rectangle[] rects)
{
var result = new Tizen.Multimedia.Rectangle[rects.Length];
return result;
}
+ internal static MediaVision.Rectangle[] ToMarShalable(this Tizen.Multimedia.Rectangle[] rects)
+ {
+ var result = new MediaVision.Rectangle[rects.Length];
+
+ for (int i = 0; i < rects.Length; i++)
+ {
+ result[i] = rects[i].ToMarshalable();
+ }
+ return result;
+ }
+
/// <summary>
/// Interop for Media Vision APIs.
/// </summary>
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_set_string_attribute")]
internal static extern MediaVisionError SetString(IntPtr handle, string name, string value);
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_set_array_string_attribute")]
+ internal static extern MediaVisionError SetStringArray(IntPtr handle, string name,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] string[] value, int size);
+
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_double_attribute")]
internal static extern MediaVisionError GetDouble(IntPtr handle, string name, out double value);
[DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_string_attribute")]
internal static extern MediaVisionError GetString(IntPtr handle, string name, out IntPtr value);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_engine_config_get_array_string_attribute")]
+ internal static extern MediaVisionError GetStringArray(IntPtr handle, string name,
+ out IntPtr value, out int size);
}
}
}
--- /dev/null
+/*
+ * Copyright (c) 2019 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;
+using Tizen.Multimedia.Vision;
+
+/// <summary>
+/// Interop APIs.
+/// </summary>
+internal static partial class Interop
+{
+ /// <summary>
+ /// Interop for Media Vision APIs.
+ /// </summary>
+ internal static partial class MediaVision
+ {
+ /// <summary>
+ /// Interop for Face APIs.
+ /// </summary>
+ internal static partial class Inference
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FaceDetectedCallback(IntPtr source, int numberOfFaces,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Rectangle[] location,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FacialLandmarkDetectedCallback(IntPtr source, int numberOfLandmarks,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Point[] locations,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ImageClassifedCallback(IntPtr source, int numberOfClasses,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] indices,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] string[] names,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ObjectDetectedCallback(IntPtr source, int numberOfObjects,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] indices,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] string[] names,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] confidences,
+ [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Rectangle[] location,
+ IntPtr userData = default(IntPtr));
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate bool SupportedBackendCallback(string backend, bool isSupported,
+ IntPtr userData = default(IntPtr));
+
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_create")]
+ internal static extern MediaVisionError Create(out IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_destroy")]
+ internal static extern MediaVisionError Destroy(IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_configure")]
+ internal static extern MediaVisionError Configure(IntPtr handle, IntPtr engineConfig);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_prepare")]
+ internal static extern MediaVisionError Load(IntPtr handle);
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_foreach_supported_engine")]
+ internal static extern MediaVisionError ForeachSupportedBackend(IntPtr handle,
+ SupportedBackendCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_image_classify")]
+ internal static extern MediaVisionError ClassifyImage(IntPtr source, IntPtr inference,
+ IntPtr roi, ImageClassifedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_object_detect")]
+ internal static extern MediaVisionError DetectObject(IntPtr source, IntPtr inference,
+ ObjectDetectedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_face_detect")]
+ internal static extern MediaVisionError DetectFace(IntPtr source, IntPtr inference,
+ FaceDetectedCallback callback, IntPtr userData = default(IntPtr));
+
+ [DllImport(Libraries.MediaVision, EntryPoint = "mv_inference_facial_landmark_detect")]
+ internal static extern MediaVisionError DetectFacialLandmark(IntPtr source, IntPtr inference,
+ IntPtr roi, FacialLandmarkDetectedCallback callback, IntPtr userData = default(IntPtr));
+ }
+ }
+}
EngineConfig.SetString(Handle, key, value).Validate("Failed to set attribute");
}
+ internal void Set(string key, string [] value)
+ {
+ EngineConfig.SetStringArray(Handle, key, value, value.Length).Validate("Failed to set attribute");
+ }
+
internal int GetInt(string key)
{
int value = 0;
}
}
+ internal string[] GetStringArray(string key)
+ {
+ IntPtr values = IntPtr.Zero;
+ int size = 0;
+
+ try
+ {
+ EngineConfig.GetStringArray(Handle, key, out values, out size).
+ Validate("Failed to get the value");
+
+ var current = values;
+ var result = new string[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ result[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(current));
+ current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
+ }
+
+ return result;
+ }
+ finally
+ {
+ var current = values;
+ for (int i = 0; i < size; i++)
+ {
+ LibcSupport.Free(Marshal.ReadIntPtr(current));
+ current = (IntPtr)((long)current + Marshal.SizeOf(typeof(IntPtr)));
+ }
+ }
+ }
+
/// <summary>
/// Releases all resources used by the <see cref="EngineConfiguration"/> object.
/// </summary>
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of face detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="FaceDetector.DetectAsync(MediaVisionSource, InferenceModelConfiguration)"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class FaceDetectionResult
+ {
+ internal FaceDetectionResult(float confidence, global::Interop.MediaVision.Rectangle location)
+ {
+ Confidence = confidence;
+ Location = location.ToApiStruct();
+ }
+
+ /// <summary>
+ /// Gets the confidence of detected face.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+
+ /// <summary>
+ /// Gets the location of detected face.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle Location { get; }
+ }
+}
*/
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using InteropFace = Interop.MediaVision.Face;
+using InteropInference = Interop.MediaVision.Inference;
namespace Tizen.Multimedia.Vision
{
/// <summary>
/// Provides the ability to detect faces on image sources.
/// </summary>
+ /// <remarks>
+ /// If you want to use face detection based on inference engine(<see cref="InferenceBackendType"/>),
+ /// please use <see cref="DetectAsync(MediaVisionSource, InferenceModelConfiguration)"/>.
+ /// </remarks>
/// <since_tizen> 4 </since_tizen>
public static class FaceDetector
{
-
/// <summary>
/// Detects faces on the source.<br/>
/// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
/// <since_tizen> 4 </since_tizen>
public static async Task<Rectangle[]> DetectAsync(MediaVisionSource source)
{
- return await DetectAsync(source, null);
+ return await DetectAsync(source, (FaceDetectionConfiguration)null);
}
/// <summary>
}
};
}
+
+ /// <summary>
+ /// Detects faces on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
+ /// </summary>
+ /// <remarks>
+ /// If there's no detected face, empty collection will be returned.
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected face, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<FaceDetectionResult>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceFace);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<FaceDetectionResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ InteropInference.DetectFace(source.Handle, config.GetHandle(), cb.Target).
+ Validate("Failed to detect face.");
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.FaceDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<FaceDetectionResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfFaces, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfFaces, confidences, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set face detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<FaceDetectionResult> GetResults(int number, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<FaceDetectionResult>();
+ }
+
+ var results = new List<FaceDetectionResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new FaceDetectionResult(confidences[i], locations[i]));
+ }
+
+ return results;
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to detect facial landmarks on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class FacialLandmarkDetector
+ {
+ /// <summary>
+ /// Detects facial landmarks on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// </summary>
+ /// <remarks>
+ /// To set region-of-interest area in source image, please set <see cref="InferenceModelConfiguration.Roi"/>.
+ /// If not set, full image area will be used to detect facial landmark.
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected facial landmark, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<Point>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceFace);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<Point>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ IntPtr roiUnmanaged = IntPtr.Zero;
+
+ try
+ {
+ if (config.Roi.HasValue)
+ {
+ var roi = config.Roi.Value.ToMarshalable();
+
+ roiUnmanaged = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(global::Interop.MediaVision.Rectangle)));
+ Marshal.WriteIntPtr(roiUnmanaged, IntPtr.Zero);
+ Marshal.StructureToPtr(roi, roiUnmanaged, false);
+ }
+
+ InteropInference.DetectFacialLandmark(source.Handle, config.GetHandle(), roiUnmanaged, cb.Target).
+ Validate("Failed to detect facial landmark.");
+ }
+ finally
+ {
+ if (roiUnmanaged != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(roiUnmanaged);
+ }
+ }
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.FacialLandmarkDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<Point>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfLandmarks, global::Interop.MediaVision.Point[] locations, IntPtr _) =>
+ {
+ try
+ {
+
+ if (!tcs.TrySetResult(GetResults(numberOfLandmarks, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set facial landmark detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<Point> GetResults(int number, global::Interop.MediaVision.Point[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<Point>();
+ }
+
+ var results = new List<Point>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(locations[i].ToApiStruct());
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of face detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="ImageClassifier"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class ImageClassificationResult
+ {
+ internal ImageClassificationResult(int indice, string name, float confidence)
+ {
+ Indice = indice;
+ Name = name;
+ Confidence = confidence;
+ }
+
+ /// <summary>
+ /// Gets the indice of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public int Indice { get; }
+
+ /// <summary>
+ /// Gets the name of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the confidence of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to classify image objects on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class ImageClassifier
+ {
+ /// <summary>
+ /// Classifies image objects on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected faces at the media source are received asynchronously.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for classifying.</param>
+ /// <returns>
+ /// A task that represents the asynchronous classify operation.<br/>
+ /// If there's no classified image object, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<ImageClassificationResult>> ClassifyAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceImage);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<ImageClassificationResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ IntPtr roiUnmanaged = IntPtr.Zero;
+
+ try
+ {
+ if (config.Roi.HasValue)
+ {
+ var roi = config.Roi.Value.ToMarshalable();
+
+ roiUnmanaged = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(global::Interop.MediaVision.Rectangle)));
+ Marshal.WriteIntPtr(roiUnmanaged, IntPtr.Zero);
+ Marshal.StructureToPtr(roi, roiUnmanaged, false);
+ }
+
+ InteropInference.ClassifyImage(source.Handle, config.GetHandle(), roiUnmanaged, cb.Target).
+ Validate("Failed to classify image.");
+ }
+ finally
+ {
+ if (roiUnmanaged != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(roiUnmanaged);
+ }
+ }
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.ImageClassifedCallback GetCallback(TaskCompletionSource<IEnumerable<ImageClassificationResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfClasses, int[] indices, string[] names, float[] confidences, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfClasses, indices, names, confidences)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set image classification result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<ImageClassificationResult> GetResults(int number, int[] indices,
+ string[] names, float[] confidences)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<ImageClassificationResult>();
+ }
+
+ var results = new List<ImageClassificationResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new ImageClassificationResult(indices[i], names[i], confidences[i]));
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections.Generic;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Represents a configuration of <see cref="FaceDetector"/>, <see cref="FacialLandmarkDetector"/>,
+ /// <see cref="ImageClassifier"/> and <see cref="ObjectDetector"/>.
+ /// </summary>
+ /// <remarks>
+ /// 'Inference model' means pre-learned data, which is represented by <see cref="ConfigurationFilePath"/> and
+ /// <see cref="WeightFilePath"/>, <see cref="CategoryFilePath"/>.<br/>
+ /// If user want to use tizen default inference model and its related value,
+ /// Please refer Tizen guide page(https://developer.tizen.org/development/guides/.net-application).
+ /// </remarks>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <since_tizen> 6 </since_tizen>
+ public class InferenceModelConfiguration : EngineConfiguration
+ {
+ private IntPtr _inferenceHandle = IntPtr.Zero;
+
+ private const string _keyModelConfigurationFilePath = "MV_INFERENCE_MODEL_CONFIGURATION_FILE_PATH";
+ private const string _keyModelWeightFilePath = "MV_INFERENCE_MODEL_WEIGHT_FILE_PATH";
+ private const string _keyModelUserFilePath = "MV_INFERENCE_MODEL_USER_FILE_PATH";
+ private const string _keyModelMeanValue = "MV_INFERENCE_MODEL_MEAN_VALUE";
+ private const string _keyModelStdValue = "MV_INFERENCE_MODEL_STD_VALUE";
+ private const string _keyBackendType = "MV_INFERENCE_BACKEND_TYPE";
+ private const string _keyTargetType = "MV_INFERENCE_TARGET_TYPE";
+ private const string _keyInputTensorWidth = "MV_INFERENCE_INPUT_TENSOR_WIDTH";
+ private const string _keyInputTensorHeight = "MV_INFERENCE_INPUT_TENSOR_HEIGHT";
+ private const string _keyInputTensorChannels = "MV_INFERENCE_INPUT_TENSOR_CHANNELS";
+ private const string _keyInputNodeName = "MV_INFERENCE_INPUT_NODE_NAME";
+ private const string _keyOutputNodeNames = "MV_INFERENCE_OUTPUT_NODE_NAMES";
+ private const string _keyOutputMaxNumber = "MV_INFERENCE_OUTPUT_MAX_NUMBER";
+ private const string _keyConfidenceThreshold = "MV_INFERENCE_CONFIDENCE_THRESHOLD";
+
+ // The following strings are fixed in native and will not be changed.
+ private const string _backendTypeOpenCV = "opencv";
+ private const string _backendTypeTFLite = "tflite";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InferenceModelConfiguration"/> class.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceModelConfiguration() : base("inference")
+ {
+ InteropInference.Create(out _inferenceHandle).Validate("Failed to create inference configuration");
+ }
+
+ /// <summary>
+ /// Loads inference model data and its related attributes.
+ /// </summary>
+ /// <remarks>
+ /// Before calling this method, user should set all properties which is required by each inference model.<br/>
+ /// The properties set after calling this method will not be affected in the result.
+ /// </remarks>
+ /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
+ /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
+ /// <feature>http://tizen.org/feature/vision.inference.face</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <exception cref="FileNotFoundException">
+ /// <see cref="ConfigurationFilePath"/>, <see cref="WeightFilePath"/> or <see cref="CategoryFilePath"/> have invalid path.
+ /// </exception>
+ /// <exception cref="FileFormatException">Invalid data type is used in inference model data.</exception>
+ /// <exception cref="InvalidDataException">
+ /// Inference model data contains unsupported operations in current backend version.
+ /// -or-<br/>
+ /// Invalid data type is used in inference model data.<br/>
+ /// </exception>
+ /// <exception cref="InvalidOperationException">Internal operation error.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public void LoadInferenceModel()
+ {
+ InteropInference.Configure(_inferenceHandle, GetHandle(this)).
+ Validate("Failed to configure inference model.");
+
+ var ret = InteropInference.Load(_inferenceHandle);
+ if (ret == MediaVisionError.InvalidData)
+ {
+ throw new InvalidDataException("Inference model data contains unsupported operations in current backend version.");
+ }
+ else if (ret == MediaVisionError.NotSupportedFormat)
+ {
+ throw new FileFormatException("Invalid data type is used in inference model data.");
+ }
+ ret.Validate("Failed to load inference model.");
+ }
+
+ internal IntPtr GetHandle()
+ {
+ return _inferenceHandle;
+ }
+
+ private IEnumerable<InferenceBackendType> _supportedBackend;
+
+ /// <summary>
+ /// Gets the list of inference backend engine which is supported in the current device.
+ /// </summary>
+ /// <returns>If there's no supported backend, empty collection will be returned.</returns>
+ /// <since_tizen> 6 </since_tizen>
+ public IEnumerable<InferenceBackendType> SupportedBackend
+ {
+ get
+ {
+ if (_supportedBackend == null)
+ {
+ GetSupportedBackend();
+ }
+
+ return _supportedBackend.Any() ? _supportedBackend : Enumerable.Empty<InferenceBackendType>();
+ }
+ }
+
+ private void GetSupportedBackend()
+ {
+ var supportedBackend = new List<InferenceBackendType>();
+
+ InteropInference.SupportedBackendCallback cb = (backend, isSupported, _) =>
+ {
+ if (isSupported && backend != null)
+ {
+ switch (backend)
+ {
+ case _backendTypeOpenCV:
+ supportedBackend.Add(InferenceBackendType.OpenCV);
+ break;
+ case _backendTypeTFLite:
+ supportedBackend.Add(InferenceBackendType.TFLite);
+ break;
+ }
+ }
+
+ return true;
+ };
+
+ InteropInference.ForeachSupportedBackend(_inferenceHandle, cb, IntPtr.Zero).
+ Validate("Failed to get supported backend");
+
+ _supportedBackend = supportedBackend;
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's configuration data file.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string ConfigurationFilePath
+ {
+ get
+ {
+ return GetString(_keyModelConfigurationFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelConfigurationFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's weight file.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string WeightFilePath
+ {
+ get
+ {
+ return GetString(_keyModelWeightFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelWeightFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the path of inference model's category file.
+ /// </summary>
+ /// <remarks>
+ /// This value should be set to use <see cref="ImageClassifier"/> or <see cref="ObjectDetector"/>.
+ /// </remarks>
+ /// <exception cref="ArgumentNullException">Input file path is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string CategoryFilePath
+ {
+ get
+ {
+ return GetString(_keyModelUserFilePath);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "File path is null.");
+ }
+
+ Set(_keyModelUserFilePath, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's mean value.
+ /// </summary>
+ /// <remarks>It should be greater than or equal to 0.</remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double MeanValue
+ {
+ get
+ {
+ return GetDouble(_keyModelMeanValue);
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ $"Value should be greater than or equal to 0");
+ }
+
+ Set(_keyModelMeanValue, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's STD(Standard deviation) value.
+ /// </summary>
+ /// <remarks>It should be greater than or equal to 0.</remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double StdValue
+ {
+ get
+ {
+ return GetDouble(_keyModelStdValue);
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ $"Value should be greater than or equal to 0");
+ }
+
+ Set(_keyModelStdValue, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's backend engine.
+ /// </summary>
+ /// <remarks>The default backend type is <see cref="InferenceBackendType.OpenCV"/></remarks>
+ /// <exception cref="ArgumentException"><paramref name="value"/> is not valid.</exception>
+ /// <exception cref="NotSupportedException">The engine type is not supported.</exception>
+ /// <seealso cref="SupportedBackend"/>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceBackendType Backend
+ {
+ get
+ {
+ return (InferenceBackendType)GetInt(_keyBackendType);
+ }
+ set
+ {
+ ValidationUtil.ValidateEnum(typeof(InferenceBackendType), value, nameof(Backend));
+
+ if (!SupportedBackend.Contains(value))
+ {
+ throw new NotSupportedException("Not supported engine type. " +
+ "Please check supported engine using 'SupportedBackendType'.");
+ }
+
+ Set(_keyBackendType, (int)value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the inference model's target.
+ /// </summary>
+ /// <remarks>
+ /// The default target is <see cref="InferenceTargetType.CPU"/>.<br/>
+ /// If target doesn't support <see cref="InferenceTargetType.GPU"/> and <see cref="InferenceTargetType.Custom"/>,
+ /// <see cref="InferenceTargetType.CPU"/> will be used internally, despite the user's choice.
+ /// </remarks>
+ /// <exception cref="ArgumentException"><paramref name="value"/> is not valid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public InferenceTargetType Target
+ {
+ get
+ {
+ return (InferenceTargetType)GetInt(_keyTargetType);
+ }
+ set
+ {
+ ValidationUtil.ValidateEnum(typeof(InferenceTargetType), value, nameof(Target));
+
+ Set(_keyTargetType, (int)value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of inference model's tensor.
+ /// </summary>
+ /// <remarks>
+ /// Both width and height of tensor should be greater than 0.<br/>
+ /// 'Size(-1, -1) is allowed when the intention is to use original image source size as TensorSize.
+ /// </remarks>
+ /// <exception cref="ArgumentException">
+ /// Only one of <paramref name="value.Width"/> or <paramref name="value.Height"/> have -1.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public Size TensorSize
+ {
+ get
+ {
+ var width = GetInt(_keyInputTensorWidth);
+ var height = GetInt(_keyInputTensorHeight);
+
+ return new Size(width, height);
+ }
+ set
+ {
+ if ((value.Width == -1 && value.Height != -1) || (value.Height == -1 && value.Width != -1))
+ {
+ throw new ArgumentException("Both width and height must be set to -1, or greater than 0.");
+ }
+
+ if (value.Width == 0 || value.Width <= -2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Both width and height must be set to -1, or greater than 0.");
+ }
+
+ if (value.Height == 0 || value.Height <= -2)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Both width and height must be set to -1, or greater than 0.");
+ }
+
+ Set(_keyInputTensorWidth, value.Width);
+ Set(_keyInputTensorHeight, value.Height);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the number of inference model's tensor channel.
+ /// </summary>
+ /// <remarks>
+ /// For example, for RGB colorspace this value should be set to 3<br/>
+ /// It should be greater than 0.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException">The value is invalid.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public int TensorChannels
+ {
+ get
+ {
+ return GetInt(_keyInputTensorChannels);
+ }
+ set
+ {
+ if (value <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value, "Tensor channel should be greater than 0.");
+ }
+
+ Set(_keyInputTensorChannels, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of an input node
+ /// </summary>
+ /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public string InputNodeName
+ {
+ get
+ {
+ return GetString(_keyInputNodeName);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "InputNodeName can't be null.");
+ }
+
+ Set(_keyInputNodeName, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of an output node
+ /// </summary>
+ /// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public IList<string> OutputNodeName
+ {
+ get
+ {
+ return GetStringArray(_keyOutputNodeNames);
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), "OutputNodeName can't be null.");
+ }
+
+ var name = new string[value.Count];
+ value.CopyTo(name, 0);
+ Set(_keyOutputNodeNames, name);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum output number of detection or classification.
+ /// </summary>
+ /// <remarks>
+ /// The input value over 10 will be set to 10 and the input value under 1 will be set to 1.<br/>
+ /// This value can be used to decide the size of <see cref="Roi"/>, it's length should be the same.
+ /// </remarks>
+ /// <since_tizen> 6 </since_tizen>
+ public int MaxOutputNumber
+ {
+ get
+ {
+ return GetInt(_keyOutputMaxNumber);
+ }
+ set
+ {
+ Set(_keyOutputMaxNumber, value > 10 ? 10 : value < 1 ? 1 : value);
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the threshold of confidence.
+ /// </summary>
+ /// <remarks>
+ /// The vaild range is greater than or equal to 0.0 and less than or equal to 1.0.<br/>
+ /// The value 1.0 means maximum accuracy.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/>is out of range.</exception>
+ /// <since_tizen> 6 </since_tizen>
+ public double ConfidenceThreshold
+ {
+ get
+ {
+ return GetDouble(_keyConfidenceThreshold);
+ }
+ set
+ {
+ if (value < 0.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Confidence threshold should be greater than or equal to 0.0.");
+ }
+ if (value > 1.0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), value,
+ "Confidence threshold should be less than or equal to 1.0.");
+ }
+
+ Set(_keyConfidenceThreshold, value);
+ }
+ }
+
+ private Rectangle? _roi;
+
+ /// <summary>
+ /// Gets or sets the ROI(Region Of Interest) of <see cref="ImageClassifier"/> and <see cref="FacialLandmarkDetector"/>
+ /// </summary>
+ /// <remarks>
+ /// Default value is null. If Roi is null, the entire region of <see cref="MediaVisionSource"/> will be analyzed.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// The width of <paramref name="value"/> is less than or equal to zero.<br/>
+ /// -or-<br/>
+ /// The height of <paramref name="value"/> is less than or equal to zero.<br/>
+ /// -or-<br/>
+ /// The x position of <paramref name="value"/> is less than zero.<br/>
+ /// -or-<br/>
+ /// The y position of <paramref name="value"/> is less than zero.
+ /// </exception>
+ /// <seealso cref="MaxOutputNumber"/>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle? Roi
+ {
+ get
+ {
+ return _roi;
+ }
+ set
+ {
+ if (value != null)
+ {
+ ValidateRoi(value.Value);
+ _roi = value;
+ }
+ }
+ }
+
+ private static void ValidateRoi(Rectangle roi)
+ {
+ if (roi.Width <= 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Width", roi.Width,
+ "The width of roi can't be less than or equal to zero.");
+ }
+
+ if (roi.Height <= 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Height", roi.Height,
+ "The height of roi can't be less than or equal to zero.");
+ }
+
+ if (roi.X < 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.X", roi.X,
+ "The x position of roi can't be less than zero.");
+ }
+
+ if (roi.Y < 0)
+ {
+ throw new ArgumentOutOfRangeException("Roi.Y", roi.Y,
+ "The y position of roi can't be less than zero.");
+ }
+ }
+
+ /// <summary>
+ /// Releases the resources used by the <see cref="InferenceModelConfiguration"/> object.
+ /// </summary>
+ /// <param name="disposing">
+ /// true to release both managed and unmanaged resources, otherwise false to release only unmanaged resources.
+ /// </param>
+ /// <since_tizen> 6 </since_tizen>
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (_inferenceHandle != IntPtr.Zero)
+ {
+ InteropInference.Destroy(_inferenceHandle).Validate("Failed to destroy inference configuration");
+ _inferenceHandle = IntPtr.Zero;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Vision
+{
+ /// <summary>
+ /// Specifies the type of inference backend.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public enum InferenceBackendType
+ {
+ /// <summary>
+ /// OpenCV backend type
+ /// </summary>
+ OpenCV,
+
+ /// <summary>
+ /// Tensor Flow Lite backend type
+ /// </summary>
+ TFLite
+ }
+
+ /// <summary>
+ /// Specifies the type of target. It's used for running inference backend.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public enum InferenceTargetType
+ {
+ /// <summary>
+ /// CPU target
+ /// </summary>
+ CPU,
+
+ /// <summary>
+ /// GPU target
+ /// </summary>
+ GPU,
+
+ /// <summary>
+ /// Custom target
+ /// </summary>
+ Custom
+ }
+}
/// <summary>
/// Invalid path (Since 3.0).
/// </summary>
- InvalidPath = MediaVisionErrorCode | 0x04
+ InvalidPath = MediaVisionErrorCode | 0x04,
+ /// <summary>
+ /// Not supported engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ NotSupportedEngine = MediaVisionErrorCode | 0x05
}
internal static class MediaVisionErrorExtensions
switch (error)
{
case MediaVisionError.NotSupported:
+ case MediaVisionError.NotSupportedEngine:
throw new NotSupportedException(msg);
case MediaVisionError.MsgTooLong:
throw new ArgumentException($"{msg} : Message too long.");
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections.ObjectModel;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to get the result of object detection using <see cref="InferenceModelConfiguration"/> and
+ /// <see cref="ObjectDetector"/>.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public class ObjectDetectionResult
+ {
+ internal ObjectDetectionResult(int indice, string name, float confidence,
+ global::Interop.MediaVision.Rectangle location)
+ {
+ Indice = indice;
+ Name = name;
+ Confidence = confidence;
+ Location = location.ToApiStruct();
+ }
+
+ /// <summary>
+ /// Gets the indice of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public int Indice { get; }
+
+ /// <summary>
+ /// Gets the name of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets the confidence of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public float Confidence { get; }
+
+ /// <summary>
+ /// Gets the location of detected object.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public Rectangle Location { get; }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 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.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using InteropInference = Interop.MediaVision.Inference;
+
+namespace Tizen.Multimedia.Vision
+{
+ /// <summary>
+ /// Provides the ability to detect objects and get its locations on image source using inference engine.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public static class ObjectDetector
+ {
+ /// <summary>
+ /// Detects objects and gets its locations on the source image using inference engine set in <paramref name="config"/>.<br/>
+ /// Each time when DetectAsync is called, a set of the detected objects at the media source are received asynchronously.
+ /// </summary>
+ /// <feature>http://tizen.org/feature/vision.inference</feature>
+ /// <feature>http://tizen.org/feature/vision.inference.image</feature>
+ /// <param name="source">The source of the media where faces will be detected.</param>
+ /// <param name="config">The engine's configuration that will be used for detecting.</param>
+ /// <returns>
+ /// A task that represents the asynchronous detect operation.<br/>
+ /// If there's no detected object, empty collection will be returned.
+ /// </returns>
+ /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="config"/> is null.</exception>
+ /// <exception cref="InvalidOperationException">Internal error.</exception>
+ /// <exception cref="NotSupportedException">The feature is not supported.</exception>
+ /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
+ /// <seealso cref="InferenceModelConfiguration"/>
+ /// <since_tizen> 6 </since_tizen>
+ public static async Task<IEnumerable<ObjectDetectionResult>> DetectAsync(MediaVisionSource source,
+ InferenceModelConfiguration config)
+ {
+ // `vision.inference` feature is already checked, when config is created.
+ ValidationUtil.ValidateFeatureSupported(VisionFeatures.InferenceImage);
+
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (config == null)
+ {
+ throw new ArgumentNullException(nameof(config));
+ }
+
+ var tcs = new TaskCompletionSource<IEnumerable<ObjectDetectionResult>>();
+
+ using (var cb = ObjectKeeper.Get(GetCallback(tcs)))
+ {
+ InteropInference.DetectObject(source.Handle, config.GetHandle(), cb.Target).
+ Validate("Failed to detect object.");
+
+ return await tcs.Task;
+ }
+ }
+
+ private static InteropInference.ObjectDetectedCallback GetCallback(TaskCompletionSource<IEnumerable<ObjectDetectionResult>> tcs)
+ {
+ return (IntPtr sourceHandle, int numberOfObjects, int[] indices, string[] names, float[] confidences,
+ global::Interop.MediaVision.Rectangle[] locations, IntPtr _) =>
+ {
+ try
+ {
+ if (!tcs.TrySetResult(GetResults(numberOfObjects, indices, names, confidences, locations)))
+ {
+ Log.Error(MediaVisionLog.Tag, "Failed to set object detection result.");
+ }
+ }
+ catch (Exception e)
+ {
+ tcs.TrySetException(e);
+ }
+ };
+ }
+
+ private static IEnumerable<ObjectDetectionResult> GetResults(int number, int[] indices,
+ string[] names, float[] confidences, global::Interop.MediaVision.Rectangle[] locations)
+ {
+ if (number == 0)
+ {
+ return Enumerable.Empty<ObjectDetectionResult>();
+ }
+
+ var results = new List<ObjectDetectionResult>();
+
+ for (int i = 0; i < number; i++)
+ {
+ results.Add(new ObjectDetectionResult(indices[i], names[i], confidences[i], locations[i]));
+ }
+
+ return results;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 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
+{
+ internal static class VisionFeatures
+ {
+ internal const string InferenceFace = "http://tizen.org/feature/vision.inference.face";
+ internal const string InferenceImage = "http://tizen.org/feature/vision.inference.image";
+ }
+}
/// <returns>The parent window of the window.</returns>
/// <since_tizen> 6 </since_tizen>
public Window GetParent() {
- Window ret = new Window(Interop.Window.GetParent(swigCPtr), true);
+ Window ret = Registry.GetManagedBaseHandleFromNativePtr(Interop.Window.GetParent(swigCPtr)) as Window;
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
return ret;
}
private bool _cacheEnabled = false;
private ResourceOptions _options;
- private static int _responseCallbackId = 1;
- private static IDictionary<IntPtr, Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback> _responseCallbacksMap = new ConcurrentDictionary<IntPtr, Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback>();
+ private static int _responseCompletionId = 1;
+ private static IDictionary<IntPtr, TaskCompletionSource<RemoteResponse>> _taskCompletionMap = new ConcurrentDictionary<IntPtr, TaskCompletionSource<RemoteResponse>>();
+
+ private static Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback _getResultCallback = NativeGetResultCallbackHandler;
+ private static Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback _putResultCallback = NativePutResultCallbackHandler;
+ private static Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback _postResultCallback = NativePostResultCallbackHandler;
+ private static Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback _deleteResultCallback = NativeDeleteResultCallbackHandler;
private Interop.IoTConnectivity.Client.RemoteResource.CachedRepresentationChangedCallback _cacheUpdatedCallback;
private Interop.IoTConnectivity.Client.RemoteResource.StateChangedCallback _stateChangedCallback;
TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
IntPtr id = IntPtr.Zero;
- lock (_responseCallbacksMap)
+ lock (_taskCompletionMap)
{
- id = (IntPtr)_responseCallbackId++;
+ id = (IntPtr)_responseCompletionId++;
}
- _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
- {
- IntPtr responseCallbackId = userData;
- _responseCallbacksMap.Remove(responseCallbackId);
-
- if (responseHandle != IntPtr.Zero)
- {
- try
- {
- tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
- }
- catch(Exception exp)
- {
- Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
- tcsRemoteResponse.TrySetException(exp);
- }
- }
- else
- {
- tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
- }
- };
+ _taskCompletionMap[id] = tcsRemoteResponse;
IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
- int errCode = Interop.IoTConnectivity.Client.RemoteResource.Get(_remoteResourceHandle, queryHandle, _responseCallbacksMap[id], id);
+ int errCode = Interop.IoTConnectivity.Client.RemoteResource.Get(_remoteResourceHandle, queryHandle, _getResultCallback, id);
if (errCode != (int)IoTConnectivityError.None)
{
Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get resource attributes");
return await tcsRemoteResponse.Task;
}
+ private static void NativeGetResultCallbackHandler(IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData)
+ {
+ IntPtr responseCompletionId = userData;
+ TaskCompletionSource<RemoteResponse> responseCompletionSource = _taskCompletionMap[responseCompletionId];
+ _taskCompletionMap.Remove(responseCompletionId);
+
+ if (responseHandle != IntPtr.Zero)
+ {
+ try
+ {
+ responseCompletionSource.TrySetResult(GetRemoteResponse(responseHandle));
+ }
+ catch(Exception exp)
+ {
+ Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
+ responseCompletionSource.TrySetException(exp);
+ }
+ }
+ else
+ {
+ responseCompletionSource.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
+ }
+ }
+
/// <summary>
/// Puts the representation of a resource asynchronously.
/// </summary>
TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
IntPtr id = IntPtr.Zero;
- lock (_responseCallbacksMap)
+ lock (_taskCompletionMap)
{
- id = (IntPtr)_responseCallbackId++;
+ id = (IntPtr)_responseCompletionId++;
}
- _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
- {
- IntPtr responseCallbackId = userData;
- _responseCallbacksMap.Remove(responseCallbackId);
+ _taskCompletionMap[id] = tcsRemoteResponse;
- if (err == (int)(IoTConnectivityError.Iotivity))
- {
- RemoteResponse response = new RemoteResponse();
- response.Result = ResponseCode.Forbidden;
- response.Representation = null;
- tcsRemoteResponse.TrySetResult(response);
- }
- else if (responseHandle != IntPtr.Zero)
- {
- try
- {
- tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
- }
- catch (Exception exp)
- {
- Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
- tcsRemoteResponse.TrySetException(exp);
- }
- }
- else
- {
- tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
- }
- };
IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
- int errCode = Interop.IoTConnectivity.Client.RemoteResource.Put(_remoteResourceHandle, representation._representationHandle, queryHandle, _responseCallbacksMap[id], id);
+ int errCode = Interop.IoTConnectivity.Client.RemoteResource.Put(_remoteResourceHandle, representation._representationHandle, queryHandle, _putResultCallback, id);
if (errCode != (int)IoTConnectivityError.None)
{
Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to put resource representation");
return await tcsRemoteResponse.Task;
}
+ private static void NativePutResultCallbackHandler(IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData)
+ {
+ IntPtr responseCompletionId = userData;
+ TaskCompletionSource<RemoteResponse> responseCompletionSource = _taskCompletionMap[responseCompletionId];
+ _taskCompletionMap.Remove(responseCompletionId);
+
+ if (err == (int)(IoTConnectivityError.Iotivity))
+ {
+ RemoteResponse response = new RemoteResponse();
+ response.Result = ResponseCode.Forbidden;
+ response.Representation = null;
+ responseCompletionSource.TrySetResult(response);
+ }
+ else if (responseHandle != IntPtr.Zero)
+ {
+ try
+ {
+ responseCompletionSource.TrySetResult(GetRemoteResponse(responseHandle));
+ }
+ catch (Exception exp)
+ {
+ Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
+ responseCompletionSource.TrySetException(exp);
+ }
+ }
+ else
+ {
+ responseCompletionSource.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
+ }
+ }
+
/// <summary>
/// Posts request on a resource asynchronously.
/// </summary>
TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
IntPtr id = IntPtr.Zero;
- lock (_responseCallbacksMap)
+ lock (_taskCompletionMap)
{
- id = (IntPtr)_responseCallbackId++;
+ id = (IntPtr)_responseCompletionId++;
}
- _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
- {
- IntPtr responseCallbackId = userData;
- _responseCallbacksMap.Remove(responseCallbackId);
+ _taskCompletionMap[id] = tcsRemoteResponse;
- if (responseHandle != IntPtr.Zero)
- {
- try
- {
- tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
- }
- catch (Exception exp)
- {
- Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
- tcsRemoteResponse.TrySetException(exp);
- }
- }
- else
- {
- tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
- }
- };
IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
- int errCode = Interop.IoTConnectivity.Client.RemoteResource.Post(_remoteResourceHandle, representation._representationHandle, queryHandle, _responseCallbacksMap[id], id);
+ int errCode = Interop.IoTConnectivity.Client.RemoteResource.Post(_remoteResourceHandle, representation._representationHandle, queryHandle, _postResultCallback, id);
if (errCode != (int)IoTConnectivityError.None)
{
Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to post request");
return await tcsRemoteResponse.Task;
}
+ private static void NativePostResultCallbackHandler(IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData)
+ {
+ IntPtr responseCompletionId = userData;
+ TaskCompletionSource<RemoteResponse> responseCompletionSource = _taskCompletionMap[responseCompletionId];
+ _taskCompletionMap.Remove(responseCompletionId);
+
+ if (responseHandle != IntPtr.Zero)
+ {
+ try
+ {
+ responseCompletionSource.TrySetResult(GetRemoteResponse(responseHandle));
+ }
+ catch (Exception exp)
+ {
+ Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
+ responseCompletionSource.TrySetException(exp);
+ }
+ }
+ else
+ {
+ responseCompletionSource.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
+ }
+ }
+
/// <summary>
/// Deletes the resource asynchronously.
/// </summary>
TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
IntPtr id = IntPtr.Zero;
- lock (_responseCallbacksMap)
+ lock (_taskCompletionMap)
{
- id = (IntPtr)_responseCallbackId++;
+ id = (IntPtr)_responseCompletionId++;
}
- _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
+
+ _taskCompletionMap[id] = tcsRemoteResponse;
+
+ int errCode = Interop.IoTConnectivity.Client.RemoteResource.Delete(_remoteResourceHandle, _deleteResultCallback, id);
+ if (errCode != (int)IoTConnectivityError.None)
{
- IntPtr responseCallbackId = userData;
+ Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to delete");
+ tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
+ }
+ return await tcsRemoteResponse.Task;
+ }
- _responseCallbacksMap.Remove(responseCallbackId);
+ private static void NativeDeleteResultCallbackHandler(IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData)
+ {
+ IntPtr responseCompletionId = userData;
+ TaskCompletionSource<RemoteResponse> responseCompletionSource = _taskCompletionMap[responseCompletionId];
+ _taskCompletionMap.Remove(responseCompletionId);
- if (err == (int)(IoTConnectivityError.Iotivity))
- {
- RemoteResponse response = new RemoteResponse();
- response.Result = ResponseCode.Forbidden;
- response.Representation = null;
- tcsRemoteResponse.TrySetResult(response);
- }
- else if (responseHandle != IntPtr.Zero)
+ if (err == (int)(IoTConnectivityError.Iotivity))
+ {
+ RemoteResponse response = new RemoteResponse();
+ response.Result = ResponseCode.Forbidden;
+ response.Representation = null;
+ responseCompletionSource.TrySetResult(response);
+ }
+ else if (responseHandle != IntPtr.Zero)
+ {
+ try
{
- try
- {
- tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
- }
- catch (Exception exp)
- {
- Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
- tcsRemoteResponse.TrySetException(exp);
- }
+ responseCompletionSource.TrySetResult(GetRemoteResponse(responseHandle));
}
- else
+ catch (Exception exp)
{
- tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
+ Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
+ responseCompletionSource.TrySetException(exp);
}
- };
-
- int errCode = Interop.IoTConnectivity.Client.RemoteResource.Delete(_remoteResourceHandle, _responseCallbacksMap[id], id);
- if (errCode != (int)IoTConnectivityError.None)
+ }
+ else
{
- Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to delete");
- tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
+ responseCompletionSource.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
}
- return await tcsRemoteResponse.Task;
}
/// <summary>
Policy = (ResourcePolicy)policy;
}
- private RemoteResponse GetRemoteResponse(IntPtr response)
+ private static RemoteResponse GetRemoteResponse(IntPtr response)
{
int result;
IntPtr representationHandle, optionsHandle;
}
}
+ /// <summary>
+ /// Indicates whether rotary event is enabled on the device.
+ /// </summary>
+ /// <privilege>http://tizen.org/privilege/systemsettings.admin</privilege>
+ /// <privlevel>platform</privlevel>
+ /// <feature>http://tizen.org/feature/systemsetting</feature>
+ /// <exception cref="NotSupportedException">Not Supported feature</exception>
+ /// <exception cref="UnauthorizedAccessException">Thrown when application does not have privilege to access this method.</exception>
+ /// <remarks>
+ /// http://tizen.org/privilege/systemsettings.admin is needed only for setting value. When getting the value, it isn't needed.
+ /// </remarks>
+ /// <since_tizen> 6 </since_tizen>
+ public static bool RotaryEventEnabled
+ {
+ get
+ {
+ bool isRotaryEventEnabled;
+ SystemSettingsError res = (SystemSettingsError)Interop.Settings.SystemSettingsGetValueBool(SystemSettingsKeys.RotaryEventEnabled, out isRotaryEventEnabled);
+ if (res != SystemSettingsError.None)
+ {
+ throw SystemSettingsExceptionFactory.CreateException(res, "unable to get isRotaryEventEnabled system setting.");
+ }
+ return isRotaryEventEnabled;
+ }
+ set
+ {
+ SystemSettingsError res = (SystemSettingsError)Interop.Settings.SystemSettingsSetValueBool(SystemSettingsKeys.RotaryEventEnabled, value);
+ if (res != SystemSettingsError.None)
+ {
+ throw SystemSettingsExceptionFactory.CreateException(res, "unable to set isRotaryEventEnabled system setting.");
+ }
+ }
+ }
+
private static readonly Interop.Settings.SystemSettingsChangedCallback s_incomingCallRingtoneChangedCallback = (SystemSettingsKeys key, IntPtr userData) =>
{
string path = SystemSettings.IncomingCallRingtone;
}
}
}
+
+ private static readonly Interop.Settings.SystemSettingsChangedCallback s_rotaryEventEnabledChangedCallback = (SystemSettingsKeys key, IntPtr userData) =>
+ {
+ bool rotaryEventEnabled = SystemSettings.RotaryEventEnabled;
+ RotaryEventEnabledChangedEventArgs eventArgs = new RotaryEventEnabledChangedEventArgs(rotaryEventEnabled);
+ s_rotaryEventEnabledChanged?.Invoke(null, eventArgs);
+ };
+ private static event EventHandler<RotaryEventEnabledChangedEventArgs> s_rotaryEventEnabledChanged;
+ /// <summary>
+ /// The RotaryEventEnabledChanged event is triggered when the RotaryEventEnabled value is changed.
+ /// </summary>
+ /// <privilege>http://tizen.org/privilege/systemsettings.admin</privilege>
+ /// <privlevel>platform</privlevel>
+ /// <feature>http://tizen.org/feature/systemsetting</feature>
+ /// <exception cref="NotSupportedException">Not Supported feature</exception>
+ /// <exception cref="UnauthorizedAccessException">Thrown when application does not have privilege to access this method.</exception>
+ /// <remarks>
+ /// http://tizen.org/privilege/systemsettings.admin is needed only for setting value. When getting the value, it isn't needed.
+ /// </remarks>
+ /// <since_tizen> 6 </since_tizen>
+ public static event EventHandler<RotaryEventEnabledChangedEventArgs> RotaryEventEnabledChanged
+ {
+ add
+ {
+ if (s_rotaryEventEnabledChanged == null)
+ {
+ SystemSettingsError ret = (SystemSettingsError)Interop.Settings.SystemSettingsSetCallback(SystemSettingsKeys.RotaryEventEnabled, s_rotaryEventEnabledChangedCallback, IntPtr.Zero);
+ if (ret != SystemSettingsError.None)
+ {
+ throw SystemSettingsExceptionFactory.CreateException(ret, "Error in callback handling");
+ }
+ }
+ s_rotaryEventEnabledChanged += value;
+ }
+
+ remove
+ {
+ s_rotaryEventEnabledChanged -= value;
+ if (s_rotaryEventEnabledChanged == null)
+ {
+ SystemSettingsError ret = (SystemSettingsError)Interop.Settings.SystemSettingsRemoveCallback(SystemSettingsKeys.RotaryEventEnabled, s_rotaryEventEnabledChangedCallback);
+ if (ret != SystemSettingsError.None)
+ {
+ throw SystemSettingsExceptionFactory.CreateException(ret, "Error in callback handling");
+ }
+ }
+ }
+ }
}
}
/// GET (bool) Indicates whether accessibility negative color is enabled on the device.
/// </summary>
/// <since_tizen> 6 </since_tizen>
- AccessibilityNegativeColor
+ AccessibilityNegativeColor,
+ /// <summary>
+ /// GET (bool) Indicates whether rotary event is enabled on the device.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ RotaryEventEnabled
}
/// <summary>
/// Enumeration for the Idle Lock State.
}
/// <summary>
- /// Indicates whether developer option state is enabled on the device or not.
+ /// Indicates whether accessibility grayscale is enabled on the device or not.
/// </summary>
/// <since_tizen> 6 </since_tizen>
public bool Value
}
/// <summary>
- /// Indicates whether developer option state is enabled on the device or not.
+ /// Indicates whether accessibility negative color is enabled on the device or not.
/// </summary>
/// <since_tizen> 6 </since_tizen>
public bool Value
}
}
}
+
+ /// <summary>
+ /// EventArgs type for the RotaryEventEnabledChanged event.
+ /// </summary>
+ /// <privilege>http://tizen.org/privilege/systemsettings.admin</privilege>
+ /// <privlevel>platform</privlevel>
+ /// <feature>http://tizen.org/feature/systemsetting</feature>
+ /// <exception cref="NotSupportedException">Not Supported feature</exception>
+ /// <exception cref="UnauthorizedAccessException">Thrown when application does not have privilege to access this method.</exception>
+ /// <remarks>
+ /// http://tizen.org/privilege/systemsettings.admin is needed only for setting value. When getting the value, it isn't needed.
+ /// </remarks>
+ /// <since_tizen> 6 </since_tizen>
+ public class RotaryEventEnabledChangedEventArgs : EventArgs
+ {
+ internal RotaryEventEnabledChangedEventArgs(bool val)
+ {
+ Value = val;
+ }
+
+ /// <summary>
+ /// Indicates whether rotary event enable is enabled on the device or not.
+ /// </summary>
+ /// <since_tizen> 6 </since_tizen>
+ public bool Value { get; }
+ }
}
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
+ <StartupObject></StartupObject>
+ <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Tizen.NET" Version="6.0.0.14697">
- <ExcludeAssets>Runtime</ExcludeAssets>
- </PackageReference>
- <PackageReference Include="Tizen.NET.Sdk" Version="1.0.1" />
+ <PackageReference Include="Tizen.NET" Version="6.2.0" />
+ <PackageReference Include="Tizen.NET.API6" Version="6.2.0" />
+ <PackageReference Include="Tizen.NET.Internals" Version="6.2.0" />
+ <PackageReference Include="Tizen.NET.Sdk" Version="1.0.3" />
</ItemGroup>
</Project>
using System.Threading.Tasks;
using System;
using Tizen.System;
-
+using Tizen;
// /opt/usr/data/settings/Ringtones/ringtone_sdk.mp3
namespace SystemSettingsUnitTest
{
Assert.IsTrue(e.Value == s_developerOptionStateValue, "OnDeveloperOptionStateChanged: The callback should receive the latest value for the property.");
}
-#if true
// AccessibilityGrayscale
////[Test]
//[Category("P1")]
Assert.IsInstanceOf<bool>(e.Value, "OnAccessibilityNegativeColorChanged: AccessibilityNegativeColor not an instance of string");
Assert.IsTrue(s_accessibilityNegativeColorValue == e.Value, "OnAccessibilityNegativeColorChanged: The callback should receive the latest value for the property.");
}
-#endif
+ // RotaryEventEnabled
+ ////[Test]
+ //[Category("P1")]
+ //[Description("Test if set/get for SystemSettings:RotaryEventEnabled is working properly")]
+ //[Property("SPEC", "Tizen.System.SystemSettings.RotaryEventEnabled A")]
+ //[Property("SPEC_URL", "-")]
+ //[Property("CRITERIA", "PRW")]
+ //[Property("AUTHOR", "Aditya Aswani, a.aswani@samsung.com")]
+ public static void RotaryEventEnabled_READ_WRITE()
+ {
+ LogUtils.StartTest();
+ /* TEST CODE */
+ Assert.IsInstanceOf<bool>(Tizen.System.SystemSettings.RotaryEventEnabled, "RotaryEventEnabled_READ_WRITE: RotaryEventEnabled not an instance of string");
+ bool preValue = Tizen.System.SystemSettings.RotaryEventEnabled;
+ var setValue = !preValue;
+
+ Tizen.System.SystemSettings.RotaryEventEnabled = setValue;
+ var getValue = Tizen.System.SystemSettings.RotaryEventEnabled;
+ Assert.IsTrue(getValue == setValue, "RotaryEventEnabled_READ_WRITE: Set value and get value of the property should be same.");
+ Tizen.System.SystemSettings.RotaryEventEnabled = preValue;
+ LogUtils.WriteOK();
+ }
+
+ private static bool s_rotaryEventEnabledCallbackCalled = false;
+ private static bool s_rotaryEventEnabledValue = false;
+ ////[Test]
+ //[Category("P1")]
+ //[Description("Check if callback to SystemSettings:RotaryEventEnabledChanged event is called")]
+ //[Property("SPEC", "Tizen.System.SystemSettings.RotaryEventEnabledChanged E")]
+ //[Property("SPEC_URL", "-")]
+ //[Property("CRITERIA", "EVL")]
+ //[Property("AUTHOR", "Aditya Aswani, a.aswani@samsung.com")]
+ public static async Task RotaryEventEnabledChanged_CHECK_EVENT()
+ {
+ LogUtils.StartTest();
+ /* TEST CODE */
+ Tizen.System.SystemSettings.RotaryEventEnabledChanged += OnRotaryEventEnabledChanged;
+ bool preValue = Tizen.System.SystemSettings.RotaryEventEnabled;
+ s_rotaryEventEnabledValue = !preValue;
+ Tizen.System.SystemSettings.RotaryEventEnabled = s_rotaryEventEnabledValue;
+ await Task.Delay(2000);
+ Assert.IsTrue(s_rotaryEventEnabledCallbackCalled, "RotaryEventEnabledChanged_CHECK_EVENT: EventHandler added. Not getting called");
+ s_rotaryEventEnabledCallbackCalled = false;
+ Tizen.System.SystemSettings.RotaryEventEnabledChanged -= OnRotaryEventEnabledChanged;
+ Tizen.System.SystemSettings.RotaryEventEnabled = !s_rotaryEventEnabledValue;
+ await Task.Delay(2000);
+ Assert.IsFalse(s_rotaryEventEnabledCallbackCalled, "RotaryEventEnabledChanged_CHECK_EVENT: EventHandler removed. Still getting called");
+ Tizen.System.SystemSettings.RotaryEventEnabled = preValue;
+ LogUtils.WriteOK();
+ }
+ private static void OnRotaryEventEnabledChanged(object sender, Tizen.System.RotaryEventEnabledChangedEventArgs e)
+ {
+ s_rotaryEventEnabledCallbackCalled = true;
+ Assert.IsInstanceOf<bool>(e.Value, "OnRotaryEventEnabledChanged: RotaryEventEnabled not an instance of string");
+ Assert.IsTrue(s_rotaryEventEnabledValue == e.Value, "OnRotaryEventEnabledChanged: The callback should receive the latest value for the property.");
+ }
public static async void TestAllAsync()