Android - получите DocumentFile с правами записи для любого пути к файлу на SD-карте (уже получив разрешение SD-карты)
В моем приложении я получаю разрешение записи SD-карты, используя следующее намерение. Если пользователь выбирает папку sd-карты в системном файловом обозревателе, то у меня есть доступ для записи sd-карты.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, 42);
После этого я могу изменять файлы на SD-карте с помощью класса DocumentFile. Но у меня проблемы с получением DocumentFile для случайного пути к файлу.
Document.fromFile(new File(path));
Document.fromSingleUri(Uri.fromFile(new File(path)));
оба возвращают объект DocumentFile, который возвращает false в.canWrite(). Хотя у меня уже есть разрешение SD-карты.
Поэтому я написал метод, опубликованный в конце моего вопроса, чтобы получить DocumentFile, который возвращает true для.canWrite(). Но это медленно... И также чувствует себя очень неправильно! Должен быть лучший способ сделать это. Я также написал метод, который возвращает ту же строку, что и
String docFileUriString = docFile.getUri().toString();
для любого файла, где docFile - это DocumentFile, который возвращается методом ниже. Но
DocumentFile.fromTreeUri(Uri.parse(docFileUriString ));
возвращает DocumentFile, который указывает на корень SD-карты вместо пути DocumentFile. Что просто странно. Может кто-нибудь предложить более элегантное решение?
public static DocumentFile getDocumentFileIfAllowedToWrite(File file, Context con){
List<UriPermission> permissionUris = con.getContentResolver().getPersistedUriPermissions();
for(UriPermission permissionUri:permissionUris){
Uri treeUri = permissionUri.getUri();
DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, treeUri);
String rootDocFilePath = FileUtil.getFullPathFromTreeUri(treeUri, con);
if(file.getAbsolutePath().startsWith(rootDocFilePath)){
ArrayList<String> pathInRootDocParts = new ArrayList<String>();
while(!rootDocFilePath.equals(file.getAbsolutePath())){
pathInRootDocParts.add(file.getName());
file = file.getParentFile();
}
DocumentFile docFile = null;
if(pathInRootDocParts.size()==0){
docFile = DocumentFile.fromTreeUri(con, rootDocFile.getUri());
}
else{
for(int i=pathInRootDocParts.size()-1;i>=0;i--){
if(docFile==null){docFile = rootDocFile.findFile(pathInRootDocParts.get(i));}
else{docFile = docFile.findFile(pathInRootDocParts.get(i)); }
}
}
if(docFile!=null && docFile.canWrite()){
return docFile;
}else{
return null;
}
}
}
return null;
}
2 ответа
Document.fromFile(File)
не работает должным образом для этой цели на последних устройствах
Вот ссылка на ответ. У меня больше всего информации о проблеме /questions/45254992/kak-ispolzovat-novyij-api-dostupa-k-sd-karte-predstavlennyij-dlya-android-50-lollipop/45255010#45255010
Ключевые шаги:
Получите доступ для записи на SD-карту и сделайте ее постоянной при перезагрузке телефона.
public void onActivityResult (int requestCode, int resultCode, Intent resultData) {
...
getContentResolver().takePersistableUriPermission(treeUri,Intent.FLAG_GRANT_READ_URI_PERMISSION |Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
После этого вы можете получить доступ к файлам и папкам, независимо от того, насколько глубоко они вложены, но вы должны знать о сложном URL-адресе, с которым вам придется иметь дело.
Если вы такой же линуксщик, как я, и привыкли думать, что все в файле и все просто. На этот раз нет. Файл на SD-карте, скажем
/storage/13E5-2604/testDir/test.txt
куда
13E5-2604
корневая папка вашей SD-карты будет:
content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt
в новой реальности . Почему они сделали это так сложно - другой вопрос...
Я не знаю метода API, который прозрачно и легко преобразует обычный путь Linux в этот новый путь реальности . Но после того, как вы это сделали (
%3A
это: и
%2F
is / См. https://www.w3schools.com/tags/ref_urlencode.ASP для справки), вы можете создать экземпляр
<tcode id="119856042"></tcode>без труда:
DocumentFile txtFile = DocumentFile.fromSingleUri(context,
Uri.parse("content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt"));
и напишите на него.
Чтобы ответить на @AndiMar (21 марта), я просто проверяю, существует ли он на внутренней памяти, если нет необходимости беспокоиться.
try {
if (sourcefile.toString().contains(Environment.getExternalStorageDirectory().toString())) {
if (sourcefile.exists()) {
sourcefile.delete();
}
} else {
FileUtilities fio = new FileUtilities();
DocumentFile filetodelete = fio.getDocumentFileIfAllowedToWrite(sourcefile, context);
if (filetodelete.exists()) {
filetodelete.delete();
}
}
} catch (Exception e) {
e.printStackTrace();
}