****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.
From August 2018, all new apps on Google Play must target Android 8 (API level 26) or higher, and from November 2018, all app updates on Google Play must do the same. If your app also needs to request “dangerous” permissions, this article will help you achieve that.
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:
MainFrm.pas
DW.SystemHelper.pas
DW.SystemHelper.Android.pas
dw-nativeactivity.jar
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).
Hi
With what versions of the SDK and NDK did you compile? What does your SDK settings look like?
Regards
Stiaan
I’d forgotten that (a while ago) I had changed to the API level 26 SDK, so if you’re having trouble, I’m assuming that’s the reason why. I’ve updated the article to reflect this. I’m using the “default” NDK which is r9c.
Hello you were able to solve the problem of the notification bar in white. It’s happening to me, too.
I’ve updated the article as well as some files.
Boa tarde… não consigo encontrar em baixar o arquivo mensionado no post: O arquivo (dw-activity.jar) está na pasta Lib do projeto KastriFree.
O arquivo é chamado dw-nativeactivity.jar
Hello! First of all thanks for the contribution of the solution, I have not yet been able to test it but I can not wait to try it … I was giving a researched and I saw a discourse about api26 in delphi, it even cites its solution, and from what I understood due to the Android 8 have more backgroud services the push notifications are not working properly. I would like to know if there is any possible solution and if there is any news of the position of the jetty regarding api26.
topic link: https://stackoverflow.com/questions/50549970/delphi-new-apps-on-google-play-must-target-android-8-api-level-26-push-not
I’m sorry for my English, I’m getting help from a translator.
Eu pretendo olhar para o problema de notificações push e atualizar o artigo que eu postei há algum tempo.
(I plan to look into the push notifications problem, and update the article I posted a while ago.)
I have the same problem. On Android 6 it works perfectly, however on Android 8 the push notification does not work in the background
does embarcadero care about this change?
I expect they do care, since their users would not be able to deploy to the app store without using workarounds. As to when the official support might be implemented, I do not know
First of all thank you very much for your articles and resources made available.
I’ll start preparing for these changes in our app. Today for notifications I use a solution published by you (http://blog.delphiworlds.com/2017/05/add-firebase-cloud-messaging-mobile-apps-part-1/) if I implement the changes for api 26 as notifications will continue to work? Do you have this information?
Thank you again.
Hi
I’m receiving this error when compile the demo
[DCC Error] MainFrm.pas(58): E2003 Undeclared identifier: ‘StatusBarHeight’
This code when I comment, works fine
StatusBarRectangle.Height := FSystemHelper.StatusBarHeight;
I’ve modified the form unit to fix the issue:
https://github.com/DelphiWorlds/KastriFree/blob/master/Demos/AndroidDangerousPermissions/MainFrm.pas
You might also like to look at this alternative solution:
http://blog.delphiworlds.com/2018/06/targeting-android-8-and-higher-continued/
Android 5.0.2 errors: Invoke error: method not found:
Project AndroidDangerousPermissions.apk raised exception class EJNIFatal with message ‘Invoke error: method not found’.
line 288: raise EJNIFatal.CreateRes(@SJNIUnknownMethod); in unit unit Androidapi.JNIMarshal
Delphi Tokyo
Hello,
I use FMX C++ Builder.
Can I use this jar file in Project?
Since I added this file and typed ‘#include ‘, the output message shows ‘[bccaarm fatal error] uMain.cpp(19): ‘DW.SystemHelper.hpp’ file not found’
Thanks for your help.
As far as I understand it, C++Builder should auto-generate the .hpp file from the .pas file. Do you have DW.SystemHelper.pas in the project compiler path?
Hi,
I can’t find the information you said.
However, I think C++ Builder should point directly to the Include file in the installation path. For example, my Project code is as follows:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //***Error***
I used the Android 4.4 device to test the Jar file you provided (but I don’t have Include DW.SystemHelper.hpp in the project), it works fine.
This should mean that Jar File has replaced the official loading method.
I used the Android 6.0 device to jump out directly because the app did not normally ask Dangerous Permission.
So in a nutshell, it’s now how to call your DW.SystemHelper.hpp in the Builder C++ environment.
🙂
Thanks for your help.
I can not call the uses DW.SystemHelper!
I recommend using the solution presented here, instead:
http://delphiworlds.com/2018/06/targeting-android-8-and-higher-continued/
I’ll update this article later..
[…] Android 8 and higher by (another) David at http://delphiworlds.com/2018/05/targeting-android-8-and-higher/ (this is an important and urgent area, we are working on this […]
Hello, I would like to know if you can help me with push notification on ANDROID8, until the 7 works perfectly in the background, already on the 8 no, grateful
What implementation are you using for Push Notifications? Is it a problem just in the background, or foreground as well? Have you had a look at: http://delphiworlds.com/2018/08/firebase-cloud-messaging-revisited/
Hello, Thank you for your article it is very helpful for me. I have a problem accessing a Sqlite local database with Firedac connection. I am using the beforconnect event to set the path for the database
sqobrascon.Params.Values[‘Database’] := TPath.Combine(TPath.GetDocumentsPath,’mydatabse.s3db’);
This code is working fine with previous api level but now the application crashes.
Can you help me with this error?
That code should work regardless of API level. It is hard to diagnose from just one line of code, and no error messages. Please provide a more complete example, plus the *exact* error message.
Hi Dave, good night. Friend, I implemented your solution for takeFromCamera using ActionList, but the EDITABLE function of the actionList is no longer available. Are there any solutions to this? Thank you.
What do you mean it is not available? The property is still there.
Hi, Dave. yes it is, however, should when selecting the photo, the same go to the editing screen, before going to the Image component. What is happening is that it goes straight to the Image component.
can you please help, i’m using c++ and i don’t know how to convert to api26 for c++
Thank you a lot, this works for me. But now i can’t write files to “TPath.GetDocumentsPath+TPath.DirectorySeparatorChar”, this files are not available on next start of my application. I use XE10 Berlin and can’t compile Kastri-Package. Pls help.
Hi Paul,
You should be able to access files in that path; can you supply the full path that you are accessing? What is the value for Remote Path in Deployment Manager for the files you are accessing?
I no longer support Delphi 10.1 Berlin in my code, however I may be able to help if you can let me know what the code is that does not compile, and what the errors are.