Launch Applications Using Intents System UI Example

Learn how to use Intents to start applications from the System UI.

The Launch Intents example with two applications running.

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
import QtApplicationManager
import QtApplicationManager.SystemUI

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 {
                id: delegate
                required property url icon
                required property string applicationId
                required property string intentId
                required property string name
                Image {
                    source: delegate.icon
                    MouseArea {
                        anchors.fill: parent
                        onPressAndHold: {
                            var app = ApplicationManager.application(delegate.applicationId)
                            if (app.runState === Am.Running)
                                app.stop()
                        }
                        onClicked: {
                            IntentClient.sendIntentRequest(delegate.intentId,
                                                           delegate.applicationId, {})
                        }
                    }
                }
                Text {
                    font.pixelSize: 20
                    text: delegate.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
import QtApplicationManager.Application

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.

Example project @ code.qt.io

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