Eskil Abrahamsen Blomfeldt

Intents with Qt for Android, part 1

Published Thursday June 30th, 2016
14 Comments on Intents with Qt for Android, part 1
Posted in Android, Dev Loop, Qt, Qt in use

The “intent” is the main facility for interprocess communication on Android. Basically, an intent is an object that is processed by the operating system and then passed to one or more of the installed applications based on its contents. It could for instance be a request to show a video, in which case the intent type would be ACTION_VIEW and the mime type would be set accordingly. Applications can subscribe to certain intents by setting an intent filter in their manifest files. The first time a type of intent is seen, the user of the device will be presented with a selection of applications known to subscribe to that type of intent. If they choose, they can set a default at this point, or select to be asked for every instance.

This is the first of a few articles showing how to use intents with Qt for Android.

The mechanism itself is quite general. What is described above is one use case, but intents are also used for other things: One such thing is launching services inside the application. This is usually formatted as an “explicit intent”, meaning that the fully qualified name of the service to start is provided, so it cannot be intercepted by any other applications.

Another way intents are used is for broadcasts, for instance when the time zone of the device changes. While the action to view a video described above would only launch the one specific application selected by the user, a broadcast will rather be passed to any application which subscribes to it.

This blog will focus on Android, but a similar mechanism called app extensions was introduced in iOS 8.

In this first article about intents on Android, I will focus on the simplest use case: Creating an implicit intent from Qt to start an unspecified application on the target device. As an example, I have made a simple application which presents food recipes. Each recipe has an estimated time for completion. By clicking a button, you can set a timer using the preferred countdown application on the device. (For a more useful use case, one might imagine a button setting a timer for each of the steps in the recipe’s directions, but for this simple example we will only have a single timer per recipe.)

I won’t go into detail about how the application itself is written, but the code can be found here. No effort has been made to make the UI look nice, so please disregard that. The application is backed by an SQLite database (which is filled with some dummy content the first time the application is started) and has a very simple UI written in Qt Quick Controls 2, allowing the user to select a recipe from a list, see its details on a separate page and click a button on the recipe to set a timer in the system.

Picture of recipe

The part we will focus on is the code that actually requests the timer from the system. This is in the recipe.cpp file, in the function Recipe::createTimer(). For our example we will be using the AlarmClock.ACTION_SET_TIMER intent type.

We will use the JNI convenience APIs in the Qt Android Extras module for this. I will go through this code step by step.

    QAndroidJniObject ACTION_SET_TIMER = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock",
                                                                                          "ACTION_SET_TIMER");
    QAndroidJniObject intent("android/content/Intent",
                             "(Ljava/lang/String;)V",
                             ACTION_SET_TIMER.object());

The first step is to create the intent. We retrieve the identifier of the ACTION_SET_TIMER intent. This is a static member of the android.provider.AlarmClock class and is of type String. Note the use of slashes in place of dots in the package name of the class. This corresponds to the bytecode representation of package names (for historical reasons according to the specification) and is therefore also used in signatures in JNI.

Since we are only using functions from Qt Android Extras and no explicit calls using the JNI APIs, we don’t have to worry about reference management. This is one primary convenience of the APIs.

The next step is to actually create the intent object. We do this by constructing a QAndroidJniObject, passing in the JNI-mangled signature of the Java constructor we want to call and we pass in the ID of the action we just got from the AlarmClock class.

    QAndroidJniObject EXTRA_MESSAGE = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock",
                                                                                       "EXTRA_MESSAGE");
    QAndroidJniObject messageObject = QAndroidJniObject::fromString(message);
    intent.callObjectMethod("putExtra",
                            "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
                            EXTRA_MESSAGE.object(),
                            messageObject.object());

    QAndroidJniObject EXTRA_LENGTH = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock",
                                                                                      "EXTRA_LENGTH");
    intent.callObjectMethod("putExtra",
                            "(Ljava/lang/String;I)Landroid/content/Intent;",
                            EXTRA_LENGTH.object(),
                            jint(m_time * 60));

    QAndroidJniObject EXTRA_SKIP_UI = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock",
                                                                                      "EXTRA_SKIP_UI");
    intent.callObjectMethod("putExtra",
                            "(Ljava/lang/String;Z)Landroid/content/Intent;",
                            EXTRA_SKIP_UI.object(),
                            jboolean(true));

Next up, we set up some arguments we want to send to the activity handling the action. We pass in a description of the timer and the number of seconds to set it for.

    QAndroidJniObject activity = QtAndroid::androidActivity();
    QAndroidJniObject packageManager = activity.callObjectMethod("getPackageManager",
                                                                 "()Landroid/content/pm/PackageManager;");
    QAndroidJniObject componentName = intent.callObjectMethod("resolveActivity",
                                                              "(Landroid/content/pm/PackageManager;)Landroid/content/ComponentName;",
                                                              packageManager.object());

Then we have to resolve the activity. This will tell the system to check if there is an application available to handle the ACTION_SET_TIMER action. We first get a reference to the current activity (our current context) in order to get the appropriate package manager. Then we call resolveActivity() on the intent and pass in the package manager.

    if (componentName.isValid()) {
        QtAndroid::startActivity(intent, 0);
    } else {
        qWarning() << "Unable to resolve activity";
    }

