2年間放置されていた Android アプリを targetSdkVersion 21 から targetSdkVersion 26 にする

# はじめに
2年間ぐらい放置されていたアプリを更新することになったのですが、ご存知の通り、

> [アプリのアップデートについては 2018 年 11 月 1 日から、アプリの対象を Android 8.0（API レベル 26）以降に設定することが必要](https://developer.android.com/distribute/best-practices/develop/target-sdk)

となってます。

更新しようとしたアプリの targetSdkVersion を確認すると `Android 5.0(API レベル 21)` …

仕方がないので必要な作業を確認しながら targetSdkVersion 26 にあげていきます。


# 環境
macOS High Sierra - 10.13.6
Android Studio - 3.3.2

# やること
アプリで利用しているパーミッションやクラスによるかと思いますが、対象のアプリでは以下を対応することにしました。

- Android Studio 3.3.2 でビルドできるようにする
    - 2年前の Android Studio からバージョンもだいぶ上がってると思うので、まずはビルドできるようにするところから。

- パーミッション（今回は android.permission.CAMERA）が必要な箇所の改修
    - [Android 6.0（API レベル 23）からパーミッションの付与の仕方が変わった](https://developer.android.com/training/permissions/requesting?hl=ja) のでその対応。

- FileProvider への変更
    - カメラ起動するときの外部ファイルの指定の仕方を FileProvider を使ったものに変更します。

- GCM → FCM対応
    - いまだに GCM を使っていたので FCM に切り替えます。

- NotificationChannel 対応
    - Android 8.0(API レベル 26) で Notification Channel 対応しないとプッシュ通知が届かなくなるので対応します。

# Android Studio 3.3.2 でビルドできるようにする

## Android Studio の言う通りにする

「gradleのバージョンあげますか？」 って聞いてきてくれるので言う通りにします。

##compile → implementation にする

<img width="566" alt="スクリーンショット 2019-04-04 20.23.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/34926/a7ab3ef3-e2d7-f9d0-c414-cd3faa9ae558.png">


## AndroidManifest.xml から android:minSdkVersion、android:targetSdkVersion 削除

（Android Studio が 「消していいですか？」 って聞いてくれた気がします。）
<img width="348" alt="スクリーンショット 2019-04-04 20.12.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/34926/25f41a7f-8494-72d1-9e55-b94bb25de1fa.png">

## FloatMath を (float)Math にする

<img width="363" alt="スクリーンショット 2019-04-04 20.13.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/34926/d5342891-0367-bf31-988b-d32fb5405e3b.png">

## レイアウトファイルの id の指定の変更

<img width="315" alt="スクリーンショット 2019-04-04 20.20.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/34926/50b517b9-05ff-48ca-215c-ebd3e980aa6e.png">
<img width="584" alt="スクリーンショット 2019-04-04 20.21.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/34926/2e5a581f-b3b5-b698-68b6-a668d5d82c9f.png">

以上でビルドできるようになりました。

# パーミッションが必要な箇所の改修

## AndroidManifest.xml に permission 追加

```xml
<uses-permission android:name="android.permission.CAMERA" />
```

## パーミッションが必要なところにパーミッション確認処理と、確認から戻ってきたときの処理を追加

```java
public static final int PERMISSION_REQUEST_CAMERA = 201;

private void startCamera() {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
        return;
    }
    imageFilePath = CameraHelper.startCameraAndGetImageFilePath(this, REQUEST_CODE_CAMERA_CAPTURE);
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    if (requestCode == PERMISSION_REQUEST_EXTERNAL_STORAGE) {
        showCameraOrGalleryDialog();
    }
    if (requestCode == PERMISSION_REQUEST_CAMERA) {    // ← 追加
        startCamera();
    }
}
```

以上でパーミッションの対応は完了です。

# FileProvider への変更

## provider の paths を定義した xmlファイル を作成

```xml:provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths> 
```

## AndroidManifest.xml に provider を定義

```xml
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>
```

## Uri の取得方法を変更

```diff
-            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
+            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", imageFile));
```

以上で FileProvider の対応は完了です。

# GCM → FCM対応

[Android Push通知 GCMからFCMに移行 #Android #Firebase](https://qiita.com/flatfisher/items/31f5c9e21ae56cccff8d)
コチラを参考にさせていただきました！

対応内容はほぼ同じなので変えたところだけ書いておきます。

## firebase-messaging を 10.2.1 にする

targetSdkVersion 26 にすると、最低でも `10.2.1` にします。
（それにあわせて他のライブラリのバージョンも `10.2.1` にします。）

```xml
implementation 'com.google.android.gms:play-services-analytics:10.2.1'
implementation 'com.google.firebase:firebase-messaging:10.2.1'
```

## message.getData() を Bundle に put する

onMessageReceived() の引数が `Bundle` ではなくなったので生成してあげます。

```java
@Override
public void onMessageReceived(RemoteMessage message) {
    Map<String, String> data = message.getData();

    Bundle bundle = new Bundle();
    for (Map.Entry<String, String> entry : data.entrySet()) {
        bundle.putString(entry.getKey(), entry.getValue());
    }

    PushMessageHandler.handleMessage(bundle);
}
```

以上で FCM対応 は完了です。

# NotificationChannel 対応

[Android Oでプッシュ通知が届かない時](https://qiita.com/mm36/items/0d356e814ef3da54c9da)
コチラを参考にさせていただきました！

特に変えたところもないので書くことはありません！

# 思ったこと

一気にバージョンが上がるとやることが多くて大変でした…
2019年から毎年対応しなくてはならなくなったので、時間をみつけてこまめにアップデートしていきたいです。
（放置しているアプリなんて消してしまえ！って思うのですが大人な事情もありますよね。。）

# 参考

- 必要な作業の確認
    - [2018年に要求されるtargetSdkVersion（対象APIレベル）を解説します](https://feel-log.net/android/require_target_sdk_version_2018/)

- GCM → FCM対応
    - [Android Push通知 GCMからFCMに移行 #Android #Firebase](https://qiita.com/flatfisher/items/31f5c9e21ae56cccff8d)

- NotificationChannel 対応
    - [Android Oでプッシュ通知が届かない時](https://qiita.com/mm36/items/0d356e814ef3da54c9da)