I want to save an image taken with the camera from my app, directly to a usb stick plugged in my phone. Is there a way to do it without asking the user to choose the directory from SAF ? I want to use scoped storage as I'm targeting Android 11 and onwards.
There is no sd card connected to the devices.
I can store files on the usb stick on Android 10 with this code but not on 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);
}
However, on Android 11 that doesn't work (I cannot create the file inside the usb stick) because getExternalFilesDirs()
doesn't give me the path to the usb otg.
ContextCompat.getExternalFilesDirs(<context>)
returns:
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
As far as I understand, scoped storage removed the possibility to access to the removable storage through those functions. So I'm trying this other way
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());
}
but I'm receiving an exception in this line
Uri imageUri = resolver.insert(collection, fileDetails);
exception
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)
Is it possible ? Am I missing any permission? should I mandatory use Storage Acess Framework? Thanks