I have chat screen where i am downloading attachment from FirebaeStorage.
I have various formats of file which can be send doc,pdf,apk etc. and for each i have same TextViews and ImageViews.
In Chat screen's recyclerview adapter i am setting file path of local storage which can be obtain by run AsyncTask which downloads files from Firebase Storage and return file path.This work perfectly but the issue is how to get back that file path in onBindViewHolder on particular if else
Here is my RecyclerAdapter where i am calling AsyncTask and need result back into same scope and wait till download completes then set views according to data returned
public void onBindViewHolder(final Chat_Adapter.ViewHolder holder, int position) {
if (fileType.equals("pdf")){
new DownloadFileFromFS(Download_URL,FileName+".pdf").execute();
//HERE I NEED THE RESULT FROM ASYNCTASK AND WAIT TILL DOWNLOAD COMPLETES
//THEN SET THE VIEWS WITH RETURN RESULT FROM ASYNCTASK
if (DownloadFilePath!=null){
File file=new File(DownloadFilePath);
long sizeFile=file.length()/1024; //In KB
holder.Doc_FileName.setText(DownloadFilePath);
holder.Doc_FileSize.setText(String.valueOf(sizeFile));
}
else {
Log.d(TAG,"DOWNLOAD FILE PATH IS NULL");
}
}
AsyncTask
public class DownloadAttachment extends AsyncTask<Void, String, String> {
String DownloadUrl,fileName;
File file;
Context context;
ProgressBar progressBar;
public static final String TAG="###Download Attachment";
public DownloadAttachment(Context context, String downloadUrl, String fileName) {
DownloadUrl = downloadUrl;
this.fileName = fileName;
this.context=context;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
#Override
protected String doInBackground(Void... params) {
int count;
try {
File root = android.os.Environment.getExternalStorageDirectory();
Log.d(TAG,"DO IN BACKGROUND RUNNING");
File dir = new File(root.getAbsolutePath() + "/Downloaded Files/");
if (dir.exists() == false) {
dir.mkdirs();
}
URL url = new URL(DownloadUrl); //you can write here any link
file = new File(dir, fileName);
long startTime = System.currentTimeMillis();
Log.d(TAG, "download begining");
Log.d(TAG, "download url:" + url);
Log.d(TAG, "downloaded file name:" + fileName);
/* Open a connection to that URL. */
URLConnection ucon = url.openConnection();
//this will be useful so that you can show a typical 0-100% progress bar
int lengthOfFile=ucon.getContentLength();
/*
* Define InputStreams to read from the URLConnection.
*/
InputStream is = ucon.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
/*
* Read bytes to the Buffer until there is nothing more to read(-1).
*/
ByteArrayOutputStream baf = new ByteArrayOutputStream(5000);
int current = 0;
long total=0;
while ((current = bis.read()) != -1) {
baf.write((byte) current);
total=total+current;
//PUBLISH THE PROGRESS
//AFTER THIS onProgressUpdate will be called
publishProgress(""+(int)(total*100)/lengthOfFile);
}
/* Convert the Bytes read to a String. */
FileOutputStream fos = new FileOutputStream(file);
fos.write(baf.toByteArray());
fos.flush();
fos.close();
Log.d("DownloadManager", "download ready in " + ((System.currentTimeMillis() - startTime) / 1000) + " sec");
Log.d(TAG,"File Path "+file);
} catch (IOException e) {
Log.d("DownloadManager", "Error: " + e);
}
return file.toString();
}
}
UPDATE As #Atef Hares suggest,i did implement in code.its working fine but what if i have different format. How to call particular if else after getting result from asynctask cuz code suggested only call pdf if statement.
if (fileType.equals("pdf")){
final String nameFile=UUID.randomUUID().toString();
new DownloadFileFromFS(chat_wrapper.getDocuments(),nameFile).execute();
filePathInterface=new FilePath() {
#Override
public void LocalFilePath(final String Path) {
//HERE I AM GETTING RESULT FROM ASYNCTASK
//AND SETTING VIEWS ACCORDING TO IT
}
};
}
else if (fileType.equals("doc")){
//HOW TO GET RESULT FROM ASYNCTASK HERE IF FILETYPE IS "doc"
}
else if (fileType.equals("ppt")){
//HOW TO GET RESULT FROM ASYNCTASK HERE IF FILETYPE IS "ppt"
}
Okay using interfaces -as you know- will achieve what you need!
just do it like this:
public interface AsyncTaskCallback {
void onSuccess (String filePath);
}
and in the Adapter, don't set any data to the view unless you got the data back from asyncTask, like this:
public void onBindViewHolder(final Chat_Adapter.ViewHolder holder, int position) {
//Initialize filetype here
new DownloadFileFromFS(Download_URL, FileName + "."+ filetype, new AsyncTaskCallback() {
#Override
public void onSuccess(String filePath) {
//HERE I NEED THE RESULT FROM ASYNCTASK AND WAIT TILL DOWNLOAD COMPLETES
//THEN SET THE VIEWS WITH RETURN RESULT FROM ASYNCTASK
if (filePath != null) {
File file = new File(filePath);
long sizeFile = file.length() / 1024; //In KB
holder.Doc_FileName.setText(filePath);
holder.Doc_FileSize.setText(String.valueOf(sizeFile));
} else {
Log.d(TAG, "DOWNLOAD FILE PATH IS NULL");
}
}
}).execute();
}
You could use a callback.
public interface FileDownloadCallback {
void onSuccess (String filePath);
void onFailed ();
}
Pass that in to your AsyncTask and call it from onPostExecute.
Related
I have created a 'MP4 File Download' Button. It's working fine in android Android Pie and Oreo but not working in Marshmallow or Lollipop. It doesn't download the file in those versions. Can someone point out what I am missing in my code? Thanks in advance.
Here is my download code
public class DownloadFile extends AsyncTask<String, String, String> {
private ProgressDialog progressDialog;
private String fileName;
private String folder;
private boolean isDownloaded;
/**
* Before starting background thread
* Show Progress Bar Dialog
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
this.progressDialog = new ProgressDialog(context);
this.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
this.progressDialog.setCancelable(false);
this.progressDialog.show();
}
/**
* Downloading file in background thread
*/
#Override
protected String doInBackground(String... f_url) {
int count;
try {
URL url = new URL(f_url[0]);
URLConnection connection = url.openConnection();
connection.connect();
// getting file length
int lengthOfFile = connection.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
//Extract file name from URL
fileName = f_url[0].substring(f_url[0].lastIndexOf('/') + 1, f_url[0].length());
//Append timestamp to file name
fileName = timestamp + "_" + fileName;
//External directory path to save file
folder = Environment.getExternalStorageDirectory() + File.separator + "Bharti News/";
//Create androiddeft folder if it does not exist
File directory = new File(folder);
if (!directory.exists()) {
directory.mkdirs();
}
// Output stream to write file
OutputStream output = new FileOutputStream(folder + fileName);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress("" + (int) ((total * 100) / lengthOfFile));
Log.d(TAG, "Progress: " + (int) ((total * 100) / lengthOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
return "Downloaded at: " + folder + fileName;
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return "Something went wrong";
}
/**
* Updating progress bar
*/
protected void onProgressUpdate(String... progress) {
// setting progress percentage
progressDialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String message) {
// dismiss the dialog after the file was downloaded
this.progressDialog.dismiss();
// Display File path after downloading
Toast.makeText(context,
message, Toast.LENGTH_LONG).show();
}
}
And this is how I am calling it.
new DownloadFile().execute(item.getBodyurl());
It gives me this error - '/storage/emulated/0/Bharti News/2019-05-22-02-19-09_dWJZ7AQesz48Ol2o.mp4: open failed: ENOENT (No such file or directory)'
You should be checking if the user has granted permission of external storage by using:
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
new DownloadFile().execute(item.getBodyurl());
return true;
}else{ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);}
I am using glide to extract a bitmap from the cache and save it elsewhere. The relevant function below (based on this post:) fails to trigger the extraction. In fact, the log line 'Log.d(TAG, "About to start extraction");' below never gets triggered.
Any ideas on why the simple target function never gets called?
public byte[] extractImageFromCache(Context context, String pictureURL) {
byte[] byteArray = new byte[0];
if (context != null && pictureURL != null && !pictureURL.isEmpty()) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Glide.with(context)
.load(pictureURL)
.asBitmap()
.toBytes()
.diskCacheStrategy(DiskCacheStrategy.SOURCE) // Load the original hires image
.into(new SimpleTarget<byte[]>() {
#Override public void onResourceReady(final byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
Log.d(TAG, "About to start extraction");
new AsyncTask<Void, Void, Void>() {
#Override protected Void doInBackground(Void... params) {
Log.d(TAG, "Start extraction");
try {
Log.d(TAG, "Image bytes len: " + resource.length);
byteArrayOutputStream.write(resource);
byteArrayOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "Unable to load image: " + pictureURL);
e.printStackTrace();
}
return null;
}
}.execute();
}
});
Log.d(TAG, String.format("Got image bytes: %d for %s", byteArrayOutputStream.size(), pictureURL));
return byteArrayOutputStream.toByteArray();
}
return null;
}
As you have mentioned, Log.d(TAG, "About to start extraction") never gets triggered, which means onResourceReady never gets called. This could happen due to some error. Try overriding error callbacks.
To identify the problem I would recommend you to override other callbacks of SimpleTarget to debug the problem.
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
// load has been cleared
}
#Override
public void onLoadStarted(#Nullable Drawable placeholder) {
// load has started
}
#Override
public void onLoadFailed(#Nullable Drawable errorDrawable) {
// load failed due to some reason, notify callers here about the same
}
By using SimpleTarget object we can easily get Bitmap we are trying to extract from cache or network since by default glide look for cache hit.
Modify your Glide loading code to this :
Glide.with(this)
.load("https://cdn-images-1.medium.com/max/1200/1*hcfIq_37pabmAOnw3rhvGA.png")
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
Log.d("Size ", "width :"+resource.getWidth() + " height :"+resource.getHeight());
imageView.setImageBitmap(resource);
storeImage(resource);
}
});
And Using this or any other mechanism for storing bitmap to external/internal storage.
private void storeImage(Bitmap image) {
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: ");// e.getMessage());
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
image.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
Log.d(TAG, "img dir: " + pictureFile);
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
private File getOutputMediaFile(){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ getApplicationContext().getPackageName()
+ "/Files");
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
return null;
}
}
File mediaFile;
Random generator = new Random();
int n = 1000;
n = generator.nextInt(n);
String mImageName = "Image-"+ n +".jpg";
mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
return mediaFile;
}
I am using Glide ver 3.7
compile "com.github.bumptech.glide:glide:3.7.0"
Here is full working example : https://github.com/nieldeokar/GlideApp
First of all, your return is invalid because Glide executes the request in an asynchronous thread. So byteArrayOutputStream is never populated in time before the function returns. This means your function will always return an empty byte array. This also means your log statement Log.d(TAG, String.format("Got image bytes: %d for %s", byteArrayOutputStream.size(), pictureURL)); will be invalid. Knowing this, your About to start extraction will show up after Got image bytes.... You will need to reformat your code to use callbacks to notify the caller of the function that your code has retrieved the array. Something like this:
public interface GlideByteLoadCallback {
void onBytesExtracted(byte[] bytes);
void onFail(String message);
}
public void extractImageFromCache(Context context, String pictureURL, GlideByteLoadCallback callback) {
if (context != null && pictureURL != null && !pictureURL.isEmpty()) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Glide.with(context)
.load(pictureURL)
.asBitmap()
.toBytes()
.diskCacheStrategy(DiskCacheStrategy.SOURCE) // Load the original hires image
.into(new SimpleTarget<byte[]>() {
#Override public void onResourceReady(final byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
Log.d(TAG, "About to start extraction");
new AsyncTask<Void, Void, Void>() {
#Override protected Void doInBackground(Void... params) {
Log.d(TAG, "Start extraction");
try {
Log.d(TAG, "Image bytes len: " + resource.length);
byteArrayOutputStream.write(resource);
byteArrayOutputStream.flush();
if (callback != null) callback.onBytesExtracted(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "Unable to load image: " + pictureURL);
e.printStackTrace();
if (callback != null) callback.onFail("Extraction failed");
}
}
}.execute();
}
});
}
if (callback != null) callback.onFail("Invalid url");
}
Then call it with:
extractImageFromCache(context, picture, new GlideByteLoadCallback() {
#Override
public void onFail(String message) {
// handle failure here
}
#Override
public void onBytesExtracted(byte[] bytes) {
// do stuff with bytes here
}
});
This is what I would do.
As you are using glide, place your loaded image to an Image view.
From code, set:
imageView = (ImageView) view.findViewById(R.id.image_view);
imageViewProfile.setDrawingCacheEnabled(true);
imageViewProfile.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
then to get the bitmap from that image view you can use
imageView.getDrawingCache() //This returns a Bitmap
The only thing you should worry about is to verify if Glide finished loading your image or not -I am not sure how to accomplish this-, but give a try and give a vote if this works for you.
I work with a RecyclerView that looks like this.
I use an AsyncTask for managing the downloads. I use this button so that each item in the list of cards can have the progress of the respective download. I am not sure how to report the status of the download to the RecyclerView. How do I get this to post updates to the cards?
The async downloader code is this
public class DownloadFileFromURL extends AsyncTask<String, String, String> {
private final String resourceType;
public DownloadFileFromURL(String resourceType) {
super();
this.resourceType = resourceType;
// do stuff
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//showDialog(progress_bar_type);
}
protected void onProgressUpdate(String... progress) {
// setting progress percentage
// pDialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected String doInBackground(String... f_url) {
int count;
try {
URL url = new URL(f_url[0]);
String fileName = url.toString().substring(url.toString().lastIndexOf('/') + 1);
URLConnection connection = url.openConnection();
connection.connect();
// this will be useful so that you can show a tipical 0-100%
// progress bar
int lengthOfFile = connection.getContentLength();
Log.d("lengthofFile", String.valueOf(lengthOfFile));
// download the file
InputStream input = new BufferedInputStream(url.openStream(),
8192);
String destinationDirectory ="";
if(resourceType.equals(SyncUtil.IMAGE_ZIP)) {
destinationDirectory= SyncUtil.TMP;
}
if(resourceType.equals(SyncUtil.VIDEOFILE)) {
destinationDirectory = SyncUtil.VIDEO;
}
File mFolder = new File(AppController.root.toString() + File.separator+destinationDirectory);
if (!mFolder.exists()) {
mFolder.mkdir();
}
OutputStream output = new FileOutputStream(AppController.root.toString()+File.separator+destinationDirectory+File.separator
+ fileName);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress("" + (int) ((total * 100) / lengthOfFile));
output.write(data, 0, count);
}
output.flush();
// closing streams
output.close();
input.close();
if(resourceType.equals(SyncUtil.IMAGE_ZIP)) {
BusProvider.getInstance().post(new ZipDownloadComplete(fileName,resourceType));
}
if(resourceType.equals(SyncUtil.VIDEOFILE)) {
// BusProvider.getInstance().post(new VideoDownloadComplete(fileName));
}
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(String file_url) {
}
}
The RecyclerView adapter is here
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Video video = videosList.get(position);
holder.title.setText(video.getTitle());
holder.description.setText(video.getDescription());
holder.downloadButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String url ="http://"+ AppController.serverAddr +":"+AppController.port +"/video/"+video.getUrl()+video.getExtension();
DownloadFileFromURL downloadFileFromURL = new DownloadFileFromURL(SyncUtil.VIDEOFILE);
downloadFileFromURL.execute(url,video.getTitle(),video.getDescription());
}
});
holder.bind(video,listener);
}
Though its not a very good solution but in my case I got that working. I'm just sharing my thoughts with some sample code snippet.
I assume you're showing the download progress with a ProgressBar. So take an instance of the ProgressBar in your adapter and pass the reference to your AsyncTask.
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final Video video = videosList.get(position);
holder.title.setText(video.getTitle());
holder.description.setText(video.getDescription());
holder.downloadButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String url ="http://"+ AppController.serverAddr +":"+AppController.port +"/video/"+video.getUrl()+video.getExtension();
// Pass the progressBar here. You might have to set it as a final variable.
DownloadFileFromURL downloadFileFromURL = new DownloadFileFromURL(SyncUtil.VIDEOFILE, holder.progressBar);
downloadFileFromURL.execute(url,video.getTitle(),video.getDescription());
}
});
holder.bind(video,listener);
}
Now modify your constructor of the AsyncTask like this.
public DownloadFileFromURL(... , ProgressBar mProgressbar) {
this.mProgressbar = mProgressbar;
this.mProgressbar.setProgress(0);
this.mProgressbar.setMax(100);
}
Add onProgressUpdate in your AsyncTask
protected void onProgressUpdate(Integer... values) {
mProgressbar.setProgress(values[0]);
}
Now in your doInBackground calculate the file size and publish the progress after a certain amount of file is downloaded.
protected void doInBackground() throws IOException {
try {
// Establish connection
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
final String contentLengthStr = connection.getHeaderField("content-length");
InputStream input = connection.getInputStream();
String data1 = f.getPath();
FileOutputStream stream = new FileOutputStream(data1);
byte data[] = new byte[4096];
int count;
int progressCount = 0;
while ((count = input.read(data)) != -1) {
stream.write(data, 0, count);
progressCount = progressCount + count;
int progress = (int) (((progressCount * 1.0f) / Integer.parseInt(contentLengthStr)) * 10000);
// Publish your progress here
publishProgress(progress);
}
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Note:
Passing the original reference of your views is not a very good solution. I would rather set a BroadcastReceiver in my activity and would publish a broadcast with the specific item position in the publishProgress function. So that when the broadcast is received in the main activity, I could call notifyDatasetChanged to take progress effect in the list.
I wrote this code for downloading some files in my android application but now downloaded files are in the root without any special order and it looks very bad! So I want to know how can I add a folder for my application's downloaded files?
A code line to explain the path of output :
OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() + "/" + downloadedfileName);
And here is my code:
private class DownloadFileFromURL extends AsyncTask<String, String, String> {
private String downloadedfileName;
private ProgressDialog pDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(getApplicationContext(), "بارگذاری...", Toast.LENGTH_SHORT).show();
pDialog = new ProgressDialog(DownloadActivity.this);
pDialog.setMessage("کتاب در حال بارگذاري، لطفا منتظر بمانيد...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
pDialog.dismiss();
AlertDialog.Builder builder = new AlertDialog.Builder(DownloadActivity.this)
.setTitle("دانلود کتاب با موفقیت انجام شد")
.setMessage("از دانلود شما سپاسگذاریم.")
.setNeutralButton("خواهش میکنم", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
Intent i = new Intent(DownloadActivity.this, LibActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
DownloadActivity.this.startActivity(i);
DownloadActivity.this.finish();
}
});
builder.create().show();
}
public void setDownloadedFileName(String downloadedfileName){
this.downloadedfileName = downloadedfileName;
}
/**
* Downloading file in background thread
* */
#Override
protected String doInBackground(String... surl) {
int count;
try {
URL url = new URL(surl[0]);
URLConnection conection = url.openConnection();
conection.connect();
// this will be useful so that you can show a typical 0-100%
// progress bar
int lenghtOfFile = conection.getContentLength();
// download the file
InputStream input = new BufferedInputStream(url.openStream(),
8192);
// Output stream
//OutputStream output = new FileOutputStream(Environment
// .getExternalStorageDirectory().toString()
// + "/data/" + downloadedfileName);
OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() + "/ketabha/" + downloadedfileName);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
// publishProgress("" + (int) ((total * 100) / lenghtOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
}
Just as simple change this to your favorite directory:
OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() + "/ANYFOLDER/ANOTHERFOLDER/" + downloadedfileName);
And remember that in the first create that directory with this code:
File dir = new File(yourdirectory);
if(!dir.exists()){
dir.mkdirs();
}
Above code work if you added related manifest in your AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and for more information see below links:
How to create directory automatically on SD card
Create folder in Android
can't create a folder in external storage on android device
I use AsyncTask with Progress Dialog for downloading a database from a server. I want to provide user with an option of cancellation the downloading task.
My purpose is to dismiss Progress Dialog and cancel AsyncTask when user click on Cancel button on Progress Dialog.
I have applied numerous code examples but they help to dismiss Progress Dialog only and fail to stop or cancel the downloading task.
Regarding my code lines below, can you please give a little help? Many thanks.
EDITED VERSION
public class Download_gram extends Activity {
// File url to download
private static String url = "https://dl.dropbox.com/u/15034088/Anhroid_Dict/grammar.nvk.zip";
private static DownloadFile newTask;
// Progress Dialog
private ProgressDialog progressDialog;
// Progress dialog type (0 - for Horizontal progress bar)
public static final int progress_bar = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download_gram);
newTask = new DownloadFile();
startDownload_gram = (Button) findViewById(R.id.btnProgressBar_gram);
//start download event and show progress bar
startDownload_gram.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Checi if file exists
File fgram = new File(Environment.getExternalStorageDirectory()+"/my_Folder/db/my_file/my_file.nvk");
if(fgram.exists())
{
Toast.makeText(getApplicationContext(), "File installed.", Toast.LENGTH_LONG).show();
}
else
{
//checking if the user has an internet connection
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null) {
if (!info.isConnected()) {
Toast.makeText(getApplicationContext(), "No Internet connection!", Toast.LENGTH_SHORT).show();
}
// execute async task
else
//new DownloadFile().execute(url);
newTask.execute(url);
}
else {
Toast.makeText(getApplicationContext(), "No Internet connection!", Toast.LENGTH_SHORT).show();
}
}
}});
}
//show progress dialog
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case progress_bar:
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Downloading… Please wait.");
progressDialog.setIndeterminate(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(false);
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dismissDialog(progress_bar);
newTask.cancel(true);
}
});
progressDialog.show();
return progressDialog;
default:
return null;
}
}
//backgroound downloading
class DownloadFile extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
showDialog(progress_bar);
}
#Override
protected String doInBackground(String... args) {
while (url != null) {
int count;
try {
URL url = new URL(args[0]);
URLConnection conection = url.openConnection();
conection.connect();
// getting file length
int fileLength = conection.getContentLength();
InputStream is = new BufferedInputStream(url.openStream(), 8192);
OutputStream os = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/_nvk_gram.zip");
byte data[] = new byte[1024];
long total = 0;
while ((count = is.read(data)) != -1) {
total += count;
publishProgress(""+(int)((total*100)/fileLength));
// writing data to file
os.write(data, 0, count);
}
// flush output
os.flush();
os.close();
is.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
//Unzip the downloaded zip file
{
String destinationDirectory = Environment.getExternalStorageDirectory().getPath() + "/my_Folder/db/my_file/";
int BUFFER = 2048;
List<String> zipFiles = new ArrayList<String>();
File sourceZipFile = new File(Environment.getExternalStorageDirectory().getPath() + "/_nvk_gram.zip");
File unzipDestinationDirectory = new File(destinationDirectory);
unzipDestinationDirectory.mkdir();
ZipFile zipFile = null;
// Open Zip file for reading
try {
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Create an enumeration of the entries in the zip file
Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(unzipDestinationDirectory, currentEntry);
//destFile = new File(unzipDestinationDirectory, destFile.getName());
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is =
new BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest =
new BufferedOutputStream(fos, BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
is.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
try {
zipFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (Iterator<String> iter = zipFiles.iterator(); iter.hasNext();) {
String zipName = (String)iter.next();
doUnzip(
zipName,
destinationDirectory +
File.separatorChar +
zipName.substring(0,zipName.lastIndexOf(".zip"))
);
if(isCancelled())return (null);
}
}
}
File fav_ifo = new File(Environment.getExternalStorageDirectory()+"/my_Folder/db/my_file/my_file.nvk");
if(fav_ifo.exists())
{
try {
AssetManager am = getAssets();
String fileName = "my_file.ifo";
File destinationFile = new File(Environment.getExternalStorageDirectory()+"/my_Folder/db/my_file/" + fileName);
InputStream in = am.open("my_file.ifo");
FileOutputStream f = new FileOutputStream(destinationFile);
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = in.read(buffer)) > 0) {
f.write(buffer, 0, len1);
}
f.close();
} catch (Exception e) {
Log.d("CopyFileFromAssetsToSD", e.getMessage());
}
}
else
{
}
//Delete the downloaded zip
File folder = Environment.getExternalStorageDirectory();
String fileName = folder.getPath() + "/_nvk_gram.zip";
File myFile = new File(fileName);
if(myFile.exists())
myFile.delete();
return null;
}
private void doUnzip(String zipName, String string) {
// TODO Auto-generated method stub
}
//updating progress bar
protected void onProgressUpdate(String... progress) {
//progress percentage
progressDialog.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String file_url) {
dismissDialog(progress_bar);
//toast to notify user of download completion
Toast.makeText(getApplicationContext(), "Database successfully downloaded.", Toast.LENGTH_LONG).show();
Intent mainIntent = new Intent(Download_gram.this, my_Main_class.class);
Download_gram.this.startActivity(mainIntent);
}
}
}
see you should keep track of your DownloadFile(aSyncTask) class object.
Create a new variable in your
public class Download_gram extends Activity {
// File url to download
private static String url = "http://www.abc.123.zip";
private static DownloadFile newTask; // ADDED BY ME
// ... ...
you should save the object reference when you call
new DownloadFile().execute("Url String"); //you must be doing this
//instead you should do
newTask = new DownloadFile();
newTask.execute("Url String"); // YOUR OBJECT IS IN newTask
...
// keep track of this newTask variable
// when ever you want to cancel just call
newTask.cancel(true); // ADDED BY ME
dialog.dismiss();//In fact, I want to apply the code to stop both Progress Dialog and AsynTask here.
after setting newTask to cancel, you should check timely in doInBackground that is you task is canceled ? if yes then exit doInBackground
// YOU CAN USE THIS IN WHILE LOOP OF doInBakcground
if(isCancelled())return (null);
//This will exit doInBackground function and hence cancel task.
// you can perform other operations too in this "if" statement as per your need.
for reference of these functions
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
##################### New Version ################.
you haven't checked is the task is canceled or not. you have to check everywhere downloading is taking place in asynctask if iscancelled is true return (null); to halt asynctask and stop downloading.
you should replace following code:
while ((count = is.read(data)) != -1) { // inside doInBackground
total += count;
publishProgress(""+(int)((total*100)/fileLength));
// writing data to file
os.write(data, 0, count);
if(isCancelled())return (null); // ADDED BY ME
}
above added line is must this will check if task is canceled this doInBackground function should be over.
You should to
yourAsyncTask.cancel(true);
and, inside your class DownloadFile, you should to check for isCanceled() and do a return if true.
You can do it in this way,
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
yourAysncTask.cancel(true);
}
});