mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(core): first class a11y support (#8909)
This commit is contained in:
committed by
Nathan Walker
parent
577b1e9dad
commit
f2e21a50a7
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user