I have an app that uses external storage to store photographs. As required, in its manifest, the following permissions are requested
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
and it uses the following to retrieve the required directory
File sdDir = Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US); String date = dateFormat.format(new Date()); storageDir = new File(sdDir, getResources().getString( R.string.storagedir) + "-" + date); // Create directory, error handling if (!storageDir.exists() && !storageDir.mkdirs()) { ... fails here
The app works fine on Android 5.1 to 2.3; it has been on Google Play for over a year.
Following an upgrade of one of my testing phones (Android One) to 6, it's now returning an error when trying to create the requisite directory, "/sdcard/Pictures/myapp-yy-mm".
The sd card is configured as "Portable storage". I've formatted the sd card. I've replaced it. I've rebooted. All to no avail.
Also, the built-in android screenshot functionality (via Power+Lower volume) is failing "due to limited storage space, or it isn't allowed by the app or your organisation".
Any ideas?
I faced the same problem. There are two types of permissions in Android:
Normal permissions are automatically approved by Android while dangerous permissions need to be approved by Android users.
Here is the strategy to get dangerous permissions in Android 6.0
Here is my case: I need to write to external storage.
First, I check if I have the permission:
... private static final int REQUEST_WRITE_STORAGE = 112; ... boolean hasPermission = (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED); if (!hasPermission) { ActivityCompat.requestPermissions(parentActivity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); }
Then check the user's approval:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_WRITE_STORAGE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //reload my activity with permission granted or use the features what required the permission } else { Toast.makeText(parentActivity, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show(); } } } }
You can read more about the new permission model here: https://developer.android.com/training/permissions/requesting.html
First i will give you Dangerous Permission List in Android M and Later version
Then give you example of how to request for permission in Android M and later version.
I ask user to WRITE_EXTERNAL_STORAGE permission.
First add permission in your android menifest file
Step 1 Declare requestcode
private static String TAG = "PermissionDemo"; private static final int REQUEST_WRITE_STORAGE = 112;
Step 2 Add this code when you want ask user for permission
//ask for the permission in android M int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission to record denied"); if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Permission to access the SD-CARD is required for this app to Download PDF.") .setTitle("Permission required"); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Log.i(TAG, "Clicked"); makeRequest(); } }); AlertDialog dialog = builder.create(); dialog.show(); } else { makeRequest(); } } protected void makeRequest() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); }
Step 3 Add override method for Request
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case REQUEST_WRITE_STORAGE: { if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission has been denied by user"); } else { Log.i(TAG, "Permission has been granted by user"); } return; } } }
Note: Do not forget to add permission in menifest file
BEST EXAMPLE BELOW WITH MULTIPLE PERMISSION PLUS COVER ALL SCENARIO
I added comments so you can easily understand.
import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.production.hometech.busycoder.R; import java.util.ArrayList; public class PermissionInActivity extends AppCompatActivity implements View.OnClickListener { private static final int REQUEST_PERMISSION_SETTING = 99; private Button bt_camera; private static final String[] PARAMS_TAKE_PHOTO = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int RESULT_PARAMS_TAKE_PHOTO = 11; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_permission_in); bt_camera = (Button) findViewById(R.id.bt_camera); bt_camera.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_camera: takePhoto(); break; } } /** * shouldShowRequestPermissionRationale() = This will return true if the user had previously declined to grant you permission * NOTE : that ActivityCompat also has a backwards-compatible implementation of * shouldShowRequestPermissionRationale(), so you can avoid your own API level * checks. * <p> * shouldShowRequestPermissionRationale() = returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the * user. * <p> * requestPermissions() = request for the permisssiion */ private void takePhoto() { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { Toast.makeText(this, "You should give permission", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } else { ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } } // This method return permission denied String[] so we can request again private String[] netPermisssion(String[] wantedPermissions) { ArrayList<String> result = new ArrayList<>(); for (String permission : wantedPermissions) { if (!hasPermission(permission)) { result.add(permission); } } return (result.toArray(new String[result.size()])); } private boolean canTakePhoto() { return (hasPermission(Manifest.permission.CAMERA) && hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)); } /** * checkSelfPermission() = you can check if you have been granted a runtime permission or not * ex = ContextCompat.checkSelfPermission(this,permissionString)== PackageManager.PERMISSION_GRANTED * <p> * ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible * implementation of requestPermissions() that you can use. * * @param permissionString * @return */ private boolean hasPermission(String permissionString) { return (ContextCompat.checkSelfPermission(this, permissionString) == PackageManager.PERMISSION_GRANTED); } /** * requestPermissions() action goes to onRequestPermissionsResult() whether user can GARNT or DENIED those permisssions * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RESULT_PARAMS_TAKE_PHOTO) { if (canTakePhoto()) { Toast.makeText(this, "You can take picture", Toast.LENGTH_SHORT).show(); } else if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) { final AlertDialog.Builder settingDialog = new AlertDialog.Builder(PermissionInActivity.this); settingDialog.setTitle("Permissioin"); settingDialog.setMessage("Now you need to enable permisssion from the setting because without permission this app won't run properly \n\n goto -> setting -> appInfo"); settingDialog.setCancelable(false); settingDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.cancel(); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, REQUEST_PERMISSION_SETTING); Toast.makeText(getBaseContext(), "Go to Permissions to Grant all permission ENABLE", Toast.LENGTH_LONG).show(); } }); settingDialog.show(); Toast.makeText(this, "You need to grant permission from setting", Toast.LENGTH_SHORT).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_PERMISSION_SETTING) { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } } } }
Special Case for Configuration change
It is possible that the user will rotate the device or otherwise trigger a configuration change while our permission dialog is in the foreground. Since our activity is still visible behind that dialog, we get destroyed and recreated… but we do not want to re-raise the permission dialog again.
That is why we have a boolean, named isInPermission, that tracks whether or not we are in the middle of requesting permissions. We hold onto that value in onSaveInstanceState()
:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(STATE_IN_PERMISSION, isInPermission); }
We restore it in onCreate()
. If we do not hold all of the desired permissions, but isInPermission is true, we skip requesting the permissions, since we are in the middle of doing so already.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With