Changing text spoken by talkback in Android - android

I am trying to change the text announced by TalkBack when an ImageView is focused through accessibility.
The Android documentation states that we should create an AccessibilityDelegate, and override onPopulateAccessibilityEvent (I am using the support library because I am also supporting GingerBread)
Thus, my code is the following:
public static void setImageDelegate(View view) {
AccessibilityDelegateCompat delegate = new AccessibilityDelegateCompat() {
#Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
event.getText().add(event.getContentDescription() + ", image");
}
};
ViewCompat.setAccessibilityDelegate(view, delegate);
}
When I call this function on my imageview, the delegate gets set, but the modified text is not being read. It simply reads the original content description. Am I doing something wrong or missing something about the accessibility functions?
Stepping through the code, it seems to be adding the correct text, but still, no change in spoken text.
Note: the above is a contrived example, content description could be used, but I'm trying to figure out why it doesn't work before I try it on custom views.

In ICS and above, TalkBack doesn't use the accessibility event text in most cases. Instead, it checks the text and content description of the AccessibilityNodeInfo exposed by the view. You would need to override onInitializeAccessibilityNodeInfo.
In most cases, though, you would just want to call View.setContentDescription.
In this particular case, you shouldn't set anything since TalkBack handles speaking control types and capabilities. We strongly advise developers against adding descriptions like "button" or "image."

Related

Android: How to change the control type for Accessibility

There are some controls in our app which we'd like to update the control type read out by Talkback. For example: A TextView which would better be described as a link, or an ImageView that would better be described as a button.
We could simply update the content description to report out the desired control type, though I am wondering if there is a better way? If there is another way, can it be done both through the view XML and dynamically in the code behind?
Yes, it is possible to change the type. It is called roleDescription. You would change it as follows:
ViewCompat.setAccessibilityDelegate(yourView,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(v, info)
info.roleDescription = "Button"
}
})
(use string resources and translate the strings to all languages supported by your app)
This cannot be done via XML by default, but you could look into writing your own binding adapter for this.

Intercept link LinkMovementMethod with a Yes/No dialog

I have a standard LinkMovementMethod established in my TextView to push a web Activity of some sort when the user touches a link. However, I want to establish a "do you want to see the link" dialog rather than taking the user straight to the webpage. I've tried overriding the touch methods but it all gets a little convoluted. A little help?
You can accomplish it in two ways:
Create custom Spans: more complicated, but you can accomplish more customised text consisting of clickable parts (or bold, differently coloured etc). To know more, check out ClickableSpan and SpannableStringBuilder
Extend LinkMovementMethod to accept custom click listener
In my opinion second solution is better in basic cases like yours. Here is how you can do it:
Copy this java class: InternalLinkMovementMethod to your project
Add set the link movement method of your TextView to this custom one, providing a click listener:
OnLinkClickedListener clickListener = new OnLinkClickedListener() {
#Override
public boolean onLinkClicked(String linkText) {
// here you can handle your click, eg show the dialog
// `linkText` is the text being clicked (the link)
// return true if handled, false otherwise
}
}
yourTextView.setMovementMethod(new InternalLinkMovementMethod(clickListener));

disabling view announcement when focused (Talkback Enabled)

I'm using a custom dynamic contentDescription for my textview, so it has been implemented in my java class and not in my xml file.
private void enableButton(TextView textView, boolean enabled) {
if (textView == null) {
return;
}
if (!enabled) {
textView.setContentDescription(textView.getResources().getString(R.string.install_disabled));
} else {
textView.setContentDescription(textView.getResources().getString(R.string.install));
}
textView.setEnabled(enabled);
}
After I'm enabling my textview to be clickable, and when talkback is enabled, focusing on my textview is announcing the state of my textview which is "disabled". Is there a way to not announce that state?
I do not want to set the accessibility to be not important because I still want my dynamic contentDescription to be recited when talkback users focus on the textview.
Suggestion:
I believe the culprit is the "setEnabled" method that is somehow triggering and announcing the state of the textview, but I'm still not able to stop it from reciting that last.
My first answer is: LEAVE IT ALONE! The "disabled" announcement tells a TalkBack user that there is a user interface control there, that under some circumstances can be interacted with, but is not currently active. Given your description, this is exactly what you have. To remove the announcement is actually going to make things WORSE from an accessibility perspective, the explanations for why this is the case are covered in WCAG 1.3.1.
Definitions:
Button = android.widget.Button
button = a user interface component that does something when you click it.
Text = a user interface component that conveys information, but is not active
Long story short, the fact that the control is ever in a state that it can be active and "not disabled" is significant information on its own, and SHOULD be shared with the user. ESPECIALLY since you're using a "TextView" to implement this. This isn't a super uncommon practice, but one of the ways TalkBack calculates roles (button, link, image, text, etc) is by looking at the Class/Type of object. So, when it sees a TextView, it is going to assume Text, unless you inform it otherwise. Now, since you have added click listeners to your TextView (or Gesture Recognizers, or whatever) TalkBack may be able to figure out that the thing you're dealing with is actually a "button", and it may not. REGARDLESS, the fact that this "button" (lower case B!) is not active is important state to share with the user, and communicates to them the fact that they can somehow enable it and come back and interact with it later. This is immensely important information! Imagine if every button/link on a WebPage looked exactly like plane text? How would you know what to interact with?
Now, I will show you the different pieces of this puzzle, as information, but I really do encourage you to leave the announcement alone. This is coming from someone who routinely speaks at Accessibility conferences on Native Android development, PLEASE LEAVE THIS ANNOUNCEMENT IN. To not do so shows a misunderstanding of how users with sight impairments want to perceive controls within your application, and the information that is important to them.
The setEnabled() function on a TextView corresponds directly with the isEnabled() property of AccessibilityNodeInfo.
In order to implement the behavior you want, you want the AccessibilityNodeInfo representation of your TextView to be different from that of the actual representation of your TextView. In order to do this you can use AccessibilityDelegate, I'm actually not sure which callback you want to use. In one of these the node is likely to be "sealed" and in one of them it might not be sealed yet. You obviously want the one where the node is not yet sealed! Regardless the general approach is:
textView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
#Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
// Let the default implementation populate the info.
super.onInitializeAccessibilityNodeInfo(host, info);
// Override this particular property
info.setEnabled(whateverYouThinkItShouldBe);
}
});
Use setClickable(false) to replace setEnabled(false) will solve this problem.

