Skip to content Skip to sidebar Skip to footer

How To Find Dimensions For Status Bar, Navigation Bar And Screen In Android Accessibilityservice

I have a Java app (API 23). In it I have a MainActivity and an AccessibilityService. In the AccessibilityService: @Override public void onServiceConnected() { oView = new Linea

Solution 1:

Update: There's a problem when the screen is rotated upside-down. See Find placement of Android Navigation Bar without Insets (inside Service)

I'm currently working on a solution with my own research combined with https://stackoverflow.com/a/55348825/1910690

But I really wish I could just use Insets, or something like that.

==============

I tried a bunch of things that seemed to fail because I didn't have an activity; it's possible if I had poked around some more I would have made it work with Insets, and I still hope for an answer using them as they are more official and allow for notches etc.

In the meantime I finally pieced various answers together to make the following. It would probably work for a normal Service as well as an AccessibilityService.

The point is that at the end of onGlobalLayout() inside CreateLayoutHelperWnd() you have the complete information on screen size, orientation, status bar and navigation bar visibility and size, and you know that either you've just started your service or something changed. I found that it gets called twice sometimes even though nothing changed, probably because sometimes it does 'hide status+nav bar' and then 'change orientation' (same results for both), and sometimes it does it the other way around (different results) so I have wrapped the results in a class, and I compare the new results with the results from the last time and only do something if a result changed (that's why I use so many global variables, because those got moved later to the special class).

Please note that this is my first serious foray into Android and Java, so apologies if it's badly done - comments welcomed. It also doesn't handle error checking (eg StatusBar not found). Also note that I have not tested this extensively across multiple devices, I have no idea what it does with folding devices, multiple displays, or even split-screen; but I tried to account for the issues I saw in the various solutions (eg reading Status Bar height sometimes gives the wrong answer when it's closed on some devices, so I try to check if it is open or closed). I'll update if needed as I test more.

In the AccessibilityService:

// global variablesintstatusBarHeight= -1;
intnavigationBarHeight= -1;
intscreenWidth= -1;
intscreenHeight= -1;
View layoutHelperWnd;

@OverridepublicvoidonServiceConnected() {
    WindowManagerwm= (WindowManager) getSystemService(WINDOW_SERVICE);
    CreateLayoutHelperWnd(wm);
}

publicvoidonUnbind() {
    WindowManagerwm= (WindowManager) getSystemService(WINDOW_SERVICE);
    DestroyLayoutHelperWnd(wm);
}

privatevoidDestroyLayoutHelperWnd(WindowManager wm) {
    wm.removeView(layoutHelperWnd);
}
privatevoidCreateLayoutHelperWnd(WindowManager wm) {
    final WindowManager.LayoutParamsp=newWindowManager.LayoutParams();
    p.type = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY :
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    p.gravity = Gravity.RIGHT | Gravity.TOP;
    p.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    p.width = WindowManager.LayoutParams.MATCH_PARENT;
    p.height = WindowManager.LayoutParams.MATCH_PARENT;
    p.format = PixelFormat.TRANSPARENT;
    layoutHelperWnd = newView(this); //View helperWnd;

    wm.addView(layoutHelperWnd, p);
    finalViewTreeObservervto= layoutHelperWnd.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(newViewTreeObserver.OnGlobalLayoutListener() {

        @OverridepublicvoidonGlobalLayout() {
            // catches orientation change and fullscreen/not fullscreen change// read basic screen setup - not sure if needed every time, but can't hurtWindowManagerwm= (WindowManager) getSystemService(WINDOW_SERVICE);
            ReadScreenDimensions(wm);   // sets up screenWidth and screenHeight
            statusBarHeight = GetStatusBarHeight();
            navigationBarHeight = GetNavigationBarHeight();

            intwindowHeight= layoutHelperWnd.getHeight();
            intwindowWidth= layoutHelperWnd.getWidth();

            Boolean isFullScreen, isStatusBar, isNavigationBar;
            isFullScreen = isStatusBar = isNavigationBar = false;

            Configurationconfig= getResources().getConfiguration();
            if (config.orientation == ORIENTATION_LANDSCAPE) {
                // landscape - screenWidth is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on rightif (screenWidth != windowHeight)
                    isStatusBar = true;

                if (screenHeight != windowWidth)
                    isNavigationBar = true;
            }
            else {
                // portrait, square, unknown - screenHeight is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on bottomif (screenHeight != windowHeight) {
                    intdifference= screenHeight - windowHeight;
                    if (difference == statusBarHeight)
                        isStatusBar = true;
                    elseif (difference == navigationBarHeight)
                        isNavigationBar = true;
                    else {
                        isStatusBar = true;
                        isNavigationBar = true;
                    }
                }
            }

            if (!isStatusBar && !isNavigationBar)
                isFullScreen = true;

            Log.v(TAG,"Screen change:\nScreen W,H: (" + screenWidth + ", " + screenHeight + ")\nOrientation: " + (config.orientation == ORIENTATION_LANDSCAPE ? "Landscape" : config.orientation == ORIENTATION_PORTRAIT ? "Portrait" : "Unknown") +
                    "\nWindow W,H: (" + windowWidth + ", " + windowHeight + ")\n" + "Status bar H: " + statusBarHeight + ", present: " + isStatusBar + "\nNavigation bar H: " + navigationBarHeight + ", present: " + isNavigationBar + "\nFullScreen: " + isFullScreen);

            // do any updates required to the service's assets here
        }
    });
}

publicvoidReadScreenDimensions(WindowManager wm) {
    DisplaymyDisplay= wm.getDefaultDisplay();
    Display.Modemode= myDisplay.getMode();
    screenHeight = mode.getPhysicalHeight();
    screenWidth = mode.getPhysicalWidth();
}

publicintGetStatusBarHeight() {
    // returns 0 for no result foundintresult=0;
    intresourceId= getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}
publicintGetNavigationBarHeight() {
    // returns 0 for no result foundintresult=0;
    intresourceId= getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        return getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

You will also need to add to your manifest (for the app, not the service):

<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW" />

and in your main activity call isSystemAlertPermissionGranted() and then do something meaningful depending on what is returned (possibly waiting until onActivityResult() in some cases). Er, I basically grabbed this wholesale from various places and have not changed it much or at all. :)

publicstaticint ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 1234;

publicbooleanisSystemAlertPermissionGranted() {
    // if this doesn't work, try https://stackoverflow.com/questions/46208897/android-permission-denied-for-window-type-2038-using-type-application-overlayif (Build.VERSION.SDK_INT >= 23) {
        if (!Settings.canDrawOverlays(this)) {
            Intentintent=newIntent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
            returnfalse;
        }
        else
        {
            Log.v(TAG,"Permission is granted");
            returntrue;
        }
    }
    else { //permission is automatically granted on sdk<23 upon installation
        Log.v(TAG, "Permission is granted");
        returntrue;
    }
}

@OverrideprotectedvoidonActivityResult(int requestCode, int resultCode,  Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
        // Check if the app get the permissionif (Settings.canDrawOverlays(this)) {
            // Run your logic with newly-granted permission.
        } else {
            // Permission not granted. Change your logic accordingly.// App can re-request permission anytime.
        }
    }
}

Post a Comment for "How To Find Dimensions For Status Bar, Navigation Bar And Screen In Android Accessibilityservice"