Summary of Android 7.0 Dynamic Permissions

Summary of Android 7.0 Dynamic Permissions

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.

  1. <android:supportsRtl= "true" >
  2.   
  3. <provider
  4. android: name = "android.support.v4.content.FileProvider"  
  5. android:authorities= "${applicationId}.myFileProvider"  
  6. android:exported= "false"  
  7. android:grantUriPermissions= "true" >
  8. <meta-data
  9. android: name = "android.support.FILE_PROVIDER_PATHS"  
  10. 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.

  1. <?xml version= "1.0" encoding= "utf-8" ?>
  2. <resources>
  3. <paths>
  4. <! --  
  5. <files-path/> represents the root directory: Context.getFilesDir()
  6. <cache-path/> represents the root directory: getCacheDir()
  7. <external-path/> represents the root directory: Environment.getExternalStorageDirectory()
  8. The root directory represented by <external-files-path/>: Context.getExternalFilesDir(String) Context.getExternalFilesDir( null ).
  9. <external-cache-path /> represents the root directory: Context.getExternalCacheDir().
  10. <root-path /> represents the root directory of the device new File( "/" );
  11. -->  
  12. <! -- path="" represents the root directory, you can also specify a specific directory, name="camera_picture" is the virtual directory camera_picture -->  
  13. <root-path name = "root" path= "" />
  14. <files-path name = "files" path= "" />
  15. <cache-path name = "cache" path= "" />
  16. <external-path name = "external" path= "" />
  17. <external-files-path name = "external_files" path= "" />
  18. <external-cache-path name = "external_cache" path= "" />
  19. </paths>
  20. </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

  1. /**
  2. * @Author: duke
  3. * @DateTime: 2017-06-06 14:43
  4. * @Description: Android 7.0 uri permission adaptation,
  5. * (When exposing URI or file to third-party apps through intent) Private directories are prohibited from access
  6. * Local path and net path have been adapted
  7. */
  8. public class FileUriPermissionCompat {
  9. private static final String TAG = FileUriPermissionCompat.class.getSimpleName();
  10.   
  11. // TODO: This needs to be changed to the corresponding value
  12. //This needs to be changed to the corresponding provider authorities value applied in AndroidManifest.xml
  13. private static final String AUTHORITIES = "com.duke.personalkeeper.myFileProvider" ;
  14.   
  15. /**
  16. * Whether to adapt to 7.0 permissions
  17. *
  18. * @return  
  19. */
  20. public   static boolean isNeedAdapt() {
  21. //24 and above versions
  22. return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
  23. }
  24.   
  25. public   static Uri adaptUriAndGrantPermission(Context context, Intent intent, File file) {
  26. Uri uri = adaptUri(context, file);
  27. if (uri == null ) {
  28. return   null ;
  29. }
  30. grantUriPermission(context, intent, uri);
  31. return uri;
  32. }
  33.   
  34. public   static Uri adaptUri(Context context, File file) {
  35. if (context == null || file == null ) {
  36. return   null ;
  37. }
  38. //Special processing of network paths, no need for 7.0 adaptation, but must use the parse() method
  39. if (file.getPath().startsWith( "http" )) {
  40. return Uri.parse(file.getPath());
  41. }
  42. Uri uri = null ;
  43. try {
  44. if (isNeedAdapt()) {
  45. //Requires 7.0 special adaptation
  46. //Create a Uri object of type content through the FileProvider class provided by the system
  47. uri = FileProvider.getUriForFile(context, AUTHORITIES, file);
  48. } else {
  49. //No need to adapt
  50. uri = Uri.fromFile(file);
  51. }
  52. } catch (Exception e) {
  53. Log.e(TAG, "authorities value error, so can't convert uri !" );
  54. e.printStackTrace();
  55. }
  56. return uri;
  57. }
  58.   
  59. /**
  60. * Grant third-party applications the permission to read and write URIs
  61. *
  62. * @param context
  63. * @param intent
  64. * @param saveUri adapted uri
  65. */
  66. public   static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
  67. if (!isNeedAdapt()) {
  68. return ;
  69. }
  70. if (context == null || intent == null || saveUri == null ) {
  71. return ;
  72. }
  73. //Special processing of network paths, no permissions required
  74. if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
  75. //No authorization required
  76. return ;
  77. }
  78. //1. Authorization (required for system album, camera, and cropping) -- This writing method needs to be analyzed  
  79. List resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
  80. for (ResolveInfo resolveInfo : resInfoList) {
  81. String packageName = resolveInfo.activityInfo.packageName;
  82. if (TextUtils.isEmpty(packageName)) {
  83. continue ;
  84. }
  85. context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  86. }
  87. //2. Authorization (required when installing apk)
  88. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  89. }
  90.   
  91. public   static void revokeUriPermission(Context context, Intent intent, Uri saveUri) {
  92. if (!isNeedAdapt()) {
  93. return ;
  94. }
  95. if (context == null || intent == null || saveUri == null ) {
  96. return ;
  97. }
  98. //Special processing of network paths, no permissions required
  99. if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
  100. //No authorization required
  101. return ;
  102. }
  103. try {
  104. context.revokeUriPermission(saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  105. } catch (Exception e) {
  106. e.printStackTrace();
  107. }
  108. }
  109. }

Core code:

  1. 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:

  1. /**
  2. * Grant third-party applications the permission to read and write URIs
  3. *
  4. * @param context
  5. * @param intent
  6. * @param saveUri adapted uri
  7. */
  8. public   static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
  9. if (!isNeedAdapt()) {
  10. return ;
  11. }
  12. if (context == null || intent == null || saveUri == null ) {
  13. return ;
  14. }
  15. //Special processing of network paths, no permissions required
  16. if (saveUri.getScheme() != null && saveUri.getScheme().startsWith( "http" )) {
  17. //No authorization required
  18. return ;
  19. }
  20. //1. Authorization (required for system album, camera, and cropping) -- This writing method needs to be analyzed  
  21. List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
  22. for (ResolveInfo resolveInfo : resInfoList) {
  23. String packageName = resolveInfo.activityInfo.packageName;
  24. if (TextUtils.isEmpty(packageName)) {
  25. continue ;
  26. }
  27. context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  28. }
  29. //2. Authorization (required when installing apk)
  30. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  31. }

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.

<<:  A complete guide to Android unit testing, making code testing a once-and-for-all

>>:  Tech Neo Technology Salon - Issue 14 - IT Operation and Maintenance Practice and Exploration Based on Algorithms

Recommend

How much does it cost to customize a watch mini program in Handan?

How much does it cost to customize the Handan wat...

Ginkgo biloba: a living fossil that can be seen and touched

Autumn is the most beautiful season of the year f...

Do you believe in lasers?

Produced by: Science Popularization China Author:...

Use the pyramid principle to operate your product well!

During the Spring Festival, I used my free time t...

Facebook Ads FAQ and Ad Creation Process

1. Troubleshooting (1) My ad has been under revie...

The changes of HTML tag input in iOS 15 are shocking

[[440196]] A long time ago, probably a few years ...

No idea for APP promotion? One map helps you get all channels! (Super complete)

Everyone working in the Internet industry knows t...

Yoo Video Product Analysis: How does Yoo Video compare to Tik Tok?

In the short video war, Douyin and Kuaishou took ...