Edited at

MarshmallowでSYSTEM_ALERT_WINDOWの権限の扱いが変わった

More than 3 years have passed since last update.


はじめに

Lollipopまではシステムオーバーレイを使用するにはAndroidManifestに以下の権限を追加しておけば大丈夫でした。

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

しかし、Android 6.0 MarshmallowでWindowManager#addViewするとandroid.view.WindowManager$BadTokenExceptionとなってしまいます。

調べていくうちに、Stack Overflowで解決策を見つけたのでメモ。

元ネタ:Permission from manifest doesn't work in Android 6 - Stack Overflow

なお、この記事内のコードはすべてKotlinです。Javaでもほぼ同じなので適宜読み替えてください。


権限の確認と要求

システムオーバーレイを使用する権限があるか確認するにはSettings#canDrawOverlays(Context)を使います。APIレベル23が必要なので下記のような感じで実行。

AndroidManifestには必要な権限が書かれている前提です。

public fun Context.checkOverlayPermission(): Boolean {

if (Build.VERSION.SDK_INT < 23) {
return true
}
return Settings.canDrawOverlays(this)
}

Marshmallowで権限がない場合は次のように設定画面のActivityを呼び出します。Intentの第2引数にはpackage:[アプリのパッケージ名]をパースしたUriを渡します。

public fun Activity.requestOverlayPermission() {

val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${getPackageName()}"));
this.startActivityForResult(intent, REQUEST_SYSTEM_OVERLAY)
}

すると次のような画面が立ち上がるのでスイッチをオンにして戻ります。

Screenshot_20150907-001437.png

startActivityForResultで起動しているのでonActivityResultで結果を受け取りますが、Backキーで戻っているためresultCodeが必ずRESULT_CANCELEDになってしまうので注意が必要です。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

when (requestCode) {
REQUEST_SYSTEM_OVERLAY -> if (checkOverlayPermission()) {
// もう一度権限を確認して、権限があれば処理をする
}
}
super.onActivityResult(requestCode, resultCode, data)
}


おわり

Marshmallowになって細かいところで動作が変わっているのが困りものですね。

あと、オーバーレイを許可する画面、自分で呼び出す以外に行く方法は無いのだろうか?使用履歴にアクセスできるアプリみたいに設定画面から辿れると思ったけど見つけられなかった...

(追記)

オーバーレイの設定画面ありました。設定->アプリ->歯車アイコン->他のアプリの上に重ねて表示です。深い!わかりにくい!