Another Android Request Permission dialog not showing issue - android

I've spend a day on this issue, and I'm stumped. I've read through numerous similar questions, and tried all the suggestions, but nothing worked for me.
I have an app, with a preference screen where the user can turn on Bluetooth. The preference screen sends an intent to the MainActivity, which then checks security permissions and if needed requests permission.
I am building against version SDK version 32.
In Manifest I have:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
In MainActivity I check if permission is already granted:
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (Objects.equals(intent.getAction(), CustomIntent.CHECK_BLUETOOTH_SECURITY_PERMISSION)) {
int check = ContextCompat.checkSelfPermission( thisActivity, Manifest.permission.BLUETOOTH_CONNECT );
if ( check == PackageManager.PERMISSION_DENIED )
getPermission( Manifest.permission.BLUETOOTH_CONNECT, RequestCodes.BT_SECURITY_REQUEST.ordinal() );
}
and if not, I request permission.
When requesting permission, I have tried the old way with:
public void getPermission( String permission, int requestCode ) {
if ( shouldShowRequestPermissionRationale( permission ) ) {
// TODO: display a dialog to explain the reason for needing the permission
Toast.makeText( this, "Need to put together an explanation for the permission", Toast.LENGTH_LONG ).show();
} else {
requestPermissions( new String[]{ permission }, requestCode );
}
}
I have tried both with CompactActivity.requestPermissions and with just requestPermissions as above.
and I have also tried with the new way. In onCreate I have:
activityResultLauncher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
#Override
public void onActivityResult(Map<String, Boolean> result) {
Log.e("ActivityResultLauncher", ""+result.toString() );
Boolean areAllGranted = true;
for ( Boolean b : result.values() ) {
areAllGranted = areAllGranted && b;
}
if ( areAllGranted )
Log.e( "ActivityResultLauncher","It worked");
else
Log.e( "ActivityResultLauncher", "wah wah wah");
}
});
and in getPermission() i then call
activityResultLauncher.launch(new String[]{
Manifest.permission.BLUETOOTH_CONNECT
});
But no matter what I do, the user dialog asking the user to grant permission never show, and the onPermissionRequestResult just immediately return with False, i.e. Denied.
I don't see any error messages in the LogCat (outside of the log entries I put there myself). I've also tried to step through the Android libraries to see i can see any exception thrown, but all looks fine there as well.
UPDATE
Just as I posted this, I tried to clear the emulator device.
After that, the first time I ran the code, the app crashed, hard.
I was able to catch this in logCat
Attempting to launch an unregistered ActivityResultLauncher with >contract
androidx.activity.result.contract.ActivityResultContracts$RequestMultiplePermissions
and input [Ljava.lang.String. You must ensure the
ActivityResultLauncher is registered before calling launch().
I do register the activityResultLauncher in onCreate. So I'm not clear on why it doesn't know it's registered.
It's also weird that this error only happens first time I run the app after clearing device files.
Then I launched the app again, (without changing anything in my code) and this time it worked. The dialog was displayed, and I could grant permissions.
Anyone know what in the world is going on?

Related

Xamarin Forms - How to check setting of RequestIgnoreBatteryOptimizations permission on Android

(See UPDATE below)
I have a Xamarin Forms app on Android which uses the Xamarin.Essentials library.
The app requires to run in the background to be fed location data (not particularly relevant to the question in hand, but included for context), and so must not be put to sleep by any battery optimisations that the OS might attempt.
I know that the user can manually opt out specific apps from Battery Optimizations, but as it is so crucial to the successful operation of the app, I would like the app to be able to :
check the Battery Optimization Opt-out permission status to ensure it is appropriately set,
and/or
force Android to opt the app of any battery optimizations.
I have added an entry into AndroidManifest.xml, but it doesn't seem to help, with the newly-installed app defaulting to being Battery Optimized.
AndroidManifest.xml
The manifest contains the following entry:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Xamarin.Essentials
This library gives access to a number of permission-related info on the device, but Battery Optimizations doesn't seem to be one of them.
Device being used
I don't know if it's relevant, but I am testing on a Samsung Galaxy S20 Ultra.
Can anyone offer any advice?
UPDATE Aug 28 2021
Following the advice from contributors and with reference to the docs at https://learn.microsoft.com/en-us/xamarin/essentials/permissions?tabs=android#extending-permissions ...
In My Shared Code
public interface IRequestIgnoreBatteryOptimizationPermission
{
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
In My Android Project
[assembly: Xamarin.Forms.Dependency(typeof(RequestIgnoreBatteryOptimizationPermission))]
namespace MyAndroidProject
{
public class RequestIgnoreBatteryOptimizationPermission : Permissions.BasePlatformPermission, IRequestIgnoreBatteryOptimizationPermission
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)>
{
(Android.Manifest.Permission.RequestIgnoreBatteryOptimizations, true)
}.ToArray();
}
}
On Shared App Initialization
// Ensure Required Permissions have been granted
var requestIgnoreBatteryOptimizationPermission = DependencyService.Get<IRequestIgnoreBatteryOptimizationPermission>();
var status = requestIgnoreBatteryOptimizationPermission.CheckStatusAsync().Result;
if (status != PermissionStatus.Granted)
{
status = requestIgnoreBatteryOptimizationPermission.RequestAsync().Result;
}
Result...
On calling CheckStatusAsync, the result comes back as Granted.
But the app settings still say otherwise...
I've tried it on both a physical device (Samsung Galaxy S20 Ultra) and on an Android Emulator (Pixel 2 API 28), with same result on both.
From this document, there are two ways to set Battery Optimization
An app can fire the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent to take the user directly to the Battery Optimization, where they can add the app.
An app holding the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission can trigger a system dialog to let the user add the app to the exemption list directly, without going to settings. The app fires a ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS Intent to trigger the dialog.
I use PowserManager.isIgnoringBatteryOptimizations to check Battery Optimization.
Firstly, add RequestIgnoreBatteryOptimizations in AndroidManifest.xml
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Then creating Interface in Shared code.
public interface IBattery
{
void getbattery();
}
Implementing this interface in Android platform.
[assembly: Dependency(typeof(ImplementBattery))]
namespace FormsSample.Droid
{
public class ImplementBattery : IBattery
{
public void getbattery()
{
Intent intent = new Intent();
String packageName = MainActivity.mac.PackageName;
PowerManager pm = (PowerManager)MainActivity.mac.GetSystemService(Context.PowerService);
if (pm.IsIgnoringBatteryOptimizations(packageName))
intent.SetAction(Android.Provider.Settings.ActionIgnoreBatteryOptimizationSettings);
else
{
intent.SetAction(Android.Provider.Settings.ActionRequestIgnoreBatteryOptimizations);
intent.SetData(Android.Net.Uri.Parse("package:" + packageName));
}
MainActivity.mac.StartActivity(intent);
}
}
}
Creating static Mainactivity field in Mainactivity.cs.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity mac;
protected override void OnCreate(Bundle savedInstanceState)
{
initFontScale();
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
mac = this;
Now, using DependencyService to fire.
private void Button_Clicked(object sender, EventArgs e)
{
DependencyService.Get<IBattery>().getbattery();
}
In your MainActivity add two properties:
private const int RequestPermissionsId = 0
and
private readonly string[] ManifestPermissions =
{
Manifest.Permission.RequestIgnoreBatteryOptimizations
// Add here other permissions you want to check
}
Then override the OnStart method and force permission check there:
public override void OnStart()
{
base.OnStart();
if ((int)Build.VERSION.SdkInt >= 23)
{
if (CheckSelfPermission(Manifest.Permission.RequestIgnoreBatteryOptimizations != Permission.Granted)
RequestPermissions(ManifestPermissions, RequestPermissionsId);
}
}
And of course, remember that you must have the OnRequestPermissionsResult method implemented:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
Well, I am guessing you are trying to check it directly through shared code
The easiest way to do it would be to extend the needed permission in native using essentials.
Check the status and request permission through an interface:
public interface IRequestIgnoreBatteryOptimizations
{
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
Implement the native part for the same:
public class IgnoreBatteryOptimizationsPlatformPermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IRequestIgnoreBatteryOptimizations
{
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new (string, bool)[] { (Manifest.Permission.RequestIgnoreBatteryOptimizations, true) };
}
Then register it in your Native XF caller class(MainActivity, Appdelegate(for some other permission))
DependencyService.Register<IRequestIgnoreBatteryOptimizations, IgnoreBatteryOptimizationsPlatformPermission>();
And then in your XF class use this method:
public static async Task<PermissionStatus> CheckAndRequestBatteryOptimizations()
{
var batteryOptimizationsPermission = DependencyService.Get<IRequestIgnoreBatteryOptimizations>();
var status = await batteryOptimizationsPermission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
status = await batteryOptimizationsPermission.RequestAsync();
}
return status;
}
And then request for it whenever you like:
var status= await CheckAndRequestBatteryOptimizations();
More information here: https://learn.microsoft.com/en-us/xamarin/essentials/permissions?tabs=android#extending-permissions

On Android 8.1&Pixel 2, how to draw on top and check this permission?

Background
I've noticed that ever since Android O came, the function of Settings.canDrawOverlays had issues telling us if the app was granted with the draw-on-top permission (AKA "Display over other apps"), so I've reported about this:
https://issuetracker.google.com/issues/62047810
https://issuetracker.google.com/issues/68465333
The problem
Not sure why they were marked as fixed, but now that I've tested on my Pixel 2 with Android 8.1, I've noticed this function still ALWAYS returns false (so I've reported about this here) .
Thing is, I've tried finding an alternative and saw others also having this issue here on StackOverflow:
Settings.canDrawOverlays is returning false even after turning the permission on from settings
Settings.canDrawOverlays(context) returns false on Android Oreo
Why in Android O method Settings.canDrawOverlays() returns "false" when user has granted permission to draw overlays and returns to my application?
I don't know if this is an issue with Android 8.1, or just my Pixel 2, but I'm using the latest, stock rom version of it (OPM171019.013) .
What I've tried
I've tried all of the solutions that were suggested on the above links.
All of them return me the result that the permission is not granted, ever.
The only solution I've found that does something is this one, which just tells me that the permission was toggled.
One way to "solve" this, is to assume the permission is granted in case the app was installed via the Play Store, because it's granted by default when installing from there.
But this is not a good solution. Apps can be installed from outside of the Play Store, and the user can always turn off this permission manually.
EDIT: I don't know if that's even a possible "solution", because when I try on the app, I can't really draw on top, even if the permission is granted.
The questions
How can I check for sure if the current app is granted with this permission?
From which Android version does this issue occur? Or maybe it's an issue only for Pixel 2 ?
Seeing that even after granting the permission, the app fails to draw on top, is there a possible solution for it?
EDIT: Speaking with others, I think this is a very specific issue on Pixel 2 with Android 8.1 . The reason is that the issue doesn't seem to appear on other devices with Android 8.1. I hope it gets fixed.
I had the same issue, there is a problem with the function Settings.canDrawOverlays because that needs an app reboot to return true when you granted the permission, try these.
I have a button that calls a Config Activity to enable Overlay
public void setOverlayPermissions(){
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName()));
startActivityForResult(intent,app.REQUEST_ID_OVERLAY_PERMISSIONS);
}
After that, in onActivityResult funtion
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == app.REQUEST_ID_OVERLAY_PERMISSIONS){
new WaitingFor().execute();
}
}
And then
private class WaitingFor extends AsyncTask{
private ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setCancelable(false);
progressDialog.setMessage("Espera un momento...");
progressDialog.show();
}
#Override
protected Object doInBackground(Object[] objects) {
try{
Thread.sleep(2000);
}catch (Exception ex){
Log.e(TAG,ex.getMessage()+"");
}
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
progressDialog.dismiss();
if(verifyWindowOverlay()){
FL.w(TAG,"Acepto Overlay");
setPaso2();
}else{
FL.w(TAG,"NO acepto Overlay [Permission error]");
Toast.makeText(MainActivity.this,"Falta configuracion, reintenta!", Toast.LENGTH_SHORT).show();
}
}
}
My verifyWindowsOverlay function
public boolean verifyWindowOverlay(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
return Settings.canDrawOverlays(this);
if(appOpsMgr.checkOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, android.os.Process.myUid(), getPackageName()) == 0 || Settings.canDrawOverlays(this))
return true;
return false;
}
Good luck.

