Merge remote-tracking branch 'origin/master' into tizen
authoradmin <tizenapi@samsung.com>
Tue, 1 Oct 2019 15:52:05 +0000 (15:52 +0000)
committeradmin <tizenapi@samsung.com>
Tue, 1 Oct 2019 15:52:05 +0000 (15:52 +0000)
36 files changed:
src/Tizen.Applications.ComponentBased.Default/Tizen.Applications.ComponentBased.Default/EFLWindowInfo.cs [changed mode: 0755->0644]
src/Tizen.Applications.ComponentBased/Tizen.Applications.ComponentBased.Common/FrameComponentStateManager.cs [changed mode: 0755->0644]
src/Tizen.Applications.ComponentBased/Tizen.Applications.ComponentBased.Common/IWindowInfo.cs
src/Tizen.Messaging/Tizen.Messaging.Email/EmailSender.cs
src/Tizen.Multimedia.MediaPlayer/Interop/Interop.Player.cs
src/Tizen.Multimedia.MediaPlayer/Player/AudioEffect.cs
src/Tizen.Multimedia.MediaPlayer/Player/Player.Properties.cs
src/Tizen.Multimedia.MediaPlayer/Player/Player.cs
src/Tizen.Multimedia.MediaPlayer/Player/PlayerEnums.cs
src/Tizen.Multimedia.MediaPlayer/Player/PlayerError.cs
src/Tizen.Multimedia.Vision/Interop/Interop.MediaVision.Common.cs [changed mode: 0755->0644]
src/Tizen.Multimedia.Vision/Interop/Interop.MediaVision.Inference.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/EngineConfiguration.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/FaceDetectionResult.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/FaceDetector.cs [changed mode: 0755->0644]
src/Tizen.Multimedia.Vision/MediaVision/FaceRecognitionModel.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/FaceTrackingModel.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/FacialLandmarkDetector.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/ImageClassificationResult.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/ImageClassifier.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/ImageObject.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/ImageTrackingModel.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/InferenceModelConfiguration.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/InferenceType.cs [new file with mode: 0755]
src/Tizen.Multimedia.Vision/MediaVision/MediaVisionError.cs
src/Tizen.Multimedia.Vision/MediaVision/MediaVisionSource.cs [changed mode: 0644->0755]
src/Tizen.Multimedia.Vision/MediaVision/ObjectDetectionResult.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/ObjectDetector.cs [new file with mode: 0644]
src/Tizen.Multimedia.Vision/MediaVision/VisionFeatures.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Window.cs
src/Tizen.Network.IoTConnectivity/Tizen.Network.IoTConnectivity/RemoteResource.cs
src/Tizen.System.SystemSettings/Tizen.System.SystemSettings/SystemSettings.cs
src/Tizen.System.SystemSettings/Tizen.System.SystemSettings/SystemSettingsEnums.cs
src/Tizen.System.SystemSettings/Tizen.System.SystemSettings/SystemSettingsEventArgs.cs
test/Tizen.System.SystemSettings.UnitTest/SystemSettings.UnitTest/SystemSettings.UnitTest.csproj
test/Tizen.System.SystemSettings.UnitTest/SystemSettings.UnitTest/test/TSSystemSettings.cs

old mode 100755 (executable)
new mode 100644 (file)
index d2ba807..e899a0c
@@ -13,6 +13,7 @@ namespace Tizen.Applications.ComponentBased.Default
         private const string LogTag = "Tizen.Applications";
         private Window _win;
         private int _resId;
+        private bool _disposed = false;
 
         /// <summary>
         /// Initializes the EFLWindow class.
@@ -44,6 +45,34 @@ namespace Tizen.Applications.ComponentBased.Default
             }
         }
 
+        /// <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);
+        }
     }
 
 }
old mode 100755 (executable)
new mode 100644 (file)
index f850e8d..702ce48
@@ -8,6 +8,7 @@ namespace Tizen.Applications.ComponentBased.Common
     {
         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)
         {
@@ -47,6 +48,7 @@ namespace Tizen.Applications.ComponentBased.Common
             if (win == null)
                 return IntPtr.Zero;
 
+            _winDic.Add(id, win);
             if (!fc.OnCreate())
             {
                 Log.Error(LogTag, "OnCreate fail");
@@ -115,6 +117,7 @@ namespace Tizen.Applications.ComponentBased.Common
                 if (fc.Handle == context)
                 {
                     fc.OnDestroy();
+                    _winDic[fc.Id].Dispose();
                     RemoveComponent(fc);
                     break;
                 }
index 16df436..61bfb7c 100644 (file)
@@ -6,7 +6,7 @@ namespace Tizen.Applications.ComponentBased.Common
     /// Interface for window information
     /// </summary>
     /// <since_tizen> 6 </since_tizen>
-    public interface IWindowInfo
+    public interface IWindowInfo : IDisposable
     {
         /// <summary>
         /// Gets window resource ID
index a153a0d..3d60634 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 using System;
+using System.Collections.Generic;
 using System.Threading.Tasks;
 
 namespace Tizen.Messaging.Email
@@ -25,6 +26,9 @@ 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>
@@ -40,15 +44,18 @@ namespace Tizen.Messaging.Email
             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);
             }
 
@@ -56,19 +63,20 @@ namespace Tizen.Messaging.Email
             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;
         }
