Merge remote-tracking branch 'origin/master' into tizen
authorTizenAPI-Bot <tizenapi@samsung.com>
Tue, 11 Jun 2024 10:40:41 +0000 (10:40 +0000)
committerTizenAPI-Bot <tizenapi@samsung.com>
Tue, 11 Jun 2024 10:40:41 +0000 (10:40 +0000)
125 files changed:
packaging/csapi-tizenfx.spec
packaging/version.txt
src/Tizen.AIAvatar/src/Animations/AnimationInfo.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Info/AnimationInfo.cs with 54% similarity]
src/Tizen.AIAvatar/src/Animations/AvatarMotionChangedEventArgs.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AvatarMotionChangedEventArgs.cs with 67% similarity]
src/Tizen.AIAvatar/src/Animations/AvatarMotionState.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AvatarMotionState.cs with 94% similarity]
src/Tizen.AIAvatar/src/Animations/EyeBlinker.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/EyeBlinker.cs with 60% similarity]
src/Tizen.AIAvatar/src/Animations/IBlendShapeModule.cs [moved from src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeValue.cs with 68% similarity]
src/Tizen.AIAvatar/src/Animations/MotionPlayer.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/MotionPlayer.cs with 76% similarity]
src/Tizen.AIAvatar/src/Common/Avatar.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Common/AvatarInfo.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Common/AvatarProperties.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Common/AvatarPropertyMapper.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarPropertyMapper.cs with 66% similarity]
src/Tizen.AIAvatar/src/Common/BlendShapeType.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Common/BlendShapeType.cs with 97% similarity]
src/Tizen.AIAvatar/src/Common/JointType.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Common/JointType.cs with 97% similarity]
src/Tizen.AIAvatar/src/Common/NodeType.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Info/VoiceInfo.cs with 60% similarity]
src/Tizen.AIAvatar/src/Emotion/EmotionAnalyzer.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarLLM.cs with 80% similarity]
src/Tizen.AIAvatar/src/Emotion/EmotionController.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Extensions/AvatarExtension.cs
src/Tizen.AIAvatar/src/Extensions/AvatarScene.cs
src/Tizen.AIAvatar/src/Extensions/Interop.SceneView.cs [moved from src/Tizen.AIAvatar/src/internal/Avatar/Interop.SceneView.cs with 96% similarity]
src/Tizen.AIAvatar/src/Extensions/SceneViewExtension.cs
src/Tizen.AIAvatar/src/Internal/AIAvatar.cs [moved from src/Tizen.AIAvatar/src/internal/Avatar/AIAvatar.cs with 97% similarity]
src/Tizen.AIAvatar/src/Internal/AvatarBlendShapeIndex.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarBlendShapeIndex.cs with 65% similarity]
src/Tizen.AIAvatar/src/Internal/AvatarJointTransformIndex.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarJointTransformIndex.cs with 98% similarity]
src/Tizen.AIAvatar/src/Internal/DefaultAvatarProperties.cs [moved from src/Tizen.AIAvatar/src/internal/DefaultAvatar/DefaultAvatarProperties.cs with 95% similarity]
src/Tizen.AIAvatar/src/Internal/Utils.cs [moved from src/Tizen.AIAvatar/src/internal/Avatar/Utils.cs with 95% similarity]
src/Tizen.AIAvatar/src/Lipsync/AsyncLipSyncer.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AsyncLipSyncer.cs with 82% similarity]
src/Tizen.AIAvatar/src/Lipsync/LipSyncController.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/LipSyncer.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/LipSyncer.cs with 61% similarity]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/CustomMfccExtractor.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/DCT.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FFT.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FilterBank.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/IMFccExtractor.cs [moved from src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeVisemeInfo.cs with 75% similarity]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/PreEmphasis.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/Window.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/ISingleShotModel.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Common/NodeType.cs with 77% similarity]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/SoftmaxLinqExtension.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModuleData/LipSyncData.cs with 65% similarity]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/TFVowel6.cs [moved from src/Tizen.AIAvatar/src/internal/LipSync/Models/TFVowel6.cs with 77% similarity]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/AnimationConverter.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/AnimationKeyFrame.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/BlendShapeInfo.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/Exception.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelClassifier.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelConverter.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Multimedia/Audio/AudioOptions.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/AudioOptions.cs with 79% similarity]
src/Tizen.AIAvatar/src/Multimedia/Audio/AudioPlayer.cs [moved from src/Tizen.AIAvatar/src/internal/Multimedia/AudioPlayer.cs with 90% similarity]
src/Tizen.AIAvatar/src/Multimedia/Audio/AudioRecorder.cs [moved from src/Tizen.AIAvatar/src/internal/Multimedia/AudioRecorder.cs with 82% similarity]
src/Tizen.AIAvatar/src/Multimedia/Audio/RecordBufferChangedEventArgs.cs [moved from src/Tizen.AIAvatar/src/internal/Multimedia/RecordBufferChangedEventArgs.cs with 95% similarity]
src/Tizen.AIAvatar/src/Multimedia/TTS/TTSController.cs [moved from src/Tizen.AIAvatar/src/internal/Uix/TTSLipSyncer.cs with 84% similarity]
src/Tizen.AIAvatar/src/Multimedia/TTS/UtteranceText.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Multimedia/TTS/VoiceInfo.cs [new file with mode: 0644]
src/Tizen.AIAvatar/src/Multimedia/TTS/VoiceType.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModuleData/MotionBehaviorData.cs with 59% similarity]
src/Tizen.AIAvatar/src/RestClient/IRestClient.cs [moved from src/Tizen.AIAvatar/src/internal/Common/IRestClient.cs with 100% similarity]
src/Tizen.AIAvatar/src/RestClient/RestClient.cs [moved from src/Tizen.AIAvatar/src/internal/Common/RestClient.cs with 100% similarity]
src/Tizen.AIAvatar/src/RestClient/RestClientFactory.cs [moved from src/Tizen.AIAvatar/src/internal/Common/RestClientFactory.cs with 100% similarity]
src/Tizen.AIAvatar/src/Tracking/TrackingController.cs [moved from src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModuleData/IAnimationModuleData.cs with 64% similarity]
src/Tizen.AIAvatar/src/Tracking/TrackingOptions.cs [moved from src/Tizen.AIAvatar/src/internal/Uix/UtteranceText.cs with 80% similarity]
src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeInfo.cs [deleted file]
src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeModelInfo.cs [deleted file]
src/Tizen.AIAvatar/src/internal/LipSync/LipInfo.cs [deleted file]
src/Tizen.AIAvatar/src/internal/LipSync/Models/ISingleShotModel.cs [deleted file]
src/Tizen.AIAvatar/src/internal/LipSync/Models/SoftmaxLinqExtension.cs [deleted file]
src/Tizen.AIAvatar/src/internal/LipSync/Viseme.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AnimationModule.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AvatarMotions.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/BlendShapePlayer.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/JointTransformer.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Avatar.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarMic.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarTTS.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Info/AvatarInfo.cs [deleted file]
src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarProperties.cs [deleted file]
src/Tizen.AIAvatar/test/Test.cs [new file with mode: 0644]
src/Tizen.NUI/src/internal/Application/Application.cs
src/Tizen.NUI/src/internal/Common/Registry.cs
src/Tizen.NUI/src/internal/Common/VisualObjectsContainer.cs [new file with mode: 0644]
src/Tizen.NUI/src/internal/Interop/Interop.InputMethodContext.cs
src/Tizen.NUI/src/internal/Interop/Interop.VisualObject.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/Interop/Interop.VisualObjectsContainer.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/BaseComponents/ImageView.cs
src/Tizen.NUI/src/public/BaseComponents/View.cs
src/Tizen.NUI/src/public/BaseComponents/ViewBindableProperty.cs
src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs
src/Tizen.NUI/src/public/BaseComponents/ViewVisuals.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/Common/PropertyMap.cs
src/Tizen.NUI/src/public/DragAndDrop/DragAndDrop.cs
src/Tizen.NUI/src/public/Input/InputMethodContext.cs
src/Tizen.NUI/src/public/Visuals/VisualConstants.cs
src/Tizen.NUI/src/public/Visuals/VisualObject/AdvancedTextVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/AnimatedImageVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/BorderVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/ColorVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/ImageVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/NPatchVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/TextVisual.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Visuals/VisualObject/VisualBase.cs [new file with mode: 0644]
src/Tizen.NUI/src/public/Window/Window.cs
test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.csproj [new file with mode: 0644]
test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.sln [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/audio2vowel_7.tflite [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/images/Irradiance.ktx [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/images/Radiance.ktx [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/images/UI_BG.png [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/voice/cs-CZ-Wavenet-A.wav [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/voice/da-DK-Wavenet-A.wav [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/voice/el-GR-Wavenet-A.wav [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/voice/voice_0.bin [new file with mode: 0644]
test/Tizen.AIAvatar.Example/res/voice/voice_1.bin [new file with mode: 0644]
test/Tizen.AIAvatar.Example/shared/res/Tizen.AIAvatar.Example.png [new file with mode: 0644]
test/Tizen.AIAvatar.Example/src/AvatarScene.cs [new file with mode: 0644]
test/Tizen.AIAvatar.Example/src/ControlPannelComponent.cs [new file with mode: 0644]
test/Tizen.AIAvatar.Example/src/SampleMain.cs [new file with mode: 0644]
test/Tizen.AIAvatar.Example/src/Styles.cs [new file with mode: 0644]
test/Tizen.AIAvatar.Example/src/Utils.cs [new file with mode: 0644]
test/Tizen.AIAvatar.Example/tizen-manifest.xml [new file with mode: 0644]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/VisualTest.cs [new file with mode: 0755]
test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/i_focus_stroke_tile_2unit.9.webp [new file with mode: 0644]
test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.code-workspace [new file with mode: 0644]
test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.csproj [new file with mode: 0644]
test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.sln [new file with mode: 0755]
test/Tizen.NUI.VisualTest/VisualTest.cs [new file with mode: 0644]
test/Tizen.NUI.VisualTest/res/image/gallery-small-1.jpg [new file with mode: 0644]
test/Tizen.NUI.VisualTest/shared/res/Tizen.NUI.VisualTest.png [new file with mode: 0644]
test/Tizen.NUI.VisualTest/tizen-manifest.xml [new file with mode: 0755]

index 0f2e2df..d015fbf 100644 (file)
@@ -1,8 +1,8 @@
 # Auto-generated from csapi-tizenfx.spec.in by makespec.sh
 
 %define TIZEN_NET_API_VERSION 12
-%define TIZEN_NET_RPM_VERSION 12.0.0.18314+nui22324
-%define TIZEN_NET_NUGET_VERSION 12.0.0.18314
+%define TIZEN_NET_RPM_VERSION 12.0.0.999+nui22327
+%define TIZEN_NET_NUGET_VERSION 12.0.0.99999
 
 %define DOTNET_ASSEMBLY_PATH /usr/share/dotnet.tizen/framework
 %define DOTNET_ASSEMBLY_DUMMY_PATH %{DOTNET_ASSEMBLY_PATH}/ref
index 33474c1..2a7dd76 100755 (executable)
@@ -6,4 +6,4 @@ RPM_VERSION=12.0.0.999
 NUGET_VERSION=12.0.0.99999
 
 # RPM Version Suffix
-RPM_VERSION_SUFFIX=nui22324
+RPM_VERSION_SUFFIX=nui22327
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  *
  */
+
 using System.ComponentModel;
 using Tizen.NUI.Scene3D;
 
 namespace Tizen.AIAvatar
 {
+    /// <summary>  
+    /// The AnimationInfo class manages animation data for an Avatar, including motion data and names. It is not meant to be directly edited by users or editors.  
+    /// </summary>  
     [EditorBrowsable(EditorBrowsableState.Never)]
     public class AnimationInfo
     {
+        /// <summary>  
+        /// Gets the motion data associated with this animation.  
+        /// </summary>  
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public MotionData MotionData { get; internal set; }
+        public MotionData MotionData { get; private set; }
+
+        /// <summary>  
+        /// Gets the name of this animation.  
+        /// </summary>  
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public string MotionName { get; internal set; }
+        public string MotionName { get; private set; }
 
+        /// <summary>  
+        /// Initializes a new instance of the AnimationInfo class with the specified motion data and name.  
+        /// </summary>  
+        /// <param name="motionData">TheThe motion data associated with this animation.</param>
+        /// <param name="motionName">The name of this animation.</param>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AnimationInfo(MotionData motionData, string motionName)
         {
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,12 +20,19 @@ using System.ComponentModel;
 
 namespace Tizen.AIAvatar
 {
-    /// <summary>
-    /// 
-    /// </summary>
+    /// <summary>   
+    /// This class provides arguments for handling avatar motion change events.
+    /// <member name = "Previous" > The previous state of the avatar's motion.</member>  
+    /// <member name = "Current" > The current state of the avatar's motion.</member>  
+    /// </summary>  
     [EditorBrowsable(EditorBrowsableState.Never)]
     public class AvatarMotionChangedEventArgs : EventArgs
     {
+        /// <summary>  
+        /// Initializes a new instance of the AvatarMotionChangedEventArgs class with the specified previous and current states.  
+        /// </summary>  
+        /// <param name="previous">The previous state of the avatar's motion.</param>  
+        /// <param name="current">The current state of the avatar's motion.</param>  
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AvatarMotionChangedEventArgs(AvatarMotionState previous, AvatarMotionState current)
         {
@@ -36,7 +43,6 @@ namespace Tizen.AIAvatar
         /// <summary>
         /// The previous state.
         /// </summary>
-        /// <since_tizen> 3 </since_tizen>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AvatarMotionState Previous
         {
@@ -47,7 +53,6 @@ namespace Tizen.AIAvatar
         /// <summary>
         /// The current state.
         /// </summary>
-        /// <since_tizen> 3 </since_tizen>
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AvatarMotionState Current
         {
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ namespace Tizen.AIAvatar
     /// <summary>
     /// Enumeration for the states.
     /// </summary>
-    /// <since_tizen> 3 </since_tizen>
     [EditorBrowsable(EditorBrowsableState.Never)]
     public enum AvatarMotionState
     {
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 
 using System;
-using System.ComponentModel;
 using Tizen.NUI;
-using Tizen.NUI.Scene3D;
 
 using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class EyeBlinker : AnimationModule
+    internal class EyeBlinker : IBlendShapeModule, IDisposable
     {
+        private AvatarMotionState currentMotionState = AvatarMotionState.Unavailable;
+
+        private readonly Object motionChangedLock = new Object();
+        private event EventHandler<AvatarMotionChangedEventArgs> motionChanged;
+
         private const int blinkIntervalMinimum = 800;
         private const int blinkIntervalMaximum = 3000;
         private Animation eyeAnimation;
@@ -36,39 +38,84 @@ namespace Tizen.AIAvatar
         private bool isPlaying = false;
         private const int blinkDuration = 200;
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal EyeBlinker()
+        public AvatarMotionState CurrentMotionState
+        {
+            get
+            {
+                return currentMotionState;
+            }
+            set
+            {
+                if (currentMotionState == value)
+                {
+                    return;
+                }
+                var preState = currentMotionState;
+                currentMotionState = value;
+                if (motionChanged != null)
+                {
+                    motionChanged?.Invoke(this, new AvatarMotionChangedEventArgs(preState, currentMotionState));
+                }
+            }
+        }
+
+        public event EventHandler<AvatarMotionChangedEventArgs> MotionStateChanged
+        {
+            add
+            {
+                lock (motionChangedLock)
+                {
+                    motionChanged += value;
+                }
+
+            }
+
+            remove
+            {
+                lock (motionChangedLock)
+                {
+                    if (motionChanged == null)
+                    {
+                        Log.Error(LogTag, "Remove StateChanged Failed : motionChanged is null");
+                        return;
+                    }
+                    motionChanged -= value;
+                }
+            }
+        }
+
+        public EyeBlinker()
         {
 
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Init(Animation eyeAnimation)
+        public void Dispose()
+        {
+            DestroyAnimation();
+        }
+
+        public void Init(Animation eyeAnimation)
         {
             this.eyeAnimation = eyeAnimation;
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Play(IAnimationModuleData data)
+        public void Play()
         {
             //data
             StartEyeBlink();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Stop()
+        public void Stop()
         {
             StopEyeBlink();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Pause()
+        public void Pause()
         {
             eyeAnimation?.Pause();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Destroy()
+        public void Destroy()
         {
             DestroyAnimation();
         }
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-using System.ComponentModel;
+using Tizen.NUI;
 
 namespace Tizen.AIAvatar
 {
-    internal class BlendShapeValue
+    internal interface IBlendShapeModule
     {
-        internal string nodeName;
-        internal BlendShapeType blendIndex;
-        internal float blendValue;
+        public void Init(Animation animation);
+
+        public void Play();
+
+        public void Stop();
+
+        public void Pause();
+
+        public void Destroy();
     }
+
 }
  *
  */
 
-using System.ComponentModel;
 using Tizen.NUI;
 using Tizen.NUI.Scene3D;
-
 using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
@@ -26,17 +24,17 @@ namespace Tizen.AIAvatar
     internal class MotionPlayer
     {
         private Animation motionAnimation;
+        private EyeBlinker eyeBlinker;
+
         internal Animation MotionAnimation { get => motionAnimation; private set => motionAnimation = value; }
 
         internal MotionPlayer()
         {
+            eyeBlinker = new EyeBlinker();
+
 
         }
 
-        /// <summary>
-        /// Play avatar animation by AnimationInfo
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal void PlayAnimation(Animation motionAnimation, int duration = 3000, bool isLooping = false, int loopCount = 1)
         {
             ResetAnimations();
@@ -51,24 +49,42 @@ namespace Tizen.AIAvatar
             }
         }
 
-        /// <summary>
-        /// Pause avatar animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal void PauseMotionAnimation()
         {
             MotionAnimation?.Pause();
         }
 
-        /// <summary>
-        /// Stop avatar animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal void StopMotionAnimation()
         {
             MotionAnimation?.Stop();
         }
 
+        internal void SetBlinkAnimation(Animation blinkerAnimation)
+        {
+            eyeBlinker?.Init(blinkerAnimation);
+        }
+
+        internal void StartEyeBlink()
+        {
+            eyeBlinker?.Play();
+        }
+
+        internal void PauseEyeBlink()
+        {
+            eyeBlinker?.Pause();
+        }
+
+
+        internal void StopEyeBlink()
+        {
+            eyeBlinker?.Stop();
+        }
+
+        internal void DestroyAnimations()
+        {
+            eyeBlinker?.Destroy();
+        }
+
         private void ResetAnimations()
         {
             if (MotionAnimation != null)
@@ -78,5 +94,6 @@ namespace Tizen.AIAvatar
                 MotionAnimation = null;
             }
         }
+
     }
 }
diff --git a/src/Tizen.AIAvatar/src/Common/Avatar.cs b/src/Tizen.AIAvatar/src/Common/Avatar.cs
new file mode 100644 (file)
index 0000000..8ff4306
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.ComponentModel;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+    /// <summary>
+    /// The Avatar class displays 3D avatars and provides easy access to their animations.
+    /// This class is a sub-class of the Model class which allows us to easily control the Avatar's animations.
+    /// Avatar also supports AR Emoji for humanoid-based 3D models.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class Avatar : Model
+    {
+        private AvatarProperties avatarProperties = new DefaultAvatarProperties();
+
+        private MotionPlayer motionPlayer;
+
+        /// <summary>  
+        /// The AvatarProperties property gets or sets the AvatarProperties object containing various information about the Avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarProperties Properties
+        {
+            get => avatarProperties;
+            set
+            {
+                avatarProperties = value;
+            }
+        }
+
+        /// <summary>
+        /// Create an initialized AvatarModel.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Avatar() : base()
+        {
+            InitAvatar();
+        }
+
+        /// <summary>
+        /// Create an initialized AREmojiDefaultAvatar.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Avatar(AvatarInfo avatarInfo) : base(avatarInfo.ResourcePath)
+        {
+            InitAvatar();
+        }
+
+        /// <summary>
+        /// Create an initialized Avatar.
+        /// </summary>
+        /// <param name="avatarUrl">avatar file url.(e.g. glTF).</param>
+        /// <param name="resourceDirectoryUrl"> The url to derectory containing resources: binary, image etc.</param>
+        /// <remarks>
+        /// If resourceDirectoryUrl is empty, the parent directory url of avatarUrl is used for resource url.
+        ///
+        /// http://tizen.org/privilege/mediastorage for local files in media storage.
+        /// http://tizen.org/privilege/externalstorage for local files in external storage.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Avatar(string avatarUrl, string resourceDirectoryUrl = "") : base(avatarUrl, resourceDirectoryUrl)
+        {
+            InitAvatar();
+        }
+
+        /// <summary>
+        /// Copy constructor.
+        /// </summary>
+        /// <param name="avatar">Source object to copy.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Avatar(Avatar avatar) : base(avatar)
+        {
+            InitAvatar();
+        }
+
+        #region Manage Animating
+
+        /// <summary>  
+        /// Plays the specified avatar animation with an optional duration and loop count.  
+        /// </summary>  
+        /// <param name="animationInfo">The AnimationInfo object containing information about the desired avatar animation.</param>  
+        /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>  
+        /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>  
+        /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PlayAnimation(AnimationInfo animationInfo, int duration = 3000, bool isLooping = false, int loopCount = 1)
+        {
+            if (animationInfo == null)
+            {
+                Tizen.Log.Error(LogTag, "animationInfo is null");
+                return;
+            }
+            if (animationInfo.MotionData == null)
+            {
+                Tizen.Log.Error(LogTag, "animationInfo.MotionData is null");
+                return;
+            }
+            motionAnimation = GenerateMotionDataAnimation(animationInfo.MotionData);
+            if (motionAnimation != null)
+            {
+                motionAnimation.Duration = duration;
+                motionAnimation.Looping = isLooping;
+                motionAnimation.LoopCount = loopCount;
+                motionAnimation.BlendPoint = 0.2f;
+                motionPlayer.PlayAnimation(motionAnimation);
+            }
+            else
+            {
+                Tizen.Log.Error(LogTag, "motionAnimation is null");
+            }
+        }
+
+        Animation motionAnimation;
+
+        /// <summary>  
+        /// Plays the specified avatar animation with MotionData and an optional duration and loop count.  
+        /// </summary>  
+        /// <param name="motionData">The MotionData object containing information about the desired avatar animation.</param>  
+        /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>  
+        /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>  
+        /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PlayAnimation(MotionData motionData, int duration = 3000, bool isLooping = false, int loopCount = 1)
+        {
+
+            if (motionData == null)
+            {
+                Tizen.Log.Error(LogTag, "motionData is null");
+                return;
+            }
+            var motionAnimation = GenerateMotionDataAnimation(motionData);
+            if (motionAnimation != null)
+            {
+                motionAnimation.Duration = duration;
+                motionAnimation.Looping = isLooping;
+                motionAnimation.LoopCount = loopCount;
+                motionAnimation.BlendPoint = 0.2f;
+                motionPlayer.PlayAnimation(motionAnimation);
+            }
+            else
+            {
+                Tizen.Log.Error(LogTag, "motionAnimation is null");
+            }
+        }
+
+        /// <summary>  
+        /// Plays the specified avatar animation based on its index within the available animations and an optional duration and loop count.  
+        /// </summary>  
+        /// <param name="index">The zero-based index of the desired avatar animation within the list of available animations.</param>  
+        /// <param name="duration">The duration of the animation in milliseconds (default is 3000).</param>  
+        /// <param name="isLooping">A boolean indicating whether the animation should be looped or not.</param>  
+        /// <param name="loopCount">The number of times to repeat the animation if it's set to loop.</param>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PlayAnimation(int index, int duration = 3000, bool isLooping = false, int loopCount = 1)
+        {
+            //TODO by index
+            //var motionAnimation = GenerateMotionDataAnimation(animationInfoList[index].MotionData);
+            /*motionAnimation.Duration = duration;
+            motionAnimation.Looping = isLooping;
+            motionAnimation.LoopCount = loopCount;
+            motionAnimation.BlendPoint = 0.2f;
+
+            motionPlayer.PlayAnimation(motionAnimation);*/
+        }
+
+        /// <summary>  
+        /// Pauses the currently playing avatar animation.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PauseMotionAnimation()
+        {
+            motionPlayer.PauseMotionAnimation();
+        }
+
+        /// <summary>  
+        /// Stops the currently playing avatar animation.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void StopMotionAnimation()
+        {
+            motionPlayer?.StopMotionAnimation();
+        }
+
+        /// <summary>  
+        /// Starts the eye blink animation for the current avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void StartEyeBlink()
+        {
+            motionPlayer?.StartEyeBlink();
+        }
+
+        /// <summary>  
+        /// Pauses the eye blink animation for the current avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PauseEyeBlink()
+        {
+            motionPlayer?.PauseEyeBlink();
+        }
+
+        /// <summary>  
+        /// Stops the eye blink animation for the current avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void StopEyeBlink()
+        {
+            motionPlayer?.StopEyeBlink();
+        }
+        #endregion
+        
+        private void InitAvatar()
+        {
+            motionPlayer = new MotionPlayer();
+            var eyeMotionData = CreateEyeBlinkMotionData(200);
+            if (eyeMotionData == null)
+            {
+                Tizen.Log.Info(LogTag, "Failed Loading eyeAnimation");
+            }
+
+            ResourcesLoaded += (s, e) =>
+            {
+                var eyeAnimation = GenerateMotionDataAnimation(eyeMotionData);
+                if (eyeAnimation != null)
+                {
+                    motionPlayer.SetBlinkAnimation(eyeAnimation);
+                }
+            };
+        }
+
+        private MotionData CreateEyeBlinkMotionData(int ms)
+        {
+            var keyFrames = new KeyFrames();
+            keyFrames.Add(0.1f, 0.0f);
+            keyFrames.Add(0.5f, 1.0f);
+            keyFrames.Add(0.9f, 0.0f);
+
+            var headBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
+            var headBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
+            var eyelashBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
+            var eyelashBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
+
+            var motionData = new MotionData(ms);
+            motionData.Add(headBlendShapeEyeLeft, new MotionValue(keyFrames));
+            motionData.Add(headBlendShapeEyeRight, new MotionValue(keyFrames));
+            motionData.Add(eyelashBlendShapeEyeLeft, new MotionValue(keyFrames));
+            motionData.Add(eyelashBlendShapeEyeRight, new MotionValue(keyFrames));
+
+            return motionData;
+        }
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Common/AvatarInfo.cs b/src/Tizen.AIAvatar/src/Common/AvatarInfo.cs
new file mode 100644 (file)
index 0000000..3a4cf3d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.ComponentModel;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+    /// <summary>
+    /// The AvatarInfo class describes the properties of an Avatar object.
+    /// It includes information such as the name of the avatar, its thumbnail image, and associated resources.
+    /// This class helps users manage and organize their Avatar assets more effectively.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class AvatarInfo
+    {
+        /// <summary>  
+        /// The Name property gets the name of the Avatar.   
+        /// This value is read-only and cannot be modified directly.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Name { get; private set; }
+
+        /// <summary>  
+        /// The ThumbnailPath property gets the path to the thumbnail image representing the Avatar.  
+        /// This value is read-only and cannot be modified directly.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string ThumbnailPath { get; private set; }
+
+        /// <summary>  
+        /// The ResourcePath property gets the path to the resource files associated with the Avatar.  
+        /// This value is intended for internal use only and should not be accessed by users directly.  
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal string ResourcePath { get; private set; }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal AvatarInfoOption avatarInfoOption { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the AvatarInfo class with the specified file path, name, and option.
+        /// If no option is provided, the default is AvatarInfoOption.Thumbnail.
+        /// </summary>
+        /// <param name="name">The name of the Avatar.</param>
+        /// <param name="path">The full path to the Avatar file.</param>
+        /// <param name="info">The option specifying what type of information should be loaded from the file (thumbnail or resource).</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarInfo(string name, string path, AvatarInfoOption info = AvatarInfoOption.Thumbnail)
+        {
+            this.Name = name;
+            this.avatarInfoOption = info;
+
+            if (info == AvatarInfoOption.Thumbnail)
+            {
+                ThumbnailPath = path;
+            }
+            else
+            {
+                ResourcePath = path;
+            }
+        }
+
+        internal AvatarInfo(string directoryPath)
+        {
+            string path = ApplicationResourcePath + EmojiAvatarResourcePath;
+            Name = directoryPath.Substring(path.Length, directoryPath.Length - path.Length);
+            ResourcePath = $"{directoryPath}/{AIAvatar.ExternalModel}";
+        }
+    }
+
+    /// <summary>
+    /// The AvatarInfoOption enumeration defines the options that determine how AvatarInfo instances are displayed or managed.
+    /// Currently it has two values: Thumbnail and Resource.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public enum AvatarInfoOption
+    {
+
+        /// <summary>  
+        /// Thumbnail indicates that the AvatarInfo instance should display or manipulate the thumbnail image of the Avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        Thumbnail = 0,
+        /// <summary>  
+        /// Resource indicates that the AvatarInfo instance should display or manipulate the resource files associated with the Avatar.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        Resource = 1,
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Common/AvatarProperties.cs b/src/Tizen.AIAvatar/src/Common/AvatarProperties.cs
new file mode 100644 (file)
index 0000000..6a3f73f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+using Tizen.NUI.Scene3D;
+
+namespace Tizen.AIAvatar
+{
+
+    /// <summary>
+    /// The Avatar class contains an inner AvatarProperties class. 
+    /// This class manages AvatarProperty information using the AvatarPropertyMapper class. 
+    /// By default, it includes jointMapper, blendShapeMapper, and nodeMapper, which automatically generate Properties based on model information.
+    /// This structure enables users to work with Avatar properties in a more convenient way.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class AvatarProperties
+    {
+        private AvatarPropertyMapper jointMapper;
+        private AvatarPropertyMapper blendShapeMapper;
+        private AvatarPropertyMapper nodeMapper;
+
+        internal event EventHandler<AvatarProperties> PropertiesChanged;
+
+        /// <summary>
+        /// The JointMapper property gets or sets the AvatarPropertyMapper responsible for mapping joint information in the Avatar model.
+        /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarPropertyMapper JointMapper
+        {
+            get
+            {
+                return jointMapper;
+            }
+            set
+            {
+                jointMapper = value;
+                PropertiesChanged?.Invoke(jointMapper, this);
+            }
+        }
+
+        /// <summary>  
+        /// The BlendShapeMapper property gets or sets the AvatarPropertyMapper responsible for managing blend shape information in the Avatar model.  
+        /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarPropertyMapper BlendShapeMapper
+        {
+            get
+            {
+                return blendShapeMapper;
+            }
+            set
+            {
+                blendShapeMapper = value;
+                PropertiesChanged?.Invoke(blendShapeMapper, this);
+            }
+        }
+
+        /// <summary>  
+        /// The NodeMapper property gets or sets the AvatarPropertyMapper responsible for managing node information in the Avatar model.  
+        /// When setting this property, any changes made will trigger the AvatarPropertiesChanged event if it has been subscribed to.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarPropertyMapper NodeMapper
+        {
+            get
+            {
+                return nodeMapper;
+            }
+            set
+            {
+                nodeMapper = value;
+                PropertiesChanged?.Invoke(nodeMapper, this);
+            }
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the AvatarProperties class with the specified joint mapper, blend shape mapper, and node mapper.
+        /// These mappers are used to map between the Avatar's underlying model data and its corresponding properties.
+        /// </summary>
+        /// <param name="jointMapper">The AvatarPropertyMapper for joints.</param>
+        /// <param name="blendShapeMapper">The AvatarPropertyMapper for blend shapes.</param>
+        /// <param name="nodeMapper">The AvatarPropertyMapper for nodes.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AvatarProperties(AvatarPropertyMapper jointMapper, AvatarPropertyMapper blendShapeMapper, AvatarPropertyMapper nodeMapper)
+        {
+            JointMapper = new AvatarPropertyMapper(jointMapper);
+            BlendShapeMapper = new AvatarPropertyMapper(blendShapeMapper);
+            NodeMapper = new AvatarPropertyMapper(nodeMapper);
+        }
+
+        /// <summary>  
+        /// This method generates a MotionIndex to be used in animations based on the NodeType and BlendShapeType using the model information of an Avatar.  
+        /// </summary>  
+        /// <param name="nodeType">Node type</param>  
+        /// <param name="blendShapeType">Blend shape type</param>  
+        /// <returns>The generated MotionIndex</returns>  
+        public MotionIndex CreateBlendShapeMotionIndex(NodeType nodeType, BlendShapeType blendShapeType)
+        {
+            var motionIndex = new AvatarBlendShapeIndex(NodeMapper, nodeType, BlendShapeMapper, blendShapeType);
+            return motionIndex;
+        }
+    }
+}
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  *
  */
+
 using System.Collections.Generic;
 using System.ComponentModel;
 
 namespace Tizen.AIAvatar
 {
     /// <summary>
-    /// TODO : Explain more detail
+    /// The AvatarPropertyMapper class manages property mapping information for specific attributes of an Avatar model.
+    /// It primarily maps the names of AvatarProperty to their actual storage locations.
+    /// By doing so, developers can directly read or write the values of these properties using their respective names.
+    /// This approach provides consistency and convenience when working with Avatar models.
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
     public class AvatarPropertyMapper
@@ -36,86 +40,70 @@ namespace Tizen.AIAvatar
         private uint customIndexCounter = 0;
 
         /// <summary>
-        /// Create an initialized AvatarPropertyNameMapper.
-        /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarPropertyMapper()
-        {
-            mapper = new Dictionary<uint, string>();
-            customIndexCounter = 0u;
-        }
-
-        /// <summary>
-        /// Copy constructor.
+        /// Get current mapper information.
         /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarPropertyMapper(AvatarPropertyMapper rhs)
+        public Dictionary<uint, string> Mapper
         {
-            if (rhs != null)
-            {
-                mapper = new Dictionary<uint, string>(rhs.Mapper);
-                customIndexCounter = rhs.customIndexCounter;
-            }
-            else
+            get
             {
-                mapper = new Dictionary<uint, string>();
-                customIndexCounter = 0u;
+                return mapper;
             }
         }
 
+        /// <summary>  
+        /// Indexer method. Allows accessing and setting the property names using array notation.  
+        /// </summary>  
+        /// <param name="index">The index of property what we want to set</param>  
+        /// <returns>The index of property, or uint.MaxValue if not exist</returns>  
         [EditorBrowsable(EditorBrowsableState.Never)]
-        protected uint CustomIndexCounter
+        public string this[uint index]
         {
-            get
+            set
             {
-                return customIndexCounter;
+                SetPropertyName(index, value);
             }
-            set
+            get
             {
-                customIndexCounter = value;
+                return GetPropertyName(index);
             }
         }
 
         /// <summary>
-        /// Get current mapper information.
+        /// Create an initialized AvatarPropertyNameMapper.
         /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public Dictionary<uint, string> Mapper
+        public AvatarPropertyMapper()
         {
-            get
-            {
-                return mapper;
-            }
+            mapper = new Dictionary<uint, string>();
+            customIndexCounter = 0u;
         }
 
-        /// <summary>
-        /// TODO : Explain me.
-        /// </summary>
-        /// <param name="index">The index of property what we want to set</param>
-        /// <returns>The index of property, or uint.MaxValue if not exist</returns>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        /// <summary>  
+        /// Copy constructor.  
+        /// Creates a new instance of the AvatarPropertyMapper class by copying the contents of another existing AvatarPropertyMapper instance.  
+        /// </summary>  
+        /// <param name="source">The source AvatarPropertyMapper instance to be copied.</param>  
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public string this[uint index]
+        public AvatarPropertyMapper(AvatarPropertyMapper source)
         {
-            set
+            if (source != null)
             {
-                SetPropertyName(index, value);
+                mapper = new Dictionary<uint, string>(source.Mapper);
+                customIndexCounter = source.customIndexCounter;
             }
-            get
+            else
             {
-                return GetPropertyName(index);
+                mapper = new Dictionary<uint, string>();
+                customIndexCounter = 0u;
             }
         }
 
-        /// <summary>
-        /// TODO : Explain me.
-        /// </summary>
-        /// <param name="name">The name of custom property</param>
-        /// <returns>The index of property matched with name.</returns>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        /// <summary>  
+        /// Registers a custom property by name and returns its index.  
+        /// </summary>  
+        /// <param name="name">The name of custom property</param>  
+        /// <returns>The index of property matched with name.</returns>  
         [EditorBrowsable(EditorBrowsableState.Never)]
         public uint RegisterCustomProperty(string name)
         {
@@ -128,12 +116,11 @@ namespace Tizen.AIAvatar
             return ret;
         }
 
-        /// <summary>
-        /// TODO : Explain me.
-        /// </summary>
-        /// <param name="name">The name of property what we want to get index</param>
-        /// <returns>The index of property, or uint.MaxValue if not exist</returns>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        /// <summary>  
+        /// Returns the index of a property by its name.  
+        /// </summary>  
+        /// <param name="name">The name of property what we want to get index</param>  
+        /// <returns>The index of property, or uint.MaxValue if not exist</returns>  
         [EditorBrowsable(EditorBrowsableState.Never)]
         public uint GetPropertyIndexByName(string name)
         {
@@ -148,25 +135,27 @@ namespace Tizen.AIAvatar
             return uint.MaxValue;
         }
 
-        /// <summary>
-        /// TODO : Explain me.
-        /// </summary>
-        /// <param name="index">The index of property what we want to set</param>
-        /// <param name="name">The name of property what we want to set</param>
-        /// <remark>
-        /// New property will be added if we use index that not exist in mapper.
-        /// </remark>
-        internal void SetPropertyName(uint index, string name)
+        /// <summary>  
+        /// Sets the property name at the given index.  
+        /// </summary>  
+        /// <param name="index">The index of property what we want to set</param>  
+        /// <param name="name">The name of property what we want to set</param>  
+        /// <remark>  
+        /// New property will be added if we use index that not exist in mapper.  
+        /// </remark>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetPropertyName(uint index, string name)
         {
             mapper.TryAdd(index, name);
         }
 
-        /// <summary>
-        /// TODO : Explain me.
-        /// </summary>
-        /// <param name="index">The index of property what we want to set</param>
-        /// <returns>The name of property, or null if not exist</returns>
-        internal string GetPropertyName(uint index)
+        /// <summary>  
+        /// Gets the property name at the given index.  
+        /// </summary>  
+        /// <param name="index">The index of property what we want to set</param>  
+        /// <returns>The name of property, or null if not exist</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string GetPropertyName(uint index)
         {
             string ret = null;
             mapper.TryGetValue(index, out ret);
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@ namespace Tizen.AIAvatar
 {
     /// <summary>
     /// The type of predefined blendshape. We can customize each type name by "TODO_mapper"
-    /// TODO : Explain me
-    /// TODO : Need to check each joint exist in AR Emoji
-    /// Note : This is temperal name of joints.
+    /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+    /// Contains the BlendShape information of AIAvatar.  
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    internal enum BlendShapeType
+    public enum BlendShapeType
     {
         #region Left Eyes
         /// <summary>
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,13 +20,12 @@ using System.ComponentModel;
 namespace Tizen.AIAvatar
 {
     /// <summary>
-    /// The type of predefined skeletal joint. We can customize each type name by "TODO_mapper"
-    /// TODO : Explain me
-    /// TODO : Need to check each joint exist in AR Emoji
-    /// Note : This is temperal name of joints.
+    /// The type of predefined skeleton joint. We can customize each type name by "TODO_mapper"
+    /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+    /// Contains the joint information of AIAvatar.  
     /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    internal enum JointType
+    public enum JointType
     {
         #region Head
         /// <summary>
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  *
  */
+
 using System.ComponentModel;
-using Tizen.Uix.Tts;
 
 namespace Tizen.AIAvatar
 {
+    /// <summary>
+    /// The type of predefined node. We can customize each type name by "TODO_mapper"
+    /// The basic names provided by AIAvatar to control the default avatar of AREmoji.
+    /// Contains the node information of AIAvatar.  
+    /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public struct VoiceInfo
+    public enum NodeType
     {
-        private string lang;
-        private Voice type;
-
         /// <summary>
-        /// 
+        /// head geometry
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public string Lang { get => lang; set => lang = value; }
-
+        HeadGeo,
+        /// <summary>
+        /// mouth geometry
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        MouthGeo,
         /// <summary>
-        /// 
+        /// eyelash geometry
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public Voice Type { get => type; set => type = value; }
+        EyelashGeo
     }
 }
@@ -19,25 +19,20 @@ using Newtonsoft.Json;
 using System.Net.Http;
 using System;
 using System.Threading.Tasks;
-using Tizen.Uix.Tts;
-using System.ComponentModel;
 
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class AvatarLLM
+    internal class EmotionAnalyzer
     {
-        private AvatarTTS avatarTTS;
+        private LipSyncController avatarTTS;
         private IRestClient restClient;
         private const string playgroundURL = "https://playground-api.sec.samsung.net";
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal AvatarLLM()
+        internal EmotionAnalyzer()
         {
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void InitAvatarLLM(AvatarTTS avatarTTS)
+        internal void InitAvatarLLM(LipSyncController avatarTTS)
         {
             this.avatarTTS = avatarTTS;
             // Setup RestClinet
@@ -45,7 +40,6 @@ namespace Tizen.AIAvatar
             restClient = restClientFactory.CreateClient(playgroundURL);
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal async Task StartTTSWithLLMAsync(string text, string token)
         {
             var bearerToken = token;
@@ -61,12 +55,13 @@ namespace Tizen.AIAvatar
                 //TTS 호출
                 var voiceInfo = new VoiceInfo()
                 {
-                    Lang = "en_US",
-                    Type = Voice.Female,
+                    Language = "en_US",
+                    Type = VoiceType.Female,
                 };
 
-                avatarTTS.PlayTTSAsync(content, voiceInfo, (o, e) => {
-                    
+                avatarTTS.PlayTTSAsync(content, voiceInfo, (o, e) =>
+                {
+
                 });
 
             }
diff --git a/src/Tizen.AIAvatar/src/Emotion/EmotionController.cs b/src/Tizen.AIAvatar/src/Emotion/EmotionController.cs
new file mode 100644 (file)
index 0000000..fd0bd42
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+    /// <summary>  
+     /// Manages facial expression control for avatars based on input text sentiment analysis results.  
+     /// </summary> 
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class EmotionController
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="EmotionController"/> class without an avatar reference.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public EmotionController()
+        {
+
+        }
+
+        /// <summary>  
+        /// Initializes the EmotionController by setting up the necessary components for managing facial expressions in the avatar.  
+        /// </summary> 
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Initialize()
+        {
+
+        }
+        /// <summary>  
+        /// This method analyzes emotion the given text.   
+        /// </summary>  
+        /// <param name="text">The text to analyze</param> 
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void AnalizeEmotion(string text)
+        {
+
+        }
+
+        /// <summary>  
+        /// This method starts playing the emotion facial.   
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PlayEmotionFacial()
+        {
+
+        }
+
+        /// <summary>  
+        /// This method pauses the emotion facial.   
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PauseEmotionFacial()
+        {
+
+        }
+
+        /// <summary>  
+        /// This method stops the emotion facial.   
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void StopEmotionFacial()
+        {
+
+        }
+    }
+}
index e2a219a..afe62cd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 using System.Collections.Generic;
 using System.IO;
+using System.ComponentModel;
 
 using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
 {
-    internal static class AvatarExtension
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public static class AvatarExtension
     {
-        internal static List<AvatarInfo> GetDefaultAvatarList()
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static List<AvatarInfo> GetDefaultAvatarList()
         {
             var list = new List<AvatarInfo>();
             var avatarModelFolders = Directory.GetDirectories(ApplicationResourcePath + EmojiAvatarResourcePath);
index 61a606d..4f498b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
index b608205..ae8552f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-using System;
-using System.Collections.Generic;
-using System.Text;
 using Tizen.NUI.Scene3D;
+using System.ComponentModel;
 
 namespace Tizen.AIAvatar
 {
-    internal static class SceneViewExtension
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public static class SceneViewExtension
     {
-        internal static void SetAlphaMaskUrl(this SceneView sceneView, string url)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void SetAlphaMaskUrl(this SceneView sceneView, string url)
         {
             var setValue = new Tizen.NUI.PropertyValue(url);
             sceneView.SetProperty(Interop.AlphaMaskURLGet(), setValue);
             setValue.Dispose();
         }
 
-        internal static string GetAlphaMaskUrl(this SceneView sceneView)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static string GetAlphaMaskUrl(this SceneView sceneView)
         {
             var returnValue = "";
             var invertYAxis = sceneView.GetProperty(Interop.AlphaMaskURLGet());
@@ -40,14 +41,16 @@ namespace Tizen.AIAvatar
             return returnValue;
         }
 
-        internal static void SetMaskContentScaleFactor(this SceneView sceneView, float factor)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void SetMaskContentScaleFactor(this SceneView sceneView, float factor)
         {
             var setValue = new Tizen.NUI.PropertyValue(factor);
             sceneView.SetProperty(Interop.MaskContentScaleGet(), setValue);
             setValue.Dispose();
         }
 
-        internal static float GetMaskContentScaleFactor(this SceneView sceneView)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static float GetMaskContentScaleFactor(this SceneView sceneView)
         {
             var returnValue = 0.0f;
             var invertYAxis = sceneView.GetProperty(Interop.MaskContentScaleGet());
@@ -56,14 +59,16 @@ namespace Tizen.AIAvatar
             return returnValue;
         }
 
-        internal static void EnableCropToMask(this SceneView sceneView, bool enableCropToMask)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void EnableCropToMask(this SceneView sceneView, bool enableCropToMask)
         {
             var setValue = new Tizen.NUI.PropertyValue(enableCropToMask);
             sceneView.SetProperty(Interop.CropToMaskGet(), setValue);
             setValue.Dispose();
         }
 
-        internal static bool IsEnabledCropToMask(this SceneView sceneView)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool IsEnabledCropToMask(this SceneView sceneView)
         {
             bool returnValue = false;
             var invertYAxis = sceneView.GetProperty(Interop.CropToMaskGet());
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -34,61 +34,38 @@ namespace Tizen.AIAvatar
     /// </code>
     /// </example>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class AvatarBlendShapeIndex : BlendShapeIndex
+    internal class AvatarBlendShapeIndex : BlendShapeIndex
     {
         internal AvatarPropertyMapper nameMapper = null;
         internal uint blendShapeType;
 
-        /// <summary>
-        /// Create an initialized avatar blend shape index.
-        /// </summary>
-        /// <param name="mapper">Name mapper for this index</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarBlendShapeIndex(AvatarPropertyMapper mapper) : base()
+        internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper) : base()
         {
             nameMapper = mapper;
         }
 
-        /// <summary>
-        /// Create an initialized avatar blend shape index with input blend shape ID.
-        /// </summary>
-        /// <param name="mapper">Name mapper for this index</param>
-        /// <param name="blendShapeId">Blend shape ID for this motion index</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarBlendShapeIndex(AvatarPropertyMapper mapper, PropertyKey blendShapeId) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeId)))
+        internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper, PropertyKey blendShapeId) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeId)))
         {
             nameMapper = mapper;
         }
 
-        /// <summary>
-        /// Create an initialized avatar blend shape index with blend shape type.
-        /// </summary>
-        /// <param name="mapper">Name mapper for this index</param>
-        /// <param name="blendShapeType">Type of blend shape for this motion index</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, PropertyKey blendShapeId) 
+            : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), blendShapeId)
+        {
+        }
+
         internal AvatarBlendShapeIndex(AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) : this(blendShapeMapper, (uint)blendShapeType)
         {
         }
 
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="mapper">Name mapper for this index</param>
-        /// <param name="blendShapeType">Type of blend shape for this motion index</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarBlendShapeIndex(AvatarPropertyMapper mapper, uint blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeType)))
+        internal AvatarBlendShapeIndex(AvatarPropertyMapper mapper, uint blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(mapper, blendShapeType)))
         {
             nameMapper = mapper;
             this.blendShapeType = blendShapeType;
         }
 
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="mapper">Name mapper for this index</param>
-        /// <param name="blendShapeType">Type of blend shape for this motion index</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), new PropertyKey(GetPropertyNameFromMapper(blendShapeMapper, (uint)blendShapeType)))
+        internal AvatarBlendShapeIndex(AvatarPropertyMapper nodeMapper, NodeType nodeType, AvatarPropertyMapper blendShapeMapper, BlendShapeType blendShapeType) 
+            : base(new PropertyKey(GetPropertyNameFromMapper(nodeMapper, (uint)nodeType)), new PropertyKey(GetPropertyNameFromMapper(blendShapeMapper, (uint)blendShapeType)))
         {
             nameMapper = blendShapeMapper;
             this.blendShapeType = (uint)blendShapeType;
@@ -110,18 +87,21 @@ namespace Tizen.AIAvatar
             {
                 return id.StringKey;
             }
-            return mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
+
+            var str = mapper?.GetPropertyName((uint)id.IndexKey) ?? "";
+            return str;
         }
 
         /// <summary>
-        /// Get the name of given JointType.
+        /// Get the name of given BlendShape.
         /// </summary>
         /// <param name="mapper">Name mapper for given index</param>
-        /// <param name="blendShapeType">Type of joint what we want to get name</param>
+        /// <param name="type">Type of joint what we want to get name</param>
         /// <returns>Name, or null if invalid</returns>
-        private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint blendShapeType)
+        private static string GetPropertyNameFromMapper(AvatarPropertyMapper mapper, uint type)
         {
-            return mapper?.GetPropertyName(blendShapeType) ?? "";
+            var str = mapper?.GetPropertyName(type) ?? "";
+            return str;
         }
 
         /// <summary>
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ namespace Tizen.AIAvatar
     /// </code>
     /// </example>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class AvatarJointTransformIndex : MotionTransformIndex
+    internal class AvatarJointTransformIndex : MotionTransformIndex
     {
         internal AvatarPropertyMapper nameMapper = null;
         internal uint jointType;
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-using System;
 using System.Collections.Generic;
-using System.Text;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
 
 namespace Tizen.AIAvatar
 {
@@ -29,23 +25,28 @@ namespace Tizen.AIAvatar
         private static AvatarPropertyMapper defaultBlendShapeNameMapper = new AvatarPropertyMapper();
         private static AvatarPropertyMapper defaultNodeMapper = new AvatarPropertyMapper();
 
-
         internal DefaultAvatarProperties() : base(defaultJointMapper, defaultBlendShapeNameMapper, defaultNodeMapper)
         {
+            Initialize();
+        }
+
+        private void Initialize()
+        {
             foreach (var indexNamePair in blendShapeList)
             {
-                defaultBlendShapeNameMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+                BlendShapeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
             }
 
             foreach (var indexNamePair in jointList)
             {
-                defaultJointMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+                JointMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
             }
 
             foreach (var indexNamePair in nodeList)
             {
-                defaultNodeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
+                NodeMapper.SetPropertyName((uint)indexNamePair.Item1, indexNamePair.Item2);
             }
+
         }
 
         #region AR Emoji BlendShape name list
@@ -195,6 +196,5 @@ namespace Tizen.AIAvatar
             (NodeType.EyelashGeo, "eyelash_GEO"),
         };
         #endregion
-
     }
 }
similarity index 95%
rename from src/Tizen.AIAvatar/src/internal/Avatar/Utils.cs
rename to src/Tizen.AIAvatar/src/Internal/Utils.cs
index 7f1acf9..e845a02 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -111,11 +111,6 @@ namespace Tizen.AIAvatar
 
 internal class SystemUtils
 {
-    internal static void ST()
-    {
-        Tizen.Log.Error("MYLOG", System.Environment.StackTrace);
-    }
-
     internal static string GetFileName(string path)
     {
         return System.IO.Path.GetFileName(path);
@@ -25,7 +25,7 @@ using static Tizen.AIAvatar.AIAvatar;
 namespace Tizen.AIAvatar
 {
     [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class AsyncLipSyncer : LipSyncer
+    internal class AsyncLipSyncer
     {
         private readonly uint AsyncPlayTime = 160;
         private Queue<Animation> lipAnimations;
@@ -57,7 +57,7 @@ namespace Tizen.AIAvatar
         {
             if (!isAsyncInit)
             {
-                CurrentMotionState = AvatarMotionState.Stopped;
+                //TODO : State is Stop
             }
             else
             {
@@ -67,8 +67,9 @@ namespace Tizen.AIAvatar
                 {
                     Tizen.Log.Error(LogTag, "Finish vowel lip sync");
 
-                    AudioPlayer.Stop();
-                    CurrentMotionState = AvatarMotionState.Stopped;
+                    
+                    //Audio Player is Stop, AudioPlayer.Stop();
+                    //TODO : State is Stop
                     DestroyVowelTimer();
                     isAsyncInit = false;
                 }
@@ -96,6 +97,29 @@ namespace Tizen.AIAvatar
             }
         }
 
+        private void EnqueueVowels(byte[] buffer, bool deleteLast = false)
+        {
+            /*
+            var vowels = PredictVowels(buffer);
+            if (vowels != null)
+            {
+                vowelPools.Enqueue(vowels);
+                if (deleteLast)
+                {
+                    var vowelList = new List<string>(vowels);
+                    vowelList.RemoveAt(vowelList.Count - 1);
+                    vowels = vowelList.ToArray();
+                }
+            }*/
+        }
+
+        internal Animation CreateAsyncLipAnimation(byte[] buffer, int sampleRate)
+        {
+            EnqueueVowels(buffer, false);
+            return null;
+            //return CreateLipAnimationByVowelsQueue(sampleRate);
+        }
+
         internal void EnqueueAnimation(byte[] recordBuffer, int sampleRate, int audioLength)
         {
             var createdAni = CreateAsyncLipAnimation(recordBuffer, sampleRate);
@@ -134,10 +158,12 @@ namespace Tizen.AIAvatar
                 var lipAnimation = lipAnimations.Dequeue();
                 lipAnimation.Finished += OnLipAnimationFinished;
 
-                ResetLipAnimation(lipAnimation);
-                PlayLipAnimation();
+                //ResetLipAnimation(lipAnimation);
+                //PlayLipAnimation();
                 var audioBuffer = lipAudios.Dequeue();
-                AudioPlayer.PlayAsync(audioBuffer, sampleRate);
+                
+                //Audio Playe rAsync Play
+                //AudioPlayer.PlayAsync(audioBuffer, sampleRate);
                 return true;
 
             }
diff --git a/src/Tizen.AIAvatar/src/Lipsync/LipSyncController.cs b/src/Tizen.AIAvatar/src/Lipsync/LipSyncController.cs
new file mode 100644 (file)
index 0000000..45349f8
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.Generic;
+using System.ComponentModel;
+using System;
+using Tizen.Uix.Tts;
+
+using static Tizen.AIAvatar.AIAvatar;
+using Tizen.Multimedia;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI;
+
+namespace Tizen.AIAvatar
+{
+     /// <summary>
+     /// A controller class used to manage lip sync functionality for avatars.
+     /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class LipSyncController : IDisposable
+    {
+        private TTSController ttsController;
+        private AudioRecorder audioRecorder;
+        private LipSyncer lipSyncer;
+
+        private Avatar avatar;
+
+        private event EventHandler ttsReadyFinished;
+        private readonly Object ttsReadyFinishedLock = new Object();
+
+        /// <summary>  
+        /// Initializes a new instance of the <see cref="LipSyncController"/> class.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public LipSyncController(Avatar avatar)
+        {
+            this.avatar = avatar;
+
+            audioRecorder = new AudioRecorder();
+            lipSyncer = new LipSyncer();
+
+            lipSyncer.CreatedKeyFrameAnimation += OnCreatedKeyFrameAnimation;
+        }
+
+        private Animation CreateLipAnimation(AnimationKeyFrame animationKeyFrames, bool isMic = false)
+        {
+            var animationTime = (int)(animationKeyFrames.AnimationTime * 1000f);
+            var lipAnimation = new Animation(animationTime);
+
+            var motionData = new MotionData(animationTime);
+            for (var i = 0; i < animationKeyFrames.NodeNames.Length; i++)
+            {
+                var nodeName = animationKeyFrames.NodeNames[i];
+                for (var j = 0; j < animationKeyFrames.BlendShapeCounts[i]; j++)
+                {
+                    var blendShapeIndex = new BlendShapeIndex(new PropertyKey(nodeName), new PropertyKey(j));
+                    var keyFrameList = animationKeyFrames.GetKeyFrames(nodeName, j);
+                    if (keyFrameList.Count == 0)
+                    {
+                        continue;
+                    }
+
+                    var keyFrames = new KeyFrames();
+                    CreateKeyTimeFrames(ref keyFrames, keyFrameList, isMic);
+
+                    motionData.Add(blendShapeIndex, new MotionValue(keyFrames));
+                    lipAnimation = avatar.GenerateMotionDataAnimation(motionData);
+                }
+            }
+            return lipAnimation;
+        }
+
+        private KeyFrames CreateKeyTimeFrames(ref KeyFrames keyFrames, List<KeyFrame> keyFrameList, bool isMic = false)
+        {
+            foreach (var key in keyFrameList)
+            {
+                keyFrames.Add(key.time, key.value);
+            }
+            if (!isMic) keyFrames.Add(1.0f, 0.0f);
+
+            return keyFrames;
+        }
+
+        private Animation OnCreatedKeyFrameAnimation(AnimationKeyFrame animationKeyFrame, bool isMic)
+        {
+            if (animationKeyFrame == null)
+            {
+                Tizen.Log.Error(LogTag, "animtionKeyFrame is null");
+            }
+            var lipAnimation = CreateLipAnimation(animationKeyFrame, isMic);
+            return lipAnimation;
+        }
+
+        /// <summary>  
+        /// Occurs when text-to-speech synthesis has finished and the audio data is ready to play.  
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler TTSReadyFinished
+        {
+            add
+            {
+                lock (ttsReadyFinishedLock)
+                {
+                    ttsReadyFinished += value;
+                }
+
+            }
+
+            remove
+            {
+                lock (ttsReadyFinishedLock)
+                {
+                    if (ttsReadyFinished == null)
+                    {
+                        Log.Error(LogTag, "ttsReadyFinished is null");
+                        return;
+                    }
+                    ttsReadyFinished -= value;
+                }
+            }
+        }
+
+        /// <summary>  
+        /// Gets the current Text-To-Speech client associated with the Lip Sync Controller.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TtsClient CurrentTTSClient => ttsController?.TtsHandle;
+
+        /// <summary>
+        /// Initializes the Text-To-Speech system for use with the Lip Sync Controller.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void InitializeTTS()
+        {
+            if (ttsController == null)
+            {
+                try
+                {
+                    ttsController = new TTSController();
+                    //TODO : LipSync Event Connect
+                    ttsController.PreparedSyncText += OnPreparedSyncText;
+                    ttsController.StoppedTTS += OnStoppedTTS;
+                    ttsController.UpdatedBuffer += OnUpdatedBuffer;
+                }
+                catch (Exception e)
+                {
+                    Log.Error(LogTag, $"error :{e.Message}");
+                    Log.Error(LogTag, $"{e.StackTrace}");
+                }
+            }
+        }
+
+        private void OnUpdatedBuffer(object sender, TTSControllerEventArgs e)
+        {
+            throw new NotImplementedException();
+            if (lipSyncer != null)
+            {
+                Log.Error(LogTag, "OnTTSBufferChanged");
+                /*
+                lipSyncer.EnqueueAnimation(recordBuffer, sampleRate, audioLength);
+                if (!isAsyncLipStarting)
+                {
+                    lipSyncer.StartAsyncLipPlayTimer();
+                    isAsyncLipStarting = true;
+                }*/
+            }
+            else
+            {
+                Log.Error(LogTag, "avatarLipSyncer is null");
+            }
+        }
+
+        private void OnStoppedTTS(object sender, TTSControllerEventArgs e)
+        {
+            lipSyncer.Stop();
+        }
+
+        private void OnPreparedSyncText(object sender, TTSControllerEventArgs e)
+        {
+            var data = e.AudioData;
+            var sampleRate = e.SampleRate;
+            
+            // Play Lipsync Animation by Audio
+            lipSyncer.PlayAudio(data, sampleRate);
+        }
+
+        /// <summary>  
+        /// Deinitializes the Text-To-Speech system for use with the Lip Sync Controller. 
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void DeInitializeTTS()
+        {
+            if (ttsController != null)
+            {
+                try
+                {
+                    ttsController.DeinitTts();
+                    ttsController = null;
+                }
+                catch (Exception e)
+                {
+                    Log.Error(LogTag, $"error :{e.Message}");
+                    Log.Error(LogTag, $"{e.StackTrace}");
+                }
+            }
+        }
+
+        /// <summary>  
+        /// Returns a list of supported voices for the Text-To-Speech system.  
+        /// </summary>  
+        /// <returns></returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public List<VoiceInfo> GetSupportedVoices()
+        {
+            return ttsController.GetSupportedVoices();
+        }
+
+        /// <summary>  
+        /// Plays the given audio with LipSync and returns whether it was successful or not. 
+        /// If the lip syncer is null, logs an error and returns false.
+        /// The lipSyncer plays the audio using the provided byte array and sample rate.  
+        /// </summary>  
+        /// <param name="audio">The audio data to be played in a byte array format.</param>  
+        /// <param name="sampleRate">The sampling rate of the audio data, default value is 24000 Hz.</param>
+        public bool PlayLipSync(byte[] audio, int sampleRate = 24000)
+        {
+            if (lipSyncer == null)
+            {
+                Log.Error(LogTag, "lipSyncer is null");
+                return false;
+            }
+            lipSyncer.PlayAudio(audio, sampleRate);
+
+            return true;
+        }
+
+        /// <summary>  
+        /// Plays the given audio with LipSync and returns whether it was successful or not. 
+        /// If the lip syncer is null, logs an error and returns false.
+        /// The lipSyncer plays the audio using the provided byte array and sample rate.  
+        /// </summary>  
+        /// <param name="audio">The audio data to be played in a byte array format.</param>  
+        /// <param name="sampleRate">The sampling rate of the audio data, default value is 16000 Hz.</param>
+        public bool PlayLipSync(string path, int sampleRate = 24000)
+        {
+            var audio = Utils.ReadAllBytes(path);
+            if (audio == null)
+            {
+                Log.Error(LogTag, "audio data is null");
+                return false;
+            }
+            lipSyncer.PlayAudio(audio, sampleRate);
+
+            return true;
+        }
+
+        /// <summary>  
+        /// Prepares the Text-To-Speech engine to synthesize speech from the provided text and voice info.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>  
+        /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>  
+        /// <returns><c>True</c> if preparation was successful, otherwise <c>False</c>.</returns> 
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PrepareTTS(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+
+            if (!ttsController.IsSupportedVoice(voiceInfo))
+            {
+                Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Info(LogTag, "TTS is not ready");
+                return false;
+            }
+
+            try
+            {
+                ttsController.AddText(text, voiceInfo);
+                ttsController.Prepare(ttsReadyFinishedCallback);
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Prepares the Text-To-Speech engine to synthesize speech from the provided text and language code.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>  
+        /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>  
+        /// <returns><c>True</c> if preparation was successful, otherwise <c>False</c>.</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PrepareTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            if (!ttsController.IsSupportedVoice(lang))
+            {
+                Log.Error(LogTag, $"{lang} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Error(LogTag, "TTS is not ready");
+                return false;
+            }
+            try
+            {
+                ttsController.AddText(text, lang);
+                ttsController.Prepare(ttsReadyFinishedCallback);
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Plays the previously prepared Text-To-Speech audio data.  
+        /// </summary>  
+        /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PlayPreparedTTS()
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            return ttsController.PlayPreparedText();
+        }
+
+        /// <summary>  
+        /// Converts the given text into speech using the specified voice info and plays the resulting audio immediately.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>  
+        /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PlayTTS(string text, VoiceInfo voiceInfo)
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            if (!ttsController.IsSupportedVoice(voiceInfo))
+            {
+                Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Info(LogTag, "TTS is not ready");
+                return false;
+            }
+
+            try
+            {
+                ttsController.AddText(text, voiceInfo);
+                ttsController.Play();
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Converts the given text into speech using the specified language code and plays the resulting audio immediately.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>  
+        /// <returns><c>True</c> if playback started successfully, otherwise <c>False</c>.</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PlayTTS(string text, string lang = "ko_KR")
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            if (!ttsController.IsSupportedVoice(lang))
+            {
+                Log.Error(LogTag, $"{lang} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Error(LogTag, "TTS is not ready");
+                return false;
+            }
+            try
+            {
+                ttsController.AddText(text, lang);
+                ttsController.Play();
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Asynchronously converts the given text into speech using the specified voice info and plays the resulting audio once it's ready.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="voiceInfo">The voice info to use for converting the text into speech.</param>  
+        /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>  
+        /// <returns><c>True</c> if asynchronous conversion was initiated successfully, otherwise <c>False</c>.</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PlayTTSAsync(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            if (!ttsController.IsSupportedVoice(voiceInfo))
+            {
+                Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Info(LogTag, "TTS is not ready");
+                return false;
+            }
+
+            try
+            {
+                ttsController.AddText(text, voiceInfo);
+                ttsController.PlayAsync(ttsReadyFinishedCallback);
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Asynchronously converts the given text into speech using the specified language code and plays the resulting audio once it's ready.  
+        /// </summary>  
+        /// <param name="text">The text to convert into speech.</param>  
+        /// <param name="lang">The two-letter ISO 639-1 language code to use for converting the text into speech (default is Korean - ko_KR).</param>  
+        /// <param name="ttsReadyFinishedCallback">An optional callback that will be invoked when the TTS process is complete.</param>  
+        /// <returns><c>True</c> if asynchronous conversion was initiated successfully, otherwise <c>False</c>.</returns>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PlayTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return false;
+            }
+
+            if (!ttsController.IsSupportedVoice(lang))
+            {
+                Log.Error(LogTag, $"{lang} is not supported");
+                return false;
+            }
+
+            Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+            if (ttsController.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
+            {
+                Log.Error(LogTag, "TTS is not ready");
+                return false;
+            }
+            try
+            {
+                ttsController.AddText(text, lang);
+                ttsController.PlayAsync(ttsReadyFinishedCallback);
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+                return false;
+            }
+            return true;
+        }
+
+        /// <summary>  
+        /// Pauses the currently playing Text-To-Speech audio.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PauseTTS()
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return;
+            }
+
+            try
+            {
+                Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+                ttsController?.Pause();
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+            }
+        }
+
+        /// <summary>  
+        /// Stops the currently playing Text-To-Speech audio.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void StopTTS()
+        {
+            if (ttsController == null || ttsController.TtsHandle == null)
+            {
+                Log.Error(LogTag, "tts is null");
+                return;
+            }
+
+            try
+            {
+                Log.Info(LogTag, "Current TTS State :" + ttsController.TtsHandle.CurrentState);
+                ttsController?.Stop();
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, $"error :{e.Message}");
+                Log.Error(LogTag, $"{e.StackTrace}");
+            }
+        }
+
+        public void InitializeMic()
+        {
+            if (audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.InitializeMic(lipSyncer, 160);
+        }
+
+        public void DeinitializeMic()
+        {
+            if (audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.StartRecording();
+        }
+
+        public void StartMic()
+        {
+            if (audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.StartRecording();
+        }
+
+        public void StopMic()
+        {
+            if (audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.StopRecording();
+        }
+
+        public void PauseMic()
+        {
+            if (audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.PauseRecording();
+        }
+
+        public void ResumeMic()
+        {
+            if(audioRecorder == null)
+            {
+                Tizen.Log.Error(LogTag, "audio record is null");
+                return;
+            }
+            audioRecorder.ResumeRecording();
+        }
+
+        /// <summary>  
+        /// Releases unmanaged resources used by this instance.  
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Dispose()
+        {
+            if (ttsController != null)
+            {
+                ttsController.Dispose();
+                ttsController = null;
+            }
+
+            if (audioRecorder != null)
+            {
+                audioRecorder.Dispose();
+                audioRecorder = null;
+            }
+        }
+    }
+}
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 using System;
 using System.Collections.Generic;
-using System.ComponentModel;
 using Tizen.NUI;
 using Tizen.NUI.Scene3D;
-
 using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class LipSyncer : AnimationModule
+    internal class LipSyncer
     {
-        private Avatar avatar;
         private Animation lipAnimation = null;
 
-        //private VowelConverter vowelConverter;
+        private VowelConverter vowelConverter;
         private AudioPlayer audioPlayer;
+        private event Func<AnimationKeyFrame, bool, Animation> TheEvent;
+
+        internal Func<AnimationKeyFrame, bool, Animation> CreatedKeyFrameAnimation;
 
         //Mic
         private Queue<string[]> vowelPools = new Queue<string[]>();
         private string prevVowel = "sil";
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal LipSyncer()
         {
+            vowelConverter = new VowelConverter();
+            audioPlayer = new AudioPlayer();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
         internal AudioPlayer AudioPlayer { get { return audioPlayer; } }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Init(Animation lipAnimation)
+        public void SetLipAnimation(Animation lipAnimation)
         {
             this.lipAnimation = lipAnimation;
-            //vowelConverter = new VowelConverter();
-            audioPlayer = new AudioPlayer();
-
-            CurrentMotionState = AvatarMotionState.Ready;
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Play(IAnimationModuleData data)
+        public void PlayAudio(byte[] audio, int sampleRate)
         {
-            if (data is LipSyncData lipSyncData)
+            if (audio != null)
             {
-                if (lipSyncData.AudioFile != null)
-                {
-                    PlayLipSync(lipSyncData.AudioFile, lipSyncData.SampleRate);
-                }
+                PlayLipSync(audio, sampleRate);
             }
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Stop()
+        public void Stop()
         {
             StopLipSync();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Pause()
+        public void Pause()
         {
             PauseLipSync();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public override void Destroy()
+        public void Destroy()
         {
             DestroyLipAnimation();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        protected Animation CreateAsyncLipAnimation(byte[] buffer, int sampleRate)
-        {
-            EnqueueVowels(buffer, false);
-            return CreateLipAnimationByVowelsQueue(sampleRate);
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
         //TODO : lipAnimation 자체를 Animator안에서 생성하고 관리될 수 있게 수정.
         protected void ResetLipAnimation(Animation lipAnimation)
         {
@@ -105,7 +84,6 @@ namespace Tizen.AIAvatar
             }
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
         protected void PlayLipAnimation()
         {
             if (lipAnimation == null)
@@ -115,10 +93,8 @@ namespace Tizen.AIAvatar
             lipAnimation?.Play();
         }
 
-        [EditorBrowsable(EditorBrowsableState.Never)]
         protected void OnLipAnimationFinished(object sender, EventArgs e)
         {
-            CurrentMotionState = AvatarMotionState.Stopped;
         }
 
         private void PlayLipSync(byte[] audio)
@@ -128,34 +104,41 @@ namespace Tizen.AIAvatar
                 Tizen.Log.Error(LogTag, "audi data is null");
                 return;
             }
-
             DestroyLipAnimation();
-            var lipAnimation = CreateLipAnimation(audio, CurrentAudioOptions.SampleRate);
-            if (lipAnimation != null)
+
+            var lipKeyframes = CreateKeyFrame(audio, CurrentAudioOptions.SampleRate);
+            if (lipKeyframes != null)
             {
-                ResetLipAnimation(lipAnimation);
-                PlayLipAnimation();
+                var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyframes, false);
+
+                if (lipAnimation != null)
+                {
+                    ResetLipAnimation(lipAnimation);
+                    PlayLipAnimation();
+                    audioPlayer.Play(audio);
+                }
+                else
+                {
+                    Tizen.Log.Error(LogTag, "lipAnimation is null");
+                }
             }
             else
             {
-
-                Tizen.Log.Error(LogTag, "lipAnimation is null");
+                Tizen.Log.Error(LogTag, "lipKeyframes is null");
             }
-            audioPlayer.Play(audio);
-            CurrentMotionState = AvatarMotionState.Playing;
         }
 
         private void PlayLipSync(byte[] audio, int sampleRate)
         {
             DestroyLipAnimation();
-            var lipAnimation = CreateLipAnimation(audio, sampleRate);
+            var lipKeyFrames = CreateKeyFrame(audio, sampleRate);
+            var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyFrames, false);
             if (lipAnimation != null)
             {
                 ResetLipAnimation(lipAnimation);
                 PlayLipAnimation();
             }
             audioPlayer.Play(audio, sampleRate);
-            CurrentMotionState = AvatarMotionState.Playing;
         }
 
         private void PlayLipSync(string path)
@@ -175,30 +158,10 @@ namespace Tizen.AIAvatar
         {
             PauseLipAnimation();
             audioPlayer.Pause();
-            CurrentMotionState = AvatarMotionState.Paused;
         }
 
         private void StopLipSync()
         {
-            StopLipAnimation();
-            audioPlayer.Stop();
-            CurrentMotionState = AvatarMotionState.Stopped;
-        }
-
-        private void PauseLipAnimation()
-        {
-            if (lipAnimation != null)
-            {
-                lipAnimation?.Pause();
-            }
-            else
-            {
-                Log.Error(LogTag, "Current Lip Animation is null");
-            }
-        }
-
-        private void StopLipAnimation()
-        {
             if (lipAnimation != null)
             {
                 DestroyLipAnimation();
@@ -218,27 +181,29 @@ namespace Tizen.AIAvatar
             {
                 Log.Error(LogTag, "Current Lip Animation is null");
             }
+            audioPlayer.Stop();
         }
 
-        private void DestroyLipAnimation()
+        private void PauseLipAnimation()
         {
             if (lipAnimation != null)
             {
-                lipAnimation.Stop();
-                lipAnimation.Dispose();
-                lipAnimation = null;
+                lipAnimation?.Pause();
+            }
+            else
+            {
+                Log.Error(LogTag, "Current Lip Animation is null");
             }
         }
 
-        private Animation CreateLipAnimation(byte[] array, int sampleRate)
+        private void DestroyLipAnimation()
         {
-            Animation lipKeyframes = null;// CreateKeyFrame(array, sampleRate);
-            if (lipKeyframes != null)
+            if (lipAnimation != null)
             {
-
-                return null;// CreateLipAnimation(lipKeyframes);
+                lipAnimation.Stop();
+                lipAnimation.Dispose();
+                lipAnimation = null;
             }
-            return null;
         }
 
         private void EnqueueVowels(byte[] buffer, bool deleteLast = false)
@@ -267,8 +232,10 @@ namespace Tizen.AIAvatar
                 AttachPreviousVowel(vowelPools.Dequeue(), out var newVowels);
                 Log.Info(LogTag, $"vowelRecognition: {String.Join(", ", newVowels)}");
 
-                //var lipKeyFrames = vowelConverter?.CreateKeyFrames(newVowels, sampleRate, true);
-                return null;// CreateLipAnimation(lipKeyFrames, true);
+                var lipKeyFrames = vowelConverter?.CreateKeyFrames(newVowels, sampleRate, true);
+                var lipAnimation = CreatedKeyFrameAnimation?.Invoke(lipKeyFrames, true);
+
+                return lipAnimation;
             }
             return null;
         }
@@ -284,7 +251,7 @@ namespace Tizen.AIAvatar
 
         private string[] PredictVowels(byte[] audioData)
         {
-            string[] vowels = null;// vowelConverter?.PredictVowels(audioData);
+            string[] vowels = vowelConverter?.PredictVowels(audioData);
             return vowels;
         }
 
@@ -296,7 +263,6 @@ namespace Tizen.AIAvatar
             prevVowel = vowels[vowels.Length - 1];
         }
 
-        /*
         private AnimationKeyFrame CreateKeyFrame(byte[] audio, int sampleRate)
         {
             var keyFrames = vowelConverter?.CreateKeyFrames(audio, sampleRate);
@@ -308,44 +274,6 @@ namespace Tizen.AIAvatar
             return keyFrames;
         }
 
-        private Animation CreateLipAnimation(AnimationKeyFrame animationKeyFrames, bool isMic = false)
-        {
-            var animationTime = (int)(animationKeyFrames.AnimationTime * 1000f);
-            var lipAnimation = new Animation(animationTime);
-
-            var motionData = new MotionData(animationTime);
-            for (var i = 0; i < animationKeyFrames.NodeNames.Length; i++)
-            {
-                var nodeName = animationKeyFrames.NodeNames[i];
-                for (var j = 0; j < animationKeyFrames.BlendShapeCounts[i]; j++)
-                {
-                    var blendShapeIndex = new BlendShapeIndex(new PropertyKey(nodeName), new PropertyKey(j));
-                    var keyFrameList = animationKeyFrames.GetKeyFrames(nodeName, j);
-                    if (keyFrameList.Count == 0)
-                    {
-                        continue;
-                    }
-
-                    var keyFrames = new KeyFrames();
-                    CreateKeyTimeFrames(ref keyFrames, keyFrameList, isMic);
-
-                    motionData.Add(blendShapeIndex, new MotionValue(keyFrames));
-                    lipAnimation = avatar.GenerateMotionDataAnimation(motionData);
-                }
-            }
-            return lipAnimation;
-        }
-        private KeyFrames CreateKeyTimeFrames(ref KeyFrames keyFrames, List<KeyFrame> keyFrameList, bool isMic = false)
-        {
-            foreach (var key in keyFrameList)
-            {
-                keyFrames.Add(key.time, key.value);
-            }
-            if (!isMic) keyFrames.Add(1.0f, 0.0f);
-
-            return keyFrames;
-        }
-        */
 
         internal void OnRecordBufferChanged(byte[] recordBuffer, int sampleRate)
         {
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/CustomMfccExtractor.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/CustomMfccExtractor.cs
new file mode 100644 (file)
index 0000000..ec9b754
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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;
+
+namespace Tizen.AIAvatar
+{
+    internal class CustomMfccExtractor : IMfccExtractor
+    {
+        private readonly int FilterBankSize = 24;
+
+        private int featureCount = 12;
+
+        private int blockSize;
+        private int frameSize;
+        private int hopSize;
+
+        private Window window;
+        private FFT fft;
+        private FilterBank filterBank;
+        private DCT dct;
+
+        private float[] block;
+        private float[] spectrum;
+        private float[] melSpectrum;
+
+        public CustomMfccExtractor(int samplingRate, float frameDuration, float hopDuration, int featureCount)
+        {
+            this.featureCount = featureCount;
+
+            frameSize = (int)Math.Round(samplingRate * frameDuration, 1);
+            hopSize = (int)Math.Round(samplingRate * hopDuration, 1);
+            blockSize = (int)Math.Pow(2, Math.Ceiling(Math.Log(frameSize, 2)));
+
+            window = new Window(frameSize);
+            fft = new FFT(blockSize);
+            filterBank = new FilterBank(FilterBankSize, samplingRate, blockSize);
+            dct = new DCT(FilterBankSize, this.featureCount);
+
+            block = new float[blockSize];
+            spectrum = new float[blockSize / 2 + 1];
+            melSpectrum = new float[FilterBankSize];
+        }
+
+        public List<float[]> ComputeFrom(float[] audio, int sampleRate)
+        {
+            int totalCount = (audio.Length - frameSize) / hopSize + 1;
+            List<float[]> mfccs = new List<float[]>(totalCount);
+
+            for (var i = 0; i < totalCount; i++)
+            {
+                mfccs.Add(new float[featureCount]);
+            }
+
+            var lastSample = audio.Length - frameSize;
+            for (int sample = 0, i = 0; sample <= lastSample; sample += hopSize, i++)
+            {
+                // 1. Framing
+                Buffer.BlockCopy(audio, sample * sizeof(float), block, 0, frameSize * sizeof(float));
+
+                for (var k = frameSize; k < blockSize; block[k++] = 0) { }
+
+                // 2. PreEmphasis
+                // PreEmphasis.PreEmphasize(ref block, 0.97f);
+
+                // 3. HammingWindow
+                window.Apply(block);
+
+                // 4. FFT
+                fft.Direct(block, spectrum);
+
+                // 5. MelFilterBank
+                filterBank.Apply(spectrum, melSpectrum);
+
+                // 6. DCT
+                dct.DirectNorm(melSpectrum, mfccs[i]);
+            }
+
+            return mfccs;
+        }
+    }
+
+    //DLL Interface
+    internal class Extractor
+    {
+        public static float[][] ComputeFrom(float[] audio, int samplingRate, float frameDuration, float hopDuration, int featureCount)
+        {
+            CustomMfccExtractor cme = new CustomMfccExtractor(samplingRate, frameDuration, hopDuration, featureCount);
+            List<float[]> mfccFeatures = cme.ComputeFrom(audio, samplingRate);
+
+            return mfccFeatures.ToArray();
+        }
+    }
+
+}
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/DCT.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/DCT.cs
new file mode 100644 (file)
index 0000000..9060f87
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+    internal class DCT
+    {
+        private int dctSize;
+        private int featureCount;
+
+        private float[][] dctMatrix;
+
+        public DCT(int filterBankSize, int featureCount = 12)
+        {
+            dctSize = filterBankSize;
+            this.featureCount = featureCount;
+
+            dctMatrix = new float[dctSize][];
+
+            float m = MathF.PI / (dctSize << 1);
+
+            for (var k = 0; k < dctSize; k++)
+            {
+                dctMatrix[k] = new float[dctSize];
+
+                for (var n = 0; n < dctSize; n++)
+                {
+                    dctMatrix[k][n] = (float)(2 * Math.Cos(((n << 1) + 1) * k * m));
+                }
+            }
+        }
+
+        public void DirectNorm(float[] input, float[] output)
+        {
+
+            var norm0 = (float)Math.Sqrt(0.5f);
+            var norm = (float)Math.Sqrt(0.5f / dctSize);
+
+            for (var k = 0; k < featureCount; k++)
+            {
+                output[k] = 0.0f;
+                for (var n = 0; n < input.Length; n++)
+                {
+                    output[k] += input[n] * dctMatrix[k][n];
+                }
+                output[k] *= norm;
+            }
+            output[0] *= norm0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FFT.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FFT.cs
new file mode 100644 (file)
index 0000000..054f28f
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+    internal class FFT
+    {
+        private int fftSize;
+
+        private float[] re, im;
+        private float[] realSpectrum, imagSpectrum;
+        private float[] cosTbl, sinTbl;
+        private float[] ar, br, ai, bi;
+
+        public FFT(int size)
+        {
+            fftSize = size / 2;
+
+            // Precalculate FFT Parameter
+            re = new float[fftSize];
+            im = new float[fftSize];
+            realSpectrum = new float[fftSize + 1];
+            imagSpectrum = new float[fftSize + 1];
+
+            var tblSize = (int)Math.Log(fftSize, 2);
+
+            cosTbl = new float[tblSize];
+            sinTbl = new float[tblSize];
+
+            for (int i = 1, pos = 0; i < fftSize; i *= 2, pos++)
+            {
+                cosTbl[pos] = (float)Math.Cos(2 * Math.PI * i / fftSize);
+                sinTbl[pos] = (float)Math.Sin(2 * Math.PI * i / fftSize);
+            }
+
+            ar = new float[fftSize];
+            br = new float[fftSize];
+            ai = new float[fftSize];
+            bi = new float[fftSize];
+
+            var f = MathF.PI / fftSize;
+
+            for (var i = 0; i < fftSize; i++)
+            {
+                ar[i] = (float)(0.5 * (1 - Math.Sin(f * i)));
+                ai[i] = (float)(-0.5 * Math.Cos(f * i));
+                br[i] = (float)(0.5 * (1 + Math.Sin(f * i)));
+                bi[i] = (float)(0.5 * Math.Cos(f * i));
+            }
+        }
+
+        public void Direct(float[] input, float[] output)
+        {
+            for (int i = 0, k = 0; i < fftSize; i++)
+            {
+                re[i] = input[k++];
+                im[i] = input[k++];
+            }
+
+            var L = fftSize;
+            var M = fftSize >> 1;
+            var S = fftSize - 1;
+            var ti = 0;
+            while (L >= 2)
+            {
+                var l = L >> 1;
+                var u1 = 1.0f;
+                var u2 = 0.0f;
+                var c = cosTbl[ti];
+                var s = -sinTbl[ti];
+                ti++;
+                for (var j = 0; j < l; j++)
+                {
+                    for (var i = j; i < fftSize; i += L)
+                    {
+                        var p = i + l;
+                        var t1 = re[i] + re[p];
+                        var t2 = im[i] + im[p];
+                        var t3 = re[i] - re[p];
+                        var t4 = im[i] - im[p];
+                        re[p] = t3 * u1 - t4 * u2;
+                        im[p] = t4 * u1 + t3 * u2;
+                        re[i] = t1;
+                        im[i] = t2;
+                    }
+                    var u3 = u1 * c - u2 * s;
+                    u2 = u2 * c + u1 * s;
+                    u1 = u3;
+                }
+                L >>= 1;
+            }
+            for (int i = 0, j = 0; i < S; i++)
+            {
+                if (i > j)
+                {
+                    var t1 = re[j];
+                    var t2 = im[j];
+                    re[j] = re[i];
+                    im[j] = im[i];
+                    re[i] = t1;
+                    im[i] = t2;
+                }
+                var k = M;
+                while (j >= k)
+                {
+                    j -= k;
+                    k >>= 1;
+                }
+                j += k;
+            }
+
+            realSpectrum[0] = re[0] * ar[0] - im[0] * ai[0] + re[0] * br[0] + im[0] * bi[0];
+            imagSpectrum[0] = im[0] * ar[0] + re[0] * ai[0] + re[0] * bi[0] - im[0] * br[0];
+
+            for (var k = 1; k < fftSize; k++)
+            {
+                realSpectrum[k] = re[k] * ar[k] - im[k] * ai[k] + re[fftSize - k] * br[k] + im[fftSize - k] * bi[k];
+                imagSpectrum[k] = im[k] * ar[k] + re[k] * ai[k] + re[fftSize - k] * bi[k] - im[fftSize - k] * br[k];
+            }
+
+            realSpectrum[fftSize] = re[0] - im[0];
+            imagSpectrum[fftSize] = 0;
+
+            for (int i = 0; i < fftSize; i++)
+            {
+                output[i] = realSpectrum[i] * realSpectrum[i] + imagSpectrum[i] * imagSpectrum[i];
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FilterBank.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/FilterBank.cs
new file mode 100644 (file)
index 0000000..a5d178a
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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;
+
+namespace Tizen.AIAvatar
+{
+    internal struct bangDouble
+    {
+        public double left;
+        public double center;
+        public double right;
+
+        public bangDouble(double l, double c, double r)
+        {
+            left = l;
+            center = c;
+            right = r;
+        }
+
+    }
+
+    internal class FilterBank
+    {
+        private float[][] _filterBank;
+        private int _filterBankSize;
+
+        public FilterBank(int filterBankSize, int samplingRate, int blockSize)
+        {
+            _filterBankSize = filterBankSize;
+            var melBands = MelBands(filterBankSize, samplingRate);
+            _filterBank = Triangular(blockSize, samplingRate, melBands, HerzToMel);
+        }
+
+        public void Apply(float[] input, float[] output)
+        {
+            for (var i = 0; i < _filterBankSize; i++)
+            {
+                var sum = 0.0f;
+
+                for (var j = 0; j < input.Length; j++)
+                {
+                    sum += _filterBank[i][j] * input[j];
+                }
+
+                output[i] = (float)Math.Log10(Math.Max(sum, float.Epsilon));
+            }
+        }
+
+        public static void Apply(float[][] filterBank, float[] spectrum, float[] filtered)
+        {
+            for (var i = 0; i < filterBank.Length; i++)
+            {
+                var en = 0.0f;
+
+                for (var j = 0; j < spectrum.Length; j++)
+                {
+                    en += filterBank[i][j] * spectrum[j];
+                }
+
+                filtered[i] = en;
+            }
+        }
+
+        public static bangDouble[] MelBands(
+            int melFilterCount, int samplingRate, float lowFreq = 0, float highFreq = 0, bool overlap = true)
+        {
+            return UniformBands(HerzToMel, MelToHerz, melFilterCount, samplingRate, lowFreq, highFreq, overlap);
+        }
+
+        public static float[][] Triangular(int fftSize,
+                                           int samplingRate,
+                                           bangDouble[] frequencies,
+                                           Func<double, double> mapper = null)
+        {
+            if (mapper is null) mapper = x => x;
+
+            var herzResolution = (float)samplingRate / fftSize;
+            var herzFrequencies = Enumerable.Range(0, fftSize / 2 + 1)
+                                            .Select(f => f * herzResolution)
+                                            .ToArray();
+
+            var filterCount = frequencies.Length;
+            var filterBank = new float[filterCount][];
+
+            for (var i = 0; i < filterCount; i++)
+            {
+                filterBank[i] = new float[fftSize / 2 + 1];
+
+                var left = frequencies[i].left;
+                var center = frequencies[i].center;
+                var right = frequencies[i].right;
+
+                left = mapper(left);
+                center = mapper(center);
+                right = mapper(right);
+
+                var j = 0;
+                for (; mapper(herzFrequencies[j]) <= left; j++) ;
+                for (; mapper(herzFrequencies[j]) <= center; j++)
+                {
+                    filterBank[i][j] = (float)((mapper(herzFrequencies[j]) - left) / (center - left));
+                }
+                for (; j < herzFrequencies.Length && mapper(herzFrequencies[j]) < right; j++)
+                {
+                    filterBank[i][j] = (float)((right - mapper(herzFrequencies[j])) / (right - center));
+                }
+            }
+
+            return filterBank;
+        }
+
+        private static bangDouble[] UniformBands(
+                                                    Func<double, double> scaleMapper,
+                                                    Func<double, double> inverseMapper,
+                                                    int filterCount,
+                                                    int samplingRate,
+                                                    float lowFreq = 0,
+                                                    float highFreq = 0,
+                                                    bool overlap = true)
+        {
+            if (lowFreq < 0) lowFreq = 0;
+            if (highFreq <= lowFreq) highFreq = samplingRate * 0.5f;
+
+            var startingFrequency = scaleMapper(lowFreq);
+            var frequencyTuples = new bangDouble[filterCount];
+
+            if (overlap)
+            {
+                var newResolution = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / (filterCount + 1);
+                var frequencies = Enumerable.Range(0, filterCount + 2)
+                                            .Select(i => inverseMapper(startingFrequency + i * newResolution))
+                                            .ToArray();
+                for (var i = 0; i < filterCount; i++)
+                {
+                    frequencyTuples[i].left = frequencies[i];
+                    frequencyTuples[i].center = frequencies[i + 1];
+                    frequencyTuples[i].right = frequencies[i + 2];
+                }
+            }
+            else
+            {
+                var newResolution = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / filterCount;
+                var frequencies = Enumerable.Range(0, filterCount + 1)
+                                            .Select(i => inverseMapper(startingFrequency + i * newResolution))
+                                            .ToArray();
+
+                for (var i = 0; i < filterCount; i++)
+                {
+                    frequencyTuples[i].left = frequencies[i];
+                    frequencyTuples[i].center = (frequencies[i] + frequencies[i + 1]) / 2;
+                    frequencyTuples[i].right = frequencies[i + 2];
+                }
+            }
+
+            return frequencyTuples;
+        }
+
+        public static double HerzToMel(double herz)
+        {
+            return 1127 * Math.Log(herz / 700 + 1);
+        }
+
+        public static double MelToHerz(double mel)
+        {
+            return (Math.Exp(mel / 1127) - 1) * 700;
+        }
+    }
+}
\ No newline at end of file
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-using System.ComponentModel;
+using System.Collections.Generic;
 
 namespace Tizen.AIAvatar
 {
-    internal class BlendShapeVisemeInfo
+    internal interface IMfccExtractor
     {
-        internal Viseme name;
-        internal BlendShapeValue[] values;
+        List<float[]> ComputeFrom(float[] samples, int samplingRate);
     }
 }
+
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/PreEmphasis.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/PreEmphasis.cs
new file mode 100644 (file)
index 0000000..2fedc33
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.AIAvatar
+{
+    internal class PreEmphasis
+    {
+        internal static void PreEmphasize(ref float[] block, float value)
+        {
+            float prevSample = block[0];
+
+            for (int i = 0; i < block.Length; i++)
+            {
+                var result = block[i] - prevSample * value;
+                prevSample = block[i];
+                block[i] = result;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/Window.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Audio2Vowel/inMFCC/Algorithm/Window.cs
new file mode 100644 (file)
index 0000000..c319fd0
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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;
+
+namespace Tizen.AIAvatar
+{
+    internal class Window
+    {
+        private float[] _window;
+
+        public Window(int size)
+        {
+            _window = Hamming(size);
+        }
+
+        public void Apply(float[] block)
+        {
+            if (block.Length < _window.Length) return;
+
+            for (int i = 0; i < _window.Length; i++)
+            {
+                block[i] *= _window[i];
+            }
+        }
+
+        public static float[] Hamming(int length)
+        {
+            var n = 2 * Math.PI / (length - 1);
+            return Enumerable.Range(0, length)
+                             .Select(i => 0.54f - 0.46f * Math.Cos(i * n))
+                             .Select(v => (float)v)
+                             .ToArray();
+        }
+    }
+}
\ No newline at end of file
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,14 +19,14 @@ using System.ComponentModel;
 
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal enum NodeType
+    internal interface ISingleShotModel
     {
+
         [EditorBrowsable(EditorBrowsableState.Never)]
-        HeadGeo,
+        public void SetTensorData(int index, byte[] buffer);
         [EditorBrowsable(EditorBrowsableState.Never)]
-        MouthGeo,
+        public byte[] GetTensorData(int index);
         [EditorBrowsable(EditorBrowsableState.Never)]
-        EyelashGeo
+        public void Invoke();
     }
 }
 
 using System;
 using System.Collections.Generic;
-using System.ComponentModel;
-using System.Text;
-using Tizen.AIAvatar;
+using System.Linq;
 
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public class LipSyncData : IAnimationModuleData
+    internal static class SoftmaxLinqExtension
     {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public byte[] AudioFile { get; set; }
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public int SampleRate { get; set; }
+        internal static IEnumerable<float> SoftMax(this IEnumerable<float> source)
+        {
+            var exp = source.Select(x => MathF.Exp(x)).ToArray();
+            var sum = exp.Sum();
+            return exp.Select(x => x / sum);
+        }
     }
 }
@@ -1,7 +1,22 @@
-using System;
-using System.Collections.Generic;
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
 using System.ComponentModel;
-using System.Text;
 using Tizen.MachineLearning.Inference;
 using static Tizen.AIAvatar.AIAvatar;
 
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/AnimationConverter.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/AnimationConverter.cs
new file mode 100644 (file)
index 0000000..742368c
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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 Newtonsoft.Json;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Tizen.AIAvatar
+{
+    internal enum Viseme
+    {
+        sil, AE, Ah, B_M_P, Ch_J, EE, Er, IH, Oh, W_OO, S_Z, F_V, TH,
+        T_L_D_N, K_G_H_NG, R
+    };
+
+    internal class AnimationConverter
+    {
+        private Dictionary<string, string> VowelToViseme
+            = new Dictionary<string, string> {
+            { "A", "Ah" },
+            { "E", "AE" },
+            { "I", "EE" },
+            { "O", "R" },
+            { "ER", "R" },
+            { "U", "W_OO" },
+            { "HM", "B_M_P" },
+            { "sil", "sil" }
+        };
+
+        private AnimationKeyFrame _animationKeyFrame;
+        private BlendShapeInfo _visemBlendShapeInfo;
+        private Dictionary<string, BlendShapeValue[]> _visemeMap;
+
+        private bool _isInitialized;
+
+        public AnimationConverter() { }
+
+        public void InitializeVisemeInfo(string info_path)
+        {
+            try
+            {
+                StreamReader v = new StreamReader(info_path);
+                var jsonString = v.ReadToEnd();
+
+                _visemBlendShapeInfo = JsonConvert.DeserializeObject<BlendShapeInfo>(jsonString);
+                _visemeMap = _visemBlendShapeInfo.GetVisemeMap();
+
+                var nodeNames = _visemBlendShapeInfo.GetNodeNames();
+                var blendShapeCounts = _visemBlendShapeInfo.GetBlendShapeCounts();
+                var blendShapeKeyFormat = _visemBlendShapeInfo.blendShape.keyFormat;
+
+                _animationKeyFrame = new AnimationKeyFrame(nodeNames,
+                                                           blendShapeCounts,
+                                                           blendShapeKeyFormat);
+                _isInitialized = true;
+            }
+            catch (Exception)
+            {
+                throw new FailedPersonalizeException(info_path);
+            }
+        }
+
+        public AnimationKeyFrame ConvertVowelsToAnimation(string[] vowels,
+                                                          float stepDuration)
+        {
+            if (!_isInitialized) throw new NotInitializedException();
+
+            _animationKeyFrame.ClearAnimation();
+            ConvertVowelsToVisemes(vowels, out var visemes);
+            ConvertVisemesToAnimationKeyFrame(visemes, stepDuration);
+
+            return _animationKeyFrame;
+        }
+
+        public AnimationKeyFrame ConvertVowelsToAnimationMic(string[] vowels,
+                                                             float stepDuration)
+        {
+            if (!_isInitialized) throw new NotInitializedException();
+
+            _animationKeyFrame.ClearAnimation();
+            ConvertVowelsToVisemes(vowels, out var visemes);
+            ConvertVisemesToAnimationKeyFrameMic(visemes, stepDuration);
+
+            return _animationKeyFrame;
+        }
+
+        private void ConvertVowelsToVisemes(in string[] vowels,
+                                            out string[] visemes)
+        {
+            visemes = new string[vowels.Length];
+
+            for (var i = 0; i < vowels.Length; i++)
+            {
+                if (!VowelToViseme.TryGetValue(vowels[i], out visemes[i]))
+                {
+                    throw new InvalidVowelTypeException(vowels[i]);
+                }
+            }
+        }
+
+        private void ConvertVisemesToAnimationKeyFrame(in string[] visemes,
+                                                       float stepDuration)
+        {
+            float animationTime = visemes.Length * stepDuration;
+            _animationKeyFrame.SetAnimationTime(animationTime);
+
+            for (int i = 0; i < visemes.Length; i++)
+            {
+                float timeStamp = GetTimeStamp(i, stepDuration) / animationTime;
+
+                foreach (var info in _visemeMap[visemes[i]])
+                {
+                    _animationKeyFrame.AddKeyFrame(info.nodeName,
+                                                   info.blendIndex,
+                                                   timeStamp,
+                                                   info.blendValue);
+                }
+            }
+        }
+
+        private void ConvertVisemesToAnimationKeyFrameMic(in string[] visemes,
+                                                          float stepDuration)
+        {
+            float animationTime = (visemes.Length - 1) * stepDuration;
+            _animationKeyFrame.SetAnimationTime(animationTime);
+
+            for (int i = 0; i < visemes.Length; i++)
+            {
+                float timeStamp = GetTimeStamp(i, stepDuration) / animationTime;
+
+                foreach (var info in _visemeMap[visemes[i]])
+                {
+                    _animationKeyFrame.AddKeyFrame(info.nodeName,
+                                                   info.blendIndex,
+                                                   timeStamp,
+                                                   info.blendValue);
+                }
+            }
+        }
+
+        private float GetTimeStamp(int idx, float stepDuration)
+        {
+            if (idx > 0)
+                return (idx * stepDuration) - (stepDuration / 2.0f);
+            else
+                return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/AnimationKeyFrame.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/AnimationKeyFrame.cs
new file mode 100644 (file)
index 0000000..04f2a6e
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.Generic;
+
+namespace Tizen.AIAvatar
+{
+    internal class KeyFrame
+    {
+        public KeyFrame(float t, float v)
+        {
+            time = t;
+            value = v;
+        }
+        public float time;
+        public float value;
+    }
+
+    internal class AnimationKeyFrame
+    {
+        private Dictionary<string, List<KeyFrame>[]> _animationKeyFrames;
+
+        public string[] NodeNames { get; private set; }
+        public int[] BlendShapeCounts { get; private set; }
+        public string BlendShapeKeyFormat { get; private set; }
+        public float AnimationTime { get; private set; }
+
+        public AnimationKeyFrame(string[] nodeNames,
+                                 int[] blendShapeCounts,
+                                 string blendShapeKeyFormat)
+        {
+            _animationKeyFrames = new Dictionary<string, List<KeyFrame>[]>();
+            NodeNames = nodeNames;
+            BlendShapeCounts = blendShapeCounts;
+            BlendShapeKeyFormat = blendShapeKeyFormat;
+
+            InitializeAnimation(nodeNames, blendShapeCounts);
+        }
+
+        public void SetAnimationTime(float animationTime)
+        {
+            AnimationTime = animationTime;
+        }
+
+        public List<KeyFrame> GetKeyFrames(string nodeName, int blendIndex)
+        {
+            return _animationKeyFrames[nodeName][blendIndex];
+        }
+
+        public void AddKeyFrame(string nodeName, int blendIndex, float time, float value)
+        {
+            _animationKeyFrames[nodeName][blendIndex].Add(new KeyFrame(time, value));
+        }
+
+        public void AddKeyFrame(string nodeName, int blendIndex, KeyFrame value)
+        {
+            _animationKeyFrames[nodeName][blendIndex].Add(value);
+        }
+
+        public void InitializeAnimation(string[] nodeNames, int[] blendShapeCounts)
+        {
+            ClearAnimation();
+
+            for (int i = 0; i < nodeNames.Length; i++)
+            {
+                var nodeName = nodeNames[i];
+                var blendShapeCount = blendShapeCounts[i];
+
+                _animationKeyFrames.Add(nodeName, new List<KeyFrame>[blendShapeCount]);
+
+                for (int j = 0; j < blendShapeCount; j++)
+                {
+                    _animationKeyFrames[nodeName][j] = new List<KeyFrame>();
+                }
+            }
+        }
+
+        public void ClearAnimation()
+        {
+            foreach (KeyValuePair<string, List<KeyFrame>[]> kvp in _animationKeyFrames)
+            {
+                foreach (List<KeyFrame> kf in kvp.Value)
+                {
+                    kf.Clear();
+                }
+            }
+        }
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/BlendShapeInfo.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/DTO/BlendShapeInfo.cs
new file mode 100644 (file)
index 0000000..8fb8163
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.Generic;
+
+namespace Tizen.AIAvatar
+{
+    internal class BlendShapeInfo
+    {
+        public BlendShapeModelInfo blendShape;
+        public BlendShapeVisemeInfo[] visemes;
+
+        public BlendShapeInfo() { }
+
+        public string[] GetNodeNames()
+        {
+            return blendShape.nodeNames;
+        }
+
+        public int[] GetBlendShapeCounts()
+        {
+            return blendShape.blendShapeCount;
+        }
+
+        public Dictionary<string, BlendShapeValue[]> GetVisemeMap()
+        {
+            Dictionary<string, BlendShapeValue[]> visemeMap
+                = new Dictionary<string, BlendShapeValue[]>();
+
+            foreach (var visemeInfo in this.visemes)
+            {
+                visemeMap.Add(visemeInfo.name, visemeInfo.values);
+            }
+
+            return visemeMap;
+        }
+    }
+
+    internal class BlendShapeModelInfo
+    {
+        public string keyFormat;
+        public string[] nodeNames;
+        public int[] blendShapeCount;
+
+        public BlendShapeModelInfo(string keyFormat, string[] nodeNames, int[] blendShapeCount)
+        {
+            this.keyFormat = keyFormat;
+            this.nodeNames = nodeNames;
+            this.blendShapeCount = blendShapeCount;
+        }
+    }
+
+    internal class BlendShapeVisemeInfo
+    {
+        public string name;
+        public BlendShapeValue[] values;
+    }
+
+    internal class BlendShapeValue
+    {
+        public string nodeName;
+        public int blendIndex;
+        public float blendValue;
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/Exception.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/Vowel2Animation/Exception.cs
new file mode 100644 (file)
index 0000000..da7b089
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+    internal class InvalidVowelTypeException : Exception
+    {
+        public InvalidVowelTypeException() { }
+
+        public InvalidVowelTypeException(string name)
+            : base($"Not supported vowel type, {name}") { }
+    }
+
+    internal class FailedPersonalizeException : Exception
+    {
+        public FailedPersonalizeException() { }
+
+        public FailedPersonalizeException(string name)
+            : base($"Failed to personalize, file_path : {name}") { }
+    }
+
+    internal class NotInitializedException : Exception
+    {
+        public NotInitializedException()
+            : base($"Animation Converter should be initialized with viseme_info") { }
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelClassifier.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelClassifier.cs
new file mode 100644 (file)
index 0000000..958b6f1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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;
+
+namespace Tizen.AIAvatar
+{
+    internal class VowelClassifier
+    {
+        private readonly int vowelForecastRange = 6;
+        private readonly string[] vowelName = { "A", "E", "I", "O", "U", "HM", "ER" };
+
+        private readonly int sampleRate;
+        private readonly int featureCount;
+
+        private float stepTime = 0.0f;
+        private const int DefaultFeatureMeanCount = 8;
+
+        private CustomMfccExtractor mfccExtractor;
+
+
+        internal VowelClassifier(int sampleRate, float frameDuration = 0.025f, float hopDuration = 0.01f, int featureCount = 12)
+        {
+            this.sampleRate = sampleRate;
+            this.featureCount = featureCount;
+            stepTime = (float)(hopDuration * DefaultFeatureMeanCount);
+            mfccExtractor = new CustomMfccExtractor(sampleRate, frameDuration, hopDuration, featureCount);
+        }
+
+        public float GetStepTime()
+        {
+            return stepTime;
+        }
+
+
+        public string[] Inference(byte[] audio, ISingleShotModel model)
+        {
+            int length = PreprocessInput(audio, out float[] features, out List<float> audioEnergy);
+            var outputData = InferenceModel(features, length, model);
+            var result = ProcessOutput(outputData, audioEnergy);
+
+            return result.ToArray();
+        }
+
+        public int PreprocessInput(byte[] audioByte, out float[] flattenedArray, out List<float> audioEnergy)
+        {
+            Utils.ConvertAudioToFloat(audioByte, out var audio);
+
+            var mfccFeatures = mfccExtractor.ComputeFrom(audio, sampleRate);
+
+            int featureLength = (int)Math.Ceiling((double)mfccFeatures.Count / DefaultFeatureMeanCount);
+            flattenedArray = new float[featureLength * featureCount];
+            audioEnergy = new List<float>();
+
+            for (int i = 0; i < featureLength; i++)
+            {
+                CalculateFeatureMean(mfccFeatures, out var featureMean, out var energy, i * DefaultFeatureMeanCount);
+                Array.Copy(featureMean, 0, flattenedArray, i * featureCount, featureCount);
+                audioEnergy.Add(energy);
+            }
+
+            return featureLength;
+        }
+
+        public List<float[]> InferenceModel(float[] input, int length, ISingleShotModel model)
+        {
+            byte[] inputBuffer = new byte[4 * 12 * 1 * 1];
+            byte[] outputBuffer;
+
+            int srcOffset = 0;
+            List<float[]> fullOutput = new List<float[]>();
+
+            for (int i = 0; i < length; i++)
+            {
+                float[] output = new float[7];
+
+                Buffer.BlockCopy(input, srcOffset, inputBuffer, 0, inputBuffer.Length);
+                srcOffset += inputBuffer.Length;
+
+                model.SetTensorData(0, inputBuffer);
+
+                model.Invoke();
+
+                outputBuffer = model.GetTensorData(0);
+
+                Buffer.BlockCopy(outputBuffer, 0, output, 0, outputBuffer.Length);
+
+                fullOutput.Add(output);
+
+            }
+
+            return fullOutput;
+        }
+
+        private List<string> ProcessOutput(List<float[]> outputData, List<float> audioEnergy)
+        {
+            var result = new List<string>();
+
+            for (int i = 0; i < outputData.Count; i++)
+            {
+                // Softmax 연산 수행
+                if (audioEnergy[i] > -1.0f)
+                {
+                    var scores = Enumerable.Range(0, vowelForecastRange).
+                       Select(j => outputData[i][j]).SoftMax().ToArray();
+                    int maxIndex = Array.IndexOf(scores, scores.Max());
+                    result.Add(vowelName[maxIndex]);
+                }
+                else
+                {
+                    result.Add("sil");
+                }
+
+            }
+
+            return result;
+        }
+
+        private void CalculateFeatureMean(in List<float[]> features,
+                                  out float[] featureMean,
+                                  out float energy,
+                                  int offset)
+        {
+            featureMean = new float[featureCount];
+            int featureLength = (DefaultFeatureMeanCount < features.Count - offset
+                                    ? DefaultFeatureMeanCount
+                                    : features.Count - offset);
+
+            float sum = 0f;
+            for (int fc = 0; fc < featureCount; fc++)
+            {
+                for (int dc = offset; dc < offset + DefaultFeatureMeanCount && dc < features.Count; dc++)
+                {
+                    featureMean[fc] += features[dc][fc];
+                }
+                featureMean[fc] /= featureLength;
+                sum += featureMean[fc];
+            }
+            energy = sum / featureCount;
+        }
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelConverter.cs b/src/Tizen.AIAvatar/src/Lipsync/VowelConverter/VowelConverter.cs
new file mode 100644 (file)
index 0000000..0b65da2
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.Generic;
+
+using static Tizen.AIAvatar.AIAvatar;
+
+namespace Tizen.AIAvatar
+{
+    internal class VowelConverter
+    {
+        private TFVowel6 vowelClassfierModel = null;
+        private VowelClassifier vowelClassifier = null;
+        private Dictionary<int, VowelClassifier> vowelClassifiers;
+        private AnimationConverter animationConverter = new AnimationConverter();
+
+        internal VowelConverter()
+        {
+            vowelClassifiers = new Dictionary<int, VowelClassifier>();
+            vowelClassifier = GetVowelClassifier(CurrentAudioOptions.SampleRate);
+            vowelClassfierModel = new TFVowel6(new int[4] { 12, 1, 1, 1 }, new int[4] { 7, 1, 1, 1 });
+            animationConverter.InitializeVisemeInfo(VisemeInfo);
+        }
+
+        internal AnimationKeyFrame CreateKeyFrames(string[] vowels, int sampleRate, bool isMic = false)
+        {
+            vowelClassifier = GetVowelClassifier(sampleRate);
+
+            if (isMic)
+            {
+                return animationConverter.ConvertVowelsToAnimationMic(vowels, vowelClassifier.GetStepTime());
+            }
+            else
+            {
+                return animationConverter.ConvertVowelsToAnimation(vowels, vowelClassifier.GetStepTime());
+            }
+        }
+
+        internal AnimationKeyFrame CreateKeyFrames(byte[] audioData, int sampleRate, bool isMic = false)
+        {
+            vowelClassifier = GetVowelClassifier(sampleRate);
+
+            if (vowelClassifier == null)
+            {
+                Log.Error(LogTag, "Failed to play Buffer");
+                return null;
+            }
+            var vowels = PredictVowels(audioData);
+            Log.Info(LogTag, $"vowelRecognition: {string.Join(", ", vowels)}");
+
+            if (isMic) return animationConverter.ConvertVowelsToAnimationMic(vowels, vowelClassifier.GetStepTime());
+            else return animationConverter.ConvertVowelsToAnimation(vowels, vowelClassifier.GetStepTime());
+        }
+
+        internal string[] PredictVowels(byte[] audioData)
+        {
+            var vowels = vowelClassifier.Inference(audioData, vowelClassfierModel);
+            return vowels;
+        }
+
+        internal VowelClassifier GetVowelClassifier(int sampleRate)
+        {
+            if (vowelClassifiers.ContainsKey(sampleRate))
+            {
+                return vowelClassifiers[sampleRate];
+            }
+            else
+            {
+                vowelClassifiers[sampleRate] = new VowelClassifier(sampleRate);
+                return vowelClassifiers[sampleRate];
+            }
+        }
+    }
+}
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,9 +23,8 @@ namespace Tizen.AIAvatar
     /// <summary>
     /// Provides the ability to audio 
     /// </summary>
-    // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class AudioOptions
+    internal class AudioOptions
     {
         private int sampleRate;
         private AudioChannel channel;
@@ -37,7 +36,6 @@ namespace Tizen.AIAvatar
         /// <param name="sampleRate">the audio sample rate (8000 ~ 192000Hz)</param>
         /// <param name="channel">the audio channel type.</param>
         /// <param name="sampleType">the audio sample type.</param>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AudioOptions(int sampleRate, AudioChannel channel, AudioSampleType sampleType)
         {
@@ -49,21 +47,18 @@ namespace Tizen.AIAvatar
         /// <summary>
         /// The audio sample rate (8000 ~ 192000Hz)
         /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
         public int SampleRate { get => sampleRate; set => sampleRate = value; }
 
         /// <summary>
         /// The audio channel type
         /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AudioChannel Channel { get => channel; set => channel = value; }
 
         /// <summary>
         /// The audio sample type
         /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
         [EditorBrowsable(EditorBrowsableState.Never)]
         public AudioSampleType SampleType { get => sampleType; set => sampleType = value; }
     }
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
 {
-    internal class AudioPlayer
+    internal class AudioPlayer : IDisposable
     {
         private AudioPlayback audioPlayback;
         private MemoryStream audioStream;
@@ -55,7 +55,7 @@ namespace Tizen.AIAvatar
             {
                 if (audioPlayback != null)
                 {
-                    DestroyAudioPlayback();
+                    Destroy();
                 }
                 if (sampleRate == 0)
                 {
@@ -66,7 +66,6 @@ namespace Tizen.AIAvatar
             catch (Exception e)
             {
                 Log.Error(LogTag, $"Failed to create AudioPlayback. {e.Message}");
-                return;
             }
 
             if (audioPlayback != null)
@@ -112,7 +111,7 @@ namespace Tizen.AIAvatar
             if (audioPlayback != null)
             {
                 audioPlayback.Pause();
-                DestroyAudioPlayback();
+                Destroy();
             }
             else
             {
@@ -120,12 +119,16 @@ namespace Tizen.AIAvatar
             }
         }
 
-        internal void Destroy()
+        public void Dispose()
         {
-            DestroyAudioPlayback();
+            Destroy();
+
+            audioStream?.Flush();
+            audioStream?.Dispose();
+            audioStream = null;
         }
 
-        private void DestroyAudioPlayback()
+        private void Destroy()
         {
             audioPlayback?.Unprepare();
             audioPlayback?.Dispose();
 
 using System;
 using System.Linq;
-using System.Text;
 using Tizen.Multimedia;
 using Tizen.NUI;
 using static Tizen.AIAvatar.AIAvatar;
 
 namespace Tizen.AIAvatar
 {
-    internal class AudioRecorder
+    internal class AudioRecorder : IDisposable
     {
         private const string privilegeForRecording = "http://tizen.org/privilege/recorder";
 
@@ -37,7 +36,7 @@ namespace Tizen.AIAvatar
         private Timer audioRecordingTimer;
 
         private Action audioRecdingAction;
-        private Action<byte[],int > bufferAction;
+        private Action<byte[], int> bufferAction;
 
         private static AudioRecorder instance;
 
@@ -61,29 +60,23 @@ namespace Tizen.AIAvatar
             desiredBufferLength = (int)(CurrentAudioOptions.SampleRate * desiredBufferDuration * 2);
         }
 
-
-        internal void InitMic(BlendShapePlayer animator, uint recordingTime = 160)
+        internal void InitializeMic(LipSyncer lipSyncer, uint recordingTime = 160)
         {
             audioRecordingTimer = new Timer(recordingTime);
-            if (animator != null)
+            if (lipSyncer != null)
             {
-                //TODO : Connection MIC
-                var lipSyncer = (animator.GetAnimationModule(AnimationModuleType.LipSyncer) as LipSyncer);
-                if(lipSyncer != null)
-                {
-                    Tizen.Log.Error(LogTag, "LipSyncer of animator is null");
-                    return;
-                }
-                this.audioRecdingAction = lipSyncer.OnRecodingTick;
-                this.bufferAction = lipSyncer.OnRecordBufferChanged;
-
-                BufferChanged += OnRecordBufferChanged;
-                audioRecordingTimer.Tick += AudioRecordingTimerTick;
+                Tizen.Log.Error(LogTag, "LipSyncer of animator is null");
+                return;
             }
+            this.audioRecdingAction = lipSyncer.OnRecodingTick;
+            this.bufferAction = lipSyncer.OnRecordBufferChanged;
+
+            BufferChanged += OnRecordBufferChanged;
+            audioRecordingTimer.Tick += AudioRecordingTimerTick;
         }
 
 
-        internal void DeinitMic()
+        internal void DeinitializeMic()
         {
             StopRecording();
             BufferChanged -= OnRecordBufferChanged;
@@ -147,5 +140,10 @@ namespace Tizen.AIAvatar
             audioRecdingAction?.Invoke();
             return true;
         }
+
+        public void Dispose()
+        {
+            throw new NotImplementedException();
+        }
     }
 }
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using Tizen.Uix.Tts;
-using System.Runtime.InteropServices;
 
 using static Tizen.AIAvatar.AIAvatar;
-using System.Linq;
-using Tizen.NUI;
 
 namespace Tizen.AIAvatar
 {
-    internal class TTSLipSyncer
+    internal class TTSControllerEventArgs : EventArgs
     {
+        public TTSControllerEventArgs()
+        {
+        }
+
+        public TTSControllerEventArgs(byte[] audioData, int sampleRate)
+        {
+            //AudioData = audioData;
+            AudioData = new byte[audioData.Length];
+            Buffer.BlockCopy(audioData, 0, AudioData, 0, audioData.Length);
+            SampleRate = sampleRate;
+        }
+
+        public byte[] AudioData
+        {
+            get;
+            internal set;
+        }
 
-        private Avatar currentAvatar;
+        public int SampleRate
+        {
+            get;
+            internal set;
+        }
+    }
 
+    internal class TTSController : IDisposable
+    {
         private List<UtteranceText> textList;
         private TtsClient ttsHandle;
         private VoiceInfo voiceInfo;
@@ -55,22 +77,24 @@ namespace Tizen.AIAvatar
         private int audioLength;
         private bool isAsyncLipStarting;
 
-        private AsyncLipSyncer lipSyncer;
+        internal event EventHandler PlayReadyCallback;
 
+        internal event EventHandler<TTSControllerEventArgs> PreparedSyncText;
+        internal event EventHandler<TTSControllerEventArgs> StoppedTTS;
+        
+        
+        internal event EventHandler<TTSControllerEventArgs> UpdatedBuffer;
 
-        internal TTSLipSyncer(AsyncLipSyncer lipSyncer)
+        internal TTSController()
         {
-            this.lipSyncer = lipSyncer;
             InitTts();
         }
 
-        ~TTSLipSyncer()
+        ~TTSController()
         {
             DeinitTts();
         }
 
-        internal event EventHandler PlayReadyCallback;
-
         internal TtsClient TtsHandle
         {
             get { return ttsHandle; }
@@ -99,7 +123,7 @@ namespace Tizen.AIAvatar
             foreach (var supportedVoice in supportedVoices)
             {
                 Log.Info(LogTag, $"{supportedVoice.Language} & {supportedVoice.VoiceType} is supported");
-                voiceInfoList.Add(new VoiceInfo() { Lang = supportedVoice.Language, Type = supportedVoice.VoiceType });
+                voiceInfoList.Add(new VoiceInfo() { Language = supportedVoice.Language, Type = (VoiceType)supportedVoice.VoiceType });
             }
             return voiceInfoList;
         }
@@ -134,9 +158,9 @@ namespace Tizen.AIAvatar
             var supportedVoices = ttsHandle.GetSupportedVoices();
             foreach (var supportedVoice in supportedVoices)
             {
-                if (supportedVoice.Language.Equals(voiceInfo.Lang) && (supportedVoice.VoiceType == voiceInfo.Type))
+                if (supportedVoice.Language.Equals(voiceInfo.Language) && ((VoiceType)supportedVoice.VoiceType == voiceInfo.Type))
                 {
-                    Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is supported");
+                    Log.Info(LogTag, $"{voiceInfo.Language} & {voiceInfo.Type} is supported");
                     return true;
                 }
             }
@@ -146,7 +170,7 @@ namespace Tizen.AIAvatar
 
         internal void AddText(string txt, VoiceInfo voiceInfo)
         {
-            if (voiceInfo.Lang == null || voiceInfo.Type == null)
+            if (voiceInfo.Language == null)
             {
                 Log.Error(LogTag, "VoiceInfo's value is null");
             }
@@ -157,7 +181,7 @@ namespace Tizen.AIAvatar
             }
             var temp = new UtteranceText();
             temp.Text = txt;
-            temp.UttID = ttsHandle.AddText(txt, voiceInfo.Lang, (int)voiceInfo.Type, 0);
+            temp.UttID = ttsHandle.AddText(txt, voiceInfo.Language, (int)voiceInfo.Type, 0);
             try
             {
                 textList.Add(temp);
@@ -202,12 +226,14 @@ namespace Tizen.AIAvatar
             Play(true);
         }
 
+
         internal bool PlayPreparedText()
         {
             if (byteList != null && byteList.Count != 0)
             {
                 Log.Info(LogTag, "PlayPreparedText TTS");
-                currentAvatar?.AvatarAnimator?.PlayLipSync(byteList.ToArray(), sampleRate);
+
+                PreparedSyncText?.Invoke(this, new TTSControllerEventArgs(byteList.ToArray(), sampleRate));
                 return true;
             }
             return false;
@@ -258,43 +284,7 @@ namespace Tizen.AIAvatar
                 return;
             }
             ttsHandle.Stop();
-            currentAvatar?.AvatarAnimator?.StopLipSync();
-        }
-
-        private void InitTts()
-        {
-            try
-            {
-                ttsHandle = new TtsClient();
-
-                // Register Callbacks
-                ttsHandle.DefaultVoiceChanged += TtsDefaultVoiceChangedCallback;
-                ttsHandle.EngineChanged += TtsEngineChangedCallback;
-                ttsHandle.ErrorOccurred += TtsErrorOccuredCallback;
-                ttsHandle.StateChanged += TtsStateChangedCallback;
-                ttsHandle.UtteranceCompleted += TtsUtteranceCompletedCallback;
-                ttsHandle.UtteranceStarted += TtsUtteranceStartedCallback;
-
-                ttsHandle.SynthesizedPcm += TtsSyntheiszedPCM;
-                ttsHandle.PlayingMode = PlayingMode.ByClient;
-
-                ttsHandle.Prepare();
-
-                voiceInfo = new VoiceInfo
-                {
-                    Lang = ttsHandle.DefaultVoice.Language,
-                    Type = ttsHandle.DefaultVoice.VoiceType
-                };
-
-                textList = new List<UtteranceText>();
-                Log.Info(LogTag, voiceInfo.Lang + ", " + voiceInfo.Type.ToString());
-
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, "[ERROR] Fail to prepare Tts");
-                Log.Error(LogTag, e.Message);
-            }
+            StoppedTTS?.Invoke(this, new TTSControllerEventArgs());
         }
 
         internal void DeinitTts()
@@ -328,7 +318,6 @@ namespace Tizen.AIAvatar
                     byteList.Clear();
                     byteList = null;
                 }
-                currentAvatar = null;
             }
             catch (Exception e)
             {
@@ -337,9 +326,44 @@ namespace Tizen.AIAvatar
             }
         }
 
-        private void TtsSyntheiszedPCM(object sender, SynthesizedPcmEventArgs e)
+        private void InitTts()
         {
+            try
+            {
+                ttsHandle = new TtsClient();
+
+                // Register Callbacks
+                ttsHandle.DefaultVoiceChanged += TtsDefaultVoiceChangedCallback;
+                ttsHandle.EngineChanged += TtsEngineChangedCallback;
+                ttsHandle.ErrorOccurred += TtsErrorOccuredCallback;
+                ttsHandle.StateChanged += TtsStateChangedCallback;
+                ttsHandle.UtteranceCompleted += TtsUtteranceCompletedCallback;
+                ttsHandle.UtteranceStarted += TtsUtteranceStartedCallback;
 
+                ttsHandle.SynthesizedPcm += TtsSyntheiszedPCM;
+                ttsHandle.PlayingMode = PlayingMode.ByClient;
+
+                ttsHandle.Prepare();
+
+                voiceInfo = new VoiceInfo
+                {
+                    Language = ttsHandle.DefaultVoice.Language,
+                    Type = (VoiceType)ttsHandle.DefaultVoice.VoiceType
+                };
+
+                textList = new List<UtteranceText>();
+                Log.Info(LogTag, voiceInfo.Language + ", " + voiceInfo.Type.ToString());
+
+            }
+            catch (Exception e)
+            {
+                Log.Error(LogTag, "[ERROR] Fail to prepare Tts");
+                Log.Error(LogTag, e.Message);
+            }
+        }
+
+        private void TtsSyntheiszedPCM(object sender, SynthesizedPcmEventArgs e)
+        {
             var dataSize = e.Data.Length;
             var audio = new byte[dataSize];
             sampleRate = e.SampleRate;
@@ -369,8 +393,7 @@ namespace Tizen.AIAvatar
                         audioTailLength = (int)(sampleRate * audioTailLengthFactor * audioBufferMultiflier);
                         audioTailBuffer = new byte[audioTailLength];
                         PlayReadyCallback?.Invoke(null, EventArgs.Empty);
-                        InitAsyncBuffer();
-                        lipSyncer.SampleRate = sampleRate;
+                        //InitAsyncBuffer();
                     }
                     break;
                 case SynthesizedPcmEvent.Continue://continue
@@ -381,8 +404,8 @@ namespace Tizen.AIAvatar
                         if (recordedBuffer.Length >= desiredBufferLength)
                         {
                             Tizen.Log.Error(LogTag, "Current recordbuffer length :" + recordedBuffer.Length);
-                            UpdateBuffer(recordedBuffer, sampleRate);
-                            
+                            //UpdateBuffer(recordedBuffer, sampleRate);
+
                             Buffer.BlockCopy(recordedBuffer, recordedBuffer.Length - audioTailLength, audioTailBuffer, 0, audioTailLength);
 
                             recordedBuffer = Array.Empty<byte>();
@@ -402,7 +425,7 @@ namespace Tizen.AIAvatar
                         if (!isPrepared)
                         {
                             //Play voice immediately
-                            //PlayPreparedText();
+                            PlayPreparedText();
                         }
                         else
                         {
@@ -412,8 +435,9 @@ namespace Tizen.AIAvatar
                         }
                     }
                     else
-                    {
-                        lipSyncer.SetFinishAsyncLip(true);
+                    {//async
+                        //FinishedSynthesizedPcm?.Invoke(null, EventArgs.Empty);
+                        //lipSyncer.SetFinishAsyncLip(true);
                     }
                     break;
                 case SynthesizedPcmEvent.Fail: //fail
@@ -462,8 +486,10 @@ namespace Tizen.AIAvatar
             Log.Debug(LogTag, "Default voice is changed from (" + e.Previous + ") to (" + e.Current + ")");
         }
 
-        internal void InitAsyncBuffer()
+        private void InitAsyncBuffer()
         {
+            /*
+            InitedAsyncBuffer?.Invoke(null, EventArgs.Empty);
             if (!lipSyncer.IsAsyncInit)
             {
                 audioLength = (int)(sampleRate * 0.16f * 2f);
@@ -473,11 +499,13 @@ namespace Tizen.AIAvatar
 
                 lipSyncer.SetFinishAsyncLip(false);
                 isAsyncLipStarting = false;
-            }
+            }*/
         }
 
-        internal void UpdateBuffer(byte[] recordBuffer, int sampleRate)
+        private void UpdateBuffer(byte[] recordBuffer, int sampleRate)
         {
+            UpdatedBuffer?.Invoke(this, new TTSControllerEventArgs(recordBuffer, sampleRate));
+            /*
             if (lipSyncer != null)
             {
                 Log.Error(LogTag, "OnTTSBufferChanged");
@@ -491,7 +519,14 @@ namespace Tizen.AIAvatar
             else
             {
                 Log.Error(LogTag, "avatarLipSyncer is null");
-            }
+            }*/
+        }
+
+        public void Dispose()
+        {
+            ttsHandle.Stop();
+            ttsHandle.Dispose();
+            ttsHandle = null;
         }
     }
 }
diff --git a/src/Tizen.AIAvatar/src/Multimedia/TTS/UtteranceText.cs b/src/Tizen.AIAvatar/src/Multimedia/TTS/UtteranceText.cs
new file mode 100644 (file)
index 0000000..2e69e1f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace Tizen.AIAvatar
+{
+    internal struct UtteranceText : IEquatable<UtteranceText>
+    {
+        private string text;
+        private int uttID;
+
+        public string Text { get => text; set => text = value; }
+        public int UttID { get => uttID; set => uttID = value; }
+
+        public override bool Equals(object obj) => obj is VoiceInfo other && this.Equals(other);
+
+        public bool Equals(UtteranceText other) => Text == other.Text && UttID == other.UttID;
+
+        public static bool operator ==(UtteranceText lhsUtternaceText, UtteranceText rhsUtternaceText) => lhsUtternaceText.Equals(rhsUtternaceText);
+
+        public static bool operator !=(UtteranceText lhsUtternaceText, UtteranceText rhsVoiceInfo) => !lhsUtternaceText.Equals(rhsVoiceInfo);
+
+        public override int GetHashCode() => (Text, UttID).GetHashCode();
+    }
+}
diff --git a/src/Tizen.AIAvatar/src/Multimedia/TTS/VoiceInfo.cs b/src/Tizen.AIAvatar/src/Multimedia/TTS/VoiceInfo.cs
new file mode 100644 (file)
index 0000000..25e8e71
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+
+namespace Tizen.AIAvatar
+{
+    /// <summary>
+    /// VoiceInfo stores information about a voice. 
+    /// The 'Language' field stores the language of the voice
+    /// The 'Type' field stores the type of the voice. 
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public struct VoiceInfo : IEquatable<VoiceInfo>
+    {
+        private string language;
+        private VoiceType type;
+
+        /// <summary>  
+        /// Gets or sets the language of the voice.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Language { get => language; set => language = value; }
+
+        /// <summary>  
+        /// Gets or sets the type of the voice.  
+        /// </summary>  
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VoiceType Type { get => type; set => type = value; }
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="obj">The object to compare with the current object.</param>
+        /// <returns>true if equal VoiceInfo, else false.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override bool Equals(object obj) => obj is VoiceInfo other && this.Equals(other);
+
+        /// <summary>
+        /// Determines whether the specified object is equal to the current object.
+        /// </summary>
+        /// <param name="other">The VoiceInfo to compare with the current VoiceInfo.</param>
+        /// <returns>true if equal VoiceInfo, else false.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Equals(VoiceInfo other) => Language == other.Language && Type == other.Type;
+
+        /// <summary>
+        /// The == operator.
+        /// </summary>
+        /// <param name="lhsVoiceInfo">VoiceInfo to compare</param>
+        /// <param name="rhsVoiceInfo">VoiceInfo to be compared</param>
+        /// <returns>true if VoiceInfo are equal</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool operator ==(VoiceInfo lhsVoiceInfo, VoiceInfo rhsVoiceInfo) => lhsVoiceInfo.Equals(rhsVoiceInfo);
+
+        /// <summary>
+        /// The != operator.
+        /// </summary>
+        /// <param name="lhsVoiceInfo">VoiceInfo to compare</param>
+        /// <param name="rhsVoiceInfo">VoiceInfo to be compared</param>
+        /// <returns>true if VoiceInfo are not equal</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool operator !=(VoiceInfo lhsVoiceInfo, VoiceInfo rhsVoiceInfo) => !lhsVoiceInfo.Equals(rhsVoiceInfo);
+
+        /// <summary>
+        /// Gets the hash code of this VoiceInfo.
+        /// </summary>
+        /// <returns>The hash code.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public override int GetHashCode() => (Language, Type).GetHashCode();
+    }
+}
  */
 
 using System.ComponentModel;
-using Tizen.NUI.Scene3D;
 
 namespace Tizen.AIAvatar
 {
+    /// <summary>
+    /// VoiceType is an enum that represents various types of voices.  
+    /// </summary>
     [EditorBrowsable(EditorBrowsableState.Never)]
-    public class MotionBehaviorData : IAnimationModuleData
+    public enum VoiceType
     {
+        /// <summary>
+        /// Automatically determines the best voice to use based on available options.  
+        /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public MotionDataType Type { get; set; } = MotionDataType.AnimationInfo;
+        Auto = 0,
+        /// <summary>
+        /// Selects a male voice for speech synthesis.
+        /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public AnimationInfo AnimationInfo { get; set; }
+        Male = 1,
+        /// <summary>
+        /// Selects a female voice for speech synthesis.
+        /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public MotionData MotionData { get; set; }
+        Female = 2,
+        /// <summary>
+        /// Selects a child's voice for speech synthesis.
+        /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public int Duration { get; set; } = 3000;
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public bool IsLooping { get; set; } = false;
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public int LoopCount { get; set; } = 1;
+        Child = 3
     }
 }
  *
  */
 
-using System.ComponentModel;
-
 namespace Tizen.AIAvatar
 {
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public interface IAnimationModuleData
+    internal class TrackingController
     {
-    }
+        internal TrackingController()
+        {
+        }
 
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public enum MotionDataType
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        AnimationInfo,
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        MotionData
+        internal void Initialize(TrackingOptions options)
+        {
+        }
+
+        internal void StartMotionTracking()
+        {
+
+        }
+
+        internal void StopMotionTracking()
+        {
+
+        }
     }
 }
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,9 +17,7 @@
 
 namespace Tizen.AIAvatar
 {
-    internal struct UtteranceText
+    internal class TrackingOptions
     {
-        internal string Text;
-        internal int UttID;
     }
 }
diff --git a/src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeInfo.cs b/src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeInfo.cs
deleted file mode 100644 (file)
index 71e0ad1..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.Generic;
-using System.ComponentModel;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    internal class BlendShapeInfo
-    {
-        internal BlendShapeModelInfo blendShape;
-        internal BlendShapeVisemeInfo[] visemes;
-
-        internal BlendShapeInfo() { }
-
-        internal string[] GetNodeNames()
-        {
-            if (blendShape == null)
-            {
-                Log.Error(LogTag, "blendShape is null");
-                return null;
-            }
-            return blendShape.nodeNames;
-        }
-
-        internal int[] GetBlendShapeCounts()
-        {
-            return blendShape.blendShapeCount;
-        }
-
-        internal Dictionary<Viseme, BlendShapeValue[]> GetVisemeMap()
-        {
-            Dictionary<Viseme, BlendShapeValue[]> visemeMap = new Dictionary<Viseme, BlendShapeValue[]>();
-
-            foreach (var visemeInfo in visemes)
-            {
-                visemeMap.Add(visemeInfo.name, visemeInfo.values);
-            }
-
-            return visemeMap;
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeModelInfo.cs b/src/Tizen.AIAvatar/src/internal/BlendShapeInfo/BlendShapeModelInfo.cs
deleted file mode 100644 (file)
index df8e125..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
-    internal class BlendShapeModelInfo
-    {
-        internal string keyFormat;
-        internal string[] nodeNames;
-        internal int[] blendShapeCount;
-
-        internal BlendShapeModelInfo(string keyFormat, string[] nodeNames, int[] blendShapeCount)
-        {
-            this.keyFormat = keyFormat;
-            this.nodeNames = nodeNames;
-            this.blendShapeCount = blendShapeCount;
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/internal/LipSync/LipInfo.cs b/src/Tizen.AIAvatar/src/internal/LipSync/LipInfo.cs
deleted file mode 100644 (file)
index 8d785c9..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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 Tizen.NUI;
-
-namespace Tizen.AIAvatar
-{
-    internal class LipInfo
-    {
-        private Animatable animatable;
-        private string nodeName;
-        private int blendShapeCount;
-
-        internal Animatable Animatable { get => animatable; set => animatable = value; }
-        internal string NodeName { get => nodeName; set => nodeName = value; }
-        internal int BlendShapeCount { get => blendShapeCount; set => blendShapeCount = value; }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/internal/LipSync/Models/ISingleShotModel.cs b/src/Tizen.AIAvatar/src/internal/LipSync/Models/ISingleShotModel.cs
deleted file mode 100644 (file)
index cf2cb8e..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
-    internal interface ISingleShotModel
-    {
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SetTensorData(int index, byte[] buffer);
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public byte[] GetTensorData(int index);
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Invoke();
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/internal/LipSync/Models/SoftmaxLinqExtension.cs b/src/Tizen.AIAvatar/src/internal/LipSync/Models/SoftmaxLinqExtension.cs
deleted file mode 100644 (file)
index de471a3..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Tizen.AIAvatar
-{
-    internal static class SoftmaxLinqExtension
-    {
-        internal static IEnumerable<float> SoftMax(this IEnumerable<float> source)
-        {
-            var exp = source.Select(x => MathF.Exp(x)).ToArray();
-            var sum = exp.Sum();
-            return exp.Select(x => x / sum);
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/internal/LipSync/Viseme.cs b/src/Tizen.AIAvatar/src/internal/LipSync/Viseme.cs
deleted file mode 100644 (file)
index 431c500..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.AIAvatar
-{
-    // Various visemes
-    internal enum Viseme
-    {
-        sil,
-        AE,
-        Ah,
-        B_M_P,
-        Ch_J,
-        EE,
-        Er,
-        IH,
-        Oh,
-        W_OO,
-        R,
-    };
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AnimationModule.cs b/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AnimationModule.cs
deleted file mode 100644 (file)
index 42f49ad..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public abstract class AnimationModule
-    {
-        private AvatarMotionState currentMotionState = AvatarMotionState.Unavailable;
-
-        private readonly Object motionChangedLock = new Object();
-        private event EventHandler<AvatarMotionChangedEventArgs> motionChanged;
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AnimationModule()
-        {
-
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarMotionState CurrentMotionState
-        {
-            get
-            {
-                return currentMotionState;
-            }
-            set
-            {
-                if (currentMotionState == value)
-                {
-                    return;
-                }
-                var preState = currentMotionState;
-                currentMotionState = value;
-                if (motionChanged != null)
-                {
-                    motionChanged?.Invoke(this, new AvatarMotionChangedEventArgs(preState, currentMotionState));
-                }
-            }
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public abstract void Init(Animation animation);
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public abstract void Play(IAnimationModuleData data);
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public abstract void Stop();
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public abstract void Pause();
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public abstract void Destroy();
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        protected void SetCurrentState(AvatarMotionState state)
-        {
-            CurrentMotionState = state;
-        }
-
-
-        /// <summary>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public event EventHandler<AvatarMotionChangedEventArgs> MotionStateChanged
-        {
-            add
-            {
-                lock (motionChangedLock)
-                {
-                    motionChanged += value;
-                }
-
-            }
-
-            remove
-            {
-                lock (motionChangedLock)
-                {
-                    if (motionChanged == null)
-                    {
-                        Log.Error(LogTag, "Remove StateChanged Failed : motionChanged is null");
-                        return;
-                    }
-                    motionChanged -= value;
-                }
-            }
-        }
-    }
-
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public enum AnimationModuleType
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        EyeBlinker,
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        LipSyncer,
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        MotionBehavior,
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        JointTransformer,
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AvatarMotions.cs b/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/AvatarMotions.cs
deleted file mode 100644 (file)
index 364cd3a..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright(c) 2024 Samsung Electronics Co., Ltd.
- *
- * 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 Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
-    internal class AvatarMotions
-    {
-        private MotionData eyeMotionData;
-        private const int blinkDuration = 200;
-
-        internal AvatarMotions(AvatarProperties properties)
-        {
-            CreateEyeBlinkMotionData(properties);
-        }
-
-        internal MotionData EyeMotionData { get { return eyeMotionData; } }
-
-        private void CreateEyeBlinkMotionData(AvatarProperties avatarProperties)
-        {
-            var keyFrames = new KeyFrames();
-            keyFrames.Add(0.1f, 0.0f);
-            keyFrames.Add(0.5f, 1.0f);
-            keyFrames.Add(0.9f, 0.0f);
-
-            var headBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
-            var headBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.HeadGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
-            var eyelashBlendShapeEyeLeft = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkLeft);
-            var eyelashBlendShapeEyeRight = new AvatarBlendShapeIndex(avatarProperties.NodeMapper, NodeType.EyelashGeo, avatarProperties.BlendShapeMapper, BlendShapeType.EyeBlinkRight);
-
-            eyeMotionData = new MotionData(blinkDuration);
-            if (headBlendShapeEyeLeft != null)
-            {
-                eyeMotionData.Add(headBlendShapeEyeLeft, new MotionValue(keyFrames));
-            }
-            if (headBlendShapeEyeRight != null)
-            {
-                eyeMotionData.Add(headBlendShapeEyeRight, new MotionValue(keyFrames));
-            }
-            if (eyelashBlendShapeEyeLeft != null)
-            {
-                eyeMotionData.Add(eyelashBlendShapeEyeLeft, new MotionValue(keyFrames));
-            }
-            if (eyelashBlendShapeEyeRight != null)
-            {
-                eyeMotionData.Add(eyelashBlendShapeEyeRight, new MotionValue(keyFrames));
-            }
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/BlendShapePlayer.cs b/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/BlendShapePlayer.cs
deleted file mode 100644 (file)
index e2beca4..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.ComponentModel;
-using System.Reflection;
-using Tizen.NUI.Scene3D;
-using Tizen.NUI;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    internal class BlendShapePlayer
-    {
-        private Dictionary<AnimationModuleType, AnimationModule> animationModules = new Dictionary<AnimationModuleType, AnimationModule>();
-
-        private EyeBlinker blinker;
-        private AsyncLipSyncer lipSyncer;
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public BlendShapePlayer()
-        {
-            blinker = new EyeBlinker();
-            lipSyncer = new AsyncLipSyncer();
-
-            animationModules.Add(AnimationModuleType.EyeBlinker, blinker);
-            animationModules.Add(AnimationModuleType.LipSyncer, lipSyncer);
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AsyncLipSyncer LipSyncer => lipSyncer;
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SetBlinkAnimation(Animation blinkerAnimation)
-        {
-            animationModules[AnimationModuleType.EyeBlinker].Init(blinkerAnimation);
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void SetLipSyncAnimation(Animation lipsyncAnimation)
-        {
-            animationModules[AnimationModuleType.LipSyncer].Init(lipsyncAnimation);
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AnimationModule GetAnimationModule(AnimationModuleType type)
-        {
-            return animationModules[type];
-        }
-
-        /// <summary>
-        /// Start eye blink animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void StartEyeBlink()
-        {
-            animationModules[AnimationModuleType.EyeBlinker]?.Play(null);
-        }
-
-        /// <summary>
-        /// Stop eye blink animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void StopEyeBlink()
-        {
-            animationModules[AnimationModuleType.EyeBlinker]?.Stop();
-        }
-
-        /// <summary>
-        /// Play synchronization avatar lip based on the voice file(byte[])
-        /// </summary>
-        /// <param name="audio">byte array of voice</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void PlayLipSync(byte[] audio)
-        {
-            if (animationModules[AnimationModuleType.LipSyncer] == null)
-            {
-                Log.Error(LogTag, $"error : avatarLipSync is null");
-                return;
-            }
-            var lipData = new LipSyncData();
-
-            lipData.AudioFile = new byte[audio.Length];
-            Buffer.BlockCopy(audio, 0, lipData.AudioFile, 0, audio.Length);
-            animationModules[AnimationModuleType.LipSyncer].Play(lipData);
-        }
-
-        /// <summary>
-        /// Play synchronization avatar lip based on the voice file(byte[])
-        /// </summary>
-        /// <param name="audio">byte array of voice</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void PlayLipSync(byte[] audio, int sampleRate)
-        {
-            if (animationModules[AnimationModuleType.LipSyncer] == null)
-            {
-                Log.Error(LogTag, $"error : avatarLipSync is null");
-                return;
-            }
-            var lipData = new LipSyncData();
-
-            lipData.AudioFile = new byte[audio.Length];
-            Buffer.BlockCopy(audio, 0, lipData.AudioFile, 0, audio.Length);
-            lipData.SampleRate = sampleRate;
-            animationModules[AnimationModuleType.LipSyncer].Play(lipData);
-        }
-
-        /// <summary>
-        /// Pause lip synchronization
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void PauseLipSync()
-        {
-            if (animationModules[AnimationModuleType.LipSyncer] == null)
-            {
-                Log.Error(LogTag, $"error : avatarLipSync is null");
-                return;
-            }
-            animationModules[AnimationModuleType.LipSyncer].Pause();
-        }
-
-        /// <summary>
-        /// Stop lip synchronization
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void StopLipSync()
-        {
-            if (animationModules[AnimationModuleType.LipSyncer] == null)
-            {
-                Log.Error(LogTag, $"error : avatarLipSync is null");
-                return;
-            }
-            animationModules[AnimationModuleType.LipSyncer].Stop();
-        }
-
-        internal void DestroyAnimations()
-        {
-            foreach ( var animationModule in animationModules.Values )
-            {
-                animationModule.Destroy();
-            }
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/JointTransformer.cs b/src/Tizen.AIAvatar/src/public/Avatar/Animations/AnimationModule/JointTransformer.cs
deleted file mode 100644 (file)
index 4ff5e96..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.ComponentModel;
-using Tizen.NUI;
-using Tizen.NUI.Scene3D;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class JointTransformer
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public JointTransformer() 
-        {
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Init(Animation animation)
-        {
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Play(IAnimationModuleData data)
-        {
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Pause()
-        {
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Stop()
-        {
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public void Destroy()
-        {
-        }
-
-        private void SetJointMotion(AvatarProperties properties, JointType jointType, MotionTransformIndex.TransformTypes type, Rotation rotation)
-        {
-            var motionTransform = new AvatarJointTransformIndex(properties.JointMapper, jointType, type);
-            var motionData = new MotionData();
-            motionData.Add(
-                motionTransform,
-                new MotionValue()
-                {
-                    //TODO : Tizen_7.0에 pitch, yaw, roll patch 추가하기
-                    //PropertyValue = new PropertyValue(new Rotation(new Radian(pitch), new Radian(yaw), new Radian(roll))),
-                }
-            );
-            //avatar.SetMotionData(motionData);
-        }
-
-        private void SetJointMotion(string keyValue, float pitch, float yaw, float roll)
-        {
-            var motionData = new MotionData();
-            motionData.Add(
-                new MotionTransformIndex()
-                {
-                    ModelNodeId = new PropertyKey(keyValue),
-                    TransformType = MotionTransformIndex.TransformTypes.Orientation,
-                },
-                new MotionValue()
-                {
-                    //TODO : Tizen_7.0에 pitch, yaw, roll patch 추가하기
-                    //PropertyValue = new PropertyValue(new Rotation(new Radian(pitch), new Radian(yaw), new Radian(roll))),
-                }
-            );
-            //avatar.SetMotionData(motionData);
-        }
-
-        private void SetJointMotion(string keyValue, MotionTransformIndex.TransformTypes type, Rotation rotation)
-        {
-            var motionData = new MotionData();
-            motionData.Add(
-                new MotionTransformIndex()
-                {
-                    ModelNodeId = new PropertyKey(keyValue),
-                    TransformType = type,
-                },
-                new MotionValue()
-                {
-                    PropertyValue = new PropertyValue(rotation),
-                }
-            );
-            //avatar.SetMotionData(motionData);
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Avatar.cs b/src/Tizen.AIAvatar/src/public/Avatar/Avatar.cs
deleted file mode 100644 (file)
index f1ffab8..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.ComponentModel;
-using Tizen.NUI.Scene3D;
-using Tizen.NUI;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    /// <summary>
-    /// Avatar is a Class to show 3D avatar objects.
-    /// It is subclass of Model s.t. we can control Avatar like models animation easly.
-    /// For example, 
-    /// 
-    /// Avatar supports AR Emoji.
-    /// </summary>
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public class Avatar : Model
-    {
-        private AvatarProperties avatarProperties = new DefaultAvatarProperties();
-        private AvatarMotions avatarMotions;
-
-        private BlendShapePlayer avatarAnimator;
-        private MotionPlayer motionPlayer;
-        private JointTransformer jointTransformer;
-
-        /// <summary>
-        /// Create an initialized AvatarModel.
-        /// </summary>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Avatar() : base()
-        {
-            InitAvatar();
-        }
-
-        /// <summary>
-        /// Create an initialized AREmojiDefaultAvatar.
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Avatar(AvatarInfo avatarInfo) : base(avatarInfo.ResourcePath)
-        {
-            InitAvatar();
-        }
-
-        /// <summary>
-        /// Create an initialized Avatar.
-        /// </summary>
-        /// <param name="avatarlUrl">avatar file url.(e.g. glTF, and DLI).</param>
-        /// <param name="resourceDirectoryUrl"> The url to derectory containing resources: binary, image etc.</param>
-        /// <remarks>
-        /// If resourceDirectoryUrl is empty, the parent directory url of avatarUrl is used for resource url.
-        ///
-        /// http://tizen.org/privilege/mediastorage for local files in media storage.
-        /// http://tizen.org/privilege/externalstorage for local files in external storage.
-        /// </remarks>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Avatar(string avatarUrl, string resourceDirectoryUrl = "") : base(avatarUrl, resourceDirectoryUrl)
-        {
-            InitAvatar();
-        }
-
-        /// <summary>
-        /// Copy constructor.
-        /// </summary>
-        /// <param name="avatar">Source object to copy.</param>
-        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public Avatar(Avatar avatar) : base(avatar)
-        {
-            InitAvatar();
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarProperties AvatarProperties
-        {
-            get => avatarProperties;
-            set => avatarProperties = value;
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal BlendShapePlayer AvatarAnimator
-        {
-            get => avatarAnimator; 
-            set => avatarAnimator = value;
-        }
-
-
-        /// <summary>
-        /// Play avatar animation by AnimationInfo
-        /// </summary>
-        /// <param name="index">index of default avatar animations</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        private void PlayAnimation(AnimationInfo animationInfo, int duration = 3000, bool isLooping = false, int loopCount = 1)
-        {
-            if (animationInfo == null)
-            {
-                Tizen.Log.Error(LogTag, "animationInfo is null");
-                return;
-            }
-            if (animationInfo.MotionData == null)
-            {
-                Tizen.Log.Error(LogTag, "animationInfo.MotionData is null");
-                return;
-            }
-            var motionAnimation = GenerateMotionDataAnimation(animationInfo.MotionData);
-            if (motionAnimation != null)
-            {
-                motionAnimation.Duration = duration;
-                motionAnimation.Looping = isLooping;
-                motionAnimation.LoopCount = loopCount;
-                motionAnimation.BlendPoint = 0.2f;
-                motionPlayer.PlayAnimation(motionAnimation);
-            }
-            else
-            {
-                Tizen.Log.Error(LogTag, "motionAnimation is null");
-            }
-        }
-
-        /// <summary>
-        /// Play avatar animation by MotionData
-        /// </summary>
-        /// <param name="index">index of default avatar animations</param>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        private void PlayAnimation(MotionData motionData, int duration = 3000, bool isLooping = false, int loopCount = 1)
-        {
-
-            if (motionData == null)
-            {
-                Tizen.Log.Error(LogTag, "motionData is null");
-                return;
-            }
-            var motionAnimation = GenerateMotionDataAnimation(motionData);
-            if (motionAnimation != null)
-            {
-                motionAnimation.Duration = duration;
-                motionAnimation.Looping = isLooping;
-                motionAnimation.LoopCount = loopCount;
-                motionAnimation.BlendPoint = 0.2f;
-                motionPlayer.PlayAnimation(motionAnimation);
-            }
-            else
-            {
-                Tizen.Log.Error(LogTag, "motionAnimation is null");
-            }
-        }
-
-        private void PlayAnimation(int index, int duration = 3000, bool isLooping = false, int loopCount = 1)
-        {
-            //TODO by index
-            //var motionAnimation = GenerateMotionDataAnimation(animationInfoList[index].MotionData);
-            /*motionAnimation.Duration = duration;
-            motionAnimation.Looping = isLooping;
-            motionAnimation.LoopCount = loopCount;
-            motionAnimation.BlendPoint = 0.2f;
-
-            motionPlayer.PlayAnimation(motionAnimation);*/
-        }
-
-        /// <summary>
-        /// Pause avatar animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        private void PauseMotionAnimation()
-        {
-            motionPlayer.PauseMotionAnimation();
-        }
-
-        /// <summary>
-        /// Stop avatar animation
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        private void StopMotionAnimation()
-        {
-            motionPlayer?.StopMotionAnimation();
-        }
-
-
-        private void InitAvatar()
-        {
-            avatarMotions = new AvatarMotions(avatarProperties);
-
-            AvatarAnimator = new BlendShapePlayer();
-
-            avatarProperties.AvatarPropertiesChanged += (s, e) =>
-            {
-                var eyeAnimation = CreateMotionAnimation(avatarMotions.EyeMotionData);
-                if (eyeAnimation != null)
-                {
-                    avatarAnimator.SetBlinkAnimation(eyeAnimation);
-                }
-            };
-
-            ResourcesLoaded += (s, e) =>
-            {
-                var eyeAnimation = CreateMotionAnimation(avatarMotions.EyeMotionData);
-                if (eyeAnimation != null)
-                {
-                    avatarAnimator.SetBlinkAnimation(eyeAnimation);
-                }
-            };
-        }
-
-        private Animation CreateMotionAnimation(MotionData data)
-        {
-            return GenerateMotionDataAnimation(data);
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarMic.cs b/src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarMic.cs
deleted file mode 100644 (file)
index 8c8e8ba..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.ComponentModel;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class AvatarMic
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal AvatarMic()
-        {
-        }
-
-        /// <summary>
-        /// Initialize Microphone for using live lip sync
-        /// </summary>
-        /// <privilege>http://tizen.org/privilege/recorder</privilege>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool InitAvatarMic(Avatar avatar)
-        {
-            if (AudioRecorder.Instance == null)
-            {
-                Log.Error(LogTag, $"Failed to initialize AudioRecorder");
-                return false;
-            }
-            AudioRecorder.Instance?.InitMic(avatar?.AvatarAnimator);
-
-            return true;
-        }
-
-        /// <summary>
-        /// Deinitialize Microphone
-        /// </summary>
-        /// <privilege>http://tizen.org/privilege/recorder</privilege>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool DeinitAvatarMIC()
-        {
-            AudioRecorder.Instance?.DeinitMic();
-
-            return true;
-        }
-
-        /// <summary>
-        /// Start synchronization avatar lip based on the microphone's voice
-        /// </summary>
-        /// <privilege>http://tizen.org/privilege/recorder</privilege>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void StartMic()
-        {
-            AudioRecorder.Instance?.StartRecording();
-        }
-
-        /// <summary>
-        /// Stop microphone
-        /// </summary>
-        /// <privilege>http://tizen.org/privilege/recorder</privilege>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void StopMic()
-        {
-            AudioRecorder.Instance?.StopRecording();
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarTTS.cs b/src/Tizen.AIAvatar/src/public/Avatar/Controller/AvatarTTS.cs
deleted file mode 100644 (file)
index 6c5954d..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * 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.Generic;
-using System.ComponentModel;
-using System;
-using Tizen.Uix.Tts;
-
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    internal class AvatarTTS
-    {
-        private TTSLipSyncer ttsLipSyncer;
-
-        private event EventHandler ttsReadyFinished;
-        private readonly Object ttsReadyFinishedLock = new Object();
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal AvatarTTS()
-        {
-        }
-
-        /// <summary>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal event EventHandler TTSReadyFinished
-        {
-            add
-            {
-                lock (ttsReadyFinishedLock)
-                {
-                    ttsReadyFinished += value;
-                }
-
-            }
-
-            remove
-            {
-                lock (ttsReadyFinishedLock)
-                {
-                    if (ttsReadyFinished == null)
-                    {
-                        Log.Error(LogTag, "ttsReadyFinished is null");
-                        return;
-                    }
-                    ttsReadyFinished -= value;
-                }
-            }
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal TtsClient CurrentTTSClient => ttsLipSyncer?.TtsHandle;
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void InitTTS(Avatar avatar)
-        {
-            if (ttsLipSyncer == null)
-            {
-                try
-                {
-                    ttsLipSyncer = new TTSLipSyncer(avatar?.AvatarAnimator?.LipSyncer);
-                }
-                catch (Exception e)
-                {
-                    Log.Error(LogTag, $"error :{e.Message}");
-                    Log.Error(LogTag, $"{e.StackTrace}");
-                }
-            }
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void DeinitTTS()
-        {
-            if (ttsLipSyncer != null)
-            {
-                try
-                {
-                    ttsLipSyncer.DeinitTts();
-                    ttsLipSyncer = null;
-                }
-                catch (Exception e)
-                {
-                    Log.Error(LogTag, $"error :{e.Message}");
-                    Log.Error(LogTag, $"{e.StackTrace}");
-                }
-            }
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal List<VoiceInfo> GetSupportedVoices()
-        {
-            return ttsLipSyncer.GetSupportedVoices();
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PrepareTTS(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-
-            if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
-            {
-                Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Info(LogTag, "TTS is not ready");
-                return false;
-            }
-
-            try
-            {
-                ttsLipSyncer.AddText(text, voiceInfo);
-                ttsLipSyncer.Prepare(ttsReadyFinishedCallback);
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PrepareTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-            if (!ttsLipSyncer.IsSupportedVoice(lang))
-            {
-                Log.Error(LogTag, $"{lang} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Error(LogTag, "TTS is not ready");
-                return false;
-            }
-            try
-            {
-                ttsLipSyncer.AddText(text, lang);
-                ttsLipSyncer.Prepare(ttsReadyFinishedCallback);
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PlayPreparedTTS()
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-            return ttsLipSyncer.PlayPreparedText();
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PlayTTS(string text, VoiceInfo voiceInfo)
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-
-            if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
-            {
-                Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Info(LogTag, "TTS is not ready");
-                return false;
-            }
-
-            try
-            {
-                ttsLipSyncer.AddText(text, voiceInfo);
-                ttsLipSyncer.Play();
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PlayTTS(string text, string lang = "ko_KR")
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-            if (!ttsLipSyncer.IsSupportedVoice(lang))
-            {
-                Log.Error(LogTag, $"{lang} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Error(LogTag, "TTS is not ready");
-                return false;
-            }
-            try
-            {
-                ttsLipSyncer.AddText(text, lang);
-                ttsLipSyncer.Play();
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-        
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PlayTTSAsync(string text, VoiceInfo voiceInfo, EventHandler ttsReadyFinishedCallback = null)
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-
-            if (!ttsLipSyncer.IsSupportedVoice(voiceInfo))
-            {
-                Log.Info(LogTag, $"{voiceInfo.Lang} & {voiceInfo.Type} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Info(LogTag, "TTS is not ready");
-                return false;
-            }
-
-            try
-            {
-                ttsLipSyncer.AddText(text, voiceInfo);
-                ttsLipSyncer.PlayAsync(ttsReadyFinishedCallback);
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal bool PlayTTS(string text, string lang = "ko_KR", EventHandler ttsReadyFinishedCallback = null)
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return false;
-            }
-
-            if (!ttsLipSyncer.IsSupportedVoice(lang))
-            {
-                Log.Error(LogTag, $"{lang} is not supported");
-                return false;
-            }
-
-            Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-            if (ttsLipSyncer.TtsHandle.CurrentState != Tizen.Uix.Tts.State.Ready)
-            {
-                Log.Error(LogTag, "TTS is not ready");
-                return false;
-            }
-            try
-            {
-                ttsLipSyncer.AddText(text, lang);
-                ttsLipSyncer.PlayAsync(ttsReadyFinishedCallback);
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-                return false;
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void PauseTTS()
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return;
-            }
-
-            try
-            {
-                Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-                ttsLipSyncer?.Pause();
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-            }
-        }
-
-        /// <summary>
-        /// <param name=""></param>
-        /// </summary>
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal void StopTTS()
-        {
-            if (ttsLipSyncer == null || ttsLipSyncer.TtsHandle == null)
-            {
-                Log.Error(LogTag, "tts is null");
-                return;
-            }
-
-            try
-            {
-                Log.Info(LogTag, "Current TTS State :" + ttsLipSyncer.TtsHandle.CurrentState);
-                ttsLipSyncer?.Stop();
-            }
-            catch (Exception e)
-            {
-                Log.Error(LogTag, $"error :{e.Message}");
-                Log.Error(LogTag, $"{e.StackTrace}");
-            }
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Info/AvatarInfo.cs b/src/Tizen.AIAvatar/src/public/Avatar/Info/AvatarInfo.cs
deleted file mode 100644 (file)
index 7f7b95e..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-using static Tizen.AIAvatar.AIAvatar;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public class AvatarInfo
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public string Name { get; private set; }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public string ThumbnailPath { get; private set; }
-        
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal string ResourcePath { get; private set; }
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        internal AvatarInfoOption avatarInfoOption { get; private set; }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarInfo(string path, string Name, AvatarInfoOption info = AvatarInfoOption.Thumbnail)
-        {
-            this.Name = Name;
-            this.avatarInfoOption = info;
-
-            if (info == AvatarInfoOption.Thumbnail)
-            {
-                ThumbnailPath = path;
-            }
-            else
-            {
-                ResourcePath = path;
-            }
-        }
-
-        internal AvatarInfo(string directoryPath)
-        {
-            string path = ApplicationResourcePath + EmojiAvatarResourcePath;
-            Name = directoryPath.Substring(path.Length, directoryPath.Length - path.Length);
-            ResourcePath = $"{directoryPath}/{AIAvatar.ExternalModel}";
-        }
-    }
-
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public enum AvatarInfoOption
-    {
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        Thumbnail = 0,
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        Resource = 1,
-    }
-}
diff --git a/src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarProperties.cs b/src/Tizen.AIAvatar/src/public/Avatar/Properties/AvatarProperties.cs
deleted file mode 100644 (file)
index a6b8612..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright(c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.ComponentModel;
-
-namespace Tizen.AIAvatar
-{
-    [EditorBrowsable(EditorBrowsableState.Never)]
-    public class AvatarProperties
-    {
-        private AvatarPropertyMapper jointMapper;
-        private AvatarPropertyMapper blendShapeMapper;
-        private AvatarPropertyMapper nodeMapper;
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarProperties(AvatarPropertyMapper jointMapper, AvatarPropertyMapper blendShapeMapper, AvatarPropertyMapper nodeMapper)
-        {
-            JointMapper = new AvatarPropertyMapper(jointMapper);
-            BlendShapeMapper = new AvatarPropertyMapper(blendShapeMapper);
-            NodeMapper = new AvatarPropertyMapper(nodeMapper);
-        }
-
-        internal event EventHandler<AvatarProperties> AvatarPropertiesChanged;
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarPropertyMapper JointMapper
-        {
-            get
-            {
-                return jointMapper;
-            }
-            set
-            {
-                jointMapper = value;
-                AvatarPropertiesChanged?.Invoke(jointMapper, this);
-            }
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarPropertyMapper BlendShapeMapper
-        {
-            get
-            {
-                return blendShapeMapper;
-            }
-            set
-            {
-                blendShapeMapper = value;
-                AvatarPropertiesChanged?.Invoke(blendShapeMapper, this);
-            }
-        }
-
-        [EditorBrowsable(EditorBrowsableState.Never)]
-        public AvatarPropertyMapper NodeMapper
-        {
-            get
-            {
-                return nodeMapper;
-            }
-            set
-            {
-                nodeMapper = value;
-                AvatarPropertiesChanged?.Invoke(nodeMapper, this);
-            }
-        }
-    }
-}
diff --git a/src/Tizen.AIAvatar/test/Test.cs b/src/Tizen.AIAvatar/test/Test.cs
new file mode 100644 (file)
index 0000000..e8f0e11
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.AIAvatar
+{
+    internal class Test
+    {
+        public void CreateTest()
+        {
+            var avatar1 = new Avatar();
+
+            var avatarInfo = new AvatarInfo("avatarName", "resourcePath");
+            var avatar2 = new Avatar(avatarInfo);
+
+            var avatar3 = new Avatar("resourcePath");
+
+            var avatar4 = new Avatar(avatar1);
+
+            avatar1.Dispose();
+            avatar2.Dispose();
+            avatar3.Dispose();
+            avatar4.Dispose();
+        }
+    }
+}
index 7bdb603..9f9551b 100755 (executable)
@@ -690,15 +690,9 @@ namespace Tizen.NUI
             ProcessorController.Instance.Initialize();
             Tizen.Tracer.End();
 
-#if REMOVE_READONLY_FOR_BINDABLE_PROPERTY
-            if(NUIApplication.IsUsingXaml)
-            {
-                Tizen.NUI.BaseComponents.View.CreateBindableProperties();
-            }
-#endif
             Log.Info("NUI", "[NUI] OnApplicationInit: GetWindow");
             Tizen.Tracer.Begin("[NUI] OnApplicationInit: GetWindow");
-            Window.Instance = GetWindow();
+            Window.Instance = Window.Default = GetWindow();
 
 #if !PROFILE_TV
             //tv profile never use default focus indicator, so this is not needed!
index 031ea14..cc872b6 100755 (executable)
@@ -82,13 +82,13 @@ namespace Tizen.NUI
                     var target = weakReference.Target;
 
                     BaseHandle ret = target as BaseHandle;
-                    if ((ret == null) || ret.Disposed || ret.IsDisposeQueued)
+                    // Note : Do not use ret == null because BaseHandle override operator ==.
+                    if ((ret?.Disposed ?? true) || (ret?.IsDisposeQueued ?? true))
                     {
-                        // Special case. If WeakReference.Target is null or it might be disposed by GC, just remove previous item forcibly.
-                        if (Instance._controlMap.TryRemove(refCptr, out weakReference) != true)
-                        {
-                            Tizen.Log.Error("NUI", $"Something Wrong when we try to remove null target! input type:{baseHandle.GetType()}, registed type:{target?.GetType()}\n");
-                        }
+                        // Special case. If WeakReference.Target is null or disposed by GC,
+                        // Unregister forcibly first. and then try to add again.
+                        ret?.UnregisterFromRegistry();
+
                         if (Instance._controlMap.TryAdd(refCptr, new WeakReference(baseHandle, true)) != true)
                         {
                             Tizen.Log.Error("NUI", $"Something Wrong when we try to replace null target! input type:{baseHandle.GetType()}, registed type:{target?.GetType()}\n");
@@ -120,7 +120,7 @@ namespace Tizen.NUI
                 }
             }
 
-            NUILog.Debug($"[Registry] Register! type:{baseHandle.GetType()} count:{Instance._controlMap.Count} copyNativeHandle:{baseHandle.GetBaseHandleCPtrHandleRef.Handle.ToString("X8")}");
+            NUILog.Debug($"[Registry] Register! type:{baseHandle.GetType()} count:{Instance._controlMap.Count} refCptr=0x{refCptr.ToInt64():X} NativeHandle:{baseHandle.GetBaseHandleCPtrHandleRef.Handle.ToString("X8")}");
             return true;
         }
 
@@ -139,7 +139,7 @@ namespace Tizen.NUI
                 Tizen.Log.Error("NUI", $"something wrong when removing refCptr!\n");
             }
 
-            NUILog.Debug($"[Registry] Unregister! type:{baseHandle.GetType()} count:{Instance._controlMap.Count} copyNativeHandle:{baseHandle.GetBaseHandleCPtrHandleRef.Handle.ToString("X8")}");
+            NUILog.Debug($"[Registry] Unregister! type:{baseHandle.GetType()} count:{Instance._controlMap.Count} refCptr=0x{refCptr.ToInt64():X} NativeHandle:{baseHandle.GetBaseHandleCPtrHandleRef.Handle.ToString("X8")}");
             return;
         }
 
@@ -164,7 +164,7 @@ namespace Tizen.NUI
             if (refObjectPtr == global::System.IntPtr.Zero)
             {
                 NUILog.Debug("Registry refObjectPtr is NULL! This means bind native object is NULL!");
-                //return null;
+                return null;
             }
             else
             {
@@ -184,9 +184,12 @@ namespace Tizen.NUI
                 }
 
                 BaseHandle ret = weakReference.Target as BaseHandle;
-                if ((ret == null) || ret.Disposed || ret.IsDisposeQueued)
+                // Note : Do not use ret == null because BaseHandle override operator ==.
+                if ((ret?.Disposed ?? true) || (ret?.IsDisposeQueued ?? true))
                 {
-                    // Special case. If WeakReference.Target is null or disposed by GC, just return null.
+                    // Special case. If WeakReference.Target is null or disposed by GC,
+                    // Unregister first. and then return null.
+                    ret?.UnregisterFromRegistry();
                     return null;
                 }
                 return ret;
diff --git a/src/Tizen.NUI/src/internal/Common/VisualObjectsContainer.cs b/src/Tizen.NUI/src/internal/Common/VisualObjectsContainer.cs
new file mode 100644 (file)
index 0000000..767b2eb
--- /dev/null
@@ -0,0 +1,191 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Text;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// VisualObjectsContainer is a container for visual objects.
+    /// For each VisualObjectContainer, there is a corresponding view.
+    /// Each view can have only one VisualObjectsContainer per rangeType.
+    /// </summary>
+    /// <remarks>
+    /// To avoid the collision between Dali toolkit logic and NUI specific policy,
+    /// this container has an internal limitation of the number of visual objects.
+    /// If user try to add visual object over the limitation, it will be ignored.
+    /// </remarks>
+    internal class VisualObjectsContainer : BaseHandle
+    {
+        private List<Tizen.NUI.Visuals.VisualBase> visuals = new List<Tizen.NUI.Visuals.VisualBase>(); // Keep visual object reference.
+
+        /// <summary>
+        /// Creates an empty visual object handle.
+        /// </summary>
+        public VisualObjectsContainer() : this(Interop.VisualObjectsContainer.NewVisualObjectsContainer(), true, false)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Creates an visual object with VisualObjectsContainer.
+        /// </summary>
+        public VisualObjectsContainer(Tizen.NUI.BaseComponents.View view, int rangeType) : this(Interop.VisualObjectsContainer.VisualObjectsContainerNew(Tizen.NUI.BaseComponents.View.getCPtr(view), rangeType), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal VisualObjectsContainer(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal VisualObjectsContainer(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+        }
+
+        public Tizen.NUI.BaseComponents.View GetView()
+        {
+            global::System.IntPtr cPtr = Interop.VisualObjectsContainer.GetOwner(SwigCPtr);
+            
+            Tizen.NUI.BaseComponents.View ret = null;
+            if (Interop.RefObject.GetRefObjectPtr(cPtr) == global::System.IntPtr.Zero)
+            {
+                // Visual container don't have owner. Return null.
+                Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+            }
+            else
+            {
+                ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Tizen.NUI.BaseComponents.View;
+                if (ret != null)
+                {
+                    Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+                }
+                else
+                {
+                    ret = new Tizen.NUI.BaseComponents.View(cPtr, true);
+                }
+            }
+            NDalicPINVOKE.ThrowExceptionIfExists();
+            return ret;
+        }
+
+        public int GetContainerRangeType()
+        {
+            return Interop.VisualObjectsContainer.GetContainerRangeType(SwigCPtr);
+        }
+
+        public Tizen.NUI.Visuals.VisualBase this[uint index]
+        {
+            get
+            {
+                return GetVisualObjectAt(index);
+            }
+        }
+
+        public uint GetVisualObjectsCount()
+        {
+            uint ret = Interop.VisualObjectsContainer.GetVisualObjectsCount(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+            return ret;
+        }
+
+        public bool AddVisualObject(Tizen.NUI.Visuals.VisualBase visualObject)
+        {
+            // Detach from previous container first.
+            var previousContainer = visualObject.GetVisualContainer();
+            if (previousContainer != null)
+            {
+                if (previousContainer == this)
+                {
+                    // Already added to this container.
+                    return false;
+                }
+                visualObject.Detach();
+            }
+
+            visuals.Add(visualObject);
+
+            bool ret = Interop.VisualObjectsContainer.AddVisualObject(SwigCPtr, Tizen.NUI.Visuals.VisualBase.getCPtr(visualObject));
+            NDalicPINVOKE.ThrowExceptionIfExists();
+            return ret;
+        }
+
+        public void RemoveVisualObject(Tizen.NUI.Visuals.VisualBase visualObject)
+        {
+            visuals.Remove(visualObject);
+
+            Interop.VisualObjectsContainer.RemoveVisualObject(SwigCPtr, Tizen.NUI.Visuals.VisualBase.getCPtr(visualObject));
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        public Tizen.NUI.Visuals.VisualBase FindVisualObjectByName(string name)
+        {
+            Tizen.NUI.Visuals.VisualBase ret = null;
+            if(!string.IsNullOrEmpty(name))
+            {
+                foreach (var visual in visuals)
+                {
+                    if (visual?.Name == name)
+                    {
+                        return visual;
+                    }
+                }
+            }
+            return ret;
+        }
+
+        private Tizen.NUI.Visuals.VisualBase GetVisualObjectAt(uint index)
+        {
+            global::System.IntPtr cPtr = Interop.VisualObjectsContainer.GetVisualObjectAt(SwigCPtr, index);
+            Visuals.VisualBase ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Visuals.VisualBase;
+            if (ret != null)
+            {
+                Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+            }
+            else
+            {
+                ret = new Visuals.VisualBase(cPtr, true);
+            }
+            NDalicPINVOKE.ThrowExceptionIfExists();
+            return ret;
+        }
+
+        /// <summary>
+        /// Dispose for VisualObjectsContainer
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                //Called by User
+                //Release your own managed resources here.
+                //You should release all of your own disposable objects here.
+            }
+
+            base.Dispose(type);
+        }
+    }
+}
\ No newline at end of file
index 7b603b3..2dfba5a 100755 (executable)
@@ -197,6 +197,10 @@ namespace Tizen.NUI
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_InputMethodContext_SetInputPanelPosition")]
             public static extern void SetInputPanelPosition(global::System.Runtime.InteropServices.HandleRef jarg1, uint jarg2, uint jarg3);
 
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_InputMethodContext_SetInputPanelPositionAlign")]
+            [return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.U1)]
+            public static extern bool SetInputPanelPositionAlign(global::System.Runtime.InteropServices.HandleRef inputMethodContext, int x, int y, int align);
+
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_InputMethodContext_ActivatedSignal")]
             public static extern global::System.IntPtr ActivatedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
 
diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.VisualObject.cs b/src/Tizen.NUI/src/internal/Interop/Interop.VisualObject.cs
new file mode 100755 (executable)
index 0000000..95922aa
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.NUI
+{
+    internal static partial class Interop
+    {
+        internal static partial class VisualObject
+        {
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_New")]
+            public static extern global::System.IntPtr VisualObjectNew();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_GetContainer")]
+            public static extern global::System.IntPtr GetContainer(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_CreateVisual")]
+            public static extern void CreateVisual(global::System.Runtime.InteropServices.HandleRef visualObject, global::System.Runtime.InteropServices.HandleRef propertyMap);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_RetrieveVisualPropertyMap")]
+            public static extern void RetrieveVisualPropertyMap(global::System.Runtime.InteropServices.HandleRef visualObject, global::System.Runtime.InteropServices.HandleRef propertyMap);
+
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_DoAction_UpdatePropertyMap")]
+            public static extern void UpdateVisualPropertyMap(global::System.Runtime.InteropServices.HandleRef visualObject, global::System.Runtime.InteropServices.HandleRef propertyMap);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_DoActionWithEmptyAttributes")]
+            public static extern void DoActionWithEmptyAttributes(global::System.Runtime.InteropServices.HandleRef visualObject, int actionId);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_DoActionWithSingleIntAttributes")]
+            public static extern void DoActionWithSingleIntAttributes(global::System.Runtime.InteropServices.HandleRef visualObject, int actionId, int actionValue);
+
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_SetSiblingOrder")]
+            public static extern void SetSiblingOrder(global::System.Runtime.InteropServices.HandleRef visualObject, uint siblingOrder);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_GetSiblingOrder")]
+            public static extern uint GetSiblingOrder(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_DetachFromContainer")]
+            public static extern void Detach(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_Raise")]
+            public static extern uint Raise(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_Lower")]
+            public static extern uint Lower(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_RaiseToTop")]
+            public static extern uint RaiseToTop(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_LowerToBottom")]
+            public static extern uint LowerToBottom(global::System.Runtime.InteropServices.HandleRef visualObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_RaiseAbove")]
+            public static extern uint RaiseAbove(global::System.Runtime.InteropServices.HandleRef visualObject, global::System.Runtime.InteropServices.HandleRef target);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObject_LowerBelow")]
+            public static extern uint LowerBelow(global::System.Runtime.InteropServices.HandleRef visualObject, global::System.Runtime.InteropServices.HandleRef target);
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.VisualObjectsContainer.cs b/src/Tizen.NUI/src/internal/Interop/Interop.VisualObjectsContainer.cs
new file mode 100755 (executable)
index 0000000..865b4e6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.NUI
+{
+    internal static partial class Interop
+    {
+        internal static partial class VisualObjectsContainer
+        {
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_ContainerRangeTypeBackgroundEffectGet")]
+            public static extern int ContainerRangeTypeBackgroundEffectGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_ContainerRangeTypeBackgroundGet")]
+            public static extern int ContainerRangeTypeBackgroundGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_ContainerRangeTypeContentGet")]
+            public static extern int ContainerRangeTypeContentGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_ContainerRangeTypeDecorationGet")]
+            public static extern int ContainerRangeTypeDecorationGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_ContainerRangeTypeForegroundEffectGet")]
+            public static extern int ContainerRangeTypeForegroundEffectGet();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_VisualObjectsContainer__SWIG_0")]
+            public static extern global::System.IntPtr NewVisualObjectsContainer();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_VisualObjectsContainer")]
+            public static extern void DeleteVisualObjectsContainer(global::System.Runtime.InteropServices.HandleRef container);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_New")]
+            public static extern global::System.IntPtr VisualObjectsContainerNew(global::System.Runtime.InteropServices.HandleRef view, int rangeType);
+
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_GetOwner")]
+            public static extern global::System.IntPtr GetOwner(global::System.Runtime.InteropServices.HandleRef container);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_GetContainerRangeType")]
+            public static extern int GetContainerRangeType(global::System.Runtime.InteropServices.HandleRef container);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_GetVisualObjectsCount")]
+            public static extern uint GetVisualObjectsCount(global::System.Runtime.InteropServices.HandleRef container);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_GetVisualObjectAt")]
+            public static extern global::System.IntPtr GetVisualObjectAt(global::System.Runtime.InteropServices.HandleRef container, uint index);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_AddVisualObject")]
+            [return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.U1)]
+            public static extern bool AddVisualObject(global::System.Runtime.InteropServices.HandleRef container, global::System.Runtime.InteropServices.HandleRef viewObject);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_VisualObjectsContainer_RemoveVisualObject")]
+            public static extern void RemoveVisualObject(global::System.Runtime.InteropServices.HandleRef container, global::System.Runtime.InteropServices.HandleRef viewObject);
+        }
+    }
+}
index 3d9188a..dc18df2 100755 (executable)
@@ -2216,43 +2216,6 @@ namespace Tizen.NUI.BaseComponents
             backgroundExtraDataUpdatedFlag &= ~BackgroundExtraDataUpdatedFlag.ContentsCornerRadius;
             backgroundExtraDataUpdatedFlag &= ~BackgroundExtraDataUpdatedFlag.ContentsBorderline;
 
-            // Do Fitting Buffer when desired dimension is set
-            // TODO : Couldn't we do this job in dali-engine side.
-            if (_desired_width != -1 && _desired_height != -1)
-            {
-                if (!string.IsNullOrEmpty(_resourceUrl))
-                {
-                    Size2D imageSize = ImageLoader.GetOriginalImageSize(_resourceUrl, true);
-                    if (imageSize.Height > 0 && imageSize.Width > 0 && _desired_width > 0 && _desired_height > 0)
-                    {
-                        int adjustedDesiredWidth, adjustedDesiredHeight;
-                        float aspectOfDesiredSize = (float)_desired_height / (float)_desired_width;
-                        float aspectOfImageSize = (float)imageSize.Height / (float)imageSize.Width;
-                        if (aspectOfImageSize > aspectOfDesiredSize)
-                        {
-                            adjustedDesiredWidth = _desired_width;
-                            adjustedDesiredHeight = imageSize.Height * _desired_width / imageSize.Width;
-                        }
-                        else
-                        {
-                            adjustedDesiredWidth = imageSize.Width * _desired_height / imageSize.Height;
-                            adjustedDesiredHeight = _desired_height;
-                        }
-
-                        PropertyValue returnWidth = new PropertyValue(adjustedDesiredWidth);
-                        cachedImagePropertyMap[ImageVisualProperty.DesiredWidth] = returnWidth;
-                        returnWidth?.Dispose();
-                        PropertyValue returnHeight = new PropertyValue(adjustedDesiredHeight);
-                        cachedImagePropertyMap[ImageVisualProperty.DesiredHeight] = returnHeight;
-                        returnHeight?.Dispose();
-                        PropertyValue scaleToFit = new PropertyValue((int)FittingModeType.ScaleToFill);
-                        cachedImagePropertyMap[ImageVisualProperty.FittingMode] = scaleToFit;
-                        scaleToFit?.Dispose();
-                    }
-                    imageSize?.Dispose();
-                }
-            }
-
             UpdateImageMap();
         }
 
index c62e37d..41f5c8f 100755 (executable)
@@ -96,11 +96,11 @@ namespace Tizen.NUI.BaseComponents
 
         static View()
         {
-#if REMOVE_READONLY_FOR_BINDABLE_PROPERTY
-            //to get "IsUsingXaml" feature working at preload, we need to remove readonly for BindableProperty.
-#else
             if (NUIApplication.IsUsingXaml)
             {
+#if REMOVE_READONLY_FOR_BINDABLE_PROPERTY
+                CreateBindableProperties();
+#else
                 StyleNameProperty = BindableProperty.Create(nameof(StyleName), typeof(string), typeof(View), string.Empty,
                     propertyChanged: SetInternalStyleNameProperty, defaultValueCreator: GetInternalStyleNameProperty);
 
@@ -446,23 +446,14 @@ namespace Tizen.NUI.BaseComponents
                 RegisterPropertyGroup(ScaleXProperty, scalePropertyGroup);
                 RegisterPropertyGroup(ScaleYProperty, scalePropertyGroup);
                 RegisterPropertyGroup(ScaleZProperty, scalePropertyGroup);
-            }
 #endif
+            }
             RegisterAccessibilityDelegate();
         }
 
         static internal new void Preload()
         {
             Container.Preload();
-
-            // not needed, at preload, APP can not set the "IsUsingXaml" flag, it have the default value at preload
-            // if (NUIApplication.IsUsingXaml)
-            // {
-            //     // Do nothing. Just call for load static values.
-            //     var temporalPositionPropertyGroup = positionPropertyGroup;
-            //     var temporalSizePropertyGroup = sizePropertyGroup;
-            //     var temporalScalePropertyGroup = scalePropertyGroup;
-            // }
         }
 
         /// <summary>
index 402bbe3..ad15c6c 100755 (executable)
@@ -3501,6 +3501,7 @@ namespace Tizen.NUI.BaseComponents
         // keep readonly for BindableProperty
 #endif
 
+
         /// <summary>
         /// Gets View's Size2D set by user.
         /// </summary>
index 206cc9a..ea3b605 100755 (executable)
@@ -1425,6 +1425,15 @@ namespace Tizen.NUI.BaseComponents
             internalCurrentScreenPosition?.Dispose();
             internalCurrentScreenPosition = null;
 
+            if (visualContainers != null)
+            {
+                foreach (var visualContainer in visualContainers)
+                {
+                    visualContainer?.Dispose();
+                }
+                visualContainers = null;
+            }
+
             if (type == DisposeTypes.Explicit)
             {
                 //Called by User
diff --git a/src/Tizen.NUI/src/public/BaseComponents/ViewVisuals.cs b/src/Tizen.NUI/src/public/BaseComponents/ViewVisuals.cs
new file mode 100755 (executable)
index 0000000..99ee999
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright(c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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.ComponentModel;
+using System.Runtime.InteropServices;
+using Tizen.NUI.Binding;
+
+namespace Tizen.NUI.BaseComponents
+{
+    /// <summary>
+    /// View is the base class for all views.
+    /// </summary>
+    /// <since_tizen> 3 </since_tizen>
+    public partial class View
+    {
+        #region Internal and Private
+        private List<Tizen.NUI.Visuals.VisualObjectsContainer> visualContainers = null;
+
+        /// <summary>
+        /// Range of visual for the container.
+        /// </summary>
+        internal struct ContainerRangeType
+        {
+            /// <summary>
+            /// Visual will be rendered under the shadow.
+            /// </summary>
+            internal static readonly int Shadow = Interop.VisualObjectsContainer.ContainerRangeTypeBackgroundEffectGet();
+
+            /// <summary>
+            /// Visual will be rendered under the background.
+            /// </summary>
+            internal static readonly int Background = Interop.VisualObjectsContainer.ContainerRangeTypeBackgroundGet();
+
+            /// <summary>
+            /// Visual will be rendered under the content.
+            /// It is default value.
+            /// </summary>
+            internal static readonly int Content = Interop.VisualObjectsContainer.ContainerRangeTypeContentGet();
+
+            /// <summary>
+            /// Visual will be rendered under the decoration.
+            /// </summary>
+            internal static readonly int Decoration = Interop.VisualObjectsContainer.ContainerRangeTypeDecorationGet();
+
+            /// <summary>
+            /// Visual will be rendered above the foreground effect.
+            /// </summary>
+            internal static readonly int ForegroundEffect = Interop.VisualObjectsContainer.ContainerRangeTypeForegroundEffectGet();
+        };
+        #endregion
+
+        #region Public Methods
+        /// <summary>
+        /// Add a Tizen.NUI.Visuals.VisualBase to the view.
+        /// </summary>
+        /// <remarks>
+        /// The visual is added to the top of the visuals.
+        /// If the container cannot add more than maxium count of visuals
+        /// or the visual is already added, It will be ignored.
+        ///
+        /// If input visual already added to another view,
+        /// visual will be detached from old view and added to this view.
+        /// </remarks>
+        /// <param name="visualBase">The visual to add.</param>
+        /// <returns>True if the visual was added successfully, false otherwise.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool AddVisual(Tizen.NUI.Visuals.VisualBase visualBase)
+        {
+            return AddVisualInternal(visualBase, ContainerRangeType.Content);
+        }
+
+        /// <summary>
+        /// Remove a Tizen.NUI.Visuals.VisualBase from the view.
+        /// </summary>
+        /// <remarks>
+        /// The <see cref="Tizen.NUI.Visuals.VisualBase.SiblingOrder"/> value of all other Visuals.VisualBases will be changed automatically.
+        /// </remarks>
+        /// <param name="visualBase">The visual to remove.</param>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RemoveVisual(Tizen.NUI.Visuals.VisualBase visualBase)
+        {
+            if (visualContainers != null)
+            {
+                foreach (var visualContainer in visualContainers)
+                {
+                    visualContainer?.RemoveVisualObject(visualBase);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Get a Tizen.NUI.Visuals.VisualBase by sibling index
+        /// </summary>
+        /// <returns>Get visual base by sibling index</returns>
+        /// <exception cref="InvalidOperationException"> Thrown when index is out of bounds. </exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.Visuals.VisualBase GetVisualAt(uint index)
+        {
+            return GetVisualAtInternal(index, ContainerRangeType.Content);
+        }
+
+        /// <summary>
+        /// Get total number of Tizen.NUI.Visuals.VisualBase which we added using <see cref="AddVisual"/>.
+        /// </summary>
+        /// <returns>Get the number of visual base.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public uint GetVisualsCount()
+        {
+            return GetVisualsCountInternal(ContainerRangeType.Content);
+        }
+
+        /// <summary>
+        /// Find Tizen.NUI.Visuals.VisualBase by name. Given name should not be empty.
+        /// </summary>
+        /// <returns>Get the visual base.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Visuals.VisualBase FindVisualByName(string name)
+        {
+            Visuals.VisualBase ret = null;
+            if (visualContainers != null)
+            {
+                foreach (var visualContainer in visualContainers)
+                {
+                    if (visualContainer != null)
+                    {
+                        ret = visualContainer.FindVisualObjectByName(name);
+                        if (ret != null)
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+            return ret;
+        }
+        #endregion
+
+        #region Internal Method
+        internal bool AddVisualInternal(Tizen.NUI.Visuals.VisualBase visualBase, int rangeType)
+        {
+            var visualContainer = EnsureVisualContainer(rangeType);
+            return visualContainer.AddVisualObject(visualBase);
+        }
+
+        internal Tizen.NUI.Visuals.VisualBase GetVisualAtInternal(uint index, int rangeType)
+        {
+            if (index >= GetVisualsCountInternal(rangeType))
+            {
+                throw new InvalidOperationException($"Index {index} is out of bounds. Bound is {GetVisualsCountInternal(rangeType)}");
+            }
+            var visualContainer = EnsureVisualContainer(rangeType);
+            return visualContainer[index];
+        }
+
+        internal uint GetVisualsCountInternal(int rangeType)
+        {
+            uint ret = 0;
+            if (visualContainers != null)
+            {
+                foreach (var visualContainer in visualContainers)
+                {
+                    if (visualContainer != null && visualContainer.GetContainerRangeType() == rangeType)
+                    {
+                        ret = visualContainer.GetVisualObjectsCount();
+                        break;
+                    }
+                }
+            }
+            return ret;
+        }
+
+        private Tizen.NUI.Visuals.VisualObjectsContainer EnsureVisualContainer(int rangeType)
+        {
+            if (visualContainers == null)
+            {
+                visualContainers = new List<Tizen.NUI.Visuals.VisualObjectsContainer>();
+            }
+
+            foreach (var visualContainer in visualContainers)
+            {
+                if (visualContainer != null && visualContainer.GetContainerRangeType() == rangeType)
+                {
+                    return visualContainer;
+                }
+            }
+
+            var newContainer = new Tizen.NUI.Visuals.VisualObjectsContainer(this, rangeType);
+            visualContainers.Add(newContainer);
+            return newContainer;
+        }
+        #endregion
+    }
+}
index 5dc953a..b37e30b 100755 (executable)
@@ -99,10 +99,7 @@ namespace Tizen.NUI
             }
             set
             {
-                using (PropertyKey pKey = new PropertyKey(key))
-                {
-                    SetValue(pKey, value);
-                }
+                SetValue(key, value);
             }
         }
 
@@ -121,10 +118,7 @@ namespace Tizen.NUI
             }
             set
             {
-                using (PropertyKey pKey = new PropertyKey(key))
-                {
-                    SetValue(pKey, value);
-                }
+                SetValue(key, value);
             }
         }
 
@@ -278,6 +272,17 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// Removes the element by the specified integer key.
+        /// </summary>
+        /// <param name="key">The index key to find.</param>
+        /// <returns>True if the element is removed, false otherwise.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Remove(int key)
+        {
+            return Interop.PropertyMap.Remove(SwigCPtr, key);
+        }
+
+        /// <summary>
         /// Determines whether the PropertyMap contains the specified key.
         /// </summary>
         /// <param name="key">The index key to find.</param>
@@ -416,7 +421,19 @@ namespace Tizen.NUI
             {
                 Interop.PropertyMap.SetValueStringKey(SwigCPtr, key.StringKey, PropertyValue.getCPtr(value));
             }
-            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal void SetValue(int key, PropertyValue value)
+        {
+            Interop.PropertyMap.SetValueIntKey(SwigCPtr, key, PropertyValue.getCPtr(value));
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal void SetValue(string key, PropertyValue value)
+        {
+            Interop.PropertyMap.SetValueStringKey(SwigCPtr, key, PropertyValue.getCPtr(value));
+            NDalicPINVOKE.ThrowExceptionIfExists();
         }
 
         /// This will not be public opened.
index 2d3ae18..c2ad8a3 100755 (executable)
@@ -189,7 +189,7 @@ namespace Tizen.NUI
                 DragType type = (DragType)Interop.DragAndDrop.GetAction(dragEvent);
                 DragEvent ev = new DragEvent();
                 global::System.IntPtr cPtr = Interop.DragAndDrop.GetPosition(dragEvent);
-                ev.Position = (cPtr == global::System.IntPtr.Zero) ? null : new Position(cPtr, false);
+                ev.Position = (cPtr == global::System.IntPtr.Zero) ? null : new Position(cPtr, true);
 
                 if (type == DragType.Enter)
                 {
index 05f825f..c188e30 100755 (executable)
@@ -377,6 +377,50 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// Enumeration for align of the input panel.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public enum InputPanelAlign
+        {
+            /// <summary>
+            /// The top-left corner.
+            /// </summary>
+            TopLeft,
+            /// <summary>
+            /// The top-center position.
+            /// </summary>
+            TopCenter,
+            /// <summary>
+            /// The top-right corner.
+            /// </summary>
+            TopRight,
+            /// <summary>
+            /// The middle-left position.
+            /// </summary>
+            MiddleLeft,
+            /// <summary>
+            /// The middle-center position.
+            /// </summary>
+            MiddleCenter,
+            /// <summary>
+            /// The middle-right position.
+            /// </summary>
+            MiddleRight,
+            /// <summary>
+            /// The bottom-left corner.
+            /// </summary>
+            BottomLeft,
+            /// <summary>
+            /// The bottom-center position.
+            /// </summary>
+            BottomCenter,
+            /// <summary>
+            /// The bottom-right corner.
+            /// </summary>
+            BottomRight
+        }
+
+        /// <summary>
         /// Gets or sets whether the IM context allows to use the text prediction.
         /// </summary>
         /// <since_tizen> 8 </since_tizen>
@@ -682,6 +726,26 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// Sets the alignment and its x,y coordinates of the input panel.<br/>
+        /// Regardless of the rotation degree, the x, y values of the top-left corner on the screen are based on 0, 0.<br/>
+        /// When the IME size is changed, its size will change according to the set alignment.
+        /// </summary>
+        /// <remarks>
+        /// This API can be used to set the alignment of a floating IME.
+        /// </remarks>
+        /// <param name="x">The x coordinate of the InputPanelAlign value.</param>
+        /// <param name="y">The y coordinate of the InputPanelAlign value.</param>
+        /// <param name="align">one of the InputPanelAlign values specifying the desired alignment.</param>
+        /// <returns>True on success, false otherwise.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool SetInputPanelPositionAlign(int x, int y, InputMethodContext.InputPanelAlign align)
+        {
+            bool ret = Interop.InputMethodContext.SetInputPanelPositionAlign(SwigCPtr, x, y, (int)align);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        /// <summary>
         /// Sets the language of the input panel.
         /// </summary>
         /// <param name="language">The language to be set to the input panel</param>
index c73700f..e80aa64 100755 (executable)
@@ -329,6 +329,11 @@ namespace Tizen.NUI
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
         FitWidth,
+        /// <summary>
+        /// The visual should not use fitting mode.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        DontCare,
     }
 
     /// <summary>
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/AdvancedTextVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/AdvancedTextVisual.cs
new file mode 100644 (file)
index 0000000..c6b53e9
--- /dev/null
@@ -0,0 +1,124 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.
+//
+
+extern alias TizenSystemSettings;
+using TizenSystemSettings.Tizen.System;
+
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The text visual with advanced options.
+    /// </summary>
+    /// <remarks>
+    /// It will be used when we want to control TextVisual with more options.
+    /// This visual allow to translated text with SID.
+    /// </remarks>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class AdvancedTextVisual : Visuals.TextVisual
+    {
+        #region Internal
+        private string textLabelSid = null;
+
+        private static Tizen.NUI.SystemLocaleLanguageChanged systemLocaleLanguageChanged = new Tizen.NUI.SystemLocaleLanguageChanged();
+        private bool hasSystemLanguageChanged = false;
+        #endregion
+
+        #region Constructor
+        public AdvancedTextVisual() : base()
+        {
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// The TranslatableText property.<br />
+        /// The text can set the SID value.<br />
+        /// </summary>
+        /// <exception cref='global::System.ArgumentNullException'>
+        /// ResourceManager about multilingual is null.
+        /// </exception>
+        public string TranslatableText
+        {
+            get
+            {
+                return textLabelSid;
+            }
+            set
+            {
+                if (NUIApplication.MultilingualResourceManager == null)
+                {
+                    throw new global::System.ArgumentNullException(null, "ResourceManager about multilingual is null");
+                }
+                string translatableText = null;
+                textLabelSid = value;
+                translatableText = NUIApplication.MultilingualResourceManager?.GetString(textLabelSid, new global::System.Globalization.CultureInfo(SystemSettings.LocaleLanguage.Replace("_", "-")));
+
+                if (translatableText != null)
+                {
+                    Text = translatableText;
+                    if (hasSystemLanguageChanged == false)
+                    {
+                        systemLocaleLanguageChanged.Add(SystemSettingsLocaleLanguageChanged);
+                        hasSystemLanguageChanged = true;
+                    }
+                }
+                else
+                {
+                    Text = value;
+                }
+            }
+        }
+        #endregion
+
+        #region Internal Method
+        private void SystemSettingsLocaleLanguageChanged(object sender, LocaleLanguageChangedEventArgs e)
+        {
+            string translatableText = null;
+            translatableText = NUIApplication.MultilingualResourceManager?.GetString(textLabelSid, new global::System.Globalization.CultureInfo(e.Value.Replace("_", "-")));
+            if (translatableText != null)
+            {
+                Text = translatableText;
+            }
+            else
+            {
+                Tizen.Log.Error("NUI", $"Fail to get translated text : {textLabelSid};");
+                Text = textLabelSid;
+            }
+        }
+
+        /// <inheritdoc/>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (Disposed)
+            {
+                return;
+            }
+
+            if (hasSystemLanguageChanged)
+            {
+                systemLocaleLanguageChanged.Remove(SystemSettingsLocaleLanguageChanged);
+            }
+
+            base.Dispose(type);
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/AnimatedImageVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/AnimatedImageVisual.cs
new file mode 100644 (file)
index 0000000..3875bc5
--- /dev/null
@@ -0,0 +1,296 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The visual which can display and control an animated image resource.
+    /// We can also set image sequences by using ResourceUrlList and FrameDelay property.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class AnimatedImageVisual : ImageVisual
+    {
+        #region Internal And Private
+        internal static readonly int ActionPlay = Tizen.NUI.BaseComponents.ImageView.ActionPlay;
+        internal static readonly int ActionPause = Tizen.NUI.BaseComponents.ImageView.ActionPause;
+        internal static readonly int ActionStop = Tizen.NUI.BaseComponents.ImageView.ActionStop;
+
+        internal static readonly int ActionJumpTo = Tizen.NUI.BaseComponents.AnimatedImageView.ActionJumpTo;
+
+        private List<string> resourceUrls = null;
+        #endregion
+
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public AnimatedImageVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal AnimatedImageVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal AnimatedImageVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.AnimatedImage;
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// Gets and Sets the url list in the AnimatedImageVisual.
+        /// </summary>
+        /// <remarks>
+        /// If we set ResourceUrlList as non-null, ImageVisual.ResourceUrl will be ignored.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public List<string> ResourceUrlList
+        {
+            get
+            {
+                return resourceUrls;
+            }
+            set
+            {
+                resourceUrls = value;
+
+                // Always request to create new visual
+                visualCreationRequiredFlag = true;
+                ReqeustProcessorOnceEvent();
+            }
+        }
+
+        /// <summary>
+        /// The number of milliseconds between each frame in the Image-Array animation.
+        /// </summary>
+        /// <remarks>
+        /// This is only used when ResourceUrlList(multiple string) are provided.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int FrameDelay
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.FrameDelay, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = 100;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.FrameDelay);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets and sets the number of times the AnimatedImageVisual will be looped.
+        /// The default is -1. If the number is less than 0 then it loops unlimited,otherwise loop loopCount times.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int LoopCount
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.LoopCount, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = -1;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.LoopCount);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Sets or gets the stop behavior.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.BaseComponents.AnimatedImageView.StopBehaviorType StopBehavior
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.StopBehavior, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)Tizen.NUI.BaseComponents.AnimatedImageView.StopBehaviorType.CurrentFrame;
+                var propertyValue = GetVisualProperty((int)Tizen.NUI.ImageVisualProperty.StopBehavior);
+                propertyValue?.Get(out ret);
+                return (Tizen.NUI.BaseComponents.AnimatedImageView.StopBehaviorType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Get the number of total frames.
+        /// Or -1 if image is invalid, or not loaded yet.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int TotalFrame
+        {
+            get
+            {
+                // Sync as current properties
+                UpdateVisualPropertyMap();
+
+                int ret = -1;
+                var propertyValue = GetCurrentVisualProperty((int)Tizen.NUI.ImageVisualProperty.TotalFrameNumber);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Set or get the current frame. When setting a specific frame, it is displayed as a still image.
+        /// </summary>
+        /// <remarks>
+        /// Gets the value set by a user. If the setting value is out-ranged, it is reset as a minimum frame or a maximum frame.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int CurrentFrame
+        {
+            set
+            {
+                // Sync as current properties
+                UpdateVisualPropertyMap();
+
+                Interop.VisualObject.DoActionWithSingleIntAttributes(SwigCPtr, ActionJumpTo, value);
+            }
+            get
+            {
+                // Sync as current properties
+                UpdateVisualPropertyMap();
+
+                int ret = -1;
+                var propertyValue = GetCurrentVisualProperty((int)Tizen.NUI.ImageVisualProperty.CurrentFrameNumber);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets and Sets the batch size for pre-loading images in the AnimatedImageVisual. (Advanced)
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int BatchSize
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.BatchSize, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = 1;
+                var propertyValue = GetVisualProperty((int)Tizen.NUI.ImageVisualProperty.BatchSize);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets and Sets the cache size for loading images in the AnimatedImageVisual. (Advanced)
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int CacheSize
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.CacheSize, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = 1;
+                var propertyValue = GetVisualProperty((int)Tizen.NUI.ImageVisualProperty.CacheSize);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+
+        #region Public Methods
+        /// <summary>
+        /// Play the animated image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Play()
+        {
+            // Sync as current properties
+            UpdateVisualPropertyMap();
+
+            Interop.VisualObject.DoActionWithEmptyAttributes(SwigCPtr, ActionPlay);
+        }
+
+        /// <summary>
+        /// Pause the animated image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Pause()
+        {
+            // Sync as current properties
+            UpdateVisualPropertyMap();
+
+            Interop.VisualObject.DoActionWithEmptyAttributes(SwigCPtr, ActionPause);
+        }
+
+        /// <summary>
+        /// Stop the animated image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Stop()
+        {
+            // Sync as current properties
+            UpdateVisualPropertyMap();
+
+            Interop.VisualObject.DoActionWithEmptyAttributes(SwigCPtr, ActionStop);
+        }
+        #endregion
+
+        #region Internal Methods
+        internal override void OnUpdateVisualPropertyMap()
+        {
+            if (resourceUrls != null && resourceUrls.Count > 0)
+            {
+                using var urlArray = new PropertyArray();
+                foreach (var url in resourceUrls)
+                {
+                    urlArray.Add(new PropertyValue(url));
+                }
+                using var urlArrayValue = new PropertyValue(urlArray);
+
+                if (cachedVisualPropertyMap != null)
+                {
+                    // Remove ResourceUrl from cachedVisualPropertyMap
+                    cachedVisualPropertyMap.Remove((int)Tizen.NUI.ImageVisualProperty.URL);
+                    cachedVisualPropertyMap.Add((int)Tizen.NUI.ImageVisualProperty.URL, urlArrayValue);
+                }
+            }
+            else
+            {
+                // If we don't use image sequence, follow the ImageVisual logic.
+                base.OnUpdateVisualPropertyMap();
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/BorderVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/BorderVisual.cs
new file mode 100644 (file)
index 0000000..4518645
--- /dev/null
@@ -0,0 +1,108 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// Simple visual to render a solid border.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class BorderVisual : VisualBase
+    {
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public BorderVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal BorderVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal BorderVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.Border;
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// Gets or sets the color of the border.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.Color BorderColor
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.BorderVisualProperty.Color, new PropertyValue(value));
+            }
+            get
+            {
+                Tizen.NUI.Color ret = new Tizen.NUI.Color();
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.BorderVisualProperty.Color);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the width of the border (in pixels).
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BorderWidth
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.BorderVisualProperty.Size, new PropertyValue(value));
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.BorderVisualProperty.Size);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets whether the anti-aliasing of the border is required. default is false.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool AntiAliasing
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.BorderVisualProperty.AntiAliasing, new PropertyValue(value));
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.BorderVisualProperty.AntiAliasing);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/ColorVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/ColorVisual.cs
new file mode 100644 (file)
index 0000000..9d6d8d7
--- /dev/null
@@ -0,0 +1,180 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// Simple visual to render a solid color.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ColorVisual : VisualBase
+    {
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ColorVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal ColorVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal ColorVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.Color;
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// Blur radius for this visual
+        /// </summary>
+        /// <remarks>
+        /// This property will ignore BorderlineWidth property when we set BlurRadius property at least one time.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BlurRadius
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ColorVisualProperty.BlurRadius, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ColorVisualProperty.BlurRadius);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+
+        #region Decorated Visual Properties
+        /// <summary>
+        /// The radius for the rounded corners of the visual.
+        /// The values in Vector4 are used in clockwise order from top-left to bottom-left : Vector4(top-left-corner, top-right-corner, bottom-right-corner, bottom-left-corner).
+        /// Each radius will clamp internally to the half of smaller of the visual's width or height.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Vector4 CornerRadius
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadius, new PropertyValue(value), false);
+            }
+            get
+            {
+                Vector4 ret = new Vector4();
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadius);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Whether the CornerRadius property value is relative (percentage [0.0f to 0.5f] of the visual size) or absolute (in world units).
+        /// It is absolute by default.
+        /// When the policy is relative, the corner radius is relative to the smaller of the visual's width and height.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType CornerRadiusPolicy
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadiusPolicy, new PropertyValue((int)value), false);
+            }
+            get
+            {
+                int ret = (int)VisualTransformPolicyType.Absolute;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadiusPolicy);
+                propertyValue?.Get(out ret);
+                return (VisualTransformPolicyType)ret;
+            }
+        }
+
+        /// <summary>
+        /// The width for the borderline of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BorderlineWidth
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineWidth, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineWidth);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The color for the borderline of the visual.
+        /// It is Color.Black by default.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Color BorderlineColor
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineColor, new PropertyValue(value), false);
+            }
+            get
+            {
+                Color ret = new Color(0.0f, 0.0f, 0.0f, 1.0f);
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineColor);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The Relative offset for the borderline of the visual.
+        /// Recommended range : [-1.0f to 1.0f].
+        /// If -1.0f, draw borderline inside of the visual.
+        /// If 1.0f, draw borderline outside of the visual.
+        /// If 0.0f, draw borderline half inside and half outside.
+        /// It is 0.0f by default.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BorderlineOffset
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineOffset, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineOffset);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/ImageVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/ImageVisual.cs
new file mode 100644 (file)
index 0000000..e31665a
--- /dev/null
@@ -0,0 +1,562 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The visual which can display an image resource.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class ImageVisual : VisualBase
+    {
+        #region Internal And Private
+        internal static readonly int ActionReload = Tizen.NUI.BaseComponents.ImageView.ActionReload;
+        internal bool isResourceUrlValid = false;
+
+        private PropertyMap temperalStoredPropertyMap = null; // To store property map when resource url is not valid.
+        #endregion
+
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ImageVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal ImageVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal ImageVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.Image;
+        }
+
+        #region Visual Properties
+        /// <summary>
+        /// Gets or sets the URL of the image.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string ResourceUrl
+        {
+            set
+            {
+                if(string.IsNullOrEmpty(value))
+                {
+                    isResourceUrlValid = false;
+
+                    UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.URL, null);
+
+                    // Special behavior. If ResourceUrl is empty, unregister visual, and do not show anything.
+                    UnregisterVisual();
+                }
+                else
+                {
+                    isResourceUrlValid = true;
+
+                    UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.URL, new PropertyValue(value));
+
+                    // Special case. If set GeneratedUrl, or FastTrackUploading, Create ImageVisual synchronously.
+                    if (value.StartsWith("dali://") || value.StartsWith("enbuf://") || FastTrackUploading)
+                    {
+                        UpdateVisualPropertyMap();
+                    }
+                }
+            }
+            get
+            {
+                string ret = "";
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.URL);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the image area to be displayed.<br />
+        /// It is a rectangular area.<br />
+        /// The first two elements indicate the top-left position of the area, and the last two elements are the areas of the width and the height respectively.<br />
+        /// If not specified, the default value is Vector4 (0.0, 0.0, 1.0, 1.0), i.e., the entire area of the image.<br />
+        /// For normal quad images only.<br />
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Vector4 PixelArea
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.PixelArea, new PropertyValue(value), false);
+            }
+            get
+            {
+                Vector4 ret = new Vector4(0.0f, 0.0f, 1.0f, 1.0f);
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.PixelArea);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// ImageView PreMultipliedAlpha, type Boolean.<br />
+        /// Image must be initialized.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool PreMultipliedAlpha
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.PremultipliedAlpha, new PropertyValue(value), true);
+            }
+            get
+            {
+                bool ret = true;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.PremultipliedAlpha);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Synchronously load the image for the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool SynchronousLoading
+        {
+            set
+            {
+                // Note : We need to create new visual if previous visual was async, and now we set value as sync.
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.SynchronousLoading, new PropertyValue(value), value);
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.SynchronousLoading);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets whether to automatically correct the orientation of an image.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool OrientationCorrection
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.OrientationCorrection, new PropertyValue(value), true);
+            }
+            get
+            {
+                bool ret = true;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.OrientationCorrection);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the URL of the alpha mask.<br />
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string AlphaMaskURL
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.AlphaMaskURL, string.IsNullOrEmpty(value) ? null : new PropertyValue(value));
+            }
+            get
+            {
+                string ret = "";
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.AlphaMaskURL);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets scale factor to apply to the content image before masking.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float MaskContentScale
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.MaskContentScale, new PropertyValue(value));
+            }
+            get
+            {
+                float ret = 1.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.MaskContentScale);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        ///  Whether to crop image to mask or scale mask to fit image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool CropToMask
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.CropToMask, new PropertyValue(value));
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.CropToMask);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets whether to apply mask on GPU or not.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.BaseComponents.ImageView.MaskingModeType MaskingMode
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.MaskingMode, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)Tizen.NUI.BaseComponents.ImageView.MaskingModeType.MaskingOnLoading;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.MaskingMode);
+                propertyValue?.Get(out ret);
+                return (Tizen.NUI.BaseComponents.ImageView.MaskingModeType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets whether to apply fast track uploading or not.<br />
+        /// </summary>
+        /// <remarks>
+        /// If we use fast track uploading feature, It can upload texture without event-thead dependency. But also,<br />
+        ///  - Texture size is invalid until ResourceReady signal comes.<br />
+        ///  - Texture cannot be cached (We always try to load new image).<br />
+        ///  - Seamless visual change didn't supported.<br />
+        ///  - Alpha masking didn't supported. If you try, It will load as normal case.<br />
+        ///  - Synchronous loading didn't supported. If you try, It will load as normal case.<br />
+        ///  - Reload action didn't supported. If you try, It will load as normal case.<br />
+        ///  - Atlas loading didn't supported. If you try, It will load as normal case.<br />
+        ///  - Custom shader didn't supported. If you try, It will load as normal case.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool FastTrackUploading
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.FastTrackUploading, new PropertyValue(value));
+
+                if (value && !string.IsNullOrEmpty(ResourceUrl))
+                {
+                    // Special case. If user set FastTrackUploading mean, user want to upload image As-Soon-As-Possible.
+                    // Create ImageVisual synchronously.
+                    UpdateVisualPropertyMap();
+                }
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.FastTrackUploading);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the Image Visual release policy.<br/>
+        /// It decides if a texture should be released from the cache or kept to reduce the loading time.<br/>
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ReleasePolicyType ReleasePolicy
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.ReleasePolicy, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)ReleasePolicyType.Detached;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.ReleasePolicy);
+                propertyValue?.Get(out ret);
+                return (ReleasePolicyType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the desired image width.<br />
+        /// If not specified, the actual image width is used.<br />
+        /// For normal quad images only.<br />
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int DesiredWidth
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.DesiredWidth, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = -1;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.DesiredWidth);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the desired image height.<br />
+        /// If not specified, the actual image height is used.<br />
+        /// For normal quad images only.<br />
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int DesiredHeight
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.DesiredHeight, new PropertyValue(value));
+            }
+            get
+            {
+                int ret = -1;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.DesiredHeight);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the Image Visual image loading policy.<br />
+        /// It decides if a texture should be loaded immediately after source set or only after the visual is added to the window.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public LoadPolicyType LoadPolicy
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.LoadPolicy, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)LoadPolicyType.Attached;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.LoadPolicy);
+                propertyValue?.Get(out ret);
+                return (LoadPolicyType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the wrap mode for the u coordinate.<br />
+        /// It decides how the texture should be sampled when the u coordinate exceeds the range of 0.0 to 1.0.<br />
+        /// If not specified, the default is WrapModeType.Default(CLAMP).<br />
+        /// For normal quad images only.<br />
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public WrapModeType WrapModeU
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.WrapModeU, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)WrapModeType.Default;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.WrapModeU);
+                propertyValue?.Get(out ret);
+                return (WrapModeType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the wrap mode for the v coordinate.<br />
+        /// It decides how the texture should be sampled when the v coordinate exceeds the range of 0.0 to 1.0.<br />
+        /// The first two elements indicate the top-left position of the area, and the last two elements are the areas of the width and the height respectively.<br />
+        /// If not specified, the default is WrapModeType.Default(CLAMP).<br />
+        /// For normal quad images only.
+        /// Optional.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public WrapModeType WrapModeV
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.WrapModeV, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)WrapModeType.Default;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.WrapModeV);
+                propertyValue?.Get(out ret);
+                return (WrapModeType)ret;
+            }
+        }
+        #endregion
+
+        #region Decorated Visual Properties
+        /// <summary>
+        /// The radius for the rounded corners of the visual.
+        /// The values in Vector4 are used in clockwise order from top-left to bottom-left : Vector4(top-left-corner, top-right-corner, bottom-right-corner, bottom-left-corner).
+        /// Each radius will clamp internally to the half of smaller of the visual's width or height.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Vector4 CornerRadius
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadius, new PropertyValue(value), false);
+            }
+            get
+            {
+                Vector4 ret = new Vector4();
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadius);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Whether the CornerRadius property value is relative (percentage [0.0f to 0.5f] of the visual size) or absolute (in world units).
+        /// It is absolute by default.
+        /// When the policy is relative, the corner radius is relative to the smaller of the visual's width and height.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType CornerRadiusPolicy
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadiusPolicy, new PropertyValue((int)value), false);
+            }
+            get
+            {
+                int ret = (int)VisualTransformPolicyType.Absolute;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.CornerRadiusPolicy);
+                propertyValue?.Get(out ret);
+                return (VisualTransformPolicyType)ret;
+            }
+        }
+
+        /// <summary>
+        /// The width for the borderline of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BorderlineWidth
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineWidth, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineWidth);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The color for the borderline of the visual.
+        /// It is Color.Black by default.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Color BorderlineColor
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineColor, new PropertyValue(value), false);
+            }
+            get
+            {
+                Color ret = new Color(0.0f, 0.0f, 0.0f, 1.0f);
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineColor);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The Relative offset for the borderline of the visual.
+        /// Recommended range : [-1.0f to 1.0f].
+        /// If -1.0f, draw borderline inside of the visual.
+        /// If 1.0f, draw borderline outside of the visual.
+        /// If 0.0f, draw borderline half inside and half outside.
+        /// It is 0.0f by default.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float BorderlineOffset
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineOffset, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.BorderlineOffset);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+
+        #region Public Methods
+        /// <summary>
+        /// Reload image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Reload()
+        {
+            Interop.VisualObject.DoActionWithEmptyAttributes(SwigCPtr, ActionReload);
+        }
+        #endregion
+
+        #region Internal Methods
+        internal override void OnUpdateVisualPropertyMap()
+        {
+            // We should not create visual if url is invalid.
+            if (!isResourceUrlValid)
+            {
+                temperalStoredPropertyMap = cachedVisualPropertyMap;
+                cachedVisualPropertyMap = null;
+            }
+        }
+
+        internal override void OnVisualCreated()
+        {
+            if (temperalStoredPropertyMap != null)
+            {
+                cachedVisualPropertyMap = temperalStoredPropertyMap;
+                temperalStoredPropertyMap = null;
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/NPatchVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/NPatchVisual.cs
new file mode 100644 (file)
index 0000000..8ab1fb7
--- /dev/null
@@ -0,0 +1,142 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The visual which can display an n-patch image resource.
+    /// It will be used when we want to display n-patch image, border only n-patch, or make regular image stretched.
+    /// </summary>
+    /// <remarks>
+    /// Following ImageVisual properties are not supported in NPatchVisual.
+    /// - CornerRadius
+    /// - BorderlineWidth
+    /// - AlphaMaskUrl
+    /// </remarks>
+    /// <remarks>
+    /// We assume that the image is a n-patch image always. So it does not support other image formats, like svg, lottie.
+    /// </remarks>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class NPatchVisual : ImageVisual
+    {
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public NPatchVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal NPatchVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal NPatchVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.NPatch;
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// Gets or sets whether to draw the borders only (If true).<br />
+        /// If not specified, the default is false.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool BorderOnly
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.NpatchImageVisualProperty.BorderOnly, new PropertyValue(value));
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.NpatchImageVisualProperty.BorderOnly);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The border of the regular image is in the order: left, right, bottom, top.<br />
+        /// </summary>
+        /// <remarks>
+        /// Note that it is not mean the value from 9 patch image.<br />
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Rectangle Border
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.NpatchImageVisualProperty.Border, (value == null) ? null : new PropertyValue(value));
+            }
+            get
+            {
+                Rectangle ret = new Rectangle();
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.NpatchImageVisualProperty.Border);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Overlays the auxiliary image on top of an NPatch image.
+        /// The resulting visual image will be at least as large as the smallest possible n-patch or the auxiliary image, whichever is larger.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string AuxiliaryImageUrl
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.AuxiliaryImageURL, string.IsNullOrEmpty(value) ? null : new PropertyValue(value));
+            }
+            get
+            {
+                string ret = "";
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.AuxiliaryImageURL);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// An alpha value for mixing between the masked main NPatch image and the auxiliary image.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float AuxiliaryImageAlpha
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.ImageVisualProperty.AuxiliaryImageAlpha, new PropertyValue(value));
+            }
+            get
+            {
+                float ret = 1.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.ImageVisualProperty.AuxiliaryImageAlpha);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/TextVisual.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/TextVisual.cs
new file mode 100644 (file)
index 0000000..5bcd517
--- /dev/null
@@ -0,0 +1,204 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The visual which can display simple text.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class TextVisual : VisualBase
+    {
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public TextVisual() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal TextVisual(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal TextVisual(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            Type = (int)Tizen.NUI.Visual.Type.Text;
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// The Text property.<br />
+        /// The text to display in the UTF-8 format.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Text
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.Text, new PropertyValue(string.IsNullOrEmpty(value) ? "" : value));
+            }
+            get
+            {
+                string ret = "";
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.Text);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The requested font family to use.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string FontFamily
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.FontFamily, new PropertyValue(string.IsNullOrEmpty(value) ? "" : value));
+            }
+            get
+            {
+                string ret = "";
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.FontFamily);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The size of font in points.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float PointSize
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.PointSize, new PropertyValue(value));
+            }
+            get
+            {
+                float ret = 0.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.PointSize);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The single-line or multi-line layout option.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool MultiLine
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.MultiLine, new PropertyValue(value));
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.MultiLine);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// The line horizontal alignment.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.HorizontalAlignment HorizontalAlignment
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.HorizontalAlignment, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)Tizen.NUI.HorizontalAlignment.Begin;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.HorizontalAlignment);
+                propertyValue?.Get(out ret);
+                return (Tizen.NUI.HorizontalAlignment)ret;
+            }
+        }
+
+        /// <summary>
+        /// The line vertical alignment.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.VerticalAlignment VerticalAlignment
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.VerticalAlignment, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)Tizen.NUI.VerticalAlignment.Top;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.VerticalAlignment);
+                propertyValue?.Get(out ret);
+                return (Tizen.NUI.VerticalAlignment)ret;
+            }
+        }
+
+        /// <summary>
+        /// The color of the text.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.Color TextColor
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.TextColor, new PropertyValue(value));
+            }
+            get
+            {
+                Tizen.NUI.Color ret = new Color(0.0f, 0.0f, 0.0f, 1.0f);
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.TextColor);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Whether the mark-up processing is enabled.<br />
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool EnableMarkup
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.TextVisualProperty.EnableMarkup, new PropertyValue(value));
+            }
+            get
+            {
+                bool ret = false;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.TextVisualProperty.EnableMarkup);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Visuals/VisualObject/VisualBase.cs b/src/Tizen.NUI/src/public/Visuals/VisualObject/VisualBase.cs
new file mode 100644 (file)
index 0000000..b82bcc0
--- /dev/null
@@ -0,0 +1,1108 @@
+// Copyright (c) 2024 Samsung Electronics Co., Ltd.
+//
+// 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.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Linq;
+using System.ComponentModel;
+
+namespace Tizen.NUI.Visuals
+{
+    /// <summary>
+    /// The base class of all visual in namespace Tizen.NUI.Visuals.
+    /// This class is abstract class. We cannot use it without type of Properties.
+    /// </summary>
+    /// <remarks>
+    /// Visual is the smallest rendering unit that application can control without custom renderer.
+    /// It will be used when we want to add a new visual to the View.
+    /// We can change the size or offset of visuals, and sibling order.
+    ///
+    /// When we change the property of visual, the visual will be recreated at end of event loop.
+    /// </remarks>
+    /// <code>
+    /// animation.AnimateTo(view, "BorderlineOffset", -1.0f);
+    /// </code>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class VisualBase : BaseHandle
+    {
+        #region Internal And Private
+        internal PropertyMap cachedVisualPropertyMap = null;
+        internal PropertyMap changedPropertyMap = null;
+
+        internal bool visualCreationRequiredFlag = true; // The first time should create visual.
+        internal bool visualUpdateRequiredFlag = false;
+
+        internal bool visualCreationManually = false;
+
+        private int internalType = (int)Tizen.NUI.Visual.Type.Invalid;
+
+        private bool visualPropertyUpdateProcessAttachedFlag = false;
+
+        private bool visualFittingModeApplied = false; // Whether we use fitting mode, or DontCare.
+
+        internal struct VisualTransformInfo
+        {
+            public float width;
+            public float height;
+            public float offsetX;
+            public float offsetY;
+
+            public VisualTransformPolicyType widthPolicy;
+            public VisualTransformPolicyType heightPolicy;
+            public VisualTransformPolicyType offsetXPolicy;
+            public VisualTransformPolicyType offsetYPolicy;
+
+            public Visual.AlignType origin;
+            public Visual.AlignType pivotPoint;
+
+            public float extraWidth;
+            public float extraHeight;
+
+            public PropertyMap cachedVisualTransformPropertyMap;
+
+            internal bool changed;
+            
+            public void Clear()
+            {
+                width = 1.0f;
+                height = 1.0f;
+                offsetX = 0.0f;
+                offsetY = 0.0f;
+
+                widthPolicy = VisualTransformPolicyType.Relative;
+                heightPolicy = VisualTransformPolicyType.Relative;
+                offsetXPolicy = VisualTransformPolicyType.Relative;
+                offsetYPolicy = VisualTransformPolicyType.Relative;
+
+                origin = Visual.AlignType.TopBegin;
+                pivotPoint = Visual.AlignType.TopBegin;
+
+                extraWidth = 0.0f;
+                extraHeight = 0.0f;
+
+                cachedVisualTransformPropertyMap = null;
+
+                changed = true;
+            }
+
+            internal void ConvertToPropertyMap()
+            {
+                if (cachedVisualTransformPropertyMap == null)
+                {
+                    cachedVisualTransformPropertyMap = new PropertyMap();
+                }
+
+                cachedVisualTransformPropertyMap.Clear();
+                cachedVisualTransformPropertyMap.Add((int)VisualTransformPropertyType.Size, new PropertyValue(new Vector2(width, height)))
+                                                .Add((int)VisualTransformPropertyType.Offset, new PropertyValue(new Vector2(offsetX, offsetY)))
+                                                .Add((int)VisualTransformPropertyType.SizePolicy, new PropertyValue(new Vector2((float)widthPolicy, (float)heightPolicy)))
+                                                .Add((int)VisualTransformPropertyType.OffsetPolicy, new PropertyValue(new Vector2((float)offsetXPolicy, (float)offsetYPolicy)))
+                                                .Add((int)VisualTransformPropertyType.Origin, new PropertyValue((int)origin))
+                                                .Add((int)VisualTransformPropertyType.AnchorPoint, new PropertyValue((int)pivotPoint))
+                                                .Add((int)VisualTransformPropertyType.ExtraSize, new PropertyValue(new Vector2(extraWidth, extraHeight)));
+            }
+
+            internal void ConvertFromPropertyMap(PropertyMap inputMap)
+            {
+                PropertyValue value = null;
+
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.Size)) != null)
+                {
+                    using var size = new Size();
+                    if (value.Get(size))
+                    {
+                        width = size.Width;
+                        height = size.Height;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.Offset)) != null)
+                {
+                    using var offset = new Position();
+                    if (value.Get(offset))
+                    {
+                        offsetX = offset.X;
+                        offsetY = offset.Y;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.SizePolicy)) != null)
+                {
+                    using var policyValue = new Vector2();
+                    if (value.Get(policyValue))
+                    {
+                        widthPolicy = (VisualTransformPolicyType)policyValue.X;
+                        heightPolicy = (VisualTransformPolicyType)policyValue.Y;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.OffsetPolicy)) != null)
+                {
+                    using var policyValue = new Vector2();
+                    if (value.Get(policyValue))
+                    {
+                        offsetXPolicy = (VisualTransformPolicyType)policyValue.X;
+                        offsetYPolicy = (VisualTransformPolicyType)policyValue.Y;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.Origin)) != null)
+                {
+                    int ret = 0;
+                    if (value.Get(out ret))
+                    {
+                        origin = (Visual.AlignType)ret;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.AnchorPoint)) != null)
+                {
+                    int ret = 0;
+                    if (value.Get(out ret))
+                    {
+                        pivotPoint = (Visual.AlignType)ret;
+                    }
+                }
+                if ((value = inputMap?.Find((int)VisualTransformPropertyType.ExtraSize)) != null)
+                {
+                    using var extraValue = new Vector2();
+                    if (value.Get(extraValue))
+                    {
+                        extraWidth = extraValue.Width;
+                        extraHeight = extraValue.Height;
+                    }
+                }
+                value?.Dispose();
+            }
+        };
+        internal VisualTransformInfo transformInfo;
+        #endregion
+
+        #region Constructor
+        /// <summary>
+        /// Creates an visual object.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualBase() : this(Interop.VisualObject.VisualObjectNew(), true)
+        {
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        internal VisualBase(global::System.IntPtr cPtr, bool cMemoryOwn) : this(cPtr, cMemoryOwn, cMemoryOwn)
+        {
+        }
+
+        internal VisualBase(global::System.IntPtr cPtr, bool cMemoryOwn, bool cRegister) : base(cPtr, cMemoryOwn, cRegister)
+        {
+            transformInfo.Clear();
+        }
+        #endregion
+
+        #region Enum
+        /// <summary>
+        /// The update mode of this VisualBase property.
+        /// Default is Auto.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public enum PropertyUpdateModeType
+        {
+            /// <summary>
+            /// Update property automatically, by NUI event loop.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Auto,
+            /// <summary>
+            /// Update property manually.
+            /// Need to call <see cref="UpdateProperty"/> function manually to update property.
+            /// </summary>
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Manual,
+        }
+        #endregion
+
+        #region Properties
+        /// <summary>
+        /// Sibling order of this VisualBase.
+        /// </summary>
+        /// <remarks>
+        /// The sibling order is used to determine the draw order of the visuals.
+        /// The visuals with smaller sibling order are drawn bottom,
+        /// and the visuals with larger sibling order are drawn top.
+        ///
+        /// It will be changed automatically when the visuals are added to the view.
+        /// The default value is 0.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public uint SiblingOrder
+        {
+            set
+            {
+                Interop.VisualObject.SetSiblingOrder(SwigCPtr, value);
+                NDalicPINVOKE.ThrowExceptionIfExists();
+            }
+            get
+            {
+                uint ret = Interop.VisualObject.GetSiblingOrder(SwigCPtr);
+                NDalicPINVOKE.ThrowExceptionIfExists();
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Type of this VisualObject. It will be set at construction time.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public int Type
+        {
+            internal set
+            {
+                if(internalType != value)
+                {
+                    internalType = value;
+                    UpdateVisualProperty((int)Tizen.NUI.Visual.Property.Type, new PropertyValue(value), true);
+                }
+            }
+            get
+            {
+                return internalType;
+            }
+        }
+
+        /// <summary>
+        /// Name of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// The way of visual property update.
+        /// Default is PropertyUpdateModeType.Auto.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public PropertyUpdateModeType PropertyUpdateMode
+        {
+            set
+            {
+                visualCreationManually = (value == PropertyUpdateModeType.Manual);
+            }
+            get
+            {
+                return visualCreationManually ? PropertyUpdateModeType.Manual : PropertyUpdateModeType.Auto;
+            }
+        }
+        #endregion
+
+        #region Visual Properties
+        /// <summary>
+        /// Color for the visual. Default color is White
+        /// </summary>
+        /// <remarks>
+        /// This is exclusive with the Opacity property.
+        /// Opacity property be applied as Color.A.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.Color Color
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.MixColor, new PropertyValue(value), false);
+
+                // warning : We should set cached Opacity after set MixColor.
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.Opacity, new PropertyValue(value.A), false);
+            }
+            get
+            {
+                Tizen.NUI.Color ret = new Tizen.NUI.Color(1.0f, 1.0f, 1.0f, 1.0f);
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.MixColor);
+                propertyValue?.Get(ret);
+                return ret;
+            }
+        }
+
+        /// <summary>
+        /// Opacity for the visual.
+        /// </summary>
+        /// <remarks>
+        /// This is exclusive with the Color property.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float Opacity
+        {
+            set
+            {
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.Opacity, new PropertyValue(value), false);
+            }
+            get
+            {
+                float ret = 1.0f;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.Opacity);
+                propertyValue?.Get(out ret);
+                return ret;
+            }
+        }
+        #endregion
+
+        #region Visual Transform Properties
+        /// <summary>
+        /// FittingMode for the visual.
+        /// </summary>
+        /// <remarks>
+        /// The fitting mode is used to decide how the visual should be fitted to the control area.
+        /// The default value is VisualFittingModeType.DontCare.
+        /// If user set one of Transform property, it will be set as VisualFittingModeType.DontCare automatically.
+        /// </remarks>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualFittingModeType FittingMode
+        {
+            set
+            {
+                if (value != VisualFittingModeType.DontCare)
+                {
+                    visualFittingModeApplied = true;
+                }
+                else
+                {
+                    visualFittingModeApplied = false;
+                }
+                UpdateVisualProperty((int)Tizen.NUI.Visual.Property.VisualFittingMode, new PropertyValue((int)value));
+            }
+            get
+            {
+                int ret = (int)VisualFittingModeType.DontCare;
+                var propertyValue = GetCachedVisualProperty((int)Tizen.NUI.Visual.Property.VisualFittingMode);
+                propertyValue?.Get(out ret);
+                return (VisualFittingModeType)ret;
+            }
+        }
+
+        /// <summary>
+        /// Width of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float Width
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.width != value)
+                {
+                    transformInfo.width = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.width;
+            }
+        }
+
+        /// <summary>
+        /// Height of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float Height
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.height != value)
+                {
+                    transformInfo.height = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.height;
+            }
+        }
+
+        /// <summary>
+        /// Policy of width.
+        /// If it is Relative, The width will be relative by the View's width.
+        /// If it is Absolute, The width will be used as the absolute Width value.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType WidthPolicy
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.widthPolicy != value)
+                {
+                    transformInfo.widthPolicy = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.widthPolicy;
+            }
+        }
+
+        /// <summary>
+        /// Policy of height.
+        /// If it is Relative, The height will be relative by the View's height.
+        /// If it is Absolute, The height will be used as the absolute Height value.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType HeightPolicy
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.heightPolicy != value)
+                {
+                    transformInfo.heightPolicy = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.heightPolicy;
+            }
+        }
+
+        /// <summary>
+        /// OffsetX of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float OffsetX
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.offsetX != value)
+                {
+                    transformInfo.offsetX = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.offsetX;
+            }
+        }
+
+        /// <summary>
+        /// OffsetY of the visual.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float OffsetY
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.offsetY != value)
+                {
+                    transformInfo.offsetY = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.offsetY;
+            }
+        }
+
+        /// <summary>
+        /// Policy of offsetX.
+        /// If it is Relative, The offsetX will be relative by the View's width.
+        /// If it is Absolute, The offsetX will be used as the absolute OffsetX value.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType OffsetXPolicy
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.offsetXPolicy != value)
+                {
+                    transformInfo.offsetXPolicy = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.offsetXPolicy;
+            }
+        }
+
+        /// <summary>
+        /// Policy of offsetY.
+        /// If it is Relative, The offsetY will be relative by the View's height.
+        /// If it is Absolute, The offsetY will be used as the absolute OffsetY value.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public VisualTransformPolicyType OffsetYPolicy
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.offsetYPolicy != value)
+                {
+                    transformInfo.offsetYPolicy = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.offsetYPolicy;
+            }
+        }
+
+        /// <summary>
+        /// Origin of the visual from view.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Visual.AlignType Origin
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.origin != value)
+                {
+                    transformInfo.origin = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.origin;
+            }
+        }
+
+        /// <summary>
+        /// Pivot point of the visual from view.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Visual.AlignType PivotPoint
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.pivotPoint != value)
+                {
+                    transformInfo.pivotPoint = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.pivotPoint;
+            }
+        }
+
+        /// <summary>
+        /// Extra width of the visual as absolute policy.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float ExtraWidth
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.extraWidth != value)
+                {
+                    transformInfo.extraWidth = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.extraWidth;
+            }
+        }
+
+        /// <summary>
+        /// Extra height of the visual as absolute policy.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public float ExtraHeight
+        {
+            set
+            {
+                if (visualFittingModeApplied)
+                {
+                    FittingMode = VisualFittingModeType.DontCare;
+                }
+                if (transformInfo.extraHeight != value)
+                {
+                    transformInfo.extraHeight = value;
+                    TransformPropertyChanged();
+                }
+            }
+            get
+            {
+                return transformInfo.extraHeight;
+            }
+        }
+        #endregion
+
+        #region Public Methods
+        /// <summary>
+        /// Get attached View.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Tizen.NUI.BaseComponents.View GetOwner()
+        {
+            var container = GetVisualContainer();
+            if (container != null)
+            {
+                return container.GetView();
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// Detach from View.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Detach()
+        {
+            if (Disposed)
+            {
+                return;
+            }
+
+            var container = GetVisualContainer();
+            if (container != null)
+            {
+                container.RemoveVisualObject(this);
+            }
+
+            Interop.VisualObject.Detach(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Raise above the next sibling visual object
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Raise()
+        {
+            Interop.VisualObject.Raise(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Lower below the previous sibling visual object
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void Lower()
+        {
+            Interop.VisualObject.Lower(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Raise above all other sibling visual objects
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RaiseToTop()
+        {
+            Interop.VisualObject.RaiseToTop(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Raise below all other sibling visual objects
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void LowerToBottom()
+        {
+            Interop.VisualObject.LowerToBottom(SwigCPtr);
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Raise above target visual objects. No effects if visual object is already above target.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void RaiseAbove(Visuals.VisualBase target)
+        {
+            Interop.VisualObject.RaiseAbove(SwigCPtr, Visuals.VisualBase.getCPtr(target));
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Lower below target visual objects. No effects if visual object is already below target.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void LowerBelow(Visuals.VisualBase target)
+        {
+            Interop.VisualObject.LowerBelow(SwigCPtr, Visuals.VisualBase.getCPtr(target));
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Update visual properties manually.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void UpdateProperty()
+        {
+            UpdateVisualPropertyMap();
+        }
+        #endregion
+
+        #region Internal Methods
+        internal Visuals.VisualObjectsContainer GetVisualContainer()
+        {
+            global::System.IntPtr cPtr = Interop.VisualObject.GetContainer(SwigCPtr);
+            Visuals.VisualObjectsContainer ret = null;
+            if (Interop.RefObject.GetRefObjectPtr(cPtr) == global::System.IntPtr.Zero)
+            {
+                // Visual contianer is not created yet. Return null.
+                Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+            }
+            else
+            {
+                ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Visuals.VisualObjectsContainer;
+                if (ret != null)
+                {
+                    Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+                }
+                else
+                {
+                    ret = new Visuals.VisualObjectsContainer(cPtr, true);
+                }
+            }
+            NDalicPINVOKE.ThrowExceptionIfExists();
+            return ret;
+        }
+
+        /// <summary>
+        /// Unregister visual from control. It will not remove cached data.
+        /// </summary>
+        internal void UnregisterVisual()
+        {
+            using var tempPropertyMap = new PropertyMap();
+            Interop.VisualObject.CreateVisual(SwigCPtr, PropertyMap.getCPtr(tempPropertyMap));
+        }
+
+        /// <summary>
+        /// Set or Get visual property.
+        /// </summary>
+        /// <remarks>
+        /// This property might change the type of visual. We should not set it from subclass of VisualBase.
+        /// </remarks>
+        internal PropertyMap Properties
+        {
+            private set
+            {
+                visualCreationRequiredFlag = true;
+                cachedVisualPropertyMap = value;
+
+                visualUpdateRequiredFlag = false;
+                changedPropertyMap?.Dispose();
+                changedPropertyMap = null;
+
+                transformInfo.Clear();
+
+                // Get transform informations from input property map.
+                using var transformValue = cachedVisualPropertyMap?.Find((int)Tizen.NUI.Visual.Property.Transform);
+                if (transformValue != null)
+                {
+                    PropertyMap transformMap = new PropertyMap();
+                    if (transformValue.Get(ref transformMap) && transformMap != null)
+                    {
+                        transformInfo.ConvertFromPropertyMap(transformMap);
+                    }
+                }
+                transformInfo.changed = false;
+
+                // Get type from the property map.
+                internalType = (int)Tizen.NUI.Visual.Type.Invalid;
+                if (cachedVisualPropertyMap?.Find((int)Tizen.NUI.Visual.Property.Type)?.Get(out internalType) ?? false)
+                {
+                    UpdateVisualPropertyMap();
+                }
+                else
+                {
+                    // If type is not set, then remove the visual.
+                    UnregisterVisual();
+                }
+            }
+            get
+            {
+                // Sync as current properties
+                UpdateVisualPropertyMap();
+
+                PropertyMap ret = new PropertyMap();
+                Interop.VisualObject.RetrieveVisualPropertyMap(SwigCPtr, PropertyMap.getCPtr(ret));
+                return ret;
+            }
+        }
+
+        virtual internal void OnUpdateVisualPropertyMap()
+        {
+        }
+
+        virtual internal void OnVisualCreated()
+        {
+        }
+
+        /// <summary>
+        /// Lazy call to UpdateVisualPropertyMap.
+        /// Collect Properties need to be update, and set properties that starts the Processing.
+        ///
+        /// If you want to update cachedVisualPropertyMap, but don't want to request new visual creation, make requiredVisualCreation value as false.
+        /// (Example : if we change SynchronousLoading property from 'true' to 'false', or if we call this function during OnUpdateVisualPropertyMap)
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        virtual internal void UpdateVisualProperty(int key, PropertyValue value, bool requiredVisualCreation = true)
+        {
+            // Update visual property map value as inputed value.
+            if (key != 0)
+            {
+                if (!HasBody())
+                {
+                    // Throw exception if VisualBase is disposed.
+                    throw new global::System.InvalidOperationException($"[NUI][VisualBase] Someone try to change disposed VisualBase property.\n");
+                }
+
+                if (cachedVisualPropertyMap == null)
+                {
+                    cachedVisualPropertyMap = new PropertyMap();
+                }
+
+                visualCreationRequiredFlag |= requiredVisualCreation;
+                if (value != null)
+                {
+                    cachedVisualPropertyMap[key] = value;
+
+                    // Store the changed values in the changedPropertyMap, only if visualCreationRequiredFlag is false.
+                    // It will be used when we create the visual, only by UpdateVisualPropertyMap
+                    if (!visualCreationRequiredFlag)
+                    {
+                        visualUpdateRequiredFlag = true;
+                        if (changedPropertyMap == null)
+                        {
+                            changedPropertyMap = new PropertyMap();
+                        }
+                        changedPropertyMap[key] = value;
+                    }
+                }
+                else
+                {
+                    // Remove value from cachedVisualPropertyMap if input property map is null.
+                    cachedVisualPropertyMap.Remove(key);
+                }
+
+                ReqeustProcessorOnceEvent();
+            }
+        }
+
+        internal void UpdateVisualPropertyMap(object o, global::System.EventArgs e)
+        {
+            // Note : To allow event attachment during UpdateVisualPropertyMap, let we make flag as false before call UpdateVisualPropertyMap().
+            visualPropertyUpdateProcessAttachedFlag = false;
+            if (!visualCreationManually)
+            {
+                UpdateVisualPropertyMap();
+            }
+        }
+
+        internal void UpdateVisualPropertyMap()
+        {
+            if (Disposed)
+            {
+                return;
+            }
+
+            if (internalType == (int)Tizen.NUI.Visual.Type.Invalid)
+            {
+                Tizen.Log.Error("NUI", "Invalid visual type.\n");
+                return;
+            }
+
+            if (!visualCreationRequiredFlag)
+            {
+                if (visualUpdateRequiredFlag && changedPropertyMap != null)
+                {
+                    // We can change property map of visuals without creating them.
+                    Interop.VisualObject.UpdateVisualPropertyMap(SwigCPtr, PropertyMap.getCPtr(changedPropertyMap));
+
+                    changedPropertyMap?.Dispose();
+                    changedPropertyMap = null;
+                }
+                return;
+            }
+
+            visualCreationRequiredFlag = false;
+            visualUpdateRequiredFlag = false;
+
+            changedPropertyMap?.Dispose();
+            changedPropertyMap = null;
+
+            if (cachedVisualPropertyMap == null)
+            {
+                cachedVisualPropertyMap = new PropertyMap();
+            }
+
+            if (transformInfo.changed)
+            {
+                transformInfo.changed = false;
+                cachedVisualPropertyMap.Remove((int)Tizen.NUI.Visual.Property.Transform);
+
+                transformInfo.ConvertToPropertyMap();
+
+                if (transformInfo.cachedVisualTransformPropertyMap != null)
+                {
+                    using var transformValue = new PropertyValue(transformInfo.cachedVisualTransformPropertyMap);
+                    cachedVisualPropertyMap.Add((int)Tizen.NUI.Visual.Property.Transform, transformValue);
+                }
+            }
+
+            // Update the sub classes property map.
+            OnUpdateVisualPropertyMap();
+
+            Interop.VisualObject.CreateVisual(SwigCPtr, PropertyMap.getCPtr(cachedVisualPropertyMap));
+
+            // Post process for sub classes.
+            OnVisualCreated();
+
+            NDalicPINVOKE.ThrowExceptionIfExists();
+        }
+
+        /// <summary>
+        /// Get visual property by key.
+        /// If we found value in local cached result, return that.
+        /// Else, get synced native map and return that.
+        /// </summary>
+        /// <returns>Matched value. If there is no matched value, return null.</returns>
+        internal PropertyValue GetVisualProperty(int key)
+        {
+            PropertyValue ret = GetCachedVisualProperty(key);
+            if (ret == null)
+            {
+                // If we cannot find result from cached map, Get value from native engine.
+                GetCurrentVisualProperty(key);
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// Get visual property by key from native engine.
+        /// </summary>
+        /// <returns>Matched value. If there is no matched value, return null.</returns>
+        internal PropertyValue GetCurrentVisualProperty(int key)
+        {
+            return Properties?.Find(key);
+        }
+
+        /// <summary>
+        /// Get visual property from NUI cached map by key.
+        /// </summary>
+        /// <returns>Matched value. If there is no matched value, return null.</returns>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal virtual PropertyValue GetCachedVisualProperty(int key)
+        {
+            return cachedVisualPropertyMap?.Find(key);
+        }
+
+        /// <summary>
+        /// Change visual transform information.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        internal void TransformPropertyChanged()
+        {
+            if (transformInfo.cachedVisualTransformPropertyMap == null)
+            {
+                transformInfo.cachedVisualTransformPropertyMap = new PropertyMap();
+            }
+
+            // Mark as transform property map.
+            visualCreationRequiredFlag = true;
+            transformInfo.changed = true;
+
+            ReqeustProcessorOnceEvent();
+        }
+
+        internal void ReqeustProcessorOnceEvent()
+        {
+            if (!visualCreationManually && !visualPropertyUpdateProcessAttachedFlag)
+            {
+                visualPropertyUpdateProcessAttachedFlag = true;
+                ProcessorController.Instance.ProcessorOnceEvent += UpdateVisualPropertyMap;
+                // Call process hardly.
+                ProcessorController.Instance.Awake();
+            }
+        }
+
+        /// <summary>
+        /// Dispose for VisualObject
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (Disposed)
+            {
+                return;
+            }
+
+            if (type == DisposeTypes.Explicit)
+            {
+                //Called by User
+                //Release your own managed resources here.
+                //You should release all of your own disposable objects here.
+
+                // Note : Do not call this function in Implicit dispose case.
+                // Since if visual is already under some VisualObjectsContainer,
+                // it will never be GC.
+                Detach();
+            }
+
+            visualCreationRequiredFlag = false;
+            visualUpdateRequiredFlag = false;
+
+            changedPropertyMap?.Dispose();
+            changedPropertyMap = null;
+            cachedVisualPropertyMap?.Dispose();
+            cachedVisualPropertyMap = null;
+
+            base.Dispose(type);
+        }
+        #endregion
+    }
+}
\ No newline at end of file
index 04ba70b..0107850 100755 (executable)
@@ -416,15 +416,22 @@ namespace Tizen.NUI
             BottomRight = 8,
         }
 
-
         /// <summary>
         /// The stage instance property (read-only).<br />
         /// Gets the current window.<br />
         /// </summary>
-        /// <since_tizen> 3 </since_tizen>
+        [Obsolete("This has been deprecated in API12, please use Default instead")]
         public static Window Instance { get; internal set; }
 
         /// <summary>
+        /// Gets the default window.
+        /// The main window or default window is automatically created when the application is launched, 
+        /// and it remains constant and unchanged throughout the application's operation.
+        /// </summary>
+        /// <since_tizen> 12 </since_tizen>
+        public static Window Default { get; internal set; }
+
+        /// <summary>
         /// Gets or sets a window type.
         /// Most of window type can be set to use WindowType, except for IME type.
         /// IME type can be set to use one of NUIApplication's constrcutors.
diff --git a/test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.csproj b/test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.csproj
new file mode 100644 (file)
index 0000000..b74ab56
--- /dev/null
@@ -0,0 +1,19 @@
+<Project Sdk="Tizen.NET.Sdk/1.1.9">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <AssemblyName>AIAvatarSample</AssemblyName>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugType>portable</DebugType>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>None</DebugType>
+  </PropertyGroup>
+
+  <ItemGroup>
+     <ProjectReference Include="..\..\src\Tizen.AIAvatar\Tizen.AIAvatar.csproj" />
+     <ProjectReference Include="../../src/Tizen.NUI.Components/Tizen.NUI.Components.csproj" />
+  </ItemGroup>
+</Project>
diff --git a/test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.sln b/test/Tizen.AIAvatar.Example/Tizen.AIAvatar.Sample.sln
new file mode 100644 (file)
index 0000000..0e2ce1b
--- /dev/null
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34009.444
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.AIAvatar.Sample", "Tizen.AIAvatar.Sample.csproj", "{0176A159-FCC2-45B8-ABEE-63FEBFEA5079}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.AIAvatar", "..\..\src\Tizen.AIAvatar\Tizen.AIAvatar.csproj", "{D1971AA8-5B1D-467A-9E86-292B01C4DDB8}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {0176A159-FCC2-45B8-ABEE-63FEBFEA5079}.Release|Any CPU.Build.0 = Release|Any CPU
+               {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {D1971AA8-5B1D-467A-9E86-292B01C4DDB8}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/test/Tizen.AIAvatar.Example/res/audio2vowel_7.tflite b/test/Tizen.AIAvatar.Example/res/audio2vowel_7.tflite
new file mode 100644 (file)
index 0000000..95bd359
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/audio2vowel_7.tflite differ
diff --git a/test/Tizen.AIAvatar.Example/res/images/Irradiance.ktx b/test/Tizen.AIAvatar.Example/res/images/Irradiance.ktx
new file mode 100644 (file)
index 0000000..df24c05
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/images/Irradiance.ktx differ
diff --git a/test/Tizen.AIAvatar.Example/res/images/Radiance.ktx b/test/Tizen.AIAvatar.Example/res/images/Radiance.ktx
new file mode 100644 (file)
index 0000000..51aa681
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/images/Radiance.ktx differ
diff --git a/test/Tizen.AIAvatar.Example/res/images/UI_BG.png b/test/Tizen.AIAvatar.Example/res/images/UI_BG.png
new file mode 100644 (file)
index 0000000..aba41d7
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/images/UI_BG.png differ
diff --git a/test/Tizen.AIAvatar.Example/res/voice/cs-CZ-Wavenet-A.wav b/test/Tizen.AIAvatar.Example/res/voice/cs-CZ-Wavenet-A.wav
new file mode 100644 (file)
index 0000000..98d72c6
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/voice/cs-CZ-Wavenet-A.wav differ
diff --git a/test/Tizen.AIAvatar.Example/res/voice/da-DK-Wavenet-A.wav b/test/Tizen.AIAvatar.Example/res/voice/da-DK-Wavenet-A.wav
new file mode 100644 (file)
index 0000000..9bae76d
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/voice/da-DK-Wavenet-A.wav differ
diff --git a/test/Tizen.AIAvatar.Example/res/voice/el-GR-Wavenet-A.wav b/test/Tizen.AIAvatar.Example/res/voice/el-GR-Wavenet-A.wav
new file mode 100644 (file)
index 0000000..43388f0
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/voice/el-GR-Wavenet-A.wav differ
diff --git a/test/Tizen.AIAvatar.Example/res/voice/voice_0.bin b/test/Tizen.AIAvatar.Example/res/voice/voice_0.bin
new file mode 100644 (file)
index 0000000..743e789
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/voice/voice_0.bin differ
diff --git a/test/Tizen.AIAvatar.Example/res/voice/voice_1.bin b/test/Tizen.AIAvatar.Example/res/voice/voice_1.bin
new file mode 100644 (file)
index 0000000..8ce3aa0
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/res/voice/voice_1.bin differ
diff --git a/test/Tizen.AIAvatar.Example/shared/res/Tizen.AIAvatar.Example.png b/test/Tizen.AIAvatar.Example/shared/res/Tizen.AIAvatar.Example.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/test/Tizen.AIAvatar.Example/shared/res/Tizen.AIAvatar.Example.png differ
diff --git a/test/Tizen.AIAvatar.Example/src/AvatarScene.cs b/test/Tizen.AIAvatar.Example/src/AvatarScene.cs
new file mode 100644 (file)
index 0000000..e35263a
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 global::System;
+using System.IO;
+using System.Collections.Generic;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Scene3D;
+using Tizen.AIAvatar;
+using Tizen;
+using Tizen.Uix.Tts;
+
+namespace AIAvatar
+{
+    public class AvatarScene : SceneView
+    {
+        private static readonly string ApplicationResourcePath = "/usr/apps/org.tizen.default-avatar-resource/shared/res/";
+        private static readonly string EmojiAvatarResourcePath = "/models/EmojiAvatar/";
+        private static readonly string DefaultMotionResourcePath = "/animation/motion/";
+        private static string resourcePath = Utils.ResourcePath;
+
+        private const int cameraAnimationDurationMilliSeconds = 2000;
+        private const int sceneTransitionDurationMilliSeconds = 1500;
+
+        private Avatar defaultAIAvatar;
+        private List<AvatarInfo> avatarList;
+        private List<AnimationInfo> animationInfoList;
+        private LipSyncController lipSyncController;
+
+        private bool isBlink = false;
+        private bool isShowing = true;
+
+        private int avatarIndex = 0;
+        private float iblFactor = 0.3f;
+
+        public float IBLFactor
+        {
+            get
+            {
+                return iblFactor;
+            }
+            set
+            {
+                iblFactor = value;
+                ImageBasedLightScaleFactor = value;
+            }
+        }
+
+        public AvatarScene()
+        {
+            PivotPoint = Tizen.NUI.PivotPoint.TopLeft;
+            ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft;
+            PositionUsesPivotPoint = true;
+
+            // Setup Image Based Light
+            SetupSceneViewIBL();
+
+            // Setup camera preset
+            SetupDefaultCamera();
+
+            // Setup Default Avatar Position & Orientation
+            SetupDefaultAvatar();
+
+            // Load Default Animations
+            LoadDefaultAnimations();
+        }
+
+        private void LoadDefaultAnimations()
+        {
+            var motionAnimations = Directory.GetFiles(ApplicationResourcePath + DefaultMotionResourcePath, "*.bvh");
+            animationInfoList = new List<AnimationInfo>();
+
+            foreach (var path in motionAnimations)
+            {
+                var motionData = new MotionData();
+                motionData.LoadMotionCaptureAnimation(path, true, new Vector3(0.01f, 0.01f, 0.01f), false);
+
+                var animationInfo = new AnimationInfo(motionData, GetFileNameWithoutExtension(path));
+                animationInfoList.Add(animationInfo);
+            }
+        }
+
+        public void SetupSceneViewIBLFactor(float value)
+        {
+            IBLFactor = value;
+        }
+
+        public void StartAvatarTalk_1()
+        {
+            PlayLip("cs-CZ-Wavenet-A.wav");
+        }
+
+        public void StartAvatarTalk_2()
+        {
+            PlayLip("da-DK-Wavenet-A.wav");
+        }
+
+        public void StartAvatarTalk_3()
+        {
+            PlayLip("el-GR-Wavenet-A.wav");
+        }
+
+        public void StartAvatarTalkByPath()
+        {
+            PlayLip($"{resourcePath}/voice/voice_0.bin");
+        }
+
+        private void PlayLip(string source)
+        {
+            var path = $"{resourcePath}/voice/{source}";
+            var bytes = ReadAllBytes(path);
+
+            Tizen.Log.Error("AIAvatar", "audio path : " + path);
+            if (lipSyncController == null)
+            {
+                lipSyncController = new LipSyncController(defaultAIAvatar);
+            }
+
+            if (bytes != null)
+            {
+                lipSyncController.PlayLipSync(bytes);
+            }
+            else
+            {
+                Tizen.Log.Error("AIAvatar", "Fail to load bytes");
+            }
+        }
+
+        public void StartRandomAnimation()
+        {
+            var randomIdx = new Random().Next(0, animationInfoList.Count);
+            defaultAIAvatar.PlayAnimation(animationInfoList[randomIdx].MotionData);
+        }
+
+        public void StartMic()
+        {
+            InitMic();
+            lipSyncController?.StartMic();
+        }
+
+        public void StopMic()
+        {
+            InitMic();
+            lipSyncController?.StopMic();
+        }
+
+        public void EyeBlink()
+        {
+            if (!isBlink)
+            {
+                defaultAIAvatar.StartEyeBlink();
+            }
+            else
+            {
+                defaultAIAvatar.StopEyeBlink();
+            }
+            isBlink = !isBlink;
+        }
+
+        public void ShowHide()
+        {
+            if (!isShowing)
+            {
+                defaultAIAvatar.Show();
+            }
+            else
+            {
+                defaultAIAvatar.Hide();
+            }
+            isShowing = !isShowing;
+        }
+
+        public void InintTTsTest()
+        {
+            InitTTS();
+        }
+
+        public void StartTTSTest()
+        {
+            lipSyncController.StopTTS();
+            VoiceInfo voiceInfo = new VoiceInfo()
+            {
+                Language = "en_US",
+                Type = VoiceType.Female,
+            };
+
+            lipSyncController.PrepareTTS(Utils.TTSText, voiceInfo, (o, e) =>
+            {
+                lipSyncController.PlayPreparedTTS();
+            });
+        }
+
+        public void StopTTSTest()
+        {
+            if (lipSyncController == null)
+            {
+                Tizen.Log.Error("AIAvatar", "lipSyncController is null");
+                return;
+            }
+            lipSyncController.StopTTS();
+        }
+
+        public void SwitchCamera()
+        {
+            CameraTransition(1, cameraAnimationDurationMilliSeconds);
+        }
+
+        public void SetupSceneViewCameraFov(float value)
+        {
+            var camera = GetSelectedCamera();
+            camera.FieldOfView = new Radian(value);
+        }
+
+        public void ChangeAvatar()
+        {
+            DestroyAvatar();
+            if (avatarIndex + 1 <= avatarList.Count - 1)
+            {
+                avatarIndex++;
+            }
+            else
+            {
+                avatarIndex = 0;
+            }
+            CreateAvatar();
+
+        }
+
+        internal static string GetFileNameWithoutExtension(string path)
+        {
+            return System.IO.Path.GetFileNameWithoutExtension(path);
+        }
+
+        private void InitTTS()
+        {
+            if (lipSyncController == null)
+            {
+                lipSyncController = new LipSyncController(defaultAIAvatar);
+            }
+            lipSyncController.InitializeTTS();
+        }
+
+        private void InitMic()
+        {
+            if (lipSyncController == null)
+            {
+                lipSyncController = new LipSyncController(defaultAIAvatar);
+            }
+            lipSyncController.InitializeMic();
+        }
+
+        private byte[] ReadAllBytes(string path)
+        {
+            try
+            {
+                var bytes = File.ReadAllBytes(path);
+                return bytes;
+            }
+            catch (Exception)
+            {
+                return null;
+            }
+        }
+
+        private void SetupDefaultAvatar()
+        {
+            avatarList = AvatarExtension.GetDefaultAvatarList();
+            foreach (var info in avatarList)
+            {
+                Tizen.Log.Info("AvatarSample", $"Avatar Name :{info.Name}\n");
+                Tizen.Log.Info("AvatarSample", $"Avatar Thumbnail :{info.ThumbnailPath}\n");
+            }
+
+            CreateAvatar();
+        }
+
+        private void CreateAvatar()
+        {
+            defaultAIAvatar = new Avatar(avatarList[avatarIndex])
+            {
+                Position = new Position(0.0f, -1.70f, -2.0f),
+                Size = new Size(1.0f, 1.0f, 1.0f),
+                Orientation = new Rotation(new Radian(new Degree(0.0f)), Vector3.YAxis)
+            };
+            //var animator = defaultAIAvatar.CurrentAnimator;
+            //animator.MotionStateChanged += OnMotionStateChanged;
+            //animator.LipStateChanged += OnLipStateChanged;
+            Add(defaultAIAvatar);
+        }
+
+        private void DestroyAvatar()
+        {
+            if (defaultAIAvatar != null)
+            {
+                Remove(defaultAIAvatar);
+                /*
+                var animator = defaultAIAvatar.CurrentAnimator;
+                animator.MotionStateChanged -= OnMotionStateChanged;
+                animator.LipStateChanged -= OnLipStateChanged;
+*/
+                defaultAIAvatar.Dispose();
+                defaultAIAvatar = null;
+            }
+
+        }
+
+        private void OnMotionStateChanged(object sender, AvatarMotionChangedEventArgs e)
+        {
+            var avatar = sender as Avatar;//Avatar changed state
+
+            switch (e.Current)
+            {
+                case AvatarMotionState.Ready:
+                    Log.Error(Utils.LogTag, "Current Avatar State is Ready");
+                    break;
+
+                case AvatarMotionState.Playing:
+                    Log.Error(Utils.LogTag, "Current Avatar State is Playing");
+                    break;
+
+                case AvatarMotionState.Paused:
+                    Log.Error(Utils.LogTag, "Current Avatar State is Paused");
+                    break;
+
+                case AvatarMotionState.Stopped:
+                    Log.Error(Utils.LogTag, "Current Avatar State is Stopped");
+                    break;
+            }
+        }
+
+        private void OnLipStateChanged(object sender, AvatarMotionChangedEventArgs e)
+        {
+            var avatar = sender as Avatar;//Avatar changed state
+
+            switch (e.Current)
+            {
+                case AvatarMotionState.Ready:
+                    Log.Error(Utils.LogTag, "Current Avatar Lip is Ready");
+                    break;
+
+                case AvatarMotionState.Playing:
+                    Log.Error(Utils.LogTag, "Current Avatar Lip is Playing");
+                    break;
+
+                case AvatarMotionState.Paused:
+                    Log.Error(Utils.LogTag, "Current Avatar Lip is Paused");
+                    break;
+
+                case AvatarMotionState.Stopped:
+                    Log.Error(Utils.LogTag, "Current Avatar Lip is Stopped");
+                    break;
+            }
+        }
+
+        private void SetupSceneViewIBL()
+        {
+            SetImageBasedLightSource(resourcePath + "images/" + "Irradiance.ktx", resourcePath + "images/" + "Radiance.ktx", IBLFactor);
+        }
+
+        private void SetupDefaultCamera()
+        {
+            // Default camera setting
+            // Note : SceneView always have 1 default camera.
+            var defaultCamera = GetCamera(0u);
+
+            defaultCamera.PositionX = 0.0f;
+            defaultCamera.PositionY = -2.3f;
+            defaultCamera.PositionZ = 0.0f;
+            defaultCamera.FieldOfView = new Radian(new Degree(45.0f));
+        }
+    }
+}
diff --git a/test/Tizen.AIAvatar.Example/src/ControlPannelComponent.cs b/test/Tizen.AIAvatar.Example/src/ControlPannelComponent.cs
new file mode 100644 (file)
index 0000000..fc33ede
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 global::System;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Scene3D;
+using Tizen.NUI.Components;
+
+namespace AIAvatar
+{
+    public class UIControlPanel
+    {
+        private readonly Vector2 SceneViewPadding = new Vector2(0.0f, 0.0f);
+        private ButtonStyle buttonStyle;
+        private SliderStyle sliderStyle;
+
+        private Window uiWindow;
+        private AvatarScene mainScene;
+        private View controlPannel;
+
+        private TextField editor;
+
+        private readonly float ControlPannelWidthScale = 0.25f;// Relative size of window width. windowSize.Width * 0.5f
+
+        public event EventHandler OnExitButtonClicked;
+
+        public void MakeControlPannel(Window uiWindow, AvatarScene mainScene)
+        {
+            this.uiWindow = uiWindow;
+            this.mainScene = mainScene;
+
+            if (controlPannel != null)
+            {
+                return;
+            }
+
+            InitializeButtonStyle();
+            InitializeSliderStyle();
+
+            controlPannel = new ScrollableBase()
+            {
+                BackgroundColor = new Color(0.85f, 0.85f, 0.85f, 0.25f),
+
+                PivotPoint = PivotPoint.TopLeft,
+                ParentOrigin = ParentOrigin.TopLeft,
+                PositionUsesPivotPoint = true,
+                //CornerRadius = 30.0f,
+                Padding = new Extents(30, 30, 30, 30),
+
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Vertical,
+                    HorizontalAlignment = HorizontalAlignment.Begin,
+                    VerticalAlignment = VerticalAlignment.Top,
+                    CellPadding = new Size2D(10, 20),
+                },
+                HideScrollbar = false,
+            };
+            uiWindow.Add(controlPannel);
+
+            // Make control buttons
+            MakeControlPannelComponents();
+        }
+
+        public void ReizeUIPanel(View avatarScene, View circleView)
+        {
+            var windowSize = uiWindow.Size;
+            var layoutLTR = (windowSize.Width >= windowSize.Height);
+
+            var sceneViewSize = new Vector2(0, 0);
+            var sceneViewPosition = new Vector2(0, 0);
+            var controlPannelSize = new Vector2(0, 0);
+            var controlPannelPosition = new Vector2(0, 0);
+            if (layoutLTR) // layout Left to Right
+            {
+                sceneViewSize = new Vector2(windowSize.Width - SceneViewPadding.X * 3.0f - ControlPannelWidthScale * windowSize.Width,
+                                            windowSize.Height - SceneViewPadding.Y * 2.0f);
+                sceneViewPosition = new Vector2(SceneViewPadding.X * 2.0f + ControlPannelWidthScale * windowSize.Width, SceneViewPadding.Y);
+                controlPannelSize = new Vector2(ControlPannelWidthScale * windowSize.Width, windowSize.Height - SceneViewPadding.Y * 2.0f);
+                controlPannelPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y);
+            }
+            else // layout Top to Bottom
+            {
+                sceneViewSize = new Vector2(windowSize.Width - SceneViewPadding.X * 2.0f,
+                                        windowSize.Height - SceneViewPadding.Y * 3.0f - ControlPannelWidthScale * windowSize.Height);
+                sceneViewPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y * 2.0f + ControlPannelWidthScale * windowSize.Height);
+                controlPannelSize = new Vector2(windowSize.Width - SceneViewPadding.X * 2.0f, ControlPannelWidthScale * windowSize.Height);
+                controlPannelPosition = new Vector2(SceneViewPadding.X, SceneViewPadding.Y);
+            }
+
+            // Update SceneView and ControlPannel size/position if we has.
+            if (avatarScene != null && circleView != null)
+            {
+                avatarScene.SizeWidth = sceneViewSize.Width;
+                avatarScene.SizeHeight = sceneViewSize.Height;
+                avatarScene.PositionX = 0;//sceneViewPosition.X;
+                avatarScene.PositionY = 0;//sceneViewPosition.Y;
+
+                circleView.SizeWidth = sceneViewSize.Width;
+                circleView.SizeHeight = sceneViewSize.Width;
+                circleView.PositionX = sceneViewPosition.X;
+                circleView.PositionY = sceneViewPosition.Y;
+            }
+
+            if (controlPannel != null)
+            {
+                controlPannel.SizeWidth = controlPannelSize.Width;
+                controlPannel.SizeHeight = controlPannelSize.Height;
+                controlPannel.PositionX = controlPannelPosition.X;
+                controlPannel.PositionY = controlPannelPosition.Y;
+            }
+        }
+
+        private void InitializeButtonStyle()
+        {
+            buttonStyle = new ButtonStyle()
+            {
+                Size = new Size(252, 48),
+                ItemSpacing = new Size2D(8, 8),
+                CornerRadius = 12.0f,
+                ItemHorizontalAlignment = HorizontalAlignment.Center,
+                ItemVerticalAlignment = VerticalAlignment.Center,
+                BorderlineWidth = 5.0f,
+                BackgroundColor = new Selector<Color>()
+                {
+                    Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+                    Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+                    Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+                    Selected = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+                    Disabled = new Color(0.792f, 0.792f, 0.792f, 1),
+                },
+                BorderlineColorSelector = new Selector<Color>()
+                {
+                    Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+                    Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+                    Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+                    Selected = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+                    Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+                },
+                Text = new TextLabelStyle()
+                {
+                    TextColor = new Color("#0D0D0D"),
+                    PixelSize = 24,
+                },
+            };
+        }
+
+        private void InitializeSliderStyle()
+        {
+            sliderStyle = new SliderStyle()
+            {
+                Size = new Size(850, 50),
+                TrackThickness = 8,
+                Track = new ImageViewStyle()
+                {
+                    Size = new Size(800, 8),
+                    CornerRadius = 4.0f,
+                    BackgroundColor = new Selector<Color>()
+                    {
+                        Normal = new Color(1.0f, 0.37f, 0.0f, 0.1f),
+                        Disabled = new Color(1.0f, 0.37f, 0.0f, 0.1f),
+                    },
+                },
+                Progress = new ImageViewStyle()
+                {
+                    Size = new Size(800, 8),
+                    CornerRadius = 4.0f,
+                    BackgroundColor = new Selector<Color>()
+                    {
+                        //Normal = new Color("#FF6200"),
+                        Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+                        Disabled = new Color("#CACACA"),
+                    },
+                },
+                Thumb = new ImageViewStyle()
+                {
+                    Size = new Size(24, 24),
+                    BackgroundColor = new Selector<Color>()
+                    {
+                        Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+                        Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR1,
+                        Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+                        Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+                    },
+                    BorderlineWidth = 5.0f,
+                    BorderlineColorSelector = new Selector<Color>()
+                    {
+                        Normal = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR2,
+                        Pressed = Styles.BIG_TAG_NORMAL_BACKGROUND_COLOR2,
+                        Focused = Styles.BIG_TAG_FOCUS_BACKGROUND_COLOR1,
+                        Disabled = new Color(0.94f, 0.95f, 0.93f, 1.0f),
+                    },
+                    CornerRadius = new Vector4(0.5f, 0.5f, 0.5f, 0.5f),
+                    CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+                },
+                ValueIndicatorImage = new ImageViewStyle()
+                {
+                    Size = new Size(43, 40),
+                    BorderlineWidth = 1.0f,
+                    BorderlineColor = new Color("#FF6200"),
+                    BackgroundColor = new Color(1.0f, 1.0f, 1.0f, 0.0f),
+                    CornerRadius = 12.0f,
+                },
+                ValueIndicatorText = new TextLabelStyle()
+                {
+                    SizeHeight = 24,
+                    PixelSize = 16,
+                    TextColor = new Color("#FF6200"),
+                }
+            };
+        }
+
+        private void MakeControlPannelComponents()
+        {
+            AddControlEditor();
+            AddControlButton("ChangeAvatar", "button_avatar", mainScene.ChangeAvatar);
+            AddControlButton("Random Animation", "button_bvh", mainScene.StartRandomAnimation);
+            AddControlButton("EyeBlink", "button_bvh", mainScene.EyeBlink);
+
+            AddControlButton("TTS Init", "button_avatar", mainScene.InintTTsTest);
+            AddControlButton("TTS Start", "button_avatar", mainScene.StartTTSTest);
+            AddControlButton("TTS Stop", "button_avatar", mainScene.StopTTSTest);
+            AddControlButton("CZ", "button_avatar", mainScene.StartAvatarTalk_1);
+            AddControlButton("DK", "button_avatar", mainScene.StartAvatarTalk_2);
+            AddControlButton("GR", "button_avatar", mainScene.StartAvatarTalk_3);
+            AddControlButton("Start Mic", "button_bvh", mainScene.StartMic);
+            AddControlButton("Stop Mic", "button_bvh", mainScene.StopMic);
+            AddControlButton("Show/Hide", "button_bvh", mainScene.ShowHide);
+            AddControlSlider("Camera FOV", "slider_camera_fov", 0.1f, 1.3f, mainScene.GetSelectedCamera().FieldOfView.ConvertToFloat(), mainScene.SetupSceneViewCameraFov);
+            AddControlSlider("IBL intensity", "slider_ibl_factor", 0.1f, 0.8f, mainScene.IBLFactor, mainScene.SetupSceneViewIBLFactor);
+            AddControlButton("Quit", "button_quit", Exit);
+        }
+
+        private void AddControlEditor()
+        {
+            editor = new TextField()
+            {
+                Text = Utils.TTSText,
+                PlaceholderText = "Input Your Message",
+                Name = "InputText",
+                BackgroundColor = Color.White,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+            };
+            editor.TextChanged += (obj, e) => {
+                Utils.TTSText = e.TextField.Text;
+            };
+
+            controlPannel.Add(editor);
+        }
+
+        private void AddControlButton(string buttonText, string buttonName, Action func)
+        {
+            var button = new Button(buttonStyle)
+            {
+                Text = buttonText,
+                Name = buttonName,
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+            };
+            button.Clicked += (o, e) =>
+            {
+                Button me = o as Button;
+                if (me == null) return;
+
+                func();
+            };
+
+            controlPannel.Add(button);
+        }
+
+        private Slider AddControlSlider(string sliderText, string sliderName, float minValue, float maxValue, float currentValue, Action<float> func)
+        {
+            var dummy = new View()
+            {
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                Layout = new LinearLayout()
+                {
+                    LinearOrientation = LinearLayout.Orientation.Horizontal,
+                    HorizontalAlignment = HorizontalAlignment.Begin,
+                    VerticalAlignment = VerticalAlignment.Center,
+                    CellPadding = new Size2D(5, 0),
+                },
+            };
+            var label = new TextLabel()
+            {
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                Text = sliderText,
+                FontSizeScale = 0.5f,
+                Focusable = false,
+                Weight = 0.2f,
+                MultiLine = true,
+            };
+            dummy.Add(label);
+            var slider = new Slider(sliderStyle)
+            {
+                WidthSpecification = LayoutParamPolicies.MatchParent,
+                MinValue = minValue,
+                MaxValue = maxValue,
+                CurrentValue = currentValue,
+                Name = sliderName,
+                Weight = 0.8f,
+            };
+            slider.ValueChanged += (o, e) =>
+            {
+                func(e.CurrentValue);
+            };
+            dummy.Add(slider);
+            controlPannel.Add(dummy);
+
+            return slider;
+        }
+
+        private void Exit()
+        {
+            OnExitButtonClicked?.Invoke(null, null);
+        }
+    }
+}
diff --git a/test/Tizen.AIAvatar.Example/src/SampleMain.cs b/test/Tizen.AIAvatar.Example/src/SampleMain.cs
new file mode 100644 (file)
index 0000000..31dc70a
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 Tizen.AIAvatar;
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.Security;
+
+using Tizen;
+using Tizen.Multimedia;
+
+namespace AIAvatar
+{
+    partial class Program : NUIApplication
+    {
+        private Window window;
+        private Tizen.NUI.Size windowSize;
+        private AvatarScene avatarScene;
+        private View circleMaskView;
+
+        private UIControlPanel uiControlPanel;
+
+        protected override void OnCreate()
+        {
+            base.OnCreate();
+            Initialize();
+        }
+
+        private void Initialize()
+        {
+            //var asyncAudioCapture = new AsyncAudioCapture(24000, AudioChannel.Mono, AudioSampleType.S16Le);
+
+            window = NUIApplication.GetDefaultWindow();
+            window.BackgroundColor = new Color(0.85f, 0.85f, 0.85f, 1.0f); ;
+
+            window.AddAvailableOrientation(Window.WindowOrientation.Landscape);
+            window.AddAvailableOrientation(Window.WindowOrientation.Portrait);
+            window.AddAvailableOrientation(Window.WindowOrientation.LandscapeInverse);
+            window.AddAvailableOrientation(Window.WindowOrientation.PortraitInverse);
+
+            window.Resized += OnResizedEvent;
+            window.KeyEvent += OnKeyEvent;
+
+            MakeBackground();
+            MakeAvatarScene();
+            MakeUIPanel();
+
+            RecalculatePositionSizeFromWindowSize();
+        }
+
+        private void OnResizedEvent(object sender, Window.ResizedEventArgs e)
+        {
+            RecalculatePositionSizeFromWindowSize();
+        }
+
+        private void MakeBackground()
+        {
+            var backgroundView = new View()
+            {
+                WidthResizePolicy = ResizePolicyType.FillToParent,
+                HeightResizePolicy = ResizePolicyType.FillToParent,
+                BackgroundImage = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/UI_BG.png",
+            };
+            backgroundView.TouchEvent += (o, e) =>
+            {
+                if (e.Touch.GetState(0) == PointStateType.Down)
+                {
+                    Tizen.Log.Error("NUI", $"Background touched.\n");
+                }
+                return true;
+            };
+            window.Add(backgroundView);
+        }
+
+        private void MakeAvatarScene()
+        {
+            avatarScene = new AvatarScene();
+            avatarScene.UseFramebuffer = true;
+
+            avatarScene.SetAlphaMaskUrl(Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/contact-cards-mask.png");
+            avatarScene.EnableCropToMask(false);
+            //window.Add(avatarScene);
+            
+            circleMaskView = new View()
+            {
+                Size = new Tizen.NUI.Size(),
+                Position = new Position(0, 0),
+                BackgroundColor = new Tizen.NUI.Color(0.0f, 0.0f, 0.0f, 0.0f),//new Tizen.NUI.Color("#221F1F"),
+            };
+            window.Add(circleMaskView);
+            circleMaskView.Add(avatarScene);
+
+            //circleMaskView.ClippingMode = ClippingModeType.ClipChildren;
+            //circleMaskView.CornerRadius = new Vector4(0.5f,0.5f,0.5f,0.5f);
+            circleMaskView.CornerRadiusPolicy = VisualTransformPolicyType.Relative;
+        }
+
+        private void MakeUIPanel()
+        {
+            uiControlPanel = new UIControlPanel();
+            uiControlPanel.OnExitButtonClicked += OnExitButtonClicked;
+            uiControlPanel.MakeControlPannel(window, avatarScene);
+        }
+
+        private void OnExitButtonClicked(object sender, EventArgs e)
+        {
+            ExitApplication();
+        }
+
+        private void RecalculatePositionSizeFromWindowSize()
+        {
+            uiControlPanel.ReizeUIPanel(avatarScene, circleMaskView);
+        }
+
+        private void OnKeyEvent(object sender, Window.KeyEventArgs e)
+        {
+            
+            if (e.Key.State != Key.StateType.Down)
+            {
+                return;
+            }
+
+            if (e.Key.KeyPressedName == "Escape" || e.Key.KeyPressedName == "XF86Back" || e.Key.KeyPressedName == "BackSpace")
+            {
+                ExitApplication();
+            }
+        }
+
+        private void ExitApplication()
+        {
+            Utils.FullGC();
+            Exit();
+        }
+
+        static void Main(string[] args)
+        {
+            var app = new Program();
+            app.Run(args);
+        }
+    }
+}
diff --git a/test/Tizen.AIAvatar.Example/src/Styles.cs b/test/Tizen.AIAvatar.Example/src/Styles.cs
new file mode 100644 (file)
index 0000000..ca0e7e3
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 Tizen.NUI;
+
+namespace AIAvatar
+{
+    public static class Styles
+    {
+        public static readonly Color BIG_TAG_NORMAL_BACKGROUND_COLOR1 = new Color(0xFF / 255.0f, 0xFE / 255.0f, 0xFE / 255.0f, 0.8f);
+        public static readonly Color BIG_TAG_NORMAL_BACKGROUND_COLOR2 = new Color(0xDB / 255.0f, 0xE3 / 255.0f, 0xFF / 255.0f, 0.8f);
+        public static readonly Color BIG_TAG_FOCUS_BACKGROUND_COLOR1 = new Color(0xD9 / 255.0f, 0xE1 / 255.0f, 0xFF / 255.0f, 0.8f);
+        public static readonly Color BIG_TAG_FOCUS_BACKGROUND_COLOR2 = new Color(0xB2 / 255.0f, 0xC4 / 255.0f, 0xFF / 255.0f, 0.8f);
+        public static readonly Color BIG_TAG_NORMAL_SHADOW_COLOR = new Color(62.0f / 255.0f, 62.0f / 255.0f, 62.0f / 255.0f, 0.8f);
+    }
+}
diff --git a/test/Tizen.AIAvatar.Example/src/Utils.cs b/test/Tizen.AIAvatar.Example/src/Utils.cs
new file mode 100644 (file)
index 0000000..4eff516
--- /dev/null
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AIAvatar
+{
+    public static class Utils
+    {
+        static public string LogTag = "AvatarSample";
+        static public string TTSText = "Select an avatar that will guide you through the functions of your age.";
+        static public string ResourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
+        static public void FullGC()
+        {
+            global::System.GC.Collect();
+            global::System.GC.WaitForPendingFinalizers();
+            global::System.GC.Collect();
+        }
+    }
+}
diff --git a/test/Tizen.AIAvatar.Example/tizen-manifest.xml b/test/Tizen.AIAvatar.Example/tizen-manifest.xml
new file mode 100644 (file)
index 0000000..106b7a3
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="7.0" package="AIAvatarSample" version="1.0.0">
+  <profile name="common" />
+  <ui-application appid="AIAvatar.Sample"
+                                        exec="AIAvatarSample.dll"
+                                        type="dotnet"
+                                        multiple="false"
+                                        taskmanage="true"
+                                        nodisplay="false"
+                                        launch_mode="single"
+                    api-version="10">
+    <label>Tizen.AIAvatar</label>
+    <icon>Tizen.AIAvatar.png</icon>
+    <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+  </ui-application>
+  <privileges>
+      <privilege>http://tizen.org/privilege/internet</privilege>
+      <privilege>http://tizen.org/privilege/window.priority.set</privilege>
+      <privilege>http://tizen.org/privilege/filesystem.read</privilege>
+      <privilege>http://tizen.org/privilege/recorder</privilege>
+  </privileges>
+</manifest>
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/VisualTest.cs b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/VisualTest.cs
new file mode 100755 (executable)
index 0000000..2f24312
--- /dev/null
@@ -0,0 +1,395 @@
+using System.Net.Mime;
+using Tizen.NUI.BaseComponents;
+using System.Collections.Generic;
+
+namespace Tizen.NUI.Samples
+{
+    public class VisualTest : IExample
+    {
+        Window window = null;
+
+        static private string DEMO_IMAGE_DIR = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "images/";
+
+        static private readonly string focusIndicatorImageUrl = DEMO_IMAGE_DIR + "i_focus_stroke_tile_2unit.9.webp";
+        static private readonly string[] ImageUrlList = {
+            DEMO_IMAGE_DIR + "Dali/DaliDemo/application-icon-1.png",
+            DEMO_IMAGE_DIR + "Dali/DaliDemo/application-icon-6.png",
+            DEMO_IMAGE_DIR + "Dali/DaliDemo/Kid1.svg",
+            DEMO_IMAGE_DIR + "Dali/ContactCard/gallery-small-2.jpg",
+        };
+
+        const float viewSizeWidth = 320.0f;
+        const float viewSizeHeight = 280.0f;
+        const float thumbnailAreaHeight = 96.0f;
+        const float thumbnailSize = 72.0f;
+        const float viewGap = 24.0f;
+
+        View rootView;
+
+        View[] visualViewList = new View[4];
+
+        Visuals.NPatchVisual focusIndicatorVisual;
+        Visuals.ColorVisual shadowVisual1;
+        Visuals.ColorVisual shadowVisual2;
+
+        public void Activate()
+        {
+            FocusManager.Instance.EnableDefaultAlgorithm(true);
+
+            // To use custom indicator, in this sample, removed default indicator
+            FocusManager.Instance.FocusIndicator = null;
+
+            window = NUIApplication.GetDefaultWindow();
+
+            CreateFocusedVisuals();
+
+            CreateLayoutViews();
+
+            window.KeyEvent += WinKeyEvent;
+        }
+
+        private void WinKeyEvent(object sender, Window.KeyEventArgs e)
+        {
+            if (e.Key.State == Key.StateType.Down)
+            {
+                if (e.Key.KeyPressedName == "1")
+                {
+                    Tizen.Log.Error("NUI", $"Reset scene\n");
+
+                    rootView?.Unparent();
+                    rootView?.Dispose();
+
+                    focusIndicatorVisual?.Dispose();
+                    shadowVisual1?.Dispose();
+                    shadowVisual2?.Dispose();
+
+                    CreateFocusedVisuals();
+
+                    CreateLayoutViews();
+                }
+                else if(e.Key.KeyPressedName == "2")
+                {
+                    Tizen.Log.Error("NUI", $"GC test\n");
+                    FullGC();
+                }
+                else if(e.Key.KeyPressedName == "3")
+                {
+                    View focusedView = FocusManager.Instance.GetCurrentFocusView();
+                    if(focusedView != null)
+                    {
+                        float tmp = focusedView.SizeWidth;
+                        focusedView.SizeWidth = focusedView.SizeHeight * 2.0f;
+                        focusedView.SizeHeight = tmp / 2.0f;
+                    }
+                }
+                else if(e.Key.KeyPressedName == "4")
+                {
+                    focusIndicatorVisual.ResourceUrl = null;
+                }
+                else if(e.Key.KeyPressedName == "5")
+                {
+                    focusIndicatorVisual.ResourceUrl = focusIndicatorImageUrl;
+                }
+            }
+        }
+
+        public void Deactivate()
+        {
+            window.KeyEvent -= WinKeyEvent;
+
+            FocusManager.Instance.EnableDefaultAlgorithm(false);
+            rootView?.Unparent();
+            rootView?.Dispose();
+
+            focusIndicatorVisual?.Dispose();
+            shadowVisual1?.Dispose();
+            shadowVisual2?.Dispose();
+        }
+
+        private void FullGC()
+        {
+            global::System.GC.Collect();
+            global::System.GC.WaitForPendingFinalizers();
+            global::System.GC.Collect();
+        }
+
+        private void CreateFocusedVisuals()
+        {
+            focusIndicatorVisual = new Visuals.NPatchVisual()
+            {
+                Name = "indicator",
+
+                ResourceUrl = focusIndicatorImageUrl,
+
+                Origin = Visual.AlignType.Center,
+                PivotPoint = Visual.AlignType.Center,
+
+                Width = 1.01f,
+                Height = 1.01f,
+
+                BorderOnly = true,
+            };
+            shadowVisual1 = new Visuals.ColorVisual()
+            {
+                Name = "shadow1",
+
+                Color = new Color(0.0f, 0.0f, 0.0f, 0.2f),
+
+                CornerRadius = new Vector4(16.0f, 16.0f, 16.0f, 16.0f),
+
+                OffsetX = 2.0f,
+                OffsetY = 5.0f,
+                Width = 1.03f,
+                Height = 1.01f,
+
+                OffsetXPolicy = VisualTransformPolicyType.Absolute,
+                OffsetYPolicy = VisualTransformPolicyType.Absolute,
+            };
+            shadowVisual2 = new Visuals.ColorVisual()
+            {
+                Name = "shadow2",
+
+                Color = new Color(0.0f, 0.0f, 0.0f, 0.3f),
+
+                CornerRadius = new Vector4(16.0f, 16.0f, 16.0f, 16.0f),
+
+                OffsetX = 2.0f,
+                OffsetY = 5.0f,
+                Width = 1.01f,
+                Height = 1.0f,
+
+                OffsetXPolicy = VisualTransformPolicyType.Absolute,
+                OffsetYPolicy = VisualTransformPolicyType.Absolute,
+            };
+        }
+
+        private void CreateLayoutViews()
+        {
+            rootView = new View()
+            {
+                Name = "rootView",
+                BackgroundColor = new Color("#7c9df0"),
+
+                WidthResizePolicy = ResizePolicyType.FillToParent,
+                HeightResizePolicy = ResizePolicyType.FillToParent,
+            };
+
+            for(int i=0; i<4; i++)
+            {
+                visualViewList[i] = CreateViewWithVisual(i);
+                rootView.Add(visualViewList[i]);
+            }
+
+            window.Add(rootView);
+        }
+
+        private View CreateViewWithVisual(int id)
+        {
+            View view = new View()
+            {
+                Name = $"ID{id}",
+
+                SizeWidth = viewSizeWidth,
+                SizeHeight = viewSizeHeight,
+                PositionX = viewGap + (id % 2) * (viewSizeWidth + viewGap),
+                PositionY = viewGap + (id / 2) * (viewSizeHeight + viewGap),
+
+                Focusable = true,
+                FocusableInTouch = true,
+                KeyInputFocus = true,
+
+                Padding = new Extents(10, 10, 10, (ushort)thumbnailAreaHeight),
+            };
+
+            Visuals.ColorVisual backgroundVisual = new Visuals.ColorVisual()
+            {
+                Name = "background",
+
+                Color = Color.Gray,
+
+                CornerRadius = new Vector4(16.0f, 16.0f, 16.0f, 16.0f),
+            };
+
+            Visuals.ImageVisual imageVisual = new Visuals.ImageVisual()
+            {
+                Name = "mainImage",
+
+                ResourceUrl = ImageUrlList[id % 4],
+
+                CornerRadius = new Vector4(16.0f, 16.0f, 0.0f, 0.0f),
+
+                FittingMode = VisualFittingModeType.FitKeepAspectRatio,
+            };
+
+            Visuals.ColorVisual foregroundVisual = new Visuals.ColorVisual()
+            {
+                Name = "foreground",
+
+                Color = Color.LightSlateGray,
+                Opacity = 0.5f,
+
+                CornerRadius = new Vector4(0.0f, 0.0f, 16.0f, 16.0f),
+
+                Origin = Visual.AlignType.BottomBegin,
+                PivotPoint = Visual.AlignType.BottomBegin,
+
+                Height = thumbnailAreaHeight,
+
+                HeightPolicy = VisualTransformPolicyType.Absolute,
+            };
+
+            Visuals.TextVisual textVisual = new Visuals.TextVisual()
+            {
+                Name = "text",
+
+                Text = $"View {id} long text long long long long looooong",
+                TextColor = Color.White,
+                PointSize = 20.0f,
+
+                Origin = Visual.AlignType.BottomBegin,
+                PivotPoint = Visual.AlignType.BottomBegin,
+                
+                HorizontalAlignment = HorizontalAlignment.Begin,
+                VerticalAlignment = VerticalAlignment.Center,
+
+                OffsetX = thumbnailAreaHeight,
+                OffsetY = -thumbnailAreaHeight * 0.4f,
+                Width = viewSizeWidth - thumbnailAreaHeight,
+                Height = thumbnailAreaHeight * 0.6f,
+
+                OffsetXPolicy = VisualTransformPolicyType.Absolute,
+                OffsetYPolicy = VisualTransformPolicyType.Absolute,
+                WidthPolicy = VisualTransformPolicyType.Absolute,
+                HeightPolicy = VisualTransformPolicyType.Absolute,
+            };
+
+            Visuals.AdvancedTextVisual subTextVisual = new Visuals.AdvancedTextVisual()
+            {
+                Name = "subtext",
+
+                Text = "subtext",
+                TextColor = Color.White,
+                PointSize = 12.0f,
+
+                Origin = Visual.AlignType.BottomBegin,
+                PivotPoint = Visual.AlignType.BottomBegin,
+                
+                HorizontalAlignment = HorizontalAlignment.Begin,
+                VerticalAlignment = VerticalAlignment.Center,
+
+                OffsetX = thumbnailAreaHeight,
+                Width = viewSizeWidth - thumbnailAreaHeight,
+                Height = thumbnailAreaHeight * 0.4f,
+
+                OffsetXPolicy = VisualTransformPolicyType.Absolute,
+                WidthPolicy = VisualTransformPolicyType.Absolute,
+                HeightPolicy = VisualTransformPolicyType.Absolute,
+            };
+
+            try
+            {
+                subTextVisual.TranslatableText = "SID_OK";
+            }
+            catch(global::System.Exception e)
+            {
+                subTextVisual.Text = e.Message;
+            }
+
+            Visuals.ImageVisual thumbnailVisual = new Visuals.ImageVisual()
+            {
+                Name = "thumbnailImage",
+
+                ResourceUrl = ImageUrlList[id % 4],
+
+                CornerRadius = new Vector4(8.0f, 8.0f, 8.0f, 8.0f),
+
+                Origin = Visual.AlignType.BottomBegin,
+                PivotPoint = Visual.AlignType.Center,
+
+                OffsetX = thumbnailAreaHeight / 2.0f,
+                OffsetY = -thumbnailAreaHeight / 2.0f,
+
+                Width = thumbnailSize,
+                Height = thumbnailSize,
+
+                DesiredWidth = (int)thumbnailSize,
+                DesiredHeight = (int)thumbnailSize,
+
+                OffsetXPolicy = VisualTransformPolicyType.Absolute,
+                OffsetYPolicy = VisualTransformPolicyType.Absolute,
+                WidthPolicy = VisualTransformPolicyType.Absolute,
+                HeightPolicy = VisualTransformPolicyType.Absolute,
+            };
+
+            view.AddVisual(backgroundVisual);
+            view.AddVisual(imageVisual);
+            view.AddVisual(foregroundVisual);
+            view.AddVisual(textVisual);
+            view.AddVisual(subTextVisual);
+            view.AddVisual(thumbnailVisual);
+
+            view.FocusGained += (s, e) =>
+            {
+                View me = s as View;
+                if(me != null)
+                {
+                    Tizen.Log.Error("NUI", $"View {me.ID} focus gained.\n");
+
+                    me.AddVisual(focusIndicatorVisual);
+                    me.AddVisual(shadowVisual1);
+                    me.AddVisual(shadowVisual2);
+
+                    focusIndicatorVisual.RaiseToTop();
+                    shadowVisual1.LowerToBottom();
+                    shadowVisual2.LowerBelow(shadowVisual1);
+
+                    var visual = me.FindVisualByName("foreground");
+                    visual.Color = Color.LightGray;
+                    visual.Opacity = 0.5f;
+
+                    //visual = me.FindVisualByName("background");
+                    visual = me.GetVisualAt(2u); // Should be background
+                    visual.Color = Color.White;
+
+                    var textVisual = me.FindVisualByName("text") as Visuals.TextVisual;
+                    textVisual.TextColor = Color.Black;
+                    textVisual = me.FindVisualByName("subtext") as Visuals.TextVisual;
+                    textVisual.TextColor = Color.Black;
+                }
+            };
+
+            view.FocusLost += (s, e) =>
+            {
+                View me = s as View;
+                if(me != null)
+                {
+                    Tizen.Log.Error("NUI", $"View {me.ID} focus losted.\n");
+
+                    me.RemoveVisual(focusIndicatorVisual);
+                    me.RemoveVisual(shadowVisual1);
+                    me.RemoveVisual(shadowVisual2);
+
+                    var visual = me.FindVisualByName("foreground");
+                    visual.Color = Color.LightSlateGray;
+                    visual.Opacity = 0.5f;
+
+                    //visual = me.FindVisualByName("background");
+                    visual = me.GetVisualAt(0u); // Should be background
+                    visual.Color = Color.Gray;
+
+                    var textVisual = me.FindVisualByName("text") as Visuals.TextVisual;
+                    textVisual.TextColor = Color.White;
+                    textVisual = me.FindVisualByName("subtext") as Visuals.TextVisual;
+                    textVisual.TextColor = Color.White;
+                }
+            };
+
+            view.TouchEvent += (o, e) =>
+            {
+                return true;
+            };
+
+            return view;
+        }
+    }
+}
diff --git a/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/i_focus_stroke_tile_2unit.9.webp b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/i_focus_stroke_tile_2unit.9.webp
new file mode 100644 (file)
index 0000000..f1224f5
Binary files /dev/null and b/test/Tizen.NUI.Samples/Tizen.NUI.Samples/res/images/i_focus_stroke_tile_2unit.9.webp differ
diff --git a/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.code-workspace b/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.code-workspace
new file mode 100644 (file)
index 0000000..f12d910
--- /dev/null
@@ -0,0 +1,14 @@
+{
+       "folders": [
+               {
+                       "path": "."
+               },
+               {
+                       "path": "../../src/Tizen.NUI"
+               },
+               {
+                       "path": "../../src/Tizen.NUI.Components"
+               }
+       ],
+       "settings": {}
+}
diff --git a/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.csproj b/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.csproj
new file mode 100644 (file)
index 0000000..37a222e
--- /dev/null
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>net6.0</TargetFramework>
+        <AssemblyName>VisualTest</AssemblyName>
+    </PropertyGroup>
+
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+        <DebugType>portable</DebugType>
+    </PropertyGroup>
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+        <DebugType>None</DebugType>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Tizen.NET.Sdk" Version="1.0.9" />
+        <ProjectReference Include="../../src/Tizen/Tizen.csproj" />
+        <ProjectReference Include="../../src/Tizen.NUI/Tizen.NUI.csproj" />
+    </ItemGroup>
+
+    <PropertyGroup>
+        <NeedInjection>True</NeedInjection>
+    </PropertyGroup>
+</Project>
diff --git a/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.sln b/test/Tizen.NUI.VisualTest/Tizen.NUI.VisualTest.sln
new file mode 100755 (executable)
index 0000000..18e7096
--- /dev/null
@@ -0,0 +1,91 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.33214.272
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.NUI.VisualTest", "Tizen.NUI.VisualTest.csproj", "{3C6CE4CE-9D35-42C9-B23D-BBFFA96B3955}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen", "..\..\src\Tizen\Tizen.csproj", "{F4ADAF15-01AA-477E-A85A-BEB297E6B07E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.Common", "..\..\src\Tizen.Applications.Common\Tizen.Applications.Common.csproj", "{0B96B17C-DACA-4745-88B1-6CFC1825A510}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.ComponentBased", "..\..\src\Tizen.Applications.ComponentBased\Tizen.Applications.ComponentBased.csproj", "{E117D074-C23D-41FD-A77D-2E9E6FF85676}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Applications.ThemeManager", "..\..\src\Tizen.Applications.ThemeManager\Tizen.Applications.ThemeManager.csproj", "{FB8B42D6-76CC-4836-8A80-58A816C6A17F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.System.SystemSettings", "..\..\src\Tizen.System.SystemSettings\Tizen.System.SystemSettings.csproj", "{EC28F259-C790-4FA3-A834-00795E2A7E2F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.System.Information", "..\..\src\Tizen.System.Information\Tizen.System.Information.csproj", "{02BEE3AD-99A6-44A5-89FC-D9F4132D9ECE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.System.Feedback", "..\..\src\Tizen.System.Feedback\Tizen.System.Feedback.csproj", "{D422D03E-7E32-4230-8306-B16DFE27E95A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Tracer", "..\..\src\Tizen.Tracer\Tizen.Tracer.csproj", "{A0AA9346-2025-4803-A168-3B39A367BB92}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.Log", "..\..\src\Tizen.Log\Tizen.Log.csproj", "{1E8250DB-92C3-44A5-8D57-3CFDE0C0021D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.NUI", "..\..\src\Tizen.NUI\Tizen.NUI.csproj", "{29B426DA-FFDE-49D2-BD73-FE155F9502E8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tizen.NUI.Components", "..\..\src\Tizen.NUI.Components\Tizen.NUI.Components.csproj", "{2A669CBF-DFA8-4EA3-852D-3137493DE884}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {3C6CE4CE-9D35-42C9-B23D-BBFFA96B3955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {3C6CE4CE-9D35-42C9-B23D-BBFFA96B3955}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {3C6CE4CE-9D35-42C9-B23D-BBFFA96B3955}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {3C6CE4CE-9D35-42C9-B23D-BBFFA96B3955}.Release|Any CPU.Build.0 = Release|Any CPU
+               {F4ADAF15-01AA-477E-A85A-BEB297E6B07E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {F4ADAF15-01AA-477E-A85A-BEB297E6B07E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {F4ADAF15-01AA-477E-A85A-BEB297E6B07E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {F4ADAF15-01AA-477E-A85A-BEB297E6B07E}.Release|Any CPU.Build.0 = Release|Any CPU
+               {0B96B17C-DACA-4745-88B1-6CFC1825A510}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {0B96B17C-DACA-4745-88B1-6CFC1825A510}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {0B96B17C-DACA-4745-88B1-6CFC1825A510}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {0B96B17C-DACA-4745-88B1-6CFC1825A510}.Release|Any CPU.Build.0 = Release|Any CPU
+               {E117D074-C23D-41FD-A77D-2E9E6FF85676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {E117D074-C23D-41FD-A77D-2E9E6FF85676}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {E117D074-C23D-41FD-A77D-2E9E6FF85676}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {E117D074-C23D-41FD-A77D-2E9E6FF85676}.Release|Any CPU.Build.0 = Release|Any CPU
+               {FB8B42D6-76CC-4836-8A80-58A816C6A17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {FB8B42D6-76CC-4836-8A80-58A816C6A17F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {FB8B42D6-76CC-4836-8A80-58A816C6A17F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {FB8B42D6-76CC-4836-8A80-58A816C6A17F}.Release|Any CPU.Build.0 = Release|Any CPU
+               {EC28F259-C790-4FA3-A834-00795E2A7E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {EC28F259-C790-4FA3-A834-00795E2A7E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {EC28F259-C790-4FA3-A834-00795E2A7E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {EC28F259-C790-4FA3-A834-00795E2A7E2F}.Release|Any CPU.Build.0 = Release|Any CPU
+               {02BEE3AD-99A6-44A5-89FC-D9F4132D9ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {02BEE3AD-99A6-44A5-89FC-D9F4132D9ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {02BEE3AD-99A6-44A5-89FC-D9F4132D9ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {02BEE3AD-99A6-44A5-89FC-D9F4132D9ECE}.Release|Any CPU.Build.0 = Release|Any CPU
+               {D422D03E-7E32-4230-8306-B16DFE27E95A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {D422D03E-7E32-4230-8306-B16DFE27E95A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {D422D03E-7E32-4230-8306-B16DFE27E95A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {D422D03E-7E32-4230-8306-B16DFE27E95A}.Release|Any CPU.Build.0 = Release|Any CPU
+               {A0AA9346-2025-4803-A168-3B39A367BB92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {A0AA9346-2025-4803-A168-3B39A367BB92}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {A0AA9346-2025-4803-A168-3B39A367BB92}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {A0AA9346-2025-4803-A168-3B39A367BB92}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1E8250DB-92C3-44A5-8D57-3CFDE0C0021D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {1E8250DB-92C3-44A5-8D57-3CFDE0C0021D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1E8250DB-92C3-44A5-8D57-3CFDE0C0021D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {1E8250DB-92C3-44A5-8D57-3CFDE0C0021D}.Release|Any CPU.Build.0 = Release|Any CPU
+               {29B426DA-FFDE-49D2-BD73-FE155F9502E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {29B426DA-FFDE-49D2-BD73-FE155F9502E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {29B426DA-FFDE-49D2-BD73-FE155F9502E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {29B426DA-FFDE-49D2-BD73-FE155F9502E8}.Release|Any CPU.Build.0 = Release|Any CPU
+               {2A669CBF-DFA8-4EA3-852D-3137493DE884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {2A669CBF-DFA8-4EA3-852D-3137493DE884}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {2A669CBF-DFA8-4EA3-852D-3137493DE884}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {2A669CBF-DFA8-4EA3-852D-3137493DE884}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+               SolutionGuid = {355D568D-D02A-490A-A6AC-FD6C7D97457A}
+       EndGlobalSection
+EndGlobal
diff --git a/test/Tizen.NUI.VisualTest/VisualTest.cs b/test/Tizen.NUI.VisualTest/VisualTest.cs
new file mode 100644 (file)
index 0000000..61d7dad
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * 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 Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Constants;
+
+class VisualTestExample : NUIApplication
+{
+    const uint VIEW_COUNT = 1000;
+
+    static string IMAGE_DIR = Tizen.Applications.Application.Current.DirectoryInfo.Resource + "image/";
+    readonly static string[] IMAGE_PATH = 
+    {
+        IMAGE_DIR + "gallery-small-1.jpg",
+    };
+
+    Window mWindow;
+    Vector2 mWindowSize;
+    float mViewSize;
+
+    TextLabel infoLabel;
+
+    View rootView;
+
+    ImageView dummyView;
+    Animation dummyAnimation;
+
+    int state = 0;
+
+    private global::System.Random mRandom = new global::System.Random();
+
+    protected void CreateScene()
+    {
+        mWindow = Window.Instance;
+        mWindow.BackgroundColor = Color.White;
+        mWindowSize = mWindow.WindowSize;
+
+        mViewSize = global::System.Math.Min(mWindowSize.X, mWindowSize.Y) / 10.0f;
+
+        infoLabel = new TextLabel(
+            $"VisualObject Test with {VIEW_COUNT} and 2 children. (ImageView + TextLabel)\n" +
+            $"Press 1 key to start test with View. (ImageView + TextLabel)\n" +
+            $"Press 2 key to start test with Visual. (ImageVisual + TextLabel)\n" +
+            $"Press 3 key to start test with various Visuals. (ColorVisual + ImageVisual + TextVisual + BorderVisual)\n" +
+            $"Press 4 key to animate the size of View.\n" +
+            $"(View size : {mViewSize})\n")
+        {
+            MultiLine = true,
+            PointSize = 20.0f,
+        };
+
+        // For image cache.
+        dummyView = new ImageView()
+        {
+            ResourceUrl = IMAGE_PATH[0],
+            SizeWidth = 1.0f,
+            SizeHeight = 1.0f,
+        };
+
+        // For keep rendering forever
+        dummyAnimation = new Animation(1000);
+        dummyAnimation.AnimateTo(dummyView, "PositionX", 1.0f);
+        dummyAnimation.Looping = true;
+        dummyAnimation.Play();
+
+        rootView = new View()
+        {
+            SizeWidth = mWindowSize.X,
+            SizeHeight = mWindowSize.Y,
+            BackgroundColor = Color.White,
+        };
+        mWindow.Add(dummyView);
+        mWindow.Add(rootView);
+
+        mWindow.GetOverlayLayer().Add(infoLabel);
+
+        mWindow.KeyEvent += OnKeyEvent;
+    }
+
+    void NormalViewTest()
+    {
+        global::System.DateTime startTime;
+        global::System.DateTime endTime;
+
+        startTime = global::System.DateTime.Now;
+
+        for(int i = 0; i < VIEW_COUNT; i++)
+        {
+            View view = new View()
+            {
+                SizeWidth = mViewSize,
+                SizeHeight = mViewSize,
+                PositionX = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.X - mViewSize),
+                PositionY = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.Y - mViewSize),
+            };
+            CreateNormalView(ref view);
+            rootView.Add(view);
+        }
+
+        endTime = global::System.DateTime.Now;
+
+        Tizen.Log.Error("NUI.Visuals", $"NormalViewTest : {(endTime - startTime).TotalMilliseconds} ms\n");
+    }
+
+    void VisualViewTest()
+    {
+        global::System.DateTime startTime;
+        global::System.DateTime endTime;
+
+        startTime = global::System.DateTime.Now;
+
+        for(int i = 0; i < VIEW_COUNT; i++)
+        {
+            View view = new View()
+            {
+                SizeWidth = mViewSize,
+                SizeHeight = mViewSize,
+                PositionX = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.X - mViewSize),
+                PositionY = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.Y - mViewSize),
+            };
+            CreateVisualView(ref view);
+            rootView.Add(view);
+        }
+
+        endTime = global::System.DateTime.Now;
+
+        Tizen.Log.Error("NUI.Visuals", $"VisualViewTest : {(endTime - startTime).TotalMilliseconds} ms\n");
+    }
+
+    void GeneralVisualTest()
+    {
+        global::System.DateTime startTime;
+        global::System.DateTime endTime;
+
+        startTime = global::System.DateTime.Now;
+
+        for(int i = 0; i < VIEW_COUNT; i++)
+        {
+            View view = new View()
+            {
+                SizeWidth = mViewSize,
+                SizeHeight = mViewSize,
+                PositionX = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.X - mViewSize),
+                PositionY = (mRandom.Next()%1000 / 999.0f) * (mWindowSize.Y - mViewSize),
+            };
+            CreateGeneralVisualView(ref view);
+            rootView.Add(view);
+        }
+
+        endTime = global::System.DateTime.Now;
+
+        Tizen.Log.Error("NUI.Visuals", $"GeneralVisualTest : {(endTime - startTime).TotalMilliseconds} ms\n");
+    }
+
+
+    void OnKeyEvent(object source, Window.KeyEventArgs e)
+    {
+        if (e.Key.State == Key.StateType.Down)
+        {
+            switch(e.Key.KeyPressedName)
+            {
+                case "Escape":
+                case "Back":
+                case "XF86Back":
+                {
+                    if(state == 0)
+                    {
+                        Deactivate();
+                        Exit();
+                    }
+                    else
+                    {
+                        state = 0;
+                        viewWidthChangeAnimation?.Stop();
+                        viewWidthChangeAnimation?.Clear();
+                        viewWidthChangeAnimation?.Dispose();
+                        viewWidthChangeAnimation = null;
+
+                        rootView?.Unparent();
+                        rootView?.DisposeRecursively();
+                        rootView = new View()
+                        {
+                            SizeWidth = mWindowSize.X,
+                            SizeHeight = mWindowSize.Y,
+                            BackgroundColor = Color.White,
+                        };
+                        mWindow.Add(rootView);
+                        FullGC();
+                        FullGC();
+                    }
+                    break;
+                }
+                case "1":
+                {
+                    if(state == 0)
+                    {
+                        state = 1;
+                        NormalViewTest();
+                    }
+                    break;
+                }
+                case "2":
+                {
+                    if(state == 0)
+                    {
+                        state = 2;
+                        VisualViewTest();
+                    }
+                    break;
+                }
+                case "3":
+                {
+                    if(state == 0)
+                    {
+                        state = 3;
+                        GeneralVisualTest();
+                    }
+                    break;
+                }
+                case "4":
+                {
+                    if(state != 0)
+                    {
+                        ViewSizeChangeTest();
+                    }
+                    break;
+                }
+                default:
+                {
+                    FullGC();
+                    FullGC();
+                    break;
+                }
+            }
+        }
+    }
+
+    public void Activate()
+    {
+        CreateScene();
+    }
+    public void FullGC()
+    {
+        global::System.GC.Collect();
+        global::System.GC.WaitForPendingFinalizers();
+        global::System.GC.Collect();
+    }
+
+    public void Deactivate()
+    {
+        DestroyScene();
+    }
+
+    private void DestroyScene()
+    {
+        rootView?.Unparent();
+        rootView?.Dispose();
+    }
+
+    private Animation viewWidthChangeAnimation = null;
+    private void ViewSizeChangeTest()
+    {
+        viewWidthChangeAnimation?.Stop();
+        viewWidthChangeAnimation?.Clear();
+        viewWidthChangeAnimation?.Dispose();
+        viewWidthChangeAnimation = new Animation(3000);
+        for(int i = 0; i < VIEW_COUNT; i++)
+        {
+            View view = rootView.GetChildAt((uint)i);
+
+            if(view == null)break;
+
+            viewWidthChangeAnimation.AnimateTo(view, "SizeWidth", mViewSize * 2.0f, 0, 1000);
+            viewWidthChangeAnimation.AnimateTo(view, "SizeWidth", mViewSize * 0.0f, 1000, 2000);
+            viewWidthChangeAnimation.AnimateTo(view, "SizeWidth", mViewSize * 1.0f, 2000, 3000);
+        }
+
+        viewWidthChangeAnimation.Play();
+    }
+
+    private void CreateNormalView(ref View view)
+    {
+        // ImageView with corner radius top.
+        ImageView imageView = new ImageView()
+        {
+            ResourceUrl = IMAGE_PATH[0],
+            CornerRadius = new Vector4(0.15f, 0.15f, 0.15f, 0.15f),
+            CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+
+            SizeWidth = mViewSize,
+            SizeHeight = mViewSize,
+        };
+
+        TextLabel textLabel = new TextLabel()
+        {
+            Text = "1",
+            PointSize = 20.0f,
+
+            SizeWidth = mViewSize,
+            SizeHeight = mViewSize,
+
+            PositionY = mViewSize * 0.25f,
+            PositionX = mViewSize * 0.5f,
+
+            ParentOrigin = Position.ParentOriginCenter,
+            PivotPoint = Position.PivotPointCenter,
+            PositionUsesPivotPoint = true,
+        };
+
+        view.Add(imageView);
+        view.Add(textLabel);
+    }
+
+    private void CreateVisualView(ref View view)
+    {
+        // ImageView with corner radius top.
+        Tizen.NUI.Visuals.ImageVisual imageVisual = new Tizen.NUI.Visuals.ImageVisual()
+        {
+            ResourceUrl = IMAGE_PATH[0],
+            CornerRadius = new Vector4(0.15f, 0.15f, 0.15f, 0.15f),
+            CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+
+            Width = mViewSize,
+            Height = mViewSize,
+
+            WidthPolicy = VisualTransformPolicyType.Absolute,
+            HeightPolicy = VisualTransformPolicyType.Absolute,
+        };
+
+        /*
+        Tizen.NUI.Visuals.TextVisual textVisual = new Tizen.NUI.Visuals.TextVisual()
+        {
+            Text = "1",
+            PointSize = 20.0f,
+
+            Width = mViewSize,
+            Height = mViewSize,
+
+            OffsetY = mViewSize * 0.25f,
+
+            Origin = Visual.AlignType.Center,
+            PivotPoint = Visual.AlignType.Center,
+
+            WidthPolicy = VisualTransformPolicyType.Absolute,
+            HeightPolicy = VisualTransformPolicyType.Absolute,
+            OffsetYPolicy = VisualTransformPolicyType.Absolute,
+        };
+        */
+
+        view.AddVisual(imageVisual);
+        //view.AddVisual(textVisual);
+
+        TextLabel textLabel = new TextLabel()
+        {
+            Text = "1",
+            PointSize = 20.0f,
+
+            SizeWidth = mViewSize,
+            SizeHeight = mViewSize,
+
+            PositionY = mViewSize * 0.25f,
+            PositionX = mViewSize * 0.5f,
+
+            ParentOrigin = Position.ParentOriginCenter,
+            PivotPoint = Position.PivotPointCenter,
+            PositionUsesPivotPoint = true,
+        };
+        view.Add(textLabel);
+    }
+
+    private void CreateGeneralVisualView(ref View view)
+    {
+        #region ColorVisual
+        Tizen.NUI.Visuals.ColorVisual colorVisual = new Tizen.NUI.Visuals.ColorVisual()
+        {
+            Color = new Color(1.0f, 0.0f, 0.0f, 0.9f),
+            CornerRadius = new Vector4(0.15f, 0.15f, 0.15f, 0.15f),
+            CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+
+            Width = 1.1f,
+            Height = 1.1f,
+
+            BlurRadius = mViewSize * 0.05f,
+            OffsetX = 0.1f,
+            OffsetY = 0.05f,
+
+            ExtraWidth = mViewSize * 0.15f,
+            ExtraHeight = -mViewSize * 0.05f,
+        };
+        view.AddVisual(colorVisual);
+        #endregion
+
+        #region ImageVisual
+        Tizen.NUI.Visuals.ImageVisual imageVisual = new Tizen.NUI.Visuals.ImageVisual()
+        {
+            ResourceUrl = IMAGE_PATH[0],
+            CornerRadius = new Vector4(0.15f, 0.15f, 0.15f, 0.15f),
+            CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+            BorderlineWidth = mViewSize * 0.05f,
+            BorderlineColor = Color.Green,
+            BorderlineOffset = 1.0f,
+
+            Width = mViewSize,
+            Height = mViewSize,
+
+            WidthPolicy = VisualTransformPolicyType.Absolute,
+            HeightPolicy = VisualTransformPolicyType.Absolute,
+        };
+        view.AddVisual(imageVisual);
+        #endregion
+
+        #region TextVisual
+        Tizen.NUI.Visuals.TextVisual textVisual = new Tizen.NUI.Visuals.TextVisual()
+        {
+            Text = "1",
+            PointSize = 20.0f,
+
+            Width = mViewSize,
+            Height = mViewSize,
+
+            OffsetY = mViewSize * 0.25f,
+
+            Origin = Visual.AlignType.Center,
+            PivotPoint = Visual.AlignType.Center,
+
+            WidthPolicy = VisualTransformPolicyType.Absolute,
+            HeightPolicy = VisualTransformPolicyType.Absolute,
+            OffsetYPolicy = VisualTransformPolicyType.Absolute,
+        };
+        view.AddVisual(textVisual);
+        #endregion
+
+        #region BorderVisual
+        Tizen.NUI.Visuals.BorderVisual borderVisual = new Tizen.NUI.Visuals.BorderVisual()
+        {
+            BorderColor = Color.Blue,
+            BorderWidth = mViewSize * 0.05f,
+            AntiAliasing = false, // For speed up
+        };
+        view.AddVisual(borderVisual);
+        #endregion
+    }
+
+    protected override void OnCreate()
+    {
+        // Up call to the Base class first
+        base.OnCreate();
+        Activate();
+    }
+
+    /// <summary>
+    /// The main entry point for the application.
+    /// </summary>
+    [global::System.STAThread] // Forces app to use one thread to access NUI
+    static void Main(string[] args)
+    {
+        VisualTestExample example = new VisualTestExample();
+        example.Run(args);
+    }
+}
diff --git a/test/Tizen.NUI.VisualTest/res/image/gallery-small-1.jpg b/test/Tizen.NUI.VisualTest/res/image/gallery-small-1.jpg
new file mode 100644 (file)
index 0000000..cac624f
Binary files /dev/null and b/test/Tizen.NUI.VisualTest/res/image/gallery-small-1.jpg differ
diff --git a/test/Tizen.NUI.VisualTest/shared/res/Tizen.NUI.VisualTest.png b/test/Tizen.NUI.VisualTest/shared/res/Tizen.NUI.VisualTest.png
new file mode 100644 (file)
index 0000000..ef30093
Binary files /dev/null and b/test/Tizen.NUI.VisualTest/shared/res/Tizen.NUI.VisualTest.png differ
diff --git a/test/Tizen.NUI.VisualTest/tizen-manifest.xml b/test/Tizen.NUI.VisualTest/tizen-manifest.xml
new file mode 100755 (executable)
index 0000000..7a85ab7
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="6" package="org.tizen.example.Tizen.NUI.VisualTest" version="1.0.0">
+  <profile name="common" />
+  <ui-application appid="org.tizen.example.Tizen.NUI.VisualTest"
+                                       exec="VisualTest.dll"
+                                       type="dotnet-nui"
+                                       multiple="false"
+                                       taskmanage="true"
+                                       nodisplay="false"
+                                       launch_mode="single"
+                                       >
+    <label>Tizen.NUI.VisualTest</label>
+    <icon>Tizen.NUI.VisualTest.png</icon>
+    <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+  </ui-application>
+</manifest>