I am facing problem of opening downloaded file after successfull download via DownloadManager API. In my code:
Uri uri=Uri.parse("http://www.nasa.gov/images/content/206402main_jsc2007e113280_hires.jpg");
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.mkdirs();
lastDownload = mgr.enqueue(new DownloadManager.Request(uri)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI |
DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false)
.setTitle("app update")
.setDescription("New version 1.1")
.setShowRunningNotification(true)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "a.apk"));
Cursor c=mgr.query(new DownloadManager.Query().setFilterById(lastDownload));
if(c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)) == 8) {
try {
mgr.openDownloadedFile(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID)));
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.d("MGR", "Error");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.d("MGR", "Error");
}
}
Problem is when is if(c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))==8) evoked. I got status -1 and an exception. Is there any better way, how to open downloaded files with DownloadManager API? In my example I am downloading a big image, in a real situation I would be downloading an APK file and I need to display an installation dialog immediately after udpate.
Edit: I figured out that status=8 is after successfull download. You might have different "checking successfull download" approach
Thanks
Problem
Android DownloadManager API - opening file after download?
Solution
/**
* Used to download the file from url.
* <p/>
* 1. Download the file using Download Manager.
*
* #param url Url.
* #param fileName File Name.
*/
public void downloadFile(final Activity activity, final String url, final String fileName) {
try {
if (url != null && !url.isEmpty()) {
Uri uri = Uri.parse(url);
activity.registerReceiver(attachmentDownloadCompleteReceive, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setMimeType(getMimeType(uri.toString()));
request.setTitle(fileName);
request.setDescription("Downloading attachment..");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager dm = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
dm.enqueue(request);
}
} catch (IllegalStateException e) {
Toast.makeText(activity, "Please insert an SD card to download file", Toast.LENGTH_SHORT).show();
}
}
/**
* Used to get MimeType from url.
*
* #param url Url.
* #return Mime Type for the given url.
*/
private String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
MimeTypeMap mime = MimeTypeMap.getSingleton();
type = mime.getMimeTypeFromExtension(extension);
}
return type;
}
/**
* Attachment download complete receiver.
* <p/>
* 1. Receiver gets called once attachment download completed.
* 2. Open the downloaded file.
*/
BroadcastReceiver attachmentDownloadCompleteReceive = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
long downloadId = intent.getLongExtra(
DownloadManager.EXTRA_DOWNLOAD_ID, 0);
openDownloadedAttachment(context, downloadId);
}
}
};
/**
* Used to open the downloaded attachment.
*
* #param context Content.
* #param downloadId Id of the downloaded file to open.
*/
private void openDownloadedAttachment(final Context context, final long downloadId) {
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
String downloadLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
String downloadMimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
if ((downloadStatus == DownloadManager.STATUS_SUCCESSFUL) && downloadLocalUri != null) {
openDownloadedAttachment(context, Uri.parse(downloadLocalUri), downloadMimeType);
}
}
cursor.close();
}
/**
* Used to open the downloaded attachment.
* <p/>
* 1. Fire intent to open download file using external application.
*
* 2. Note:
* 2.a. We can't share fileUri directly to other application (because we will get FileUriExposedException from Android7.0).
* 2.b. Hence we can only share content uri with other application.
* 2.c. We must have declared FileProvider in manifest.
* 2.c. Refer - https://developer.android.com/reference/android/support/v4/content/FileProvider.html
*
* #param context Context.
* #param attachmentUri Uri of the downloaded attachment to be opened.
* #param attachmentMimeType MimeType of the downloaded attachment.
*/
private void openDownloadedAttachment(final Context context, Uri attachmentUri, final String attachmentMimeType) {
if(attachmentUri!=null) {
// Get Content Uri.
if (ContentResolver.SCHEME_FILE.equals(attachmentUri.getScheme())) {
// FileUri - Convert it to contentUri.
File file = new File(attachmentUri.getPath());
attachmentUri = FileProvider.getUriForFile(activity, "com.freshdesk.helpdesk.provider", file);;
}
Intent openAttachmentIntent = new Intent(Intent.ACTION_VIEW);
openAttachmentIntent.setDataAndType(attachmentUri, attachmentMimeType);
openAttachmentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(openAttachmentIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, context.getString(R.string.unable_to_open_file), Toast.LENGTH_LONG).show();
}
}
}
Initialize FileProvider Details
Decleare FileProvider in AndroidManifest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.freshdesk.helpdesk.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_path"/>
</provider>
Add the following file "res -> xml -> file_path.xml"
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="attachment_file" path="."/>
</paths>
Note
Why Use FileProvider
From Android 7.0 we can't share FileUri with other appliction.
Using "DownloadManager.COLUMN_LOCAL_URI" we will get only FileUri hence we need to convert it into ContentUri & share with other application.
Provblem with using "DownloadManager.getUriForDownloadedFile(long id)"
Don't use "DownloadManager.getUriForDownloadedFile(long id)" - To get Uri from downloadId to open the file using external application.
Because from Android 6.0 & 7.0 "getUriForDownloadedFile" method returns local uri (Which can be accessed only by our application), we can't share that Uri with other application because they can't access that uri (But it is fixed in Android 7.1 see Android Commit Here).
Refere Android source code DownloadManager.java & Downloads.java
Hence always use Column "DownloadManager.COLUMN_LOCAL_URI" to get Uri.
Reference
https://developer.android.com/reference/android/app/DownloadManager.html
https://developer.android.com/reference/android/support/v4/content/FileProvider.html
You need to register a reciever for when the download is complete:
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
and a BroadcastReciever handler
BroadcastReceiver onComplete=new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
// Do Something
}
};
Buy instead of me ripping off everything, I suggest you'll check this out.
EDIT:
Just as a suggestion, I wouldn't recommend using API 9 just yet: http://developer.android.com/resources/dashboard/platform-versions.html
There are ways around this, by creating your very own download handler, like I did, because we didn't want to alienate most of our android's user base, for that you'll need:
Create AsyncTask which handles the file download.
and i'll recommend to create a download dialog of some sort (if you say it's a big file, i'd make it appear in the notification area).
and than you'll need to handle the opening of the file:
protected void openFile(String fileName) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(Uri.fromFile(new File(fileName)),
"MIME-TYPE");
startActivity(install);
}
For Kotlin, you can easily just use the URL.openStream() method to read and save your file in your directory.
If you want to do more fancier, like background threads.
You should checkout Elye's article on Medium.
https://medium.com/mobile-app-development-publication/download-file-in-android-with-kotlin-874d50bccaa2
private fun downloadVcfFile() {
CoroutineScope(Dispatchers.IO).launch {
val url = "https://srv-store5.gofile.io/download/JXLVFW/vcard.vcf"
val path = "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/contacts.vcf"
URL(url).openStream().use { input ->
FileOutputStream(File(path)).use { output ->
input.copyTo(output)
val file = File(path)
file.createNewFile()
onMain { saveVcfFile(file) }
}
}
}
}
remember add <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> to your AndroidMannifest.xml file
Related
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);
}
}
On Android N, i am getting an exception. It is a known issue per the documentation, which asks me to use ContentResolver.openFileDescriptor()
https://developer.android.com/reference/android/app/DownloadManager.html#COLUMN_LOCAL_FILENAME
I am not sure how to use. Where is the ContentResolver object here that I can use to get the filename? I never used it. So, I will appreciate any help.
08-04 11:20:59.765 7010 7290 W System.err: java.lang.SecurityException: COLUMN_LOCAL_FILENAME is deprecated; use ContentResolver.openFileDescriptor() instead
08-04 11:20:59.765 7010 7290 W System.err: at android.app.DownloadManager$CursorTranslator.getString(DownloadManager.java:1499)
Here is my code.
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor cursor = downloadManager.query(query);
final String downloadFilePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
cursor.close();
I tried the downlaodManager.getFileUri, but isn't what I am looking for. Appreciate any help.
Thanks
The following is working for me now:
String downloadFilePath = null;
String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (downloadFileLocalUri != null) {
File mFile = new File(Uri.parse(downloadFileLocalUri).getPath());
downloadFilePath = mFile.getAbsolutePath();
}
I solve this issue by using DownloadManager.COLUMN_LOCAL_URI instead of DownloadManager.COLUMN_LOCAL_FILENAME
DownloadManager.COLUMN_LOCAL_URI returns file path including "file://" so you have to exclude it from your downloadFilePath by using downloadFilePath = downloadFilePath.replace("file://","");
Here is one line solution of this issue:
String downloadFilePath = (c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))).replace("file://","");
Check below complete code of DownloadManager:
DownloadFinishedReceiver.java
public class DownloadFinishedReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action) && intent.getExtras()!=null) {
Bundle extras = intent.getExtras();
DownloadManager.Query q = new DownloadManager.Query();
long downloadId = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
q.setFilterById(downloadId);
Cursor c = ((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).query(q);
if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String downloadFilePath = (c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))).replace("file://","");
String downloadTitle = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE));
c.close();
Log.e("DownloadPath", downloadFilePath); // Print DownloadPath in Logcat
Log.e("DownloadTitle", downloadTitle); // Print DownloadTitle in Logcat
} else if (status == DownloadManager.STATUS_FAILED) {
removeTempOnFailure(context, downloadId);
}
}
}
}
// Remove temp/cache data
private void removeTempOnFailure(Context con, long downloadId) {
File cacheFileDir = new File(con.getCacheDir().getAbsolutePath());
for (File f : cacheFileDir.listFiles()) {
if (f.getName().contains(String.valueOf(downloadId))) {
f.delete();
break;
}
}
}
}
Register BroadcastReceiver in AndroidMenifest.xml file:
<receiver
android:name="com.example.receiver.DownloadFinishedReceiver"
android:exported="true"
android:process=".downloadFinished">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
Put below method in your Activity and pass appropriate arguments:
/**
*
* #param downloadUrl -> Your file download url
* #param downloadTitle -> Your file title to display in download manager
* #param fileName -> filename with extension like music.mp3 it will store in download folder
* #param hide -> true to hide downloadmanager in status bar, false to display it
* #return -> it will return downloadId
*/
private long downloadFromUrl(String downloadUrl, String downloadTitle, String fileName, boolean hide) {
Uri uri = Uri.parse(downloadUrl);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setTitle(downloadTitle);
if (hide) {
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setVisibleInDownloadsUi(false);
} else
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
return manager != null ? manager.enqueue(request) : 0;
}
If you are passing hide = true in above method then you have to add following permission in AndroidManifext.xml
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
Use the getUriForDownloadedFile to get the downloaded Uri.
DownloadManager downloadManager = DownloadManager.class.cast(getSystemService(DOWNLOAD_SERVICE));
Uri uri = downloadManager.getUriForDownloadedFile(id);
String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
Above method may cause CursorIndexOutOfBoundsException in case of empty list of rows.
A Cursor is an object, which is positioned before the first entry. So we should first check if there is any result at all. Here is my example:
int index = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
if (cursor.moveToFirst()) {
String downloadFileLocalUri = cursor.getString(index);
if (downloadFileLocalUri != null) {
File mFile = new File(downloadFileLocalUri);
downloadFileName = mFile.getName();
downloadFilePath = mFile.getAbsolutePath();
}
}
cursor.close();
For whoever finds this useful
I had to use DownloadManager.COLUMN_URI
instead of DownloadManager.COLUMN_LOCAL_URI
Here are my results for each respective column
c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) -> content://downloads/my_downloads/46
c.getString(c.getColumnIndex(DownloadManager.COLUMN_URI)) -> http://test-domain.com/files/encrypted/212125/bsd1e-411cd7-e3229fb-fdddsa12a974.pdf
c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)) -> http://test-domain.com/files/encrypted/212125/bsd1e-411cd7-e3229fb-fdddsa12a974.pdf
techtinkerer's answer also didn't work for me because I wasn't always getting a file URI, as CommonsWare pointed out, but often a content URI. But there are several ways you can still get the file from the content URI.
1) You can call getContentResolver().openInputStream(myContentUri) to get an input stream, that you can then write to a file you create in external or internal storage.
2) You can call getContentResolver().openFileDescriptor(myContentUri, "r") to open a read-only ParcelFileDescriptor. While you cannot get an absolute file path from a ParcelFileDescriptor, a lot of methods that accept files or URIs also accept ParcelFileDescriptor.
For example I was using DownloadManager to download a PDF then open it with a PdfRenderer, whose constructor accepts a ParcelFileDescriptor:
PdfRenderer renderer;
ParcelFileDescriptor pdfFileDescriptor = getContentResolver().openFileDescriptor(pdfUri, "r");
if (pdfFileDescriptor != null) renderer = new PdfRenderer(pdfFileDescriptor);
pdfFileDescriptor.close();
For another example, BitmapFactory also has a method to decode a ParcelFileDescriptor:
(from https://developer.android.com/guide/topics/providers/document-provider.html)
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return image;
}
So depending on what you want to do, a ParcelFileDescriptor may be all that you need.
You can decrease the targetSdk less than 24 in gradle.Actually this approach is not a recommended approach but we can overcome this error by decreasing the target sdk 23 or anything(but less than 24) as well.
I've got an Activity from which i need to download and save list of files. Files and names are stored in Map <String,String>. After downloading all files I need to call function in my Activity.
I've already have AsyncTask class which downloads and saves files and an interface which callbacks to Activity.
How can i pass Map<String,String> to Asynctask or maybe there is another solutions?
Thanks.
This is not something that is suitable for an AsyncTask. You should look into implementing a custom Service, probably an IntentService.
There's a very good tutorial at http://www.vogella.com/tutorials/AndroidServices/article.html
I would recommend using Android DownloadManager and a list of IDs
DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse("http://www.example.com/myfile.mp3");
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setTitle("My File");
request.setDescription("Downloading");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setVisibleInDownloadsUi(false);
request.setDestinationUri(Uri.parse("file://" + folderName + "/myfile.mp3"));
mList_refid.add(downloadmanager.enqueue(request));
To setup a callback, use registerReceiver
mList_refid = new ArrayList<>();
mDownloadReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
if (mList_refid != null && mList_refid.contains(downloadId)) {
mList_refid.remove(downloadId); // File is downloaded, remove it from ID list.
}
} else
Log.e(LOG, "BroadcastReceiver on receive action not handled : " + action);
}
};
registerReceiver(mDownloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
I'm using Android Download Manager to download list of files. Lately I came across a crash report saying
Unknown java.lang.IllegalArgumentException: Unknown URL content://downloads/my_downloads
Then later, I figured it out that the reason is because user disabled Android Download Manager. I check if the Download Manager is disabled by checking it's package name with the code below.
int state = this.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
And now, I need to find a way to enable the Download Manager if it is disabled. I tried setting it's enable state with the permission in Manifest but I keep getting Security Exception.
this.getPackageManager().setApplicationEnabledSetting("com.android.providers.downloads", PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0);
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
So I thought it might not be reachable because of it is a system app. (Google Play App does it).
Is there any way to redirect the user to the Download Manager Application Info view ? to let the user enables it ? If there is no way to enable it on run time programmatically.
Please edit my answer if is not valid
Check if download manager is available:
int state = this.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
if(state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED||
state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
||state==PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED){
// Cannot download using download manager
}
else {
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
request.setDescription(fileName);
manager.enqueue(request);
}
And the solution for trying to enable download manager is:
packageName = "com.android.providers.downloads"
try {
//Open the specific App Info page:
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
} catch ( ActivityNotFoundException e ) {
//e.printStackTrace();
//Open the generic Apps page:
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
startActivity(intent);
}
Some folks were looking for an answer to this question and I just realized that the Answer made to this question is somehow deleted. So I want to answer my own question.
There is no way to activate/deactivate Download Manager directly, since it's system application and we don't have access to it.
Only alternative left is to redirect the User to the Info Page of Download Manager Application.
try {
//Open the specific App Info page:
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + "com.android.providers.downloads"));
startActivity(intent);
} catch ( ActivityNotFoundException e ) {
e.printStackTrace();
//Open the generic Apps page:
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
startActivity(intent);
}
Google Gmail Inbox has check whether the DownloadManager is been disabled,if true then show an AlertDialog to tell user to enable the DownloadManager in Settings.The screenshot show below:
I wrote a class called DownloadManagerResolver to fix this,hope this can help your.:)
public final class DownloadManagerResolver {
private static final String DOWNLOAD_MANAGER_PACKAGE_NAME = "com.android.providers.downloads";
/**
* Resolve whether the DownloadManager is enable in current devices.
*
* #return true if DownloadManager is enable,false otherwise.
*/
public static boolean resolve(Context context) {
boolean enable = resolveEnable(context);
if (!enable) {
AlertDialog alertDialog = createDialog(context);
alertDialog.show();
}
return enable;
}
/**
* Resolve whether the DownloadManager is enable in current devices.
*
* #param context
* #return true if DownloadManager is enable,false otherwise.
*/
private static boolean resolveEnable(Context context) {
int state = context.getPackageManager()
.getApplicationEnabledSetting(DOWNLOAD_MANAGER_PACKAGE_NAME);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
return !(state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED ||
state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
} else {
return !(state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED ||
state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
}
}
private static AlertDialog createDialog(final Context context) {
AppCompatTextView messageTextView = new AppCompatTextView(context);
messageTextView.setTextSize(16f);
messageTextView.setText("DownloadManager is disabled. Please enable it.");
return new AlertDialog.Builder(context)
.setView(messageTextView, 50, 30, 50, 30)
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
enableDownloadManager(context);
}
})
.setCancelable(false)
.create();
}
/**
* Start activity to Settings to enable DownloadManager.
*/
private static void enableDownloadManager(Context context) {
try {
//Open the specific App Info page:
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + DOWNLOAD_MANAGER_PACKAGE_NAME));
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
//Open the generic Apps page:
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
context.startActivity(intent);
}
}
}
May be it's help to you.
downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Download_Uri);
//Restrict the types of networks over which this download may proceed.
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
//Set whether this download may proceed over a roaming connection.
request.setAllowedOverRoaming(false);
//Set the title of this download, to be displayed in notifications (if enabled).
request.setTitle("My Data Download");
//Set a description of this download, to be displayed in notifications (if enabled)
request.setDescription("Android Data download using DownloadManager.");
//Set the local destination for the downloaded file to a path within the application's external files directory
request.setDestinationInExternalFilesDir(this,Environment.DIRECTORY_DOWNLOADS,"CountryList.json");
//Enqueue a new download and same the referenceId
downloadReference = downloadManager.enqueue(request);
http://www.mysamplecode.com/2012/09/android-downloadmanager-example.html
I have an Android app where I intercept a PDF file download event in the WebView, download it using the DownloadManager, and launch a new intent with the Adobe Reader to display the file. It works fine, except that when the Adobe Reader starts, it displays the following message prior to displaying the actual file:
Read-only document | To modify this document save a copy on your device.
Save | View Read-only
After I dismiss this prompt, the document gets displayed correctly. How can I get rid of the Read-only prompt?
Here is my code:
public class MyDownloadListener implements DownloadListener {
MainActivity activity;
BroadcastReceiver receiver;
DownloadManager downloadManager;
public MyDownloadListener(MainActivity a) {
activity = a;
downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
Query query = new Query();
query.setFilterById(downloadId);
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
File fileSrc = new File(uriString);
Intent intentPdf = new Intent(Intent.ACTION_VIEW);
intentPdf.setDataAndType(Uri.fromFile(fileSrc), "application/pdf");
intentPdf.setPackage("com.adobe.reader");
intentPdf.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivity(intentPdf);
}
}
}
}
};
activity.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
#Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
Request request = new Request(Uri.parse(url));
downloadManager.enqueue(request);
}
}
As per the official documentation of class DownloadManager.Request
This class contains all the information necessary to request a new
download. The URI is the only required parameter. Note that the
default download destination is a shared volume where the system might
delete your file if it needs to reclaim space for system use. If this
is a problem, use a location on external storage (see
setDestinationUri(Uri).
So default location is more of a cache location and system can delete the file if it require more space. So if you want to kep the file then you can use setDestinationUri to provide the path in the SD card..
And it looks like the default space does not allow any other thread/process other then the download manager to write file in that space, hence the read only message from the adobe reader..