如何在 Android AccessibilityService 中查找状态栏、导航栏和屏幕的尺寸

如何解决如何在 Android AccessibilityService 中查找状态栏、导航栏和屏幕的尺寸

我有一个 Java 应用程序 (API 23)。其中我有一个 MainActivity一个 AccessibilityService。在无障碍服务中:

@Override
public void onServiceConnected() {
    oView = new LinearLayout(this);
    oView.setBackgroundColor(0x88ff0000); // The translucent red color
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            width,height,xPos,yPos,WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,PixelFormat.TRANSLUCENT
    );
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    wm.addView(oView,params);
} 

我需要调整 widthheightxPosyPos,使其覆盖整个屏幕,包括状态栏和导航栏。这意味着我必须找到状态栏、屏幕和导航栏的尺寸(一旦有了这些,我就可以计算出我需要的值)。

我找到了 Height of statusbar? - 最佳答案似乎是

ViewCompat.setonApplyWindowInsetsListener(rootView,new OnApplyWindowInsetsListener() {
                @Override
                public WindowInsetsCompat onApplyWindowInsets(View v,WindowInsetsCompat insets) {
                    //THIS is the value you want.
                    int statusBarHeight = insets.getSystemWindowInsetTop();
                    int navigationBar = insets.getSystemWindowInsetBottom();

                    // Let the view handle insets as it likes.
                    return ViewCompat.onApplyWindowInsets(v,insets);
                }
            });

但是我可以使用什么 rootView,因为不能保证我的 Activity 会可见?我是否必须将视图的创建从 onServiceConnected() 移到 onApplyWindowInsets() 内部?

这真的是最好的方法吗?

要记住上述问题的后续问题 - 根据以上内容以及是否也回答了这些问题,我会在这里提问或在单独的问题中提问:

屏幕旋转时会发生什么?如果状态栏和/或导航栏消失(例如,如果它全屏显示)怎么办?似乎我需要处理这个问题,重新读取高度,然后重新创建视图。欢迎任何有关如何做到这一点的想法或指导。

=== 更新 - 到目前为止的进展 ===

我以为我只需要屏幕大小和状态栏信息(大小、位置),但导航栏有时位于屏幕左侧,所以我需要它,而键盘也可能会影响事情。

我可以获得屏幕尺寸

    display mydisplay = wm.getDefaultdisplay();
    display.Mode mode = mydisplay.getMode();
    int height = mode.getPhysicalHeight();
    int width = mode.getPhysicalWidth();

我仍在研究各种位置和尺寸。我尝试创建一个视图,然后阅读插图,但它给了我错误的答案。因此,除非出现更好的答案,否则我可能不得不使用

    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height","dimen","android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }

但在某些情况下,如果状态栏被隐藏,并且它不会告诉我它在屏幕上的位置,这可能会给出错误的答案。我还在努力;有一些事情要尝试,我们拭目以待。

可以使用 onConfigurationChanged() 处理服务中的轮换 - 参见 How do I use a service to monitor Orientation change in Android

希望对某人有所帮助 - 请拼命寻找那些最后的作品。

解决方法

更新:屏幕倒置时出现问题。见Find placement of Android Navigation Bar without Insets (inside Service)

我目前正在结合我自己的研究和 https://stackoverflow.com/a/55348825/1910690

制定解决方案

但我真的希望我可以使用 Insets 或类似的东西。

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

我尝试了很多似乎失败的事情,因为我没有活动;如果我再多研究一些,我可能会让它与 Insets 一起工作,我仍然希望使用它们得到答案,因为它们更正式,并允许使用缺口等。

在此期间,我终于将各种答案拼凑在一起,得出以下答案。它可能适用于普通 Service 和 AccessibilityService。

