Accessibility function implementation problems in Android
Asked Answered
S

2

27

I'm developing application that views books. There is a screen (Activity) which shows a book. It has custom view, something similar to ViewSwitcher and every page is a bitmap that is rendered by a custom View.

Now I should implement accessibility function - book should be read by the phone (audio).

I've read Accessibility section here https://developer.android.com/guide/topics/ui/accessibility/index.html but it is not clear enough.

I use SupportLibrary for accessibility management and now I have this code in ViewGroup (which manages book pages). Code 1:

private class EditionPagesViewSwitcherAccessibilityDelegate extends AccessibilityDelegateCompat {

    private int mPageCount;
    private double[] mPageRange;

    @Override
    public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(host, event);
        event.setClassName(EditionPagesViewSwitcher.class.getName());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            event.setScrollable(canScroll());
        }
        if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED && updatePageValues()) {
            event.setItemCount(mPageCount);
            // we use +1 because of user friendly numbers (from 1 not 0)
            event.setFromIndex((int) (mPageRange[0] + 1));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                event.setToIndex((int) (mPageRange[1] + 1));
            }
        }
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);
        info.setClassName(EditionPagesViewSwitcher.class.getName());

        info.setScrollable(canScroll());
        info.setLongClickable(true);
        if (canScrollForward()) {
            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
        }
        if (canScrollBackward()) {
            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
        }
    }

    @Override
    public boolean performAccessibilityAction(final View host, final int action, final Bundle args) {
        if (super.performAccessibilityAction(host, action, args)) {
            return true;
        }
        switch (action) {
            case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
                if (canScrollForward()) {
                    showNext();
                    return true;
                }
            }
            return false;
            case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
                if (canScrollBackward()) {
                    showPrevious();
                    return true;
                }
            }
            return false;
        }
        return false;
    }

Here is code from page view Code 2:

    @Override
    public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(host, event);

        event.setClassName(EditionPageView.class.getName());
        if (hasText()) {
            event.getText().add(getPageRangeText());
            final String trimText = mSurfaceUpdateData.getPageText().trim();
            if (trimText.length() > MAX_TEXT_LENGTH) {
                event.getText().add(trimText.substring(0, MAX_TEXT_LENGTH));
//              event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
            }
            else {
                event.getText().add(trimText);
            }
        }
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);

        info.setClassName(EditionPageView.class.getName());
    }

Because page text data loads asynchronous first time accessibility don't have any text while executes onInitializeAccessibilityEvent code. And then when data have been loaded I fire AccessibilityEvent.TYPE_VIEW_SELECTED and AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED events. Then onInitializeAccessibilityEvent executes again and phone "read" book text.

So my questions:

  1. Is my Accessibility implementation right? May be it is design wrong? Because I didn't find any good tutorial about this feature.
  2. Why I need to use SDK versions checks in Support implementations in Code 1? Why support implementation doesn't handle it correctly?
  3. Is firing TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED really needed? Or may be some other code should be implemented?
  4. The main question. In Code 2 there is commented code line. This code statement substring text to be less then MAX_TEXT_LENGTH (it's 3800) because if text is bigger nothing is played. Nothing. Is it accessibility restriction? Any other text that is less then this value is played well.
  5. Does anyone know where I can find any good tutorial? (yes I saw samples).
  6. Does anyone have any custom realizations to look through?

UPDATED

Well. Here is some answers:

  1. As I can see TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED events are not needed if you don't want this text to be read as soon as you get it.
  2. On Nexus 7 all large text is played well (text up to 8000 symbols), so this issue doesn't reproduce on it, but on Samsung Galaxy Tab 10.1 (Android 4.0.4) and Genymotion emulator of Tab 10.1 with Android 4.3 does. And this is strange...
Spinthariscope answered 9/12, 2013 at 9:30 Comment(1)
In project for USA National Library Service for the Blind, the bookreader downloaded books in audio format. Each book was read by professional readers. Other accessibility features did work but support varied by platform and Android version.Clique
G
1

4.. According to the documentation of String.substring() The first argument you pass is the start index in the original string, the second argument is the end index in the original string.

Example:

String text = "Hello";
partOfText = text.substring(2,text.length() - 1);

partOfText equals to "llo" (the first char is index 0)

So by putting your constant MAX_TEXT_LENGTH as a first argument, it would start at index 3800 to take out the substring.

http://developer.android.com/reference/java/lang/String.html#substring(int)

Glottochronology answered 17/12, 2013 at 15:41 Comment(2)
Hi, thanks, but if you look through developer.android.com/reference/java/lang/…, int) you will see that end simbol is exclusive and my implementation is right. Moreover this is not my question. My question is why Accessibility has this restriction on Android Galaxy Tab 10.1?Spinthariscope
Sorry in that case I misunderstood your questionGlottochronology
H
0

You are right MAX_TEXT_LENGTH is 3800.

About your doubt,

this code:

 event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
        }

you are trying to substring "trimText" from MAX_TEXT_LENGTH to trimText.length() ! Supposing that trimText = "STACK", trimText.length() = 5, then trimText.substring(3800,5) is going to be ?

At first, this doesn't have sense, using correctly would be like this: trimText.substring(0,2) = "ST";

Hemia answered 15/7, 2014 at 17:54 Comment(1)
Yes I know that this line will fall if the text is less than MAX_TEXT_LENGTH. But in my case it is not. So I remove this check for stackoverflow code. Moreover in my code this line is commented. And if I uncomment this line then nothing will play. And this is question - why? :) This question is quite old.. I have already finished this project.Spinthariscope

© 2022 - 2024 — McMap. All rights reserved.