Launch Applications Using Intents System UI Example
Learn how to use Intents to start applications from the System UI.
Introduction
This example shows you how to use intents as a basis for a launcher in a System UI.
Prerequisites: You're already familiar with the concepts and topics introduced in the System UI Example: "Hello World!". This example uses the same folder structure and can be started in the same way.
Unlike the Hello World example, we are not using the ApplicationObject abstraction layer directly to launch appliations, but instead we are using launch
intents registered by the packages. This way we can have multiple icons in a launcher that can start the same application, triggering different functionality within the application.
Note: Any intent directed at an application would launch it, if it was not already running. For the purpose of this example we are calling our intents launch
, but the application manager does not attach a special meaning to this name.
All the intents' icons and names that are in the launcher
category are on the left. You can click on their icons to send the respective intent request to the system: if the corresponding application is not yet running, it will be started automatically. Doing a press-and-hold on the icon will stop the application via the ApplicationObject. Just like in the "Hello World!" example, the column layout on the right shows the windows of all running applications.
The applications display their application name against a background of that color. In addition they also show how many times they have received an intent of the launch
category.
Implement the System UI
Like any simple QML application, our example's code starts with some imports and a plain Item at the root. The only difference is that our System UI also imports the QtApplicationManager
and QtApplicationManager.SystemUI
modules, besides QtQuick
.
import QtQuick 2.12 import QtApplicationManager 2.0 import QtApplicationManager.SystemUI 2.0 Item { width: 800 height: 600
Unlike in the "Hello World!" example, we now create an IntentModel to get access to all intents that are in the launcher
category. Since this model is unsorted by default, we also set the sorting order to descending based on the name of the intent.
Note: The name of the category could be anything you like it to be - as long as it is consistent between here and the applications' info.yaml
metadata and the corresponding IntentHandler within the application.
IntentModel { id: intentModel filterFunction: function(i) { return i.categories.includes("launcher") } sortFunction: function(li, ri) { return li.name > ri.name } }
Next, we have a Column on the left side of the root Item where we place the icons of the available intents along with their names.
// Show intent names and icons Column { spacing: 20 Repeater { model: intentModel Column { Image { source: model.icon MouseArea { anchors.fill: parent onPressAndHold: { var app = ApplicationManager.application(model.applicationId) if (app.runState === Am.Running) app.stop() } onClicked: { IntentClient.sendIntentRequest(model.intentId, model.applicationId, {}) } } } Text { font.pixelSize: 20 text: model.name } } } }
We use our IntentModel, which provides a row for each intent available. In each row, we have:
- an
icon
role with the icon URL - a
name
role with the localized intent's name - an
intentId
role that contains the id of the intent - an
applicationId
role that contains the id of the handling application
For information on the other roles available, see IntentServer QML Type.
Next, we place a Column
anchored to the right side of the root Item
. This item is exactly the same as in the "Hello World!" example, showing all the application windows.
Implement the Application
Our Launch Intents applications display their application name as text against a colored background.
import QtQuick 2.12 import QtApplicationManager.Application 2.0 ApplicationManagerWindow { color: "blue" Text { id: txt property int counter: 0 anchors.centerIn: parent text: ApplicationInterface.name["en"] + "\nLaunch intents received: " + counter } IntentHandler { intentIds: [ "launch" ] onRequestReceived: (request) => { txt.counter++ request.sendReply({}) } } }
The only difference between this example and the one from the "Hello World" example is that we are additionally handling incoming launch
intent requests via an IntentHandler: this intent handler doesn't do anything useful in this example, besides increasing a counter in the Text object, so that you can see how many times the launch intent has been received. Have a look at the documentation for IntentHandler::requestReceived for more information on the IntentClientRequest::sendReply call.
Application Metadata
The info.yaml
file contains the metadata about a package. It starts with some boilerplate describing that this file contains Qt Application Manager package metadata.
formatVersion: 1 formatType: am-package ---
Then comes the package ID, which uniquely identifies the package. It's recommended to follow a reverse DNS scheme, but it's not enforced. Here it's the "Blue" package from the "Launch Intents" example UI.
id: 'launch-intents.blue'
Then the icon filename:
icon: 'icon.png'
Next comes the user-visible name of the application in any number of languages. For this example, we only provide English:
name: en: 'Blue App'
For each package, we define one application that gets the same id as the package itself (this is not a requirement, but useful approach to single-application packages.
applications: - id: launch-intents.blue runtime: 'qml' code: 'main.qml'
Every package will at least register one launch
intent in the launcher
category. The exact name of the intent does not really matter in this case, since the system-ui will just request all intents that are in the launcher
category. The name and icon of an intent default to the name and icon of the package, but we override the name here:
intents: - id: launch name: en: 'Launch Blue' categories: [ launcher ]
Alias handling
The red application does a bit more then the blue and green counterparts: it implements the deprecated "aliases" mechanism using intents.
In its info.yaml
file it defines a second intent in the launcher
category, named another-launch
:
- id: another-launch name: en: 'Launch Another Red' categories: [ launcher ]
In order to handle this intent, the IntentHandler in the main.qml
file is extended to accept both intents:
IntentHandler { intentIds: [ "launch", "another-launch" ]
The signal handler for incoming intent request can then disambiguate between the requested intents based on their intentId
and increase either the normal counter
or the anotherCounter
property of the text field.
onRequestReceived: (request) => { if (request.intentId === "launch") txt.counter++ else txt.anotherCounter++ request.sendReply({}) }
Note: Instead of one IntentHandler handling both intents, this could have also been implemented using two separate IntentHandlers, handling one intent each.
© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.