Android AccessibilityDelegate force reading of ViewGroup not children - java

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

Related

How to click button inside method Adapter in RecyclerView?

I am working on stripe-terminal-android-app, to connect to BBPOS 2X Reader device,
wanted to click-item from list,(recyclerView).
I am trying to do:
when list of devices appears(readers), I am checking if readers.size()==1, then click first-device from list,else show recyclerView();
I have very less experience in Android(coming from JS, PY), :)
After going through debugger to understand flow of program-running, I used F8 key, or stepOver the functions one by one,
and where value is assigned to convert in displayble-format in adapter as here.
public ReaderAdapter(#NotNull DiscoveryViewModel viewModel) {
super();
this.viewModel = viewModel;
if (viewModel.readers.getValue() == null) {
readers = new ArrayList<>();
} else {
readers = viewModel.readers.getValue();
if(readers.size() == 1){
Log.e(TAG, "readers.size() is 1 "+ readers.size());
}
}
}
then in ReaderHolder-file, values are bind() as
void bind(#NotNull Reader reader) {
binding.setItem(reader);
binding.setHandler(clickListener);
binding.executePendingBindings();
}
}
I tried assigining button and manually clicking when only-one device appears, by clicing on reader[0], can't do that by findViewById inside Adapter file, to call onClick() method manually,
I tired another StackOverflow's answer but didn't understood, from here.
Main fragment is discovery-fragment,
how can I click first-device by checking readers.size()==1, then click onClick()?
my final-goal is to automate, whole stripe-terminal-payment process on android.
extra-info:
I am fetching data from python-odoo server, then using url, will open app through browser, (done this part), then device will be selected automatically as everytime-no any devices will be present except one,
so will automatically select that from recyclerView, then proceed.
I have asked for help in detailed way on GitHub-issues, and started learning Android's concepts for this app(by customizing stripe's demo app, which works great, but I wanted to avoid manually clicking/selection of devices).

Prevent Android from showing sensible information on multi tasking view

I need to hide the content of my application when it goes to the background so sensitive information are not showing up on the android multitasking view.
It's been suggested to use the following line to hide the screen
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
It works fine.
However, this prevents the user from taking screenshot as well which is not an expected behavior for me. I want to let the user take screenshot of the app if they need to. What I don't want is Android to display the latest screen on the multitasking view.
Would it be possible to set the FLAG_SECURE only when the app goes in the background?
We've ended up with this solution which worked the best for us:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
You can use Activity Lifecycle Callbacks. Just call setVisiblity(View.INVISIBLE) on the views that you want to hide in the onPause() Callback and setVisiblity(View.VISIBLE) in onResume() Callback.

Validate CTabFolder before switching tabs

In a CTabFolder, I'd like to check the content for unsaved data before the user can switch from one tab to another. SWT does not provide a PreSelection event, as stated here.
I found a workaround, suggesting to switch back to the old tab when a selection is triggered, validate the data and then perform the desired switch again, if data is valid.
I do understand the general idea of this workaround, however, it is not working for me. oldPageIndex and newPageIndex do always have the same value, though I did not click on the same tab.
this.tabContainer.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent event) {
int oldPageIndex = tabContainer.getSelectionIndex();
int newPageIndex = tabContainer.indexOf((CTabItem)event.item);
// Here: oldPageIndex == newPageIndex
...
}
});
Is this workaround still working or is there anything I could possibly be doing wrong? Or maybe, has there been any fix for a real PreSelection event in the meantime? I tried using event.doit, but the SelectionEvent is fired, when the tabs have been switched already.
You can use the selection listener but as you have found the getSelectionIndex() does not give you the old tab. So you will have to maintain the old tab index yourself.
This is the technique used by the Eclipse FormEditor.

GWT event prevent default not working in Java

I'm new to GWT and I need to get a right click working. The doco I've read suggests that I need to override the onBrowserEvent() method. I'm just experimenting at this stage. the event is processed and my pop-up appears. However, as soon as I close the pop-up, the usual browser drop down menu appears (with options like "Bookmark this page" and such).
I'm using IceWeasel 24.5.0 (FireFox clone for Debian) and, obviously, Debian (wheezy).
Here's the relevant code:
public ActivityTextCell() {
super(BrowserEvents.MOUSEDOWN, BrowserEvents.MOUSEUP);
}
#Override
public void onBrowserEvent(
com.google.gwt.cell.client.Cell.Context context,
Element parent,
ActivityDTO value,
NativeEvent event,
ValueUpdater<ActivityDTO> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
event.preventDefault();
event.stopPropagation();
if (event.getType().equals(BrowserEvents.MOUSEUP)) {
Window.alert("mouse up event");
}
else {
switch ( event.getButton()){
case NativeEvent.BUTTON_RIGHT:
Window.alert("right mouseclick");
break;
case NativeEvent.BUTTON_LEFT:
Window.alert("left mouseclick");
break;
case NativeEvent.BUTTON_MIDDLE:
default:
break; // Do nothing
}
}
The class ActivityTextCell extends AbstractCell.
So what am I missing? How do I stop the browser from reaticng to the mouse click?
Well it certainly wasn't a matter of a few minutes (as can be seen by the fact that it has taken me a week to get back to this), but I have a solution. I tried reversing the order of the the event.preventDefault() and super.onBrowserEvent() but it didn't really help.
I tried a little experiment on a normal web page. It turns out, that the MOUSEDOWN event doesn't do anything in that context and the usual browser selection menu appears on the MOUSEUP. So the if/else logic sort of fell by the wayside.
What did the trick is to include the following in the top level GUI class immediately after adding the main page:
RootLayoutPanel.get().addDomHandler(new ContextMenuHandler() {
#Override
public void onContextMenu(ContextMenuEvent event) {
event.preventDefault();
event.stopPropagation();
}
}, ContextMenuEvent.getType());
This has the additional benefit (for my purposes, at least) of preventing the Browser from reacting to a right click anywhere in the application view.
As an aside: The purpose of preventing the default action is to stop the Browser doing its own thing Stopping propagation is possibly not required, but I left it in anyway (propagation goes fro the node up to the root, not the other way around). The purpose of overridinging onBrowserEvent() is to enable your own application to handel that event. The use of super.onBrowserEvent() is to allow the event to be handled by your code in the first place. I've given the relevant reference in my previous comment. The book "GWT in Action" is well worth a read if you're likely to be doing a lot of GWT coding.
You call super.onBrowserEvent() which triggers the standard browser response.
You should move event.preventDefault() to the if part of your code, and super.onBrowserEvent() to the else part. You want one of them executed depending on a browser event, but not both.

Android: Adding accessibility to a custom view

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

Categories

Resources