An overview of the

Recently I read nanChen’s ImagePicker ImagePicker, I feel it is very good, and I plan to write down what I have learned from it. In many cases, it’s good to come across a good framework that reduces development costs. But also understand the internal implementation logic, so that one day you need to implement a similar small feature, you can write it out quickly if you know the principle, rather than introducing the whole framework.

This article covers the first of these functions: how to turn on the camera of the phone to take a picture?

System existing camera applications

Here’s a quick look at how to call the system’s existing applications. To invoke an existing application in a developed application, specify the Action and Category of the application using the Intent. Then start the specified Activity with startActivity(Intent) or startActivityForResult(Intent, int). If the startActivityForResult() function is enabled and a value is returned, override onActivityResult(int, int, Intent).

Let’s take a look at the Activity defined in the androidmanifest.xml manifest file of the system’s existing camera application:

        <activity
            android:name="com.android.camera.Camera"
            android:clearTaskOnLaunch="true"
            android:configChanges="orientation|keyboardHidden"
            android:screenOrientation="landscape"
            android:taskAffinity="android.task.camera"
            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <categroy android:name="android.intent.category.DEFAULT" />
                <categroy android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.media.action.IMAGE_CAPTURE" />
                <categroy android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
                <categroy android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.android.camera.VideoCamera"
            android:clearTaskOnLaunch="true"
            android:configChanges="origientation|keyboardHidden"
            android:label="@string/video_camera_label"
            android:screenOrientation="landscape"
            android:taskAffinity="android.task.camcorder"
            android:theme="@android:style/theme.Black.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="android.media.action.VIDEO_CAMERA" />
                <categroy android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.media.action.VIDEO_CAPTURE" />
                <categroy android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>    
Copy the code

It defines two Activity, com. Android. Camera. The camera camera, com. Android. Camera. The camera VideoCamera. To capture the data returned by the system camera, use the following two actions to start the camera and camera:

  • Android. Media. Action. IMAGE_CAPTURE: Intent action types, request a picture from the existing camera application.
  • Android. Media. Action. VIDEO_CAPTURE: Intent action types, from the current request a video camera applications.

ACTION_IMAGE_CAPTURE (camera) and mediastore.action_video_capture (camera) are static constants defined in the MediaStore class.

Get the picture taken by the system’s existing camera

In a newly started Activity, if you want to retrieve its return value, start the Activity using the startActivityForResult(Intent,int) method. Override onActivityResult(int, int, Intent) to retrieve the data returned by the system camera, so we just need to retrieve the value in onActivityResult().

Photos taken by the system camera, if not specified, are stored in the system default folder, which can be retrieved using intent.getextra (), which returns a Uri address that represents the address of a content provider. EXTRA_OUTPUT (intent.getextra ()) is an empty address if mediastore.extra_output is specified, but there’s no need to worry about finding it.

If 7.0 or higher, however, you need to use the FileProvider to get this URI address.

Implementation scheme

Now that the flow is clear, here is the code implementation:

The first is to declare photo permissions under Androidmanifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
Copy the code

After declaring the permission, we still need to determine whether the user has given us the permission to take photos before we start taking photos:

if (( mActivity).checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
  ActivityCompat.requestPermissions(mActivity, new String[]{Manifest.permission.CAMERA}, GalleryActivity.REQUEST_PERMISSION_CAMERA);
} else {
  imagePicker.takePicture(mActivity, GalleryActivity.REQUEST_CODE_TAKE);
}
Copy the code

If the user does not grant permission, then the user needs to apply for permission. After the permission is granted, a callback will notify the developer whether the permission is granted. For details, see the code delivered:

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSION_CAMERA) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { imagePicker.takePicture(this, REQUEST_CODE_TAKE); } else {showToast(" permissions forbidden, camera cannot open "); }}}Copy the code

After permission is granted, use imagePicker. TakePicture to take photos. Let’s look at the specific code logic for taking photos:

/ / public void takePicture(Activity Activity, int requestCode) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePictureIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (takePictureIntent.resolveActivity(activity.getPackageManager()) ! = null) { if (Utils.existSDCard()) takeImageFile = new File(Environment.getExternalStorageDirectory(), "/DCIM/camera/"); else takeImageFile = Environment.getDataDirectory(); takeImageFile = createFile(takeImageFile, "IMG_", ".jpg"); if (takeImageFile ! = null) {intent.putextra (mediastore. EXTRA_OUTPUT, uri); // The camera has its own default storage path, and photos taken will return a thumbnail. If you want to access the original image, // dat extra can be used to obtain the original image location. If the destination URI is specified, data has no data, and if no URI is specified, data returns data! Uri uri; if (VERSION.SDK_INT <= VERSION_CODES.M) { uri = Uri.fromFile(takeImageFile); } else {/** * Should be replaced with FileProvider * and this can solve the beautiful MIUI system returns the size of 0 * / uri = FileProvider getUriForFile (activity, "com.example.myapplication.provider", takeImageFile); List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resInfoList) { String packageName = resolveInfo.activityInfo.packageName; activity.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); } } // Log.e("nanchen", ProviderUtil.getFileProviderName(activity)); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } } activity.startActivityForResult(takePictureIntent, requestCode); }Copy the code

Intent.resolveactivity (intent.resolveActivity) does not work on all Android devices when you invoke third-party software or system activities, such as opening the camera or sending images. This method determines whether the activity corresponding to the intent exists to ensure that it does not crash.

TakeImageFile is an image storage address. Prior to 7.0, it was processed with uri.fromfile. 7.0 or later uses FileProvider. The following describes how to use FileProvider:

registered

To use a FileProvider, you need to declare it in androidmanifest.xml:

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapplication.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
        </provider> 
Copy the code

Here is the FileProvider from the V4 package we use directly. We can also inherit the FileProvider class directly and override the overloaded functions appropriately, but this is not recommended. Here are some of the Settings above:

  • name: If the provider of the name of the class, use the default v4 FileProvider can use “android. Support. The v4. Content. FileProvider”, You can also set it to a custom Provider class that inherits FileProvider.
  • Authorities: a signing authentication that can be customized but needs to be consistent in obtaining urIs;
  • GrantUriPermissions: Using FileProvider requires us to grant temporary access (READ and WRITE) to the outgoing URIs. This setting allows us to exercise this power;
  • Meta -data: Meta -data configures the path configuration information of accessible files. It needs to be configured using an XML file. The FileProvider obtains the configuration item by parsing the XML file. Android.support. FILE_PROVIDER_PATHS, resource is the configuration item for configuring path information.

The path configuration

Accessible path configuration You can create an XML file in res and create a configuration file in the following format:

<? The XML version = "1.0" encoding = "utf-8"? > < paths > <! --path: path to which temporary authorization is required (. Represents all paths) --> <! --> <external-path name=" CAM "path="."Copy the code

Here’s an explanation:

  • **
    ** represents the root directory: context.getfilesdir ()
  • < external path / > represents the root directory of the: Environment. External.getexternalstoragedirectory ()
  • represents the root directory: getCacheDir()

Finally, place the URI in mediastore. EXTRA_OUTPUT.

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
Copy the code

OnActivityResult is called after the photo has been taken, where we can display the image to ImageView based on the prequel value:

@override protected void onActivityResult(int requestCode, int resultCode, Intent data) {Log. resultCode=" + resultCode + " " + requestCode); if (requestCode == REQUEST_CODE_TAKE) { Uri uri = Uri.fromFile(takeImageFile); iv_CameraImg.setImageURI(uri); }}Copy the code

At this point, the process of calling the system camera to take a picture is over. \