есть ли способ сохранить изображение на USB-накопитель без SAF на Android 11?

Я хочу сохранить изображение, снятое камерой из моего приложения, прямо на USB-накопитель, подключенный к моему телефону. Есть ли способ сделать это, не прося пользователя выбрать каталог из SAF? Я хочу использовать хранилище с ограниченным доступом, поскольку я ориентируюсь на Android 11 и более поздние версии.

К устройствам не подключена sd карта.

Я могу хранить файлы на USB-накопителе на Android 10 с этим кодом, но не на Android 11

// I pass the image taken from the camera by parameter
    private void legacyToUsb(File image){
        File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null);
        String path = null;
        for (File externalDir : externalStorageVolumes) {
            Log.d(TAG, "Found dir at : " + externalDir);
            path = externalDir.getPath();
        }
        if (path == null) {
            showToast("Failed to found mounted device");
            return;
        }
        String fileName = mDateFormat.format(System.currentTimeMillis()) + ".jpg";
        File destFile = new File(path, fileName);
        showToast("Going to save data to " + destFile.getPath());
        if (destFile == null)
            showToast("Failed to open file");
        FileOutputStream os;
        String msg = "Successfully saved to " + destFile.getPath();
        try{
            byte[] imageData = getBytesFromFile(image);
            os = new FileOutputStream(destFile);
            os.write(imageData);
        } catch (IOException e) {
            e.printStackTrace();
            msg = "Failed to save file : " + e.getMessage();
        }
        showToast(msg);
    }

Однако на Android 11 это не работает (я не могу создать файл на USB-накопителе), потому что getExternalFilesDirs() не сообщает мне путь к usb otg.

ContextCompat.getExternalFilesDirs(<context>) возвращает:

Android 10

Found dir at : /storage/emulated/10/Android/data/com.example.MyApp/files
Found dir at : /storage/4406-15E5/Android/data/com.example.MyApp/files //I'm using this one

Android 11

Found dir at : /storage/emulated/0/Android/data/com.example.MyApp/files 

Насколько я понимаю, хранилище с ограниченным объемом удалено возможность доступа к съемному хранилищу с помощью этих функций. Так что я пытаюсь по-другому

    private void scopedToUsb(File image){
        ContentResolver resolver = getContentResolver();
        String fileName = mDateFormat.format(System.currentTimeMillis());

        ContentValues fileDetails = new ContentValues();
        fileDetails.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        fileDetails.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        fileDetails.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        fileDetails.put(MediaStore.Images.Media.IS_PENDING, 1);

        Set<String> volumeNames = MediaStore.getExternalVolumeNames(getApplicationContext());
        String storageVolume = null;
        // Get the first volume found here, it returns the hex with which the usb stick is identified
        // in my case 4406-15e5
        for (String volumeName : volumeNames){
            storageVolume = volumeName;
            Log.d(TAG, "Found volumeName : "+ volumeName);
            break;
        }
        // I have tried here MediaStore.VOLUME_EXTERNAL and MediaStore.VOLUME_EXTERNAL_PRIMARY
        // They both point to the same directory not in the usb stick
        Uri collection = MediaStore.Images.Media.getContentUri(storageVolume);
        Uri imageUri =  resolver.insert(collection, fileDetails);
        if(imageUri == null) {
            showToast("Failed to insert image row to ContentResolver");
            return;
        }
        Log.d(TAG, "Inserted, going to create ouput stream");
        OutputStream out = null;
        try {
            out = resolver.openOutputStream(imageUri);
            Bitmap bm = BitmapFactory.decodeFile(image.getAbsolutePath());
            bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            showToast("Failed to sqve picture to Usb");
            return;
        }
        showToast("Successfully saved to " + collection.getPath());
    }

но я получаю исключение в этой строке

Uri imageUri =  resolver.insert(collection, fileDetails);

исключение

java.lang.IllegalStateException: Failed to create directory: /mnt/media_rw/4406-15E5/Pictures/.pending-1621349186-2021-05-11-16-46-26-671.jpg
        at android.os.Parcel.createExceptionOrNull(Parcel.java:2384)
        at android.os.Parcel.createException(Parcel.java:2360)
        at android.os.Parcel.readException(Parcel.java:2343)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
        at android.content.ContentProviderProxy.insert(ContentProviderNative.java:549)
        at android.content.ContentResolver.insert(ContentResolver.java:2177)
        at android.content.ContentResolver.insert(ContentResolver.java:2138)
        at com.example.MyApp.MainActivity.scopedToUsb(MainActivity.java:368)
        at com.example.MyApp.MainActivity.saveToUsb(MainActivity.java:294)
        at com.example.MyApp.MainActivity.access$000(MainActivity.java:72)
        at com.example.MyApp.MainActivity$1.onImageSaved(MainActivity.java:273)
        at androidx.camera.core.ImageCapture$2.onImageSaved(ImageCapture.java:844)
        at androidx.camera.core.ImageSaver.lambda$postSuccess$1$ImageSaver(ImageSaver.java:308)
        at androidx.camera.core.-$$Lambda$ImageSaver$29vxg6qyjKwPRXgWrIwgYVInWKE.run(Unknown Source:4)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

