As we all know, Android 11 uses proprietary directories and makes them mandatory. As for the introduction of the exclusive directory, I will not go into details here, because the official document has been very clear, and it may be written later. Here are some of the potholes I encountered in the external storage root.
Exclusive directory is the exclusive space opened up by Android11 for applications. APP will save files to the exclusive directory, no longer need to request storage permission, can be directly saved. In addition, other applications cannot access the files in the exclusive directory, ensuring the privacy of users.
Instead of saving files in a private directory, or in a media directory, I want to create a new folder, save my CSV files, and share the CSV.
In fact, FOR Android10, I had already adopted the method of FileProvider, but a year later, the same code error, and Android10 is ok, Android11 has a problem. I had two main problems,
- CSV file does not exist in Android 10:
Failed to find configured root ....
- Android 11 save as CSV, prompt:
EPERM (Operation not permitted)
Here’s my solution from start to finish:
1. Manifest.xml
(1) under the application to add: android: requestLegacyExternalStorage = “true”
② Define FileProvider: Add an element to your application manifest.
Name use fixed androidx. Core. Content. FileProvider,
Authorities + fileProvider,
Exported to false,
GrantUriPermissions is true and grants temporary access to the file for sharing.
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.adsale.registersite.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<! Metadata -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
Copy the code
(3) inres
Creating a Directoryxml
Folder, create file namefile_paths
Add the following code to it.
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<external-path name="com.adsale.ConcurrentEvent" path="." />
</paths>
</resources>
Copy the code
Path attributes:
External – code path is equal to the Environment. External.getexternalstoragedirectory (), so if you use this path code, use external – path.
Name is you’re ExternalStorageDirectory () established under the folder’s name, such as my file name is here: com. Adsale. ConcurrentEvent
Path uses., or /.
Other path codes:
The < files – path / > — Context. GetFilesDir ()
< cache – the path / > — Context. GetCacheDir ()
< external path / > — Environment. External.getexternalstoragedirectory ()
< external files – path / > — Context. GetExternalFilesDir (String)
< external cache – the path / > — Context. GetExternalCacheDir ()
< external – media – path / > — Context. GetExternalMediaDirs ()
2. Code creates an external storage directory
Request external read and write permissions first, needless to say. If the folder does not exist, create it
public String setRootDir(Activity activity) {
String rootPath = "";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {/ / a SD card
rootPath = Environment.getExternalStorageDirectory() + "/";
} else {
rootPath = Environment.getDataDirectory() + "/";
}
rootPath = rootPath + "com.adsale.ConcurrentEvent/";
if (RequestPermissionUtil.requestPermissionWriteStorage(activity)) { // If you have the storage permission, create a folder
createRootDir(rootPath);
}
return rootPath; // Return the absolute path of the folder
// /storage/emulated/0/com.adsale.ConcurrentEvent/
}
/** * create folder **/
public boolean createRootDir(String rootPath) {
File dirRoot = new File(rootPath);
if(! dirRoot.exists() || ! dirRoot.isDirectory()) {boolean isCreateRoot = dirRoot.mkdirs();
return isCreateRoot;
}
App.rootDir = rootPath ;
return true;
}
Copy the code
So if we get the absolute path, we can save our files in this directory. The file writing process is omitted.
3. Share files
private void sendCSVByEmail(a) {
/ / App. RootDir is first step to get the absolute path: / storage/emulated / 0 / com. Adsale. ConcurrentEvent /
// csvName is the file name: check-in query -2021-09-28 15.42.26.csv
String csvPath = App.rootDir + csvName;
File file = new File(csvPath);
if(! file.exists()) { Toast.makeText(getApplicationContext(),"CSV does not exist", Toast.LENGTH_SHORT).show();
return;
}
try {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("subject", csvName);
intent.putExtra("body".""); / / body
Uri uri = FileProvider.getUriForFile(getApplicationContext(), "com.adsale.registersite.fileprovider", file);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, uri); // Add an attachment. The attachment is the file object
if (csvName.endsWith(".csv")) {
intent.setType("application/octet-stream"); // All others use the stream as binary data to be sent
}
startActivity(intent); // Call the system's mail client for sending
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), "System has no mail client!", Toast.LENGTH_SHORT).show(); }}Copy the code
Key codes:
Uri uri = FileProvider.getUriForFile(getApplicationContext(), "com.adsale.registersite.fileprovider", file);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Copy the code
Uri uri = FileProvider.getUriForFile(getApplicationContext(), “com.adsale.registersite.fileprovider”, file);
Com. Adsale. Registersite fileprovider is defined in the XML of authorities, the file is a CSV file, through fileprovider access to content Uri. Grant temporary read and write permissions to the returned content URI by setFlags.
This fulfills the need to store directories externally.
QAQ
On Android11, because I saved the file name as the current time, it was generated using the time format YYYY-MM-DD HH: MM :ss, but I kept encountering the EPERM (Operation not permitted) problem. I thought there was a problem with the file directory permissions on Android11. Search just know, the original file name is saved when the problem…… This problem will not be solved if the time format is changed to. Vomiting blood…
Reference:
Creating a CSV file in Android 11 Return Error “java.io.FileNotFoundException: EPERM (Operation not permitted)”
Developer documentation FileProvider