[NUI][AT-SPI] Add Accessibility.Enabled and .Disabled signals
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Accessibility / Accessibility.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.ComponentModel;
20 using System.Runtime.InteropServices;
21 using Tizen.NUI.BaseComponents;
22 using System.Diagnostics.CodeAnalysis;
23
24 namespace Tizen.NUI.Accessibility
25 {
26     /// <summary>
27     /// Accessibility provides Dali-ATSPI interface which has functionality of Screen-Reader and general accessibility
28     /// </summary>
29     // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
30     [SuppressMessage("Microsoft.Design", "CA1724: Type names should not match namespaces")]
31     [SuppressMessage("Microsoft.Design", "CA1001:Types that own disposable fields should be disposable", Justification = "This is a singleton class and is not disposed")]
32     [EditorBrowsable(EditorBrowsableState.Never)]
33     public class Accessibility
34     {
35         #region Constructor, Destructor, Dispose
36         private Accessibility()
37         {
38             dummy = new View();
39             dummy.Name = "dali-atspi-singleton";
40         }
41
42         static Accessibility()
43         {
44             enabledSignalHandler = () =>
45             {
46                 Enabled?.Invoke(typeof(Accessibility), EventArgs.Empty);
47             };
48
49             disabledSignalHandler = () =>
50             {
51                 Disabled?.Invoke(typeof(Accessibility), EventArgs.Empty);
52             };
53
54             Interop.Accessibility.RegisterEnabledDisabledSignalHandler(enabledSignalHandler, disabledSignalHandler);
55         }
56
57         /// <summary>
58         /// destructor. This is HiddenAPI. recommended not to use in public.
59         /// </summary>
60         ~Accessibility()
61         {
62             Interop.Accessibility.RegisterEnabledDisabledSignalHandler(null, null);
63
64             Tizen.Log.Debug("NUI", $"Accessibility is destroyed\n");
65         }
66         #endregion Constructor, Destructor, Dispose
67
68
69         #region Property
70         /// <summary>
71         /// Instance for singleton
72         /// </summary>
73         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public static Accessibility Instance
76         {
77             get => accessibility;
78         }
79
80         /// <summary>
81         /// Flag to check whether the state of Accessibility is enabled or not.
82         /// </summary>
83         /// <remarks>
84         /// Getter returns true if Accessibility is enabled, false otherwise.
85         /// </remarks>
86         /// This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
87         [EditorBrowsable(EditorBrowsableState.Never)]
88         public static bool IsEnabled
89         {
90             get
91             {
92                 return (bool)Interop.Accessibility.IsEnabled();
93             }
94         }
95
96         #endregion Property
97
98
99         #region Method
100         /// <summary>
101         /// Get the current status
102         /// </summary>
103         /// <returns>Current enabled status</returns>
104         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
105         [EditorBrowsable(EditorBrowsableState.Never)]
106         static public bool GetStatus()
107         {
108             return true;
109         }
110
111         /// <summary>
112         /// Start to speak
113         /// </summary>
114         /// <param name="sentence">Content to be spoken</param>
115         /// <param name="discardable">true to be stopped and discarded when other Say is triggered</param>
116         /// <returns></returns>
117         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
118         [EditorBrowsable(EditorBrowsableState.Never)]
119         public bool Say(string sentence, bool discardable)
120         {
121             IntPtr callbackIntPtr = IntPtr.Zero;
122             if (sayFinishedEventHandler != null)
123             {
124                 callback = SayFinishedEventCallback;
125                 callbackIntPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(callback);
126             }
127             bool ret = Interop.Accessibility.Say(View.getCPtr(dummy), sentence, discardable, callbackIntPtr);
128             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
129             return ret;
130         }
131
132         /// <summary>
133         /// To make Say be paused or resumed
134         /// </summary>
135         /// <param name="pause">true to be paused, false to be resumed</param>
136         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
137         [EditorBrowsable(EditorBrowsableState.Never)]
138         public void PauseResume(bool pause)
139         {
140             Interop.Accessibility.PauseResume(View.getCPtr(dummy), pause);
141             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
142         }
143
144         /// <summary>
145         /// Cancels anything screen-reader is reading / has queued to read
146         /// </summary>
147         /// <param name="alsoNonDiscardable">whether to cancel non-discardable readings as well</param>
148         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
149         [EditorBrowsable(EditorBrowsableState.Never)]
150         public void StopReading(bool alsoNonDiscardable)
151         {
152             Interop.Accessibility.StopReading(View.getCPtr(dummy), alsoNonDiscardable);
153             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
154         }
155
156         /// <summary>
157         /// Suppress reading of screen-reader
158         /// </summary>
159         /// <param name="suppress">whether to suppress reading of screen-reader</param>
160         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
161         [EditorBrowsable(EditorBrowsableState.Never)]
162         public bool SuppressScreenReader(bool suppress)
163         {
164             bool ret = Interop.Accessibility.SuppressScreenReader(View.getCPtr(dummy), suppress);
165             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
166             return ret;
167         }
168
169         /// <summary>
170         /// Re-enables auto-initialization of AT-SPI bridge
171         /// </summary>
172         /// <remarks>
173         /// Normal applications do not have to call this function. The AT-SPI bridge is initialized on demand.
174         /// </remarks>
175         [EditorBrowsable(EditorBrowsableState.Never)]
176         public static void BridgeEnableAutoInit()
177         {
178             Interop.Accessibility.BridgeEnableAutoInit();
179             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
180         }
181
182         /// <summary>
183         /// Blocks auto-initialization of AT-SPI bridge
184         /// </summary>
185         /// <remarks>
186         /// Use this only if your application starts before DBus does, and call it early in Main().
187         /// When DBus is ready, call BridgeEnableAutoInit().
188         /// </remarks>
189         [EditorBrowsable(EditorBrowsableState.Never)]
190         public static void BridgeDisableAutoInit()
191         {
192             Interop.Accessibility.BridgeDisableAutoInit();
193             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
194         }
195
196         /// <summary>
197         ///  Get View that is used to highlight widget.
198         /// </summary>
199         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
200         [EditorBrowsable(EditorBrowsableState.Never)]
201         public View GetHighlightFrameView()
202         {
203             var ptr = Interop.ControlDevel.DaliAccessibilityAccessibleGetHighlightActor();
204             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
205             if (ptr == IntPtr.Zero)
206                 return null;
207             return new View(ptr, true);
208         }
209
210         /// <summary>
211         ///  Set view that will be used to highlight widget.
212         /// </summary>
213         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
214         [EditorBrowsable(EditorBrowsableState.Never)]
215         public void SetHighlightFrameView(View view)
216         {
217             Interop.ControlDevel.DaliAccessibilityAccessibleSetHighlightActor(View.getCPtr(view));
218             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
219         }
220
221         /// <summary>
222         ///  Get highligted View.
223         /// </summary>
224         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
225         [EditorBrowsable(EditorBrowsableState.Never)]
226         public View GetCurrentlyHighlightedView()
227         {
228             var ptr = Interop.ControlDevel.DaliAccessibilityAccessibleGetCurrentlyHighlightedActor();
229             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
230             if (ptr == IntPtr.Zero)
231                 return null;
232             return new View(ptr, true);
233         }
234
235         /// <summary>
236         ///  Clear highlight.
237         /// </summary>
238         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
239         [EditorBrowsable(EditorBrowsableState.Never)]
240         public bool ClearCurrentlyHighlightedView()
241         {
242             using (View view = GetCurrentlyHighlightedView())
243             {
244                 return view?.ClearAccessibilityHighlight() ?? false;
245             }
246         }
247         #endregion Method
248
249
250         #region Event, Enum, Struct, ETC
251         /// <summary>
252         /// Enum of Say finished event argument status
253         /// </summary>
254         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
255         [EditorBrowsable(EditorBrowsableState.Never)]
256         public enum SayFinishedState
257         {
258             /// <summary>
259             /// Invalid
260             /// </summary>
261             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
262             [EditorBrowsable(EditorBrowsableState.Never)]
263             Invalid = -1,
264             /// <summary>
265             /// Cancelled
266             /// </summary>
267             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
268             [EditorBrowsable(EditorBrowsableState.Never)]
269             Cancelled = 1,
270             /// <summary>
271             /// Stopped
272             /// </summary>
273             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
274             [EditorBrowsable(EditorBrowsableState.Never)]
275             Stopped = 2,
276             /// <summary>
277             /// Skipped
278             /// </summary>
279             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
280             [EditorBrowsable(EditorBrowsableState.Never)]
281             Skipped = 3,
282             /// <summary>
283             /// Paused
284             /// </summary>
285             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
286             [EditorBrowsable(EditorBrowsableState.Never)]
287             Paused = 4,
288             /// <summary>
289             /// Resumed
290             /// </summary>
291             // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
292             [EditorBrowsable(EditorBrowsableState.Never)]
293             Resumed = 5
294         }
295
296         /// <summary>
297         /// When Say is finished, this event is triggered
298         /// </summary>
299         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
300         [EditorBrowsable(EditorBrowsableState.Never)]
301         public event EventHandler<SayFinishedEventArgs> SayFinished
302         {
303             add => sayFinishedEventHandler += value;
304             remove => sayFinishedEventHandler -= value;
305         }
306
307         /// <summary>
308         /// Triggered whenever the value of IsEnabled would change from false to true
309         /// </summary>
310         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
311         [EditorBrowsable(EditorBrowsableState.Never)]
312         public static event EventHandler Enabled;
313
314         /// <summary>
315         /// Triggered whenever the value of IsEnabled would change from true to false
316         /// </summary>
317         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
318         [EditorBrowsable(EditorBrowsableState.Never)]
319         public static event EventHandler Disabled;
320
321         #endregion Event, Enum, Struct, ETC
322
323
324         #region Internal
325         internal void PauseResume(View target, bool pause)
326         {
327             Interop.Accessibility.PauseResume(View.getCPtr(target), pause);
328             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
329         }
330
331         internal bool Say(View target, string sentence, bool discardable)
332         {
333             IntPtr callbackIntPtr = IntPtr.Zero;
334             if (sayFinishedEventHandler != null)
335             {
336                 callback = SayFinishedEventCallback;
337                 callbackIntPtr = Marshal.GetFunctionPointerForDelegate<Delegate>(callback);
338             }
339             bool ret = Interop.Accessibility.Say(View.getCPtr(target), sentence, discardable, callbackIntPtr);
340             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
341             return ret;
342         }
343         #endregion Internal
344
345
346         #region Private
347         private static readonly Accessibility accessibility = new Accessibility();
348
349         private event EventHandler<SayFinishedEventArgs> sayFinishedEventHandler;
350
351         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
352         private delegate void SayFinishedEventCallbackType(int result);
353
354         private SayFinishedEventCallbackType callback = null;
355
356         private static Interop.Accessibility.EnabledDisabledSignalHandler enabledSignalHandler = null;
357
358         private static Interop.Accessibility.EnabledDisabledSignalHandler disabledSignalHandler = null;
359
360         private void SayFinishedEventCallback(int result)
361         {
362             NUILog.Debug($"sayFinishedEventCallback(res={result}) called!");
363             sayFinishedEventHandler?.Invoke(this, new SayFinishedEventArgs(result));
364         }
365
366         private View dummy;
367
368         #endregion Private
369     }
370
371     /// <summary>
372     ///  Say Finished event arguments
373     /// </summary>
374     // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
375     [EditorBrowsable(EditorBrowsableState.Never)]
376     public class SayFinishedEventArgs : EventArgs
377     {
378         /// <summary>
379         /// The state of Say finished
380         /// </summary>
381         // This will be public opened after ACR done. (Before ACR, need to be hidden as Inhouse API)
382         [EditorBrowsable(EditorBrowsableState.Never)]
383         public Accessibility.SayFinishedState State
384         {
385             private set;
386             get;
387         }
388
389         internal SayFinishedEventArgs(int result)
390         {
391             State = (Accessibility.SayFinishedState)(result);
392             NUILog.Debug($"SayFinishedEventArgs Constructor! State={State}");
393         }
394     }
395 }