0.はじめに
以前、こちらの記事を投稿したんですが、
書いた以上はやらないとな…、
と思い、
Android 全く使ったことないですが…、
頑張って Android から Amazon SNS を使ってみました。
1.Android Studio で、新規プロジェクトを作成する
- Android Studio を起動し、「Start a new Android Studio project」リンクをクリックします。
- 「Create Android Project」画面が表示されるので、以下の項目を入力し「Next」ボタンを押下します。
- 「Target Android Devices」画面が表示されるので、とりあえずデフォルトの値のままで「Next」ボタンを押下します。
- 「Add an Activity to Mobile」画面が表示されるので、「Empty Activity」を選択し「Next」ボタンを押下します。
- 「Configure Activity」画面が表示されるので、とりあえずデフォルトの値のままで「Next」ボタンを押下します。
- そのまま待っていると、プロジェクトが作成されます。
2.Android Studio で、作成したプロジェクトを実機で動かす
- 実機を接続して、以下のサイトを参考に開発モードの設定をします。
- Android Studio からプロジェクトを開き、「Run」アイコンをクリックします。
- 「Select Deployment Target」ダイアログが表示されるので、接続した実機が表示されていることを確認し、選択後、「OK」ボタンを押下します。
- 実機にアプリがインストール後、実行され、以下の「Hello World!」の画面が表示されます。
3.Firebase プロジェクトを作成する
- 以下のサイトから、FireBase のコンソールを開き、「プロジェクトを追加」ボタンを押下します。
- 「プロジェクトを追加」ダイアログが表示されるので、以下の項目を設定し、「プロジェクトを作成」ボタンを押下します。
- 以下の画面が表示され、Firebase プロジェクトが作成されます。
4.Firebase プロジェクトにアプリを登録する。
- 作成した Firebase プロジェクトのオーバービューから、「Android アプリに Firebase を追加」ボタンを押下します。
- 「Android アプリに Firebase を追加」ダイアログが表示されるので、以下の項目を設定し、「アプリの登録」ボタンを押下します。
- 「Android アプリに Firebase を追加」ダイアログが更新され、「Android Studio の使用手順」が表示されるので、手順に従って google-services.json をダウンロードし、所定の場所に移動後、「続行」ボタンを押下します。
- 「Android アプリに Firebase を追加」ダイアログが更新され、「Gradle の使用手順」が表示されるので、手順に従って2つの build.gradle を変更し、「Sync now」リンククリック後、「終了」ボタンを押下します。
- 「Android アプリに Firebase を追加」ダイアログが閉じ、Firebase プロジェクトの「概要」画面が表示されるので、アプリが登録されたことを確認します。
5.Firebase プロジェクトに登録したアプリのサーバーキーを確認する
- Firebase プロジェクトの「概要」画面に表示されている登録したアプリエリアの右上のメニューを開き、「設定」を選択します。
- Firebase プロジェクトの「設定」画面が表示されるので、「クラウドメッセージング」タブを選択します。
- Firebase プロジェクトの「設定」画面に「クラウドメッセージング」の情報が表示されるので、「プロジェクト認証情報」エリアの「サーバーキー」を確認します。
6.Amazon SNS にプラットフォームアプリケーションを作成する
- 以下のサイトから、Amazon SNS のコンソールを開きます。
- Amazon SNS のコンソールの左側ペインから、「アプリケーション」を選択します。
- Amazon SNS の「アプリケーション」画面が表示されるので、「プラットフォームアプリケーションの作成」ボタンを押下します。
- 「プラットフォームアプリケーションの作成」ダイアログが表示されるので、以下の項目を設定し、「プラットフォームアプリケーションの作成」ボタンを押下します。
- Amazon SNS のプラットフォームアプリケーションが作成されます。
7.Amazon Cognito に ID プールを作成する
- 以下のサイトから、Amazon Cognito フェデレーテッドアイデンティティのコンソールを開きます。
- 「新しい ID プールの作成」ボタンを押下します。
- 「使用開始ウィザード - ステップ 1:ID プールを作成する」画面が表示され、以下の項目を設定し、「プールの作成」ボタンを押下します。
- 画面が更新されるので、新しく作成される2つ(Auth と Unauth)の IAM ロールのロール名を確認し、「許可」ボタンを押下します。
- 「サンプルコード」画面が表示され、ID プールが作成されます。
8.作成した Amazon Cognito ID プールの IAM ロール に Amazon SNS 用のインラインポリシーを追加する
- 以下のサイトから、AWS Identity and Access Management (IAM) のコンソールを開きます。
- AWS Identity and Access Management (IAM) のコンソールの左側ペインから、「ロール」を選択します。
- ロールの一覧が表示されるので、作成した Amazon Cognito ID プールの IAM ロール (Unauth) の「ロール名」リンクをクリックします。
- 作成した Amazon Cognito ID プールの IAM ロール (Unauth) の概要の画面が表示されるので、「アクセス権限」のタブを選択し、「インラインポリシーの追加」リンクをクリックします。
- 「ポリシーの作成」画面が表示されるので、「ビジュアルエディタ」のタブを選択後、以下の項目を設定し、「Review policy 」ボタンを押下します。
- 「ポリシーの作成 - ポリシーの確認」画面が表示されるので、以下の項目を設定し、「Create policy 」ボタンを押下します。
- 作成したインラインポリシーの内容を確認します。
9.Android アプリへ実装する
- `//build.gradle` へ、以下のライブラリの登録の設定を追加する。
- AndroidManifest.xml へ、追加するクラスの設定を追加する。
- 以下の MyFirebaseInstanceIDService.java を追加する。
- 以下の MyFirebaseMessagingService.java を追加する。
- MainActivity.java へ、以下のコードを追加する。
- こちらのサイト ( Android に Firebase Cloud Messaging クライアント アプリを設定する | Firebase ) にも記載がありますが、FCM の登録トークンを改めて取得したい場合は、アプリをアンインストールしたりすればいい様なので、とりあえず一度アプリをアンインストールします。アプリのアンインストールは、ググれば出て来ると思います。
- Android Studio からデバッグモードで RUN 実行します。
- Android Studio の Logcat に登録トークンがログ出力されます。
- Amazon SNS のプラットフォームアプリケーションの詳細画面を開き、登録トークンからエンドポイントが作成されたか確認します。
- 作成されたエンドポイントを選択し、「エンドポイントの発行」ボタンを押下します。
- 「メッセージを発行」画面が表示されるので、以下の項目を設定し、「メッセージの発行」ボタンを押下します。
- 実機に通知が届いたか確認します。
- 通知の確認のところでも確認しましたが、登録トークンがどうやったら改めて取得できるのかよくわからなく、ちょっと戸惑いました。
- 今回、Firebase プロジェクトのアプリの登録を手動で行いましたが、以下のサイトを見ると Firebase Assistant を使用して登録する手順を推奨している様にも思いました。
- 後は単純に Android 開発自体ほぼ初めてだったので、Android Studio の使い方からよくわからなかったので、ちょっと苦労しました。
- っていうか、Android も触ったことなかったので、アプリのアンインストールのやり方とか、通知の確認の仕方とか、逐一わからなくて苦労しました。
- [Android]プッシュ通知実装[Firebase][FCM] – チェンぴログ
- マニフェストの設定
- [Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発
- 【Android】NotificationからIntentでデータを渡してアプリ起動するときのIntent.setFlagsメモ - Qiita
build.gradle
〜
dependencies {
〜
// [2018.01.12] ADD - BGN
// 「Android端末で、FCM経由でAWSSNSを受け取るまで - Qiita」
// <https://qiita.com/k_keisuke/items/24afa62955808721033e>
// ...
compile 'com.google.firebase:firebase-core:10.2.1'
compile 'com.google.firebase:firebase-messaging:10.2.1'
// ...
compile 'com.amazonaws:aws-android-sdk-core:2.4.+'
compile 'com.amazonaws:aws-android-sdk-sns:2.4.+'
// [2018.01.12] ADD - END
}
〜
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kusokamayarou.android_sns_sample0001">
<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">
〜
<!-- [2018.01.12] ADD - BGN -->
<!--「マニフェストの設定」-->
<!-- https://docs.kii.com/ja/samples/push-notifications/push-notifications-android-fcm/modify-manifest/ -->
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<!-- [2018.01.12] ADD - END -->
</application>
</manifest>
MyFirebaseInstanceIDService.java
package com.kusokamayarou.android_sns_sample0001;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// 「Android端末で、FCM経由でAWSSNSを受け取るまで - Qiita」
// <https://qiita.com/k_keisuke/items/24afa62955808721033e>
import com.amazonaws.regions.Regions;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.CreatePlatformEndpointRequest;
import com.amazonaws.services.sns.model.CreatePlatformEndpointResult;
import com.amazonaws.services.sns.model.InvalidParameterException;
import com.amazonaws.services.sns.model.SetEndpointAttributesRequest;
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private final static String TAG = MyFirebaseInstanceIDService.class.getSimpleName();
private static final String APPLICATION_ARN = "[プラットフォームアプリケーションの ARN]";
private static final String ENDPOINT = "https://sns.ap-northeast-1.amazonaws.com";
//private static final String ACCESS_KEY = "AWSのアクセスキー";
//private static final String SECRET_KEY = "AWSのSecretKey";
@Override
public void onTokenRefresh() {
super.onTokenRefresh();
String token = FirebaseInstanceId.getInstance().getToken();
sendRegistrationToServer(token);
}
@SuppressLint("LongLogTag")
private void sendRegistrationToServer(String token) {
//AmazonSNSClient client = new AmazonSNSClient(generateAWSCredentials());
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"[Amazon Cognito の ID プールの ID]", // ID プールの ID
Regions.AP_NORTHEAST_1 // リージョン
);
AmazonSNSClient client = new AmazonSNSClient(credentialsProvider);
client.setEndpoint(ENDPOINT);
//SharedPreferenceに保存したendpointArnが存在したらそちらから取得するようにしてもOK
String endpointArn = createEndpointArn(token, client);
// 「[Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発」
// <https://akira-watson.com/android/sharedpreferences.html>
SharedPreferences dataStore = getSharedPreferences("DataStore", MODE_PRIVATE);
SharedPreferences.Editor editor = dataStore.edit();
editor.putString("DeviceToken", token);
editor.putString("EndpointArn", endpointArn);
editor.apply();
Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | DeviceTokenArray: " + token);
Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | EndpointArnArray: " + endpointArn);
HashMap<String, String> attr = new HashMap<>();
attr.put("Token", token);
attr.put("Enabled", "true");
SetEndpointAttributesRequest req = new SetEndpointAttributesRequest().withEndpointArn(endpointArn).withAttributes(attr);
client.setEndpointAttributes(req);
}
@SuppressLint("LongLogTag")
private String createEndpointArn(String token, AmazonSNSClient client) {
String endpointArn;
try {
System.out.println("Creating platform endpoint with token " + token);
CreatePlatformEndpointRequest cpeReq =
new CreatePlatformEndpointRequest()
.withPlatformApplicationArn(APPLICATION_ARN)
.withToken(token);
CreatePlatformEndpointResult cpeRes = client
.createPlatformEndpoint(cpeReq);
endpointArn = cpeRes.getEndpointArn();
} catch (InvalidParameterException ipe) {
String message = ipe.getErrorMessage();
System.out.println("Exception message: " + message);
Pattern p = Pattern
.compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
"with the same token.*");
Matcher m = p.matcher(message);
if (m.matches()) {
// The platform endpoint already exists for this token, but with
// additional custom data that
// createEndpoint doesn't want to overwrite. Just use the
// existing platform endpoint.
endpointArn = m.group(1);
} else {
// Rethrow the exception, the input is actually bad.
throw ipe;
}
}
storeEndpointArn(endpointArn);
return endpointArn;
}
/*
private AWSCredentials generateAWSCredentials() {
return new AWSCredentials() {
@Override
public String getAWSAccessKeyId() {
return ACCESS_KEY;
}
@Override
public String getAWSSecretKey() {
return SECRET_KEY;
}
};
}
*/
private void storeEndpointArn(String endpointArn) {
//SharedPreferenceにでもendpointArnを保存して、次回以降はcreateEndpointArnの処理を省略しても良い(公式はその方式になってる)
}
/*
private String getEndPointArn() {
//SharedPreferenceからendpointArnを取得して、次回以降はcreateEndpointArnの処理を省略しても良い(公式はその方式になってる)
}
*/
}
MyFirebaseMessagingService.java
package com.kusokamayarou.android_sns_sample0001;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private final static String TAG = MyFirebaseMessagingService.class.getSimpleName();
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
@SuppressLint("LongLogTag")
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.i(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.i(TAG, "Message data payload: " + remoteMessage.getData());
}
// 「【Android】NotificationからIntentでデータを渡してアプリ起動するときのIntent.setFlagsメモ - Qiita」
// <https://qiita.com/SnowMonkey/items/7cdc52c83bb9490d97a5>
NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("hoge", "fuga");
intent.setFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP // 起動中のアプリがあってもこちらを優先する
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED // 起動中のアプリがあってもこちらを優先する
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS // 「最近利用したアプリ」に表示させない
);
PendingIntent contentIntent =
PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setContentTitle(remoteMessage.getData().toString())
.setContentText("Notification Message")
.setStyle(new NotificationCompat.BigTextStyle().bigText("Notification Message"));
mBuilder.setContentIntent(contentIntent);
notificationManager.notify(0, mBuilder.build());
}
}
MainActivity.java
package com.kusokamayarou.android_sns_sample0001;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
// [2018.01.12] ADD - BGN
private final static String TAG = MainActivity.class.getSimpleName();
// [2018.01.12] ADD - END
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// [2018.01.12] ADD - BGN
// 「[Android] データを保存し Android Studio で確認 SharedPreferences | nyanのアプリ開発」
// <https://akira-watson.com/android/sharedpreferences.html>
SharedPreferences dataStore = getSharedPreferences("DataStore", MODE_PRIVATE);
String DeviceTokenArray = dataStore.getString("DeviceToken", "");
Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | DeviceTokenArray: " + DeviceTokenArray);
String EndpointArnArray = dataStore.getString("EndpointArn", "");
Log.i(TAG, "@@@@@@@@@@@@@@@@@@@@ | EndpointArnArray: " + EndpointArnArray);
// [2018.01.12] ADD - END
}
}
10.アプリを実行し、Amazon SNS からの通知を確認する。
99.ハマりポイント
XX.まとめ
コードにもコメントで記載していますが、今回も多くのサイトに助けられました。
特に、こちらのサイトはお世話になりました。
っていうか、
ほぼ丸コピに近い様な…。
本当にありがとうございました。
他にも、
など、
参考にさせて頂きました。
ありがとうございました。
次は、iOS だな…。