In my Android app using Java, I define a checkForPermission() method to check the grant status of a single permission. If the permission has not already been granted, it requests this permission from the user using ActivityCompat.requestPermissions().
Here is the method:
private boolean checkForPermission(String permission, int rationaleMessageId, int requestCode) {
// Check if permission is already granted.
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
// Permission has not been granted. Check if a rationale AlertDialog should be shown.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) {
// Show rationale AlertDialog explaining why this permission is needed.
new AlertDialog.Builder(this)
.setTitle(R.string.rationale_dialog_title)
.setMessage(rationaleMessageId)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
// Request permission again.
ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);
}
})
.create()
.show();
}
// Do not show rationale AlertDialog. Just request permission again.
else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode);
}
return false;
}
return true;
}
I've noticed that the method works perfectly fine for simple permissions such as android.permission.WRITE_EXTERNAL_STORAGE, but does not work for more dangerous permissions such as android.permission.WRITE_SETTINGS.
I'm wondering which types of permissions ActivityCompat.requestPermissions() may actually request. My assumption is that dangerous permissions must be requested using other means, but I would like any feedback I can get on this. Thank you.
Specifically, requestPermissions() is used for permissions with a protectionLevel of dangerous.
WRITE_EXTERNAL_STORAGE is dangerous. WRITE_SETTINGS is not.
Related
so, I tried to get a permission with the new registerForActivityResult() method and ask for with button click with .launch() and it doesn´t seem to be opening any window to ask for it.
I´m always getting false in registerForActivityResult().
// Permission to get photo from gallery, gets permission and produce boolean
private ActivityResultLauncher<String> mPermissionResult = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
new ActivityResultCallback<Boolean>() {
#Override
public void onActivityResult(Boolean result) {
if(result) {
Log.e(TAG, "onActivityResult: PERMISSION GRANTED");
} else {
Log.e(TAG, "onActivityResult: PERMISSION DENIED");
}
}
});
// Launch the permission window -- this is in onCreateView()
floatingActionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mPermissionResult.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
});
This is my LOG always: onActivityResult: PERMISSION DENIED
UPDATE
This answer works, but I found a better solution for permission requests with no open holes here.
From docs:
In your Activity/Fragment, create this field:
// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher, as an instance variable.
private ActivityResultLauncher<String> requestPermissionLauncher =
registerForActivityResult(new RequestPermission(), isGranted -> {
if (isGranted) {
// Permission is granted. Continue the action or workflow in your
// app.
} else {
// Explain to the user that the feature is unavailable because the
// features requires a permission that the user has denied. At the
// same time, respect the user's decision. Don't link to system
// settings in an effort to convince the user to change their
// decision.
}
});
Somewhere in the same Activity/Fragment:
if (ContextCompat.checkSelfPermission(
context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected. In this UI,
// include a "cancel" or "no thanks" button that allows the user to
// continue using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
If you are getting unreasonable "Permission denied" all the time, maybe you did not declare it in your manifest.xml?
Looking at Update to androidx.fragment:fragment:1.3.0-alpha08: registerForActivityResult not allowed after onCreate anymore. How to use after onCreate?,
private lateinit var checkLocationPermission: ActivityResultLauncher<Array<String>>
// Initialize checkLocationPermission in onAttach or onCreate.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkLocationPermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true ||
permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true) {
initUserLocation()
} else {
// Permission was denied. Display an error message.
}
}
}
fun showMap() {
if (ActivityCompat.checkSelfPermission(requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(requireContext(),
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
initUserLocation()
} else {
checkLocationPermission.launch(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
}
}
private fun initUserLocation() {
googleMap?.isMyLocationEnabled = true
}
In phone settings permissions are granted. Phone with Android 8.0.
compileSdkVersion 28
minSdkVersion 16
targetSdkVersion 28
Manifest has:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
In my code this "permissions" variable equals -1 always:
int permissions = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
Starting with Android 6 (API 23) you must also request this permission at runtime. This video may help: https://youtu.be/WGz-alwVh8A
Ok, thanks to Larry Schiefer for runtime permissions.
Considering to android docs:
link 1
link 2
I made some changes to the method from link 2. Before doing something it checks permissions and if necessary asks user for confirmation, in other case it sets permissions forcibly.
public void requestRuntimePermission(final Context context, final String manifestPermission, final int requestCode) {
int permissions = ContextCompat.checkSelfPermission(context, manifestPermission);
if (permissions != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context,
manifestPermission)) {
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(context);
builder.setMessage("Allow access?")
.setCancelable(true)
//Cancel
.setNegativeButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
})
//Ok
.setPositiveButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ActivityCompat.requestPermissions((Activity) context,
new String[]{manifestPermission},
requestCode);
}
});
android.support.v7.app.AlertDialog alert = builder.create();
alert.show();
} else {
// Permission has not been granted yet. Request it directly.
ActivityCompat.requestPermissions((Activity) context, new String[]{manifestPermission},
requestCode);
}
}
}
Example of usage:
requestRuntimePermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE, 0);
P.S. requestCode parameter in requestRuntimePermission can be used to identify which of permission allowed in onRequestPermissionsResult callback. (link 3)
I'm trying to test the Runtime Permissions specifically for Android sdk > 23. But my app is being granted permissions automatically without asking.
Note : I'm using sdk version 24. Here's a snippet of code I'm using:
public void onCalendarClick(View view) {
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR) == PackageManager
.PERMISSION_DENIED) {
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_CALENDAR)) {
//Display Explanation to the user
//For granting permissions to the app.
}
else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_CALENDAR}, CALLBACK_CALENDAR);
}
}
}
#Override
public void onRequestPermissionsResult(int resultCode, String permission[], int grantResults[]) {
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast toast;
switch (resultCode) {
case CALLBACK_CALENDAR : toast = Toast.makeText(this,"Calendar Permission Granted!!",Toast.LENGTH_LONG);
toast.show(); break;
//Other Cases
}
}
}
When I click on Calendar Button, the onCalendarClick() method run, but without asking for any permission, the App directly displays Calendar Permission Granted!! toast. In the App's settings, there are though No Permissions Granted/Requested being displayed.
Am I missing something or doing it the wrong way? Thanks for any help.
So here it is. I found out that for android sdk > 22, though Runtime permissions are added programatically for your application but you still need to declare your app's permission in the AndroidManifest.xml file. So, after adding the code:
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
in the AndroidManifest.xml, the app asks for the permission and it's finally working.
For more information : Android M permission dialog not showing
.Thanks to all for helping me out )
You are missing the order of the code. Check this:
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case CALLBACK_CALENDAR: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// calendar-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
There is a little difference, you are asking if the permission is granted even before you know you are talking about CALENDAR permissions. So, you should first check if the current permission response is the one you want, and then check if the permission is granted.
Source: https://developer.android.com/training/permissions/requesting.html
Below code is working fine on pre-Marshmallow devices but not in Marshmallow.
These are the permissions in Manifest
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Here is the code
public void saveImageToSDCard(Bitmap bitmap) {
File myDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
pref.getGalleryName());
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Wallpaper-" + n + ".jpg";
File file = new File(myDir, fname);
if (file.exists())
file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
Uri uri = getImageContentUri(_context,file);
Log.d(TAG, "Wallpaper saved to: " + file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
And the same code works when I manually allow the storage permission
Here is the solution given by Nitesh Pareek.
private boolean hasPermissions(Context context, String[] permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
String[] PERMISSIONS = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!hasPermissions(this, PERMISSIONS)) {
ActivityCompat.requestPermissions(this, PERMISSIONS, 11);
return;
}
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app.
This is why it works in pre-lolipop versions, and doesn't on API 23. Permissions in Android Manifest alone are not enough, you need to add them at runtime as well. Refer here for more details.
give read write permissions on run time for marshmallow or newer version.
Do like below:-
String[] PERMISSIONS = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (!hasPermissions(this, PERMISSIONS)) {
ActivityCompat.requestPermissions(this, PERMISSIONS, 11);
return;
}
private boolean hasPermissions(Context context, String... permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
You need to take application permissions at runtime instead of taking when install/update as convention
Beginning in Android 6.0 (API level 23), users grant permissions to
apps while the app is running, not when they install the app. This
approach streamlines the app install process, since the user does not
need to grant permissions when they install or update the app
For more help: Requesting Permissions at Run Time
By focusing on the documentation and after doing some google searches, finally I have compiled the code below to handle runtime permissions efficiently
To make it work, you need to follow the instructions below:
Call this method to check if storage permission is granted by user?
If not, then you need to request for it
public static boolean isStoragePermissionGranted(Activity activity) {
boolean flag = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flag = activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
return flag;
}
Call this method to request storage permission
public static void requestStoragePermission(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isStoragePermissionGranted(activity)) {
return;
}
// Fire off an async request to actually get the permission
// This will show the standard permission request dialog UI
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_STORAGE_PERMISSION);
}
}
Implement this method in your activity to handle response of permission callback
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE_STORAGE_PERMISSION:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
boolean shouldShowRationale = shouldShowRequestPermissionRationale(permissions[0]);
if (!shouldShowRationale) {
// user denied flagging NEVER ASK AGAIN, you can either enable some fall back,
// disable features of your app or open another dialog explaining again the permission and directing to
// the app setting
dialogReasonStoragePermissionToSettings(this);
} else if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permissions[0])) {
// user denied WITHOUT never ask again, this is a good place to explain the user
// why you need the permission and ask if he want to accept it (the rationale)
dialogReasonStoragePermission(this);
}
} /*else {
// Do on permission granted work here
}*/
}
}
break;
}
}
public static void dialogReasonStoragePermission(final Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(activity.getString(R.string.reason_storage_permission));
builder.setCancelable(false);
builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
requestStoragePermission(activity);
}
});
builder.setNegativeButton("Dismiss", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
public static void dialogReasonStoragePermissionToSettings(final Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(activity.getString(R.string.reason_storage_permission));
builder.setCancelable(false);
builder.setPositiveButton("Go to Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
goToAppDetailsForPermissionSettings(activity);
}
});
builder.setNegativeButton("Dismiss", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private static final int REQUEST_CODE_APP_DETAILS_PERMISSION_SETTING = 3995;
private static void goToAppDetailsForPermissionSettings(Activity activity) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent, REQUEST_CODE_APP_DETAILS_PERMISSION_SETTING);
}
I am not providing you direct code for this but here is a reason API level 23 introduce a new Permission structure for more security below is a short but wast description of thing, in documentation here
Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. This approach streamlines the app install process, since the user does not need to grant permissions when they install or update the app. It also gives the user more control over the app's functionality; for example, a user could choose to give a camera app access to the camera but not to the device location. The user can revoke the permissions at any time, by going to the app's Settings screen.
Code is good just you have to put something additional and that is Runtime Permissions for storage.
Read this blog to know everything from deep inside about Runtime Permissions gave me a clear picture about it, hope it helps you too.
Thanks
My application asks for permission when I first installed the app, however if I deny the permission and open my app again, it doesn't ask for permission and gets stuck at the
if (ActivityCompat.shouldShowRequestPermissionRationale(Script_list.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE))
I dont quite understand what does this actually do as the android's documentation says it's supposed to tell users why the permission is requested
Am I supposed to do ActivityCompat.requestPermissions in it too?
if (ContextCompat.checkSelfPermission(Script_list.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(Script_list.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(Script_list.this, "PERMISSION NOT DETECTED", Toast.LENGTH_SHORT).show();
} else {
ActivityCompat.requestPermissions(Script_list.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
}
}
else
{
Toast.makeText(Script_list.this, "USER ALREADY ALLOWED", Toast.LENGTH_SHORT).show();
setListView();
}