I am new to android. I am trying to grant permissions from custom class. I have two interfaces and a singleton class, like this
public interface RequestPermissionsResultInterface
{
void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults);
}
public interface PermissionManagerInterface
{
void onPermissionGranted(String message, int requestCode);
void onPermissionDenied(String message, int requestCode);
}
public class PermissionManager
{
private Activity mActivity;
private static volatile PermissionManagerInterface mManagerInterface;
public static PermissionManager getInstance(Context context)
{
if (mPermissionManager == null)
{
synchronized (PermissionManager.class)
{
if (mPermissionManager == null)
{
mPermissionManager = new PermissionManager(context);
}
}
}
return mPermissionManager;
}
private boolean isReadStorageAllowed()
{
int result = ContextCompat.checkSelfPermission(this.mActivity, Manifest.permission.READ_EXTERNAL_STORAGE);
return ((result == PackageManager.PERMISSION_GRANTED));
}
public RequestPermissionsResultInterface askPermission(
Activity mActivity,
String permissionName,
final PermissionManagerInterface managerInterface,
final int requestCode)
{
boolean isReadExternalStorageAllowed = isReadStorageAllowed();
if(isReadExternalStorageAllowed == false)
{
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity, permissionName))
{
final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setMessage("Please allow all permissions in App Settings for additional functionality.");
builder.setCancelable(false);
builder.setPositiveButton("Allow", new DialogInterface.OnClickListener() {
public void onClick(#SuppressWarnings("unused") final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
managerInterface.onPermissionGranted("Permission Granted", requestCode);
}
});
builder.setNegativeButton("Deny", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
managerInterface.onPermissionDenied("Permission Denied", requestCode);
}
});
final AlertDialog alert = builder.create();
alert.show();
}
}
else managerInterface.onPermissionGranted("Permission Already Granted", requestCode);
}
}
And here is how I use this class in MainActivity
PermissionManager permManager = PermissionManager.getInstance();
permManager.askPermission(
MainActivity.this,
Manifest.permission.READ_EXTERNAL_STORAGE,
mPermissionManagerInterface, // assume this exists
EXTERNAL_STORAGE_PERMISSION_CODE);
Again.... it is not persistent. If I give permission, then stop and restart the app again....this is asking me for permission again. What could be wrong?? Thanks
If you are determined to go the "custom" method, you still need to handle certain things.
shouldShowRequestPermissionRationale is only true if you have previously requested a permission and was denied it. You seem to be using it to display a dialog saying "please grant permissions" but not actually asking the system to show the permission request dialog.
The command to actually request permissions is ActivityCompat.requestPermissions. Can't see this in your code. The user cannot grant permission unless you actually ask for them.
onRequestPermissionsResult is where you should be calling your interface to say permission was granted.
Full instructions are in the documentation.
https://developer.android.com/training/permissions/requesting.html
From Android Documentation
Note: When your app calls requestPermissions(), the system shows a standard dialog box to the user. Your app cannot configure or alter that dialog box.
I was trying to grant permission through custom dialog which was never going to work. This was only part of the problem.
I was not requesting permission in custom dialog code section (which would have been bad because I end up showing 2 dialogs: standard + my custom dialog). So I remove my custom dialog and stick to the standard one.
Kudos goes to kuffs because s/he put me in the right direction.
Related
I am facing a problem. Which doesn't occur when user has successfully given all the required permissions. It occurs when user had denied to permission. I show user a dialog to enable the permissions from Apps->AppName->Settings. I take the user there. But when user try to enable the any of the permissions, it shows the message. System Overlay Detected and doesn't allow user to enable any permissions. I searched google and Stackoverflow a lot but didn't find any solution. Some people solved the solution by disabling the toast, but I am not showing any toast.
Note: This problem only occurs when denied for any of the permission.
Here is my code
private void showPermissionDialog(String message, final int permissionCode){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Permission Required");
if(permissionCode == Constants.REQ_CODE_PERMISSION_SYSTEM_OVERLAY_WINDOW) {
builder.setMessage(Constants.getOverlayNotEnabledMessage(Constants.getInstance(this)));
} else {
builder.setMessage(message + " To enable go to apps, Select " +
getResources().getString(R.string.app_name) + " Enable permissions now?");
}
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
if(permissionCode == Constants.REQ_CODE_PERMISSION_READ_CONTACTS){
MainActivity.this.finish();
}
}
});
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if(permissionCode == Constants.REQ_CODE_PERMISSION_SYSTEM_OVERLAY_WINDOW) {
requestOverlayPermissions();
} else {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
intent.setData(uri);
MainActivity.this.startActivityForResult(intent, permissionCode);
}
}
});
AlertDialog alertDialog = builder.create();
alertDialog.setCancelable(false);
alertDialog.show();
}
In android M, there is no solution to this problem. If your application require SYSTEM_OVERLAY_WINDOW permission you cannot enable or disable permissions from Settings. The only solution (No solution at developer side) is clear app data, this will again request permissions
I am following the example of Pokemon Go app to show a window message likes
You need to [Location, Camera,...] access to use this application. Please allow access in this application [Setting]. Click [Cancel] to exit from this application
When I click the Setting, the application will go to Setting window of this application and I can accept all Permissions.
The Setting window can access by using the code
public void gotoSetting(){
final Intent i = new Intent();
i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + getApplicationContext().getPackageName()));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(i);
}
There are my permisions which are stored in a String array
String[] PERMISSIONS = {Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA}
I used this code to check if all permission are checked, otherwise it returns false
public static boolean hasAllPermissions(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;
}
However, I got an issue that event I did not check all permissions, but the application did not go to Setting again. In additions, I can show an window message to notify if the user did not check all permission. Finally, This is my code
private AlertDialog buildNotificationServiceAlertDialog(){
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("Pemissions Setting");
alertDialogBuilder.setMessage("You need to [Location, Camera,...] access to use this application. Please allow access in this application [Setting]. Click [Cancel] to exit from this application");
alertDialogBuilder.setPositiveButton("Setting",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
gotoSetting();
}
});
alertDialogBuilder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// If you choose to not enable the notification listener
// the app. will not work as expected
}
});
return(alertDialogBuilder.create());
}
In onCreate I have
private AlertDialog enableNotificationListenerAlertDialog;
if(!hasAllPermissions(this, PERMISSIONS)){
enableNotificationListenerAlertDialog = buildNotificationServiceAlertDialog();
enableNotificationListenerAlertDialog.show();
}
Update: Solved:. I was missing to call the hasAllPermissions in the onResume() function. Finally, my solution is
#Override
protected void onResume() {
super.onResume();
if(hasAllPermissions(this, PERMISSIONS)==false){
enableNotificationListenerAlertDialog = buildNotificationServiceAlertDialog();
enableNotificationListenerAlertDialog.show();
}
}
I read your question carefully and i notice one mistake in your first line of code.As your code start with "public voide gotoSetting()",The mistake is in the keyword spelling.By correcting, it can be written as "public void gotoSetting()".
I hope this will help you.
Hi I'm Using Easy Permission library to handle android 6+ permission.
There is a method to call when "never ask again" is check
EasyPermissions.checkDeniedPermissionsNeverAskAgain
I dont know what parameter should we pass to the method
This is the method definition
public static boolean checkDeniedPermissionsNeverAskAgain(final Object object,
String rationale,
#StringRes int positiveButton,
#StringRes int negativeButton,
#Nullable DialogInterface.OnClickListener negativeButtonOnClickListener,
List<String> deniedPerms) {
boolean shouldShowRationale;
for (String perm : deniedPerms) {
shouldShowRationale = shouldShowRequestPermissionRationale(object, perm);
if (!shouldShowRationale) {
final Activity activity = getActivity(object);
if (null == activity) {
return true;
}
AlertDialog dialog = new AlertDialog.Builder(activity)
.setMessage(rationale)
.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
startAppSettingsScreen(object, intent);
}
})
.setNegativeButton(negativeButton, negativeButtonOnClickListener)
.create();
dialog.show();
return true;
}
}
return false;
}
But how to we pass the Stringres. Any help is much appreciate. Thanks
It just wants a resource value from your strings.xml file. So just pass something like R.string.okay and R.string.cancel. If you don't have a strings.xml file setup (you probably should) but could also use system defaults and pass android.R.string.ok and android.R.string.cancel to that method. There are several built in system strings like that. Look here if you're curious.
Basically, I want to create a wrapper for my application to handle the runtime permission checks using my own callback. I want to do some equivalent of adding a new callback to the requestPermissions method or Overriding the Activity method onRequestPermissionsResult in this function so I don't have to worry about application states. Is this possible?
I know that there are other ways to manage this using the requestCode and Fragments but I'm just curious about this as a possibility.
See example:
interface IPermissionResponse {
void permissionGranted();
void providePermissionRationale();
void permissionDenied();
}
private final static int REQUEST_INTERNET = 1;
#SuppressLint("NewApi")
public static void checkInternetPermission(Activity activity, boolean giveRationale, IPermissionResponse callback) {
if(!platformVersionCheck()){
// not android M, proceed with permission granted
callback.permissionGranted();
return;
}
Context nContext = activity.getApplicationContext();
// android M things...
if(nContext.checkSelfPermission(Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED){
// permission granted
callback.permissionGranted();
} else {
// permission not granted
// provide additional rationale to the user if the permission was not granted
// and the user would benefit from additional information about the use of the permission
if(giveRationale && activity.shouldShowRequestPermissionRationale(Manifest.permission.INTERNET)){
callback.providePermissionRationale();
}
// request the permission
activity.requestPermissions(new String[] {Manifest.permission.INTERNET}, REQUEST_INTERNET
/*, new IRequestPermissionCallback(){
// create new callback here
// if accepted, callback.permissionGranted
// if denied, callback.permissionDenied
}*/);
// OR SOMETHING LIKE
// handle the result callback ?????
// activity.onRequestPermissionsResult(........){
// // create new callback here
// // if accepted, callback.permissionGranted
// // if denied, callback.permissionDenied
// };
}
}
private static boolean platformVersionCheck() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
The RxPermission maybe helpful.
RxPermissions.getInstance(activity)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean grant) {
if (grant) {
save(); // granted
} else {
Snackbar.make(mContainer, getString(R.string.no_permission),
Snackbar.LENGTH_LONG).show();
}
}
});
No, there isn't any callback. To have an automatic management you can look here: Permission library
Because the Android SDK 23 gives users the possibility to deny apps access to certain functionalities I wanted to update one of my apps to request permissions as it is described in here: https://developer.android.com/preview/features/runtime-permissions.html.
In one of the activities I embed a SupportMapFragment. To make it work you need to have the WRITE_EXTERNAL_STORAGE permission, so I request it when I start the activity which results in a creation of a permission request dialog.
Now the problem is that when the dialog is still open and I rotate the device the activity will be restarted and open a new permission request dialog while the old one is still there. The result is two of those dialogs on top of each other and only one of it being useful.
Is there a way to get rid of the dialog that was started first?
As CommonsWare said in his comment the best solution is to put a boolean into the savedInstanceState-Bundle to know if the dialog is still open.
Example:
// true if dialog already open
private boolean alreadyAskedForStoragePermission = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
alreadyAskedForStoragePermission = savedInstanceState.getBoolean(STORAGE_PERMISSION_DIALOG_OPEN_KEY, false);
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY, alreadyAskedForStoragePermission);
}
private void checkStoragePermission(){
if(alreadyAskedForStoragePermission){
// don't check again because the dialog is still open
return;
}
if(ActivityCompat.checkSelfPermission(this, STORAGE_PERMISSIONS[0]) != PackageManager.PERMISSION_GRANTED){
// the dialog will be opened so we have to keep that in memory
alreadyAskedForStoragePermission = true;
ActivityCompat.requestPermissions(this, STORAGE_PERMISSIONS, STORAGE_PERMISSION_REQUEST_CODE);
} else {
onStoragePermissionGranted();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode){
case STORAGE_PERMISSION_REQUEST_CODE:
// the request returned a result so the dialog is closed
alreadyAskedForStoragePermission = false;
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
onStoragePermissionGranted();
}
break;
}
}
As #user1991776 mentioned there is actually an undocumented extra that contains whether or not there is a permission dialog open at the moment, in Activity:
private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY =
"android:hasCurrentPermissionsRequest";
However there is a better way. When you request a permission dialog the second time (due to a rotation), Activity automatically cancels the old dialog by calling your onRequestPermissionResult() with empty arrays:
public final void requestPermissions(#NonNull String[] permissions, int requestCode) {
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can reqeust only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
Or course this behaviour isn't documented because this is Android, and who wants to document complex behaviour?
Anyway you can just always request permissions in onCreate() and then ignore calls to onRequestPermissionsResult() with zero-length permissions arrays.
I guess as this is a system dialog you cannot control it. You could instead prevent that your activity gets reloaded if you turn your device.