I'm new to the accessibility stuff on Android. While going through the classes and documentation I came across TYPE_ACCESSIBILITY_OVERLAY inside the WindowManager class.
The documentation says (only the relevant text)
For example, if there is a full screen accessibility overlay that is
touchable, the windows below it will be introspectable by an
accessibility service even though they are covered by a touchable
window.
So I set out to achieve just that, a full screen accessibility overlay and try to introspect the windows below it
Extended AccessibilityService and added my full screen overlay when onServiceConnected is called (the inspiration for adding overlay came from here)
#Override
protected void onServiceConnected() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
FrameLayout mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
lp.gravity = Gravity.TOP;
wm.addView(mLayout, lp);
mLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Here I'm getting the touch events on the overlay I added
return false;
}
});
}
Now, the question is, how do I introspect or find the windows below this overlay? Even in the onAccessibilityEvent callback I get just this overlay window. getWindows() always has a size of 1. Doesn't it refute the assertion made above for TYPE_ACCESSIBILITY_OVERLAY?
Relevant info: To receive the touch events on the overlay I have disabled touchExplorationMode in the service settings
android:canRequestTouchExplorationMode="false"
What you seem to be missing is flagRetrieveInteractiveWindows on your configuration. These properties and window layout paremeters configuration should work, without requiring for you to disable canRequestTouchExplorationMode in order to get the events and having getWindows return the AccessibilityWindowInfo instances underneath yours:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:packageNames="test.demo.com.tests"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagRetrieveInteractiveWindows|flagReportViewIds|flagIncludeNotImportantViews"
android:accessibilityFeedbackType="feedbackAllMask"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
And on service connected:
#Override
protected void onServiceConnected() {
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
FrameLayout layout = new FrameLayout(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
windowManager.addView(layout, params);
layout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//You can either get the information here or on onAccessibilityEvent
return false;
}
});
}
EDIT:
Added FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS to accomplish full screen and removed canRequestTouchExplorationMode since the flag associated to this property should not be included and, therefore, of no use.
Related
I want an imageview for my app just like fb messenger chat header which will be shown everywhere when the app is running in background and we are working on another app. I have seen some codes but they just make it visible at a fixed position not movable the code is here and i have just used textview for the time being instead of imageview.
public void onCreate() {
super.onCreate();
//mView = new HUDView(this);
mButton = new Button(this);
mButton.setText("Overlay button");
mButton.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(getApplicationContext(),"Overlay button event", Toast.LENGTH_SHORT).show();
return false;
}
});
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.CENTER;
params.setTitle("Load Average");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mButton, params);
}
Any help would be appreciated.
The link of Lalit Poptani is good. You have to use a service for this kind of feature.
Please, look the example form Waza_Be, here: https://stackoverflow.com/a/15980900/5098294
I'm trying to make a custom navigation button and have disabled the default navigation buttons provided by android by rooting my device. I want my custom navigation buttons to disappear after some interval of time (say 5 seconds). I have managed to do so. I want to make something such that whenever I touch anywhere on the screen, I can detect the touch event and show my navigation bar. Whether I'm on homescreen or any application, I can receive touch event. Is there a way to do so or do I need to go android source code?
Thank you
OnCreate of your Service: used WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH flag.
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();
mView = new HUDView(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.setTitle("Load Average");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mView, params);
}
Now, you will start getting each and every click event.
see this Creating a system overlay window (always on top)
I want to overlay android status bar. In my case it is on top. I do not want to overlay or hide navigation bar.
Note: Solution must work on android 4.2.2+. I prefer answers for non-rooted device.
I have searched many SO questions and answers, but none works on 4.2.2.
Below is my code, but it does not consume touch events. That's why status bar opens its panel. And I do not want this.
#Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
int statusBarHeight = (int) Math.ceil(25 * getResources().getDisplayMetrics().density);
overlay = new Button(this);
overlay.setBackgroundColor(Color.GREEN);
overlay.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("StatusBar", "touched");
return false;
}
});
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FILL_PARENT,
statusBarHeight,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH|
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.RIGHT;
windowManager.addView(overlay, params);
}
#Override
public void onDestroy() {
super.onDestroy();
if (overlay != null) windowManager.removeView(overlay);
}
In main activity I start service:
startService(new Intent(this, StatusBarService.class));
And permission is added in AndroidManifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
And now proof it can be done and it works on 4.2.2:
Play Google - MobiLock
How?
Screenshots:
After try&repeat, this did it for me:
int statusBarHeight = (int) Math.ceil(25 * getResources().getDisplayMetrics().density);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.FILL_PARENT,
statusBarHeight,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
It works on 4.2.2 and 4.4.2
We could not prevent the status appearing in full screen mode in kitkat devices, so made a hack which still suits the requirement ie block the status bar from expanding.
For that to work, the app was not made full screen. We put a overlay over status bar and consumed all input events. It prevented the status from expanding.
note:
customViewGroup is custom class which extends any
layout(frame,relative layout etc) and consumes touch event.
to consume touch event override the onInterceptTouchEvent method of
the view group and return true
Updated
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
customViewGroup implementation
Code :
WindowManager manager = ((WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources()
.getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
customViewGroup view = new customViewGroup(this);
manager.addView(view, localLayoutParams);
Note not rooting required
Can't you put your app in a notitlebar theme [shouldn't remove the navigation bar] and implement your own view on top that mimics the notification bar look as a container and the rest of the contents would be fragments ?
I have implemented some features by integrating accessibility service for my application.
I currently have a 'system overlay' ImageView that can detect TouchEvent's. however i have seen it done that immersive mode, hiding the statusbar or navbar is possible without root.
i need to beable to access the view of my touch detection window in order to process any accessibility
My Accessibility service contains:
...
#Override
public void onServiceConnected() {
serviceInstance = this;
}
...
my touch detector:
...
WindowManager wm;
private MyAccessibilityService myAccessibilityService;
private ImageView mTouchDetector;
#Override
public void onCreate() {
super.onCreate();
myAccessibilityService = MyAccessibilityService.getSharedInstance();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
100,
100,
0, 0,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, debugMode());
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mTouchDetector, params);
}
...
I have partially solved this problem, however having some trouble with the back button, check here to see if it gets resolved.
create the service as normal but add these flags to the overlay:
to the window manager:
TYPE_PRIORITY_PHONE, FLAG_FULLSCREEN |
FLAG_NOT_TOUCH_MODAL
to the view:
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
FloatingView.setSystemUiVisibility(uiOptions);
Android floating window with hidden statusbar Accessibility problems
I am trying to add a view(ImageButton for example) to Window using WindowManager.addView(button),
What are the layout params so that the button can be seen on "lock screen".
Thanks in advance.
You can try adding type as WindowManager.LayoutParams.TYPE_SYSTEM_ERROR in the WindowManager.LayoutParams constructor which is illustrated below :
WindowManager.LayoutParams params=new WindowManager.LayoutParams();
params.type=WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
The aforementioned lines will make the view to appear on lock screen where you can capture touch events, click events and many others; However, you can use the below-mentioned flag for the non-touchable view.
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
Note: As per Android Developers The above-mentioned both flags are deprecated in API Level 26 and so you can use the flag TYPE_APPLICATION_OVERLAY as a substitute.
You have to make an activity with transparent background with view on the top which you want to show on the lockscreen. Now call that activity when your phone wakes up.
Note : You have to make an service which will start your activity. You have to register a broadcast receiver to that service.
public class CrackService extends Service
{
CrackView renderView;
LayoutParams params;
WindowManager wm;
#Override
public void onCreate()
{
params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(renderView, params);
}
#Override
public IBinder onBind(Intent intent)
{
// TODO Auto-generated method stub
return null;
}
}