Developing Built-in QML Apps

To create a built-in QML app, you must write the source code and prepare the required configuration files.

For easier understanding, the process to create a built-in QML app is explained using a sample app in Sample Code Repository. The sample app has the following features:

  • When the app is launched, it displays a “Hello, QML Application!!” message on the screen.
  • When the user clicks on the screen, it calls the com.webos.service.systemservice/clock/getTime method. “UTC” time on the response is printed on the screen.
  • Prints logs in the following conditions:
    • When it is first launched and relaunched, outputting the params value which is passed on the launch method of System and Application Manager (SAM)
    • When windowState changed.

The directory structure of the sample app must be as follows:

qml-apps/
├── build-config/
│   ├── com.example.app.qml.bb
│   └── webos-local.conf
└── com.example.app.qml/
    ├── appinfo.json
    ├── com.example.app.qml.pro
    ├── icon.png
    ├── main.qml
    └── README.md

Developing a built-in QML app requires the following steps:

Before you begin

  • Build and flash the webOS OSE image. For detailed information, see Building webOS OSE and Flashing webOS OSE.

  • Download the sample repository, and move into samples/qml-apps directory.

    $ git clone https://github.com/webosose/samples
    $ cd samples/qml-apps
    

Step 1: Implement the QML App

Note
In this guide, we will only explain essential parts of the sample codes. For full list of codes, refer to the sample repository.

Source Code

First, define the functionality of the QML app on the source code.

main.qml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import QtQuick 2.4
import WebOSServices 1.0
import Eos.Window 0.1
import PmLog 1.0

WebOSWindow {
    id: root
    width: 1920
    height: 1080
    visible: true
    appId: "com.example.app.qml"
    title: "QML app"
    color: "lightblue"
    displayAffinity: params["displayAffinity"]

    Text {
        id: mainText
        anchors.centerIn: parent
        font.family: "Helvetica"
        font.pointSize: 50
        text: "Hello, QML Application!!"
    }

    property var launchParams: params
    onLaunchParamsChanged: {
        pmLog.info("LAUNCH_PARAMS", {"params": launchParams})
    }

    Service {
        id: systemService
        appId: "com.example.app.qml"

        function getTime() {
            call("luna://com.webos.service.systemservice","/clock/getTime","{}")
        }

        onResponse: {
            var jsonObject = JSON.parse(payload);
            pmLog.info("GETTIME", {"utc": jsonObject.utc});
            mainText.text = "UTC : " + jsonObject.utc
        }

        onError: {
            var jsonObject = JSON.parse(payload);
            pmLog.error("GETTIME", {"error": jsonObject});
        }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: systemService.getTime()
    }

    onWindowStateChanged: {
        pmLog.info("WINDOW_CHANGED", {"status": windowState})
    }

    PmLog {
        id: pmLog
        context: "QMLApp"
    }
}

A brief explanation of the above file:

  • Line(1) : Import QtQuick 2.4 to use QML.
  • Line(2) : Import WebOSServices to call system services via luna-service.
  • Line(3) : Import Eos.Window to use WebOSWindow QML component.
  • Line(4) : Import PmLog to print logs.
  • Line(6~62) : Declare a WebOSWindow object with child objects.
    • Line(7~13) : Set WebOSWindow properties and size and color.
    • Line(14): Set the displayAffinity property so that the app can be launched on the display corresponding to the displayAffinity value passed as a launch parameter.
    • Line(16~22) : Declare a Text object and string.
    • Line(24~27) : A QML app (with the type “qml” on appinfo.json) is launched and registered to SAM by qml-runner. Through this process, the QML app can receive the parameters passed with the launch method call as params. With each launch method call, onLaunchParamsChanged is called even if the value of params ds not change from that of the previous call. For details of PmLogLib usage, refer to Using PmLogLib in QML.
    • Line(29~47) : Declare a Service object to call systemservice’s getTime method. If the object receives the response, the app prints the UTC time on the screen.
    • Line(49~52) : When the user clicks on the screen, systemservice’s getTime method is called.
    • Line(54~56) : windowState is a value that the WebOSWindow QML component sends to the app. Whenever the windowState value changes, onWindowStateChanged is called. Its value is 1 when the app is in the background and 4 when the app is in the foreground, following the definition of Qt::WindowState. For details, see Qt::WindowState on Qt documentation.
    • Line(58~61) : Declare a PmLog object.
Note
webOS OSE supports use of QtQuick module up to version 2.12, because webOS OSE supports Qt 5.12 LTS since 2.0.0 release. However, using lower version of QtQuick module can be helpful for keeping backward compatibility with other environments using a lower version of Qt. For details, refer to Qt documentation.

