FlutterでAndroidのリリース用APKをビルドする前の準備いろいろ

そろそろリリースビルドが近づいてきたので、いろいろ準備をしました。
その覚え書きです。

iOS版は開発者アカウントの承認が進んでいないのでいったん保留です(汗)

# 環境など

|ツールなど|バージョンなど|
|:--|:--|
| MacBook Air Early2015|macOS Mojave 10.14.5|
|Android Studio |3.6.1|
|Java |1.8.0_131|
|Flutter|1.12.13+hotfix.5|
|Dart |2.7.0|

Flutter SDKは、Mojaveで動かせる最終版を使用しているため、最新版ではありません

# アプリの表示名の変更

## 1.Android

### (1)build.gradleの変更
debugビルドとreleaseビルドでアプリ名を変えたかったので、`app/build.gradle`にいつものようにこうしました。

```app/build.gradle
    buildTypes {
        debug {
            resValue "string", "app_name", "(d)アプリ名"
            applicationIdSuffix ".debug"
            versionNameSuffix "-d"
        }
        release {
            resValue "string", "app_name", "アプリ名"
        }
    }
```

※`build.gradle`は、`android`直下と、`android/app`下とに、二つありますのでご注意を。

`applicationIdSuffix`を付けてパッケージ名を変えたり、アプリ名を変えたりしているのは、debugビルド版とreleaseビルド版のアプリを端末に同居させたいことがあるからです。
パッケージ名が違うと、同一端末内に同時にインストール可能になりますが、そうなるとアプリ名で判断が付かなくなるので、変えているというわけです。

アプリ内課金などがあると使えない手なのですが、今回は関係ないのでこれでOK。

### (2)マニフェストファイルを変更

`android/app/src/main`下にある、`AndroidManifest.xml`を変更します。

```AndroidManifest.xml
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher">
```

`android:label="@string/app_name"`の箇所が変更箇所です。
これで、`build.gradle`で指定したリソース文字列を代入してくれます。


## 2.iOS

info.plistを直接書き変えます。

```info.plist
	<key>CFBundleName</key>
	<string>アプリ名</string>
```


# アプリアイコンの作成

## 1.素材の用意
私にとって最難関でした(笑)
というかもともと自分でデザインなど出来るわけがないので、最初からフリー素材にターゲットを絞って検索していました。

なんとなく、

- カレンダー風
- メモ帳風
- 食べ物系
- 豚
- 柴犬(黒柴)

というイメージで探していました。柴犬はまあ冗談ですがw

カレンダー風とメモ帳風は、カレンダー形式の記録アプリだからです。
食べ物系は、食事を思い起こさせる何かだと良いなと思って。
豚は、「豚になっちゃアカンから」という意味で(笑)
最後のは、黒柴が大好きなので、今後他に出そうと思ってるアプリでも使う感じでブランドアイコン的になるならと思ってましたが、なかなかなかったです。残念。

で、食事系に落ち着きました。
なかったら最悪お金を払っても、と思ってましたが、しっくりくるものが見つかりました。

ということで、こちらから素材をお借りしました。
http://flat-icon-design.com/

## 2.画像の調整

アイコンにするのには色々と調整が必要です。特にAndorid8.0以降のAdaptiveIconに対応させるために、余白などに気をつけなければなりません。

まず、背景と前景を分離します。
その上で、こちらのサイトで、前景が思った位置に納まるように微調整を繰り返しました。

https://adapticon.mariusclaret.com/

