How to make downloaded file visible in Gallery in Android Q? - android

I am using download manager to download file like so:
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setTitle(createTitle());
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalFilesDir(activity, Environment.DIRECTORY_DOWNLOADS, subFolders + createFileName());
request.allowScanningByMediaScanner();
After it is downloaded, I am adding it to the media store like so:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
ContentResolver database = activity.getContentResolver();
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.Downloads.MIME_TYPE, mimeType);
contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/subfolder");
database.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
}
I will see the folder created in the gallery but the file always shows up as corrupted.
Can anyone help ?

To be able to load or view your downloaded image in gallery you need to scan the saved file using MediaScannerConnection:
private void scanFile(String path) {
MediaScannerConnection.scanFile(context,
new String[] { path }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.d("Tag", "Scan finished. You can view the image in the gallery now.");
}
});
}
Call this after you create your file path or in your checking if the file does exist:
scanFile(filePath);
Hope it will work :)

Related

How to copy Images/Videos from DocumentFile Object to Gallery Folder using MediaStore Api

I gained access to this folder using Document Tree Intent :-
content://com.android.externalstorage.documents/tree/primary%3AExampleApp%2FMedia%2F.hiddenMedia
The URI of a image present in the above folder :-
content://com.android.externalstorage.documents/tree/primary%3AExampleFolder%2FMedia%2F.hiddenMedia/document/primary%3AExampleFolder%2FMedia%2F.hiddenMedia%2FCristiano.jpg
Now I have that image as a DocumentFile and its URI from the above folder.
DocumentFile documentFile = DocumentFile.fromSingleUri(context, fileUri);
fileUri is the URI of the DocumentFile.
Note :- The files in the folder cannot be accessed via MediaStore API because the folder is hidden
Usually this DocumentFile can be either an Image or a Video file.
How do I copy the Image/Video from the DocumentFile to Pictures/My App using MediaStore API.
Thanks in Advance!
Thanks to #blackapps!
All I had to do was to request a writable URI from the MediaStore using the insert() method and then open a InputStream for source URI and OutputStream for destination URI.
Code for above action :-
public void saveFile(Uri sourceUri, String fileName, String mimeType) throws IOException{
ContentValues values = new ContentValues();
Uri destinationUri;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P){
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
if (fileName.endsWith(".mp4")){
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/MyFolder");
destinationUri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} else {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyFolder");
destinationUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
InputStream inputStream = context.getContentResolver().openInputStream(sourceUri);
OutputStream outputStream = context.getContentResolver().openOutputStream(destinationUri);
IOUtils.copy(inputStream, outputStream);
Toast.makeText(context, "The File has been saved!", Toast.LENGTH_SHORT).show();
}
Note :- You have to add the Commons-io dependency in your build.gradle file to access IOUtils.copy() function
If you find any better way to do this, Do share it!

Correct way to use DownloadManager and Content Provider for Android Q (API 29) and above?

The problem with my code is, it is downloading the file and it is showing in the gallery having path like
=> "/storage/emulated/0/Download/filename.jpg"
But when I click on the finished download notification it opens a file with a URI => "content://com.android.providers.downloads.documents/document/7123"
and it is of 0kb.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
//Setting title of request
request.setTitle(fileName);
request.setAllowedOverRoaming(true);
request.allowScanningByMediaScanner();
String mimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
request.setAllowedOverMetered(true);
if (!Utils.checkNullOrblank(mimeType))
request.setMimeType(mimeType);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
}else
request.setDestinationInExternalPublicDir(filepath, fileName);
downloadId = downloadManager.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Downloads.TITLE,fileName);
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.Downloads.MIME_TYPE, mimeType);
contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
// Insert into the database
ContentResolver database = context.getContentResolver();
uri.setValue(database.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues));
}
what am i doing wrong? how to do this process in a correct way?
With ContentValues you only registers in the database(inner) but does not insert the downloaded object itself. After you have configured content value use bitmap compress to save your object itself. uri = database.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)), uri = your file uri
Use only:
contentValues.put(MediaStore.DownloadColumns.IS_PENDING, 1);
contentValues.put(MediaStore.Downloads.TITLE,fileName);
without contentValues.put(MediaStore.DownloadColumns.DISPLAY_NAME, fileName).
After saving with bitmap compress
contentValues.clear();
contentValues.put(MediaStore.DownloadColumns.IS_PENDING, 0); contentValues.put(MediaStore.Downloads.DISPLAY_NAME, fileName + ".ext");
then
uri.update(insert, values, null, null);