For detailed information for Qt, see Qt documentation.

README.md

This file provides general information of the QML app.

Caution
  • If the README.md file is missing, a build error occurs.
  • Make sure the ‘Summary’ section is a single line. Even any whitespace at the line above the ‘Description’ section is considered a part of the summary and can cause the build to fail.
Sample README.md
Summary
-------
QML app sample

Description
-----------

QML app sample

How to Build on Linux
---------------------

## Dependencies

Below are the tools and libraries (and their minimum versions) required to build sample program:

* qmake

## Building

    $ cd build-webos
    $ source oe-init-build-env
    $ bitbake com.example.app.qml

Copyright and License Information
=================================
Unless otherwise specified, all content, including all source code files and
documentation files in this repository are:

Copyright (c) 2020 LG Electronics, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache-2.0

Step 2: Configure the QML App

This section describes how to prepare the configuration files required to build and test the QML app.

appinfo.json

Apps are required to have metadata before they can be packaged. This metadata is stored in a file called appinfo.json, which is used by the webOS device to identify the app, its icon, and other information that is needed to launch the app.

appinfo.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "id": "com.example.app.qml",
    "version": "1.0.0",
    "vendor": "My Company",
    "type": "qml",
    "main": "main.qml",
    "title": "QML App",
    "icon": "icon.png",
    "requiredPermissions" : ["time.query", "application.operation"]
}

A brief explanation of the above file:

  • Line(2) : The ID for the app.
  • Line(5) : The type of the QML app.
  • Line(6) : The executable file name.
  • Line(7) : The title to be shown on the Launchpad.
  • Line(8) : The icon to be shown on the Launchpad and App Bar. Make sure the icon file is available in the project root directory. You can use your own icon.png (80*80) file or attached icon.png.
  • Line(9) : Specify the group to which the external service’s method called by the app belongs.
    • Because systemservice’s getTime method belongs to “time.query” group, put “time.query” in this property.
    • When qml-runner launches QML app, qml-runner calls the method to register the app to SAM. To enable qml-runner to call this method, put “application.operation” group.
    • To check the group of each method, use ls-monitor command with “-i” option.

For more details, see appinfo.json.

qmake Project File

This file specifies the application name and the qmake template to be used for generating the project, as well as the source, header, and UI files included in the project.

com.example.app.qml.pro
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
TEMPLATE = aux
!load(webos-variables):error("Cannot load webos-variables.prf")

# install
defined(WEBOS_INSTALL_WEBOS_APPLICATIONSDIR, var) {
    INSTALL_APPDIR = $$WEBOS_INSTALL_WEBOS_APPLICATIONSDIR/com.example.app.qml
    target.path = $$INSTALL_APPDIR

    appinfo.path = $$INSTALL_APPDIR
    appinfo.files = appinfo.json

    base.path = $$INSTALL_APPDIR
    base.files = main.qml

    icon.path = $$INSTALL_APPDIR
    icon.files = icon.png

    INSTALLS += target appinfo base icon
}

A brief explanation of the above file:

  • Line(1) : We do not require any actual compilation or link steps for the QML app. So, we set TEMPLATE = aux.
  • Line(2) : webOS platform load(webos-variables) will set WEBOS_INSTALL_WEBOS_APPLICATIONSDIR, which we will use as the deployment target folder.
  • Line(6) : Set installation directory on the target board. INSTALL_APPDIR would be /usr/palm/applications/com.example.app.qml on the target.
  • Line(7~16) : *.files specifies a path in the project directory and *.path specifies the path to the file system to be installed on the target.
  • Line(18) : Add target, appinfo, base, and icon to INSTALLS list.

For more details, see qmake Project Files.

Step 3: Build the QML App

After implementing and configuring the QML app, you must build the app.

Add the Recipe File

webOS OSE uses OpenEmbedded of Yocto Project to build its components. OpenEmbedded needs a recipe file that configures the build environment. For more details about the recipe, see Yocto Project Reference Manual.

You must move the recipe file into webOS OSE project directory.

  • Recipe file: samples/qml-apps/build-config/com.example.app.qml.bb
  • Destination Directory: build-webos/meta-webosose/meta-webos/recipes-webos/<qml app name>

where <qml app name> is the name of the qml app. For the sample qml app, <qml app name> must be replaced by “com.example.app.qml”.

com.example.app.qml.bb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
SUMMARY = "QML App"
SECTION = "webos/apps"
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"

