feat(core): first class a11y support (#8909)

This commit is contained in:
Morten Sjøgren
2021-01-29 20:51:51 +01:00
committed by Nathan Walker
parent 577b1e9dad
commit f2e21a50a7
43 changed files with 2938 additions and 47 deletions

View File

@@ -6,6 +6,7 @@ import { layout, RESOURCE_PREFIX, isFontIconURI } from '../../utils';
import { colorProperty } from '../styling/style-properties';
import { ImageSource } from '../../image-source';
import * as application from '../../application';
import { isAccessibilityServiceEnabled, updateContentDescription } from '../../accessibility';
export * from './action-bar-common';
@@ -298,7 +299,7 @@ export class ActionBar extends ActionBarBase {
}
}
public _updateTitleAndTitleView() {
public _updateTitleAndTitleView(): void {
if (!this.titleView) {
// No title view - show the title
const title = this.title;
@@ -313,6 +314,9 @@ export class ActionBar extends ActionBarBase {
}
}
}
// Update content description for the screen reader.
updateContentDescription(this, true);
}
public _addActionItems() {
@@ -447,6 +451,74 @@ export class ActionBar extends ActionBarBase {
this.nativeViewProtected.setContentInsetsAbsolute(this.effectiveContentInsetLeft, this.effectiveContentInsetRight);
}
}
public accessibilityScreenChanged(): void {
if (!isAccessibilityServiceEnabled()) {
return;
}
const nativeView = this.nativeViewProtected;
if (!nativeView) {
return;
}
const originalFocusableState = android.os.Build.VERSION.SDK_INT >= 26 && nativeView.getFocusable();
const originalImportantForAccessibility = nativeView.getImportantForAccessibility();
const originalIsAccessibilityHeading = android.os.Build.VERSION.SDK_INT >= 28 && nativeView.isAccessibilityHeading();
try {
nativeView.setFocusable(false);
nativeView.setImportantForAccessibility(android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO);
let announceView: android.view.View | null = null;
const numChildren = nativeView.getChildCount();
for (let i = 0; i < numChildren; i += 1) {
const childView = nativeView.getChildAt(i);
if (!childView) {
continue;
}
childView.setFocusable(true);
if (childView instanceof androidx.appcompat.widget.AppCompatTextView) {
announceView = childView;
if (android.os.Build.VERSION.SDK_INT >= 28) {
announceView.setAccessibilityHeading(true);
}
}
}
if (!announceView) {
announceView = nativeView;
}
announceView.setFocusable(true);
announceView.setImportantForAccessibility(android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES);
announceView.sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED);
announceView.sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
} catch {
// ignore
} finally {
setTimeout(() => {
// Reset status after the focus have been reset.
const localNativeView = this.nativeViewProtected;
if (!localNativeView) {
return;
}
if (android.os.Build.VERSION.SDK_INT >= 28) {
nativeView.setAccessibilityHeading(originalIsAccessibilityHeading);
}
if (android.os.Build.VERSION.SDK_INT >= 26) {
localNativeView.setFocusable(originalFocusableState);
}
localNativeView.setImportantForAccessibility(originalImportantForAccessibility);
});
}
}
}
function getAppCompatTextView(toolbar: androidx.appcompat.widget.Toolbar): typeof AppCompatTextView {

View File

@@ -5,6 +5,7 @@ import { Color } from '../../color';
import { colorProperty, backgroundColorProperty, backgroundInternalProperty } from '../styling/style-properties';
import { ImageSource } from '../../image-source';
import { layout, iOSNativeHelper, isFontIconURI } from '../../utils';
import { accessibilityHintProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityValueProperty } from '../../accessibility/accessibility-properties';
export * from './action-bar-common';
@@ -99,6 +100,46 @@ export class ActionBar extends ActionBarBase {
return null;
}
[accessibilityValueProperty.setNative](value: string): void {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityValue = value;
const navigationItem = this._getNavigationItem();
if (navigationItem) {
navigationItem.accessibilityValue = value;
}
}
[accessibilityLabelProperty.setNative](value: string): void {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityLabel = value;
const navigationItem = this._getNavigationItem();
if (navigationItem) {
navigationItem.accessibilityLabel = value;
}
}
[accessibilityHintProperty.setNative](value: string): void {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityHint = value;
const navigationItem = this._getNavigationItem();
if (navigationItem) {
navigationItem.accessibilityHint = value;
}
}
[accessibilityLanguageProperty.setNative](value: string): void {
value = value == null ? null : `${value}`;
this.nativeViewProtected.accessibilityLanguage = value;
const navigationItem = this._getNavigationItem();
if (navigationItem) {
navigationItem.accessibilityLanguage = value;
}
}
public createNativeView(): UIView {
return this.ios;
}
@@ -148,7 +189,18 @@ export class ActionBar extends ActionBarBase {
}
}
public update() {
private _getNavigationItem(): UINavigationItem | null {
const page = this.page;
// Page should be attached to frame to update the action bar.
if (!page || !page.frame) {
return null;
}
const viewController = <UIViewController>page.ios;
return viewController.navigationItem;
}
public update(): void {
const page = this.page;
// Page should be attached to frame to update the action bar.
if (!page || !page.frame) {
@@ -228,6 +280,12 @@ export class ActionBar extends ActionBarBase {
if (!this.isLayoutValid) {
this.layoutInternal();
}
// Make sure accessibility values are up-to-date on the navigationItem
navigationItem.accessibilityValue = this.accessibilityValue;
navigationItem.accessibilityLabel = this.accessibilityLabel;
navigationItem.accessibilityLanguage = this.accessibilityLanguage;
navigationItem.accessibilityHint = this.accessibilityHint;
}
private populateMenuItems(navigationItem: UINavigationItem) {