白眼鏡のblog

新しく得た知見を備忘録的に書き連ねていく

Android TaskManagerで表示される画面を設定する

TaskManagerで表示される画面

Androidでバックグラウンドのアプリをキルしたりする際に使用するタスクマネージャ
OSVersion等によって差異はあるかもしれませんがこんなの
f:id:wadada420:20190325002423p:plain:w200

セキュリティ的な問題でTaskManagerから今開いている画面を見えないようにしたい
となったため、行った対処方法について残しておきます。

デフォルト設定

デフォルト設定(何も設定してない場合)ですが、以下のように今まで表示していた画面がそのまま表示されます。
f:id:wadada420:20190325002404p:plain:w200
この場合、例えば暗証番号などが表示されていると、タスクマネージャから簡単に覗き見ることができてしまいます。

FLAG_SECUREを設定する方法

対処方法として、一番簡単なのがFLAG_SECUREを設定してあげることです。
このフラグを設定してあげることでタスクマネージャから画面を見ることはできなくなります。
実装方法としては、以下のようにwindowにFLAG_SECUREを追加するだけです。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_flagsecure)
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}

この方法を使用すると画面のキャプチャを取ることができなくなり、以下の画像のようにタスクマネージャから見る画面が真っ黒になります。
f:id:wadada420:20190325002412p:plain:w200
キャプチャを取る必要もなく、画面が真っ黒でも問題ないという場合には使えるのですが、そうでない場合にこの方法は使えません。

Splash画面を被せる方法

タスクマネージャを起動した場合や、ホームボタンを押した際に一時的にsplash画面を被せるという処理を加える方法です。 ユーザが、ホームボタンまたはタスクボタンなどでアプリから離れる場合、onUserLeaveHintというイベントが発火します。
ここに、Splash画面を表示すると行った処理を加えてあげることで実装します。

また、ユーザがアプリに戻ってきた場合には表示しているSplash画面を消してあげないといけません。
そのためにonRestartでSplash画面を消すといった処理を加えます。

実際のコードは以下のような形です。
activity_sample.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
            android:id="@+id/main_content"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_margin="8dp"
            android:background="#ffffff"
            android:padding="12dp"
            android:textSize="15sp"
            android:textColor="#000000"
            android:text="hoge"
            app:layout_constraintTop_toBottomOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>


    <!--適当なSplash画面-->
    <include
        android:id="@+id/splash"
        layout="@layout/splash_view"
        android:visibility="gone"/>  

</androidx.constraintlayout.widget.ConstraintLayout>

SampleActivity.kt

class SampleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splashoverlay)
    }

    override fun onUserLeaveHint() {
        super.onUserLeaveHint()
        val splash: View = findViewById(R.id.splash)
        splash.visibility = View.VISIBLE
    }

    override fun onRestart() {
        super.onRestart()
        val view: View = findViewById(R.id.splash)

        val anim = AlphaAnimation(1f, 0f)
        anim.duration = 500
        anim.setAnimationListener(object : Animation.AnimationListener{
            override fun onAnimationStart(p0: Animation?) {
                // NOP
            }

            override fun onAnimationEnd(p0: Animation?) {
                view.visibility = View.GONE

            }

            override fun onAnimationRepeat(p0: Animation?) {
                // NOP
            }
        })

        view.startAnimation(anim)
    }
}

このようにonUserLeaveHint()でviewを表示する、onRestart()でviewを消すという処理を加えることで、タスクマネージャから見た場合に画面が以下の画像のようになります。
f:id:wadada420:20190325002410p:plain:w200

注意点

onUserLeaveHintですが、このイベントはActivityが遷移する際にも発火します。
必要に応じて、処理の切り分けが必要です。
また、この実装方法ですが、複数バージョンで確認したところ6系以下7.1以下では正常に動作しないようです。
実際に動かしていただければわかるかと思いますが、アプリから離れる場合にSplash画面が出たり出なかったり、出てもタスクマネージャ上では表示されなかったりという事象が発生します。
どうやらonUserLeaveHint()の発生にラグがあるようでした。
7系以下にも対応する良い方法が思いついたら追記します。

サンプルアプリ