Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Download File Using DownloadManager in API 29 or Android Q?

As I am new in Android Development, I am trying to simple App using DownloadManager.

Here is the code

public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback{  Button btn;  private long referenceID; private DownloadManager downloadManager; private static final int PERMISSION_REQUEST_CODE = 1;  @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      btn = findViewById(R.id.btn);      btn.setOnClickListener(new View.OnClickListener() {         @Override         public void onClick(View view) {                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){                   if (checkPermission())                 {                      /*** If Storage Permission Is Given, Check External storage is available for read and write***/                      Uri image_uri = Uri.parse("https://unifiedclothes.com/Unifiedclothes/App_Gallery/thumb_8_121432471036-1432471036-SC-505.jpg");                      referenceID = DownloadImage(image_uri);                     } else {                      requestPermission();                 }              }              else{                 Toast.makeText(MainActivity.this,"Permission Is Granted..",Toast.LENGTH_SHORT).show();              }         }     });      registerReceiver(receiver,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); }  private BroadcastReceiver receiver = new BroadcastReceiver() {     @Override     public void onReceive(Context context, Intent intent) {          String action = intent.getAction();          if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)){                DownloadManager.Query ImageDownloadQuery = new DownloadManager.Query();             //set the query filter to our previously Enqueued download             ImageDownloadQuery.setFilterById(referenceID);              //Query the download manager about downloads that have been requested.             Cursor cursor = downloadManager.query(ImageDownloadQuery);              if(cursor.moveToFirst()){                  Toast.makeText(MainActivity.this,DownloadStatus(cursor),Toast.LENGTH_SHORT).show();             }            }      } };  private String DownloadStatus(Cursor cursor){      //column for download  status     int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);     int status = cursor.getInt(columnIndex);     //column for reason code if the download failed or paused     int columnReason = cursor.getColumnIndex(DownloadManager.COLUMN_REASON);     int reason = cursor.getInt(columnReason);        String statusText = "";     String reasonText = "";      switch(status){         case DownloadManager.STATUS_FAILED:             statusText = "STATUS_FAILED";             switch(reason){                 case DownloadManager.ERROR_CANNOT_RESUME:                     reasonText = "ERROR_CANNOT_RESUME";                     break;                 case DownloadManager.ERROR_DEVICE_NOT_FOUND:                     reasonText = "ERROR_DEVICE_NOT_FOUND";                     break;                 case DownloadManager.ERROR_FILE_ALREADY_EXISTS:                     reasonText = "ERROR_FILE_ALREADY_EXISTS";                     break;                 case DownloadManager.ERROR_FILE_ERROR:                     reasonText = "ERROR_FILE_ERROR";                     break;                 case DownloadManager.ERROR_HTTP_DATA_ERROR:                     reasonText = "ERROR_HTTP_DATA_ERROR";                     break;                 case DownloadManager.ERROR_INSUFFICIENT_SPACE:                     reasonText = "ERROR_INSUFFICIENT_SPACE";                     break;                 case DownloadManager.ERROR_TOO_MANY_REDIRECTS:                     reasonText = "ERROR_TOO_MANY_REDIRECTS";                     break;                 case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:                     reasonText = "ERROR_UNHANDLED_HTTP_CODE";                     break;                 case DownloadManager.ERROR_UNKNOWN:                     reasonText = "ERROR_UNKNOWN";                     break;             }             break;         case DownloadManager.STATUS_PAUSED:             statusText = "STATUS_PAUSED";             switch(reason){                 case DownloadManager.PAUSED_QUEUED_FOR_WIFI:                     reasonText = "PAUSED_QUEUED_FOR_WIFI";                     break;                 case DownloadManager.PAUSED_UNKNOWN:                     reasonText = "PAUSED_UNKNOWN";                     break;                 case DownloadManager.PAUSED_WAITING_FOR_NETWORK:                     reasonText = "PAUSED_WAITING_FOR_NETWORK";                     break;                 case DownloadManager.PAUSED_WAITING_TO_RETRY:                     reasonText = "PAUSED_WAITING_TO_RETRY";                     break;             }             break;         case DownloadManager.STATUS_PENDING:             statusText = "STATUS_PENDING";             break;         case DownloadManager.STATUS_SUCCESSFUL:             statusText = "Image Saved Successfully";             //reasonText = "Filename:\n" + filename;             Toast.makeText(MainActivity.this, "Download Status:" + "\n" + statusText + "\n" + reasonText, Toast.LENGTH_SHORT).show();             break;     }      return statusText + reasonText;   }   private long DownloadImage(Uri uri){      long downloadReference;      downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);      DownloadManager.Request request = new DownloadManager.Request(uri);     //Setting title of request     request.setTitle("Image Download");      //Setting description of request     request.setDescription("Image download using DownloadManager.");       request.setDestinationInExternalPublicDir(getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "/NewFile","sample2.jpg");     //request.allowScanningByMediaScanner();     request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);     downloadReference = downloadManager.enqueue(request);       return  downloadReference; }   private boolean checkPermission() {     int result = ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);     if (result == PackageManager.PERMISSION_GRANTED) {         return true;     } else {         return false;     } }   private void requestPermission() {      ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); }  @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {     super.onRequestPermissionsResult(requestCode, permissions, grantResults);      if (requestCode == PERMISSION_REQUEST_CODE) {          if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {              Uri image_uri = Uri.parse("https://www.dccomics.com/sites/default/files/Char_GetToKnow_Batman80_5ca54cb83a27a6.53173051.png");              referenceID = DownloadImage(image_uri);           }          else {              Toast.makeText(MainActivity.this, "Permission Denied... \n You Should Allow External Storage Permission To Download Images.", Toast.LENGTH_LONG).show();         }     } } 

}

