【Android】FirebaseCloudMessagingを使ってみた

お約束

この記事は「いのべこ夏休みアドベントカレンダー2020」の19日目(8月19日)の記事です。 本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません(お約束)。

本題

L●NEは、ユーザ側でアプリをタスクキルしていても通知が届く。 さて、どうやって実現するんだろう?という話。

クラウドメッセージという存在

Googleが出しているクラウドメッセージサービスがある。 2019年まではGCM(Google Cloud Messaging)だったが、2020年現在はFCM(Firebase Cloud Messaging)である。

Firebaseのサイトに沿ってプロジェクト情報の登録、アプリ情報(アプリ名とかパッケージ名)の登録をして。 アプリ実装側では、Firebaseでアプリ情報を登録してる間に生成されたJSONファイルを組み込んで。 SDKもbuild.gradleに追記しつつ、デバイストークンを取得。 取得したデバイストークン&登録したアプリのパッケージ名に向けて、通知を送信してやる流れ。

軽くやってみることにする。

Firebaseのサイトが1ステップごとに説明してくれるので、それにそってやれば大体できる。

ステップ Firebase/AndroidStudio やること
事前準備 Firebase コンソール画面にGoogleアカウントでログイン。
必要ならアカウント作って。
事前準備 AndroidStudio AndroidStudioを使い、アプリを新規作成する
アプリ名、パッケージ名はあとで使うので覚えておくこと。
プロジェクト登録 Firebase コンソールトップ画面から、プロジェクトを追加する。
プロジェクト登録 Firebase プロジェクト名をつける
プロジェクト登録 Firebase Googleアナリティクスを有効にするか否か選ぶ。
地域も選ぶ
今回はアナリティクスは有効。地域は日本にした。
プロジェクト登録 Firebase いろいろ同意するチェックを入れるとプロジェクトができる。
アプリ登録 Firebase プロジェクト作成後の画面で、作りたいアプリの種類を選ぶ。
今回はAndroid
アプリ登録 Firebase 先ほどAndroidStudioで作ったアプリのアプリ名・パッケージ名を入力する
アプリ登録 Firebase google-service.jsonが生成される。
このJSONファイルはapp配下にコピーする
アプリ登録 Firebase
&
AndroidStudio
[Firebase]google-service.jsonが生成される。
[AndroidStudio]このJSONファイルはapp配下にコピーする
アプリ登録 AndroidStudio [プロジェクトフォルダ]/build.gradleに依存関係を追加する。※ソース1
アプリ登録 AndroidStudio app/build.gradleに依存関係を追加する。※ソース2
実装 AndroidStudio バイストークンを取得し、画面に表示されるMainActivity.ktを作った。※ソース3
実装 AndroidStudio FirebaseMessagingServiceを継承したクラスを作る。※ソース4
実装 AndroidStudio AndroidManifestを修正。※ソース5
実装 AndroidStudio レイアウトをいじる。※ソース6
テスト AndroidStudio エミュレータを起動し、画面に表示されているデバイストークンをコピーしておく
テスト Firebase 左側メニューの「拡張>CloudMessaging>Send your First Message」を選ぶ | 
テスト Firebase 通知タイトル・内容を入力し、「テストメッセージを送信」を押下
テスト Firebase FCMトークンを入力して、送信を押す
テスト AndroidStudio エミュレータにインストールしたアプリに対して通知されてることを確認する

ね?簡単でしょ? Googleアカウント無料で作れるし、ぜひ遊んでみて!

いのべこ夏休みアドベントカレンダー2020では2本目。 また画面キャプチャとソースばかりの記事になってしまった。。 次回以降もこんな感じで、このお祭りに参加していきたい。

以下、ソースと画面キャプチャ

ソース1:プロジェクトフォルダ直下のbuild.gradle

buildscript {
    repositories {
        // 追加
        google()
    }
    dependencies {
        // 追加
        classpath 'com.google.gms:google-services:4.3.3'
    }
}

ソース2:app/bild.gardle

apply plugin: 'com.google.gms.google-services'

dependencies {
    implementation 'com.google.firebase:firebase-analytics:17.4.4'
    implementation 'com.google.firebase:firebase-messaging:20.2.4'
}

ソース3:MainActivity.kt

class MainActivity : AppCompatActivity() {
    @SuppressLint("StringFormatInvalid")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        FirebaseInstanceId.getInstance().instanceId
            .addOnCompleteListener(OnCompleteListener { task ->
                if (!task.isSuccessful) {
                    Log.w("narita", "getInstanceId failed", task.exception)
                    return@OnCompleteListener
                }
                // Get new Instance ID token
                val token = task.result?.token
                textView.text = token

                Log.i("token:",token.toString())

                // サーバにトークン送信していないため、トークンをTextBoxに格納
                textBox.setText(token, TextView.BufferType.NORMAL)
            })
    }
}

ソース4:FirebaseMessagingServiceを継承したMyFirebaseMessage.kt

private val TAG: String = com.test.notification.MyFirebaseMessage::class.java.simpleName

    override fun onMessageReceived(message: RemoteMessage) {
        val from = message.from
        val data: Map<*, *> = message.data
        Log.d(TAG, "from:$from")
        Log.d(TAG, "data:$data")
        val msg = data["data"].toString()
        sendNotification(msg)
    }

    private fun sendNotification(message: String) {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent,
            PendingIntent.FLAG_ONE_SHOT
        )
        val defaultSoundUri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
        val notificationBuilder = NotificationCompat.Builder(this)
            .setSmallIcon(R.mipmap.sym_def_app_icon)
            .setContentTitle("Push通知のタイトル")
            .setSubText("Push通知のサブタイトル")
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setStyle(NotificationCompat.BigTextStyle().bigText(message))
            .setContentIntent(pendingIntent)
        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(0, notificationBuilder.build())
    }

ソース5:AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.notification">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_icon"
            android:resource="@drawable/ic_launcher_foreground" />
        <!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
             notification message. See README(https://goo.gl/6BKBk7) for more. -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/colorAccent" />

        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/common_google_play_services_notification_channel_name" />
        <service
            android:name=".MyFirebaseMessage"
            android:enabled="true"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

ソース6:レイアウトactivity_main.xml

<?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:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/textBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="114dp"
        android:layout_marginTop="419dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

公式サイト


画面キャプチャ

プロジェクトを作る

f:id:levia9071:20200831192232p:plain f:id:levia9071:20200831192401p:plain f:id:levia9071:20200831192521p:plain f:id:levia9071:20200831192543p:plain f:id:levia9071:20200831211135p:plain

アプリを登録する

f:id:levia9071:20200831211211p:plain f:id:levia9071:20200831211220p:plain f:id:levia9071:20200831211229p:plain f:id:levia9071:20200831211239p:plain f:id:levia9071:20200831211252p:plain f:id:levia9071:20200831211302p:plain f:id:levia9071:20200831211315p:plain f:id:levia9071:20200831211321p:plain f:id:levia9071:20200831211334p:plain f:id:levia9071:20200831211342p:plain

テスト

f:id:levia9071:20200831214351p:plain f:id:levia9071:20200831214518p:plain f:id:levia9071:20200831214655p:plain f:id:levia9071:20200831215107p:plain f:id:levia9071:20200831215323p:plain