Adobe Air file run-time permission rejection doesn't dispatch an event

I'm using the relatively new Adobe Air run-time permissions for Android, to make use of the file system.
The problem is that when the user refuses to give permission which is essential, I would like to ask him again, yet the event handler is not being called upon rejection.
file.addEventListener(PermissionEvent.PERMISSION_STATUS, function(e:PermissionEvent):void {
// does not reach to this point if user declined permission request
if (e.status == PermissionStatus.GRANTED)
{
contRead();
}});
try {
file.requestPermission();
} catch(e:Error)
{
// another request is in progress
trace("REQUEST ERROR!!!");
}
How do I tackle it? Do you have a separate event handler?
I'm using Adobe Air 25.
Thanks.
You need to create a separate function, so it can be called multiple times whenever it's needed. Right now, in your code, it only exists once (when the evenListener is added).
Try something like this setup below :
file.addEventListener(PermissionEvent.PERMISSION_STATUS, checkPermissions);
try
{ file.requestPermission(); }
catch(e:Error)
{
// another request is in progress
trace( "REQUEST ERROR!!! : " + e.toString() );
}
function checkPermissions (e:PermissionEvent) : void
{
trace( "Status is : " + e.status.toString() );
// does not reach to this point if user declined permission request
if (e.status == PermissionStatus.GRANTED)
{ contRead(); }
}

