#java #android
Вопрос:
У меня возникли трудности с простым переименованием файла, созданного приложением, но помещенного в папку «Документы».
Редактировать:
Как это бывает, видео не создаются приложением, но, как ожидается, будут переименованы приложением. Пользователь сбрасывает видео в папку «Документы» вручную при запуске. Моя ошибка.
Вот мой код:
public static boolean renameVideoFile(Context c, File from, File to) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
Uri fromUri = FileProvider.getUriForFile(c, c.getPackageName() ".provider", new File(FileUtils.getVideosDir(), from.getName()));
ContentResolver contentResolver = c.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
contentResolver.update(fromUri, contentValues, null, null);
contentValues.clear();
contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, to.getName());
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
contentResolver.update(fromUri, contentValues, null, null);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
} else {
if (from.renameTo(to)) {
removeMedia(c, from);
addMedia(c, to);
return true;
} else {
return false;
}
}
}
Я прошел через несколько ошибок, но моя последняя ошибка:
java.lang.Исключение UnsupportedOperationException: Внешних обновлений нет
Что является внутренней проблемой с поставщиком файлов в
на androidx.core.контент.Файлообменник.обновление(файлообменник.java:523)
ПРАВКА №2 Также вот мои объявления поставщика в манифесте:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
И вот мои трогательные заявления. Опять же, это не вызывает проблем с сохранением:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path
name="internal_images"
path="files/Pictures" />
<external-files-path
name="internal_images_alternate"
path="Pictures" />
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Комментарии:
1. Когда изображение / видео попадает в папку «Документы», обречено ли оно оставаться, как бы оно ни называлось, навсегда?
2. Сначала расскажите, как вы создали этот файл. Опубликованный вами код смешивает класс File и MediaStore и никогда не сможет работать, конечно, там, где вы также используете файлообменник. Все это не имеет смысла.
3. Старый код работает нормально, и при создании файла проблем не возникает. Проблема возникает при переименовании файла. Также видео будут удалены в папку » Документы
4. Ваш код не имеет смысла. И вы даже не показали, как вы назвали renameVideoFile.
5. Значения содержимого.поместите(MediaStore. Файлы. FileColumns. DISPLAY_NAME, в.getName()); переименовывает видео
Ответ №1:
ИЗМЕНИТЬ: Внешняя папка, которую я выбрал, была папкой «Документы», К вашему сведению
Так что я наконец-то заставил его работать. Вот код для переименования видео (возможно, он не самый лучший, но он делает свое дело!)
private static void tryAddVideosToMediaStore(Activity context) {
List<File> files = MediaUtils.getVideoFilesFromDirectory();
for (File file : files) {
try {
Uri fromUri = FileProvider.getUriForFile(context, context.getPackageName() ".provider", file);
if (getRealPathFromURI(context, fromUri) == null) {
String nameWoExtension = MediaUtils.getNameWithoutStatus(file.getAbsolutePath());
ContentValues values = new ContentValues(3);
values.put(MediaStore.Video.Media.TITLE, nameWoExtension);
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.DATA, file.getAbsolutePath());
context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Images.Media.DATA};
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} catch(Exception e) {
return null;
}finally {
if (cursor != null) {
cursor.close();
}
}
}
А затем вызывающие методы
public static String getVideoNameFromPath(String path) {
return path.substring(path.lastIndexOf("/") 1, path.indexOf(".mp4"));
}
public static boolean renameVideoFile(MainActivityViewModel viewModel, SharedPreferenceHelper sharedPreferenceHelper, Activity c, File from, File to) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
tryAddVideosToMediaStore(c);
Uri fromUri = MediaUtils.getVideoUriFromFS(c, from);
try {
ContentResolver contentResolver = c.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
contentResolver.update(fromUri, contentValues, null, null);
contentValues.clear();
contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, to.getName());
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
contentResolver.update(fromUri, contentValues, null, null);
return true;
} catch (Exception securityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
sharedPreferenceHelper.get().edit().putString("from", from.getAbsolutePath()).putString("to", to.getAbsolutePath()).apply();
RecoverableSecurityException recoverableSecurityException;
viewModel.setContentUri(fromUri);
if (securityException instanceof RecoverableSecurityException) {
recoverableSecurityException =
(RecoverableSecurityException) securityException;
} else {
requestVideoWritePermissions(c, Uri.parse(MediaStore.Video.Media.EXTERNAL_CONTENT_URI "/" MediaUtils.getVideoId(c, from)));
return false;
}
IntentSender intentSender = recoverableSecurityException.getUserAction()
.getActionIntent().getIntentSender();
try {
c.startIntentSenderForResult(intentSender, 55,
null, 0, 0, 0);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
throw new RuntimeException(
securityException.getMessage(), securityException);
}
}
return false;
} else {
if (from.renameTo(to)) {
removeMedia(c, from);
addMedia(c, to);
return true;
} else {
return false;
}
}
}
public static Uri getVideoUriFromFS(Context c, File file) {
long id = getFilePathToMediaID(file, c);
Uri fromUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI,id);
return fromUri;
}
public static long getFilePathToMediaID(File videoPath, Context context)
{
Uri mainUri;
Cursor cursor1 = context.getContentResolver().query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Video.Media._ID},
MediaStore.Video.Media.DATA "=? ",
new String[]{videoPath.getAbsolutePath()}, null);
long id = 0;
if (cursor1 != null amp;amp; cursor1.moveToFirst()) {
id = cursor1.getLong(cursor1.getColumnIndex(MediaStore.MediaColumns._ID));
cursor1.close();
}
return id;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 55) { //rename video request code
if (resultCode == RESULT_OK) {
//update UI
String from = presenter.getFromFilePath();
String to = presenter.getToFilePath();
if (from != null amp;amp; to != null) {
Uri fromUri = MediaUtils.getVideoUriFromFS(this, new File(from));
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
contentResolver.update(fromUri, contentValues, null, null);
contentValues.clear();
contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, new File(to).getName());
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
contentResolver.update(fromUri, contentValues, null, null);
//update UI
}
}
}
}
Если я что-то забыл, пожалуйста, дайте мне знать, и я опубликую это здесь. Потребовалось буквально несколько часов поисков, чтобы найти это решение. Я очень расстроен простотой и сложностью, которые представил Google.
ПРАВКА: Я думаю, что забыл этот метод, который был очень важен
public static boolean requestVideoWritePermissions(Activity activity, Uri fromUri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
boolean hasPermission = true;
if (activity.checkUriPermission(fromUri, Binder.getCallingPid(), Binder.getCallingUid(),
Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
hasPermission = false;
}
List<Uri> uriList = new ArrayList<>();
uriList.add(fromUri);
if (!hasPermission) {
PendingIntent pi = MediaStore.createWriteRequest(activity.getContentResolver(), uriList);
try {
activity.startIntentSenderForResult(pi.getIntentSender(), 55, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return false;
}
return true;
}
return true;
}
Я также должен упомянуть, что каждое видео является подсказкой таким образом. Пользователь выбирает, разрешать или нет перезаписывать каждое видео, которое было менее оптимальным. Я хотел бы просто создать целую папку внешнего доступа, но я предполагаю, что этого не произойдет с изменениями области хранения.
Комментарии:
1.
Uri fromUri = FileProvider.getUriForFile(context, context.getPackageName() ".provider", file); if (getRealPathFromURI(context, fromUri) == null) {
Этот код не имеет смысла. Конечно, он возвращает значение null. Для всех уриев, которые вы могли бы изобрести. Вызовите его несколько раз, и он останется нулевым.2. И снова вы не показываете, как вы вызываете renameVideoFile(). И разве этот пост не был о переименовании?
3.
I wish I could just do a whole folder of
Жаль, что вы не дали небольшого представления о том, что вы делаете в своем коде, например, добавляете в хранилище mediastore и перехватываете исключение SecurityException, что является правильным решением. И далее Google пообещал добавить функцию для обработки пакета uries за один раз. Вполне возможно, что он уже там.4. И вы могли бы использовать SAF, чтобы позволить пользователю выбрать каталог. После этого у вас будет полный доступ ко всем файлам, и в небольшом цикле вы сможете переименовать все.
5. И этот фрагмент делает именно то, что он должен делать. Он переименовывает файлы, которые не были добавлены моим приложением