My Android app contains a custom slider control based on the SeekBar, and I want to attach a custom text phrase to my control to explain its use for Accessibility.
I have done this successfully using View.setContentDescription(text), and TalkBack correctly speaks the phrase when I request focus on my slider control from Activity.onCreate.
So far, so good. However, when I touch the control, which I believe sets the AccessibilityFocus on my Android API 16 test device, extra words are being added to the spoken phrase, i.e. '...seek control. 0 per cent'. I want to remove these additional words.
I have tried to eliminate them using event.getText().clear() in View.onInitializeAccessibilityEvent(event) without success. Echoing the event to LogCat reports the correct phrase in event.contentDescription and no entries in event.text, but the extra words appear both in the audio output from the device hardware and in the on-screen debug text displayed by Menu->Settings->Accessibility->TalkBack->Settings->Developer Settings->Display Speech Output.
Please can anyone suggest where the extra words are being added, and how to eliminate them?
Any constructive suggestions would be welcomed. Thanks.
Update
I can see that some Explore By Touch (initial single-tap) event on my custom control does not pass through either its onInitializeAccessibilityEvent or dispatchPopulateAccessibilityEvent methods as I am deliberately calling event.setContentDescription(null). Despite this, there is an AccessibilityEvent being generated with my custom control's ContentDescription, set in Activity.onCreate in code, plus the extra words I'm trying to eliminate.
I've also set an AccessibilityDelegate on my custom control's parent ViewGroup to give visibility of its onRequestSendAccessibilityEvent calls. This confirms that no event containing my ContentDescription is passing through.
This is very puzzling, and happens on both the emulator and real hardware with API 16. Any ideas?
You also need to override http://developer.android.com/reference/android/view/View.html#onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo)
and set the contentDescription there.
If you want to remove the 0%, I would try to change the class in AccessibilityNodeInfo/AccessibilityEvent:
http://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html#setClassName(java.lang.CharSequence)
I believe that this is a bug in TalkBack, and have raised Google Eyes-Free issue #375, including example code.
Update: Google have now archived this. Link moved to: http://code.google.com/archive/p/eyes-free/issues/375
Related
I have been building relatively simple Android Keyboard from ground up following this Android SoftKeyboard sample. I can't seem to find any solution that would allow me to disable spell-checking functionality with my custom keyboard. Every text typed has a black underline indicating possible spell error even though I have not implemented spell-checking services.
Tried to find appropriate code fragment that disables spell-checking in Android/LatinIME but in vain.
Any tips are highly appreciated
The black underline is called composing text. Its used to show text that may be replaced by an autocorrect or other action- it isn't fully finished text yet. Its done by calling setComposingText. Instead of using that, use commitText and it won't use the underline version (and a dozen other differences under the hood).
Note that if you're exactly following the linked code you'll have to make a lot of other changes too, to move from word at a time to letter at a time input (composing text is completely replaced each time a new input is made, so you need to send down the entire word until you commitText of complete the composing text. SO you probably have a but of work to change it to use commitText).
I have an unlock screen where the user is prompted to enter a four digit pin. If the user enters their pin incorrectly, a previously invisible TextView is shown with an error message. At this point it would be useful for TalkBack to read the contents of the error message out loud.
Through some experimentation, I realized I could set android:focusableInTouchMode="true" on the view and programmatically call View#requestFocus(). This works the first time, but fails on subsequent errors since the view already has focus. Also it seems like a bad idea in general to override the current view focus.
I then tried invoking View#announceForAccessibility(java.lang.CharSequence) when the error message is displayed. Apparently this method will silently fail if the view is not currently visible. No problem and otherwise it works perfectly. However, it's only available in API level 16+ (Jelly Bean) which really limits it's usefulness. There has to be a better solution since TalkBack supports API level 7+.
I've watched both the 2011 and 2012 Google I/O sessions on accessibility, but neither seem to cover this basic use case. What's the best way to do this?
Edit 1: TLDR; Is there a way to force TalkBack to read some text out loud prior to the introduction of View#announceForAccessibility(java.lang.CharSequence) in Jelly Bean?
You should be able to use View.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) on your TextView to trigger TalkBack in the same way that View.requestFocus() would. Since it only triggers the event, and doesn't actually focus the View, it shouldn't crash after the first time.
I was using the accepted answer, which works well. However, I didn't like the misleading sound when accessibility focus was set on the text view - the same sound as when input focus is given to an EditField by double-tapping (a sort of drawer-open sound), because the input focus had not actually moved from the EditText with inputfocus (eg with cursor).
So I tried:
m_textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);`
and interestingly it works - the label is read, without moving any focus or giving any other sound.
OK, if you are using L or later the better answer is to use:
http://developer.android.com/reference/android/view/View.html#setAccessibilityLiveRegion(int)
This will do all the work for you.
Another way would be to, when TalkBack is activated, additionally show a Toast message with the error text. This is also being read out aloud.
Recommended way is to use the below code after textview change.
textview.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
It will read the contents without focusing on it.
To get a TextView to display (and act friendly with) Html strings my code looks something like:
// itemHtml is a String of HTML defined above
TextView itemContent = (TextView) findViewById(R.id.itemContent);
itemContent.setText(Html.fromHtml(itemHtml));
itemContent.setMovementMethod(LinkMovementMethod.getInstance());
If the Html string has a link, TextView results in links that are clickable and focusable. When the user focuses on a specific link (e.g. by using the d-pad), the link text changes in some significant way to show that focus was obtained.
The problem is that when I test this same pattern using devices with a d-pad using Honeycomb (e.g. a Google TV) or Ice Cream Sandwich flavors of Android, the link in the text shows no noticeable indication that the link has focus.
I know it is getting focus, because when you then hit enter it does the specified action. You can even move around between various links in the text; you're just left guessing which link you're currently at, which results in a very bad user experience.
Is there something I'm doing wrong? Is there some way to fix this or work around this?
Edit: After going a bit nuts, I finally thought I found a solution. However, this solution only works for Honeycomb. ICS is still not solved!
As of API 11, Android has a new setting on TextViews for defining whether the text is selectable.
You can set it using setTextIsSelectable(true) on a TextView, or define it in the XML layout android:textIsSelectable="true"
It's probably best to define it in XML, so that keeping the app backwards-compatible is trivial. Just make sure you're targeting version >= 11, or you'll probably get an error.
The way HTML.fromHTML operates is by creating "spans" with varying effects throughout the various characters of the string. One workaround for this would be to use ClickableSpan coupled with another of the CharacterStyles to colorize the text as clickable. The previous span will allow you to register a callback, and this callback could be to broadcast an intent to view a url (which would open a browser).
The text colour state lists for Honeycomb+ might not set the focused state to a different colour, or you override the colour to be constant.
Check the colors + styles in your_android_sdk_directory/android-14/data/res/
Setting the text to android:autoLink="web" might also help?
The best way to do that is to add CSS styling to your html. I know Android supports :hover selector. So you might right something like this:
String myLink = "your link"
Html.fromHtml(myLink);
and find a way to include CSS data to it: (I'm not sure how but I think it's possible)
a :hover {
color: red;
}
UPDATE:
I think the answer of your question is there.
I'm designing my first Android IME and am a bit overwhelmed. My goals are accessibility-related, and as such I wonder if perhaps I'm trying things with the IME framework that just aren't possible. I've taken the default softkeyboard example and have it working along with some of my modifications, so I've definitely understood at least some of this, but there isn't a whole lot of documentation on some of the things I'm attempting. Here's what I mean:
Currently, Android phones with touchscreen-only keyboards are inaccessible. I currently have an accessible touchscreen keyboard using methods similar to those used in IOS/VoiceOver, so that part of the project is done and fairly straight-forward to accomplish in the IME framework.
Currently, Android's accessibility API doesn't provide accessible feedback for navigating text fields. That is, with an Android screen reader loaded, if you type the word "this", you'll hear individual characters spoken as you type them, but if you then try left-arrowing over the "s", that isn't spoken. So I want to trap movements and provide spoken feedback for the elements navigated past. Here's where I start encountering problems.
I currently have speech feedback for left and right arrowing, using getCurrentInputConnection.getTextBeforeCursor(1, 0) for arrowing left, and a similar call for arrowing right. This gets the character currently under the cursor after the movement is processed, and all is good.
My challenge, though, comes when arrowing up and down between lines. Ideally, I'd like to grab the current line of text and speak that, but I don't see any way to do that. The only thing I can think of is some combination of getTextBefore/AfterCursor(Integer.MAX_VALUE, 0) and combining those values, determining the current line by filtering for the previous and next \n. Is there another way--getting the entire text content of the field as a single block of text and using the cursor position to determine which piece of that text represents the current line? I'm seeing something about extracted text in the various input method classes, and it seems like there may be a way to monitor that, but I don't know if that is at all useful to me (I.e. would that return the entire field content?)
My next goal is providing standard text navigation capabilities. Android accessibility doesn't currently include touchscreen exploration, so it is impossible to otherwise scroll a large block of text. I don't necessarily expect folks to write novels on their phones, but I'd like to provide quick gestures or commands to move up/down paragraphs, and to the top/bottom of longer fields. Does the IMF provide methods for cursor movement, or is that outside of its authority?
Honestly, I didn't get the first part :(
For your second question, you will need to handle it by hand.
For instance, to add a key with a down drawable and make it work you will need to:
In the onKey method check for the code.
If it's 20, you should do a sendDownUpKeyEvents of that key event.
What I'd like to do is change the state (really, the background) of an EditText to reflect validity of its contents. E.g. if the user enters 999 where 999 is contextually invalid, the EditText should have a red border in place of the default orange border, likewise once the text is valid it should have a green border.
Methods I've explored:
Changing the style of the EditText programmatically via something like editor.setStyle(R.styles.SomeID). Seems to be impossible within android.
Adding custom states (state_valid, state_invalid) in R.attr, associating them with red/ green 9-patches, then calling drawable.setState() with one of these states. This worked in the sense that the state could be read back via getState(), but the border did not change colour.
Setting the background resource directly upon detection of (in)validity. This works ok, causing the correct visual effect, but seems a little hokey, and allows only one state (e.g. I have to manually check for whether the EditText is pressed, enabled etc).
Given limited UI real-estate I am hesitant to introduce a separate UI element to visually feedback the text's validity to the user, hence my desire to display it in the EditText itself.
So.. is this something that's even feasible? It seems like a fairly common use case, so has anyone achieved what I'm trying to do in a straightforward and elegant manner?
I would recommend changing the text color to indicate validity, rather than changing the color of the focus ring by any of the techniques you describe (of which only #3 seems practical).
Another possibility is to try setCompoundDrawablesWithIntrinsicBounds() to modify an icon on the left or right side of the EditText contents to indicate validity. I remember discussing this technique with somebody a few months back and forget if they got it working or not.
And, of course, another option is to not allow invalid input, via a custom input filter or listener or something.
Well, I'd just extend the EditText class and build the desired functionality on top ( using the third approach you are suggesting, because it works :-) ). Doing this, you have to walk the way only once, and are open to change your implementation once you know the best way ( I would have personally solved it also using the third approach, seems fine to me ).
i think a call to invalidateDrawable(yourDrawable) would work with approach number 2.
i didn't try .. but it make sense