It works well when i run it on any device below API 29(My testing device was Nexus 5X ,ApI 28 emulator). But when I run it on Nexus 5X ,API 29 The app gets crashed. Here is the Logs:

2019-09-24 20:51:46.354 11322-11344/? E/DatabaseUtils: Writing exception to parcel java.lang.IllegalStateException: Not one of standard directories: /storage/emulated/0/Android/data/com.blz.prisoner.downloadmanager/files/Pictures/NewFile     at com.android.providers.downloads.DownloadProvider.call(DownloadProvider.java:651)     at android.content.ContentProvider.call(ContentProvider.java:2152)     at android.content.ContentProvider$Transport.call(ContentProvider.java:477)     at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:277)     at android.os.Binder.execTransactInternal(Binder.java:1021)     at android.os.Binder.execTransact(Binder.java:994) 

2019-09-24 20:51:46.355 15023-15023/com.blz.prisoner.downloadmanager D/AndroidRuntime: Shutting down VM

--------- beginning of crash 2019-09-24 20:51:46.360 15023-15023/com.blz.prisoner.downloadmanager E/AndroidRuntime: FATAL EXCEPTION: main     Process: com.blz.prisoner.downloadmanager, PID: 15023     java.lang.IllegalStateException: Not one of standard directories: /storage/emulated/0/Android/data/com.blz.prisoner.downloadmanager/files/Pictures/NewFile         at android.os.Parcel.createException(Parcel.java:2079)         at android.os.Parcel.readException(Parcel.java:2039)         at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:188)         at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)         at android.content.ContentProviderProxy.call(ContentProviderNative.java:658)         at android.content.ContentProviderClient.call(ContentProviderClient.java:558)         at android.content.ContentProviderClient.call(ContentProviderClient.java:546)         at android.app.DownloadManager$Request.setDestinationInExternalPublicDir(DownloadManager.java:567)         at com.blz.prisoner.downloadmanager.MainActivity.DownloadImage(MainActivity.java:206)         at com.blz.prisoner.downloadmanager.MainActivity.access$200(MainActivity.java:29)         at com.blz.prisoner.downloadmanager.MainActivity$1.onClick(MainActivity.java:60)         at android.view.View.performClick(View.java:7140)         at android.view.View.performClickInternal(View.java:7117)         at android.view.View.access$3500(View.java:801)         at android.view.View$PerformClick.run(View.java:27351)         at android.os.Handler.handleCallback(Handler.java:883)         at android.os.Handler.dispatchMessage(Handler.java:100)         at android.os.Looper.loop(Looper.java:214)         at android.app.ActivityThread.main(ActivityThread.java:7356)         at java.lang.reflect.Method.invoke(Native Method)         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 

I think the problem is on line " request.setDestinationInExternalPublicDir(getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "/NewFile","sample2.jpg");" in DownloadImage(Uri uri) function. How to solve the problem??

Another Problem is when I run the app on the device below API 29 it runs well but when I click on notification after completing the download it doesn't open the image on gallery/on the folder it was saved.

like image 727
Bishal Imtiaz Avatar asked Sep 24 '19 14:09

Bishal Imtiaz


People also ask

How do I download from download manager on Android?

