I'm trying to download file using DownloadManager in Async Task. Here's the doInBackground() method
protected Boolean doInBackground(String... params) {
DownloadManager mgr = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
//String DIRECTORY_DOWNLOADS = m_context.getExternalFilesDir(null).getAbsolutePath();
Uri uri = Uri.parse(params[0]);
typ_mapy = Integer.parseInt(params[2]);
File download = new File(getExternalFilesDir(null) ,params[1]);
if(download.exists()){
info = "Existuje";
nazev_souboru=params[1];
return false;
}else{
try {
long lastDownload = mgr.enqueue(new DownloadManager.Request(uri)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
.setAllowedOverRoaming(false)
.setNotificationVisibility(Request.VISIBILITY_HIDDEN)
.setDestinationInExternalFilesDir(m_context,"/",params[1]));
Cursor c = mgr.query(new DownloadManager.Query().setFilterById(lastDownload));
while(c.moveToFirst())
{
publishProgress(String.valueOf(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))), String.valueOf(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))));
/*if(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))>=c.getLong(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))){
c.close();
return true;
}*/
c.close();
c = mgr.query(new DownloadManager.Query().setFilterById(lastDownload));
c.moveToFirst();
}
} catch (Exception e) {
return false;
}
return true;
}
}
You can see that I have a comment there. I thought that that condition will stop the loop, when download is finished, but when it's there, it simply calls onPostExecute() immediatly (although the download is running and finishes).
The problem is it stops calling the onProgressUpdate() where I update my progress bar.
Is there any way how to keep it in the while loop so the progress bar gets updated? If it's like this, it stays there in an endless loop. If I uncomment the condition, it finishes instantly.
EDIT:
I solved it by changing the loop like this:
boolean downloading = true;
while (downloading) {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(lastDownload);
Cursor cursor = mgr.query(q);
cursor.moveToFirst();
long bytes_downloaded = cursor.getLong(cursor
.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
long bytes_total = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
publishProgress(String.valueOf(bytes_total), String.valueOf(bytes_downloaded));
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false;
}
}
Answer found here: Show Download progress inside activity using DownloadManager
Hi you have to register a broadcast receiver to receive downloading complete
you can refer https://github.com/commonsguy/cw-android/blob/master/Internet/Download/src/com/commonsware/android/download/DownloadDemo.java
Related
I'm currently creating an app that needs to download a couple of videos then save the local path of it on a SQLite database.
At first, I wanted to get the URL of the video I downloaded but I can't seem to find anything that discusses about it. I tried to get COLUMN_MEDIAPROVIDER_URI and COLUMN_URI from the intent passed on the BroadcastReceiver for DownloadManager.ACTION_DOWNLOAD_COMPLETE but they return null.
Then I found about EXTRA_DOWNLOAD_ID. But if I use that, I still need to use something like a new HashMap that got the EXTRA_DOWNLOAD_ID of my download and the id of the video on my SQLite database for checking which is which.
I'm fine with that but I want to know if there's an easier way to do the thing I want.
I did this using OkHttp, as follows:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(YOUR_URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// ERROR MESSAGE
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
response.body().byteStream(); // byteStream with your result.
}
}
});
Another thing, maybe would be better if you store the videos on memory and just the address in your SQLite.
Using the code below from the SO question here
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
// get the DownloadManager instance
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query q = new DownloadManager.Query();
Cursor c = manager.query(q);
if(c.moveToFirst()) {
do {
String name = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
Log.i("DOWNLOAD LISTENER", "file name: " + name);
} while (c.moveToNext());
} else {
Log.i("DOWNLOAD LISTENER", "empty cursor :(");
}
c.close();
}
}
and saving the download id on my ArrayList I was able to make a simpler way to check which download is finished.
I modified it to look like this for my use case.
Cursor c = dlMgr.query(new DownloadManager.Query());
boolean found = false;
if(c.moveToFirst()) {
do {
String dlFilePath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
int dlId = Integer.parseInt( c.getString(c.getColumnIndex(DownloadManager.COLUMN_ID)) );
for(int x = 0; x < vidArrLst.size(); x++){
VideoAd va = vidArrLst.get(x);
if(va.getDownloadId() == dlId){
dbHelper.updateLocalPath(va.getVideoId(), dlFilePath);
va.setLocalPath(dlFilePath);
found = true;
break;
}
}
} while (c.moveToNext() && !found);
} else {
Log.d(TAG, "empty cursor :(");
}
UPDATE:
Sometimes this method will show that 2 downloads finished with the same file name which results to a video item to not have a local path. What I did is check if the local path is empty, download id is greater than 0, and if the download id is still downloading before playing a video so I can redownload a video and fix the gap and play the local file the next time the video needs to be played.
I try to use Download Manager to download some files form specific URL,
but the download request was never completed.
So I log some information to see what went wrong, it turns out the request is always in pending status, and the COLUMN_REASON is 0 which I couldn't find the corresponding description on the document.
COLUMN_STATUS: 1
COLUMN_REASON: 0
COLUMN_TOTAL_SIZE_BYTES: -1
COLUMN_BYTES_DOWNLOADED_SO_FAR: 0
Here is how to start a download.
val req = DownloadManager.Request(uri).apply {
addRequestHeader("Cookie", cookie)
allowScanningByMediaScanner()
setTitle(fullname)
setDescription(/* description text */)
setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, fullname)
}
val downloadId = downloadManager.enqueue(req)
And log information for debugging.
val filterQuery = DownloadManager.Query().setFilterById(downloadId)
val cursor = downloadManager.query(filterQuery)
if (cursor.moveToFirst()) {
val total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
val current = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
val reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))
Log.d("App", "status: " + status.toString())
Log.d("App", "reason: " + reason.toString())
Log.d("App", "total: " + total.toString())
Log.d("App", "current: " + current.toString())
}
So what's a possible reason that status of request was always pending and how do I debug it?
Any help is going to be appreciated.
In my case, settings up a VPN seem to solve this problem. It looks like google services have been blocked in my network and after I set up a system global VPN the issue has gone.
DownloadManager outputs its logs to logcat but not under your application's id, so you'll need to show logs for all apps. There should clues to the failed download in there. For example, here are a couple of my failure cases.
D/DownloadManager: [1988] Starting
W/DownloadManager: [1988] Stop requested with status 500: Internal Server Error
D/DownloadManager: [1988] Finished with status WAITING_TO_RETRY
and
W/DownloadManager: [1988] Stop requested with status 403: Unhandled HTTP response: 403 Forbidden
You have to wait(delay) before checking the status or set the download ID every time in a timer.
enqueue seems to return the download ID too late.
My code works very well:
private void startDownload(View v)
{
Uri uri=Uri.parse("http://example.com/app/name.apk");
DownloadManager mgr = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request req = new DownloadManager.Request(uri)
.setTitle(title)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
|DownloadManager.Request.NETWORK_MOBILE)
.setDescription("downloading")
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,"name.apk");
downloadId = mgr.enqueue(req);
getDownloadStatus();
}
check status method
private void getDownloadStatus()
{
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor cursor = ((DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE))
.query(query);
if (cursor.moveToFirst())
{
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
query.setFilterById(downloadId);
Cursor cursor = ((DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE))
.query(query);
cursor.moveToFirst();
int status=cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_RUNNING) {
Log.i("DM_STATUS","status is "+" running");
}else if (status == DownloadManager.STATUS_SUCCESSFUL) {
Log.i("DM_STATUS","status is "+" success");
timer.cancel();
}
}
}, 100,1);
}
}
Download Manager is the best way to download single file in android, It maintain Notification bar also.but how i can download Multiple files by it and show the whole downloading status by progressing bar in Notification.
Please suggest any library for it or any code snippet.
You can probably hide the DownloadManager's notification and show your own, that should do what you want.
To disable setNotificationVisibility(DownloadManger.VISIBILITY_HIDDEN); to hide the notification.
To show download progress, you can register a ContentObserver on DownloadManager's database to get periodic updates and update your own notification with it.
Cursor mDownloadManagerCursor = mDownloadManager.query(new DownloadManager.Query());
if (mDownloadManagerCursor != null) {
mDownloadManagerCursor.registerContentObserver(mDownloadFileObserver);
}
And the ContentObserver will look something like:
private ContentObserver mDownloadFileObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
#Override
public void onChange(boolean selfChange) {
Cursor cursor = mDownloadManager.query(new DownloadManager.Query());
if (cursor != null) {
long bytesDownloaded = 0;
long totalBytes = 0;
while (cursor.moveToNext()) {
bytesDownloaded += cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
totalBytes += cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
}
float progress = (float) (bytesDownloaded * 1.0 / totalBytes);
showNotificationWithProgress(progress);
cursor.close();
}
}
};
And the notification with progress can be shown with:
public void showNotificationWithProgress(Context context, int progress) {
NotificationManagerCompat.from(context).notify(0,
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Downloading...")
.setContentText("Progress")
.setProgress(100, progress * 100, false)
.setOnGoing(true)
.build());
}
I used Download Manager class inside my activity to perform downloads; it works fine and my next task is to show the same progress percentage inside my activity. I am not sure how to do it.
My code so far
public class DownloadSampleBook extends Activity{
private long enqueue;
private DownloadManager dm;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample_download);
BroadcastReceiver 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(enqueue);
Cursor c = dm.query(query);
if (c.moveToFirst()) {
int columnIndex = c
.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c
.getInt(columnIndex)) {
view.setImageURI(Uri.parse(uriString));
}
}
}
}
};
registerReceiver(receiver, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
public void onClick(View view) {
dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
Request request = new Request(
Uri.parse("http://abc.com/a.png"));
enqueue = dm.enqueue(request);
}
public void showDownload(View view) {
Intent i = new Intent();
i.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
startActivity(i);
}
}
Is there any method that give the progress download percentage?
If you are looking for a decent way to determine when to query the DownloadManager for progress updates, consider registering a ContentObserver for the uri content://downloads/my_downloads
Example:
DownloadManager manager = (DownloadManager) getSystemService( Context.DOWNLOAD_SERVICE );
manager.enqueue( myRequest );
Uri myDownloads = Uri.parse( "content://downloads/my_downloads" );
getContentResolver().registerContentObserver( myDownloads, true, new DownloadObserver() );
...
public static class DownloadObserver extends ContentObserver {
#Override
public void onChange( boolean selfChange, Uri uri ) {
Log.d( "DownloadObserver", "Download " + uri + " updated" );
}
This yields the following output as each chunk of the long running download is received
D/DownloadObserver(15584): Download content://downloads/my_downloads/437 updated
D/DownloadObserver(15584): Download content://downloads/my_downloads/437 updated
D/DownloadObserver(15584): Download content://downloads/my_downloads/437 updated
D/DownloadObserver(15584): Download content://downloads/my_downloads/437 updated
where '437' is the ID of your download.
Note that this follows the content URI defined in the class android.provider.Downloads which appears to be hidden in the framework and may not work consistently on all devices. (https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/provider/Downloads.java#89)
You can query the number of bytes downloaded so far, and the total number of bytes that need to be downloaded, using the query method, in much the same way as you have queried the status in your example code. Once you have those values, it's fairly easy to calculate the progress as a percentage.
There doesn't appear to be any way for you to be notified when new data is received, so it would be up to you to poll the download manager at some regular interval to determine the current status of any download that you want to monitor.
Query query = new Query();
query.setfilterById(downloadId);
Cursor c = dm.query(query);
if (c.moveToFirst()) {
int sizeIndex = c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
int downloadedIndex = c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
long size = c.getInt(sizeIndex);
long downloaded = c.getInt(downloadedIndex);
double progress = 0.0;
if (size != -1) progress = downloaded*100.0/size;
// At this point you have the progress as a percentage.
}
Note that the total size will initially be -1 and will only be filled in once the download starts. So in the sample code above I've checked for -1 and set the progress to 0 if the size is not yet set.
However, you may find in some cases that the total size is never returned (for example, in an HTTP chunked transfer, there will be no Content-Length header from which the size can be determined). If you need to support that kind of server, you should probably provide some kind of indication to the user that the download is progressing and not just a progress bar that is stuck at zero.
I had a requirement of tracking download of multiple files. After a lot of thinking and experimenting, I came up with the following code:
private void startDownloadThread(final List<DownloadFile> list) {
// Initializing the broadcast receiver ...
mBroadCastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
mFinishedFilesFromNotif.add(intent.getExtras()
.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));
}
};
IntentFilter intentFilter = new IntentFilter(
"android.intent.action.DOWNLOAD_COMPLETE");
DownloadProgressUIFragment.this.getActivity().registerReceiver(mBroadCastReceiver,
intentFilter);
// initializing the download manager instance ....
mDownloadManager = (DownloadManager) getActivity()
.getSystemService(Context.DOWNLOAD_SERVICE);
// adding files to the download manager list ...
for(DownloadFile f: list) {
mDownloadIds.add(FileUtils.addFileForDownloadInBkg(getApplicationContext(),
f.getUrl(),
f.getPath()));
}
// starting the thread to track the progress of the download ..
mProgressThread = new Thread(new Runnable() {
#Override
public void run() {
// Preparing the query for the download manager ...
DownloadManager.Query q = new DownloadManager.Query();
long[] ids = new long[mDownloadIds.size()];
final List<Long> idsArrList= new ArrayList<>();
int i = 0;
for (Long id: mDownloadIds) {
ids[i++] = id;
idsArrList.add(id);
}
q.setFilterById(ids);
// getting the total size of the data ...
Cursor c;
while(true) {
// check if the downloads are already completed ...
// Here I have created a set of download ids from download manager to keep
// track of all the files that are dowloaded, which I populate by creating
//
if(mFinishedFilesFromNotif.containsAll(idsArrList)) {
isDownloadSuccess = true;
// TODO - Take appropriate action. Download is finished successfully
return;
}
// start iterating and noting progress ..
c = mDownloadManager.query(q);
if(c != null) {
int filesDownloaded = 0;
float fileFracs = 0f; // this stores the fraction of all the files in
// download
final int columnTotalSize = c.getColumnIndex
(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
final int columnStatus = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
//final int columnId = c.getColumnIndex(DownloadManager.COLUMN_ID);
final int columnDwnldSoFar =
c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
while (c.moveToNext()) {
// checking the progress ..
if(c.getInt(columnStatus) == DownloadManager.STATUS_SUCCESSFUL) {
filesDownloaded++;
}
// If the file is partially downloaded, take its fraction ..
else if(c.getInt(columnTotalSize) > 0) {
fileFracs += ((c.getInt(columnDwnldSoFar) * 1.0f) /
c.getInt(columnTotalSize));
} else if(c.getInt(columnStatus) == DownloadManager.STATUS_FAILED) {
// TODO - Take appropriate action. Error in downloading one of the
// files.
return;
}
}
c.close();
// calculate the progress to show ...
float progress = (filesDownloaded + fileFracs)/ids.length;
// setting the progress text and bar...
final int percentage = Math.round(progress * 100.0f);
final String txt = "Loading ... " + percentage + "%";
// Show the progress appropriately ...
}
}
}
});
mProgressThread.start();
}
And the function to enqueue to files are:
public static long addFileForDownloadInBkg(Context context, String url, String savePath) {
Uri uri = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.setDestinationUri(Uri.fromFile(new File(savePath)));
final DownloadManager m = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
return m.enqueue(request);
}
Basically, I receive a notification individually for each of the files whose download has been finished and then add them to a set which is basically the set which helps me decide if all the downloads have been finished or not. I track the the progress based on the number of files and the fraction of each being complete. I hope this helps.
I have the following problem: Whenever I download a file with the DownloadManager it is downloaded twice (saved in the fashion "filename.extension" and "filename-1.extension"). Here is my code:
public void download() {
Request request = new Request(Uri.parse(_wrapper.getURL()));
request.setTitle(getFileName(_wrapper.getURL()));
request.setVisibleInDownloadsUi(false);
request.setDestinationInExternalFilesDir(_context, null, "/" + getFileName(_wrapper.getURL()));
_downloadID = _downloadManager.enqueue(request);
}
public BroadcastReceiver getDownloadFinishedBroadcastReceiver() {
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context pContext, Intent pIntent) {
String action = pIntent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
Query query = new Query();
query.setFilterById(_downloadID);
Cursor cursor = _downloadManager.query(query);
if (cursor.moveToFirst()) {
File file = new File(ScruloidConstants.APPLICATION_DIRECTORY);
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String path = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
_wrapper.setFilePath(path);
_wrapper.setLastDownloaded(new Date());
if (_listener != null) {
_listener.onDownloadProjectTaskFinished(new TaskResult<ProjectWrapper>(_wrapper));
}
}
else if (status == DownloadManager.STATUS_FAILED) {
int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
DownloadFailedException ex = new DownloadFailedException(reason);
if (_listener != null) {
_listener.onDownloadProjectTaskFinished(new TaskResult<ProjectWrapper>(ex));
}
}
}
}
}
};
return receiver;
}
The ProjectWrapper _wrapper is just a simple Class that holds data, no logic is done there. The _listener just displays on the callback method a little Toast message. I debugged my app to make shure the download() Method is invoked only once. I hope you can help me find the error.
Unfortunately, DownloadManager is buggy and doesn't work correctly on all devices. Your problem is reported here: https://code.google.com/p/android/issues/detail?id=18462
I've got the same error on mobile devices with API 21, I've made a workaround to verify before creating a request, if the file name used to set de request destination was equal one of the last files already downloaded, or if its a substring of any previews downloaded
if (!mLastMediaDownloadedId.any { it.contains(outputFile.name) }) {
mLastMediaDownloadedId.add(outputFile.name)
val url =
AppConstants.AWS_MEDIA_BUCKET_PATH + scoutObjectType.endPoint() + "$scoutObjectId.png"
val request = DownloadManager.Request(Uri.parse(url))
.setDestinationUri(Uri.fromFile(outputFile))
.setTitle("Downlading media")
.setDescription("Downloading image medias")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
.setAllowedOverRoaming(true)
.setAllowedOverMetered(true)
val downloadId = it.enqueue(request)
downloadIds.add(downloadId)
downloadId
}
and where "outputFile" is the file name itself to be downloaded, in your case this should be "filename.extension"
PS: Sorry for the Kotlin code, but it should be a good representation for the workaround itself