QML - Lesson 026. Intents with Qt for Android, part 1

Android, Qt, QML, Intent

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.

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());

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 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.

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.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

DK
April 1, 2020, 8:03 a.m.
Dmitry Kozhinov

C++ - Test 001. The first program and data types

  • Result:40points,
  • Rating points-8
A
March 30, 2020, 12:47 p.m.
Anna

C++ - Test 001. The first program and data types

  • Result:60points,
  • Rating points-1
A
March 29, 2020, 12:14 p.m.
Alexanderv66

C++ - Тест 003. Условия и циклы

  • Result:71points,
  • Rating points1
Last comments
April 3, 2020, 8:06 a.m.
Konstantin Grudnitskiy

Я надеюсь вы уже разобрались в чем дело, но если вдруг нет, то проблема состоит в том, что вы пытаетесь запустить программу из интерпретатора питона. Файл main.py это уже готова…
April 3, 2020, 6:18 a.m.
Konstantin Grudnitskiy

>>> text = 'hello world'>>> ' '.join(word for word in text.split()[:-1])'hello'>>> def remove_last_word(text):... return text and ' '.join(word for word in text.s…
March 27, 2020, 2:40 p.m.
Evgenij Legotskoj

Добрый день. В конце пятой статьи скачать можете.
March 27, 2020, 2:28 p.m.
mkdir _

Здравствуйте, а можно, пожалуйста, ссылку на целые исходники, если есть?
March 27, 2020, 4:36 a.m.
Evgenij Legotskoj

Скорее всего также, как и для установки всех остальных переменых в CMake, через использование set
Now discuss on the forum
April 3, 2020, 12:53 p.m.
BlinCT

Само собою на компе этого незаметно.
April 3, 2020, 8:48 a.m.
Intruder

Евгений, добрый день. Спасибо!
s
April 3, 2020, 7:52 a.m.
solmik

да вроде много чего установленно, если неправильный путь указать то же самое, пробовал запустить видео через плей лист (по примерам из док)и из него назад путь взять, не получилось
April 3, 2020, 5:50 a.m.
Jurij-V

Спасибо ! Поковырявшись и посмотрев интерфейс QSvgRender и т.д. понимаю что похоже нужно будет лезть в реализацию ::render(..). Кто может подсказать как и где выкачать актуальные исходники…
April 2, 2020, 2:46 a.m.
Evgenij Legotskoj

Я думаю, что это где-то описано, но в такой глубине документации и максимум в самых базовых вещах, то есть отдельно в классах этого не пишут, просто потому, что придётся писать для каждого класс…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB