Android: Adding accessibility to a custom view - java

I'm attempting to implement accessibility on a few custom views for an Android app.
I've condensed what is done in the Google Authenticator app with no luck:
public class CardView extends RelativeLayout {
// ...
#Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
event.setClassName(this.getClass().getName());
event.setPackageName(this.getContext().getPackageName());
event.getText().add("Card Test");
return true;
}
}
All TalkBack reports back is "Double-tap to select" when it's inside a ListView or ViewPager.
Does ViewPager override accessibility events?
What do I need to do in order to have TalkBack say "Card Test" inside ViewPagers and ListViews like I expect it to?

For current versions of Android, you need to set the content description of the view.
myView.setContentDescription("Card Test");

ListView and associated classes expect you to use the onItemSelectedListener instead of assigning an onClickListener to each View (and rightfully so).

If incorporating alanv's suggestion, try to convince android system to read out the content description
by either
If(accessibilityModeIsEnabled())//custom method that checks context.getSystemService(Context.ACCESSIBILITY_SERVICE).isEnabled()
myView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
or AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED.
or requestFocus
Above should done when myView is visible. May be during onMesasure when width and height are both positive
If list view is still unable to do so, then try doing the above tricks on the first element of list view. Accessibility in Android varies devices to device and not one strategy fits all

Related

Kotlin Custom Android Keyboard IME - method onUpdateCursorAnchorInfo from InputMethodService is not working

I'm developing a custom android keyboard in Kotlin and I have looked at this post for how to detect cursor movement/change when the user clicks on text editor.
Here is a snippet of my code of my custom keyboard class which extends InputMethodService.
class CustomKeyboard: InputMethodService() {
override fun onCreateInputView(): View {
// initiated keyboard
}
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
// call onUpdateCursorAnchorInfo() whenever cursor/anchor position is changed
this.currentInputConnection.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR)
Log.i("--------------", "onStartInputView is called")
}
override fun onUpdateCursorAnchorInfo(cursorAnchorInfo: CursorAnchorInfo) {
Log.i("--------------", "onUpdateCursorAnchorInfo is called")
}
}
Then I ran this app on Android Studio emulator and tested on an EditText view in the following way:
I click on the EditText view on my app
I input many random characters in the EditText view
I click randomly on different characters in the EditText view
The system log did print out "onStartInputView is called" when I first clicked on the EditText view.
But it did not print out "onUpdateCursorAnchorInfo is called" anywhere.
What I thought would happen was that "onUpdateCursorAnchorInfo is called" would have been printed multiple times as I clicked on different parts of the EditText view.
However, when I changed to a different app and click on the text editor there, the Log did print out "onStartInputView is called" and "onUpdateCursorAnchorInfo is called" once.
But when I repeat step 1~3 on that app it did not work either
I have no idea why this behaviour occurs.
Any help would be appreciated.
Okay never mind somehow it works now. It must have been a bug or something.

Is there any way to track what fragment/s are visible right now?

I work on a very large project. It has a lot of modules and views - activities and fragments. I need to understand what fragment and what activity are running at the moment. Then I need to move to this class of activity and fragment in the project. Is there any way to find the class name? maybe by logs or smth else?
You can use Android Profiler for this. You will also get a lot of details from this tool. Just hover the mouse over the graph, it will show the fragment currently showing.
You can try Layout Inspector, a feature of android studio. When the app is running on a device that is connected to Android Studio, you can click on any item to see the view id, you can use these ids to find out which activity/fragment they belong to (through xml file)
Use UserVisibleHint for that:
boolean isVisible;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
}
When you want to check the visibility of the fragment, simply check it.
if (isVisible) {
//Fragment is visible
} else {
//Fragment is not visible
}

How to switch between layouts in custom dialog fragment (Android)

