Androidアプリ内で画像保存を行う方法(MediaStore)


## はじめに
この記事では、アンドロイドアプリ内で画像保存をするためのストレージの権限の確認を行い、MediaStoreを使って画像保存機能を実装する方法について説明します。
使用した言語はKotlinです。開発環境はWindows10,仕様したアプリケーションはAndroid Studio Flamingoです。

## 手順
### 1.外部ストレージの書き込み権限の確認
Androidでのメディアファイルは、通常「共有ストレージ」と呼ばれる場所に保存されます。共有ストレージに保存されているデータはアプリ間で共有できます。しかし、共有ストレージに保存されたデータはセキュリティ上の懸念があるため、アクセスには制限が設けられています。

共有ストレージに保存されているデータは、AndroidのMediaStore APIによってデータベースとして管理されています。MediaStore APIを使用することで、アプリは共有ストレージ上のメディアファイルにアクセスできます。ただし、このアクセスにはユーザーの許可が必要です。これは、悪意を持つアプリが端末内のメディアファイルに不正にアクセスできないようにするためのセキュリティ機能です。

MediaStore APIを使用する際には、ユーザーの許可を取得する仕組みが実行時に必要とされ、これによってユーザーのセキュリティとプライバシーが保護されます。

以上が外部ストレージの書き込み権限の確認する理由です。

```kotlin:
private fun checkPermissionAndSaveImage() {
    // 外部ストレージの書き込み権限がアプリに対して既に付与されているかを確認
    if (ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED
    ) {
        // 書き込み権限が付与されていない場合はリクエストを行う
        ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
            REQUEST_WRITE_EXTERNAL_STORAGE
        )
    } else {
        // 権限が既に付与されている場合は画像を保存
        saveImage()
     }
}
```

### 2.外部ストレージ書き込み権限リクエストの結果を受け取る
onRequestPermissionsResult()関数はユーザーが権限リクエストに対して反応した際に、その結果に応じた処理を行うために使用されます。このようにして、ユーザーからの権限の取得や拒否に対する適切な対応をアプリが行えるようになります。
```kotlin: 
/**
 * @param requestCode リクエストコード
 * @param permissions リクエストされた権限のリスト
 * @param grantResults 権限の結果を示す配列
 */
override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    // 外部ストレージ書き込み権限が許可された場合、画像の保存を行う
    if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            saveImage()
        } else {
            // 外部ストレージ書き込み権限が拒否された場合、ダイアログを表示させる
            showPermissionRequestDialog()
        }
    }
}
```

### 3.権限のリクエストのダイアログを表示する
外部ストレージへの書き込み権限が拒否された場合、画像を保存することはできません。画像を保存するためには、ユーザー自身が外部ストレージへの書き込み権限を許可する必要があります。ユーザーに手間をかけさせず、外部ストレージへの書き込み権限が必要であることを伝えるために、以下の手順を取ります。

ダイアログの表示:
ユーザーが画像を保存しようとしたとき、外部ストレージへの書き込み権限がない場合、ダイアログを使ってメッセージを表示します。

「設定を開く」 オプション:
ダイアログには、ユーザーが設定アプリに移動するためのオプション 「設定を開く」 も含まれています。ユーザーはこのオプションを選択することで、アプリ情報の設定画面に移動することができます。

この方法により、ユーザーは外部ストレージへの書き込み権限を簡単に設定でき、アプリからの画像保存がスムーズに行えるようになります。

```kotlin:
private fun showPermissionRequestDialog() {
    val dialog = AlertDialog.Builder(this)
        .setTitle("権限が必要です")
        .setMessage("画像を保存するためにはストレージへのアクセス許可が必要です。設定画面でアクセス許可を設定してください。")
        .setPositiveButton("設定を開く") { _, _ ->
             val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
             val uri = Uri.fromParts("package", packageName, null)
             intent.data = uri
             startActivity(intent)
        }
        .setNegativeButton("キャンセル") { dialog, _ ->
            dialog.dismiss()
        }
        .create()

    dialog.show()
}
```
画面上にはこのように表示されます。
<img width="600" alt="gazouhozon-2.png" 
src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477154/09b50d2a-673f-9dc2-3a42-197387f4ea35.png
">

### 4.画像保存処理の実装
saveImageメソッド内で、MediaStore画像保存の具体的な処理を実装します。

```kotlin:
private fun saveImage() {
    // 新しい画像パスを設定
    val PhotoPath = "/path/to/your/new/photo.jpg"

    // 画像のBitmapを取得
    val imageBitmap = ImageUtil.getImage(this, PhotoPath)

    // 保存に必要なインスタンスを生成
    val contentResolver = contentResolver
    val values = ContentValues().apply {
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
    }
    val imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

    // 取得したBitmapを外部ストレージに保存
    contentResolver.openOutputStream(imageUri)?.use { outputStream ->
        imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
        outputStream.flush()
    } 
}

```
コード中の「保存に必要なインスタンスを生成」について説明します。
contentResolverのインスタンスを生成し、アプリ内でデータベースや他のアプリのコンテンツにアクセスするためのインターフェースを提供します。

ContentValuesのインスタンスを生成し、データベースに挿入するデータを格納します。この場合、画像のMIMEタイプを指定しています。

contentResolverを使用して、指定したURI（MediaStore.Images.Media.EXTERNAL_CONTENT_URI）にContentValuesに格納されたデータを挿入します。挿入操作の結果、新しい画像のURIが取得されます。このURIを使用して、後で画像を読み書きすることができます。

この部分は画像の保存準備を行い、保存された画像にアクセスするためのURIを取得するためのものです。

## まとめ
この記事では、アンドロイドアプリ内で画像保存をするためのディレクトリの権限の確認を行い、MediaStoreを使って画像保存機能を実装する方法について説明しました。

参考資料
ダイアログを表示させる方法
https://developer.android.com/guide/topics/ui/dialogs?hl=ja
MediaStoreを使った画像保存の方法
https://developer.android.com/training/data-storage/shared/media?hl=ja#add-item