+
     }
 }
index 1818a01..585510f 100644 (file)
@@ -381,6 +381,12 @@ internal static partial class Interop
 
         [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
index d58edef..7319b69 100644 (file)
@@ -31,7 +31,7 @@ namespace Tizen.Multimedia
         {
             Player = owner;
 
-            if (IsAvailable== false)
+            if (IsAvailable == false)
             {
                 return;
             }
@@ -82,12 +82,17 @@ namespace Tizen.Multimedia
         /// <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");
         }
@@ -98,6 +103,8 @@ namespace Tizen.Multimedia
         /// <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
         {
@@ -105,6 +112,9 @@ namespace Tizen.Multimedia
             {
                 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");
 
@@ -118,6 +128,8 @@ namespace Tizen.Multimedia
         /// <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
         {
@@ -125,6 +137,9 @@ namespace Tizen.Multimedia
             {
                 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");
 
@@ -135,9 +150,13 @@ namespace Tizen.Multimedia
         /// <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
         {
index fd90eee..0fb6bba 100644 (file)
@@ -545,12 +545,15 @@ namespace Tizen.Multimedia
         /// </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
         {
@@ -579,14 +582,17 @@ namespace Tizen.Multimedia
         /// </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
         {
@@ -616,7 +622,8 @@ namespace Tizen.Multimedia
         /// </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/>
@@ -628,7 +635,9 @@ namespace Tizen.Multimedia
         ///     -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
         {
@@ -667,6 +676,56 @@ namespace Tizen.Multimedia
             }
         }
 
+        /// <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>
index e1693a1..5d6518e 100644 (file)
@@ -966,7 +966,8 @@ namespace Tizen.Multimedia
         /// <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">
@@ -976,8 +977,10 @@ namespace Tizen.Multimedia
         ///     -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)
         {
@@ -1008,10 +1011,13 @@ namespace Tizen.Multimedia
         /// 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()
         {
@@ -1026,15 +1032,15 @@ namespace Tizen.Multimedia
         /// <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>
index 024f083..37628de 100644 (file)
@@ -323,4 +323,23 @@ namespace Tizen.Multimedia
         /// </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,
+    }
 }
index 70146da..996a8b0 100644 (file)
@@ -48,7 +48,8 @@ namespace Tizen.Multimedia
         ServiceDisconnected = PlayerErrorClass | 0x0d,
         NotSupportedAudioCodec = PlayerErrorClass | 0x0e,
         NotSupportedVideoCodec = PlayerErrorClass | 0x0f,
-        NotSupportedSubtitle = PlayerErrorClass | 0x10
+        NotSupportedSubtitle = PlayerErrorClass | 0x10,
+        NotAvailable = PlayerErrorClass | 0x12
     }
 
     internal static class PlayerErrorCodeExtensions
@@ -129,6 +130,9 @@ namespace Tizen.Multimedia
 
                 case PlayerErrorCode.NotSupportedVideoCodec:
                     throw new CodecNotSupportedException(CodecKind.Video);
+
+                case PlayerErrorCode.NotAvailable:
+                    throw new NotAvailableException(msg);
             }
 
             return null;
@@ -166,5 +170,21 @@ namespace Tizen.Multimedia
         {
         }
     }
+
+    /// <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)
+        {
+        }
+    }
 }
 
old mode 100755 (executable)
new mode 100644 (file)
index 3762322..a6e1645
@@ -80,7 +80,7 @@ internal static partial class Interop
         };
     }
 
-    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];
 
@@ -91,6 +91,17 @@ internal static partial class Interop
         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>
@@ -176,6 +187,10 @@ internal static partial class Interop
             [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);
 
@@ -187,6 +202,10 @@ internal static partial class Interop
 
             [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);
         }
     }
 }
diff --git a/src/Tizen.Multimedia.Vision/Interop/Interop.MediaVision.Inference.cs b/src/Tizen.Multimedia.Vision/Interop/Interop.MediaVision.Inference.cs
new file mode 100644 (file)
index 0000000..9f93b0e
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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));
+        }
+    }
+}
old mode 100644 (file)
new mode 100755 (executable)
index 964f00f..38cda12
@@ -111,6 +111,11 @@ namespace Tizen.Multimedia.Vision
             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;
@@ -147,6 +152,38 @@ namespace Tizen.Multimedia.Vision
             }
         }
 
+        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>
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/FaceDetectionResult.cs b/src/Tizen.Multimedia.Vision/MediaVision/FaceDetectionResult.cs
new file mode 100644 (file)
index 0000000..899e1e6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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; }
+    }
+}
old mode 100755 (executable)
new mode 100644 (file)
index 71c4d8c..bec227f
  */
 
 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.
@@ -43,7 +49,7 @@ namespace Tizen.Multimedia.Vision
         /// <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>