DEPENDS = "qtbase qt-features-webos qtdeclarative pmloglib"
RDEPENDS:${PN} += "qml-webos-framework qml-webos-bridge"

WEBOS_VERSION="1.0.0"
PR = "r0"

inherit webos_qmake5
inherit webos_submissions
inherit webos_app

FILES:${PN} += "${webos_applicationsdir}"

A brief explanation of the above file:

  • Line(1~4) : Basic descriptions of the component.
  • Line(6) : A list of a package’s build dependencies. Add qtbase, qt-features-webos, qtdeclarative, and pmloglib.
  • Line(7) : A list of a package’s runtime dependencies (other packages) that must be installed in order for the built package to run correctly. Add qml-webos-bridge and qml-webos-framework.
  • Line(9) : Version of the component. For the webOS OSE component, this field is mandatory.
  • Line(10) : Revision version of the recipe. Each recipe requires a counter to track its modification history. Make sure that you increment the version when you edit the recipe, unless you only change the value of the WEBOS_VERSION field or comments.
  • Line(12) : Instruct OpenEmbedded that the component uses QMake for configuration, which is the preferred choice for webOS components.
  • Line(13) : Instruct OpenEmbedded to use the WEBOS_VERSION value as the component version number. If you develop your component on a local repository, this entry is required.
  • Line(14) : Inherit webos_app, because the component is an app.
  • Line(16) : ${webos_applicationsdir} indicates /usr/palm/applications. ${PN} is the package name, which is set to com.example.app.qml.

Configure the Local Source Directory

To build a component that is located on the local system, you must specify the directory information.

You must move the configuration file into webOS OSE project directory.

  • Configuration file: samples/qml-apps/build-config/webos-local.conf
  • Destination Directory: build-webos

For the sample QML app (com.example.app.qml), you must provide the local path where the source exists.

webos-local.conf
1
2
3
4
INHERIT += "externalsrc"
EXTERNALSRC:pn-com.example.app.qml = "/home/username/project/com.example.app.qml/"
EXTERNALSRC_BUILD:pn-com.example.app.qml = "/home/username/project/com.example.app.qml/build/"
PR:append:pn-com.example.app.qml =".local0"

A brief explanation of the above file:

  • Line(1) : Inherit externalsrc bbclass file.
  • Line(2) : The local source directory. The syntax of the property is EXTERNALSRC:pn-<component>. For the value, input "<absolute path of the project directory>"
  • Line(3) : The local build directory. The syntax of the property is EXTERNALSRC_BUILD:pn-<component>. For the value, input "<absolute path of the project directory>/build/"
  • Line(4) : The appended revision version (PR) for building local source files. The syntax of the property is PR:append:pn-<component>. This property is optional.
Note
We recommend that you add a trailing slash (/) at the end of all local directory paths, as in Line(2) and Line(3).

Build the App

To build the component on the OpenEmbedded environment, enter the following commands on the shell:

build-webos$ source oe-init-build-env
build-webos$ bitbake com.example.app.qml

Step 4: Run and Verify the QML App

