スクリーンリーダー対応


概要

KARTEのアプリ内メッセージが表示されている場合のOSのスクリーンリーダー機能であるVoiceOver / TalkBackの挙動について説明します。

要約

  • 配信されるアプリ内メッセージは必ずmessage_close イベントを発火させる必要があり、場合によってはアクションのカスタマイズが必要です
  • HTML,CSSが登録されず、JavaScriptのみ記載されているアクションについては、スクリーンリーダー利用環境では非推奨です
  • message_closeイベントを発火できない接客を配信する場合は、アプリ側にスクリーンリーダー機能のフォーカスが当たりません。
    • 適切な体験を提供できない場合には、スクリーンリーダーに対応できないアクションはスクリーンリーダー利用環境下に配信しない、もしくはスクリーンリーダー利用環境下でも利用可能なアクションを用意するといった対応が必要です
    • message_closeできない接客の具体例としては、画面に常駐する接客(例: chat機能、最小化時にユーザーの操作で閉じることができない接客)などが挙げられます

KARTEのアプリ内メッセージの挙動と、スクリーンリーダーのフォーカス

iOSのVoiceOver/ AndroidのTalkBack機能はジェスチャに基づくスクリーンリーダー機能です。

KARTEのアプリ内メッセージはアプリの前面のWebViewを展開し、その上に表示しています。この前面のWebview上に表示されているアプリ内メッセージがスクリーンリーダーの読み上げ/操作の対象になります。アプリ内メッセージを閉じた場合には前面のWebviewが消え、Webviewからアプリにフォーカスが戻りユーザーは引き続きアプリの操作を続けることができます。


アプリ内メッセージのスクリーンリーダー対応

配信されるアクションの推奨要件

配信する接客は、必ずユーザーの操作でアクションを閉じれるようにする必要があります
通常はアクションを閉じた場合、アプリ側にスクリーンリーダーのフォーカスが戻るため、ユーザーは通常通りアプリの利用を続けることができます。閉じれない状態になっているアクションが配信された場合は、アプリ側にフォーカスを戻すことができなくなります。

以下がアクションを閉じる方法です。

  • タップ時に閉じる要素にはkarte-closeのクラスを付与して、タップ時に確実に閉じるようにする
    • 注意点として、閉じるためのUI(バツボタン等)がスクリーンリーダーで操作できない状態になっていると、閉じることはできないため、アクション自体はスクリーンリーダーに対応した記述である必要があります
  • アクションのスクリプト上でtracker.track(”message_close”)を実行し、message_closeイベントを発火させる

message_closeできないアクションはアプリ側にスクリーンリーダーのフォーカスを戻せないため、そのような状態のアクションはスクリーンリーダー環境下には配信しないことが適切です。
以下ではスクリーンリーダー環境下での配信制御、もしくは配信されてしまったアクションを非表示化するためのワークアラウンドを紹介します。

ターゲティングのためにスクリーンリーダーの状態をイベントで送信する

iOSでの実装方法

VoiceOver有効化状態のイベント送信

Swift APIのUIAccessibility.isVoiceOverRunning の値をイベントのフィールドとして送信してください。以下が実装例です。

let is_screen_reader = UIAccessibility.isVoiceOverRunning;
if is_screen_reader {
    Tracker.track("screen_reader", values: [
        "screen_reader_enabled": true
    ])
}

送信されたイベントに基づき、スクリーンリーダーの有効化状態を判定するセグメントが作成可能です。

UIAccessibility.isVoiceOverRunning に関してはこちらの参照をお願いします

https://developer.apple.com/documentation/uikit/uiaccessibility/1615187-isvoiceoverrunning

スクリーンリーダー有効化状態の切り替え検知

アプリ上でスクリーンリーダーの有効化状態の切り替えを検知してイベントを送信する方法もあります。以下はUIAccessibility.voiceOverStatusDidChangeNotification の値の変更を検知してイベント送信する例です。

 NotificationCenter.default.addObserver(self,
                                        selector: #selector(voiceOverStatusChanged),
                                        name: UIAccessibility.voiceOverStatusDidChangeNotification,
                                        object: nil)
@objc func voiceOverStatusChanged() {
    if UIAccessibility.isVoiceOverRunning {
		    Tracker.track("screen_reader", values: [
		        "screen_reader_enabled": true
		    ])
    } 
}

Androidでの実装方法

TalkBack有効化状態の送信

accessibilityManager の値でスクリーンリーダーの作動状態を取得します。以下が取得とイベント送信の実装例です。

