1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Androidで「どのアプリの上にも浮かぶ読書定規」を作ったときのオーバーレイ実装メモ(タッチ透過・Android 12の不透明度制限・マスク)

1
Posted at

ディスレクシアやADHDの方向けに、画面上に半透明の帯(リーディングルーラー)を浮かべるAndroidアプリ「ReadFocus+」を個人で作っています。電子書籍・PDF・ブラウザなどどのアプリの上でも動かす必要があり、SYSTEM_ALERT_WINDOW のオーバーレイで実装しました。その中でハマった点をまとめます。

構成:帯1枚+マスク2枚+操作パネル

  • 帯(フォーカスストリップ):幅 MATCH_PARENT、高さは可変のウィンドウ1枚。TYPE_APPLICATION_OVERLAY(API 26未満は TYPE_PHONE)、PixelFormat.TRANSLUCENT。
  • 行ハイライトモード:帯の中の View に GradientDrawable でテーマ色を塗り、View の alpha で濃さを調整。カスタム onDraw は使っていません。
  • 読書マスクモード:帯の上と下にそれぞれ別ウィンドウ(Gravity.TOP / Gravity.BOTTOM の単色 View)を置き、その隙間を「読む窓」にしています。帯をドラッグするとマスク2枚も追従させます。
  • 操作パネル:モード切替やロックのボタンを持つ別ウィンドウ。

タッチを下のアプリに通す

(以下のコードは説明用に簡略化しています)

読書定規は「上に乗っているのに、下のアプリを普通にスクロールできる」ことが必須です。

int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
          | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
          | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
if (locked) flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; // ロック中=タッチ透過
  • 既定はロック(タッチ透過)。帯を動かしたいときだけパネルのロックボタンで解除し、帯自体を ACTION_DOWN/MOVE/UP でドラッグして params.y を updateViewLayout します。
  • FLAG_NOT_TOUCHABLE を付け外しするときは、ウィンドウを一度外して付け直しています。
  • y はステータスバーとナビゲーションバーの間にクランプ(WindowInsets の statusBars / navigationBars / systemGestures / displayCutout から取得)。

Android 12:タッチ透過ウィンドウの不透明度上限

Android 12(API 31)以降、他アプリのタッチを素通りさせるオーバーレイは不透明度に上限があり、超えるとタッチがブロックされます(untrusted touch 対策)。

float maxAlpha = 1f;
if (Build.VERSION.SDK_INT >= 31) {
    InputManager im = getSystemService(InputManager.class);
    maxAlpha = (im != null) ? im.getMaximumObscuringOpacityForTouch() : 0.8f;
}
params.alpha = maxAlpha;

ウィンドウ自体の alpha は上限値に固定し、見た目の濃さは中の View の alpha とテーマ色で作るようにしました。マスクを「真っ黒」にできないのはこの制約のためです。

ノッチ(ディスプレイカットアウト)

ノッチのある端末(カットアウト75pxの機種など)で、マスクの上端に隙間が残らないよう指定しています。

  • API 30+:LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
  • API 28–29:LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

回転時は onConfigurationChanged で帯を再クランプし、マスクを引き直します。読書中に画面が消えないよう、全ウィンドウに FLAG_KEEP_SCREEN_ON を付けています。

常駐とプロセス再起動

  • foregroundServiceType="specialUse"(FOREGROUND_SERVICE_SPECIAL_USE)、通知は IMPORTANCE_LOW・ongoing。
  • START_STICKY に加え、アプリ起動時・プロセス再起動時・WorkManager の15分ごとのウォッチドッグでオーバーレイを復帰させています。
  • 権限を後から取り消されたケースで BadTokenException が出たので、addView の前に毎回 Settings.canDrawOverlays() を確認しています。

権限導線

ACTION_MANAGE_OVERLAY_PERMISSION(package: 付き)→ 付けずに再試行 → ACTION_APPLICATION_DETAILS_SETTINGS の順にフォールバックし、案内画面の onResume で canDrawOverlays() が true になったら戻るようにしています。

おわりに

「上に乗っているのに邪魔をしない」ための細かい処理がほとんどでした。同じようにオーバーレイを作る方の参考になればうれしいです。アプリはこちらです(Windows版もあります)。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?