【问题标题】:How to find dimensions for status bar, navigation bar and screen in Android AccessibilityService如何在 Android AccessibilityService 中查找状态栏、导航栏和屏幕的尺寸
【发布时间】:2021-01-02 16:05:08
【问题描述】:

我有一个 Java 应用程序 (API 23)。其中我有一个MainActivity 和一个AccessibilityService。在 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,因为不能保证我的活动会可见?我是否必须将视图的创建从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

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

【问题讨论】:

  • WindowInsets 应用于视图。如果您没有视图,则无法获得它们。还有其他方法可以获得status-barnavigation-bar 高度,但它们不是100% 可靠
  • 那是我的担心。如果事实证明这是真的,那么我怀疑我要么必须阅读我的 Activity 视图并以某种方式将其传递,然后有时继续检查它,要么创建具有父级大小的窗口(即除了状态/导航栏),阅读它下面的视图,找到一个全屏视图,使用它来读取高度,然后删除窗口并以正确的高度重新创建它。后者似乎更复杂但更一致。我得看看别人怎么说。
  • @AhmadSattout 您会推荐哪些替代方法?

标签: android accessibilityservice android-statusbar android-navigation-bar


【解决方案1】:

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

我目前正在通过我自己的研究结合https://stackoverflow.com/a/55348825/1910690 来制定解决方案

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

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

我尝试了一堆似乎失败的事情,因为我没有活动;如果我再四处寻找,我可能会使其与 Insets 一起使用,我仍然希望使用它们得到答案,因为它们更正式并且允许缺口等。

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

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

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

在 AccessibilityService 中:

// 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), status bar may be on top, 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", "dimen", "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.
        }
    }
}

【讨论】:

    猜你喜欢
    • 2011-01-15
    • 2014-04-23
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 2016-05-12
    • 2016-12-29
    相关资源
    最近更新 更多