How can I get read_phone_state permission at runtime to get IMEI number?
if not HasPermission('android.permission.READ_PHONE_STATE') then
begin
//ASK AND GET PERMISSION ?
end;
function TForm1.HasPermission(const Permission: string): Boolean;
begin
//Permissions listed at http://d.android.com/reference/android/Manifest.permission.html
{$IF RTLVersion >= 30}
Result := TAndroidHelper.Context.checkCallingOrSelfPermission(
{$ELSE}
Result := SharedActivityContext.checkCallingOrSelfPermission(
{$ENDIF}
StringToJString(Permission)) =
TJPackageManager.JavaClass.PERMISSION_GRANTED;
end;
EDIT: Sorry I didn't do a little more homework on FireMonkey. This is what I get for sticking my head into topics where it doesn't belong. I've added this content to try to make my answer more deserving of the bounty.
If you can restrict the targetSdk
on the app manifest to 22 (5.1 Lollipop) then the user will have to grant the permission on install so HasPermission
should never return false. (Not sure how that works with FireMonkey).
If you want to use the dynamic permissions capabilities in Marshmallow+, here is some information that I gleaned from this page:
You need to have access to the Activity
callback method onRequestPermissionsResult
. Here's all the hoops you will have to jump through:
classes.dex
file from Delphi back to Java so you can compile against the FMXNativeActivity
class.FMXNativeActivity
in Java that defines a native
method (let's call it onRequestPermissionsResultNative
and also overrides the onRequestPermissionsResult
method to call through to the native method.javac
to get a .class file with your subclassjar
to put the .class file into a .jar filedx.bat
to turn your .jar file into an Android .dex fileDexMerger
to merge your .dex file into Delphi's classes.dex fileonRequestPermissionsResultNative
method and register it with the JNI Environment. Oh, and don't forget to switch to the correct thread in your native method.The link I referenced shows how to do this with onActivityResult
. You'll have to adapt these steps for the other method.
And I haven't even talked about how to handle the OS pausing your app to ask the user for permission and resuming it after.
Let this be a lesson to all: Don't put your faith in cross-platform tools; you will be disappointed.
I work in Java not Delphi, so you'll have to extrapolate a little bit here.
Like you, I have to get the IMEI number, and the system dialog asks the user something like: "Allow app to make and manage phone calls?" I need to explain to the user that that app is just getting the device ID and isn't going to make or manage phone calls. So
I should mention that shouldShowRequestPermissionRationale
and requestPermissions
are methods on the Activity
class.
private static final int READ_PHONE_STATE_PERMISSIONS_REQUEST = 2;
private boolean mHasReadRationale;
void doPermissionsStuff() {
// version checking code omitted, this block runs for marshmallow and later
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
// do the operation that needs the permission here
} else {
// the flag indicates if the rationale dialog has already been displayed
if (! mHasReadRationale && shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_STATE)) {
// pop a dialog that explains what's going on to the user
} else {
requestPermissions(new String[] {Manifest.permission.READ_PHONE_STATE}, READ_PHONE_STATE_PERMISSIONS_REQUEST);
}
}
}
In the positive button of this dialog (i.e. user wants to continue) set the mHasReadRationale
flag to true and call doPermissionsStuff
again. (For Cancel, I send the user back to the previous screen.)
In order to get the result of the requestPermissions
operation you need to override the Activity
's onRequestPermissionsResult
method:
private boolean mPermissionDenied;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case READ_PHONE_STATE_PERMISSIONS_REQUEST:
// I'm only checking for one permission, so I make assumptions here
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// you can do the operation that needs the permission now
} else {
mPermissionDenied = true; // set a flag for checking later
}
}
}
Apparently when the system asks the user for the permission, it stops your app, so you can't show a UI at that point to tell the user you don't have permission. So I set a flag and when the app resumes, then I tell the user that the app doesn't have permission to do the operation.
@Override
protected void onResumeFragments() {
super.onResumeFragments();
if (mPermissionDenied) {
// show dialog to the user that the app can't do the operation because it doesn't have permission
mPermissionDenied = false;
}
}
So here's an example flow:
doPermissionsStuff()
.checkSelfPermission()
and determines the permission isn't granted yetshouldShowRequestPermissionRationale()
. In my experience, shouldShowRequestPermissionRationale()
only returns true after the user has denied the permission once. So you don't display the rationale UI to the user yet.requestPermissions()
onRequestPermissionsResult()
is called with the deny result and the mPermissionDenied
gets set.onResumeFragments()
gets called and a dialog is displayed to the user that they can't get the free trial because the app doesn't have permission.doPermissionsStuff()
is called.checkSelfPermission()
and (again) determines the permission isn't granted yetshouldShowRequestPermissionRationale()
. This time it returns true.mHasReadRationale
flag is set to true and doPermissionsStuff()
method gets called again.checkSelfPermission()
and — guess what? the permission isn't granted yetrequestPermissions()
onRequestPermissionsResult()
is called with the granted result and the free trial registration moves forward.You should also check out Google's sample code at https://developer.android.com/samples/RuntimePermissions/index.html
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