Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kotlin FirebaseMessaging 通知受信時にアプリの状態を参照

Last updated at Posted at 2024-11-16

Kotlin FirebaseMessaging 通知受信時にアプリの状態を知る

通知を受信した際に、アプリがフォアグランド/バックグラウンド/ターミネートの状態で処理を分けたい場合がります。とくにフルスクリーン通知やFlutterでの処理を行う場合など。FirebaseMessagingService内でその状態を知ることができます。

フォアグランド/バックグラウンドか判定

PushNotificationListenerService.kt
class PushNotificationListenerService : FirebaseMessagingService() {

    override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)

        val isForeground = isAppInForeground(this)

        if (isForeground) {
            Log.d("FCM", "アプリはフォアグラウンド")
            handleForegroundNotification(message)
        } else {
            Log.d("FCM", "アプリはバックグラウンド")
            handleBackgroundNotification(message)
        }
    }

    private fun isAppInForeground(context: Context): Boolean {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val appProcesses = activityManager.runningAppProcesses ?: return false

        for (appProcess in appProcesses) {
            if (appProcess.processName == context.packageName) {
                return appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
            }
        }
        return false
    }

    private fun handleForegroundNotification(message: RemoteMessage) {
        // フォアグラウンド時の通知処理
        Log.d("FCM", "フォアグラウンド通知処理")
    }

    private fun handleBackgroundNotification(message: RemoteMessage) {
        // バックグラウンド時の通知処理
        Log.d("FCM", "バックグラウンド通知処理")
    }
}

こんなふうにして利用

AndroidManifest.xml
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    <!-- MESSAGING_EVENTコールバックハンドラ登録 -->
    <service
            android:name=".PushNotificationListenerService"
            android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>

フォアグランド/バックグラウンド/ターミネートの判定

PushNotificationListenerService.kt
class PushNotificationListenerService : FirebaseMessagingService() {

    override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)

        val appState = getAppState()
        Log.d("FCM", "アプリの状態: $appState")

        when (appState) {
            AppState.FOREGROUND -> {
                Log.d("FCM", "フォアグラウンド: アプリが表示中です")
                handleForegroundNotification(message)
            }
            AppState.BACKGROUND -> {
                Log.d("FCM", "バックグラウンド: アプリは非表示ですが動作中です")
                handleBackgroundNotification(message)
            }
            AppState.TERMINATED -> {
                Log.d("FCM", "ターミネート: アプリが完全に終了していました")
                handleTerminatedNotification(message)
            }
        }
    }

    private fun handleForegroundNotification(message: RemoteMessage) {
        Log.d("FCM", "フォアグラウンド通知処理を実行")
    }

    private fun handleBackgroundNotification(message: RemoteMessage) {
        Log.d("FCM", "バックグラウンド通知処理を実行")
    }

    private fun handleTerminatedNotification(message: RemoteMessage) {
        Log.d("FCM", "ターミネート通知処理を実行")
        // フルスクリーン通知などを呼び出し
    }

    private fun getAppState(): AppState {
        val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val appProcesses = activityManager.runningAppProcesses

        // アプリプロセスが存在しない場合はターミネート
        if (appProcesses.isNullOrEmpty()) {
            return AppState.TERMINATED
        }

        // アプリプロセスを検索
        for (appProcess in appProcesses) {
            if (appProcess.processName == packageName) {
                return when (appProcess.importance) {
                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> AppState.FOREGROUND
                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND,
                    ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> AppState.BACKGROUND
                    else -> AppState.TERMINATED
                }
            }
        }

        // プロセス情報が取得できない場合もターミネートと見なす
        return AppState.TERMINATED
    }

    enum class AppState {
        FOREGROUND,
        BACKGROUND,
        TERMINATED
    }
}

フルスクリーン通知の例 Android 14以降


    // 新しいトークンが生成された時の処理
    // 中でサーバにトークンを送信する処理などを定義
    override fun onNewToken(p0: String) {
        super.onNewToken(p0)

        // チャンネルidを設定
        addChannelId()
    }
    
    // チャンネルの設定
    private fun addChannelId() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channelId = resources.getString(R.string.channel_id)
            val channelName = resources.getString(R.string.channel_name)
            val importance = NotificationManager.IMPORTANCE_HIGH
            val notificationChannel = NotificationChannel(channelId, channelName, importance).apply {
                enableLights(true) // ライトの設定を有効にする
                lightColor = Color.BLUE // ライトの色を設定
                enableVibration(true) // バイブレーションを有効にする
                description = "This channel is used for important notifications"
            }

            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

    private fun sendNotificationFullScreen(title: String, text: String) {
        // 通知タップ時に遷移するアクティビティを指定
        val intent = Intent(this, MainActivity::class.java)
        //val intent = Intent(this, CustomActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        // 何度も遷移しないようにする(1度だけ!)
        val pendingIntent: PendingIntent = PendingIntent.getActivity(this,0, intent, PendingIntent.FLAG_IMMUTABLE)

        // 通知メッセージのスタイルを設定(改行表示に対応)
        val inboxStyle = NotificationCompat.InboxStyle()
        val messageArray: List<String> = text.split("\n")
        for (msg: String in messageArray) {
            inboxStyle.addLine(msg)
        }

        // 通知音の設定
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)

        // 通知の見た目を設定
        val notificationBuilder
                = NotificationCompat.Builder(this, resources.getString(R.string.channel_id))
                .setContentTitle(title)
                .setContentText(text)
                // ステータスバーに表示されるアイコン
                .setSmallIcon(R.mipmap.ic_launcher)
                // 上で設定したpendingIntentを設定
                .setContentIntent(pendingIntent)
                // 優先度を最大化
                .setPriority(PRIORITY_MAX)
                .setDefaults(NotificationCompat.DEFAULT_ALL)
                // 通知音を出すように設定
                .setSound(defaultSoundUri)
                .setFullScreenIntent(pendingIntent, true)

        // Android 8.0 以上はチャンネル設定 必須
        // strings.xml に channel_id を指定してください
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationBuilder.setChannelId(resources.getString(R.string.channel_id))
        }
        
        // 通知を実施
        // UUIDを付与することで各通知をユニーク化
        val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val uuid = UUID.randomUUID().hashCode()
        notificationManager.notify(uuid, notificationBuilder.build())

    }

補足

実行環境によってはうまくいかないこともあるようなので、、、

private fun getAppState(): AppState {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val appProcesses = activityManager.runningAppProcesses ?: return AppState.TERMINATED

    // プロセスがリスト内に存在するかを確認
    val appProcess = appProcesses.find { it.processName == packageName }

    if (appProcess == null) {
        // プロセスが見つからない場合はターミネートとみなす
        return AppState.TERMINATED
    }

    // プロセスが見つかった場合は状態を判定
    return when (appProcess.importance) {
        ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> AppState.FOREGROUND
        ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND,
        ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> AppState.BACKGROUND
        else -> AppState.TERMINATED
    }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Comments

No comments

Let's comment your feelings that are more than good

1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address