I have a transparent view in my app on top of a few fragments. So my fragments are below the transparent overlay, but they are not reachable by the user while the overlay is opened, as touching the overlay will dismiss it. All work fine up to the point when I enable TalkBack
My problem is that when I have TalkBack enabled and I swipe to select the next element, once TalkBack has finished with everything in the overlay, it will start setting focus on items below overlay. Is there any way to stop it from doing so? Something similar to the android:clickable="true" or using an onClick listener for making the transparent overlay intercept all clicks?
On API 19+, you can set the accessibility importance of root view of the hierarchy that you want to hide from TalkBack to be NO_HIDE_DESCENDANTS. This will prevent TalkBack from focusing anything in that hierarchy, and this is how we handle the navigation drawer scrim.
On previous versions, there's no good way to accomplish this. There are some bad ways involving manually hiding nodes from the AccessibilityNodeInfo for the root view, though, and you can check those in the DrawerLayout source code.
Related
The goal is to obtain views that can be interacted with instantly (that can be clicked right now and something would happen). If the view is visible and clickable in general but hovered by another view/menu/side panel, it should be omitted.
Voice Access do that. And it seems to use Accessibility API.
The perfect example is the bottom menu in Google Maps. When it expands, "Search along the route" button underneath is still visible but it's not highlighted by the app.
So what do we have?
There is a stream of AccessibilityEvent. The most useful is
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, so we can be notified when something is happening.
With getSource() we can get an instance of AccessibilityNodeInfo that triggered the event.
Or we can get a root of a window with AccessibilityService.getRootInActiveWindow(). And having that we are able to traverse the whole hierarchy within an app.
AccessibilityNodeInfo doesn't provide any information about z-order of views, so it's not possible to understand what is above and what is beneath.
The bottom menu is in the same window (it's not modal).
If you try to click "Search along the route" button while the bottom menu is expanded, the bottom menu collapses. So you can't actually click it, it's beneath the menu.
I've looked through all parameters of the AccessibilityNodeInfo, like isVisibleToUser(), isClickable(), isContextClickable(), isSelected(), isFocusable(), isFocused(), isAccessibilityFocused() and the button has the same parameters when the bottom menu is collapsed/expanded. It's visible to the user, focusable and clickable.
I've looked into hidden APIs and don't see anything that can be useful.
What I'm missing?
The key point is that in an AccessibilityService.onAccessibilityEvent() the tree hierarchy is not final. To get views that are interactable at the moment, AccessibilityService.getRootInActiveWindow() should be called with a delay.
AccessibilityNodeInfo#getDrawingOrder() will probably help you. Note that you need to do tree traversal to determine what is on top of what.
There are still corner cases with transparent views that will give you trouble, but that should get you 95% of the way there. We're working on a better answer for that case.
I'm trying to do an Accessibility Service where the purpose it's to create some "layer" between the user when he touches the screen and the tap.
For example : when I touch the screen I when to double tap at this precise position I touch the Screen.
I think, but I'm really open to suggestion, that I will have to create an invisible layout that will cover all the screen where a would be able to activate an onTouchListener to get the position and use my accessibility service to create gesture and transfer the touch behind the layout to click anywhere.
As far I only found a solution for Android 4.1 or less.
I also want to use a kind of cursor, the app Open Sesame do it well and the cursor can go over the navigation bar and interact with.
I also found the open source project Eva facial mouse but they don't perform complex gesture and don't go over the navigation bar.
So my big question is, I am in the right way by wanting to create an invisible layout to detect touch even on the navigation bar and is there someone would help me to enlighten my search in the right direction.
I succeed in putting an overlay layout over the status bar, just add the right Flags to your LayoutParams.
For my case I use: FLAG_FULLSCREEN, FLAG_LAYOUT_IN_SCREEN and FLAG_LAYOUT_NO_LIMITS.
There are some application that disables all the touch inputs, including the touch events that occur on the navigation bar.
Examples are Touch Lock or UnTouch. How one can do that?
By analyzing the second linked app seems that there is a hidden layout that capture the touch events (like an empty onClickListener).
Initially I tried to draw a transparent foreground using the SYSTEM_ALERT_WINDOW permission and by assigning an empty touch listener. However in this way I cannot draw on the navigation bar, so the user can touch the home button and the back button.
Another way that I tried is to launch an Activity with transparent background and in fullscreen mode. In this way I can capture all the events. This works, but obviously this causes other activities to go in pause state.
So my question is, how can one reach the goal? Alternatively is possible to use some root/system commands?
Thanks!
Try this link. If you want to do it on just 1 view then edit out the iteration in the methods given.
I'm implementing a sliding from top drawer custom widget from some source code (AFAIR, from sephiroth). It works well as is, but I need a small layout (i.e. 3 buttons) in the handle of the drawer. So, I had to modify the code a little. I'm using dispatchTouchEvent in the main onTouchEvent of the widget, to propagate the necessary events down to the children of the handle view.
It works well when the drawer is closed (the handle and its buttons are on the top of the screen), but when it is open all the way down handle buttons stop working. I have a proper response on touch from the handle layout frame (its color changing and I can close the drawer) though.
By an accident I've realized that handle buttons can be triggered by touching their old locations near the top of the screen! But their images are properly shown at the bottom, and react to the setText() properly. What's happening? And how to fix that?
I am trying to make mapView can be moved when navigation movement (when user click device arrow key left, right, up, down).
How to make it ?
All you need to do is add the android:focusable="true" attribute to your MapView in your layout XML. If you're creating your MapView programmatically, you can make it focusable by calling mapView.setFocusable(true). You can also call mapView.requestFocus() if you need to explicitly give the map focus and allow keyboard panning.
You can read more about how the UI framework handles focus in the Developer Guide section on Handling UI Events.