【Android開発】スクリーンショット撮影を検出する方法(Android 14)

こんにちは。株式会社アドグローブ ソリューション第三事業部Androidアプリエンジニアの新井です。
本日はAndroid14から可能になったスクリーンショット撮影検出APIについて紹介しようと思います。

スクリーンショット撮影検出機能について

スクリーンショット撮影検出機能は、情報漏洩や不正利用防止、ユーザー体験向上など、様々な用途で利用できるものになります。
使用例としては、以下のものがあります。

  • メッセージのやり取りをスクショして保存したり画面録画しようとすると通知する。
  • 機密性の高い情報を取り扱う画面でスクリーンショット撮影を禁止し注意メッセージを表示する。
  • スクリーンショット撮影後、画像を共有するように誘導するメッセージを表示する。 等

ただし、この機能はiOS側では簡単にできるもののAndroid側は各開発者が独自で実装する必要があることからあまり見かけないものかと思います。
またその独自の実装もフォアグラウンドでファイルの変更を検知することでスクリーンショットが撮影されたことを検知するという手間の掛かる実装になります。
そんな中、Android14からの新機能としてスクリーンショットが撮影されたときにアプリに通知する新しい API が導入され、セキュリティ向上且つ簡単に導入できるようになりました。
ここからはその機能の実装手順と実装例を紹介したいと思います。

スクリーンショット撮影検出の実装手順

developer.android.com

1.権限を宣言する

AndroidManifest.xmlにて、DETECT_SCREEN_CAPTUREをインストール時の権限に追加します。

AmdroidManifest.xml

<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />

2.スクリーンショット撮影を検出した際に行うコールバック処理を実装する

ここでスクリーンショット撮影を検出した後の処理を実装します。
あくまで検出のみなのでスクリーンショットの内容を取得するようなことはできません。

val screenCaptureCallback = Activity.ScreenCaptureCallback {
    // Add logic to take action in your app.
}

3.スクリーンショット撮影検出のコールバックを登録/解除

アクティビティのonStart()メソッド / onStop()メソッドで、スクリーンショット撮影検出のコールバックを登録 / 解除します。

override fun onStart() {
    super.onStart()
    // Pass in the callback created in the previous step 
    // and the intended callback executor (e.g. Activity's mainExecutor).
    registerScreenCaptureCallback(mainExecutor, screenCaptureCallback)
}

override fun onStop() {
    super.onStop()
    unregisterScreenCaptureCallback(screenCaptureCallback)
}

実装例:画面のスクリーンショット撮影を禁止し注意メッセージを表示する実装を行う

画面のスクリーンショット撮影を禁止し注意メッセージを表示する実装を行いたいと思います。
画面のスクリーンショット撮影を禁止するには下記をActivity等で設定することでその画面のスクリーンショット撮影を無効化できます。
WindowManagerのLayoutParamsに FLAG_SECURE というものがあり、Activity等で設定することでその画面のスクリーンショット撮影を無効化できます。

window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)

端末やOSによって挙動は異なりますが、基本的にスクリーンショットを撮ろうとすると真っ黒な画面の画像が保存されて画面の内容が撮影できないようになります。
ただし、ユーザーにはなぜ画面が真っ黒で塗りつぶされるのか何の説明もなくかなり不親切です。
そこで、今回のAndroid14から可能になったスクリーンショット撮影検出APIを使用して注意メッセージを表示することで親切なユーザ体験を提供できるようにしようと思います。

実装コード

AmdroidManifest.xml

<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val screenCaptureCallback = ScreenCaptureCallback {
        // 注意メッセージはシンプルにAlertDialogを利用してダイアログ表示
        AlertDialog.Builder(this)
            .setMessage("個人情報が含まれる為スクリーンショット撮影は禁止です。")
            .setPositiveButton("OK",null)
            .show()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // スクリーンショット撮影を無効化
        window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
    }

    override fun onStart() {
        super.onStart()
       // Android14の機能の為Android13以前では使用しないようにする
        if (Build.VERSION.SDK_INT >= 34) {
            registerScreenCaptureCallback(mainExecutor, screenCaptureCallback)
        }
    }

    override fun onStop() {
        super.onStop()
        if (Build.VERSION.SDK_INT >= 34) {
    // Android14の機能の為Android13以前では使用しないようにする
            unregisterScreenCaptureCallback(screenCaptureCallback)
        }
    }
}

※実際ではAlertDialogは画面回転時にメモリリークが発生する可能性がある為、DialogFragmentを継承したクラスを作って使用することをおすすめします。(今回は簡略化の為直接記載しています)

実際の挙動について

ややこしいですが、こちらはスクリーンショット撮影後をスクリーンショット撮影した画像になります。

スクリーンショット撮影を無効化している為、後ろは真っ黒い画面になっています。
そして画面中央には実装した注意メッセージのダイアログが表示されてますね。
それ以外の画面下の2つに関してはOS側から自動的に表示されているものになります。
スクリーンショット撮影検出APIによって新たに表示されるようになったのは「サンプルアプリ がこのスクリーンショットを検出」表示の部分で、"サンプルアプリ"の部分に関しては設定されているアプリ名が表示されるようになっていました。

このようにスクリーンショット撮影検出APIを利用することで、ユーザとしては「スクリーンショットを撮ったのに何故か真っ黒い画面の画像が保存された。なぜ?」という事にならず、理由が明記されていてすぐに理解することができます。

まとめ

見ていただいた通りスクリーンショット撮影検出APIによってとても簡単にスクリーンショット撮影検出をすることができます。 うまく活用することでより優れたユーザー体験が提供できるものになります。
今後、Andoroid14が普及されると共に各アプリに導入されて活用されていくかもしれませんね。

最後までお読みいただきありがとうございました。


アドグローブでは、さまざまなポジションで一緒に働く仲間を募集しています!
詳細については下記からご確認ください。みなさまからのご応募お待ちしております。

採用情報