startActivityForResult / requestPermissions が deprecated になる話

# はじめに
影響範囲が大きそうなのでまとめておきます。
※本記事は Medium にも投稿済みです。 Qiita の方が見る人が多そうなのでこちらにも投稿しました。

`androidx.fragment:fragment-ktx:1.3.0-alpha04` から、以下のメソッドが `deprecated` になります。

- startActivityForResult
- onActivityResult
- requestPermissions
- onRequestPermissionsResult

[公式ソース](https://developer.android.com/jetpack/androidx/releases/fragment#1.3.0-alpha04)

代わりに 新しく追加された Activity Result APIs を使えとのアナウンスがあります。

[Getting a result from an activity](https://developer.android.com/training/basics/intents/result)

> While the underlying startActivityForResult() and onActivityResult() APIs are available on the Activity class on all API levels, it is strongly recommended to use the Activity Result APIs introduced in AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02.

どうやら、`ActivityResultContract` の 「`registerForActivityResult`」 を使えば良いようです。
そこで、`registerForActivityResult` の使い方をまとめました。

# リポジトリ
以下にサンプルリポジトリがあります。
[https://github.com/nanaten/Activity-Contract-Example](https://github.com/nanaten/Activity-Contract-Example)
# 使い方

## build.gradle
build.gradleに以下を追加します。

```groovy:build.gradle
implementation "androidx.activity:activity-ktx:1.2.0-alpha04"
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha03"
```

## シンプルな使い方
シンプルにActivityの結果だけを受け取りたい場合は以下のように書けます。

```kotlin:MainActivity
// 普段どおりIntentを生成する
val intent = Intent(this, SecondActivity::class.java)

// registerForActivityResult を定義する
val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
    // 呼び出し先のActivityを閉じた時に呼び出されるコールバック
    if(result.resultCode == Activity.RESULT_OK) {
        // RESULT_OK時の処理
        val intent = result.data
        intent?.getStringExtra("result_key")
    }
}

// Activityを起動
launcher.launch(intent)
```

`registerForActivityResult` に `ActivityResultContracts.StartActivityForResult()` を引数として渡すことによって、 `ActivityResult` 型の結果をコールバックで受け取ることが出来ます。

`ActivityResult` には resultCode ( `onActivityResult` の resultCode と同様)と、 data (Intent) というプロパティがあるので、そちらから結果を処理します。

呼び出し先のActivityは以前と変わりません。

```kotlin:SecondActivity
intent.putExtra("result_key", "result")
// 結果をセット
setResult(Actiivty.RESULT_OK, intent)
finish()
```

## カスタムの ActivityResultContracts
`ActivityResultContracts` を継承した class を定義することで、カスタムの `ActivityResultContracts` を利用することもできます。

### ActivityResultContracts 継承クラスの作成

今回はobjectで定義しました。

```kotlin:ResultContracts
object ResultContracts: ActivityResultContract<Int, String?>() {
    // Activity起動時の処理
    override fun createIntent(context: Context, input: Int): Intent {
        val intent = Intent(context, SecondActivity::class.java)
        intent.putExtra("select_id", input)
        return intent
    }

    // 結果受け取り時の処理
    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        if(resultCode == Activity.RESULT_OK) {
            return intent?.getStringExtra("result_key")
        }
        return null
    }
}
```

class に `ActivityResultContract<{Inputの型}, {Outputの型}>` の形で継承させます。

作成したクラスには、Activityを起動する時に呼ばれる `createIntent()` とActivityから結果を受け取る時に呼ばれる `parseResult()` をoverrideする必要があります。

`createIntent()` には `registerForActivityResult.launch()` が呼び出された時の処理を、 `parseResult()` には Activityから結果を受け取った時の処理を書きます。

あとは、作成した class を `registerForActivityResult` に渡してあげます。

```kotlin
val launcher = registerForActivityResult(ResultContracts) { result ->
    // 受け取った結果を処理する
    text_view.text = result 
}

launcher.launch(0) // InputにInt型を定義したのでInt型の引数を渡す
```

### どんな時に使う？

上記ではInputとOutputで定義した型をそのまま渡しているため恩恵はありませんが、Activityから受け取った値を加工してから渡したい場合などに便利です。

```kotlin
object ResultContracts: ActivityResultContract<Int, User?>() { // Outputの型をUser型に修正
    // Activity起動時の処理
    ...

    // 結果受け取り時の処理
    override fun parseResult(resultCode: Int, intent: Intent?): User? {
        if(resultCode == Activity.RESULT_OK) {
            // Activityから文字列を受け取る
            val text = intent?.getStringExtra("result_key")
            // 受け取った値を独自クラスに詰め替えて返す
            val user = User(name = text)
            return user
        }
        return null
    }
}
```

```kotlin
val launcher = registerForActivityResult(ResultContracts) { result: User? ->
    // OutputをUser?型で定義したのでUser?型が返ってくる
    text_view.text = result?.name
}

launcher.launch(0) // Inputは変えていないのでInt型のまま
```

# requestPermissions
requestPermissions も ActivityResultContracts を利用して代替可能です。
`ActivityResultContracts.RequestPermission` という関数が用意されているので、 `registerForActivityResult` に引数として渡してあげます。

```kotlin
val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
    if(result) {
        // リクエスト許可時の処理
        Toast.makeText(this, "Permission Accepted.", Toast.LENGTH_SHORT).show()
    }
    else {
        // リクエスト拒否時の処理
        Toast.makeText(this, "Permission Denied.", Toast.LENGTH_SHORT).show()
    }
}

// アクセスしたい権限を引数に渡す
requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
```

コールバックの引数(result)はBoolean型です。権限が許可された場合 true が、拒否された場合は false が渡されてきます。

AndroidManifest.xmlに要求する権限を記載する必要があるのはいつも通りです。

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

# 注意点
すたぜろさんの[こちらの記事](https://medium.com/@star_zero/activityresultcontract%E3%81%AE%E6%B3%A8%E6%84%8F-a5a408c15f50)の注意点は `registerForActivityResult` の場合にも当てはまると思われます。 `prepareCall` -> `registerForActivityResult` に置き換えて読んでください。

（このあたりは自分で検証出来てません。すみません）

# おわりに
Activityへの値の受け渡しは長い間 `startActivityForResult` が使われてきましたが、ここにきて大きな変更が加わりました。

これから新規作成するアプリは、将来的に `registerForActivityResult` へ置き換えることを念頭に入れて作る必要があると思われます。

本記事を書くにあたり、以下の記事を参考にさせて頂きました。

- [ActivityResultContractを使ったActivity間のデータ受け渡し](https://medium.com/@star_zero/activityresultcontract%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Factivity%E9%96%93%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97-17052bfafde0)
- [ActivityResultContractの注意](https://medium.com/@star_zero/activityresultcontract%E3%81%AE%E6%B3%A8%E6%84%8F-a5a408c15f50)