How to delete a file from external storage android studio, using only Uri

So I have taken a look at examples from other people and implemented this.
File delete= new File(uri.getPath());
if(delete.exists()){
Log.d("Delete", "File Exists");
if(delete.delete()){
Log.d("Deleted", ""+uri);
}
else{
Log.d("Unable to delete", ""+uri);
}
}
else{
Log.d("Delete", "File is not found");
}
Now the only problem is that the path name i get is "content://downloads/all_downloads/644", and according to my printed logcat, This file could not be found. Note: This file does exist and I used the same uri to actually play the video. Thank you for your help. EDIT: OK This is how I got my URI.
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
Log.d("REFERENCE", "E"+"Entering Broadcast");
long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
uri=( manager.getUriForDownloadedFile(referenceId));
}
};
Now I appreciate very much for the suggestions given bellow, but I am still stuck even after reading the documentation. Suppose I cannot delete using a Uri, is there a way to convert my uri into something useful instead. I don't want to manually enter the address to the location of my files.
#Here is my entire code...
public class Cacher {
private DownloadManager manager;
private Uri uri;
public Cacher(String urlIn , Context context){
Log.d("REFERENCE", "A1- casher");
manager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Log.d("REFERENCE", "A");
Uri temp=Uri.parse(urlIn);
Log.d("REFERENCE", "B");
DownloadManager.Request request= new DownloadManager.Request(temp);
//
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
Long reference=manager.enqueue(request);
// Toast.makeText(this,""+reference,Toast.LENGTH_LONG);
Log.d("REFERENCE", "C"+"Downloading video");
Log.d("REFERENCE", "D"+"Setting broadcast");
context.registerReceiver(onComplete,
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Log.d("REFERENCE", "F"+"Now Setting the uri table with");
}
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
Log.d("REFERENCE", "E"+"Entering Broadcast");
long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
uri=( manager.getUriForDownloadedFile(referenceId));
}
};
//TODO: Add Utility to remove all Cached Videos
//TODO: Add Utility to delete a single cached video after it has been watched.
public Uri getUri() {
return uri;
}
}
So I have taken a look at examples from other people and implemented this.
That would only work if the Uri has a file scheme and you have write access to the directory that holds the file.
How to delete a file from external storage android studio, using only Uri
Most likely, you don't.
If you get the Uri from ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT, wrap the Uri in a DocumentFile using fromSingleUri(), then call delete() on the DocumentFile.
For any other content Uri, you are welcome to try calling delete() on a ContentResolver, but do not expect it to work. There is no requirement that a ContentProvider offer you any way to delete a piece of content. The app that supplies the ContentProvider should have its own way of allowing users to delete things.
So here is the solution to my problem for those who do not want to read through our comments. First, chose a directory to download to. Then use that files path and use the delete() functionality. Keep in mind that once you create a directory with a folder name, you only need to reference the folder name and not the entire path. Here is the code I hope it helps:
String in= GenerateDirectory.createVideoDirectory(context);// Might be useless here.
manager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Uri temp=Uri.parse(urlIn);
Ringtone r= RingtoneManager.getRingtone(context, temp);
String filename=(r.getTitle(context));
DownloadManager.Request request= new DownloadManager.Request(temp);
request.setDestinationInExternalPublicDir("/secretVideos", filename);
uri=Uri.parse(in+"/"+filename);
Log.d("REFERENCE", "Uri that is returned "+uri);
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
manager.enqueue(request);
// And then
public void removeCache(){
//TODO: Fix this. For some reason the Uri used gives invalid path name.
Log.d("Delete", "Directory to find ... "+uri);
File delete= new File(uri.getPath());
if(delete.exists()){
Log.d("Delete", "File Exists");
if(delete.delete()){
Log.d("Deleted", ""+uri);
}
}
}
// Where I create the directory
private int RESULT=0;
public static String createVideoDirectory(Context context){
String pathToExternalStorage = Environment.getExternalStorageDirectory().toString();
File appDirectory = new File(pathToExternalStorage + "/" + "secretVideos");
if(!appDirectory.exists()){
Log.d("Directory","Directory DNE");
if(appDirectory.mkdir()) {
Log.d("Directory", "Directory Created");
//Log.d("Directory","Unable to create directory, using default directory");
}
else if(appDirectory.canWrite()){
Log.d("Directory", "Permission given");
}
else{
Log.d("Directory", "Lack of permission");
}
}
else{
Log.d("Directory","Already Exists");
}
return appDirectory.toString();
}
private void writeAndReadPermissions(Context context){
if(ContextCompat.checkSelfPermission(context, permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions((Activity) context, new String[]{permission.WRITE_EXTERNAL_STORAGE, permission.READ_EXTERNAL_STORAGE}, RESULT);
}
}

Store media in internal storage using download manager

Want to store music file inside getFilesDir() using Uri. I had tried to store by this way
Uri Download_Uri = Uri.parse(songBean.vSongsFileName);
DownloadManager.Request request = new DownloadManager.Request(Download_Uri);
mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
request.setAllowedOverRoaming(false);
request.setTitle(songBean.vTitle);
request.setDescription("Downloading File");
try {
request.setDestinationUri(Uri.parse(createDirectory("tmp.mp3").getPath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
downloadReference = mDownloadManager.enqueue(request);
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
}
}, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Method for the create directory inside internal storage
public File createDirectory(String filename) throws FileNotFoundException {
File file = new File(context.getFilesDir(), filename);
Log.i("Tag", "createDirectory: " + file.getAbsolutePath());
return file;
}
Not able to store the file in internal storage.request.setDestinationUri(Uri.parse(createDirectory("tmp.mp3").getPath())); throwing not a file uri error.Please help me out for this
The getFilesDir() is private internal storage for your app only. You cannot ask another app to put files there as it has no access.
For DownloadManager use external storage.

Android - Why my saved image is not appearing in the default gallery of my phone?

I am trying to save an image from my application to the default gallery of my phone. The code below works perfectly if I have a SD card on the phone. The image saved appears in the phone's gallery and everything, as expected:
private Uri saveMediaEntry(File f, String title, String description, int orientation, Location loc) {
ContentValues v = new ContentValues();
v.put(Images.Media.TITLE, title);
v.put(Images.Media.DISPLAY_NAME, title);
v.put(Images.Media.DESCRIPTION, description);
v.put(Images.Media.ORIENTATION, orientation);
String nameFile = f.getName();
File parent = f.getParentFile() ;
String path = parent.toString().toLowerCase() ;
String nameParent = parent.getName().toLowerCase() ;
v.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
v.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, nameParent);
v.put(Images.Media.SIZE,f.length()) ;
if( nameFile.toLowerCase().contains(".png") ){
v.put(Images.Media.MIME_TYPE, "image/png");
}else if( nameFile.toLowerCase().contains(".jpg") ||
nameFile.toLowerCase().contains(".jpeg") ){
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}else{
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}
String imagePath = f.getAbsolutePath();
v.put("_data", imagePath) ;
ContentResolver c = getContentResolver() ;
Uri uriOfSucessfulySavedImage = null;
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, v);
return uriOfSucessfulySavedImage;
}
However, if I try to save the same image into the internal storage (for when the phone does not have a SD card), the image does not appear in the phone's gallery! To try to do that, I only change one line from the above code:
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, v);
The interesting thing about this, however, is that the variable uriOfSucessfulySavedImage is not null (it returns content://media/internal/images/media/x, where 'x' is a number). So, the image is being saved somewhere in the internal storage of the phone, but it is not getting displayed in the phone gallery's as when I use MediaStore.Images.Media.EXTERNAL_CONTENT_URI.
Does anybody have any clue what is going on? How can I save an image into the internal storage of the phone and have that image in the phone's gallery?
Update
I forgot one important information. The File "f" in the parameters of the method "saveMediaEntry" is coming from this other method for when the SD card is mounted (that is, for the first code):
public static File getCacheDirectory(String desiredNameOfTheDirectory){
File fileCacheDir = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ){
fileCacheDir = new File( Environment.getExternalStorageDirectory(), desiredNameOfTheDirectory );
}
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
and from the following code for when the SD card is not founded:
public static File getCacheDirectory(String desiredNameOfTheDirectory, Context context){
File fileCacheDir = null;
fileCacheDir = context.getCacheDir();
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
Another easy way to do it. Add this after saving your file.
File imageFile = ...
MediaScannerConnection.scanFile(this, new String[] { imageFile.getPath() }, new String[] { "image/jpeg" }, null);
I haven't tried this, but I believe you need to run the Media Scanner to scan the internal storage directory so that the gallery can see your newly saved image. Check this post here.
Copy Past this Function in your Activity
private void scanner(String path) {
MediaScannerConnection.scanFile(FrameActivity.this,
new String[] { path }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("TAG", "Finished scanning " + path);
}
});
}
And then add this Line where you save your image
scanner(imageFile.getAbsolutePath());
Try this.
Write down this line once image stored in gallery.
File file = ..... // Save file
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
} else {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
}

Categories

Resources