AWS SDK for Android SamplesのAndroidPubSubを動かす

## モチベーション
AndroidアプリでIoTデバイスの操作をしたいと思い、
AWS SDK for Android Samplesの[AndroidPubSub](https://github.com/awslabs/aws-sdk-android-samples/tree/master/AndroidPubSub)を動かしてみましたが、
READMEが英語版のページをもとに作成してあり、（個人的に）分かりづらかったので、
自分なりに日本語版ページでの操作方法をまとめ、備忘録として情報を残そうと思いました。
READMEの建付けにしたがって説明していきます。

## 前提条件
- Android Studio（自分の場合はVer3.2.1）
- Android API 10以上

## 各種設定
### Android StudioにAndroidPubSubプロジェクトをインポートする
まずはプロジェクトのソースコードをダウンロードします。
Gitをインストールしていれば、下記のコマンドで任意のフォルダにダウンロードできます。

```
git clone https://github.com/awslabs/aws-sdk-android-samples.git
```

ダウンロードしたら、Android StudioでImport ProjectからAndroidPubSubのフォルダを選択して、インポートします。
![import_androidpubsub_project.png](https://qiita-image-store.s3.amazonaws.com/0/305999/ee2cd84b-e5fe-19b9-cc8c-18aad3663ed1.png)

インポートするとGradleがビルドを始めますが、下記メッセージが出ると思うので
「Add Google Maven repository and sync project」をクリックして対処します。
![gradle_build_failed_1.png](https://qiita-image-store.s3.amazonaws.com/0/305999/c477d734-88c7-6f0f-2a7f-a80728e75a6e.png)
コンパイルするターゲットのAndroidSDKが見つからないと言われたら、
「Install missing platform(s) and sync project」をクリックしてインストールするか、
すでに別のバージョンがインストール済みなら、build.gradleに書いてある「compileSdkVersion」の数値を変更して、
もう一度ビルドします。
![gradle_build_failed_2.png](https://qiita-image-store.s3.amazonaws.com/0/305999/9b617a98-892b-6572-2ea3-1a67e19120bc.png)
![gradle_build_failed_3.png](https://qiita-image-store.s3.amazonaws.com/0/305999/799d7163-ed67-3cfb-299e-f244a4fe4e49.png)
下記のようなメッセージが出たら、Updateしましょう。
![gradle_build_failed_4.png](https://qiita-image-store.s3.amazonaws.com/0/305999/e65f3396-d3e5-25a4-f53c-28f80911abee.png)
下記メッセージが出たら、build.gradleのdependenciesのcompileをimplementationに変更します。
buildToolsVersionを26.0.2から28.0.3に変更します（環境によって違うかも）。
![gradle_build_failed_5.png](https://qiita-image-store.s3.amazonaws.com/0/305999/56e834e6-c096-534b-cd0e-026ef0645b6c.png)
![gradle_build_failed_6.png](https://qiita-image-store.s3.amazonaws.com/0/305999/9426c9a8-8464-3392-8703-fcd5d2e65686.png)
下記メッセージが出たら、AndroidManifest.xmlに書いてある「uses-sdk」を削除します。
![gradle_build_failed_7.png](https://qiita-image-store.s3.amazonaws.com/0/305999/8ca0179f-69ac-e0e0-927a-758a5c0f404c.png)
![gradle_build_failed_8.png](https://qiita-image-store.s3.amazonaws.com/0/305999/bbd8beb0-005a-55ed-6d2f-a3bb6f1fc90b.png)
非推奨の書き方はまだ残っていると思いますが、自分の場合はここまででビルドエラーやワーニングはなくなりましたので先に進みます。

### Cognitoの設定
AWS IoTにアクセスするためのCognito IDを作成します。
[Amazon Cognito Console](https://console.aws.amazon.com/cognito/)にアクセスして、「IDプールの管理」ボタンをクリックします。
IDプール名を入力します。任意の名前でOKです。
その後、「認証されていないIDに対してアクセスを有効にする」チェックボックスにチェックを入れます。
「プールの作成」ボタンをクリックします。
次の画面では、「詳細を表示」をクリックすると、2つのIAMロールの情報が現れます。
特に設定は必要ありませんので、そのまま「許可」ボタンを押します。
さらに次の画面では、下記のようなサンプルコードが生成されます。
<font color ="red">このIDをメモしておきます（下記のap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxの部分）。</font>

```
// Amazon Cognito 認証情報プロバイダーを初期化します
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
    getApplicationContext(),
    "ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // ID プールの ID
    Regions.AP_NORTHEAST_1 // リージョン
);
```

Cognitoの設定は完了です。

### IAMの設定
続いて、[IAM Console](https://console.aws.amazon.com/iam/home#/roles)にアクセスします。
IAMロールの一覧が表示されるので、先ほどのCognitoの設定で作成したIDプール名がついた、
認証されていないID用のロールをクリックします。
例えば、IDプール名が「TestAppName」なら「Cognito_TestAppNameUnauth_Role」をクリックします。
「インラインポリシーの追加」ボタンをクリックします。
「JSON」タブをクリックし、下記を入力します。
入力後、「Review policy」ボタンをクリックします。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:AttachPrincipalPolicy",
        "iot:CreateKeysAndCertificate"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
```

任意のポリシーの名前を入力して、「Create policy」ボタンをクリックします。
IAMの設定は完了です。

### AWS IoTの設定
続いて、[AWS IoT Console](https://console.aws.amazon.com/iot/home)にアクセスします。
サイドパネルの「安全性」の中にある「ポリシー」をクリックします。
「作成」ボタンをクリックします。
次の画面で任意のポリシーの名前を入力します。<font color ="red">このポリシー名をアプリ内に設定するので、メモしておきます。</font>
「アドバンストモード」をクリックし、下記を入力します。
「作成」ボタンをクリックします。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Subscribe",
        "iot:Receive"
      ],
      "Resource": "*"
    }
  ]
}
```

続いて、サイドパネルの「設定」ボタンをクリックします。
<font color ="red">アプリ内に設定するので、エンドポイントをメモしておきます。</font>

AWS IoTの設定は完了です。

### Androidアプリの設定
最後に、Androidアプリに通信に必要な項目を設定していきます。

CUSTOMER_SPECIFIC_ENDPOINTに、「AWS IoTの設定」でメモしておいたエンドポイントを設定します。

```java:PubSubActivity.java
// AWS Iot CLI describe-endpoint call returns: XXXXXXXXXX.iot.<region>.amazonaws.com
private static final String CUSTOMER_SPECIFIC_ENDPOINT = "CHANGE_ME";
```

COGNITO_POOL_IDに、「Cognitoの設定」でメモしておいたプールIDを設定します。

```java:PubSubActivity.java
// AWS IoT permissions.
private static final String COGNITO_POOL_ID = "CHANGE_ME";
```

AWS_IOT_POLICY_NAMEに、「AWS IoTの設定」でメモしておいたポリシー名を設定します。

```java:PubSubActivity.java
// Name of the AWS IoT policy to attach to a newly created certificate
private static final String AWS_IOT_POLICY_NAME = "CHANGE_ME";
```

MY_REGIONに、リージョンを設定します。
エンドポイントがXXXXXXXXXX.iot.ap-northeast-1.amazonaws.comなら
「Regions.AP-NORTHEAST_1」を設定します。

```java:PubSubActivity.java
// Region of AWS IoT
private static final Regions MY_REGION = Regions.US_EAST_1;
```

下記のKeyStoreファイルの名前やパスワード、証明書IDは任意でOKです。

```java:PubSubActivity.java
// Filename of KeyStore file on the filesystem
private static final String KEYSTORE_NAME = "iot_keystore";
// Password for the private key in the KeyStore
private static final String KEYSTORE_PASSWORD = "password";
// Certificate and key aliases in the KeyStore
private static final String CERTIFICATE_ID = "default";
```

以上で設定はすべて完了です。

## 実機確認
証明書の設定が成功すると、「Connect」ボタンをクリックできるようになります。
「Connect」ボタンをクリックして、StatusがConnecting…→Connectedになることを確認します。
適当なトピック名を入力し、Subscribeしている状態にします。
さらに任意のメッセージをPublishし、Last Messageが更新されるのを確認します。
![hello_pubsub.png](https://qiita-image-store.s3.amazonaws.com/0/305999/2329ca5f-1c9e-c706-36d7-dd82970d4023.png)

以上です。