Android Acccessibility: How do I change the text read out loud for an EditText View

By default, the Accessibility Services will read out the following for an EditText view
If the EditText has a value entered = it will read out that value
If there is not value entered = it will read out the "hint"
I want it to read out something completely different in both cases.
My xml snippet is
<EditText
android:id="#+id/my_edit_text"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:editable="false"
android:focusable="true"
android:hint="my hint text"/>
I have to support API 14 and onwards.
I do not want to go to the trouble of extending EditText for this one single case, therefore I am using am AccessibilityDelegate.
mEditTextView.setAccessibilityDelegate(accessibilityDelegate);
From the documentation I understand that in my delegate I only have to overwrite those methods in the delegate for which I would like to change the behaviour. All other methods will default to the View's implementation.
http://developer.android.com/reference/android/view/View.AccessibilityDelegate.html
http://developer.android.com/reference/android/view/View.html
The doc for "onPopulateAccessibilityEvent" says : "Gives a chance to the host View to populate the accessibility event with its text content."
The doc for "dispatchPopulateAccessibilityEvent" says : "Dispatches an AccessibilityEvent to the host View first and then to its children for adding their text content to the event." and that the default behaviour is to call "onPopulateAccessibilityEvent" for the view itself and then "dispatchPopulateAccessibilityEvent" on all its children
http://developer.android.com/guide/topics/ui/accessibility/apps.html
This doc says under "onPopulateAccessibilityEvent" "*If your implementation of this event completely overrides the output text without allowing other parts of your layout to modify its content, then do not call the super implementation of this method in your code."
Therefore my delegate is the following
View.AccessibilityDelegate accessibilityDelegate = new View.AccessibilityDelegate() {
#Override
public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
event.getText().add("Apples");
}
};
Why when I use the keyboard to navigate to or use the screen to tap on the EditText view it is still reading "my hint text" and not "Apples"?
If I use a debugger, I see that before I set the event text, the text is empty and after I set it, it is "Apples" yet the TalkBack still reads out the hint.
Weirdly if I overwrite "onInitializeAccessibilityNodeInfo" and send an event with my desired text, then this desired text gets read out (see code snippet below). But this seems wrong to me as the "onInitializeAccessibilityNodeInfo" is reacting to the EditText's event but then just raising a new one.
#Override
public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info){
super.onInitializeAccessibilityNodeInfo(v, info);
...
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.getText().add("Pears");
event.setClassName(className);
event.setPackageName(packageName);
...
v.getParent().requestSendAccessibilityEvent(v, event);
}
We can change the text read out loud of EditText view by doing the following:
View.AccessibilityDelegate accessibilityDelegate = new View.AccessibilityDelegate() {
#Override
public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(v, info);
info.setText("some customized text")
}
};
and then set this delegate to the EditText View.
This sounds very similar to a problem I previously encountered: Android: How to eliminate spoken text from AccessibilityEvents when extending SeekBar?
Have you tried (as a test) clearing the hint text in onPopulateAccessibilityEvent, rather than just adding the 'Apples' text? I seem to remember that my empirical results did not match the Android documentation, particularly for Android OS prior to API 14.

Text selection listener in Android(API level 7)

I need a listener which is called every time the selection in an EditText changes.
I googled around but I couldn't find anything useful for API level 7.
I'm writing a Text Editor and I want the bold/italic/underlined button appear selected every time the user selects bold/italic/underlined text.
Pretty old question, but someone might still need this, so here's my solution : since the text selection accomplished with long press on the text, I simply used the following :
editText.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
// do whatever you need to do on text selection
}
});
This allows for custom behavior on text selection and doesn't prevent the user from copying/pasting.
The better way to do it would be to extend the EditText and then based upon how you would want to manage the changing text, you could override one of the 2 methods to work out your customized behavior.
If you want the selection changed then you could use the onSelectionChanged() method and implement your code there.
In case you want to implement something when the text changes in your editor then you could use, onTextChanged().

Categories

Resources