I programmed an Android App that downloads a sample PDF file to the Download directory using DownloadManager. Here's the code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vertretungsplan);
Button dlbutton = (Button) findViewById(R.id.buttondownload);
final Context c = this;
dlbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
request.setTitle("Vertretungsplan");
request.setDescription("wird heruntergeladen");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
});
}
Now, I tried to debug-run it from Android Studio on my Xperia Z3 Android 6.0. As I clicked on the download button, this error occurs:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.markwitt.schul_app, PID: 29297
java.lang.SecurityException: No permission to write to /storage/emulated/0/Download/hrpsampletable.pdf: Neither user 10047 nor current process has android.permission.WRITE_EXTERNAL_STORAGE.
at android.os.Parcel.readException(Parcel.java:1627)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
at android.content.ContentResolver.insert(ContentResolver.java:1240)
at android.app.DownloadManager.enqueue(DownloadManager.java:946)
at com.example.markwitt.schul_app.Vertretungsplan$1.onClick(Vertretungsplan.java:59)
at android.view.View.performClick(View.java:5280)
at android.view.View$PerformClick.run(View.java:21239)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:5526)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Disconnected from the target VM, address: 'localhost:8600', transport: 'socket'
(on the third line) it displays me that it has no write permission on external storage.
But my AndroidManifest is set up correctly:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.markwitt.schul_app">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:launchMode="singleTop"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Stundenplan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Vertretungsplan"></activity>
</application>
What do I need to do?
You are getting this error because your app is running in Android 6.0(API level 23). From API level >= 23 you will need to check for the permission in run time. Your code is just fine for below level 23. So please check first if your user has given the permission to use the storage:
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.e("Permission error","You have permission");
return true;
}
If not then prompt the request:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
Total things looks like this:
public boolean haveStoragePermission() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.e("Permission error","You have permission");
return true;
} else {
Log.e("Permission error","You have asked for permission");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //you dont need to worry about these stuff below api level 23
Log.e("Permission error","You already have the permission");
return true;
}
}
And last thing :) receive the result by callback:
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
//you have the permission now.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(myurl));
request.setTitle("Vertretungsplan");
request.setDescription("wird heruntergeladen");
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
String filename = URLUtil.guessFileName(myurl, null, MimeTypeMap.getFileExtensionFromUrl(myurl));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
DownloadManager manager = (DownloadManager) c.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
}
Or, you can just use my custom class PermissionCheck
to handle all this implementation very easily. Here is the class:
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
/**
* Created by Tushar on 6/30/2017.
*/
public class PermissionCheck{
public static boolean readAndWriteExternalStorage(Context context){
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
return false;
}else{
return true;
}
}
public static boolean audioRecord(Context context){
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ){
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.RECORD_AUDIO}, 2);
return false;
}else{
return true;
}
}
public static boolean readAndWriteContacts(Context context){
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS}, 3);
return false;
}else{
return true;
}
}
public static boolean vibrate(Context context){
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.VIBRATE}, 4);
return false;
}else{
return true;
}
}
public static boolean sendSms(Context context){
if(ActivityCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.SEND_SMS}, 5);
return false;
}else{
return true;
}
}
//Just like this you can implement rest of the permissions.
}
usage:
if(PermissionCheck.readAndWriteExternalStorage(context)){
//Your read write code.
}
if(PermissionCheck.sendSms(context)){
//Your sms sending code.
}
**Notice that, these methods call will ask for the permissions automatically and also your onRequestPermissionsResult
will be fired. :)