****IMPORTANT****: Please use the solution presented in my follow-up article at this link, in preference to the one presented here. This article is retained here for reference only.
Note: The demo in this article was devised with Delphi Tokyo 10.2.3, however it may work with earlier versions.
UPDATE: Brian Long has alerted me to the fact that using this solution, the status bar just appears as a white rectangle (i.e. no icons, time etc), something I should have noticed in my testing (too focused on problem at hand!). I’ll be looking into how to resolve this.
UPDATE 2: The files have now been updated to include an interim solution for the status bar problem. This includes:
The interim solution involves setting the status bar translucent, which results in the total screen space for the app to include the status bar area, so there is now a rectangle at the top of the main form to account for this.
UPDATE 3: I’ve created another article that presents an alternative solution to “overriding” the activity, includes a workaround for an issue with TTakePhotoFromCameraAction, and an alternative solution for the status bar issue.
Up until now, by default, Delphi targets Android 4.0 (API level 14, otherwise known as Icecream Sandwich). The value (14) is inserted as the targetSdkVersion value in the AndroidManifest.xml file which is deployed with your app.
What does this mean for your apps? According to the documentation, this value tells the system it should not enable any compatibility behaviours to maintain your app’s forward-compatibility with the target version. If your app is set to target a particular API level, all features at that level that you use should be thoroughly tested to ensure your application works.
With the requirement for new apps to have the target API level of 26, there is at least one feature that is yet to be catered for in Delphi apps. In Android 6 (API level 23), requirements were introduced regarding application users privacy; namely that some resources are now designated “dangerous”, and applications will need to explicitly request permission to access to these resources at runtime, as well as having the permissions listed in the manifest.
In order to handle requests for permissions correctly, the application should call the requestPermissions method (of the Activity class), and the activity must override the onRequestPermissionsResult method of the Activity class in order to determine whether the user granted or denied access. Unfortunately at present, this method is not implemented by the FMXNativeActivity class (part of the FMX Java runtime), so unless you’re keen on modifying the FMX Java source and recompiling it, a little bit of “hacking” is required.
Fortunately, I (with the invaluable assistance of Brian Long) have done the work for you. Rather than go into an in-depth discussion of how the solution was devised, I’ll field questions in the comments. I will however describe how the demo is put together, which should guide you in how you can integrate it into your own apps.
First, you will need to download the Android SDK for API level 26 (i.e. Android 8). Run SDKManager located in the Android SDK folder, which by default appears in the directory as per the following image. Select at least the Android 8 SDK Platform and click Install:
Use the SDK Manager to change the illustrated SDK settings to API 26 (in my case 26.0.2):
Once you have created your project, the manifest template needs to be modified to replace the FMXNativeActivity with a class that descends from that class, but implements onRequestPermissionsResult. The following shows the part that needs to be changed:
NOTE: A side-effect of replacing the activity is that when debugging via the IDE, when the IDE says “Launching”, you will need to start the application manually on the device.
Next, the application needs to have the .jar file that contains the replacement activity added to the Android Libraries node, which is under the Android target in the Project Manager:
The file (dw-nativeactivity.jar) is in the Lib folder of the KastriFree project.
I’ve devised a class called TSystemHelper, which follows a similar pattern to other classes I’ve constructed to implement functionality in a cross-platform manner. In the main form of the demo, I create an instance of TSystemHelper, assign the OnPermissionsResult event to a handler, and call the RequestPermissions method for various permissions that are classified as “dangerous”, such as Camera, Location and SMS.
The permission request results are passed back in a dynamic array of records (of type TPermissionsResults). The type has a helper class that adds convenience methods such as AreAllGranted, which indicates whether all permissions in the request were granted. In my tests on my Android 8 device, I was expecting the user to be able to choose which permissions to grant or deny, however it just prompted me with a single query. Perhaps this is different (or will be) on other versions of Android, so I’ve left the code as it is.
You can find the code for the demo in the KastriFree project (the demo relies on units contained in the project).