After building the app, you must verify its functionality.

  1. Copy the IPK to the target.

    When the build is successful, oe-related directories are created under the project root directory. These directories are linked to the directory where the build output is generated from the actual build-webos sub-directory.

    com.example.app.qml
    ├── README.md
    ├── appinfo.json
    ├── build
    ├── com.example.app.qml.pro
    ├── icon.png
    ├── main.qml
    ├── oe-logs -> /home/username/build/build-webos/BUILD/work/raspberrypi4_64-webos-linux-gnueabi/com.example.app.qml/1.0.0-r0.local0/temp
    └── oe-workdir -> /home/username/build/build-webos/BUILD/work/raspberrypi4_64-webos-linux-gnueabi/com.example.app.qml/1.0.0-r0.local0
    

    If you go to oe-workdir/deploy-ipks/raspberrypi4_64, you can see com.example.app.qml_1.0.0-r0.local0_raspberrypi4_64.ipk file.

    com.example.app.qml/oe-workdir/deploy-ipks/raspberrypi4_64$
    └── com.example.app.qml_1.0.0-r0.local0_raspberrypi4_64.ipk
    

    Copy the IPK file to the target device using the scp command.

    ~/project/com.example.app.qml/oe-workdir/deploy-ipks/raspberrypi4_64$ scp com.example.app.qml_1.0.0-r0.local0_raspberrypi4_64.ipk root@<target IP address>:/media/internal/downloads/
    
  2. Install the app on the target.

    Connect to the target using the ssh command and install com.example.app.qml_1.0.0-r0.local0_raspberrypi4_64.ipk.

    $ ssh root@<target IP address>
    root@raspberrypi4-64:/sysroot/home/root# cd /media/internal/downloads/
    root@raspberrypi4-64:/media/internal/downloads# opkg install com.example.app.qml_1.0.0-r0.local0_raspberrypi4_64.ipk
    Installing com.example.app.qml (1.0.0) on root.
    Configuring com.example.app.qml.
    
  3. Discover the LS2 configuration files.

    To make LS2 daemon scan the LS2 configuration files of the app, use the ls-control command as follows.

    root@raspberrypi4-64:/media/internal/downloads# ls-control scan-services
      telling hub to reload setting and rescan all directories
    
    Note
    For the QML app, LS2 configuration files are generated during the build process. To run the app properly, you must make the system scan the newly generated configuration files.
  4. Scan the app.

    To make System and Application Manager (SAM) scan the app, restart SAM using the systemctl command. This step is required so that the app can be added to the app list, which in turn makes the app appear on the Launchpad.

    root@raspberrypi4-64:/# systemctl restart sam
    
    Note
    Rebooting the target after installing the app will have the same effect as running the ls-control and systemctl commands. However, using the commands allows you to continue testing without rebooting.
  5. Run the QML app.

    Drag the mouse cursor upward from the bottom of the screen (or swipe up from the bottom of the screen if you’re using a touch display).

    Note
    On webOS OSE 1.x, press the Windows key.

    Click the Launchpad icon.

    Launchpad icon

    Click the app icon to see the window titled “QML app” with the following page:

    QML app screen
  6. Verify the execution of the QML app.

    • Using SAM

      You can check whether the app is running by using SAM. For more SAM methods, see com.webos.service.applicationmanager.

      root@raspberrypi4-64:/# luna-send -i -f luna://com.webos.service.applicationmanager/running '{"subscribe":true}'
      {
          "subscribed": true,
          "running": [
              {
                  "webprocessid": "",
                  "instanceId": "d641285a-5289-43d7-ac87-df2a509446290",
                  "displayId": 0,
                  "defaultWindowType": "card",
                  "appType": "native_qml",
                  "id": "com.example.app.qml",
                  "processid": "1485",
                  "launchPointId": "com.example.app.qml_default"
              }
          ],
          "returnValue": true
      }
      
    • Using the app

      Click on the screen, and the UTC time is printed on the screen.

      QML app UTC time displayed
    • Using the log file

      You can use the journalctl command on the target for debugging the QML app. For details on how to use the command, see Viewing Logs.

      root@raspberrypi4-64:/# journalctl | grep UTC
      
      Nov 22 00:05:00 raspberrypi4-64 qml-runner[2446]: [] [pmlog] QMLApp LAUNCH_PARAMS {"params":{"displayAffinity":0}}
      Nov 22 00:05:00 raspberrypi4-64 qml-runner[2446]: [] [pmlog] QMLApp LAUNCH_PARAMS {"params":{}}
      Nov 22 00:05:00 raspberrypi4-64 qml-runner[2446]: [] [pmlog] QMLApp WINDOW_CHANGED {"status":4}
      Nov 22 00:05:01 raspberrypi4-64 qml-runner[2446]: [] [pmlog] QMLApp GETTIME {"utc":1637568301}
      

Step 5: Deploy the QML App

You are now ready to build the webOS image including the QML app and flash it to the target device.

Perform the following steps:

  1. Add the QML app to the build recipe file.

    • Filename: packagegroup-webos-extended.bb

    • Directory: build-webos/meta-webosose/meta-webos/recipes-core/packagegroups

    • Updates to be made: Add the QML app name to RDEPENDS:${PN} =

    ...
    RDEPENDS:${PN} = " \
        activitymanager \
        audiod \
        ...
        com.example.app.qml \
        ${VIRTUAL-RUNTIME_appinstalld} \
        ...
    

    For more details, see Yocto Project Reference Manual.

  2. Build the webOS image using the following commands:

    build-webos$ source oe-init-build-env
    build-webos$ bitbake webos-image
    
  3. Flash the generated webOS image to the SD card.

    • Path to image: build-webos/BUILD/deploy/images/raspberrypi4-64/webos-image-raspberrypi4-64.rootfs.wic
    build-webos/BUILD/deploy/images/raspberrypi4-64$ sudo dd bs=4M if=webos-image-raspberrypi4-64.rootfs.wic of=/dev/sdc
    

    For more details, see the Flashing webOS OSE page.

After rebooting, the QML app becomes available on the Launchpad.

Contents