Adaptation of Android 10 and Android 11

Posted by saltious on Mon, 20 Dec 2021 07:38:59 +0100

background

Recently, when I started to adapt Android 10 and Android 11 in the project, I encountered many pits. Previously, there were adaptations specially written for qq and wechat sharing. However, some new problems have been encountered during the adaptation work on the service side. Write it down for future reference. I hope it can help relevant students who encounter this problem.

1, Resource access under private directory

There is such a scenario: we want to share a picture to qq or wechat. The first step is to get the bitmap (generated locally or loaded through the network), then store it on the local sd card, and finally transfer the absolute path of the stored picture to qq or wechat.

In the above scenario, these key points are involved:

  • Store pictures on sd card
  • Pass the absolute path to qq or wechat

1.1 directly access the root directory of sd card

It is completed through FileOutPutStream, and there is no problem below Android 10. The path is as follows:

/storage/emulated/0/demo/sharePicture/1637048769163_share.jpg

However, on Android 10 and above, there will be errors:

java.io.FileNotFoundException: /storage/emulated/0/demo/sharePicture/1637048769163_share.jpg: open failed: EACCES (Permission denied)
//In fact, the storage permission is agreed

This is because we are limited by the storage partition and cannot directly access the external directory. Therefore, we need to modify the app specific directory whose storage path is scope.

1.2 change to app specific private directory

You do not need permission to access the directory yourself. If you need permission for third-party access! Therefore, we can temporarily authorize it through FileProvider later. If you are not familiar with FileProvider, please refer to the article at the beginning of this article.

/storage/emulated/0/Android/data/com.demo.test/files

When you store pictures through FileOutPutStream, it is successful.

private fun saveImage(bitmap: Bitmap, storePath: String, filePath: String): Boolean {
        val appDir = File(storePath)
        if (!appDir.exists()) {
            appDir.mkdirs()
        }
        val file = File(filePath)
        if (file.exists()) {
            file.delete()
        }
        var fos: FileOutputStream? = null
        try {
            fos = FileOutputStream(file)
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
            fos.flush()
            return true
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } finally {
            fos?.close()
        }
        return false
    }

After testing, sharing qq and wechat have been successful under 29 and 29 devices.

1.3 summary of sharing principles

The essence of sharing is to give the picture path to qq or wechat so that they can access our pictures. The partition was previously stored on an external sd card, and there was no problem.

After partitioning, qq or wechat cannot access our private directory app specific. Therefore, we need to convert the fileprovider into content: / / format to share, and temporarily authorize qq or wechat to access our pictures.

qq adapts the fileprovider internally, so we only need to pass in the absolute path file: / / format, while wechat needs to receive the content: / / format, so we need to convert it externally.

For specific adaptation logic, refer to the article at the beginning of the article~

2, Resource access under public directory

Google suggests that we use mediaStore or SAF to access. The pictures in the public directory on Android 10 cannot be accessed through file: / / format, indicating that the path cannot be found. Such as glide loading, image selection library, clipping frame and so on will be affected.

However, there is a pit: not on Android 10, but on Android 11!! Why?

Because Google has changed back, Android 11 supports the file: / / format.... (wtf? I thank you ~ ~)

**By Android 10 and android 11, I mean targetSdkVersion**

2.1 insert a picture into the public directory

Only via mediaStore:

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DESCRIPTION, "This is an image");
values.put(MediaStore.Images.Media.DISPLAY_NAME, "Image.png");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.TITLE, "Image.png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/test");

Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver resolver = context.getContentResolver();
//You can get this insertUri here
Uri insertUri = resolver.insert(external, values);
LogUtil.log("insertUri: " + insertUri);

OutputStream os = null;
try {
    if (insertUri != null) {
        os = resolver.openOutputStream(insertUri);
    }
    if (os != null) {
        final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);
        // write what you want
    }
} catch (IOException e) {
    LogUtil.log("fail: " + e.getCause());
} finally {
    try {
        if (os != null) {
            os.close();
        }
    } catch (IOException e) {
        LogUtil.log("fail in close: " + e.getCause());
    }
}

2.2 content uri to file format path

public static String getFilePathFromContentUri(Uri selectedVideoUri,
                                                  ContentResolver contentResolver) {
       String filePath;
       String[] filePathColumn = {MediaStore.MediaColumns.DATA};

       Cursor cursor = contentResolver.query(selectedVideoUri, filePathColumn, null, null, null);
       cursor.moveToFirst();

       int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
       filePath = cursor.getString(columnIndex);
       cursor.close();
       return filePath;
   }

2.3 obtain the file format path according to the picture name

String imageName="test";

Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver resolver = BaseApp.getContext().getContentResolver();
String selection = MediaStore.Images.Media.TITLE + "=?";
String[] args = new String[] {imageName};
String[] projection = new String[] {MediaStore.Images.Media._ID};
Cursor cursor = resolver.query(external, projection, selection, args, null);
// Here we get the uri in content format 
Uri imageUri = null;
//content://media/external/images/media/318952
if (cursor != null && cursor.moveToFirst()) {
    imageUri = ContentUris.withAppendedId(external, cursor.getLong(0));
    cursor.close();
}

After you get the absolute path, you can normally access glide, qq sharing and third-party image selection framework on Android 11.

3, Ultimate fit

  • On Android 10

Enable flag bit: android:requestLegacyExternalStorage="true" to enable compatibility mode and turn off partition adaptation. It is equivalent to running in the old mode when targetSdkVersion=29. There is no problem at all. Perfectly avoid the pit where you can't access the public directory!!!

  • On Android 11

The above signs will automatically become invalid. Therefore, the things stored in the application are still placed in the app specific directory. The shared private directory can be adapted through fileprovider. To share the public directory, because the File api supports direct access to the public directory, you can directly convert the content format to file format. For details, please refer to the second part of the article.

Finally, I would like to ask two questions:

1. targetSdk=30, android:requestLegacyExternalStorage="false" what happens when running on an Android 10 device?

A: you will definitely encounter permission problems. Because the Android 10 device still runs in the Android 10 compatible mode. So change it to true.

2. targetSdk=30, android:requestLegacyExternalStorage="false" what happens when running on an Android 11 device?

A: if you follow the above normal adaptation, there must be no problem at all!

The above is my own adaptation experience. It is inevitable that there are omissions. If there are problems or better suggestions in the article, please comment and correct~

This article is transferred from https://juejin.cn/post/7032525748686553095 , in case of infringement, please contact to delete.

Topics: Android