安卓系统悬浮窗口

20

我正在尝试创建系统覆盖层。但我一直收到“权限被拒绝”的错误提示。我正在使用SDK版本23。

我的清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test" >

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

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".activity.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

</manifest>

我用来创建叠加层的代码:

    //mView = new HUDView(this);
    mButton = new Button(this);
    mButton.setText("Overlay button");
    mButton.setOnTouchListener(this);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            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(mButton, params);

7个回答

35

首先,不存在名为SYSTEM_OVERLAY_WINDOW的权限。正确的是SYSTEM_ALERT_WINDOW

其次,如果您的targetSdkVersion为23或更高版本,并且在Android 6.0及以上设备上运行,则您的应用程序一开始不会获得此权限。调用Settings.canDrawOverlays()查看是否具有权限,并使用ACTION_MANAGE_OVERLAY_PERMISSION引导用户前往设置页面进行授权。


我已将权限更改为SYSTEM_ALERT_WINDOW,但这并没有解决问题。因此我正在尝试实现您的第二个建议。但是,我该如何使用ACTION_MANAGE_OVERLAY_PERMISSION引导用户到设置界面? - Tom
2
@VeryEvilBetty: 这是一个 Intent 操作。startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)) 应该会打开一个活动,在那里用户可以选择允许您的应用程序在屏幕上绘制。 - CommonsWare
有没有一种方法可以仅为我的应用程序启用覆盖,而无需要求用户在所有其他应用程序中搜索我的应用程序?理想情况下,像权限一样的小弹出窗口将是最好的选择。有什么办法可以做到这一点吗? - Meymann
@Meymann:据我所知,没有这样的工具,很抱歉。即使有,如果他们认为不合理,他们可能会禁止您在Play商店中使用它,如此文章所述(https://commonsware.com/blog/2015/11/11/google-anti-trust-issues.html)。 - CommonsWare
@Meymann 只需将settingsIntent.setData(Uri.parse("package:" + getPackageName()));添加到意图中,这将在该设置列表中打开您的应用程序的详细页面。 - Ion Aalbers
显示剩余3条评论

27

在 AndroidManifest 中(适用于版本<23)

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


public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 5469;
//Random value

    public void testPermission() {
        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);
        }
    }

结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
        if (Settings.canDrawOverlays(this)) {
            // You have permission
        }
    }
}

在官方网站上,它说我们可以在API v23上执行相同的操作,你尝试过在23上使用这段代码吗?http://developer.android.com/reference/android/provider/Settings.html#canDrawOverlays(android.content.Context) - MBH
可以的。只要在清单文件中授予权限,对于版本小于23的应用程序而言是必须的,但对于版本23及以上的应用程序来说会忽略这个权限,因为运行时会询问权限。我的其余回答适用于两个版本。 - Anthone
我检查了运行时权限列表,SYSTEM_ALERT_WINDOW不在列表中。我猜唯一的解决方案是指导用户如何授予权限! - MBH
1
@Anthone 我已经尝试了你的代码,Settings.canDrawOverlays(this)onActivityResult 方法中返回 false。但稍后调用它时,返回值为 true。看起来更改是异步完成的。有没有办法在用户更改此设置时得到通知? - Piotr Aleksander Chmielowski
1
注意:在某些运行 Android 8.0 和 8.1 的设备上,当用户刚刚启用权限并返回到您的应用程序时,Settings.canDrawOverlays(this) 可能始终返回 false。在此方法返回真实值 true 之前,可能需要 5-7 秒钟。请查看此答案以了解如何绕过此已知错误:https://dev59.com/NVYO5IYBdhLWcg3wN--f - Kirill Karmazin
显示剩余2条评论

0
作为替代方案,您可以尝试使用WindowManager.LayoutParams.TYPE_TOAST而不是WindowManager.LayoutParams.TYPE_SYSTEM_ALERT。 这不需要任何权限。 我正在使用它来显示图像,也许按钮也可以。

0

我在我的中处理了每个 Android 版本的所有覆盖权限。请在此处查看gist输入图像说明


0

要在其他应用程序上显示,需要在清单中添加权限

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

检查您的应用程序是否具有权限

Settings.canDrawOverlays(context)

检查权限 checkOverlayPermission

private const val CODE_DRAW_OVER_OTHER_APP_PERMISSION = 2084

private fun checkOverlayPermission(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
        //If the draw over permission is not available open the settings screen
        //to grant the permission.
        val intent = Intent(
            Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
            Uri.parse("package:$packageName")
        )
        startActivityForResult(
            intent,
            CODE_DRAW_OVER_OTHER_APP_PERMISSION
        )
    }
}

在onActivityResult中,您可以检查权限是否已被授予。
override fun onActivityResult(
    requestCode: Int,
    resultCode: Int,
    data: Intent?
) {
    if (requestCode == CODE_DRAW_OVER_OTHER_APP_PERMISSION) {
        if (Settings.canDrawOverlays(this)) {
            // Permission Granted
        } else {
            // Permission not Granted
            // If you want then call again checkOverlayPermission until permission granted
        }
    } 
}

0

您还可以将用户引导到特定应用程序的覆盖页面。文档states

输入:可选地,Intent 的数据 URI 可以指定应用程序包名称,以直接调用与包名称特定的管理 GUI。例如,“package:com.my.app”。

因此,类似于以下内容:

intent.setData(Uri.fromParts("package", getPackageName(), null));

0

对于 Android 8 及以上和以下的用户,请使用

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接