2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Androidでアプリ連携して画像を取得する(その2)

Last updated at Posted at 2020-07-18

はじめに

前回は、端末やカメラアプリから画像を取得して表示する簡単なアプリを作成しました。

しかし、カメラアプリで新規に写真撮影した場合の解像度が低いという不満がありました。

そこで今回は、カメラアプリに新規撮影を指示してフルサイズの画像を受け取る方法を模索してみます。

フルサイズの画像取得方法

前回の考察にも書いた通り、今回は MediaStore#ACTION_IMAGE_CAPTUREEXTRA_OUTPUT を指定する方法を用いることにします。

注意事項

Android 7.0 (API level 24) 以降は EXTRA_OUTPUT に渡す Uri の作成に Uri.fromFile(File) が使えないので、FileProvider を使用します。

For more recent apps targeting Android 7.0 (API level 24) and higher, passing a file:// URI across a package boundary causes a FileUriExposedException. Therefore, we now present a more generic way of storing images using a FileProvider.

https://developer.android.com/training/camera/photobasics より引用

機能仕様

前回とほぼ同様です。

画面仕様

ボタンを1つだけにしました。※前回はこちら

  • GET IMAGE ボタン
    • 端末から画像コンテンツを取得するアプリもしくはカメラアプリを選択する chooser を起動する。
    • カメラが使えない場合はカメラアプリは chooser から除外される。

実装

◆ AndroidManifest.xml

追加部分のみ抜粋

AndroidManifest.xml
<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application ...>
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.objectfanatics.ex_camera.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
        </provider>
    </application>
</manifest>

◆ file_path.xml

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

◆ activity_main.xml

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:gravity="center"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintBottom_toTopOf="@id/get_image_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/get_image_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:text="get image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

◆ MainActivity

MainActivity
package com.objectfanatics.ex_camera

import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val launcher = registerForActivityResult(PickImageOrTakePicture(this, "画像を取得"), this::onImagePick)
        findViewById<View>(R.id.get_image_button).setOnClickListener { launcher.launch(null) }
    }

    private fun onImagePick(uri: Uri?) {
        findViewById<ImageView>(R.id.image_view).setImageURI(uri ?: return)
    }
}

◆ ActivityResultContract の独自実装

PickImageOrTakePicture
package com.objectfanatics.ex_camera

import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_PICK
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.content.FileProvider
import java.io.File
import java.io.IOException

class PickImageOrTakePicture(private val context: Context, private val chooserTitle: String) : ActivityResultContract<Void?, Uri?>() {
    private var imageUri: Uri? = null

    override fun createIntent(context: Context, input: Void?): Intent =
        Intent.createChooser(pickerIntent, chooserTitle).apply {
            val uri = context.createImageUri().apply { imageUri = this }
            if (context.hasCameraFeature && uri != null) {
                putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(takePictureIntent(uri)))
            }
        }

    override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
        val data = intent?.data
        return when {
            resultCode != RESULT_OK -> null
            data == null            -> imageUri
            else                    -> intent.data
        }
    }

    private fun Context.createImageUri(): Uri? {
        return try {
            val dir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES) ?: return null
            val file = File.createTempFile("IMG", ".jpg", dir)
            FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", file)
        } catch (e: IOException) {
            null
        }
    }

    companion object {
        private val Context.hasCameraFeature: Boolean
            get() = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)

        private val pickerIntent = Intent().apply {
            action = ACTION_PICK
            type = "image/*"
        }

        private fun takePictureIntent(input: Uri): Intent =
            Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply { putExtra(MediaStore.EXTRA_OUTPUT, input) }
    }
}

実行

◆ 初期状態

◆ Chooser 起動

◆ カメラアプリ起動

シャッターを切る前

◆ 撮影直後

シャッターを切った直後

◆ Gallery アプリ

◆ 画像選択後

今回やったこと

フルサイズの写真をカメラアプリから取得しました。

おわりに

それでは、また。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?