Переименование Видео / Изображения При Ориентации На Android 11 (Api 30)

#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. И этот фрагмент делает именно то, что он должен делать. Он переименовывает файлы, которые не были добавлены моим приложением