preface

Adaptation is one of the essential tasks of the foreground programmer and can take a lot of time and effort.

What is a front-end programmer is the user-facing end, including front-end, mobile, PC, and so on.

What is adaptation, adaptation is when our development environment, operating environment changes, the program can still run stably.

And the adaptation of the most difficult for programmers is Android, in addition to the development environment, operating environment and other factors, because of the Android open source, but also to adapt to the major manufacturers.

The number of conditions is often a headache for Android programmers.

Take a look at the camera, photo album related adaptation process:

  • Android 6 Permission adaptation
  • Android 7 file adaptation
  • Android 10/11 storage adapter

Ok, let’s take a quick example of changing your avatar.

The sample

Click on your avatar, then pop up, give you different options, do different things.

mBinding.llImg.setOnClickListener {
    TakeImageDialog {
        when (it) {
            TakeImageDialog.ALBUM -> { 
                openAlbum()
            }
            TakeImageDialog.CAMERA -> { 
                checkPermission()
            }
        }
    }.show(supportFragmentManager, "TakeImageDialog")}Copy the code

Define some parameter variables that will be used later:

    // The location where the camera saves the photo
    private lateinit var photoUri: Uri
    
    companion object {
        private const val REQUEST_CODE_PERMISSIONS = 1000 / / permission
        private const val REQUEST_CODE_ALBUM = 1001 / / photo album
        private const val REQUEST_CODE_CAMERA = 1002 / / camera
    }
Copy the code

Open the photo album

Choose picture

    private fun openAlbum(a) {
        val intent = Intent()
        intent.type = "image/*"
        intent.action = "android.intent.action.GET_CONTENT"
        intent.addCategory("android.intent.category.OPENABLE")
        startActivityForResult(intent, REQUEST_CODE_ALBUM)
    }
Copy the code

Fixed way of writing it.

Since it’s startActivityForResult, take a look at the onActivityResult callback

The callback

override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == RESULT_OK) {
        when (requestCode) {
            REQUEST_CODE_ALBUM -> {
                doCrop(data? .data!!) }... }}}Copy the code

In the case of requestCode being REQUEST_CODE_ALBUM:

doCrop(data? .data!!)
Copy the code

data? .data!! That is, select the Uri returned by the image, which can be used directly. Here is the next step, clipping

clipping

    private fun doCrop(sourceUri: Uri) {
        Intrinsics.checkParameterIsNotNull(sourceUri, "Resources are empty")
        UCrop.of(sourceUri, getDestinationUri())// The current resource saves the target location
            .withAspectRatio(1f.1f)/ / aspect ratio
            .withMaxResultSize(500.500)/ wide/high
            .start(this)}Copy the code

For convenience, a three-party library UCrop is used here, which is easy to use.

GetDestinationUri () is the target location where the current resource is clipped to save

    private fun getDestinationUri(a): Uri {
        val fileName = String.format("fr_crop_%s.jpg", System.currentTimeMillis())
        val cropFile = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName)
        return Uri.fromFile(cropFile)
    }
Copy the code

The UCrop callback is also in onActivityResult

override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == RESULT_OK) {
        when (requestCode) {
            REQUEST_CODE_ALBUM -> {
                doCrop(data? .data!!)
            }
            UCrop.REQUEST_CROP -> {
                val resultUri: Uri = UCrop.getOutput(data!!) !!!!!val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(resultUri))
                // todo
            }
            UCrop.RESULT_ERROR -> {
                val error: Throwable = UCrop.getError(data!!) !!!!! ToastUtil.show("Picture cropping failed" + error.message)
            }
        }
    }
}
Copy the code

UCrop.getOutput(data!!) !!!!! Is the returned Uri, which can be manipulated directly or converted into a bitmap.

Ok, here to open the album to introduce the end.

Let’s get to the point. Turn on the camera.

Author: yechaoa

Open the camera

Opening the camera is a little more complicated.

permissions

The first step is not to turn it on, but to check whether you have camera permissions, which is mandatory on some phones like Huawei.

  • Config file add:
<uses-permission android:name="android.permission.CAMERA" />
Copy the code
  • Code:
    private fun checkPermission(a) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
            openCamera()
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CODE_PERMISSIONS)
        }
    }
Copy the code
  • The callback:
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                openCamera()
            } else {
                ToastUtil.show("Refusal will result in the inability to use the camera.")}}}Copy the code

The openCamera method is just open the camera.

Before opening fit

    private fun openCamera(a) {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        photoUri = getDestinationUri()
        photoUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // Apply Android 7.0 file permissions to create a Uri of the content type through the FileProvider
            FileProvider.getUriForFile(this."$packageName.fileProvider", File(photoUri.path!!))
        } else {
            getDestinationUri()
        }
        // After Android11 force partition storage, external resources can not access, so add an output save location, and then value operation
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
        startActivityForResult(intent, REQUEST_CODE_CAMERA)
    }
Copy the code
  • Adaptation:
FileProvider.getUriForFile(this."$packageName.fileProvider", File(photoUri.path!!) )Copy the code

7.0 or above, use fileProvider to share files.

  • Adapter 2:
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
Copy the code

Since Android 11 forces partition storage, external resources cannot be accessed, so add an output save location, photoUri, and then select the value

The callback

override fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == RESULT_OK) {
        when (requestCode) {
            REQUEST_CODE_ALBUM -> {
                doCrop(data? .data!!)
            }
            REQUEST_CODE_CAMERA -> {
                // Take the value from the saved location
                doCrop(photoUri)
            }
            UCrop.REQUEST_CROP -> {
                val resultUri: Uri = UCrop.getOutput(data!!) !!!!!val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(resultUri))
                // todo
            }
            UCrop.RESULT_ERROR -> {
                val error: Throwable = UCrop.getError(data!!) !!!!! ToastUtil.show("Picture cropping failed" + error.message)
            }
        }
    }
Copy the code

And notice, instead of evaluating it from data like an album, we’re evaluating it from where we saved it.

The rear clipping is the same process as the album.

conclusion

The biggest change to this feature point is partition storage, which Android 10 May be able to do, but Android 11 will enforce partition storage.

The application can access its own partition and perform read/write operations without requiring read/write permissions. After uninstallation, the partition files are deleted accordingly, so there is no need to put cached files in the folder of competing products.

Below Android 10, you still need read and write permissions, and you can still run amok.

Get your own partition address:

getExternalFilesDir(Environment.DIRECTORY_PICTURES)
Copy the code

Corresponding Address:

file:/ / / storage/emulated / 0 / Android/data/files/Pictures/package name
Copy the code

File starts with sandbox files and Content starts with shared files.

What if I have access to other files, such as albums, music, that still need to read and write permission, and have to access through the MediaStore API, can specifically view the document.

The last

Writing is hard. If it works for you, give it a thumbs up

Android 11 Development Manual

Android 11 Developer Manual

reference

  • Official Camera documentation
  • Official Permissions Document
  • Official Storage documents