Recently I decided to try to implement the idea of how to share a location via the VKontakte API with friends in a mode close to real time. The output was a cross-platform Qt-application for iOS / Android, a web application for VKontakte and a couple of pull requests for the VK API. In this article, I would like to share some unobvious moments of implementation that may be interesting to someone. So, I ask those interested under the cat.
Why do I need it at all
Sooner or later, children grow up. Banal truth. So my ten-year-old daughter one day said: “Dad, don't take me by car anymore, I want to go to school on my own!”. Well, I considered the demand to be fair, asked for a two-week reprieve and began preparation.
Since I have some experience in writing apps, and the daughter is constantly carrying in the pocket of the iPhone SE, then as a preparation, it was decided to quickly write an application that would show where the daughter is at this particular moment. Yes, I know that now there are quite a few such applications (even Google Maps recently had similar functionality), and it was possible to use some kind of ready-made solution, but it was interesting for me to write something of my own.
Why VKontakte?
Since I would not like to deal with the processing and storage of other people's (in the potentially possible future) personal data on my equipment with all the “charms” that follow from this, I wondered how to do without my own server part. And then it dawned on me - after all, there is such a monster as VKontakte! It is fashionable, powerful and with its developed API, and most importantly - all our children have been sitting together for a long time and in a tight place (I can’t say I like it, but it’s a reality). But damn it, Holmes, how do I stuff location data into it so that, firstly, they don’t show where they don’t need, and secondly, you can regulate access to this data so that bad pedobirs do didn't get it?
')
Notes came to the rescue. Yes, the very Wikited notes that were once (rumored) very popular, and are now in the pen, for the most part have ceased to shine in the tapes and have moved to a separate section of the site that you cannot get to on the lame goat. You can place arbitrary text data in them, but the main thing is that you can assign access lists in which people and groups are listed who can see and comment on this note.
The general scheme of the application in my eyes began to look like this:
- Create a list of friends with whom you want to share location data (I called it "Trusted Friends");
- We create a note with a specific name, give the rights to view it to the above list, write down the location data there and update it periodically;
- We regularly go over the list of trusted friends, checking whether they have a note with this specific name, if it does, we try to extract friend’s location data from its contents, and if we succeed, then show it on the map;
- ???
- PROFIT!
So, the idea is there, the case for small is to implement it.
iOS
Since the daughter uses the iPhone, it was logical to start the implementation with the iOS version. With an eye to cross-platform, Qt was chosen as the framework, because I’ve been familiar with it for quite some time, and the good old Open Street Map, for which Qt Location has a plugin, was chosen as the map engine. Since the application was originally created as an open source, Qt’s licensing restrictions did not frighten me.
The GUI was written in QML, I connected and used the standard
VK iOS SDK to work with VK, it is written in Objective C, so its integration did not cause any problems. Work in the background on iOS is implemented through the Significant Change Location Service. To reduce power consumption, the application monitors the activity of movements, and if it understands that a person is sitting for about a long time in about one place (say, came to school or office), then it lowers the required accuracy of geo-location determination, forcing the OS to switch to less energy-intensive ways to determine it ( usually on cell towers). If the application understands that the person has begun to move actively, the accuracy rises again.
The full set of iOS sources is available on
GitHub . Here are a couple of unobvious moments that I encountered during the implementation process:
Overriding NSApplicationDelegate Methods in a Qt ApplicationThe iOS SDK from VKontakte requires adding calls to some of its functions in the application: didFinishLaunchingWithOptions: and application: openURL: options: methods. Prior to some version of Qt (in my opinion, before 5.11) it was enough to create a category for QIOSApplicationDelegate something like this:
@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate> @end @interface QIOSApplicationDelegate (QIOSApplicationDelegateVKGeoCategory) @end @implementation QIOSApplicationDelegate (QIOSApplicationDelegateVKGeoCategory) - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [...] } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { [...] } @end
But in recent versions of Qt, QIOSApplicationDelegate already has an application: openURL: options :, so the option with categories is no longer rolling. I had to make a successor from QIOSApplicationDelegate and assign a delegate via setDelegate:
@interface VKGeoApplicationDelegate : QIOSApplicationDelegate @end @implementation VKGeoApplicationDelegate - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { [...] } @end void InitializeVKGeoApplicationDelegate() { [[UIApplication sharedApplication] setDelegate:[[VKGeoApplicationDelegate alloc] init]]; } int main(int argc, char *argv[]) { [...] InitializeVKGeoApplicationDelegate(); [...] }
Error handling from VKRequestFaced with the fact that VKRequest returned an empty (empty) NSError on error. I made a patch, which patches someone's previous patch and
pull request this patch, but it still hangs in the unexamined.
Android
The next was a version for Android. The GUI code was almost completely reused from iOS, the
VK Android SDK was also used to interact with the VC, which interacts via the JNI, the background work is implemented in accordance with Google's precepts for such applications - namely, Foreground Service. Of course, the energy-saving logic is also implemented, similar to that used in iOS.
The full set of source code for the Android version is again available on
GitHub , and here are a couple of unobvious moments that I encountered in the process of implementing this version:
How to create an Android service on QtTo create a service in Qt 5.10, a QAndroidService class appeared, which should be used instead of QGuiApplication. You can build separate .so for activity and for service, or you can use one .so for everything, and in order for the code to understand in which mode it works, you can specify this mode via the command line key, like this:
<service android:name=".VKGeoService"> <meta-data android:name="android.app.arguments" android:value="-service"/> </service>
int main(int argc, char *argv[]) { if (argc == 1) { QGuiApplication app(argc, argv); [...] } else if (argc == 2 && QString(argv[1]) == "-service") { QAndroidService app(argc, argv); [...] } else { return 0; } }
Strange "hanging" activity in onDestroy ()In the process of implementing the service, a funny problem emerged - QtActivity “hung out” somewhere in the depths of its onDestroy () subject to the availability of a working foreground service. Apparently, Qt does not expect that after the activity is completed, something else may remain from the application. The problem was solved by separating activity and service across various processes through the use of android: process in the manifest:
<service android:name=".VKGeoService" android:process=":VKGeoService"> [...] </service>
and nailing the process in which the activity works in the overridden onDestroy ():
@Override public void onDestroy() { [...] Process.killProcess(Process.myPid()); }
Yes, lint constantly swears at this, but how to deal with it differently is not obvious to me yet, but my hands do not reach QTBUG with PoC on this topic yet.
Failure to call errorBlock when canceling VKBatchRequestI widely use batch requests to ease the load on VKontakte servers, and it was under Android (everything works fine under iOS) I had a problem - if VKBatchRequest errorBlock was canceled, there were no calls for canceled requests. I fixed this problem in the local version of the library, made the corresponding patch and
pull request for this patch, but again, it still hangs in unexamined.
Conclusion
The iOS version of Apple has easily placed on the App Store, and it is available there to this day, the Android version lived on Google Play for a while until the Google Play Policy was tightened (it was prescribed that geo-location tracking applications must be explicitly intended for either family or corporate use), after which my application was safely blocked there. On my appeal attempt, a Google employee in charge (or maybe it was a bot, now it’s not so obvious who answers your questions) relentlessly stated that “well, this application CAN BE USED NOT ONLY for family or corporate tracking” , to which I could not find, to argue - indeed, with a hammer, you can not only hammer in nails, but also pierce heads ... I posted the Android version in Yandex.Store and Amazon Appstore and in the form of an APK on the project website.
I would be glad if this application is useful to someone as a visual aid for clarifying some unobvious moments when writing a Qt application for iOS / Android, especially related to the implementation of Android service on Qt (this functionality is relatively new, examples of implementation, I know, not so much). Also I will be glad to answer questions in the comments, if any.