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.