久しぶりにAndroidのコードを書いていたら、ビューを他のアプリに重ねて表示しようとして盛大に躓いたのでメモ。
要点だけ抜き出しているので、細かなコードは参考サイトとして挙げているリンク先を参照のこと。
Android 4.0 ~ 5.0 (APIレベル 14~22)
- AndroidManifestにパーミッションを記述する
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- WindowManager#addViewする
LayoutParams生成時のレイヤータイプ(コンストラクタのint _type
変数)に、WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
を渡す。
参考サイト : 画面上にアプリの情報を常時表示する
Android 6.0 ~ 7.1.1 (APIレベル 23~25)
Android 5.0までの対応に加えて以下の対応が必要。
- RuntimePermissionに対応させる
APIが更新され、一部のパーミッションは個別にユーザが許可を出すことを求められるようになった。
まずは、Settings#canDrawOverlays
を呼び出して、アプリの権限をチェックする。
権限がない場合は、オーバーレイ権限を変更する為の画面を呼び出す。
Intentには Settings.ACTION_MANAGE_OVERLAY_PERMISSION
と、自アプリのパッケージ名を指定する。
(ユーザに対して表示して、利用開始時に権限設定を許可してもらう)
権限設定画面から自アプリに制御が戻ったら再度権限を確認し、権限が有効になっていたらAndroid 5.0までと同じ方法でビューを追加する。
参考サイト : MarshmallowでSYSTEM_ALERT_WINDOWの権限の扱いが変わった
Android 8.0 ~ (APIレベル 26~)
Android 7.1.1までの対応に加えて以下の対応が必要。
- WindowManagerでTYPE_APPLICATION_OVERLAYレイヤーを使う
APIが更新され、TYPE_SYSTEM_ALERTレイヤーは利用できなくなった。
よって、今まで通りのレイヤータイプ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
を使用するとエラーとなる。
(下記の例外が発生する。この例外で検索しても情報が見つからなくて困った)
java.lang.RuntimeException: Unable to start service com.example.app.TestService@57b8b88 with Intent { cmp=com.example.app/.TestService }: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@4938b2b -- permission denied for window type 2003
これを回避する為にはAPIレベルが26以上の場合に、LayoutParams生成時のレイヤータイプに WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
を渡すようにする。