ExpoのImagePickerをAndroid向けにビルドする際に必要なパーミッションについて

# 本記事の簡単なまとめ
[create-react-native-app](https://github.com/react-community/create-react-native-app)と[expo](https://expo.io/)でカメラアプリを作成する際に[ImagePicker](https://docs.expo.io/versions/latest/sdk/imagepicker/)を使用した場合、Android向けにbuildする際にはapp.jsonに以下のpermissionを指定してやる必要がある。

```app.json
{
  "expo": {
    ・・・,
    "android": {
      ・・・,
      "permissions": [
        "CAMERA",
        "READ_EXTERNAL_STORAGE",
        "WRITE_EXTERNAL_STORAGE"
      ]
    }
  }
}
```

# 問題点

Expoの公式ページのようにImagePickerを使用する際にはまずソースコード上にpermissionを取得するための処理が必要です。
askAsync関数は必要なpermissionがない場合にユーザーに確認ダイアログを表示し、permissionを取得する関数ですが、そもそもandroid側でアプリに許可していないと確認ダイアログすら出ませんでした。
expoアプリを用いてデバッグする際にはexpoアプリ自体に設定されているので大丈夫ですが、自作アプリだと設定する必要があります。

- サンプルコード

```App.js
import * as React from 'react';
import { Button, Image, View } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import Constants from 'expo-constants';
import * as Permissions from 'expo-permissions';

export default class ImagePickerExample extends React.Component {
  state = {
    image: null,
  };

  render() {
    let { image } = this.state;

    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Button
          title="Run camera"
          onPress={this._takePhoto }
        />
        {image &&
          <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />}
      </View>
    );
  }

  _takePhoto = async () => {
    let res = await Permissions.askAsync(Permissions.CAMERA_ROLL)
    if (res.status !== 'granted') {
      console.log('require camera permission')
      return
    }
    res = await Permissions.askAsync(Permissions.CAMERA)
    if (res.status !== 'granted') {
      console.log('require camera permission')
      return
    }

    let result = await ImagePicker.launchCameraAsync({
      allowsEditing: false,
    });

    if (!result.cancelled) {
      this.setState({ image: result.uri });
    }
  }
}
```

- 確認ダイアログの例

※通常は以下のようなダイアログがでますが、Androidアプリとして自作アプリをビルドするとpermissionがなければ確認すらされず、拒否されます。

![tmp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152318/1294470e-fc1c-edce-50be-b0f9205a8c2e.png)


# 対処方法

expo build:androidコマンドでビルドする際に使用するapp.jsonファイルにandroidで使用するpermissionを指定してやります。
私のためした感じですと以下の3つは必須でした。

- app.jsonの全容

```app.json
{
  "expo": {
    "name": "test",
    "slug": "test-app",
    "privacy": "public",
    "sdkVersion": "35.0.0",
    "platforms": [
      "ios",
      "android",
      "web"
    ],
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "updates": {
      "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "package": "com.example",
      "versionCode": 1,
      "permissions": [
        "CAMERA",
        "READ_EXTERNAL_STORAGE",
        "WRITE_EXTERNAL_STORAGE"
      ]
    }
  }
}
```

# ※参考: Androidアプリのビルド方法

```bash
cd /path/to/your/app
expo build:android -t app-bundle
# expoのURLからaabファイルをダウンロード
# aabファイルをGoogle Play Consoleからアップロード
```

# 所感

サンプルサイトだけだと引っかからないのでバグだと気付きづらいのと、ImagePickerの場合どのpermissionを指定してやればいいかがわかりにくかったです。
また、expoアプリを使用した場合は正常に動作するので、確認が大変でした。