1 | import { Application } from '../application';
|
2 | import { notifyAccessibilityFocusState } from './accessibility-common';
|
3 | import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait } from './accessibility-types';
|
4 | export * from './accessibility-common';
|
5 | export * from './accessibility-types';
|
6 | export * from './font-scale';
|
7 | function enforceArray(val) {
|
8 | if (Array.isArray(val)) {
|
9 | return val;
|
10 | }
|
11 | if (typeof val === 'string') {
|
12 | return val.split(/[, ]/g).filter((v) => !!v);
|
13 | }
|
14 | return [];
|
15 | }
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | function inputArrayToBitMask(values, map) {
|
23 | return (enforceArray(values)
|
24 | .filter((value) => !!value)
|
25 | .map((value) => `${value}`.toLocaleLowerCase())
|
26 | .filter((value) => map.has(value))
|
27 | .reduce((res, value) => res | map.get(value), 0) || 0);
|
28 | }
|
29 | let AccessibilityTraitsMap;
|
30 | let RoleTypeMap;
|
31 | let nativeFocusedNotificationObserver;
|
32 | let lastFocusedView;
|
33 | function ensureNativeClasses() {
|
34 | if (AccessibilityTraitsMap && nativeFocusedNotificationObserver) {
|
35 | return;
|
36 | }
|
37 | AccessibilityTraitsMap = new Map([
|
38 | [AccessibilityTrait.AllowsDirectInteraction, UIAccessibilityTraitAllowsDirectInteraction],
|
39 | [AccessibilityTrait.CausesPageTurn, UIAccessibilityTraitCausesPageTurn],
|
40 | [AccessibilityTrait.NotEnabled, UIAccessibilityTraitNotEnabled],
|
41 | [AccessibilityTrait.Selected, UIAccessibilityTraitSelected],
|
42 | [AccessibilityTrait.UpdatesFrequently, UIAccessibilityTraitUpdatesFrequently],
|
43 | ]);
|
44 | RoleTypeMap = new Map([
|
45 | [AccessibilityRole.Adjustable, UIAccessibilityTraitAdjustable],
|
46 | [AccessibilityRole.Button, UIAccessibilityTraitButton],
|
47 | [AccessibilityRole.Checkbox, UIAccessibilityTraitButton],
|
48 | [AccessibilityRole.Header, UIAccessibilityTraitHeader],
|
49 | [AccessibilityRole.KeyboardKey, UIAccessibilityTraitKeyboardKey],
|
50 | [AccessibilityRole.Image, UIAccessibilityTraitImage],
|
51 | [AccessibilityRole.ImageButton, UIAccessibilityTraitImage | UIAccessibilityTraitButton],
|
52 | [AccessibilityRole.Link, UIAccessibilityTraitLink],
|
53 | [AccessibilityRole.None, UIAccessibilityTraitNone],
|
54 | [AccessibilityRole.PlaysSound, UIAccessibilityTraitPlaysSound],
|
55 | [AccessibilityRole.RadioButton, UIAccessibilityTraitButton],
|
56 | [AccessibilityRole.Search, UIAccessibilityTraitSearchField],
|
57 | [AccessibilityRole.StaticText, UIAccessibilityTraitStaticText],
|
58 | [AccessibilityRole.StartsMediaSession, UIAccessibilityTraitStartsMediaSession],
|
59 | [AccessibilityRole.Summary, UIAccessibilityTraitSummaryElement],
|
60 | [AccessibilityRole.Switch, UIAccessibilityTraitButton],
|
61 | ]);
|
62 | nativeFocusedNotificationObserver = Application.ios.addNotificationObserver(UIAccessibilityElementFocusedNotification, (args) => {
|
63 | const uiView = args.userInfo?.objectForKey(UIAccessibilityFocusedElementKey);
|
64 | if (!uiView?.tag) {
|
65 | return;
|
66 | }
|
67 | const rootView = Application.getRootView();
|
68 |
|
69 | let view = rootView.getViewByDomId(uiView?.tag);
|
70 | if (!view) {
|
71 | for (const modalView of rootView._getRootModalViews()) {
|
72 | view = modalView.getViewByDomId(uiView?.tag);
|
73 | if (view) {
|
74 | break;
|
75 | }
|
76 | }
|
77 | }
|
78 | if (!view) {
|
79 | return;
|
80 | }
|
81 | const lastView = lastFocusedView?.deref();
|
82 | if (lastView && view !== lastView) {
|
83 | const lastFocusedUIView = lastView.nativeViewProtected;
|
84 | if (lastFocusedUIView) {
|
85 | lastFocusedView = null;
|
86 | notifyAccessibilityFocusState(lastView, false, true);
|
87 | }
|
88 | }
|
89 | lastFocusedView = new WeakRef(view);
|
90 | notifyAccessibilityFocusState(view, true, false);
|
91 | });
|
92 | Application.on(Application.exitEvent, () => {
|
93 | if (nativeFocusedNotificationObserver) {
|
94 | Application.ios.removeNotificationObserver(nativeFocusedNotificationObserver, UIAccessibilityElementFocusedNotification);
|
95 | }
|
96 | nativeFocusedNotificationObserver = null;
|
97 | lastFocusedView = null;
|
98 | });
|
99 | }
|
100 | export function setupAccessibleView(view) {
|
101 | const uiView = view.nativeViewProtected;
|
102 | if (!uiView) {
|
103 | return;
|
104 | }
|
105 | |
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | uiView.tag = view._domId;
|
112 | }
|
113 | export function updateAccessibilityProperties(view) {
|
114 | const uiView = view.nativeViewProtected;
|
115 | if (!uiView) {
|
116 | return;
|
117 | }
|
118 | ensureNativeClasses();
|
119 | const accessibilityRole = view.accessibilityRole;
|
120 | const accessibilityState = view.accessibilityState;
|
121 | if (!view.accessible || view.accessibilityHidden) {
|
122 | uiView.accessibilityTraits = UIAccessibilityTraitNone;
|
123 | return;
|
124 | }
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | let a11yTraits = UIAccessibilityTraitNone;
|
132 | if (RoleTypeMap.has(accessibilityRole)) {
|
133 | a11yTraits |= RoleTypeMap.get(accessibilityRole);
|
134 | }
|
135 | switch (accessibilityRole) {
|
136 | case AccessibilityRole.Checkbox:
|
137 | case AccessibilityRole.RadioButton:
|
138 | case AccessibilityRole.Switch: {
|
139 | if (accessibilityState === AccessibilityState.Checked) {
|
140 | a11yTraits |= AccessibilityTraitsMap.get(AccessibilityTrait.Selected);
|
141 | }
|
142 | break;
|
143 | }
|
144 | default: {
|
145 | if (accessibilityState === AccessibilityState.Selected) {
|
146 | a11yTraits |= AccessibilityTraitsMap.get(AccessibilityTrait.Selected);
|
147 | }
|
148 | if (accessibilityState === AccessibilityState.Disabled) {
|
149 | a11yTraits |= AccessibilityTraitsMap.get(AccessibilityTrait.NotEnabled);
|
150 | }
|
151 | break;
|
152 | }
|
153 | }
|
154 | const UpdatesFrequentlyTrait = AccessibilityTraitsMap.get(AccessibilityTrait.UpdatesFrequently);
|
155 | switch (view.accessibilityLiveRegion) {
|
156 | case AccessibilityLiveRegion.Polite:
|
157 | case AccessibilityLiveRegion.Assertive: {
|
158 | a11yTraits |= UpdatesFrequentlyTrait;
|
159 | break;
|
160 | }
|
161 | default: {
|
162 | a11yTraits &= ~UpdatesFrequentlyTrait;
|
163 | break;
|
164 | }
|
165 | }
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | if (view.accessibilityMediaSession) {
|
171 | a11yTraits |= RoleTypeMap.get(AccessibilityRole.StartsMediaSession);
|
172 | }
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | uiView.accessibilityTraits = a11yTraits;
|
184 | }
|
185 | export const sendAccessibilityEvent = () => { };
|
186 | export const updateContentDescription = () => null;
|
187 | let accessibilityServiceEnabled;
|
188 | let nativeObserver;
|
189 | export function isAccessibilityServiceEnabled() {
|
190 | if (typeof accessibilityServiceEnabled === 'boolean') {
|
191 | return accessibilityServiceEnabled;
|
192 | }
|
193 | let isVoiceOverRunning;
|
194 | if (typeof UIAccessibilityIsVoiceOverRunning === 'function') {
|
195 | isVoiceOverRunning = UIAccessibilityIsVoiceOverRunning;
|
196 | }
|
197 | else {
|
198 |
|
199 | if (typeof UIAccessibilityIsVoiceOverRunning !== 'function') {
|
200 | accessibilityServiceEnabled = false;
|
201 | return accessibilityServiceEnabled;
|
202 | }
|
203 | }
|
204 | accessibilityServiceEnabled = isVoiceOverRunning();
|
205 | let voiceOverStatusChangedNotificationName = null;
|
206 | if (typeof UIAccessibilityVoiceOverStatusDidChangeNotification !== 'undefined') {
|
207 | voiceOverStatusChangedNotificationName = UIAccessibilityVoiceOverStatusDidChangeNotification;
|
208 | }
|
209 | else if (typeof UIAccessibilityVoiceOverStatusChanged !== 'undefined') {
|
210 | voiceOverStatusChangedNotificationName = UIAccessibilityVoiceOverStatusChanged;
|
211 | }
|
212 | if (voiceOverStatusChangedNotificationName) {
|
213 | nativeObserver = Application.ios.addNotificationObserver(voiceOverStatusChangedNotificationName, () => {
|
214 | accessibilityServiceEnabled = isVoiceOverRunning();
|
215 | });
|
216 | Application.on(Application.exitEvent, () => {
|
217 | if (nativeObserver) {
|
218 | Application.ios.removeNotificationObserver(nativeObserver, voiceOverStatusChangedNotificationName);
|
219 | }
|
220 | accessibilityServiceEnabled = undefined;
|
221 | nativeObserver = null;
|
222 | });
|
223 | }
|
224 | Application.on(Application.resumeEvent, () => {
|
225 | accessibilityServiceEnabled = isVoiceOverRunning();
|
226 | });
|
227 | return accessibilityServiceEnabled;
|
228 | }
|
229 |
|
\ | No newline at end of file |