■Android: アプリにPush機能を実装
■app\build.gradle
implementation 'com.google.firebase:firebase-analytics:17.2.0'
↓
implementation 'com.google.firebase:firebase-analytics:17.2.0'
implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-messaging:19.0.0'
「firebase-messaging」の最新バージョンは以下で確認できる
バージョン情報以外にも、手順は参考にできる
Android プロジェクトに Firebase を追加する | Firebase
https://firebase.google.com/docs/android/setup?hl=ja
追加したら「今すぐ同期」
■app\src\main\AndroidManifest.xml
<activity android:name=".MainActivity">
〜略〜
</activity>
の直後に以下を追加
<service
android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
■app\src\main\res\drawable-hdpi
プッシュ用のアイコン画像を追加(アイコンの指定を省略すると、プッシュを受信できないようなので指定する)
ic_launcher.png ... 72px×72pxのPNG画像にしたが、サイズなど改めて調査したい
ic_stat_notification.png ... 38px×38pxのPNG画像にしたが、サイズなど改めて調査したい
★アイコン無しで動作するか確認したい
★アプリが起動中か否かで、プッシュにアイコンが反映されたりされなかったりする。対応したい
■app\src\main\res\layout\activity_main.xml
★Android版とiOS版で動きをできるだけ統一したい。両方ともタイトルをサーバ側から指定できるようにしたり。バイブレーションや音の設定をしたり
<?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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:text="DeviceToken"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_device_token"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="24dp"
android:ems="10"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.545"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
■app\src\main\res\values\strings.xml
<resources>
<string name="app_name">Push Test1</string>
<string name="default_notification_channel_id">0</string>
</resources>
■app\src\main\java\net\refirio\pushtest1\MainActivity.kt
package net.refirio.pushtest1
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.EditText
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.iid.FirebaseInstanceId
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// テキストフィールド
val textDeviceToken = findViewById<EditText>(R.id.text_device_token)
// トークンを取得
FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.d("MainActivity", "instanceId failed", task.exception)
return@OnCompleteListener
}
val token: String = task.result!!.token
textDeviceToken.setText(token)
})
}
}
■app\src\main\java\net\refirio\pushtest1\MyFirebaseMessagingService.kt
package net.refirio.pushtest1
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
/**
* メッセージ受信時の処理
*/
override fun onMessageReceived(remoteMessage: RemoteMessage?) {
Log.d("MyFirebaseMsgService", "From: " + remoteMessage!!.from!!)
// メッセージがdataを含むかどうかを確認
if (remoteMessage.getData().isNotEmpty()) {
Log.d("MyFirebaseMsgService", "Message data payload: " + remoteMessage.getData().get("default"))
// 10秒以上処理にかかる場合は、Firebase Job Dispatcherを使用する
sendNotification(this, remoteMessage.getData().get("default"))
}
// メッセージがnotificationを含むかどうか確認
if (remoteMessage.notification != null) {
Log.d("MyFirebaseMsgService", "Message Notification Body: " + remoteMessage.notification!!.body!!)
// 通知を表示する
sendNotification(this, remoteMessage.notification!!.body!!)
}
}
/**
* 通知を表示する
*/
private fun sendNotification(packageContext : Context, messageBody : String?) {
val intent = Intent(packageContext, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent : PendingIntent = PendingIntent.getActivity(packageContext, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT)
val channelId : String= getString(R.string.default_notification_channel_id)
val notificationBuilder : NotificationCompat.Builder = NotificationCompat.Builder(packageContext, channelId)
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(getString(R.string.app_name))
.setContentText(messageBody)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
val notificationManager : NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
■動作確認
アプリを実行すると、画面内のテキストフィールドに文字列(デバイストークン)が表示される
この文字列をもとに、サーバサイドプログラムからプッシュを送信する
■Firebaseから送信(デバッグ用)
プロジェクトのページを開く
左メニュー
拡大 → Cloud Messaging → Send your first message(最初のメッセージを送信)
※「拡大」はメニューの下の方にある。初期表示では隠れている可能性があるので、下にスクロールする
※2回目以降に送信するときは「新しい通知」をクリック
通知のタイトル: テストのタイトル
通知テキスト: テストのテキスト
ターゲット: ユーザーセグメント
ターゲットとするユーザー: アプリ net.refirio.pushtest1
その他必要に応じて設定し、「確認」をクリックすると確認画面が表示されるので、「公開」をクリックすると送信できる