From 151377269b96adaa94968e1c9b62cd8300207072 Mon Sep 17 00:00:00 2001
From: dongsug-song <35130733+dongsug-song@users.noreply.github.com>
Date: Fri, 26 Jul 2019 11:15:44 +0900
Subject: [PATCH] [NUI] Add Say, PauseResume of Dali-ATSPI (#947)
---
.../src/internal/Interop/Interop.Accessibility.cs | 40 ++++
src/Tizen.NUI/src/public/Accessibility.cs | 227 +++++++++++++++++++++
.../NUITestSample/examples/AtspiTest.cs | 126 ++++++++++++
3 files changed, 393 insertions(+)
create mode 100755 src/Tizen.NUI/src/internal/Interop/Interop.Accessibility.cs
create mode 100755 src/Tizen.NUI/src/public/Accessibility.cs
create mode 100755 test/NUITestSample/NUITestSample/examples/AtspiTest.cs
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
index 0000000..820028f
--- /dev/null
+++ b/src/Tizen.NUI/src/internal/Interop/Interop.Accessibility.cs
@@ -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
index 0000000..f772346
--- /dev/null
+++ b/src/Tizen.NUI/src/public/Accessibility.cs
@@ -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
+{
+ ///
+ /// Accessibility provides Dali-ATSPI interface which has funtionality of Screen-Reader and general accessibility
+ ///
+ // 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
+ ///
+ /// Instance for singleton
+ ///
+ // 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
+ ///
+ /// Get the current status
+ ///
+ /// Current enabled status
+ // 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;
+ }
+
+ ///
+ /// Start to speak
+ ///
+ /// Content to be spoken
+ /// true to be stopped and discarded when other Say is triggered
+ ///
+ // 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(callback);
+ }
+ bool ret = Interop.Accessibility.accessibility_say(View.getCPtr(dummy), sentence, discardable, callbackIntPtr);
+ if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ return ret;
+ }
+
+ ///
+ /// To make Say be paused or resumed
+ ///
+ /// true to be paused, false to be resumed
+ // 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
+ ///
+ /// Say Finished event arguments
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SayFinishedEventArgs : EventArgs
+ {
+ ///
+ /// The state of Say finished
+ ///
+ // 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}");
+ }
+ }
+
+ ///
+ /// Enum of Say finished event argument status
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public enum SayFinishedStates
+ {
+ ///
+ /// Invalid
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Invalid = -1,
+ ///
+ /// Cancelled
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Cancelled = 1,
+ ///
+ /// Stopped
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Stopped = 2,
+ ///
+ /// Skipped
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ Skipped = 3
+ }
+
+ ///
+ /// When Say is finished, this event is triggered
+ ///
+ // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler 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(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 _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
index 0000000..675ebc2
--- /dev/null
+++ b/test/NUITestSample/NUITestSample/examples/AtspiTest.cs
@@ -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()
+ {
+ }
+ }
+}
--
2.7.4