重点是,在 onGlobalLayout() 内的 CreateLayoutHelperWnd() 末尾,您拥有有关屏幕尺寸、方向、状态栏和导航栏可见性和尺寸的完整信息,并且您知道刚刚开始您的服务或发生了一些变化。我发现它有时会被调用两次,即使没有任何改变,可能是因为有时它会“隐藏状态+导航栏”然后“改变方向”(两者的结果相同),有时它会反过来(不同的结果) ) 所以我将结果包装在一个类中,并将新结果与上次的结果进行比较,并且仅在结果发生变化时才执行某些操作(这就是我使用这么多全局变量的原因,因为这些变量后来移到了特殊的类)。

请注意,这是我第一次认真地涉足 Android 和 Java,如果做得不好,我深表歉意 - 欢迎评论。它也不处理错误检查(例如未找到 StatusBar)。另请注意,我没有在多个设备上进行过广泛的测试,我不知道它对折叠设备、多个显示器甚至分屏有什么作用;但我试图解释我在各种解决方案中看到的问题(例如,在某些设备上关闭状态栏高度时,有时会给出错误的答案,因此我尝试检查它是打开还是关闭)。如果需要,我会在测试更多时更新。

在无障碍服务中:

// global variables
int statusBarHeight = -1;
int navigationBarHeight = -1;
int screenWidth = -1;
int screenHeight = -1;
View layoutHelperWnd;

@Override
public void onServiceConnected() {
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    CreateLayoutHelperWnd(wm);
}

public void onUnbind() {
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    DestroyLayoutHelperWnd(wm);
}

private void DestroyLayoutHelperWnd(WindowManager wm) {
    wm.removeView(layoutHelperWnd);
}
private void CreateLayoutHelperWnd(WindowManager wm) {
    final WindowManager.LayoutParams p = new WindowManager.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 = new View(this); //View helperWnd;

    wm.addView(layoutHelperWnd,p);
    final ViewTreeObserver vto = layoutHelperWnd.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            // catches orientation change and fullscreen/not fullscreen change

            // read basic screen setup - not sure if needed every time,but can't hurt
            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
            ReadScreenDimensions(wm);   // sets up screenWidth and screenHeight
            statusBarHeight = GetStatusBarHeight();
            navigationBarHeight = GetNavigationBarHeight();

            int windowHeight = layoutHelperWnd.getHeight();
            int windowWidth = layoutHelperWnd.getWidth();

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

            Configuration config = 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 right
                if (screenWidth != windowHeight)
                    isStatusBar = true;

                if (screenHeight != windowWidth)
                    isNavigationBar = true;
            }
            else {
                // portrait,square,unknown - screenHeight is for comparison to windowHeight (top to bottom),nav bar may be on bottom
                if (screenHeight != windowHeight) {
                    int difference = screenHeight - windowHeight;
                    if (difference == statusBarHeight)
                        isStatusBar = true;
                    else if (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
        }
    });
}

public void ReadScreenDimensions(WindowManager wm) {
    Display myDisplay = wm.getDefaultDisplay();
    Display.Mode mode = myDisplay.getMode();
    screenHeight = mode.getPhysicalHeight();
    screenWidth = mode.getPhysicalWidth();
}

public int GetStatusBarHeight() {
    // returns 0 for no result found
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height","dimen","android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}
public int GetNavigationBarHeight() {
    // returns 0 for no result found
    int result = 0;
    int resourceId = getResources().getIdentifier("navigation_bar_height","android");
    if (resourceId > 0) {
        return getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

您还需要添加到您的清单中(针对应用,而不是服务):

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

并在您的主要活动中调用 isSystemAlertPermissionGranted(),然后根据返回的内容执行一些有意义的操作(在某些情况下可能等到 onActivityResult())。呃这个批发我基本上都是从各个地方抓来的,并没有太大的变化或者根本没有改变。 :)

public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 1234;

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

@Override
protected void onActivityResult(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 permission
        if (Settings.canDrawOverlays(this)) {
            // Run your logic with newly-granted permission.
        } else {
            // Permission not granted. Change your logic accordingly.
            // App can re-request permission anytime.
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?