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