Working example of getting permission from a Service in Android Marshmallow?

I am aware about the changes introduced in Android 6.0/SDKVersion 23 regarding the run-time permission. There is already discussion and talks on this topic in the below post which talks about the various aspects of new permission model.
"How to request permissions from a Service in Android Marshmallow"
Google I/O 2015 - Android M Permissions
"Mother, May I?" Asking for Permissions (Android Dev Summit 2015)
After going thorough these article,I believe below is suggested to "workaround" (as Service doesnot have UI support)this problem.
Check permission checkSelfPermission() in the context of
Service.
Notify it to status bar in case the permission is denied.
User would now click on status bar which would launch a new
DialogActivity to request for permission when users press on the
notification.
I am not able to make the workable version of the suggestions provided to achieve this task. When we launch DialogActivity to ask for permission, it would be assigned and available for that activity. It would not be applicable for background service who had put it on status bar and even after this service would not be granted this permission(instead the permission would be given to DialogActivity).
Could somebody provide the input(possibly workable logic/code) so that I can make it workable(.i.e. execute the code in the context of Service which would be dependent on whether permission is granted or not).
My Scenario
Develop application which would send the SMS at regular interval
regarding the my current location.
I have designed the UI for this application as mentioned below. It had some settings parameters and also buttons which would start the main logic which is:
Start monitoring the current location and
Send the SMS to configured contact numbers.
I believe user would minimize this application( as it does not have any interesting logic apart from sending the SMS) and it should continue to work in background(.i.e. monitoring the location and sending the SMS). Hence I thought to use move this logic in Service instead of keeping it the Activity itself. If I keep these logic in Activity itself, it works as expected however for that user is suppose to keep the application in foreground all the time.
So far I have been able to achieve the following:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Define START/STOP button handles.
mStartApp = (ImageButton)findViewById(R.id.startApp);
mStopApp = (ImageButton)findViewById(R.id.stopApp);
//Write event handlers for above grabbed buttons.
mStartApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent startService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
startService(startService);
}
});
mStopApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent stopService = new Intent(getApplicationContext(),
CurrentLocationTrackerService.class);
stopService(stopService);
}
});
// Support for the ActionBar widgets menu in current activity.
Toolbar mToolBar = (Toolbar)findViewById(R.id.myToolbar);
if(mToolBar != null) {
setSupportActionBar(mToolBar);
}
}
//Other methods.....
}
public class CurrentLocationTrackerService extends Service {
public CurrentLocationTrackerService() { }
#Override
public void onCreate() { }
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int permissionLocationAccess = ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION);
// If permissionLocationAccess is true execute further
// If permissionLocationAccess false(which would always as we are in Service and it is dangerous
// permission) we need to put some information to status bar. Post that user would click on that
// which would launch some new activity where UI of asking permission would be shown. But still
// Service would not get the permission( only new activity would have that permission but it is
// of no use here as I am planning to put these logic iside the Service as my app may not be always
// be in foreground)
}
}
Hope I have provided all detail regarding the problem and also my application context regarding why I require to model in this way.The real point over here is how to achieve this.
I am really stuck at this point and any help would be highly appreciated. Kindly let me know in case anything else is required from my side.

Duplicate permission request after orientation change

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.

Categories

Resources