Using a Camera Intent

The next step is to actually take the picture. This is the easy part: You get to use an implicit intent again.

Start by stashing the location of the photo file. You will use it a few more times, so this will save a bit of work.

Listing 16.10  Grabbing the photo file location (CrimeFragment.kt)

class CrimeFragment : Fragment(), DatePickerFragment.Callbacks {

    private lateinit var crime: Crime
    private lateinit var photoFile: File
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        crimeDetailViewModel.crimeLiveData.observe(
            viewLifecycleOwner,
            Observer { crime ->
                crime?.let {
                    this.crime = crime
                    photoFile = crimeDetailViewModel.getPhotoFile(crime)
                    updateUI()
                }
            })
    }
    ...
}

Next you will hook up the camera button to actually take the picture. The camera intent is defined in MediaStore, Android’s lord and master of all things media related. You will send an intent with an action of MediaStore.ACTION_IMAGE_CAPTURE, and Android will fire up a camera activity and take a picture for you.

But hold that thought for one minute.

Firing the intent

Now you are ready to fire the camera intent. The action you want is called ACTION_IMAGE_CAPTURE, and it is defined in the MediaStore class. MediaStore defines the public interfaces used in Android for interacting with common media – images, videos, and music. This includes the image capture intent, which fires up the camera.

By default, ACTION_IMAGE_CAPTURE will dutifully fire up the camera application and take a picture, but it will not be a full-resolution picture. Instead, it will take a small-resolution thumbnail picture and stick it inside the Intent object returned in onActivityResult(…).

For a full-resolution output, you need to tell it where to save the image on the filesystem. This can be done by passing a Uri pointing to where you want to save the file in MediaStore.EXTRA_OUTPUT. This Uri will point to a location serviced by FileProvider.

First, create a new property for the photo URI and initialize it after you have a reference to the photoFile.

Listing 16.11  Adding a photo URI property (CrimeFragment.kt)

class CrimeFragment : Fragment(), DatePickerFragment.Callbacks {

    private lateinit var crime: Crime
    private lateinit var photoFile: File
    private lateinit var photoUri: Uri
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        crimeDetailViewModel.crimeLiveData.observe(
            viewLifecycleOwner,
            Observer { crime ->
                crime?.let {
                    this.crime = crime
                    photoFile = crimeDetailViewModel.getPhotoFile(crime)
                    photoUri = FileProvider.getUriForFile(requireActivity(),
                        "com.bignerdranch.android.criminalintent.fileprovider",
                        photoFile)
                    updateUI()
                }
            })
    }
    ...
}

Calling FileProvider.getUriForFile(…) translates your local file path into a Uri the camera app can see. The function takes in your activity, provider authority, and photo file to create the URI that points to the file. The authority string you pass to FileProvider.getUriForFile(…) must match the authority string you defined in the manifest (Listing 16.4).

Next, write an implicit intent to ask for a new picture to be taken into the location saved in photoUri (Listing 16.12). Add code to ensure that the button is disabled if there is no camera app or if there is no location to save the photo to. (To determine whether there is a camera app available, you will query PackageManager for activities that respond to your camera implicit intent, as discussed in the section called Checking for responding activities in Chapter 15.)

Listing 16.12  Firing a camera intent (CrimeFragment.kt)

private const val REQUEST_CONTACT = 1
private const val REQUEST_PHOTO = 2
private const val DATE_FORMAT = "EEE, MMM, dd"

class CrimeFragment : Fragment(), DatePickerFragment.Callbacks {
    ...
    override fun onStart() {
        ...
        suspectButton.apply {
            ...
        }

        photoButton.apply {
            val packageManager: PackageManager = requireActivity().packageManager

            val captureImage = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            val resolvedActivity: ResolveInfo? =
                packageManager.resolveActivity(captureImage,
                        PackageManager.MATCH_DEFAULT_ONLY)
            if (resolvedActivity == null) {
                isEnabled = false
            }

            setOnClickListener {
                captureImage.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)

                val cameraActivities: List<ResolveInfo> =
                    packageManager.queryIntentActivities(captureImage,
                            PackageManager.MATCH_DEFAULT_ONLY)

                for (cameraActivity in cameraActivities) {
                    requireActivity().grantUriPermission(
                        cameraActivity.activityInfo.packageName,
                        photoUri,
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                }

                startActivityForResult(captureImage, REQUEST_PHOTO)
            }
        }

        return view
    }
    ...
}

To actually write to photoUri, you need to grant the camera app permission. To do this, you grant the Intent.FLAG_GRANT_WRITE_URI_PERMISSION flag to every activity your cameraImage intent can resolve to. That grants them all a write permission specifically for this one Uri. Adding the android:grantUriPermissions attribute in your provider declaration was necessary to open this bit of functionality. Later, you will revoke this permission to close up that gap in your armor again.

Run CriminalIntent and press the camera button to run your camera app (Figure 16.2).

Figure 16.2  [Insert your camera app here]

[Insert your camera app here]
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.161.77