i'ld like to know how i can switch my dialogs layout file without creating a separate one.
I have a custom dialog fragment i use for connecting to bluetooth devices in my app. It pops up a list of devices and i connect to a device of my choosing.
I have two xml layouts i want to use with this dialog fragment:
- The first one holds the listview for devices i want to connect to
- The other houses and image view
When i connect to a device, i want to switch the layout from the list to the one that houses the imageview. Some where in my code, i have a variable that checks the connection status.
if i'm connected, i switch to the other layout like this:
getDialog().setContentView(R.layout.xml2);
and it works but then when i want to show the dialog again, i get this error.
Attempt to invoke virtual method 'void android.app.Dialog.setContentView(int)' on a null object reference
In my onCreateView method, i'm check my connection state.
if (connected) {
return inflater.inflate(R.layout.xml2, container, false);
} else {
return inflater.inflate(R.layout.xml1, container, false);
}
I know the error has to do with the changing getDialog().setContentView when state changed to connected. I'm thinking about how to revert back to the default view on dismiss so the onCreateView can take effect. If there is another method to doing this, i'ld like to hear about it. Any Ideas?
Thanks in advance...
Your getDialog() method returns null. You may want to have a look at it.

Android AccessibilityDelegate force reading of ViewGroup not children

I'm using a viewpager which is made up of some number of relative layout siblings which are quite complex.
If I click on the relative layout, it will highlight the entire page and read the title and a few textviews one after the other as expected.
If I scroll the viewpager I'd like talkback to read the next page in the same way it reads the first if I click. Secondly, if I scroll to the second, third, etc. pages and click on those layouts, talkback will read as expected.
I am trying to achieve the click behavior after the scroll event has completed.
Here is what I have for the accessibilityDelegate.
viewPager.setAccessibilityDelegate(new AccessibilityDelegate () {
#Override
public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
View page = viewPager.getCurrentPageView();
performAccessibilityAction(page, AccessibilityNodeInfo.ACTION_CLICK, Bundle.EMPTY);
}
return super.onRequestSendAccessibilityEvent(host, child, event);
}
});
I've verified that 'page' is the RelativeLayout parent that I think it is. I've also confirmed that the onRequestSendAccessibilityEvent is being fired, but it doesn't read the contents of its children. Am I missing something?
Updated
I've also tried using
viewpager.getCurrentPageView().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
The above worked for another example when I needed to force talkback to reread a single item but does not have any affect if I try it on the page.
Thanks
Some background -- When you tap on the relative layout, TalkBack generates speech based on the layout's contents. On ICS, this is triggered by a HOVER_ENTER event. On Jelly Bean, it's triggered by an ACCESSIBILITY_FOCUS event. These events are sent automatically by the framework and should, generally speaking, never be sent manually from an app. The same goes for FOCUS events, except in the special case of custom views (see Accessibility talk from Google I/O 2013 for more details).
So, back on topic.
You can control the speech for SCROLLED events by populating the outgoing event with the text you want read. The down side of this is that you'll need to manually generate the text you want read, and it's very likely that this text will differ from what TalkBack will read if the user touches the layout.
viewPager.setAccessibilityDelegate(new AccessibilityDelegate () {
#Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(host, event);
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
event.setContentDescription(/** your text */);
}
}
});
Another option is to do nothing and let the user explore the view on their own. This is the preferred interaction model for Android accessibility.
Edit: Video URL is broken, Changed.
This issue was reported on google check
where ViewPager does not set AccessibilityEvent parameters properly
when scrolling.
But after releas of Android Support Library, revision 23.2.1 (March 2016) This issue has been resolved.
update Support Library to Android Support Library to 23.2.1
I had the same issue before. And now I add android:focusable="true" to the ViewGroup, the TalkBalk will read the ViewGroup, instead of its children

Load recent apps into GridView

I am creating a feature inside my Android app that will allow users to see their 6 last used apps in a gridview with only the application image. So far I have tried this:
//Load recent used apps
ActivityManager result = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
ArrayList<RecentTaskInfo> apps = (ArrayList<RecentTaskInfo>) result.getRecentTasks(10, ActivityManager.RECENT_WITH_EXCLUDED);
ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,
android.R.layout.simple_list_item_1, apps);
recentappsGridView.setAdapter(adapter);
But this only shows a lot of text in each row and column. How can I fix this/make it happen? Please note that I am already using a ListView within the same activity with a method for its click events.
Create a subclass of ArrayAdapter. Override getView(). Set up your cells to be images, not TextView. Use resolveActivityInfo() on the Intent you get from baseIntent in the RecentTaskInfo to get an ActivityInfo object. Use icon on ActivityInfo to populate your cell.
I haven't tried that recipe, but it should get you closer.

Categories

Resources