Android 10(Android Q) Adaptation tips

Refer to the website

Official website – Sandbox storage

Privacy in Android Q – Significant privacy changes

Official website – displays time sensitive notifications

1. The device can read hardware information

In Android10, the system does not allow common apps to request android.permission.READ_PHONE_STATE permission, so new apps need to cancel the application for dynamic permission.

The current way to get a unique device ID is to use SSAID, or if it is empty, use uuID.randomuuid ().toString() to get a random ID and store it. This ID is guaranteed to be unique, but will change after the App is uninstalled and reinstalled

SSAID can be obtained as follows:

String id = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
Copy the code

2. Background App startup restrictions

In Android10, when the App does not have an Activity displayed in the foreground, its Activity will be blocked by the system, resulting in invalid startup.

Personally, I think the official purpose of doing this is to prevent users from being forced to interrupt by other apps in the process of using the alarm clock, but this is not very friendly to apps with call function.

The official compromise is to use a full-screen Intent(full-screen Intent) to set the Intent when creating a notification bar. The sample code is as follows (modified based on the official document):

Intent fullScreenIntent = new Intent(this, CallActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
        fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);

NotificationCompat.Builder notificationBuilder =
        new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("Incoming call")
    .setContentText("(919) 555-1234") 3 rows. / / the following as the key setPriority (NotificationCompat. PRIORITY_HIGH). SetCategory (NotificationCompat. CATEGORY_CALL) .setFullScreenIntent(fullScreenPendingIntent,true);
    
NotificationManager notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notifyManager.notify(notifyId, builder.build());
Copy the code

Note that USE_FULL_SCREEN_INTENT declaration is required in the AndroidManifest for Target SDk 29 and above

/ / in the AndroidManifest < USES - permission android: name ="android.permission.USE_FULL_SCREEN_INTENT" />
Copy the code

After the test, a notification bar will be displayed when the phone is in the on-screen state; when the phone is in the locked or off-screen state, the screen will be on and the phone directly enters the CallActivity

3. Sandbox App storage

On Android10, when the App target SDK is 29 or above or specified in the AndroidManifest, the App cannot access the external storage directly, even if it has write permission to the external storage

confusion

There are other articles on the Internet that have some claims

The new storage permissions cancel and replace the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. If you want to access media shared files outside the sandbox, such as photos, music, and videos, You need to apply for new media permissions :READ_MEDIA_IMAGES,READ_MEDIA_VIDEO, and READ_MEDIA_AUDIO. The application method is the same as the original storage permission

I did not find these three permission types in the actual test and checked the official documentation

The official document reads:

You do not need the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permission to access external storage. You need to have the READ_EXTERNAL_STORAGE permission and use MediaStore to access it

On Android 10, you still need to apply for READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE dynamic permissions to access external files

Adaptation scheme

Data of the corresponding App needs to be stored in the sandbox of the App. The corresponding path is below

The file type address
Video file context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
Audio file context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
Image files context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
  • The actual path to these addresses stored in the sandbox is in /data/user/0/ package name /, so these files will be cleared after the App uninstalls
  • Files in these directories are protected by the system and cannot be directly accessed by other apps

To save a sandbox video or image file to external storage, use ContentValues and MediaStore. I’ve wrapped a utility class for this:

public static boolean SavePictureFile(Context context, File file) {
        if (file == null) {
            return false;
        }
        Uri uri = insertFileIntoMediaStore(context, file, true);
        return SaveFile(context, file, uri);
}

public static boolean SaveVideoFile(Context context, File file) {
        if (file == null) {
            return false;
        }
        Uri uri = insertFileIntoMediaStore(context, file, false);
        return SaveFile(context, file, uri);
}

private static boolean SaveFile(Context context, File file, Uri uri) {
        if (uri == null) {
            LogUtil.e("url is null");
            return false;
        }
        LogUtil.i("SaveFile: " + file.getName());
        ContentResolver contentResolver = context.getContentResolver();

        ParcelFileDescriptor parcelFileDescriptor = null;
        try {
            parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "w");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        if (parcelFileDescriptor == null) {
            LogUtil.e("parcelFileDescriptor is null");
            return false;
        }

        FileOutputStream outputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
        FileInputStream inputStream;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            LogUtil.e(e.toString());
            try {
                outputStream.close();
            } catch (IOException ex) {
                LogUtil.e(ex.toString());
            }
            return false;
        }

        try {
            copy(inputStream, outputStream);
        } catch (IOException e) {
            LogUtil.e(e.toString());
            return false; } finally { try { outputStream.close(); } catch (IOException e) { LogUtil.e(e.toString()); } try { inputStream.close(); } catch (IOException e) { LogUtil.e(e.toString()); }}return true; } private static void copy(InputStream ist, OutputStream ost) throws IOException { byte[] buffer = new byte[4096]; int byteCount;while((byteCount = ist.read(buffer)) ! = -1) { ost.write(buffer, 0, byteCount); } ost.flush(); } private static URI insertFileIntoMediaStore(Context Context, File File, boolean isPicture) { ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName()); contentValues.put(MediaStore.Video.Media.MIME_TYPE, isPicture ?"image/jpeg" : "video/mp4");
        if (Build.VERSION.SDK_INT >= 29) {
            contentValues.put(MediaStore.Video.Media.DATE_TAKEN, file.lastModified());
        }

        Uri uri = null;
        try {
            uri = context.getContentResolver().insert(
                    (isPicture ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                    , contentValues
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return uri;
    }

Copy the code
  • Before Android10, you needed read/write permissions to write files to system folders. In Android10, you can write files directly to system folders without permissions
  • By default, videos are saved in Movies and Pictures are saved in Pictures. If you want to save them in the corresponding subfolders, you need to set the following Settings
/ / note MediaStore. Images. Media. RELATIVE_PATH need compileSdkVersion = 29, / / it is only the method can be performed on the mobile phone Android10 / / picture, the address of the corresponding storage for Pictures /test
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES + File.separator + test); // Videos stored at Movies/test
contentValues.put(MediaStore.Video.Media.RELATIVE_PATH,
Environment.DIRECTORY_MOVIES + File.separator + test);
Copy the code

conclusion

The above is my summary in the adaptation, I hope to help you with the adaptation, if there is a mistake, please be corrected.