Является ли это возможным ? У меня нет разрешения? я должен обязательно использовать Storage Acess Framework? Спасибо


person afvmil    schedule 11.05.2021    source источник
comment
on Android 11 this doesn't work as I cannot get the file inside the usb Что ты имеешь в виду? Я не могу следовать за тобой.   -  person blackapps    schedule 11.05.2021
comment
Извините. Я думаю On Android 10, I can do this Почему ты не можешь сделать это 11, я хотел спросить. Ты не сказал   -  person blackapps    schedule 11.05.2021
comment
когда я использую свою legacyToUsb() функцию с телефоном Android 11, он сохраняет изображение в /storage/emulated/0/Android/data/com.example.MyApp/files, что не соответствует USB-накопителю. Я думаю, что вопрос был неясным, поэтому я немного изменил фразу. Спасибо !   -  person afvmil    schedule 12.05.2021
comment
Вы по-прежнему не объяснили However, on Android 11 that doesn't work. Таким образом, код выше этого предложения не работает на Android 11. Хорошо. Но почему нет? Вы еще не сказали. Теперь я спрашиваю в третий раз: почему этот код не работает на Android 11? А почему на Android 10 работает?   -  person blackapps    schedule 12.05.2021
comment
I cannot get the file inside the usb Очень непонятное предложение. Вы имеете в виду: I cannot create a file on the usb-stick.? Пожалуйста, будьте ясны.   -  person blackapps    schedule 12.05.2021
comment
Apparently ContextCompat.getExternalFilesDirs(<context>) returns the path to /storage/emulated/0/Android/data/com.example.MyApp/files which is located internally on the phone Хорошо, если это работает на Android 10 и 11, тогда в чем разница? И эта функция возвращает массив. Расскажите, пожалуйста, об этом массиве для Android 10 и об этом массиве для Android11.   -  person blackapps    schedule 12.05.2021
comment
And that function returns an array. So please tell something about that array for Android 10 and about that array for Android 11. В чем разница? И в оба использованных устройства вставлена ​​карта micro sd?   -  person blackapps    schedule 12.05.2021
comment
for (File externalDir : externalStorageVolumes) { Log.d(TAG, "Found dir at : " + externalDir); } Скажите, пожалуйста, что зарегистрировано на устройстве Android 10. После этого расскажите, что зарегистрировано на устройстве Android 11. Поместите все в свой пост там, где вы объясняете, что это не работает для Android 11.   -  person blackapps    schedule 12.05.2021
comment
Вы все еще не сказали, вставлены ли в ваши устройства Android карты micro SD. (Ваши устройства! Я не говорю о вашем USB-накопителе, как вы).   -  person blackapps    schedule 12.05.2021
comment
Но ... Теперь, когда вы внимательно изучили журналы, вы можете сказать нам, почему ваш первый подход не работает для Android 11.   -  person blackapps    schedule 12.05.2021
comment
However, on Android 11 that doesn't work as I cannot see the file inside the usb stick after calling the legacyToUsb() function. Но ... причина не в этом. Лучше скажи настоящую причину. Я спрашиваю об этом с момента моего первого комментария.   -  person blackapps    schedule 12.05.2021
comment
getExternalFilesDirs() doesn't give me the path to the removable storage. Ну .. да и нет. Это даст путь к съемной карте micro sd (в основном как второй элемент), если вы вставили ее в свое устройство. На Android 10 он также часто указывал путь к USB-накопителю. Но на Android 11 последнего больше нет. И это ваша проблема, и она имеет мало общего с вашим неправильным кодом, а только с тем, как ведет себя getExternalFilesDir (). И это вы должны были сообщить из первого предложения вашего сообщения.   -  person blackapps    schedule 12.05.2021
comment
хорошо, спасибо, я не видел этого раньше. Итак, моя проблема в том, что getExternalFilesDirs() не возвращает путь к usb otg на нем, который совпадает с здесь. Вы предлагаете использовать SAF, но есть ли способ получить доступ к USB-накопителю без него?   -  person afvmil    schedule 12.05.2021