UNPKG

27.6 kBJavaScriptView Raw
1import { Application } from '../application';
2import { Trace } from '../trace';
3import { SDK_VERSION } from '../utils/constants';
4import { resources } from '../utils/android';
5import { GestureTypes } from '../ui/gestures';
6import { notifyAccessibilityFocusState } from './accessibility-common';
7import { getAndroidAccessibilityManager } from './accessibility-service';
8import { AccessibilityRole, AccessibilityState, AndroidAccessibilityEvent } from './accessibility-types';
9export * from './accessibility-common';
10export * from './accessibility-types';
11export * from './font-scale';
12let clickableRolesMap = new Set();
13let lastFocusedView;
14function accessibilityEventHelper(view, eventType) {
15 const eventName = accessibilityEventTypeMap.get(eventType);
16 if (!isAccessibilityServiceEnabled()) {
17 if (Trace.isEnabled()) {
18 Trace.write(`accessibilityEventHelper: Service not active`, Trace.categories.Accessibility);
19 }
20 return;
21 }
22 if (!eventName) {
23 Trace.write(`accessibilityEventHelper: unknown eventType: ${eventType}`, Trace.categories.Accessibility, Trace.messageType.error);
24 return;
25 }
26 if (!view) {
27 if (Trace.isEnabled()) {
28 Trace.write(`accessibilityEventHelper: no owner: ${eventName}`, Trace.categories.Accessibility);
29 }
30 return;
31 }
32 const androidView = view.nativeViewProtected;
33 if (!androidView) {
34 if (Trace.isEnabled()) {
35 Trace.write(`accessibilityEventHelper: no nativeView`, Trace.categories.Accessibility);
36 }
37 return;
38 }
39 switch (eventType) {
40 case android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED: {
41 /**
42 * Android API >= 26 handles accessibility tap-events by converting them to TYPE_VIEW_CLICKED
43 * These aren't triggered for custom tap events in NativeScript.
44 */
45 if (SDK_VERSION >= 26) {
46 // Find all tap gestures and trigger them.
47 for (const tapGesture of view.getGestureObservers(GestureTypes.tap) ?? []) {
48 tapGesture.callback({
49 android: view.android,
50 eventName: 'tap',
51 ios: null,
52 object: view,
53 type: GestureTypes.tap,
54 view: view,
55 });
56 }
57 }
58 return;
59 }
60 case android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
61 const lastView = lastFocusedView?.get();
62 if (lastView && view !== lastView) {
63 const lastAndroidView = lastView.nativeViewProtected;
64 if (lastAndroidView) {
65 lastAndroidView.clearFocus();
66 lastFocusedView = null;
67 notifyAccessibilityFocusState(lastView, false, true);
68 }
69 }
70 lastFocusedView = new WeakRef(view);
71 notifyAccessibilityFocusState(view, true, false);
72 return;
73 }
74 case android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
75 const lastView = lastFocusedView?.get();
76 if (lastView && view === lastView) {
77 lastFocusedView = null;
78 androidView.clearFocus();
79 }
80 notifyAccessibilityFocusState(view, false, true);
81 return;
82 }
83 }
84}
85let TNSAccessibilityDelegate;
86const androidViewToTNSView = new WeakMap();
87let accessibilityEventMap;
88let accessibilityEventTypeMap;
89function ensureNativeClasses() {
90 if (TNSAccessibilityDelegate) {
91 return;
92 }
93 // WORKAROUND: Typing refers to android.view.View.androidviewViewAccessibilityDelegate but it is called android.view.View.AccessibilityDelegate at runtime
94 const AccessibilityDelegate = android.view.View['AccessibilityDelegate'];
95 const RoleTypeMap = new Map([
96 [AccessibilityRole.Button, android.widget.Button.class.getName()],
97 [AccessibilityRole.Search, android.widget.EditText.class.getName()],
98 [AccessibilityRole.Image, android.widget.ImageView.class.getName()],
99 [AccessibilityRole.ImageButton, android.widget.ImageButton.class.getName()],
100 [AccessibilityRole.KeyboardKey, android.inputmethodservice.Keyboard.Key.class.getName()],
101 [AccessibilityRole.StaticText, android.widget.TextView.class.getName()],
102 [AccessibilityRole.Adjustable, android.widget.SeekBar.class.getName()],
103 [AccessibilityRole.Checkbox, android.widget.CheckBox.class.getName()],
104 [AccessibilityRole.RadioButton, android.widget.RadioButton.class.getName()],
105 [AccessibilityRole.SpinButton, android.widget.Spinner.class.getName()],
106 [AccessibilityRole.Switch, android.widget.Switch.class.getName()],
107 [AccessibilityRole.ProgressBar, android.widget.ProgressBar.class.getName()],
108 ]);
109 clickableRolesMap = new Set([AccessibilityRole.Button, AccessibilityRole.ImageButton]);
110 const ignoreRoleTypesForTrace = new Set([AccessibilityRole.Header, AccessibilityRole.Link, AccessibilityRole.None, AccessibilityRole.Summary]);
111 var TNSAccessibilityDelegateImpl = /** @class */ (function (_super) {
112 __extends(TNSAccessibilityDelegateImpl, _super);
113 function TNSAccessibilityDelegateImpl() {
114 var _this = _super.call(this) || this;
115 return global.__native(_this);
116 }
117 TNSAccessibilityDelegateImpl.prototype.getTnsView = function (androidView) {
118 var _a;
119 var view = (_a = androidViewToTNSView.get(androidView)) === null || _a === void 0 ? void 0 : _a.get();
120 if (!view) {
121 androidViewToTNSView.delete(androidView);
122 return null;
123 }
124 return view;
125 };
126 TNSAccessibilityDelegateImpl.prototype.onInitializeAccessibilityNodeInfo = function (host, info) {
127 _super.prototype.onInitializeAccessibilityNodeInfo.call(this, host, info);
128 var view = this.getTnsView(host);
129 if (!view) {
130 if (Trace.isEnabled()) {
131 Trace.write("onInitializeAccessibilityNodeInfo ".concat(host, " ").concat(info, " no tns-view"), Trace.categories.Accessibility);
132 }
133 return;
134 }
135 // Set resource id that can be used with test frameworks without polluting the content description.
136 var id = host.getTag(resources.getId(":id/nativescript_accessibility_id"));
137 if (id != null) {
138 info.setViewIdResourceName(id);
139 }
140 var accessibilityRole = view.accessibilityRole;
141 if (accessibilityRole) {
142 var androidClassName = RoleTypeMap.get(accessibilityRole);
143 if (androidClassName) {
144 var oldClassName = info.getClassName() || (SDK_VERSION >= 28 && host.getAccessibilityClassName()) || null;
145 info.setClassName(androidClassName);
146 if (Trace.isEnabled()) {
147 Trace.write("".concat(view, ".accessibilityRole = \"").concat(accessibilityRole, "\" is mapped to \"").concat(androidClassName, "\" (was ").concat(oldClassName, "). ").concat(info.getClassName()), Trace.categories.Accessibility);
148 }
149 }
150 else if (!ignoreRoleTypesForTrace.has(accessibilityRole)) {
151 if (Trace.isEnabled()) {
152 Trace.write("".concat(view, ".accessibilityRole = \"").concat(accessibilityRole, "\" is unknown"), Trace.categories.Accessibility);
153 }
154 }
155 if (clickableRolesMap.has(accessibilityRole)) {
156 if (Trace.isEnabled()) {
157 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set clickable role=").concat(accessibilityRole), Trace.categories.Accessibility);
158 }
159 info.setClickable(true);
160 }
161 if (SDK_VERSION >= 28) {
162 if (accessibilityRole === AccessibilityRole.Header) {
163 if (Trace.isEnabled()) {
164 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set heading role=").concat(accessibilityRole), Trace.categories.Accessibility);
165 }
166 info.setHeading(true);
167 }
168 else if (host.isAccessibilityHeading()) {
169 if (Trace.isEnabled()) {
170 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set heading from host"), Trace.categories.Accessibility);
171 }
172 info.setHeading(true);
173 }
174 else {
175 if (Trace.isEnabled()) {
176 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set not heading"), Trace.categories.Accessibility);
177 }
178 info.setHeading(false);
179 }
180 }
181 switch (accessibilityRole) {
182 case AccessibilityRole.Switch:
183 case AccessibilityRole.RadioButton:
184 case AccessibilityRole.Checkbox: {
185 if (Trace.isEnabled()) {
186 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set checkable and check=").concat(view.accessibilityState === AccessibilityState.Checked), Trace.categories.Accessibility);
187 }
188 info.setCheckable(true);
189 info.setChecked(view.accessibilityState === AccessibilityState.Checked);
190 break;
191 }
192 default: {
193 if (Trace.isEnabled()) {
194 Trace.write("onInitializeAccessibilityNodeInfo ".concat(view, " - set enabled=").concat(view.accessibilityState !== AccessibilityState.Disabled, " and selected=").concat(view.accessibilityState === AccessibilityState.Selected), Trace.categories.Accessibility);
195 }
196 info.setEnabled(view.accessibilityState !== AccessibilityState.Disabled);
197 info.setSelected(view.accessibilityState === AccessibilityState.Selected);
198 break;
199 }
200 }
201 }
202 if (view.accessible) {
203 info.setFocusable(true);
204 }
205 };
206 TNSAccessibilityDelegateImpl.prototype.sendAccessibilityEvent = function (host, eventType) {
207 _super.prototype.sendAccessibilityEvent.call(this, host, eventType);
208 var view = this.getTnsView(host);
209 if (!view) {
210 console.log("skip - ".concat(host, " - ").concat(accessibilityEventTypeMap.get(eventType)));
211 return;
212 }
213 try {
214 accessibilityEventHelper(view, eventType);
215 }
216 catch (err) {
217 console.error(err);
218 }
219 };
220 return TNSAccessibilityDelegateImpl;
221}(AccessibilityDelegate));
222 TNSAccessibilityDelegate = new TNSAccessibilityDelegateImpl();
223 accessibilityEventMap = new Map([
224 /**
225 * Invalid selection/focus position.
226 */
227 [AndroidAccessibilityEvent.INVALID_POSITION, android.view.accessibility.AccessibilityEvent.INVALID_POSITION],
228 /**
229 * Maximum length of the text fields.
230 */
231 [AndroidAccessibilityEvent.MAX_TEXT_LENGTH, android.view.accessibility.AccessibilityEvent.MAX_TEXT_LENGTH],
232 /**
233 * Represents the event of clicking on a android.view.View like android.widget.Button, android.widget.CompoundButton, etc.
234 */
235 [AndroidAccessibilityEvent.VIEW_CLICKED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED],
236 /**
237 * Represents the event of long clicking on a android.view.View like android.widget.Button, android.widget.CompoundButton, etc.
238 */
239 [AndroidAccessibilityEvent.VIEW_LONG_CLICKED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED],
240 /**
241 * Represents the event of selecting an item usually in the context of an android.widget.AdapterView.
242 */
243 [AndroidAccessibilityEvent.VIEW_SELECTED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SELECTED],
244 /**
245 * Represents the event of setting input focus of a android.view.View.
246 */
247 [AndroidAccessibilityEvent.VIEW_FOCUSED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED],
248 /**
249 * Represents the event of changing the text of an android.widget.EditText.
250 */
251 [AndroidAccessibilityEvent.VIEW_TEXT_CHANGED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED],
252 /**
253 * Represents the event of opening a android.widget.PopupWindow, android.view.Menu, android.app.Dialog, etc.
254 */
255 [AndroidAccessibilityEvent.WINDOW_STATE_CHANGED, android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED],
256 /**
257 * Represents the event showing a android.app.Notification.
258 */
259 [AndroidAccessibilityEvent.NOTIFICATION_STATE_CHANGED, android.view.accessibility.AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED],
260 /**
261 * Represents the event of a hover enter over a android.view.View.
262 */
263 [AndroidAccessibilityEvent.VIEW_HOVER_ENTER, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_HOVER_ENTER],
264 /**
265 * Represents the event of a hover exit over a android.view.View.
266 */
267 [AndroidAccessibilityEvent.VIEW_HOVER_EXIT, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_HOVER_EXIT],
268 /**
269 * Represents the event of starting a touch exploration gesture.
270 */
271 [AndroidAccessibilityEvent.TOUCH_EXPLORATION_GESTURE_START, android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START],
272 /**
273 * Represents the event of ending a touch exploration gesture.
274 */
275 [AndroidAccessibilityEvent.TOUCH_EXPLORATION_GESTURE_END, android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END],
276 /**
277 * Represents the event of changing the content of a window and more specifically the sub-tree rooted at the event's source.
278 */
279 [AndroidAccessibilityEvent.WINDOW_CONTENT_CHANGED, android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED],
280 /**
281 * Represents the event of scrolling a view.
282 */
283 [AndroidAccessibilityEvent.VIEW_SCROLLED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED],
284 /**
285 * Represents the event of changing the selection in an android.widget.EditText.
286 */
287 [AndroidAccessibilityEvent.VIEW_TEXT_SELECTION_CHANGED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED],
288 /**
289 * Represents the event of an application making an announcement.
290 */
291 [AndroidAccessibilityEvent.ANNOUNCEMENT, android.view.accessibility.AccessibilityEvent.TYPE_ANNOUNCEMENT],
292 /**
293 * Represents the event of gaining accessibility focus.
294 */
295 [AndroidAccessibilityEvent.VIEW_ACCESSIBILITY_FOCUSED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED],
296 /**
297 * Represents the event of clearing accessibility focus.
298 */
299 [AndroidAccessibilityEvent.VIEW_ACCESSIBILITY_FOCUS_CLEARED, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED],
300 /**
301 * Represents the event of traversing the text of a view at a given movement granularity.
302 */
303 [AndroidAccessibilityEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, android.view.accessibility.AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY],
304 /**
305 * Represents the event of beginning gesture detection.
306 */
307 [AndroidAccessibilityEvent.GESTURE_DETECTION_START, android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_START],
308 /**
309 * Represents the event of ending gesture detection.
310 */
311 [AndroidAccessibilityEvent.GESTURE_DETECTION_END, android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_END],
312 /**
313 * Represents the event of the user starting to touch the screen.
314 */
315 [AndroidAccessibilityEvent.TOUCH_INTERACTION_START, android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_START],
316 /**
317 * Represents the event of the user ending to touch the screen.
318 */
319 [AndroidAccessibilityEvent.TOUCH_INTERACTION_END, android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_END],
320 /**
321 * Mask for AccessibilityEvent all types.
322 */
323 [AndroidAccessibilityEvent.ALL_MASK, android.view.accessibility.AccessibilityEvent.TYPES_ALL_MASK],
324 ]);
325 accessibilityEventTypeMap = new Map([...accessibilityEventMap].map(([k, v]) => [v, k]));
326}
327let accessibilityStateChangeListener;
328let touchExplorationStateChangeListener;
329function updateAccessibilityServiceState() {
330 const accessibilityManager = getAndroidAccessibilityManager();
331 if (!accessibilityManager) {
332 return;
333 }
334 accessibilityServiceEnabled = !!accessibilityManager.isEnabled() && !!accessibilityManager.isTouchExplorationEnabled();
335}
336let accessibilityServiceEnabled;
337export function isAccessibilityServiceEnabled() {
338 if (typeof accessibilityServiceEnabled === 'boolean') {
339 return accessibilityServiceEnabled;
340 }
341 const accessibilityManager = getAndroidAccessibilityManager();
342 accessibilityStateChangeListener = new androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener({
343 onAccessibilityStateChanged(enabled) {
344 updateAccessibilityServiceState();
345 if (Trace.isEnabled()) {
346 Trace.write(`AccessibilityStateChangeListener state changed to: ${!!enabled}`, Trace.categories.Accessibility);
347 }
348 },
349 });
350 touchExplorationStateChangeListener = new androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener({
351 onTouchExplorationStateChanged(enabled) {
352 updateAccessibilityServiceState();
353 if (Trace.isEnabled()) {
354 Trace.write(`TouchExplorationStateChangeListener state changed to: ${!!enabled}`, Trace.categories.Accessibility);
355 }
356 },
357 });
358 androidx.core.view.accessibility.AccessibilityManagerCompat.addAccessibilityStateChangeListener(accessibilityManager, accessibilityStateChangeListener);
359 androidx.core.view.accessibility.AccessibilityManagerCompat.addTouchExplorationStateChangeListener(accessibilityManager, touchExplorationStateChangeListener);
360 updateAccessibilityServiceState();
361 Application.on(Application.exitEvent, (args) => {
362 const activity = args.android;
363 if (activity && !activity.isFinishing()) {
364 return;
365 }
366 const accessibilityManager = getAndroidAccessibilityManager();
367 if (accessibilityManager) {
368 if (accessibilityStateChangeListener) {
369 androidx.core.view.accessibility.AccessibilityManagerCompat.removeAccessibilityStateChangeListener(accessibilityManager, accessibilityStateChangeListener);
370 }
371 if (touchExplorationStateChangeListener) {
372 androidx.core.view.accessibility.AccessibilityManagerCompat.removeTouchExplorationStateChangeListener(accessibilityManager, touchExplorationStateChangeListener);
373 }
374 }
375 accessibilityStateChangeListener = null;
376 touchExplorationStateChangeListener = null;
377 Application.off(Application.resumeEvent, updateAccessibilityServiceState);
378 });
379 Application.on(Application.resumeEvent, updateAccessibilityServiceState);
380 return accessibilityServiceEnabled;
381}
382export function setupAccessibleView(view) {
383 updateAccessibilityProperties(view);
384}
385let updateAccessibilityPropertiesMicroTask;
386let pendingViews = new Set();
387export function updateAccessibilityProperties(view) {
388 if (!view.nativeViewProtected) {
389 return;
390 }
391 pendingViews.add(view);
392 if (updateAccessibilityPropertiesMicroTask)
393 return;
394 updateAccessibilityPropertiesMicroTask = true;
395 Promise.resolve().then(() => {
396 updateAccessibilityPropertiesMicroTask = false;
397 let _pendingViews = Array.from(pendingViews);
398 pendingViews = new Set();
399 for (const view of _pendingViews) {
400 if (!view.nativeViewProtected)
401 continue;
402 setAccessibilityDelegate(view);
403 applyContentDescription(view);
404 }
405 _pendingViews = [];
406 });
407}
408export function sendAccessibilityEvent(view, eventType, text) {
409 if (!isAccessibilityServiceEnabled()) {
410 return;
411 }
412 const cls = `sendAccessibilityEvent(${view}, ${eventType}, ${text})`;
413 const androidView = view.nativeViewProtected;
414 if (!androidView) {
415 if (Trace.isEnabled()) {
416 Trace.write(`${cls}: no nativeView`, Trace.categories.Accessibility);
417 }
418 return;
419 }
420 if (!eventType) {
421 if (Trace.isEnabled()) {
422 Trace.write(`${cls}: no eventName provided`, Trace.categories.Accessibility);
423 }
424 return;
425 }
426 if (!isAccessibilityServiceEnabled()) {
427 if (Trace.isEnabled()) {
428 Trace.write(`${cls} - TalkBack not enabled`, Trace.categories.Accessibility);
429 }
430 return;
431 }
432 const accessibilityManager = getAndroidAccessibilityManager();
433 if (!accessibilityManager?.isEnabled()) {
434 if (Trace.isEnabled()) {
435 Trace.write(`${cls} - accessibility service not enabled`, Trace.categories.Accessibility);
436 }
437 return;
438 }
439 if (!accessibilityEventMap.has(eventType)) {
440 if (Trace.isEnabled()) {
441 Trace.write(`${cls} - unknown event`, Trace.categories.Accessibility);
442 }
443 return;
444 }
445 const eventInt = accessibilityEventMap.get(eventType);
446 if (!text) {
447 return androidView.sendAccessibilityEvent(eventInt);
448 }
449 const accessibilityEvent = android.view.accessibility.AccessibilityEvent.obtain(eventInt);
450 accessibilityEvent.setSource(androidView);
451 accessibilityEvent.getText().clear();
452 if (!text) {
453 applyContentDescription(view);
454 text = androidView.getContentDescription() || view['title'];
455 if (Trace.isEnabled()) {
456 Trace.write(`${cls} - text not provided use androidView.getContentDescription() - ${text}`, Trace.categories.Accessibility);
457 }
458 }
459 if (Trace.isEnabled()) {
460 Trace.write(`${cls}: send event with text: '${JSON.stringify(text)}'`, Trace.categories.Accessibility);
461 }
462 if (text) {
463 accessibilityEvent.getText().add(text);
464 }
465 accessibilityManager.sendAccessibilityEvent(accessibilityEvent);
466}
467export function updateContentDescription(view, forceUpdate) {
468 if (!view.nativeViewProtected) {
469 return;
470 }
471 return applyContentDescription(view, forceUpdate);
472}
473function setAccessibilityDelegate(view) {
474 if (!view.nativeViewProtected) {
475 return;
476 }
477 ensureNativeClasses();
478 const androidView = view.nativeViewProtected;
479 if (!androidView || !androidView.setAccessibilityDelegate) {
480 return;
481 }
482 androidViewToTNSView.set(androidView, new WeakRef(view));
483 let hasOldDelegate = false;
484 if (typeof androidView.getAccessibilityDelegate === 'function') {
485 hasOldDelegate = androidView.getAccessibilityDelegate() === TNSAccessibilityDelegate;
486 }
487 if (hasOldDelegate) {
488 return;
489 }
490 androidView.setAccessibilityDelegate(TNSAccessibilityDelegate);
491}
492function applyContentDescription(view, forceUpdate) {
493 let androidView = view.nativeViewProtected;
494 if (!androidView || (androidView instanceof android.widget.TextView && !view._androidContentDescriptionUpdated)) {
495 return null;
496 }
497 if (androidView instanceof androidx.appcompat.widget.Toolbar) {
498 const numChildren = androidView.getChildCount();
499 for (let i = 0; i < numChildren; i += 1) {
500 const childAndroidView = androidView.getChildAt(i);
501 if (childAndroidView instanceof androidx.appcompat.widget.AppCompatTextView) {
502 androidView = childAndroidView;
503 break;
504 }
505 }
506 }
507 const cls = `applyContentDescription(${view})`;
508 const titleValue = view['title'];
509 const textValue = view['text'];
510 if (!forceUpdate && view._androidContentDescriptionUpdated === false && textValue === view['_lastText'] && titleValue === view['_lastTitle']) {
511 // prevent updating this too much
512 return androidView.getContentDescription();
513 }
514 const contentDescriptionBuilder = new Array();
515 // Workaround: TalkBack won't read the checked state for fake Switch.
516 if (view.accessibilityRole === AccessibilityRole.Switch) {
517 const androidSwitch = new android.widget.Switch(Application.android.context);
518 if (view.accessibilityState === AccessibilityState.Checked) {
519 contentDescriptionBuilder.push(androidSwitch.getTextOn());
520 }
521 else {
522 contentDescriptionBuilder.push(androidSwitch.getTextOff());
523 }
524 }
525 if (view.accessibilityLabel) {
526 if (Trace.isEnabled()) {
527 Trace.write(`${cls} - have accessibilityLabel`, Trace.categories.Accessibility);
528 }
529 contentDescriptionBuilder.push(`${view.accessibilityLabel}`);
530 }
531 if (view.accessibilityValue) {
532 if (Trace.isEnabled()) {
533 Trace.write(`${cls} - have accessibilityValue`, Trace.categories.Accessibility);
534 }
535 contentDescriptionBuilder.push(`${view.accessibilityValue}`);
536 }
537 else if (textValue) {
538 if (textValue !== view.accessibilityLabel) {
539 if (Trace.isEnabled()) {
540 Trace.write(`${cls} - don't have accessibilityValue - use 'text' value`, Trace.categories.Accessibility);
541 }
542 contentDescriptionBuilder.push(`${textValue}`);
543 }
544 }
545 else if (titleValue) {
546 if (titleValue !== view.accessibilityLabel) {
547 if (Trace.isEnabled()) {
548 Trace.write(`${cls} - don't have accessibilityValue - use 'title' value`, Trace.categories.Accessibility);
549 }
550 contentDescriptionBuilder.push(`${titleValue}`);
551 }
552 }
553 if (view.accessibilityHint) {
554 if (Trace.isEnabled()) {
555 Trace.write(`${cls} - have accessibilityHint`, Trace.categories.Accessibility);
556 }
557 contentDescriptionBuilder.push(`${view.accessibilityHint}`);
558 }
559 const contentDescription = contentDescriptionBuilder.join('. ').trim().replace(/^\.$/, '');
560 if (contentDescription) {
561 if (Trace.isEnabled()) {
562 Trace.write(`${cls} - set to "${contentDescription}"`, Trace.categories.Accessibility);
563 }
564 androidView.setContentDescription(contentDescription);
565 }
566 else {
567 if (Trace.isEnabled()) {
568 Trace.write(`${cls} - remove value`, Trace.categories.Accessibility);
569 }
570 androidView.setContentDescription(null);
571 }
572 view['_lastTitle'] = titleValue;
573 view['_lastText'] = textValue;
574 view._androidContentDescriptionUpdated = false;
575 return contentDescription;
576}
577//# sourceMappingURL=index.android.js.map
\No newline at end of file