ロック画面でのDialog表示する時の注意（Theme, windowIsFloatingをtrue）


# ロック画面でDialog表示をしたいとき。

電話などの通知を行う時、
LINEアプリなどで用いられている、ロック画面にダイアログを表示する方法についての自分用メモです。
普通に実現するには以下が参考になります。

* [[Android]ロックスクリーンの上にダイアログを表示する](http://blog.yohei.org/android-dialog-on-lockscreen/)
* [Android　FLAG_DISMISS_KEYGUARD と FLAG_SHOW_WHEN_LOCKED を使う](http://y-anz-m.blogspot.jp/2011/08/androidflagdismisskeyguard.html)

Lollipopでの挙動は調べていません。。


## 注意すること

フルスクリーンを表示するようなActivityをロック画面に表示する場合はテーマについて特に注意する必要はないかと思います。

しかし、LINEやYahooアプリのようにお知らせをロックスクリーンで表示したいことがあると思います。
LINEもYahooも全画面の黒いActivity上にダイアログを表示しているのですが、ちょっと違和感があります。
なので、ロックスクリーン上にDialogだけを表示したいと思います。

その上で若干注意すべきことがあります。

### テーマの設定

背景が透明のActivityを作るのはPanelテーマを思い出しました。
テーマはPanelのテーマを継承したテーマかAppCompatのテーマなら同様の属性を付けたテーマを作るといいと思います。

注意点として、[windowIsFloating](http://developer.android.com/reference/android/R.attr.html#windowIsFloating)と[windowNoDisplay](http://developer.android.com/reference/android/R.attr.html#windowNoDisplay)がtrueだとロック画面に表示されませんでした。
DialogテーマやPanelテーマはデフォルトでwindowIsFloatingがtrueなので注意してください。

isFloatingがtrueだとだめってなんでしょう。。どこかに書いてあるんでしょうね。。
http://stackoverflow.com/questions/10800683/android-ics-native-lockscreen

```
    <!-- Panelテーマを継承した場合-->
    <style name="MyPanelTheme" parent="android:style/Theme.Light.Panel">
        <!--重要:この二つが少なくともfalse出ないとロックスクリーンには表示されません-->
        <item name="android:windowIsFloating">false</item>
        <item name="android:windowNoDisplay">false</item> 
    </style>
    
    <!-- AppCompatなテーマからPanelテーマにする場合-->
    <style name="AppCompatPanelTheme" parent="style/Theme.AppCompat.Light">
        <!--重要:この二つが少なくともfalse出ないとロックスクリーンには表示されません-->
        <item name="android:windowIsFloating">false</item>
        <item name="android:windowNoDisplay">false</item>
        
        <!--その他Panelテーマにするための例-->
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowDisablePreview">true</item>
        <item name="android:backgroundDimEnabled">true</item> 
    </style>
```

おそらく必要なのはanimationやら背景にnullを入れたりすることなので、不要なものは除いてください。。

この設定は単純にActivityに透明なテーマを設定しているだけで、
Windowは全画面で作成されていると思います。

### Activityの実装

次にActivityにロック画面に表示するためのフラグを設定します。

```:MyDialogActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        …
        
        // ロック画面解除とロック画面上に表示するフラグを追加します。
        getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
                        
        // もしロック中で画面を点灯させたい場合はさらにフラグを追加します。
        if (makeScreenOn) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        }
        
        …
        
        // 適宜ダイアログを表示してください。
        MyDialogFragment.show(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        …
        
        // ロックされている時に起動中のアプリ画面が見えてしまうのが嫌なので壁紙を表示して擬似的にロック画面のように見せる。
        KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
        if (km.inKeyguardRestrictedInputMode()) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
        } else {
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
        }
        
        …
    }
        
    …
```

基本的に[FLAG_SHOW_WHEN_LOCKED](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SHOW_WHEN_LOCKED)と[FLAG_DISMISS_KEYGUARD](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_DISMISS_KEYGUARD)を設定すればいいのですが、Activity起動時に画面を明るくしたい場合はさらに[FLAG_TURN_SCREEN_ON](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_TURN_SCREEN_ON)を設定するといいです。


もう一つ、ポイントとしてFLAG_DISMISS_KEYGUARDを使ってロック画面上にActivityを表示させているので、Activityの裏にはロック画面がありません。
そしてActivityのテーマを透明にしてしまったのでロック中でもDialog表示のためにActivityを起動させてしまうと、
後ろの起動中のアプリ画面(標準ならホーム画面など)が見えてしまうので、
プライバシー？的によろしくないので壁紙表示のFlagを追加します。
そうすると、さもダイアログの下がロック画面のように見えますw

(ロック解除や時計などが表示されないので、完璧に同じではないですし、
ロックアプリなど使ってる場合はうまくいかないこともあると思います。
不都合な場合は普通に真っ黒なActivity上に表示するといいと思います。)


今回はonResume等でロック画面のステータスを確認します。
[KeyguardManager::isKeyguardLocked](http://developer.android.com/reference/android/app/KeyguardManager.html#isKeyguardLocked())とかもあるのですが、新しい端末(API16以上)でないと使えないのでひとまず[inKeyguardRestrictedInputMode](http://developer.android.com/reference/android/app/KeyguardManager.html#inKeyguardRestrictedInputMode())で判別しています。
壁紙を背景に表示するために、ロック中の場合は[FLAG_SHOW_WALLPAPER](http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_SHOW_WALLPAPER)を追加し、そうでない場合は裏のアプリがむしろ見えていた方が自然なのでclearしています。


##　まとめ

特に理由がなければロック画面でダイアログなど表示するべきではないと思いますが、
もしする場合はこんな方法がありました。