AccessibilityManager accessibilityManager = (AccessibilityManager) this.getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
boolean isAccessibilityEnabled = accessibilityManager.isEnabled();
boolean isExploreByTouchEnabled = accessibilityManager.isTouchExplorationEnabled();
boolean isScreenReader = isAccessibilityEnabled && isExploreByTouchEnabled;

if (isScreenReader) {
    Tracker.track("screen_reader", new HashMap<String, Object>() {{
        put("screen_reader_enabled", true);
    }});
}

送信されたイベントに基づき、スクリーンリーダーの有効化状態を判定するセグメントが作成可能です。

スクリーンリーダー有効化状態の切り替え検知

アプリ上でスクリーンリーダーの有効化状態の切り替えを検知してイベント送信する場合は、accessibilityManager.addTouchExplorationStateChangeListener に変更時に行いたい関数を登録することで実現可能です。以下がその実装例です。

accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);

listener = new TouchExplorationStateChangeListener() {
    @Override
    public void onTouchExplorationStateChanged(boolean enabled) {
        if (enabled) {
				    Tracker.track("screen_reader", new HashMap<String, Object>() {{
				        put("screen_reader_enabled", true);
				    }});
        }
    }
};

// リスナーを登録
accessibilityManager.addTouchExplorationStateChangeListener(listener);

アプリ内メッセージを抑制/非表示化させるワークアラウンド

スクリーンリーダーに対応していないアクションを配信し、アプリ内メッセージがスクリーンリーダーを阻害してしまうような場合に、強制的にアプリ内メッセージの表示を抑制する方法を以下で説明します。

iOSでの抑制/非表示化の実装方法

アプリ上でスクリーンリーダーが利用されている場合にUIAccessibility.isVoiceOverRunningの値がtrueになります。この値を利用してKARTEによるアプリ内メッセージの表示を抑制します。

抑制したい場合には、下記のコードのような形式で表示抑制モードにすることができます。

if UIAccessibility.isVoiceOverRunning {
    InAppMessaging.shared.suppress()
}

InAppMessaging.shared.suppress() に関してはサポートサイトのこちらの参照をお願いします。

https://developers.karte.io/docs/appendix-iam-control-ios-sdk-v2#アプリ内メッセージの表示を抑制する

画面上でスクリーンリーダーのON/OFFを切り替えを検知する場合はUIAccessibility.voiceOverStatusDidChangeNotification の値の変更を検知して、接客の表示抑制モードの切り替えを行います。表示抑制モードを解除した場合、アクセシビリティの処理に関係のないところで表示抑制モードへの切り替えを行っている場合でも接客の表示が再開されるためアプリに合わせて実装してください。

 NotificationCenter.default.addObserver(self,
                                        selector: #selector(voiceOverStatusChanged),
                                        name: UIAccessibility.voiceOverStatusDidChangeNotification,
                                        object: nil)
@objc func voiceOverStatusChanged() {
  if UIAccessibility.isVoiceOverRunning {
    InAppMessaging.shared.suppress();
  } else {
    InAppMessaging.shared.unsuppress();
  }
}

Androidでの抑制/非表示化の実装方法

スクリーンリーダーが利用されている場合にaccessibilityManager.isTouchExplorationEnabled() の値がtrueになります。この値がtrueの時は表示抑制モードにして接客が表示されないようにできます。

AccessibilityManager accessibilityManager = (AccessibilityManager) this.getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
boolean isAccessibilityEnabled = accessibilityManager.isEnabled();
boolean isExploreByTouchEnabled = accessibilityManager.isTouchExplorationEnabled();
if (isAccessibilityEnabled && isExploreByTouchEnabled) {
    InAppMessaging.suppress();
}

InAppMessaging.shared.suppress() に関してはサポートサイトのこちらの参照をお願いします。

https://developers.karte.io/docs/appendix-iam-control-android-sdk-v2#アプリ内メッセージの表示を抑制する

画面上でスクリーンリーダーのON/OFFを切り替えを検知する場合はaccessibilityManager.addTouchExplorationStateChangeListener に変更時に行いたい関数を登録することで検知時に関数を実行することができます。現在のスクリーンリーダーの有効化状態に対応して接客の表示抑制モードを切り替えることができます。

accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);

listener = new TouchExplorationStateChangeListener() {
    @Override
    public void onTouchExplorationStateChanged(boolean enabled) {
        if (enabled) {
		        InAppMessaging.suppress();
        } else {
		        InAppMessaging.unsuppress();
        }
    }
};

// リスナーを登録
accessibilityManager.addTouchExplorationStateChangeListener(listener);