If you want to download large files/streaming you can use Android Download Manager. The download manager handles HTTP connections, monitors connectivity changes, reboots, and ensures each download completes successfully. First, we will have to generate URI from URL.

How do I download files on Android?

Go to the webpage where you want to download a file. Touch and hold what you want to download, then tap Download link or Download image. To see all the files you've downloaded to your device, open the Downloads app. Learn more about managing downloaded files.

Is there a download manager for Android?

Advanced Download Manager is one of the most popular download managers for Android. It has a clean UI, plenty of functionality, and support for all kinds of neat things. Some of the features include simultaneous downloads, support for SD cards, speed management, and support for very large files.


Video Answer


1 Answers

I solved just by using:

setDestinationInExternalFilesDir(context, relativePath, filename); 

Instead of:

setDestinationInExternalPublicDir(relativePath, filename); 

My relative path is:

Environment.getExternalStorageDirectory().getPath() + "MyExternalStorageAppPath" 

I also have in my manifest:

android:requestLegacyExternalStorage="true" 

To use Legacy storage management (Shared Storage) instead of new storage management (Scoped Storage) used from Android 10 and above.

Remember that by using "setDestinationInExternalFilesDir" files will be download to the external memory dedicated to your app, so: "external/Android/data/your_app_name/path_you_used_on_function". If you want to download it to another place you need to move It after you downloaded It by using Input & Output streams. To open the file with another app in Android version 10 or above you must use FileProvider.

If someone need it, this is the code to move (move, not copy. So the original file will be deleted. Remove "source.delete();" if you want to copy the file and not delete the source file) a file from one location to another:

public static boolean moveFile(File source, String destPath){         if(source.exists()){             File dest = new File(destPath);             checkMakeDirs(dest.getParent());             try (FileInputStream fis = new FileInputStream(source);                  FileOutputStream fos = new FileOutputStream(dest)){                 if(!dest.exists()){                     dest.createNewFile();                 }                 writeToOutputStream(fis, fos);                 source.delete();                 return true;             } catch (IOException ioE){                 Log.e(TAG, ioE.getMessage());             }         }         return false;     }  private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException {         byte[] buffer = new byte[1024];         int length;         if (is != null) {             while ((length = is.read(buffer)) > 0x0) {                 os.write(buffer, 0x0, length);             }         }         os.flush();     } 

Usage ("source" is the File you need to move, "path" is the destination):

if(FilesUtils.moveFile(source, path)) {      // Success Moving File, do what you need with it } 

Broadcast receiver for when the DownloadManager has finished:

private static class DownloadFileReceiver extends BroadcastReceiver {          private DownloadManager mDownloadManager;         private String mPath;          private DownloadFileReceiver(DownloadManager dManager, String path){             mDownloadManager = dManager;             mPath = path;         }          /** Override BroadcastReceiver Methods **/         @Override         public void onReceive(Context context, Intent intent) {             String action = intent.getAction();             if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {                 Bundle extras = intent.getExtras();                 DownloadManager.Query q = new DownloadManager.Query();                 q.setFilterById(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));                 Cursor c = mDownloadManager.query(q);                 if (c.moveToFirst()) {                     int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));                     if (status == DownloadManager.STATUS_SUCCESSFUL) {                         String fullPath = null; File source = null;                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {                             fullPath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));                             source = new File(Uri.parse(fullPath).getPath());                         } else {                             fullPath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));                             source = new File(fullPath);                         }                     }                 }                 c.close();             }             Objects.requireNonNull(context).unregisterReceiver(this);         }     } 

Register it to the DownloadManager instance:

context.registerReceiver(new DownloadFileReceiver(downloadManager, path),                 new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 

checkMakeDirs (this check if the dir exists or if it can make it successfully) and makeDirs (just make it without checking) code:

public static boolean checkMakeDirs(String dirPath){         try {             File dir = new File(dirPath);             return dir.exists() || dir.mkdirs();         } catch (Exception e) {             Log.e(TAG, e.getMessage());         }         return false;     }      public static void makeDirs(String dirPath){         try {             File dir = new File(dirPath);             if(!dir.exists()){                 dir.mkdirs();             }         } catch (Exception e){             Log.e(TAG, e.getMessage());         }     } 

Important:
from 05/07/2021 if your app is in the Google Play Store, theorically, you must targetSdk=30 and also you must use Scoped Storage only to access your files (so use only the app-specific directory for your app). This means you need to use:

context.getFilesDir();

like image 200
Z3R0 Avatar answered Oct 16 '22 12:10

Z3R0