In response to the company's project requirements, we adapted to Android 7.0. For us programmers, adapting to 7.0 mainly involves converting the Uri of local files on the phone. Note the red font, which means that for files starting with http, etc., files not stored in the phone are not required. Uri.parse("package") is also not required!!!! The adaptation of 7.0 is to protect the private file path in the mobile phone storage. When the system finds that you have taken away a URI through intent, and the address is a local file, it will be restricted. For other principles, please search other articles, which are omitted here. Below are the usage steps and my tool class. 1. You need to modify the AndroidManifest.xml file of the current module, add the provider tag, and map the path. - <android:supportsRtl= "true" >
-
- <provider
- android: name = "android.support.v4.content.FileProvider"
- android:authorities= "${applicationId}.myFileProvider"
- android:exported= "false"
- android:grantUriPermissions= "true" >
- <meta-data
- android: name = "android.support.FILE_PROVIDER_PATHS"
- android:resource= "@xml/path_file" />
The red part in the text is a fixed way of writing, which is what the official website says. Don't try it randomly. The value of the android:resource tag is the main/res/xml/path_file.xml file, that is, create a new xml folder under res, and then create a new xml file. 2. Create a new path_file.xml file under res/xml. The file name can be anything you want, but it should be the same as the previous step. - <?xml version= "1.0" encoding= "utf-8" ?>
- <resources>
- <paths>
- <!
- <files-path/> represents the root directory: Context.getFilesDir()
- <cache-path/> represents the root directory: getCacheDir()
- <external-path/> represents the root directory: Environment.getExternalStorageDirectory()
- The root directory represented by <external-files-path/>: Context.getExternalFilesDir(String) Context.getExternalFilesDir( null ).
- <external-cache-path /> represents the root directory: Context.getExternalCacheDir().
- <root-path /> represents the root directory of the device new File( "/" );
-
- <!
- <root-path name = "root" path= "" />
- <files-path name = "files" path= "" />
- <cache-path name = "cache" path= "" />
- <external-path name = "external" path= "" />
- <external-files-path name = "external_files" path= "" />
- <external-cache-path name = "external_cache" path= "" />
- </paths>
- </resources>
There are 6 path tags in total, and their meanings are all written. You can select the corresponding tags according to your needs. For example, in the <external-path/> tag, when the path="" is inside, this tag maps to the root directory of the external SD card. The name attribute is useless and is used to confuse the virtual directory of third-party applications to cover up the real path of the file. 3. Tools are here. FileUriPermissionCompat.Java - /**
- * @Author: duke
- * @DateTime: 2017-06-06 14:43
- * @Description: Android 7.0 uri permission adaptation,
- * (When exposing URI or file to third-party apps through intent) Private directories are prohibited from access
- * Local path and net path have been adapted
- */
- public class FileUriPermissionCompat {
- private static final String TAG = FileUriPermissionCompat.class.getSimpleName();
-
- // TODO: This needs to be changed to the corresponding value
- //This needs to be changed to the corresponding provider authorities value applied in AndroidManifest.xml
- private static final String AUTHORITIES = "com.duke.personalkeeper.myFileProvider" ;
-
- /**
- * Whether to adapt to 7.0 permissions
- *
- * @return
- */
- public static boolean isNeedAdapt() {
- //24 and above versions
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
- }
-
- public static Uri adaptUriAndGrantPermission(Context context, Intent intent, File file) {
- Uri uri = adaptUri(context, file);
- if (uri == null ) {
- return null ;
- }
- grantUriPermission(context, intent, uri);
- return uri;
- }
-
- public static Uri adaptUri(Context context, File file) {
- if (context == null || file == null ) {
- return null ;
- }
- //Special processing of network paths, no need for 7.0 adaptation, but must use the parse() method
- if (file.getPath().startsWith( "http" )) {
- return Uri.parse(file.getPath());
- }
- Uri uri = null ;
- try {
- if (isNeedAdapt()) {
- //Requires 7.0 special adaptation
- //Create a Uri object of type content through the FileProvider class provided by the system
- uri = FileProvider.getUriForFile(context, AUTHORITIES, file);
- } else {
- //No need to adapt
- uri = Uri.fromFile(file);
- }
- } catch (Exception e) {
- Log.e(TAG, "authorities value error, so can't convert uri !" );
- e.printStackTrace();
- }
- return uri;
- }
-
- /**
- * Grant third-party applications the permission to read and write URIs
- *
- * @param context
- * @param intent
- * @param saveUri adapted uri
- */
- public static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
- if (!isNeedAdapt()) {
- return ;
- }
- if (context == null || intent == null || saveUri == null ) {
- return ;
- }
- //Special processing of network paths, no permissions required
- if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
- //No authorization required
- return ;
- }
- //1. Authorization (required for system album, camera, and cropping)
- List resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- if (TextUtils.isEmpty(packageName)) {
- continue ;
- }
- context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
- //2. Authorization (required when installing apk)
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- public static void revokeUriPermission(Context context, Intent intent, Uri saveUri) {
- if (!isNeedAdapt()) {
- return ;
- }
- if (context == null || intent == null || saveUri == null ) {
- return ;
- }
- //Special processing of network paths, no permissions required
- if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
- //No authorization required
- return ;
- }
- try {
- context.revokeUriPermission(saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
Core code: - uri = FileProvider.getUriForFile(context, AUTHORITIES, file);
file is the file address you want to expose to other applications, for example, you want to take a photo and save the result to a file. AUTHORITIES is the actual value of android:authorities="${applicationId}.myFileProvider" in the first step above. ${applicationId} is the applicationid value of the defaultConfig tag in app/build.gradle. Use the static method of the FileProvider class provided by the system to convert the file address to a special uri starting with content://. If you don't convert it and use Uri.fromFile(file) directly, you will get a uri like file:///xxxxx. That's the difference. After converting the URI, authorization is still required: - /**
- * Grant third-party applications the permission to read and write URIs
- *
- * @param context
- * @param intent
- * @param saveUri adapted uri
- */
- public static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
- if (!isNeedAdapt()) {
- return ;
- }
- if (context == null || intent == null || saveUri == null ) {
- return ;
- }
- //Special processing of network paths, no permissions required
- if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
- //No authorization required
- return ;
- }
- //1. Authorization (required for system album, camera, and cropping)
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- if (TextUtils.isEmpty(packageName)) {
- continue ;
- }
- context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
- //2. Authorization (required when installing apk)
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
There are two parts of authorization method. After many rounds of testing, it is found that it is better to use them at the same time. The first method, the for loop, is because sometimes you are not sure what the package name of the application you need to share is, so find all possible third-party applications and authorize them all. Later testing found that when installing the apk, the above authorization alone was not enough, and it was necessary to add the intent.addFlag method to authorize again. Finally, it is important to note that: 1. Pay attention to the version judgment of 7.0. 2. Remember that the 7.0 permissions may be needed to read and write SD cards, which requires the 6.0 permissions to read and write SD cards. When you fail to test the 7.0 permissions, consider whether the 6.0 permissions are in place. There is nothing else to say. That’s all. |