Android: ограниченное хранилище: getContentResolver(). Update(...): COLUMN_LAST_MODIFIED: UnsupportedOperationException: обновление не поддерживается
Я пытаюсь обновить дату последнего изменения документа / файла, но получаю сообщение "UnsupportedOperationException: обновление не поддерживается"
Действия по воспроизведению:
- Выбор дерева документов
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, 1972);
- В результате действия создается новый документ в выбранном каталоге:
Uri treeUri = resultData.getData();
String treeDocumentId = DocumentsContract.getTreeDocumentId(treeUri);
treeUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, treeDocumentId);
Uri uri = DocumentsContract.createDocument(getContentResolver(), treeUri, "text/plain", "test.txt");
- Попытка обновить дату последнего изменения документа / файла
ContentValues values = new ContentValues();
values.put(DocumentsContract.Document.COLUMN_LAST_MODIFIED, 1592143965000L);
getContentResolver().update(uri, values, null, null);
Пытался вставить, но результат всегда один:
Caused by: java.lang.UnsupportedOperationException: Update not supported
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.update(ContentProviderNative.java:578)
at android.content.ContentResolver.update(ContentResolver.java:2009)
Кто-нибудь испытывал ту же проблему, но нашел решение этой проблемы?
3 ответа
Вы упоминаете ограниченное хранилище. Я искал способ обновить некоторые атрибуты, такие как дата создания, дата модификации ... файлов, скопированных / созданных через Storage Access Framework . Это требует также использования
DocumentsContract
,
DocumentFiles
Согласно документу , все столбцы доступны только для чтения для клиентских приложений.
Однако то, что я сделал для обновления атрибутов файлов, созданных с помощью
DocumentsContract.createDocument
должен был преобразовать Uri в путь в файловой системе. А затем напрямую обновить атрибуты этих файлов следующим образом:
// sourceUri & targetUri reference tree Uris
Path inFilePath = Paths.get(FileUtil.getFullDocIdPathFromTreeUri(sourceUri, context));
Path outFilePath = Paths.get(FileUtil.getFullDocIdPathFromTreeUri(targetUri, context));
BasicFileAttributes inAttrs = Files.readAttributes(inFilePath, BasicFileAttributes.class);
Files.getFileAttributeView(outFilePath, BasicFileAttributeView.class).setTimes(inAttrs.lastModifiedTime(), inAttrs.lastAccessTime(), inAttrs.creationTime());
FileUtil class (адаптировано из: this PullRequest https://github.com/nzbget/android/pull/12/files, который также ссылается на /questions/38598070/android-50-documentfile-iz-dereva-uri/38598084#38598084)
package com......;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
public final class FileUtil {
private static final String PRIMARY_VOLUME_NAME = "primary";
public static String getFullDocIdPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
if (treeUri == null) return null;
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null) return File.separator;
if (volumePath.endsWith(File.separator))
volumePath = volumePath.substring(0, volumePath.length() - 1);
String documentPath = getDocumentIdFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
documentPath = documentPath.substring(0, documentPath.length() - 1);
if (documentPath.length() > 0) {
if (documentPath.startsWith(File.separator))
return volumePath + documentPath;
else
return volumePath + File.separator + documentPath;
}
else return volumePath;
}
@SuppressLint("ObsoleteSdkInt")
private static String getVolumePath(final String volumeId, Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;
try {
StorageManager mStorageManager =
(StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
// primary volume?
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
return (String) getPath.invoke(storageVolumeElement);
// other volumes?
if (uuid != null && uuid.equals(volumeId))
return (String) getPath.invoke(storageVolumeElement);
}
// not found.
return null;
} catch (Exception ex) {
return null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static String getVolumeIdFromTreeUri(final Uri treeUri) {
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0) return split[0];
else return null;
}
}
@usilo благодарим вас за отзыв, но, к сожалению, ваш обходной путь решает мою проблему лишь частично.
В моем приложении SFTP Server s0 v1 я удаленно получаю доступ через протокол SFTP к файлам на устройстве Android с помощью «java.io. *», но, к сожалению, у меня нет доступа на запись к определенному внешнему хранилищу, и поэтому я реализовал функцию монтирования, которая на самом деле оболочка файла java.io. * поверх DocumentContract, которая позволяет мне писать во внешнее хранилище, но (как я уже писал), к сожалению, я не могу обновить дату последнего изменения.
Короче говоря, ваше решение работает с файлами, к которым у меня есть доступ для записи (и где я рекомендую использовать java.io. * в целом), но, к сожалению, не с некоторыми файлами на внешнем хранилище, где я могу писать только с использованием хранилища с ограниченным объемом.
Потею через тему SAF последние 3 дня. Мои усилия заключались в том, чтобы загрузить файлы с FTP на внешнюю SD-карту на Android API28). Мне удалось это сделать, однако я столкнулся с той же проблемой, что я не мог изменить дату последнего изменения файла на дату на FTP. Я попробовал так же, как описано выше. Достаточно забавно, я попробовал старый добрый способ утилиты java-файлов с
file.setLastModified(l_lastModified);
и, что удивительно, это работает! Вы не можете записать файл с помощью утилит java-файла, вы должны использовать SAF, и после того, как файл будет загружен, как ни странно, вы можете изменить атрибуты этого файла, что не имеет абсолютно никакого смысла. Однако это решило мою проблему, и это работает.