画像の調整には、[GIMP](https://www.gimp.org/)を使いましたが、結構時間かかりました(汗)

## 3.アプリアイコンリソースの生成

最後に、これらを解像度毎に吐き出す必要があるのですが、これがなかなか厄介です。
で、Flutterにはとても便利なパッケージを作って下さっている方がいました！

[flutter_launcher_icons](https://pub.dev/packages/flutter_launcher_icons)パッケージです。

```yaml:pubspec.yaml
dev_dependencies:
  flutter_test:
    sdk: flutter

  # icon generate
  flutter_launcher_icons: ^0.7.5

flutter_icons:
  ios: true
  android: true
  image_path: "assets/icon/icon.png"
  adaptive_icon_background: "assets/icon/ic_background.png"
  adaptive_icon_foreground: "assets/icon/ic_foreground.png"
```

上記のように記述して(インデントなど注意)、

```console
$ flutter pub get
$ flutter pub run flutter_launcher_icons:main
```

とすれば生成されます。
生成されたリソースファイルは、それぞれ以下のフォルダで見つけられます。

- `android/app/src/main/res`
- `ios/Runner/Assets.xcassets/AppIcon.appiconset`

# Apkのビルド

App Bundleにしろと言われますが、多言語対応など特にする予定は無いし、解像度別のリソースもアプリアイコンくらいなので、やりません。Apkでいきます。

## 1.アップロード鍵の用意
ビルドの前に、アップロード鍵を作って署名する準備をします。
割愛しようかと思ったけど、Androidを普通にビルドしたことが無い方もご覧になるかも知れないので、載せておきます。

※アップロード鍵での署名方法は、従来のアプリ署名方法と変わりません。
ここで署名した内容が、最初にPlayストアにApkなどを登録した際に、アップロード鍵として保管されます。

### (1)証明書を作成

`jks`ファイルを作成して`keystore.properties`を作成して・・・
Androiderにはおなじみのやつです。

今回は、コンソールでやってみました。

```console
$ keytool -genkey -v -keystore release.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
キーストアのパスワードを入力してください:  (パスワード)
新規パスワードを再入力してください: (再入力)
姓名は何ですか。(氏名または部署名などを入力)
組織単位名は何ですか。(会社等ならCOとか。個人なら何も入れずにリターンでOK)
組織名は何ですか。(会社名を入力。個人なら何も入れずにリターンでOK)
都市名または地域名は何ですか。(市区町村名。入れたくなければ何も入れずにリターンでOK)
都道府県名または州名は何ですか。(都道府県名。これぐらいは入れましょう)
この単位に該当する2文字の国コードは何ですか。JP(必須です)
CN=sk, OU=Unknown, O=Unknown, L=Unknown, ST=Tokyo, C=JPでよろしいですか。
(良ければy,やり直したければnを入れてリターン)
10,000日間有効な2,048ビットのRSAの鍵ペアと自己署名型証明書(SHA256withRSA)を生成しています
	ディレクトリ名: CN=姓名, OU=Unknown, O=Unknown, L=Unknown, ST=Tokyo, C=JP
<alias_name>の鍵パスワードを入力してください
	(キーストアのパスワードと同じ場合はRETURNを押してください):  (リターンでOK)
```

`alias_name`には、任意の適切なエイリアス名を付けて下さい。
パスワードも忘れないようにメモしておいて下さい。

作成した`release.jks`は、`android/app`下に置きます。

### (2)署名情報ファイルを作成

`android`下に、`keystore.properties`を作成します。

```keystore.properties
storePassword=パスワード
keyPassword=パスワード
keyAlias=alias_name
storeFile=release.jks
```

### (3)build.gradleを編集

```app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

// 追加する
def keystorePropertiesFile = rootProject.file("keystore.properties") 

android{
   ...

    defaultConfig {
        ...
    }

    // 以下を追加
    signingConfigs {
        release {
            if (keystorePropertiesFile.exists()) {
                def keystoreProperties = new Properties()
                keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
                storeFile file(keystoreProperties['storeFile'])
                storePassword keystoreProperties['storePassword']
            }
        }
    }
    // 追加ここまで

    buildTypes {
        debug {
            ...
        }
        release {
            resValue "string", "app_name", "アプリ名"

            // 以下を追加
            signingConfig signingConfigs.release
            // 追加ここまで
        }
    }
 ...
}
```

### (4)gitignoreに署名ファイルなどを追加

privateリポジトリなら上げてしまっても良いかも知れませんが、後で公開したときに履歴で見えてしまうので、やっぱり上げない方が吉だとは思います。コードを公開する予定は一切無い！という場合にはいいですけど。あと社内のGitLabだとか、そういう場合にも上げてしまっても良いでしょうね。

```.gitignore
*.jks
keystore.properties
```

## 2. Apkをビルド

次のコマンドでビルドされます。

```console
$ flutter build apk
```

生成されたapkは、以下のフォルダで見つけられます。

- `build/app/outputs/apk/release`

# Codemagicに入れてみた

まず、[こちらの記事](https://qiita.com/kasa_le/items/c8d45dcfaad6989009ac)などを参考にワークフローが作成済みとします。

基本的にはこのページの通りです。
https://docs.codemagic.io/code-signing/android-code-signing/#setting-up-android-code-signing-on-codemagic

## 1.Codemagic用の署名設定をする

後段のところで設定できるのが見えていたので、これは不要になったかと思っていましたが、やっぱり必要みたいです。
1つに出来ないのかなあー。ねえ、Codemagicさん？
(でもFlutterがリリースビルド時に未署名のapkを作れないみたいなので、やむを得ないのかな・・・)

### (1)build.gradleを変更する

先ほど、リリース署名用の情報を設定しましたが、そこに**Codemagic用**の設定も入れてやります。

```app/build.gradle
    signingConfigs {
        release {
            if (System.getenv()["CI"]) { // CI=true is exported by Codemagic
                storeFile file(System.getenv()["FCI_BUILD_DIR"] + "/keystore.jks")
                storePassword System.getenv()["FCI_KEYSTORE_PASSWORD"]
                keyAlias System.getenv()["FCI_KEY_ALIAS"]
                keyPassword System.getenv()["FCI_KEY_PASSWORD"]
            } else if (keystorePropertiesFile.exists()) {
                ...
            }
        }
    }
```

### (2)証明書ファイルのBase64エンコード文字列を書き出す

```console
$ cd android/app
$ openssl base64 -in <infile> -out <outfile>
```

このファイルもコミットされないように注意してください。(後で使ったら削除して大丈夫)

以上でコミットしておきましょう。
pushでワークフローがトリガーされるようになっている人は、まだpushしない方が良いです。

## 2.ワークフローの変更
私はCodemagicではmasterへのPRで走るようにし、そもそもリリースビルドする用にすることが目的でした。なので既存のワークフローを変更しますが、developテスト用にも回すのを残しておきたい方は、ワークフローを複製してもう一つ用意するのでも良いかも知れませんね。

### (1)ビルドトリガーの追加

Githubでtagを作成したときに走るように設定します。

![trigger_on_tag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/02624ead-bf7a-20cd-50f5-31691bf20d3c.png)

### (2)セキュアな環境変数を追加する

**Environment variables**設定を開き、以下の内容を**Secure**として保存する。

```
FCI_KEYSTORE_PASSWORD=キーストアパスワード
FCI_KEY_ALIAS=エイリアス名
FCI_KEY_PASSWORD=鍵パスワード
FCI_KEYSTORE_FILE=Base64エンコードした署名ファイルの文字列
```

1つずつ、変数名を入れ、値をコピペし、**Secure**というチェックボックスにチェックを入れ、[Add]をクリックして追加していきます。

![add_secure_env.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/cdf6e643-8131-1c82-9110-7ff0be2f8a6e.png)

全部追加し終わったら、[Save]をクリックするのを忘れずに。

### (3)証明書ファイルの内容をdecodeするスクリプトを追加する

以下の内容を、スクリプト欄に貼り付けます。

```bash
#!/usr/bin/env sh
set -e # exit on first failed commandset
echo $FCI_KEYSTORE_FILE | base64 --decode > $FCI_BUILD_DIR/keystore.jks
```

これは**Build**タスクの前ならどこでも良いです。
例として、**Post-clone script**に入れています。

![post-clone_script.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/49e8b224-a287-0e1d-8107-8b31e548562d.png)

[Save]をクリックするのを忘れずに!

### (4)Buildモードを変更する

**Build**設定を開き、Modeを**release**にします。

![change_build_mode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/2c7e7054-cc97-a3ef-1c99-fcfdcd836d8e.png)

変更したら[Save]をクリックするのを忘れずに!!

### (5)Android code signingを設定する

こっちは多分ビルドには関係なくて、Playストアなどにアップロードする際に必要っぽいですが、一緒に設定してしまっておきます。

※ここの設定は、前段で設定した内容と明らかに重複していて冗長な作業なので、なんとか統一して貰いたいところです・・・

- **Publish**設定を開く

|![open_publish.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/7cc31c13-ffb1-2231-f5ef-640e9478fb7e.png)|
|:--|

- **Android code signing**設定を開く

- 証明書ファイル(`release.jks`など)をDnDでアップロード
  - アップロードすると、**Enable Android code signing**がチェックできるようになる
- パスワード、エイリアス情報を入力する
- **Enable Android code signing**にチェックを入れる

![upload_and_setting_key.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/c0b5206d-f32a-905a-880a-43b0e4d1c441.png)

- [Save]をクリック!!!

### (6)Github releasesの設定をする

- **Publish**設定を開く
- **Github releases**を開く

![github_releases.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/57d6ac22-dc02-e738-d133-c6bee31e9bbc.png)

- [Save]をクリック!!!!

## 3.ワークフローを走らせる

pushで動くようにしている人は、ここで変更をpushします。
PRやtag付けで動くようにしている人は、pushした上で、PRを作るかtag付けをします。

CodemagicからのSlack通知。

|![codemagic_pass.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/e5a507b8-f5ff-8ddf-c5f4-a89a19c442fd.png)|
|:--|

GithubのPR。

|![github_pr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/9afc62b5-956c-a81b-8f22-31b614504d09.png)|
|:--|

tag付けしたら、またワークフローが走って、GithubのReleaseページに追加されました！

|![github_release.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/4b19527e-0d3a-81f4-1b57-44c3978d5b90.png)|
|:--|





# 感想

[Save]をクリックってうるさいって？だって忘れるんだもん。しょっちゅう。
Codemagicの公式ドキュメントを読んでても、書いてあるよ、しょっちゅう。Saveクリックすんの忘れんな、って。
みんな忘れるんだね(笑・・・えない)

Playストアへのアップロードは次にでも。
Google開発者登録して10年。やっとアップロードするときが・・・

# おまけ：Firebaseのテスト

このままリリースするのもなんか怖いので、念のためFirebaseのTest Labでも回してみましょうか。
Test Labは、ロボテスト(ランダムUIテスト)をしてくれるツールです。
UnitTestを実行も可能ですが、これはネイティブ言語で書いたときだけなので、Flutterでは使えません。

## 1.Firebaseコンソールにログイン(or サインアップ)

Google、G Suiteのアカウントがあれば直ぐにログイン出来ます。

## 2.プロジェクトを作成

![firebase_add_project.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/24fb7d95-8bdd-c651-d9e8-84d46a0daeb2.png)

Google Analyticsを入れるか聞かれますが、まあ入れておいた方が良いでしょう。ただ、今はFirebaseSDKを入れないので、何も取れないと思いますが。
(クラッシュレポートも、いわゆる"NDK"で作られているので、Java/Kotlinのようにスタックトレースが分かりやすいってことも無いはず・・・)

GAのアカウントを用意してない場合は、ここで作るように言われると思います。

少し待つと、プロジェクトページに遷移します。
  
## 3.Androidアプリを登録

トップページで、ドロイド君をタップします。

![firebase_add_app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/2399c56f-181d-362f-9321-3645dc3eafa5.png)

アプリのパッケージ名と、表示用の名称(これは日本語可)を入れます。
デバッグ証明書のSHA-1は今は特に不要です。

![firebase_app_info.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/7eaa399b-b25a-6e4f-543b-91dcb8a3645f.png)

以下は今はスキップします。

- `play-services.json`のダウンロード
  - 必要なときにダウンロードできます
- 接続の確認
  - `play-services.json`を使ってSDKを初期化し、アプリを起動しないと行けないのですが、今はSDKを入れないのでパスします。

## 4.Test Labを実行する

左側のメニュー「品質」から、**Test Lab**を選びます

![testlab.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/6563cd86-1f34-19c4-e937-bae2f80bb7ff.png)

一番上の、[Android Rob...]となっているところにapkをDnDします。

![firebase_test_lab_start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/0516c11d-9d57-2c72-2119-7a617df0eb8d.png)

勝手に始まります。

![testlab_start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/48639a60-3fc6-a385-4d83-1c3a925778d2.png)

結構時間かかります。
メールか何か飛んでくるはずなので、待ちましょう。

終わってから、タップして詳細ページを見ると、こんなページが見られます。

|![firebase_test_done.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103886/65403628-d1f3-68d7-891e-3a64b0b93928.png)|
|:--|

やっぱり画面1個でNDKで描画ゴリゴリしてるんだな・・・
なんてことが分かりますね。

実際、Apkを**unzip**してみると、`classes.dex`は僅かなファイルサイズしか無くて、`lib`下に`.so`ファイルがいくつかあり、そちらが巨大になっています。

そっか、ホットリロードって、dllの再読み込みみたいなことしてるんかな・・・？

# 参考サイトなど

Flutterで初めてのリリースビルド
https://qiita.com/Renkon117/items/80d9775eb870416d69bf

How to encode base64 via command line?
https://superuser.com/a/120815