I was able to download a file from Firebase Storage to storage/emulated/0/Pictures
which is a default folder for picture that is being used by most popular app as well such as Facebook or Instagram. Now that Android Q has a lot of behavioral changes in storing and accessing a file, my app no longer be able to download a file from the bucket when running in Android Q.
This is the code that write and download the file from the Firebase Storage bucket to a mass storage default folders like Pictures, Movies, Documents, etc. It works on Android M but on Q it will not work.
String state = Environment.getExternalStorageState();
String type = "";
if (downloadUri.contains("jpg") || downloadUri.contains("jpeg")
|| downloadUri.contains("png") || downloadUri.contains("webp")
|| downloadUri.contains("tiff") || downloadUri.contains("tif")) {
type = ".jpg";
folderName = "Images";
}
if (downloadUri.contains(".gif")){
type = ".gif";
folderName = "Images";
}
if (downloadUri.contains(".mp4") || downloadUri.contains(".avi")){
type = ".mp4";
folderName = "Videos";
}
//Create a path from root folder of primary storage
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + Environment.DIRECTORY_PICTURES + "/MY_APP_NAME");
if (Environment.MEDIA_MOUNTED.equals(state)){
try {
if (dir.mkdirs())
Log.d(TAG, "New folder is created.");
}
catch (Exception e) {
e.printStackTrace();
Crashlytics.logException(e);
}
}
//Create a new file
File filePath = new File(dir, UUID.randomUUID().toString() + type);
//Creating a reference to the link
StorageReference httpsReference = FirebaseStorage.getInstance().getReferenceFromUrl(download_link_of_file_from_Firebase_Storage_bucket);
//Getting the file from the server
httpsReference.getFile(filePath).addOnProgressListener(taskSnapshot ->
showProgressNotification(taskSnapshot.getBytesTransferred(), taskSnapshot.getTotalByteCount(), requestCode)
);
With this it will download the files from server to your device storage with path storage/emulated/0/Pictures/MY_APP_NAME
but with Android Q this will no longer work as many APIs became deprecated like Environment.getExternalStorageDirectory()
.
Using android:requestLegacyExternalStorage=true
is a temporary solution but will no longer work soon on Android 11 and above.
So my question is how can I download files using Firebase Storage APIs on default Picture or Movie folder that is in the root instead of Android/data/com.myapp.package/files
.
Does MediaStore
and ContentResolver
has solution for this? What changes do I need to apply?
2
Answers
==NEW ANSWER==
If you wanted to monitor the download progress you can use
getStream()
of FirebaseStorage SDK like this:==OLD ANSWER==
Finally after tons of hours I manage to do it but using
.getBytes(maximum_file_size)
instead of.getFile(file_object)
as last resort.Big big thanks to @Kasim for bringing up the idea of
getBytes(maximum_file_size)
with also sample code working withInputStream
andOutputStream
.By searching across S.O topic related to I/O also is a big help here and hereThe idea here is
.getByte(maximum_file_size)
will download the file from the bucket and return abyte[]
on itsaddOnSuccessListener
callback. The downside is you must specify the file size you allowed to download and no download progress computation can be done AFAIK unless you do some work withoutputStream.write(0,0,0);
I tried to write it chunk by chunk like here but the solution is throwingArrayIndexOutOfBoundsException
since you must be accurate on working with index into an array.So here is the code that let you saved file from your Firebase Storage Bucket to your device default directories:
storage/emulated/0/Pictures, storage/emulated/0/Movies, storage/emulated/0/Documents, you name it
Here is my solution:
Download file with Glide
Get file mimeType
And save file to external storage
save file to stream
I tried with emulator Android 29. It works fine.
Note : getExternalStorageDirectory() was deprecated in API level 29. To improve user privacy, direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.