Android(Kotlin)Navigation 通知タップで遷移

September 01, 2020

はじめに

過去の記事で 、Navigation コンポーネントでアプリバーを実装しました。今回は「通知タップで目的のデスティネーションへ遷移」を実装してみます。 (後述する差分は前述記事からの差分になります)。

実装内容

概要

通知タップアクションと遷移を紐付けるには、ドキュメント記載の通り、遷移用 PendingIntent を用意すれば良いです。

Navigation コンポーネントにおいては、ディープリンクが用意されているので、今回は明示的ディープリンクとしての PendingIntent を作成します。

差分

app/src/main/java/com/example/myapplication/FirstFragment.kt

通知の処理詳細はともかく、ここで重要なのは NavDeepLinkBuilderPendingIntent を作成し、それを setContentIntent していることです。

チャネル作成に関しては通知ドキュメント記載のサンプルコードを機械的に持って来ていますので細かいことは気にしないでください。

@@ -1,11 +1,18 @@
 package com.example.myapplication
 
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.os.Build
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Button
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
 import androidx.fragment.app.Fragment
+import androidx.navigation.NavDeepLinkBuilder
 import androidx.navigation.findNavController
 
 class FirstFragment : Fragment() {
@@ -20,6 +27,45 @@ class FirstFragment : Fragment() {
                     it.findNavController().navigate(action)
                 }
             }
+            findViewById<Button>(R.id.first_notification_button).apply {
+                setOnClickListener {
+                    val notificationId = 0 // 動作確認のためだけに適当に。
+                    val context = requireContext()
+                    createNotificationChannel(context)
+                    val pendingIntent = NavDeepLinkBuilder(context)
+                        .setGraph(R.navigation.nav_graph)
+                        .setDestination(R.id.secondFragment)
+                        .createPendingIntent()
+                    val builder = NotificationCompat.Builder(context, CHANNEL_ID)
+                        .setSmallIcon(R.drawable.ic_launcher_foreground)
+                        .setContentTitle("タイトル")
+                        .setContentText("内容")
+                        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+                        .setContentIntent(pendingIntent)
+                        .setAutoCancel(true)
+                    with(NotificationManagerCompat.from(context)) {
+                        notify(notificationId, builder.build())
+                    }
+                }
+            }
+        }
+    }
+
+    companion object {
+        private const val CHANNEL_ID = "CHANNEL_ID"
+    }
+
+    private fun createNotificationChannel(context: Context) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            val name = getString(R.string.channel_name)
+            val descriptionText = getString(R.string.channel_description)
+            val importance = NotificationManager.IMPORTANCE_DEFAULT
+            val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
+                description = descriptionText
+            }
+            val notificationManager: NotificationManager =
+                context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+            notificationManager.createNotificationChannel(channel)
         }
     }
 }

app/src/main/res/layout/fragment_first.xml

通知を表示するためのボタンを用意しただけです。

@@ -11,6 +11,12 @@
         android:layout_height="wrap_content"
         android:text="first_fragment" />
 
+    <Button
+        android:id="@+id/first_notification_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Notification" />
+
     <Button
         android:id="@+id/first_button"
         android:layout_width="wrap_content"

app/src/main/res/values/strings.xml

持ってきた通知チャネル作成サンプルコードに応じて設定を追加しただけです。

diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 82d06a3..c573279 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,4 +2,6 @@
     <string name="app_name">My Application</string>
     <string name="destination_first">画面1</string>
     <string name="destination_second">画面2</string>
+    <string name="channel_name">channel_name</string>
+    <string name="channel_description">channel_description</string>
 </resources>

補足

遷移時に引数が必要な場合は下記の通り、Safe Args により生成された引数クラスをインスタンス化し、toBundle() したものを setArguments してあげれば良いみたいです。

val pendingIntent = NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.secondFragment)
    .setArguments(SecondFragmentArgs(arg).toBundle())
    .createPendingIntent()

Product

「いつやるかは決めてないけれど、必ずやらなければいけないタスク」を忘れないためのアプリ