@@ -103,5 +109,89 @@ namespace Tizen.Multimedia.Vision
                 }
             };
         }
+
+        /// <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;
+        }
     }
 }
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/FacialLandmarkDetector.cs b/src/Tizen.Multimedia.Vision/MediaVision/FacialLandmarkDetector.cs
new file mode 100644 (file)
index 0000000..7a50bdf
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/ImageClassificationResult.cs b/src/Tizen.Multimedia.Vision/MediaVision/ImageClassificationResult.cs
new file mode 100644 (file)
index 0000000..d7741b1
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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; }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/ImageClassifier.cs b/src/Tizen.Multimedia.Vision/MediaVision/ImageClassifier.cs
new file mode 100644 (file)
index 0000000..44ca7b6
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/InferenceModelConfiguration.cs b/src/Tizen.Multimedia.Vision/MediaVision/InferenceModelConfiguration.cs
new file mode 100644 (file)
index 0000000..2b53959
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ * 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;
+            }
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/InferenceType.cs b/src/Tizen.Multimedia.Vision/MediaVision/InferenceType.cs
new file mode 100755 (executable)
index 0000000..d2519f7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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
+    }
+}
index ef43a24..40e3a74 100755 (executable)
@@ -82,7 +82,12 @@ namespace Tizen.Multimedia.Vision
         /// <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
@@ -97,6 +102,7 @@ namespace Tizen.Multimedia.Vision
             switch (error)
             {
                 case MediaVisionError.NotSupported:
+                case MediaVisionError.NotSupportedEngine:
                     throw new NotSupportedException(msg);
                 case MediaVisionError.MsgTooLong:
                     throw new ArgumentException($"{msg} : Message too long.");
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/ObjectDetectionResult.cs b/src/Tizen.Multimedia.Vision/MediaVision/ObjectDetectionResult.cs
new file mode 100644 (file)
index 0000000..9922341
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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; }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/ObjectDetector.cs b/src/Tizen.Multimedia.Vision/MediaVision/ObjectDetector.cs
new file mode 100644 (file)
index 0000000..31caef6
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/src/Tizen.Multimedia.Vision/MediaVision/VisionFeatures.cs b/src/Tizen.Multimedia.Vision/MediaVision/VisionFeatures.cs
new file mode 100644 (file)
index 0000000..31e25cd
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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";
+    }
+}
index 9a6edd9..4f40777 100755 (executable)
@@ -1524,7 +1524,7 @@ namespace Tizen.NUI
         /// <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;
         }
index d8101b1..ccc7ec5 100755 (executable)
@@ -38,8 +38,13 @@ namespace Tizen.Network.IoTConnectivity
         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;
@@ -398,36 +403,15 @@ namespace Tizen.Network.IoTConnectivity
             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");
@@ -436,6 +420,30 @@ namespace Tizen.Network.IoTConnectivity
             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>
@@ -451,42 +459,15 @@ namespace Tizen.Network.IoTConnectivity
             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");
@@ -495,6 +476,37 @@ namespace Tizen.Network.IoTConnectivity
             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>
@@ -510,35 +522,15 @@ namespace Tizen.Network.IoTConnectivity
             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");
@@ -547,6 +539,30 @@ namespace Tizen.Network.IoTConnectivity
             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>
@@ -560,48 +576,51 @@ namespace Tizen.Network.IoTConnectivity
             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>
@@ -842,7 +861,7 @@ namespace Tizen.Network.IoTConnectivity
             Policy = (ResourcePolicy)policy;
         }
 
-        private RemoteResponse GetRemoteResponse(IntPtr response)
+        private static RemoteResponse GetRemoteResponse(IntPtr response)
         {
             int result;
             IntPtr representationHandle, optionsHandle;
index 75b3ca6..9343522 100755 (executable)
@@ -1145,6 +1145,40 @@ namespace Tizen.System
             }
         }
 
+        /// <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;
@@ -2975,6 +3009,54 @@ namespace Tizen.System
                 }
             }
         }
+
+        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");
+                    }
+                }
+            }
+        }
     }
 }
 
index 2842e84..608bfae 100755 (executable)
@@ -181,7 +181,12 @@ namespace Tizen.System
         /// 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.
index e078ff9..8ad81c0 100755 (executable)
@@ -1142,7 +1142,7 @@ namespace Tizen.System
         }
 
         /// <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
@@ -1175,7 +1175,7 @@ namespace Tizen.System
         }
 
         /// <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
@@ -1186,4 +1186,30 @@ namespace Tizen.System
             }
         }
     }
+
+    /// <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; }
+    }
 }
index 595f0cd..c08e1ce 100755 (executable)
@@ -3,6 +3,8 @@
   <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>
index add665a..be52db2 100755 (executable)
@@ -2,7 +2,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using System;
 using Tizen.System;
-
+using Tizen;
 // /opt/usr/data/settings/Ringtones/ringtone_sdk.mp3
 namespace SystemSettingsUnitTest
 {
@@ -2148,7 +2148,6 @@ 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")]
@@ -2306,8 +2305,63 @@ namespace SystemSettingsUnitTest
             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()