If the returned component name is non-null, we start the activity using the convenience function in Qt Android Extras. We will look at more features of this function in a later blog. If no appropriate activity is found, then this code only outputs a warning to the console. A proper application might complain in the actual user interface, but note that the operating system has also alerted the user of the problem at this point.

Screenshot of alarm that has been set

That’s it for this first introduction to using intents in Qt for Android. Look out for more complex examples in the future.

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Android, Dev Loop, Qt, Qt in use

14 comments

Richard says:

What happened to the font? (Windows, Firefox 47.0 and 47.0.1)
The font now looks very pixellated and has no or very poor anti-aliasing. I guess the font hints (intents!) are not right.

It looks like the top and bottom few pixels have been cut off before a naive zoom.

This font is really quite hard to read and makes the blog look absolutely hideous.

Fortunately uBlock lets me block this, but really guys – why?

Olli Puurunen Olli Puurunen says:

Hi,

sorry about this. It’s probably because Firefox recently started blocklisting some graphics drivers to disable the use of some (or all) it’s hardware accelerated features, including font rendering for some fonts. See https://wiki.mozilla.org/Blocklisting/Graphics for more info. By writing about:support to location bar, you can see under Graphics section if the features are disabled (for example “Direct2D Enabled: Blocked for your graphics card…”).

We’ll investigate if there’s a way fix this issue.

Richard says:

Thanks. I don’t see this on my other machine with an nVidia card, only my laptop with Intel/AMD switchable graphics.
It is a new issue, unfortunately I do not recall when I had previously checked the blog so can’t say when it first appeared.

azadkuh says:

nice article,

isn’t it cleaner to write the whole android part in java and then simply call our thin java interface through `QAndroidJniObject`?

even with aid of Qt Android Extra the jni convention is still freaking and lacks compile time checks.

Eskil Abrahamsen Blomfeldt Eskil Abrahamsen Blomfeldt says:

Yes, that’s definitely an option and probably less error-prone, as you say. I wanted to show the startActivity() function in Qt Android Extras because I plan to expand on this to show some more advanced features later on. I will also write a blog where I add Java code to the project, so this will also be covered.

azadkuh says:

gr8! i’m waiting for your next articles.

ekke ekke says:

thx starting this blog series – will help me to implement Intents.

Hopefully someone can do the same project for iOS app extensions ?

Qt is great for mobile x-platform development and I’m just writing a blog series about this.
Presenting Qt at customers there’s always the question “can you use Intents and App Extensions to provide same functionality for Android and iOS”.

Jake Petroules Jake Petroules says:

First I need to finish my work in progress to bring shared library support to Qt on iOS, but that’s a great idea for a future blog post or perhaps even a new demo/example.

Sassan says:

Thanks Eskil, I hope we’ll see more post like this in Qt blog. A problem I have when in my current Qt project that is going to be deployed on Android too is the poor documentation on Qt for Android. For example when I search the Qt examples for the term JNI, it shows nothing. It’d be great if you manage to cover different features of Qt for Android in blog posts, provide some examples in Qt’s examples and ideally an in-depth documentation in Qt’s documentation.

For example when I needed to read device id in Android it took me 10 hours to first find out that I should use JNI, then find read the related docs in Android SDK docs (I don’t expect that to be in Qt’s doc) and then find out how to implement it with Qt. Hopefully there was an old blog post (not in Qt Blog, in some other site) that had an example and it was really helpful.

Now my next step in the development of the Android app is to make it open files with a specific extension and I’m more than happy I just saw this post by accident. Now I know I should use intends for this purpose and I think I can implement it now.

I’m sure Qt can be a perfect solution for Android and IOS applications, but it needs better documentation.

ekke ekke says:

Sassan, I made same experiences. It’s really hard to know what to do to make common mobile features work on Android and iOS. Would be great to have some recipes. So it’s great to have this blog for use of Intents, but writing a x-platform app I have no idea HowTop solve this to integrate with iOS app extensions.
(I’m coming from BlackBerry Cascades with some knowledge of Android and less knowledge on iOS. )

Marco Marco says:

Sassan, make sure you read ALL Android-related blog posts by Bogdan Vatra on KDAB’s blog.
There’s a goldmine of information in there which covers many essential aspects:

https://www.kdab.com/category/blogs/android/

It’s very good to see Android aspects covered by the Qt Company as well. +1 for having tutorial-like coverage on how to extend Qt on iOS pretty much like Bogdan and now Eskil are doing for Android.

Many thanks for your hard work guys!

Marco Marco says:

I also want to let interested people know that there is a Slack devoted to Qt mobile app developers to exchange experiences and code: http://slackin.qtmob.org

Sassan says:

Thank you so much, this slack team is such a valuable thing for someone like me trying to develop mobile apps in Qt.

Sassan says:

Thanks Macro, kdab was the blog that saved me by introduction JNI. It’s the one I mentioned in my comment but forgot its name. Yeah I should real all its posts.
I hope some day we have a definite documentation about programming Android applications in Qt with all best practices and anti patterns documented explicitly.

Commenting closed.

Get started today with Qt Download now