Downloading file with DownloadManager Android - android

I am trying to download a file using the DownloadManager inside an AsyncTask
private class DownloadTask extends AsyncTask<String, Void, Boolean>
{
private Context mContext;
public DownloadTask(Context context)
{
mContext = context;
}
#Override
protected Boolean doInBackground(String... strings) {
String fileName = strings[2]+"_"+strings[3]+ strings[4];
String destination = mDestination + fileName;
final Uri uri = Uri.parse("file://" + destination);
mDownloading = true;
//If the file is already downloading just return.
File file = new File(destination);
if (file.exists()) {
return true;
}
//set downloadmanager
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(strings[0]));
request.setDescription(mContext.getString(R.string.downloading)+ " "+strings[1]);
request.setTitle(mContext.getString(R.string.downloading_title));
//set destination
request.setDestinationUri(uri);
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
//set BroadcastReceiver to enable next download
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
unregisterReceiver(this);
mDownloading = false;
}
};
//register receiver for when file download is compete
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
return true;
}
}`
The code works fine when I pass the url of the file. The thing is, that we want to make a GET call to a WEB made on PHP. This WEB method creates or selects a file and redirects using header("Location: ".$database->single()['Url']);
But when we make the call from DownloadManager it just calls registerReceiver right away.
Does anyone knows why this happens?
If we use a HttpURLConnection it works fine, but we would like to delegate all the hard work of the download in the DownloadManager.
Thank you for your comments.

3xx: redirects is't supported by DownloadManger. Source code at line 510
And it will download the redirect response and finish right way.
So you should get the response head[Location] by yourself, and pass it to the task.
By the way, you needn't put the download task in the AsyncTask.

Related

Reusing code for sharing/saving pdf document

So I have a fragment where I show the user terms and conditions for something and those terms and conditions are in the form of pdf file which is retrieved from the server.
This is the code that retrieves the pdf and gives the pdfView an inputstream to show the data.
class RetrievePDFFromUrl extends AsyncTask<String, Void, InputStream> {
#Override
protected InputStream doInBackground(String... strings) {
InputStream inputStream = null;
try {
URL url = new URL(strings[0]);
HttpURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
if (urlConnection.getResponseCode() == 200) {
inputStream = new BufferedInputStream(urlConnection.getInputStream());
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return inputStream;
}
#Override
protected void onPostExecute(InputStream inputStream) {
pdfView.fromStream(inputStream).load();
}
}
So far so good.
But now I have to add the functionality to share and save the document.
The problem is that I have to use other pieces of code to accomplish the task.
And since I cant share the document without downloading it, its a little messy.
Heres how I download the document.
private void downloadPDFContent(){
String fileName = getCurrentDocumentName();;
String urlToDownload = !secondDocument ? documentUrl1 : documentUrl2;
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName);
if (outputFile.exists()) {
return;
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(urlToDownload));
request.setTitle(fileName);
request.setMimeType("application/pdf");
request.allowScanningByMediaScanner();
request.setAllowedOverMetered(true);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
DownloadManager downloadManager = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
downloadManager.enqueue(request);
}
The problem comes when trying to share the document, Its just wrong to put 200ms delay before trying to share it, because no one knows how slow a connection can be sometimes and it wont work.
private void shareDocument() {
downloadPDFContent();
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), getCurrentDocumentName());
Uri uri = FileProvider.getUriForFile(getContext(),
getContext().getPackageName() + ".provider", outputFile);
Intent share = new Intent();
share.setAction(Intent.ACTION_SEND);
share.setType("application/pdf");
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Share document"));
}
}, 200);
}
Does anyone have any better ideas how can I achieve those 3 tasks - using inputstream to load the doc for the user to view and to share/save it also while reusing code and not doing it in different and unstable ways?
**UPDATE: I added a broadcastreceiver to start when the downloading is finished, instead of waiting a fixed amount of time like i do here.
Which is one idea better but still not what i wanted.
You have been using many legacy tools for this task and it is not clear do you have business constraints for it or not.
If your business use case just to download pdf and share it with another android app within your device, I would use Kotlin Flow for the async download task.
When you download your pdf and save it in storage, you could use a callback from Kotlin Flow as a trigger for your sharing intent. You would not need anymore any delay.
Please note, depends on your business use case you could use ContentProvider to give access to your app's files and p2p 3rd party tools for downloading and sharing your files.

Android download list of files with callback

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));

WebView Direct Download returns HTML which use the original file extension

I am trying to perform a direct download inside the WebView, not linking to the browser.
webview.setDownloadListener(new DownloadListener() {
#SuppressLint("DefaultLocale")
#Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
MimeTypeMap mtm = MimeTypeMap.getSingleton();
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
Uri downloadUri = Uri.parse(url);
// get file name. if filename exists in contentDisposition, use it. otherwise, use the last part of the url.
String fileName = downloadUri.getLastPathSegment();
int pos = 0;
if ((pos = contentDisposition.toLowerCase().lastIndexOf("filename=")) >= 0) {
fileName = contentDisposition.substring(pos + 9);
pos = fileName.lastIndexOf(";");
if (pos > 0) {
fileName = fileName.substring(0, pos - 1);
}
}
// predict MIME Type
String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
String mimeType = mtm.getMimeTypeFromExtension(fileExtension);
// request saving in Download directory
Request request = new DownloadManager.Request(downloadUri);
request.setTitle(fileName);
request.setDescription(url);
request.setMimeType(mimeType);
request.setDestinationInExternalPublicDir( Environment.DIRECTORY_DOWNLOADS, fileName);
Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS).mkdirs();
// request in download manager
downloadManager.enqueue(request);
}
});
And to open it,
// download complete toast
private BroadcastReceiver completeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Resources res = context.getResources();
// make toast
Toast.makeText(context, res.getString(R.string.download_complete), Toast.LENGTH_SHORT).show();
// go to download finished window
startActivity(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS));
}
};
#Override
protected void onPause() {
super.onPause();
// if app stops, stop reciever
unregisterReceiver(completeReceiver);
}
#Override
protected void onResume() {
// app start, start reciever
IntentFilter completeFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(completeReceiver, completeFilter);
super.onResume();
}
}
However, the result is not the original file, but the HTML format of the source using the original file extension; e.g. it uses .pdf, but is a HTML file.
What is causing this problem, and How can I fix it?
Files I get
First of all, it is impossible to open with a normal PDF viewer, and when I don't open it as PDF, I get a normal HTML document:
<!DOCTYPE html>
<html>
…
</html>
There is NOTHING SPECIAL in the HTML document.
I found that your filename is coming with quote so just replace it.
fileName=fileName.replaceAll("\"", "");
you will get the proper file. I have also used that code in mine and it's successfully worked.

Launch Adobe Reader in Android App

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..

Downloading an mp3 file from server in android

I am trying to create an app that can download music files, .mp3 to be precise, from the server.As I am a rookie in this Android Development field so I will appreciate any help from you guys.
I need something to start on and I will really appreciate if u can give me some links for useful resources.
Thanks
If you want to play the .mp3 file from any url then follow the code suggested by nik.
But if you want to download a file form the server and store it in any place on sdcard or internal storage device then follow this code,
private class DownloadFile extends AsyncTask<String, Integer, String>{
#Override
protected String doInBackground(String... urlParams) {
int count;
try {
URL url = new URL("url of your .mp3 file");
URLConnection conexion = url.openConnection();
conexion.connect();
// this will be useful so that you can show a tipical 0-100% progress bar
int lenghtOfFile = conexion.getContentLength();
// downlod the file
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream("/sdcard/somewhere/nameofthefile.mp3");
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
publishProgress((int)(total*100/lenghtOfFile));
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {}
return null;
}
EDIT: manifest permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
You can do it this way:
try {
MediaPlayer player = new MediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDataSource("http://xty/MRESC/images/test/xy.mp3");
player.prepare();
player.start();
} catch (Exception e) {
// TODO: handle exception
}
Manifest permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Use this method
private void beginDownload(){
/*
Create a DownloadManager.Request with all the information necessary to start the download
*/
DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://examplewebsite.com/aaa.mp3"))
.setTitle("Dummy File")// Title of the Download Notification
.setDescription("Downloading")// Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
// Uri of the destination file
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "" + System.currentTimeMillis());
.setRequiresCharging(false)// Set if charging is required to begin the download
.setAllowedOverMetered(true)// Set if download is allowed on Mobile network
.setAllowedOverRoaming(true);// Set if download is allowed on roaming network
DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
}
Here is the full code
private Button btnDownload;
private long downloadID;
private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Fetching the download id received with the broadcast
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
//Checking if the received broadcast is for our enqueued download by matching download id
if (downloadID == id) {
Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=findViewById(R.id.download);
registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
btnDownload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
beginDownload();
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(onDownloadComplete);
}
private void beginDownload(){
File file=new File(getExternalFilesDir(null),"Dummy");
/*
Create a DownloadManager.Request with all the information necessary to start the download
*/
DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://speedtest.ftp.otenet.gr/files/test10Mb.db"))
.setTitle("Dummy File")// Title of the Download Notification
.setDescription("Downloading")// Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
.setDestinationUri(Uri.fromFile(file))// Uri of the destination file
.setRequiresCharging(false)// Set if charging is required to begin the download
.setAllowedOverMetered(true)// Set if download is allowed on Mobile network
.setAllowedOverRoaming(true);// Set if download is allowed on roaming network
DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.

Categories

Resources