Androidアプリで通知にボタンを追加して、タップ時にForeground Serviceを停止する

Foreground Serviceを使うと通知表示が必要です。この通知表示に「停止する」ボタンを表示して、選択時にForeground Serviceを停止させてみました。
2020.03.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ユーザが任意のタイミングでForeground Serviceを終了したいとき、アプリを起動して操作するのは地味に手間です。 そこで、通知に「停止する」ボタンを表示して、そのボタンをタップしたらForeground Serviceを停止させてみました。

環境

  • Android Studio 3.6
  • Pixcel 3a (Android 10)

サンプルアプリを作ってみる

下記の続きから作成していきます。

ブロードキャストを受信するクラスを作成

通知の停止するボタンが選択されたことを受け取るブロードキャストレシーバーとして、LocationBroadcastReceiver.ktを新規作成します。 onReceive()stopService()を呼んでForeground Serviceを停止させます。

LocationBroadcastReceiver.kt

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class LocationBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val targetIntent = Intent(context, LocationService::class.java)
        context.stopService(targetIntent)
    }
}

AndroidManifestファイルにブロードキャストレシーバークラスを追加

AndroidManifest.xmlreceiverを追加します。

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.test.tryforegroundservicesample">
    ...
    <application ...>
        <activity android:name=".MainActivity">
            ...
        </activity>

        <service android:name=".LocationService" />

        <receiver android:name=".LocationBroadcastReceiver" android:exported="true">
            <intent-filter>
                <action android:name="jp.test.tryforegroundservicesample.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

通知表示に「停止する」ボタンを追加する

ForegroundServiceとして動くLocationService.ktで作成している通知に「停止する」ボタンを追加します。

LocationService.kt

class LocationService : Service() {
    ...

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        ...

        val openIntent = Intent(this, MainActivity::class.java).let {
            PendingIntent.getActivity(this, 0, it, 0)
        }

        val sendIntent = Intent(this, LocationBroadcastReceiver::class.java).apply {
            action = ACTION_SEND
        }
        val sendPendingIntent = PendingIntent.getBroadcast(this, 0, sendIntent, 0)

        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("位置情報テスト")
            .setContentText("位置情報を取得しています...")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setContentIntent(openIntent)
            .addAction(R.drawable.ic_launcher_foreground, "停止する", sendPendingIntent)
            .build()

        startForeground(9999, notification)

        startLocationUpdates()

        return START_STICKY
    }
}

動作確認

アプリをビルドして起動し、「START」ボタンを押すと、通知が表示されます。

通知画面の様子

ログもしっかり出力されています。

[1] 35.xxxx , 139.yyyy
[2] 35.xxxx , 139.yyyy
[3] 35.xxxx , 139.yyyy

この状態で通知にある「停止する」ボタンを押すと、通知が消えてログの出力も止まりました!

さいごに

Foreground Service実行時に表示される通知画面にボタンを追加し、そのボタンを選択するとForeground Serviceを終了させてみました。 BroadcastReceiverなるものまで登場し、色々と絡み合っているんだなぁと思いつつ、BroadcastReceiverについても分かりました。 どなたかの参考になれば幸いです。

参考