はじめに
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)
}
すると次のような画面が立ち上がるのでスイッチをオンにして戻ります。
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になって細かいところで動作が変わっているのが困りものですね。
あと、オーバーレイを許可する画面、自分で呼び出す以外に行く方法は無いのだろうか?使用履歴にアクセスできるアプリ
みたいに設定画面から辿れると思ったけど見つけられなかった...
(追記)
オーバーレイの設定画面ありました。設定->アプリ->歯車アイコン->他のアプリの上に重ねて表示
です。深い!わかりにくい!