If the targetSdkVersion is lower than 23 then the standard permissions system will be used on all devices including Android 6+, however soon all Google Play apps will need to set the targetSdkVersion to 26+.
B4A v6.0 adds support for runtime permissions. The nice thing about runtime permissions is that the user is not asked for any permission when they install your application from Google Play. Instead they will be asked to approve "dangerous" permissions at runtime.
Luckily most permissions are not considered dangerous. You can see the list of permissions that are considered dangerous here: https://developer.android.com/guide/topics/permissions/overview.html#permission-groups
Requesting permissions at runtime
Sub Process_Globals Private rp As RuntimePermissions End Sub Sub Globals Private gmap As GoogleMap Private MapFragment1 As MapFragment End Sub Sub Activity_Create(FirstTime As Boolean) Activity.LoadLayout("1") End Sub Sub MapFragment1_Ready gmap = MapFragment1.GetMap rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION) End Sub Sub Activity_PermissionResult (Permission As String, Result As Boolean) If Permission = rp.PERMISSION_ACCESS_FINE_LOCATION Then gmap.MyLocationEnabled = Result End If End Sub
The method (simplified) logic:
If <user has already approved> Or <older device> Then Activity_PermissionResult (Permission, True) Else ShowDialog Activity_PermissionsResult (Permission, Dialog result) End If
The CheckAndRequest method can only be called from an Activity.
There is another method named Check that only tests whether the permission has already been approved or not. This method can be called from any module.
It might be tempting to first test whether there is a permission and only if there is no permission call CheckAndRequest. However it will just make the program flow more complicated as you will need to deal with all cases anyway.
As a general rule, you shouldn't call RuntimePermissions.Check from an Activity. It will be simpler to always call CheckAndRequest.
Listing the permissions
Not many are aware to the fact that you can see the project permissions by clicking on the List Permissions button that is inside the Logs tab:
The dangerous permissions are marked with * (in B4A v6+).
You don't need to ask for non-dangerous permissions.
READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE
This is the most common dangerous permission. It is added automatically when you use File.DirDefaultExternal or File.DirRootExternal.
However there is a simple workaround for this.
1. Use RuntimePermissions.GetSafeDirDefaultExternal("") instead of File.DirDefaultExternal. The parameter passed is an optional subfolder that will be created under the default folder.
2. Add this code to the manifest editor:
AddManifestText( <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="19" /> )
You only need to deal with WRITE_EXTERNAL_STORAGE at runtime if you need access to a folder other than the app's default external folder.
Notes & tips:
- You can only request permissions that were declared in the manifest file (this is usually taken care by the compiler).
- Testing the permissions can be confusing as the user only needs to give permissions once. The solution is to uninstall the app from the device. Click on Ctrl + P (clean project) in the IDE and run again.
- The user actually approves groups of permissions. So if the user has approved the read contacts permission they will not be asked to approve the write contacts permission.
- Once you've uploaded your app to Google Play with targetSdkVersion set to 23 you cannot downgrade the target version back.
- Some Android 4.4 (API 19) devices do not allow access to RuntimePermissions.GetSafeDirDefaultExternal without explicit permission although they should. It it therefore recommended to set android:maxSdkVersion to 19 in the version based permission. It was previously set to 18.