[NUI] Add Say, PauseResume of Dali-ATSPI (#947)
authordongsug-song <35130733+dongsug-song@users.noreply.github.com>
Fri, 26 Jul 2019 02:15:44 +0000 (11:15 +0900)
committerGitHub <noreply@github.com>
Fri, 26 Jul 2019 02:15:44 +0000 (11:15 +0900)
src/Tizen.NUI/src/internal/Interop/Interop.Accessibility.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/Accessibility.cs [new file with mode: 0755]
test/NUITestSample/NUITestSample/examples/AtspiTest.cs [new file with mode: 0755]

diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.Accessibility.cs b/src/Tizen.NUI/src/internal/Interop/Interop.Accessibility.cs
new file mode 100755 (executable)
index 0000000..820028f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright(c) 2019 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.
+ *
+ */
+
+#if (NUI_DEBUG_ON)
+
+
+namespace Tizen.NUI
+{
+    internal static partial class Interop
+    {
+        internal static partial class Accessibility
+        {
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "csharp_dali_accessibility_get_status")]
+            public static extern bool accessibility_get_status(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "csharp_dali_accessibility_say")]
+            public static extern bool accessibility_say(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2, bool jarg3, global::System.IntPtr jarg4);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "csharp_dali_accessibility_pause_resume")]
+            public static extern void accessibility_pause_resume(global::System.Runtime.InteropServices.HandleRef jarg1, bool jarg2);
+        }
+    }
+}
+
+
+#endif
\ No newline at end of file
diff --git a/src/Tizen.NUI/src/public/Accessibility.cs b/src/Tizen.NUI/src/public/Accessibility.cs
new file mode 100755 (executable)
index 0000000..f772346
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright(c) 2019 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.
+ *
+ */
+
+#if (NUI_DEBUG_ON)
+
+
+using global::System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using Tizen.NUI.BaseComponents;
+#if (NUI_DEBUG_ON)
+using tlog = Tizen.Log;
+#endif
+
+namespace Tizen.NUI
+{
+    /// <summary>
+    /// Accessibility provides Dali-ATSPI interface which has funtionality of Screen-Reader and general accessibility
+    /// </summary>
+    // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public class Accessibility
+    {
+        #region Constructor, Distructor, Dispose
+        private Accessibility()
+        {
+            dummy = new View();
+            dummy.Name = "dali-atspi-singleton";
+        }
+        ~Accessibility()
+        {
+
+        }
+        #endregion Constructor, Distructor, Dispose
+
+
+        #region Property
+        /// <summary>
+        /// Instance for singleton
+        /// </summary>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static Accessibility Instance
+        {
+            get => _accessibility;
+        }
+        #endregion Property
+
+
+        #region Method
+        /// <summary>
+        /// Get the current status
+        /// </summary>
+        /// <returns>Current enabled status</returns>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        static public bool GetStatus()
+        {
+            return true;
+        }
+
+        /// <summary>
+        /// Start to speak
+        /// </summary>
+        /// <param name="sentence">Content to be spoken</param>
+        /// <param name="discardable">true to be stopped and discarded when other Say is triggered</param>
+        /// <returns></returns>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool Say(string sentence, bool discardable)
+        {
+            IntPtr callbackIntPtr = IntPtr.Zero;
+            if (_sayFinishedEventHandler != null)
+            {
+                _sayFinishedEventCallbackType callback = _sayFinishedEventCallback;
+                callbackIntPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(callback);
+            }
+            bool ret = Interop.Accessibility.accessibility_say(View.getCPtr(dummy), sentence, discardable, callbackIntPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        /// <summary>
+        /// To make Say be paused or resumed
+        /// </summary>
+        /// <param name="pause">true to be paused, false to be resumed</param>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void PauseResume(bool pause)
+        {
+            Interop.Accessibility.accessibility_pause_resume(View.getCPtr(dummy), pause);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+        #endregion Method
+
+
+        #region Event, Enum, Struct, ETC
+        /// <summary>
+        ///  Say Finished event arguments
+        /// </summary>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public class SayFinishedEventArgs : EventArgs
+        {
+            /// <summary>
+            /// The state of Say finished
+            /// </summary>
+            // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            public SayFinishedStates State
+            {
+                private set;
+                get;
+            }
+
+            internal SayFinishedEventArgs(int result)
+            {
+                State = (SayFinishedStates)(result);
+                tlog.Fatal(tag, $"SayFinishedEventArgs Constructor! State={State}");
+            }
+        }
+
+        /// <summary>
+        /// Enum of Say finished event argument status
+        /// </summary>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public enum SayFinishedStates
+        {
+            /// <summary>
+            /// Invalid
+            /// </summary>
+            // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Invalid = -1,
+            /// <summary>
+            /// Cancelled
+            /// </summary>
+            // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Cancelled = 1,
+            /// <summary>
+            /// Stopped
+            /// </summary>
+            // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Stopped = 2,
+            /// <summary>
+            /// Skipped
+            /// </summary>
+            // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+            [EditorBrowsable(EditorBrowsableState.Never)]
+            Skipped = 3
+        }
+
+        /// <summary>
+        /// When Say is finished, this event is triggered
+        /// </summary>
+        // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public event EventHandler<SayFinishedEventArgs> SayFinished
+        {
+            add => _sayFinishedEventHandler += value;
+            remove => _sayFinishedEventHandler -= value;
+        }
+        #endregion Event, Enum, Struct, ETC
+
+
+        #region Internal
+        internal void PauseResume(View target, bool pause)
+        {
+            Interop.Accessibility.accessibility_pause_resume(View.getCPtr(target), pause);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+
+        internal bool Say(View target, string sentence, bool discardable)
+        {
+            IntPtr callbackIntPtr = IntPtr.Zero;
+            if (_sayFinishedEventHandler != null)
+            {
+                _sayFinishedEventCallbackType callback = _sayFinishedEventCallback;
+                callbackIntPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(callback);
+            }
+            bool ret = Interop.Accessibility.accessibility_say(View.getCPtr(target), sentence, discardable, callbackIntPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+        #endregion Internal
+
+
+        #region Private
+        private static readonly Accessibility _accessibility = new Accessibility();
+
+        private event EventHandler<SayFinishedEventArgs> _sayFinishedEventHandler;
+
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void _sayFinishedEventCallbackType(int result);
+
+        private void _sayFinishedEventCallback(int result)
+        {
+            tlog.Fatal(tag, $"_sayFinishedEventCallback(res={result}) called!");
+            _sayFinishedEventHandler?.Invoke(this, new SayFinishedEventArgs(result));
+        }
+
+        private View dummy;
+
+        private static string tag = "NUITEST";
+        #endregion Private
+    }
+}
+
+
+#endif
\ No newline at end of file
diff --git a/test/NUITestSample/NUITestSample/examples/AtspiTest.cs b/test/NUITestSample/NUITestSample/examples/AtspiTest.cs
new file mode 100755 (executable)
index 0000000..675ebc2
--- /dev/null
@@ -0,0 +1,126 @@
+
+
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using tlog = Tizen.Log;
+
+namespace Tizen.TV.NUI.Example
+{
+    public class AtspiTest : IExample
+    {
+        View view;
+        const string tag = "NUITEST";
+
+        public void Activate()
+        {
+            Window.Instance.KeyEvent += OnKeyEvent;
+            Window.Instance.BackgroundColor = Color.Green;
+
+            guide = new TextLabel();
+            guide.Position = new Position(100, 100, 0);
+            guide.MultiLine = true;
+            guide.PointSize = 30.0f;
+            guide.Text = "dali-atspi test\n" +
+                            "Return Key: stop Say().\n" +
+                            "Up Key: Say() script3. discardable=false.\n" +
+                            "Right Key: Say() script1.\n" +
+                            "Down Key: Say() repeat infinitely.\n" +
+                            "Left Key: Say() script2.";
+
+            Window.Instance.GetDefaultLayer().Add(guide);
+
+            view = new View();
+            view.Size2D = new Size2D(100, 100);
+            view.Position2D = new Position2D(700, 700);
+            view.BackgroundColor = Color.Red;
+            Window.Instance.GetDefaultLayer().Add(view);
+
+            view.KeyEvent += OnKeyPressed;
+            view.Focusable = true;
+            FocusManager.Instance.SetCurrentFocusView(view);
+
+            //var accessbilityStatus = NDalicPINVOKE.accessibility_get_status(View.getCPtr(view));
+            //Tizen.Log.Fatal("NUITEST", $"accessbilityStatus={accessbilityStatus}");
+        }
+
+        public void OnKeyEvent(object sender, Window.KeyEventArgs e)
+        {
+        }
+
+        private TextLabel guide;
+
+        string testScript1 = "¾È³çÇϼ¼¿ä. ´Þ¸® ¿¡ÀÌƼ¿¡½ºÇǾÆÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. ¼ýÀÚÀÏÅ°¸¦ ´©¸£¸é ÆÛÁîÀÌ°í ¼ýÀÚÀÌÅ°¸¦ ´©¸£¸é ¸®ÁÜÀÔ´Ï´Ù. " +
+            "Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved PROPRIETARY / CONFIDENTIAL " +
+            "This software is the confidential and proprietary information of SAMSUNG ELECTRONICS(Confidential Information). " +
+            "You shall not disclose such Confidential Information and shall use it only in accordance with the terms of the license agreement " +
+            "you entered into with SAMSUNG ELECTRONICS.SAMSUNG make no representations or warranties about the suitability of the software, " +
+            "either express or implied, including but not limited to the implied warranties of merchantability, fitness for a particular purpose, " +
+            "or non-infringement.SAMSUNG shall not be liable for any damages suffered by licensee as a result of using, modifying or distributing " +
+            "this software or its derivatives.";
+
+        const string testScript2 = "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 1 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 2 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 3 " +
+            "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 4 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 5 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 6 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 7 " +
+            "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 8 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 9 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 10 " +
+            "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 11 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 12 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 13 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 14 " +
+            "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 15 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 16 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 17 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 18 " +
+            "ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 19 ÀÌ°Ç ¼¼ÀÌ Å×½ºÆ® ÀÔ´Ï´Ù. This is Say test 20";
+
+        const string testScript3 = "Say another test 1 Say another test 2 Say another test 3 Say another test 4 Say another test 5";
+
+        bool repeatFlag = false;
+        private bool OnKeyPressed(object source, View.KeyEventArgs e)
+        {
+            if (e.Key.State == Key.StateType.Down)
+            {
+                tlog.Fatal(tag, $"KeyPressedName={e.Key.KeyPressedName}");
+                //var accessbilityStatus = NDalicPINVOKE.accessibility_get_status(View.getCPtr(view));
+                if (e.Key.KeyPressedName == "Return")
+                {
+                    Accessibility.Instance.SayFinished -= Instance_SayFinished;
+                    Accessibility.Instance.Say("", true);
+                    repeatFlag = false;
+                }
+                else if (e.Key.KeyPressedName == "Right")
+                {
+                    Accessibility.Instance.Say(testScript1, true);
+                }
+                else if (e.Key.KeyPressedName == "Left")
+                {
+                    Accessibility.Instance.Say(testScript2, true);
+                }
+                else if (e.Key.KeyPressedName == "Up")
+                {
+                    Accessibility.Instance.Say(testScript3, true);
+                }
+                else if (e.Key.KeyPressedName == "Down")
+                {
+                    repeatFlag = true;
+                    Accessibility.Instance.SayFinished += Instance_SayFinished;
+                    Accessibility.Instance.Say("my name is say api !!!", true);
+                }
+                else if (e.Key.KeyPressedName == "1")
+                {
+                    Accessibility.Instance.PauseResume(true);
+                }
+                else if (e.Key.KeyPressedName == "2")
+                {
+                    Accessibility.Instance.PauseResume(false);
+                }
+            }
+            return false;
+        }
+
+        private void Instance_SayFinished(object sender, Accessibility.SayFinishedEventArgs e)
+        {
+            tlog.Fatal(tag, $"Instance_SayFinished()! State={e.State}");
+            if (e.State == Accessibility.SayFinishedStates.Stopped)
+            {
+                Accessibility.Instance.Say("ÀÌ°Ç Äݹé Å×½ºÆ® ÀÔ´Ï´Ù. this is callback test!  ÄݹéÀ» »©·Á¸é È®ÀÎÅ°¸¦ ´©¸£¼¼¿ä. to remove callback please push Return key", true);
+            }
+        }
+
+        public void Deactivate()
+        {
+        }
+    }
+}