画面の固定化


# 概要

Android App の開発で画面の遷移を無効化したり通知などの抑制の相談を受けることがあります。
画面をフルスクリーンにしてもNotificationは見えてしまうし、そこからユーザーの意識が途切れたり離脱したりすることを懸念してチュートリアルだけは絶対に終わるようにしたいという意図などがあるようです。

# 画面の固定化
Android で画面を固定化する方法を探すと、多くが画面の回転を抑制する方法が多いので Android 5.0 から導入された Screen Pinning について調査してみました。

## Screen Pinning
[Screen Pinning](http://developer.android.com/about/versions/android-5.0.html)については下記のように述べられています。

> Android 5.0 introduces a new screen pinning API that lets you temporarily restrict users from leaving your task or being interrupted by notifications. This could be used, for example, if you are developing an education app to support high stakes assessment requirements on Android, or a single-purpose or kiosk application. Once your app activates screen pinning, users cannot see notifications, access other apps, or return to the home screen, until your app exits the mode.

現在発売されている多くのデバイスは Android 5.0 以上が搭載されているため Android 5.0 から対応しているAPIについては問題なく利用できると思います。

## 方法
画面の固定化には大きく３つあると指定されており、手動で行う方法を選択した場合はアプリ側の都合は特に関係なくユーザーが自由に好きな画面を固定化する方法から順に説明されています。

1. 任意のアプリの画面を固定（履歴から選択）
2. プログラムで行う方法
3. デバイスオーナーの権限を利用する方法

任意のアプリの画面を固定する場合はシステムの設定を変更する必要があります。

### 画面の固定化（ユーザーが任意のアプリを固定化）
手動で画面の固定化する場合は「設定」>「セキュリティ」>「画面の固定」をONにすることで機能が有効化されます。

> Manually: Users can enable screen pinning in Settings > Security > Screen Pinning, and select the tasks they want to pin by touching the green pin icon in the recents screen.

![setting_screen.png](https://qiita-image-store.s3.amazonaws.com/0/63129/237f106a-e3de-bde0-3393-705166fb42cc.png)
設定項目の説明が見切れているのがなんとも残念です。

この設定を有効すると現在実行している全てのタスクをユーザーが任意に固定化することができます。設定を有効後に履歴ボタンを押した際に表示されるアプリに対してピンのアイコンが右下に表示されるようになります。このピンは最後に画面に表示されていたアプリのみ対象となっています。

![recent_pin.png](https://qiita-image-store.s3.amazonaws.com/0/63129/bcaa9384-5ae5-b13b-d1bd-1df13f030e5f.png)

ピンのアイコを押すと対象のアプリが自動的に固定化されます。
その際にユーザーは固定化の解除方法も同時に知らされます。

![recent_pin_lock.png](https://qiita-image-store.s3.amazonaws.com/0/63129/894712e6-e82f-d326-a3b6-733b48cab108.png)

「画面を固定化しました」というトーストが表示され画面が固定されます。
その際に画面内に操作は通常通りに行えますが後述する[startLockTask()](http://developer.android.com/reference/android/app/Activity.html#startLockTask())を利用した時と同様に画面遷移に対して、いくつかの制限が行われます。

1. 通知バーに何も表示されなくなる。（フルスクリーンのように消えるのではなく何も表示されなくなる）
2. 通知バーをフリックで下すことができない
3. ホームボタン、履歴ボタン、バックボタンは機能しない
4. 固定化されたアプリから他のアプリへの遷移する操作も無効化される

4 の検証は実際に url を渡してブラウザ起動するような処理を用意してみたのですが Intent 自体は飛ぶものの対象の Activity が起動することはありませんでした。
バックボタンは、そのアプリのActivityスタックの最後まで戻ることはできるようです。（アプリ内であればイベントもハンドリングできる）


Google Play アプリではバックボタンを押すと固定化された状態では下のような画面まで遷移します。

![empty_screen.png](https://qiita-image-store.s3.amazonaws.com/0/63129/d39d65c5-64f8-4b9b-d858-29d7e7bd1489.png)

通知に時間や電波状況も何も見えないので、とてもシンプルな画面に見えます。
ホームボタンや履歴ボタン、この状態でバックボタンを押すとトーストで画面内に解除方法が通知されます。

![device-2015-12-21-231429.png](https://qiita-image-store.s3.amazonaws.com/0/63129/9f5944c7-f9f5-7d84-a6e5-ac629c3f7baf.png)

ここで表示されている通り「戻る」と「最近（履歴）」ボタンを同時に長押ししていると画面の固定化は解除されます。

画面が固定化されている状態でスクリーンがオフになった場合（=消灯してしまった場合）は先ほどの設定画面に応じてパスワードを要求するタイミングが変化します。

「画面固定の解除前に解除パターンを...」をONにしていた場合は、スクリーンオフから復帰した際にセキュリティ解除を要求しません。その代わり固定化を解除する際にセキュリティ解除の要求をします。OFF にしていた場合は画面復帰時にセキュリティの解除を要求（ロック画面が表示される）します。


### プログラムで画面の固定化

ドキュメントでは手動での固定化の次にプログラム的に画面の固定化に対応するための順が示されています。

> Programmatically: To activate screen pinning programmatically, call startLockTask() from your app. If the requesting app is not a device owner, the user is prompted for confirmation. A device owner app can call the setLockTaskPackages() method to enable apps to be pinnable without the user confirmation step.

これには二つの方法があります。

1. [startLockTask()](http://developer.android.com/reference/android/app/Activity.html#startLockTask())を呼び出して画面を固定
2. デバイスのオーナー/所有者である時に[setLockTaskPackages()](http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setLockTaskPackages(android.content.ComponentName, java.lang.String[])) を使って固定

## startLockTask
[startLockTask()](http://developer.android.com/reference/android/app/Activity.html#startLockTask())は呼び出すと現在の画面のロックを行います。
ユーザーに対しては画面をロックすることを通知し、それに対して同意が得られれば実際に画面をロックします。専用のパーミッションなども必要もありません。

[startLockTask()](http://developer.android.com/reference/android/app/Activity.html#startLockTask())を呼び出すと画面に対して固定化された旨を伝えるシステムのメッセージが表示されユーザーに同意確認が行われます。

画面は Nexus6 でキャプチャしたものです。
![screen_lock.png](https://qiita-image-store.s3.amazonaws.com/0/63129/624ba3c1-2562-b9fb-fbdb-dc737103d79d.png)

やや説明がわかりにくい気もしますがバックボタンと履歴ボタンを同時に長押しして、画面の固定化が解除をすることができることを説明した上で画面の固定化が通知されます。
ここで「はい」を選択すると画面が固定化されます。
「いいえ」を選んだ場合は解除されます。
ユーザーが「はい」以外の場所を選択したばいですが、Nexus6 ではバックボタン、ホームボタン、履歴ボタンは一切反応しません。緑色のエリアや説明の文字を選択しても特に反応はなく呼び出し元の画面(Activity)の領域を選択した場合。
今回のケースでは Hello World! と書かれている場所を選んだり通知を引っ張ったりアクションバーの領域に触れると画面は固定化されます。

固定化している際に [startActivity()](http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent))  などを使って Chrome の起動を試みても画面遷移は行われませんので [startActivity()](http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)) は画面(Activity)単位で画面遷移などを固定化するようです。

WindowLayer に AddView して画面上に常駐するような機能（Facebook MessengerのChathead) などは固定化前に表示されているモノは継続して表示されますが固定化された後では表示されません。
[Javelin](https://play.google.com/store/apps/details?id=com.nubelacorp.javelin&hl=ja) でテストした感じでは画面の固定化を解除した後も常駐アイコンが表示さないことから AddView 自体が成功していないのかもしれません。


## setLockTaskPackages()
これは startLockTask() と異なり端末管理(Device Admin)APIを使っての実装です。
[端末管理API](http://www.techdoctranslator.com/android/guide/device-admin) とは Android 2.2 から採用されているエンタープライズ向けの機能です。
システム管理者が企業向けのセキュリティを意識したアプリケーションを開発するための仕組みです。

残念ながらこちらの機能は手元の端末では動作しませんでした。DevicePolicyManager を制御しようとするとSecurityException が throw されてしまうので実装方法について検証することができませんでした。
興味がある方や、エンタープライズ向けのサービスの開発に携わる方は[調査](http://developer.android.com/intl/ja/reference/android/app/admin/DeviceAdminReceiver.html)しても面白いかもしれません。

# 画面の固定化について
この機能はアプリ側で勝手に判断して固定化を要求しても、不便なことも多くあまり利用されないかもしれません。逆にゲームや音楽、ビデオプレイヤーなど一つの機能に対して集中した際にはアプリ側で「画面の固定化」をユーザーに選択肢として用意してあげると使い勝手が良いかもしれません。

Android 6.0 での Activity のライフサイクルを調査したのに図が複雑になりすぎて間に合わなかったので、それは別の機会に投稿しようと思います。