I have a bit of a unique situation. I have a front end QML/Qt UI and a backend application written in C# that I'm developing to replace old software. I was previously using Android tablets with an all in one Xamarin UI. This new combo will replace that application. Problem is on the old Android tablets to put the new software on there we need some way to run our C# backend without having to duplicate it in another language or build it into the UI.
I stripped out everything of the Xamarin UI so now its just a blank UI with a foreground service within the project. I tested out executing/starting the foreground service within the Xamarin activity to make sure it works. The service will continually make a beeping noise every second (as a proof of concept that I can get this to work).
[Service(Exported = true, Name = "com.backend.RUN_BACKEND")]
public class ForegroundBackendService : Service
//This is the function that gets called currently when the intent
//comes in. I create a notification, start foreground service,
//launch the process/task, and then return a sticky result.
public override StartCommandResult OnStartCommand(Intent intent,
StartCommandFlags flags, int startId)
The exported setting should mean that this service will be available to any application on the Android tablet. Another application should be able to use the "com.backend.RUN_BACKEND" name to execute this foreground service.
The Xamarin project/app is named TestProject. And this is running on Android 8.1 minimum / 8.1 target (Qt UI is also running there).
The Qt application is built with Qt 6.5.0 but it seems most of the examples are a bit dated and the libraries like QtAndroid have been replaced.
This code below seems to be close to what I'm looking for but QAndroidApplication::androidActivity().object isn't available anymore it seems. So I'm trying to figure out what the new syntax to execute the service.
QAndroidIntent serviceIntent(QAndroidApplication::androidActivity().object(),
"org/qtproject/example/qtandroidservice/QtAndroidService");
QJniObject result = QAndroidApplication::androidActivity().callObjectMethod(
"startService",
"(Landroid/content/Intent;)Landroid/content/ComponentName;",
serviceIntent.handle().object());
What would the syntax/code be to execute my ForegroundBackgroundService named "com.backend.RUN_BACKEND" from the Qt UI?
Thanks ahead of time I really appreciate the help!
###EDIT/UPDATE:###
So I found a link that was sort of helpful.
How do I access Android activity in QT6
That links to this page which I had already come across but I'm now taking a deeper look at it.
So now I am trying to figure out what the equivalent syntax is. I'm new to Qt/Android/Android services so maybe the answer is obvious and I'm just missing it.
Whats confusing me is that QAndroidApplication::androidActivity() has members object() and callObjectMethod(). Here is a list of what is available in QNativeInterface::QAndroidApplication. Its not the same so I'm trying to figure out how to write an equivalent piece of code.
context() : int
hideSplashScreen(int)
isActivityContext() : bool
runOnAndroidMainThread(const std::function<QVariant ()> &, const
QDeadlineTimer)
: QFuture<QVariant>
sdkVersion() : int
Based on this information can anyone list out an equivalent bit of code using the new QNativeInterface::QAndroidApplication?
I'm still digging into it so hopefully I can figure it out.
###EDIT 2###
Ok so I had overlooked some comments on that QtBug Report link I posted before. I found some new code.
QJniObject jClass = QJniObject("poc/dialogue/Hello", "
(Landroid/app/Activity;)V", QtAndroidPrivate::activity());
jClass.callMethod<void ("showDialogue");
//This seems to solve part of the problem
QAndroidIntent serviceIntent =
QAndroidIntent(QtAndroidPrivate::activity(),
"com.example.MyService");
//Now I need some sort of piece of code that is equivalent to this
QJniObject result =
QAndroidApplication::androidActivity().callObjectMethod(
"startService",
"(Landroid/content/Intent;)Landroid/content/ComponentName;",
serviceIntent.handle().object());
I also need to execute functions from a Java SDK so the first couple lines are still useful.
###EDIT 3###
I have code now that isn't highlighted so I think I'm headed in the right direction.
Does this look right?
QAndroidIntent serviceIntent =
QAndroidIntent(QtAndroidPrivate::activity(),
"com.example.MyService");
QJniObject startService = QJniObject();
QJniObject intentResult =
startService.callObjectMethod("startService",
"(Landroid/content/Intent;)Landroid/content/ComponentName;",
serviceIntent.handle().object());
Is there anything obviously wrong with the above code. I'm going to test it now so I guess I'll find out.
###EDIT 4###
I am honing in on what I think is a solution.
//I have this line of code but it produces an error
QAndroidIntent serviceIntent =
QAndroidIntent(QtAndroidPrivate::activity(),
"SOB.Bacon.com.backend.RunBackend");
//JNI DETECTED ERROR IN APPLICATION: illegal class name
//SOB.Bacon/com.backend.RunBackend' F zygote : runtime.cc:550]
//(should be of //the form 'package/Class', [Lpackage/Class;' or
'[[B') F zygote : runtime.cc:550] //in call to FindClass
I have tried all interations of "SOB.Bacon/com.backend.RunBackend" such as "SOB/Bacon/com/backend/RunBackend" and "SOB.Bacon.com.backend.RunBackend". Nothing seems to work.
I know for a fact that this service works. I created a second bare bones blank Xamarin project and it does one simple thing the project loads up and using this code it launches the foreground service of the other application. And bam the service starts beeping and I know it worked. It launches it by using ONLY the package name "SOB.Bacon" and the class name "com.backend.RunBackend" thats it. So I should be able to do the exact same thing from Qt.
This is the Xamarin code that launches the exported service in the other Xamarin application. Just to show what I'm trying to achieve in Qt.
const string REMOTE_SERVICE_COMPONENT_NAME =
"com.backend.RunBackend";
// This is the name of the service, according the value of
ServiceAttribute.Name
const string REMOTE_SERVICE_PACKAGE_NAME = "SOB.Bacon";
// Provide the package name and the name of the service with a
ComponentName object.
ComponentName cn = new ComponentName(REMOTE_SERVICE_PACKAGE_NAME,
REMOTE_SERVICE_COMPONENT_NAME);
Intent serviceToStart = new Intent();
serviceToStart.SetComponent(cn);
StartForegroundService(serviceToStart);
I do not understand why Qt can't seem to find this package/service. It does exist so hopefully I'm just missing something obvious.
Can anyone shed any light on this?
I mean how hard is it to replicate the above Xamarin code in Qt. I feel like I'm so close.
Any help would be very much appreciated I am going in circles now.
###EDIT 5###
I really think I identified the actual issue now so I need help essentially getting my Xamarin service added/linked to the Qt project. I am unsure how to do this.
I followed the tutorial here and I have tried both starting up the function in the service and binding to it and neither work.
I am pretty convinced that the problem is that in the example they created a class QtAndroidService that extends QtService. In my case I just extended Service as QtService isn't available in Xamarin/C#. The examples says this should work.
Here is the thing though. In the example it executes the function for example but calling the method startQtAndroidService located at "org/qtproject/example/qtandroidservice/QtAndroidService".
I looked at my current Qt project and its directory starts off the exact same "org.qtproject.example.myappname". I guess I must have just built the UI off an example and never changed it.
So I think this means that somehow they put the library file QtAndroidService within the Qt project.
I'm assuming its a .so file because when I currently run it I'll get back an error saying "Didn't find class "SOB.Bacon.com.backend.RunBackend.ForegroundBackendService" on path: DexPathList[[],nativeLibraryDirectories=[/system/lib, /system/vendor/lib]]". The class is still named ForegroundBackendService but its not a foreground service anymore I followed the example exactly just kept the same name I'll change it.
If I look in the /system/lib folder on the Android device it is filled with files with lib in the name ending in .so.
If I look in my SOB.Bacon project install directory on the Android device there is a lib folder. I do not see a library with the same name as my service though, the .so files that are in there seem to have nothing to do with anything other than what Xamarin needs.
So first off I do not know how to get Xamarin to generate a .so file. It will make a .dll file but that is for use by other Xamarin applications.
However I was able to create a second standalone Xamarin application that could execute the service that was built into the other Xamarin application.
So I have two routes/questions:
Based on that example how do I link/point Qt to look for the service in a different location on the Android tablet, right now it seems to only be looking in /system/lib or /system/vendor/lib.
If a .so file is required, how was the other xamarin application able to execute the service, and how was ADB able to execute the service? In which case how do I make Qt execute a service that is NOT part of its own package?
Thanks a lot!