summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@qt.io>2022-08-18 22:17:05 +0200
committerRobert Griebl <robert.griebl@qt.io>2022-09-16 13:15:00 +0200
commit22929f522153d66187e4cb930b71a11c0b4166c6 (patch)
treef3407c13f7cf50c5fcb3330ad8cce68f9474d973
parent1163402cf942842297dc4a24911f063da84fc97b (diff)
downloadqtapplicationmanager-22929f522153d66187e4cb930b71a11c0b4166c6.tar.gz
Intents: add broadcasts
This commit adds a new intent type: a broadcast. Broadcasts will be delivered to all applications (*), but they do not have the possibility to return a reply back to the sender. Implementation wise, they are just treated as normal requests that are multiplexed for every application. (*) This can be limited with the new handleOnlyWhenRunning flag. Change-Id: If9f954cf5e52707624b95c80c8e984dfd6c4315a Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Dominik Holland <dominik.holland@qt.io>
-rw-r--r--doc/intents.qdoc4
-rw-r--r--doc/manifest.qdoc9
-rw-r--r--examples/applicationmanager/intents/CMakeLists.txt3
-rw-r--r--examples/applicationmanager/intents/am-config.yaml15
-rw-r--r--examples/applicationmanager/intents/apps/intents.blue/info.yaml2
-rw-r--r--examples/applicationmanager/intents/apps/intents.green/info.yaml5
-rw-r--r--examples/applicationmanager/intents/apps/intents.red/info.yaml2
-rw-r--r--examples/applicationmanager/intents/doc/src/intents.qdoc30
-rw-r--r--examples/applicationmanager/intents/intents.pro4
-rw-r--r--examples/applicationmanager/intents/shared/IntentsApplicationWindow.qml23
-rw-r--r--examples/applicationmanager/intents/shared/IntentsUIPage.qml25
-rw-r--r--examples/applicationmanager/intents/system-ui.qml16
-rw-r--r--qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes3
-rw-r--r--qmltypes/QtApplicationManager/plugins.qmltypes12
-rw-r--r--src/application-lib/intentinfo.cpp13
-rw-r--r--src/application-lib/intentinfo.h4
-rw-r--r--src/application-lib/yamlpackagescanner.cpp3
-rw-r--r--src/intent-client-lib/intentclient.cpp59
-rw-r--r--src/intent-client-lib/intentclient.h9
-rw-r--r--src/intent-client-lib/intentclientrequest.cpp29
-rw-r--r--src/intent-client-lib/intentclientrequest.h8
-rw-r--r--src/intent-server-lib/intent.cpp20
-rw-r--r--src/intent-server-lib/intent.h10
-rw-r--r--src/intent-server-lib/intentserver.cpp121
-rw-r--r--src/intent-server-lib/intentserver.h2
-rw-r--r--src/intent-server-lib/intentserverrequest.cpp31
-rw-r--r--src/intent-server-lib/intentserverrequest.h14
-rw-r--r--src/intent-server-lib/intentserversysteminterface.cpp5
-rw-r--r--src/intent-server-lib/intentserversysteminterface.h4
-rw-r--r--src/launcher-lib/intentclientdbusimplementation.cpp3
-rw-r--r--src/manager-lib/intentaminterface.cpp77
-rw-r--r--src/manager-lib/intentaminterface.h16
-rw-r--r--tests/auto/applicationinfo/tst_applicationinfo.cpp2
-rw-r--r--tests/auto/qml/intents/apps/intents1/info.yaml1
-rw-r--r--tests/auto/qml/intents/apps/intents1/intents1.qml7
-rw-r--r--tests/auto/qml/intents/apps/intents2/info.yaml2
-rw-r--r--tests/auto/qml/intents/apps/intents2/intents2.qml7
-rw-r--r--tests/auto/qml/intents/tst_intents.qml46
38 files changed, 475 insertions, 171 deletions
diff --git a/doc/intents.qdoc b/doc/intents.qdoc
index 6021cb8d..127ec3fa 100644
--- a/doc/intents.qdoc
+++ b/doc/intents.qdoc
@@ -48,10 +48,10 @@ involved in an intent request:
\li the \c applicationId denotes the application that is responsible for handling the request. This
directly maps to the ApplicationObject::id value.
If the handling application is in fact the System UI, this id will have the special value
- \c {:sysui:}.
+ IntentClient::systemUiId (\c{:sysui:}).
\li the \c requestingApplicationId does the same as the \c applicationId, but for the application
sending the request. If the requesting application is in fact the System UI, this id will have
- the special value \c {:sysui:}.
+ the special value IntentClient::systemUiId (\c{:sysui:}).
\endlist
Arbitrary data can be attached to an intent request (see IntentClient::sendIntentRequest) and to an
diff --git a/doc/manifest.qdoc b/doc/manifest.qdoc
index daf28286..df328344 100644
--- a/doc/manifest.qdoc
+++ b/doc/manifest.qdoc
@@ -285,6 +285,15 @@ The fields used for each item within the \c intents list are as follows:
application will be used as the default handler for all intents.
The given application id has to be from within this package.
\row
+ \li \c handleOnlyWhenRunning
+ \li bool
+ \li By default, applications are automatically started when a request is targeted at them, but
+ they are not currently running. Setting the \c handleOnlyWhenRunning flag to \c true will
+ prevent this behavior and any requests for this intent will only be forwarded to its
+ handling application, if the application is actuallly running.
+ This is useful for system-wide broadcasts that are only relevant if an application is active
+ (e.g. changes in internet availability).
+\row
\li \c requiredCapabilities
\li list<string>
\li Limit the client applications that are able to call this intent to applications having at
diff --git a/examples/applicationmanager/intents/CMakeLists.txt b/examples/applicationmanager/intents/CMakeLists.txt
index 218de802..b3c38a93 100644
--- a/examples/applicationmanager/intents/CMakeLists.txt
+++ b/examples/applicationmanager/intents/CMakeLists.txt
@@ -19,7 +19,6 @@ find_package(Qt6 COMPONENTS Gui)
find_package(Qt6 COMPONENTS AppManMainPrivate)
qt6_am_add_systemui_wrapper(intents
- MAIN_QML_FILE system-ui.qml
+ CONFIG_YAML am-config.yaml
EXTRA_FILES apps shared
- EXTRA_ARGS --builtin-apps-manifest-dir apps -o \"ui: { style: Material }\"
)
diff --git a/examples/applicationmanager/intents/am-config.yaml b/examples/applicationmanager/intents/am-config.yaml
new file mode 100644
index 00000000..7d995bf3
--- /dev/null
+++ b/examples/applicationmanager/intents/am-config.yaml
@@ -0,0 +1,15 @@
+formatVersion: 1
+formatType: am-configuration
+---
+applications:
+ builtinAppsManifestDir: "${CONFIG_PWD}/apps"
+
+ui:
+ mainQml: "${CONFIG_PWD}/system-ui.qml"
+ style: Material
+
+# development setup:
+flags:
+ noSecurity: yes
+ noUiWatchdog: yes
+ developmentMode: yes
diff --git a/examples/applicationmanager/intents/apps/intents.blue/info.yaml b/examples/applicationmanager/intents/apps/intents.blue/info.yaml
index 7ce3930e..31abd47f 100644
--- a/examples/applicationmanager/intents/apps/intents.blue/info.yaml
+++ b/examples/applicationmanager/intents/apps/intents.blue/info.yaml
@@ -16,7 +16,7 @@ intents:
- id: scale-window
name:
en: Scale Blue
-- id: blink-window
+- id: broadcast/blink-window
name:
en: Blink Blue
- id: blue-window-private
diff --git a/examples/applicationmanager/intents/apps/intents.green/info.yaml b/examples/applicationmanager/intents/apps/intents.green/info.yaml
index 40575f03..dc6bef5f 100644
--- a/examples/applicationmanager/intents/apps/intents.green/info.yaml
+++ b/examples/applicationmanager/intents/apps/intents.green/info.yaml
@@ -12,6 +12,7 @@ intents:
- id: rotate-window
name:
en: Rotate Green
-- id: scale-window
+- id: broadcast/blink-window
name:
- en: Scale Green
+ en: Blink Green
+ handleOnlyWhenRunning: yes
diff --git a/examples/applicationmanager/intents/apps/intents.red/info.yaml b/examples/applicationmanager/intents/apps/intents.red/info.yaml
index d61ba316..d95f9b29 100644
--- a/examples/applicationmanager/intents/apps/intents.red/info.yaml
+++ b/examples/applicationmanager/intents/apps/intents.red/info.yaml
@@ -17,6 +17,6 @@ intents:
- id: scale-window
name:
en: Scale Red
-- id: blink-window
+- id: broadcast/blink-window
name:
en: Blink Red
diff --git a/examples/applicationmanager/intents/doc/src/intents.qdoc b/examples/applicationmanager/intents/doc/src/intents.qdoc
index ef7cfce1..86fed1e6 100644
--- a/examples/applicationmanager/intents/doc/src/intents.qdoc
+++ b/examples/applicationmanager/intents/doc/src/intents.qdoc
@@ -24,8 +24,8 @@ Each application, as well as the System UI, look alike and have the same functio
the UI: You can choose from one of the available intent IDs in the top combo-box (labeled \e Intent),
and optionally also specify the corresponding application that should handle the chosen request
(labeled \e Application).
-Clicking the \e Request button will create and send the corresponding intent request to the
-application manager's IntentServer for handling:
+Clicking the \e Request / \e Broadcast button will create and send the corresponding intent request
+to the application manager's IntentServer for handling:
\list
\li The combination of \e Intent and \e Application was valid and the target application was able to
handle the request; the \l{IntentRequest::result}{result of this request} will be shown
@@ -53,6 +53,7 @@ Each application is put in its own separate directory as described below. Since
directory.
\list
+\li \tt{am-config.yaml}
\li \tt{system-ui.qml}
\li \tt{\b{apps}}
\list
@@ -91,38 +92,42 @@ definition of the intents that this application can handle.
Assuming the \c appman executable is in your path, you can run the System UI as follows:
\badcode
-examples/applicationmanager/intents$ appman --builtin-apps-manifest-dir ./apps system-ui.qml
+examples/applicationmanager/intents$ appman -c am-config.yaml
\endcode
-Adding \c{-o "ui: { style: material }" } will make the example look and feel a lot nicer.
-
And this is what you should see:
\image intents-launched.png
-For information on these and other command line options you can run \tt{appman --help} or have a
-look at the \l{Configuration} documentation.
+For information on the contents of \c am-config.yaml and other command line options you can run
+\tt{appman --help} or have a look at the \l{Configuration} documentation.
\section1 Application Implementation
All the applications (red, green and blue) are identical and their \c main.qml just
-instantiates the shared IntentsApplicationWindow component.
+instantiates the shared \c IntentsApplicationWindow component.
\snippet applicationmanager/intents/apps/intents.red/main.qml Main
The IntentsApplicationWindow component is actually a top-level ApplicationManagerWindow, with its
UI contents being defined by instantiating the \c IntentsUIPage component, that is also shared. This
UI component does not have any intent-specific code, so the actual sending is done in the signal
-handler attached to the IntentsUIPage request signal:
+handlers attached to the IntentsUIPage request and broadcast signals:
\snippet applicationmanager/intents/shared/IntentsApplicationWindow.qml Send Intent
+and
+
+\snippet applicationmanager/intents/shared/IntentsApplicationWindow.qml Broadcast Intent
+
After calling IntentClient::sendIntentRequest with the parameters as chosen in the UI, the
example code will connect a function object to the \l{IntentRequest::replyReceived}
{request's replyReceived} signal. The result is placed in the \e Request field in the UI.
+Broadcasts on the other hand do not generate replies.
-In addition, it defines all the necessary IntentHandlers for the applications, for example:
+In addition, IntentsApplicationWindow defines all the necessary IntentHandlers for the applications,
+for example:
\snippet applicationmanager/intents/shared/IntentsApplicationWindow.qml Intent Handler
@@ -153,7 +158,7 @@ intents in the \b Blue application.
The \b Green application defines only two available intents. Note that even though this
-application has an IntentHandler for the \c blink-window intent through the shared
+application has an IntentHandler for the \c scale-window intent through the shared
IntentsApplicationWindow component, this handler is never called, since this intent ID is not
registered with the system via the \c info.yaml manifest:
@@ -161,6 +166,9 @@ registered with the system via the \c info.yaml manifest:
\skipto intents:
\printto
+On top of that, the \c broadcast/blink-window intent is marked as \c{handleOnlyWhenRunning: yes},
+which prevents auto-starting the \c Green application on broadcasts of this specific intent.
+
The \b Blue application has the most complex intent definition. In addition to handling the same
three intents as the \b Red application, it registers the \c blue-window-private intent that has the
attribute \c{visibility: private}. Private intents can only be requested from the same application
diff --git a/examples/applicationmanager/intents/intents.pro b/examples/applicationmanager/intents/intents.pro
index af1c0238..dd57e0ef 100644
--- a/examples/applicationmanager/intents/intents.pro
+++ b/examples/applicationmanager/intents/intents.pro
@@ -20,12 +20,12 @@ target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/intents
INSTALLS += target
AM_COPY_DIRECTORIES += apps shared
-AM_COPY_FILES += system-ui.qml
+AM_COPY_FILES += system-ui.qml am-config.yaml
prefix_build:tpath = $$target.path
else:tpath = $$_PRO_FILE_PWD_
-AM_DEFAULT_ARGS = --builtin-apps-manifest-dir $$tpath/apps -o \"ui: { style: Material }\" $$tpath/system-ui.qml
+AM_DEFAULT_ARGS = -c $$tpath/am-config.yaml
example_sources.path = $$target.path
example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES
diff --git a/examples/applicationmanager/intents/shared/IntentsApplicationWindow.qml b/examples/applicationmanager/intents/shared/IntentsApplicationWindow.qml
index a6ebecf0..6f42eb7f 100644
--- a/examples/applicationmanager/intents/shared/IntentsApplicationWindow.qml
+++ b/examples/applicationmanager/intents/shared/IntentsApplicationWindow.qml
@@ -3,9 +3,9 @@
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.11
-import QtApplicationManager.Application 2.0
-import QtApplicationManager 2.0
+import QtQuick
+import QtApplicationManager.Application
+import QtApplicationManager
ApplicationManagerWindow {
id: root
@@ -19,7 +19,7 @@ ApplicationManagerWindow {
//! [Send Intent]
onRequest: (intentId, applicationId, parameters) => {
- var request = IntentClient.sendIntentRequest(intentId, applicationId, parameters)
+ let request = IntentClient.sendIntentRequest(intentId, applicationId, parameters)
request.onReplyReceived.connect(function() {
intentPage.setResult(request.requestId, request.succeeded,
request.succeeded ? request.result : request.errorMessage)
@@ -27,6 +27,12 @@ ApplicationManagerWindow {
}
//! [Send Intent]
+ //! [Broadcast Intent]
+ onBroadcast: (intentId, parameters) => {
+ IntentClient.broadcastIntentRequest(intentId, parameters)
+ }
+ //! [Broadcast Intent]
+
//! [Intent Animation]
RotationAnimation on rotation {
id: rotationAnimation
@@ -76,18 +82,17 @@ ApplicationManagerWindow {
}
IntentHandler {
- intentIds: "blink-window"
+ intentIds: "blue-window-private"
onRequestReceived: (request) => {
- blinkAnimation.start()
+ blueAnimation.start()
request.sendReply({ "done": true })
}
}
IntentHandler {
- intentIds: "blue-window-private"
+ intentIds: "broadcast/blink-window"
onRequestReceived: (request) => {
- blueAnimation.start()
- request.sendReply({ "done": true })
+ blinkAnimation.start()
}
}
}
diff --git a/examples/applicationmanager/intents/shared/IntentsUIPage.qml b/examples/applicationmanager/intents/shared/IntentsUIPage.qml
index d71fa21c..22432b4c 100644
--- a/examples/applicationmanager/intents/shared/IntentsUIPage.qml
+++ b/examples/applicationmanager/intents/shared/IntentsUIPage.qml
@@ -3,9 +3,10 @@
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.11
-import QtQuick.Controls 2.4
-import QtQuick.Layouts 1.4
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtApplicationManager
Rectangle {
id: root
@@ -19,6 +20,7 @@ Rectangle {
}
signal request(string intentId, string applicationId, var parameters)
+ signal broadcast(string intentId, var parameters)
color: "white"
Rectangle {
@@ -50,25 +52,32 @@ Rectangle {
Label { text: "Intent:" }
ComboBox {
id: cbIntent
- model: [ "rotate-window", "scale-window", "blink-window", "blue-window-private" ]
+ model: [ "rotate-window", "scale-window", "blue-window-private", "broadcast/blink-window" ]
+ property bool isBroadcast: currentText.startsWith("broadcast/")
Layout.fillWidth: true
}
Label { text: "Application:" }
ComboBox {
id: cbApplication
- model: [ "<not specified>", "intents.red", "intents.green", "intents.blue", ":sysui:" ]
+ model: [ "<not specified>", "intents.red", "intents.green", "intents.blue", IntentClient.systemUiId ]
+ enabled: !cbIntent.isBroadcast
+ property bool needsDisambiguation: currentIndex === 0
Layout.fillWidth: true
}
Button {
id: btRequest
- text: "Request"
+ text: cbIntent.isBroadcast ? "Broadcast" : "Request"
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
onClicked: {
- var appId = cbApplication.currentIndex === 0 ? "" : cbApplication.currentText
- root.request(cbIntent.currentText, appId, { "Request": { "Parameters": { "Testing": 1 }}})
+ if (cbIntent.isBroadcast) {
+ root.broadcast(cbIntent.currentText, { "Broadcast": { "Parameters": { "Testing": 42 }}})
+ } else {
+ let appId = cbApplication.needsDisambiguation ? "" : cbApplication.currentText
+ root.request(cbIntent.currentText, appId, { "Request": { "Parameters": { "Testing": 1 }}})
+ }
}
}
Label { text: "Request:" }
diff --git a/examples/applicationmanager/intents/system-ui.qml b/examples/applicationmanager/intents/system-ui.qml
index 31e5a5cb..b4708532 100644
--- a/examples/applicationmanager/intents/system-ui.qml
+++ b/examples/applicationmanager/intents/system-ui.qml
@@ -3,11 +3,11 @@
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.11
-import QtQuick.Controls 2.4
-import QtQuick.Layouts 1.4
-import QtApplicationManager.SystemUI 2.0
-import QtApplicationManager 2.0
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtApplicationManager.SystemUI
+import QtApplicationManager
import "shared"
Item {
@@ -57,12 +57,16 @@ Item {
title: "System UI"
onRequest: (intentId, applicationId, parameters) => {
- var request = IntentClient.sendIntentRequest(intentId, applicationId, parameters)
+ let request = IntentClient.sendIntentRequest(intentId, applicationId, parameters)
request.onReplyReceived.connect(function() {
sysui_page.setResult(request.requestId, request.succeeded,
request.succeeded ? request.result : request.errorMessage)
})
}
+ onBroadcast: (intentId, parameters) => {
+ IntentClient.broadcastIntentRequest(intentId, parameters)
+ }
+
RotationAnimation on rotation {
id: rotationAnimation
running: false
diff --git a/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes b/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
index c2192f9f..858fc226 100644
--- a/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
+++ b/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
@@ -94,7 +94,7 @@ Module {
}
Component {
name: "Intent"
- exports: [ "QtApplicationManager.SystemUI/IntentObject 2.0" ]
+ exports: [ "QtApplicationManager.SystemUI/IntentObject 2.1" ]
exportMetaObjectRevisions: [ 0 ]
prototype: "QObject"
isCreatable: false
@@ -111,6 +111,7 @@ Module {
Property { name: "description"; type: "string"; isReadonly: true }
Property { name: "descriptions"; type: "QVariantMap"; isReadonly: true }
Property { name: "categories"; type: "QStringList"; isReadonly: true }
+ Property { name: "handleOnlyWhenRunning"; type: "bool"; isReadonly: true }
}
Component {
name: "IntentServer"
diff --git a/qmltypes/QtApplicationManager/plugins.qmltypes b/qmltypes/QtApplicationManager/plugins.qmltypes
index cad720f2..befa8dee 100644
--- a/qmltypes/QtApplicationManager/plugins.qmltypes
+++ b/qmltypes/QtApplicationManager/plugins.qmltypes
@@ -135,7 +135,7 @@ Module {
}
Component {
name: "IntentClientRequest"
- exports: [ "QtApplicationManager/IntentRequest 2.0" ]
+ exports: [ "QtApplicationManager/IntentRequest 2.1" ]
exportMetaObjectRevisions: [ 0 ]
prototype: "QObject"
isCreatable: false
@@ -149,6 +149,7 @@ Module {
Property { name: "succeeded"; type: "bool"; isReadonly: true }
Property { name: "errorMessage"; type: "string"; isReadonly: true }
Property { name: "result"; type: "QVariantMap"; isReadonly: true }
+ Property { name: "broadcast"; type: "bool"; isReadonly: true }
Signal {
name: "requestIdChanged"
}
@@ -166,10 +167,11 @@ Module {
}
Component {
name: "IntentClient"
- exports: [ "QtApplicationManager/IntentClient 2.0" ]
+ exports: [ "QtApplicationManager/IntentClient 2.1" ]
exportMetaObjectRevisions: [ 0 ]
prototype: "QObject"
isSingleton: true
+ Property { name: "systemUiId"; type: "string"; isReadonly: true }
Method {
name: "sendIntentRequest"
type: "IntentClientRequest"; isPointer: true;
@@ -183,6 +185,12 @@ Module {
Parameter { name: "applicationId"; type: "string"; }
Parameter { name: "parameters"; type: "QVariantMap"; }
}
+ Method {
+ name: "broadcastIntentRequest"
+ type: "bool"
+ Parameter { name: "intentId"; type: "string"; }
+ Parameter { name: "parameters"; type: "QVariantMap"; }
+ }
}
Component {
name: "Notification"
diff --git a/src/application-lib/intentinfo.cpp b/src/application-lib/intentinfo.cpp
index b4854c13..f57a5688 100644
--- a/src/application-lib/intentinfo.cpp
+++ b/src/application-lib/intentinfo.cpp
@@ -64,8 +64,13 @@ QString IntentInfo::icon() const
return m_icon.isEmpty() ? m_packageInfo->icon() : m_icon;
}
+bool IntentInfo::handleOnlyWhenRunning() const
+{
+ return m_handleOnlyWhenRunning;
+}
+
-const quint32 IntentInfo::DataStreamVersion = 2;
+const quint32 IntentInfo::DataStreamVersion = 3;
void IntentInfo::writeToDataStream(QDataStream &ds) const
@@ -80,7 +85,8 @@ void IntentInfo::writeToDataStream(QDataStream &ds) const
<< m_categories
<< m_names
<< m_descriptions
- << m_icon;
+ << m_icon
+ << m_handleOnlyWhenRunning;
}
IntentInfo *IntentInfo::readFromDataStream(PackageInfo *pkg, QDataStream &ds)
@@ -98,7 +104,8 @@ IntentInfo *IntentInfo::readFromDataStream(PackageInfo *pkg, QDataStream &ds)
>> intent->m_categories
>> intent->m_names
>> intent->m_descriptions
- >> intent->m_icon;
+ >> intent->m_icon
+ >> intent->m_handleOnlyWhenRunning;
intent->m_visibility = (visibilityStr == qSL("public")) ? Public : Private;
intent->m_categories.sort();
diff --git a/src/application-lib/intentinfo.h b/src/application-lib/intentinfo.h
index 08df4315..94b9d2b7 100644
--- a/src/application-lib/intentinfo.h
+++ b/src/application-lib/intentinfo.h
@@ -44,6 +44,8 @@ public:
QMap<QString, QString> descriptions() const;
QString icon() const;
+ bool handleOnlyWhenRunning() const;
+
void writeToDataStream(QDataStream &ds) const;
static IntentInfo *readFromDataStream(PackageInfo *pkg, QDataStream &ds);
@@ -60,6 +62,8 @@ private:
QMap<QString, QString> m_descriptions; // language -> description
QString m_icon; // relative to the manifest's location
+ bool m_handleOnlyWhenRunning = false;
+
friend class YamlPackageScanner;
Q_DISABLE_COPY(IntentInfo)
};
diff --git a/src/application-lib/yamlpackagescanner.cpp b/src/application-lib/yamlpackagescanner.cpp
index a34bfb80..f092172b 100644
--- a/src/application-lib/yamlpackagescanner.cpp
+++ b/src/application-lib/yamlpackagescanner.cpp
@@ -318,6 +318,9 @@ PackageInfo *YamlPackageScanner::scan(QIODevice *source, const QString &fileName
intentInfo->m_categories = p->parseStringOrStringList();
intentInfo->m_categories.sort();
});
+ intentFields.emplace_back("handleOnlyWhenRunning", false, YamlParser::Scalar, [&intentInfo](YamlParser *p) {
+ intentInfo->m_handleOnlyWhenRunning = p->parseScalar().toBool();
+ });
p->parseFields(intentFields);
diff --git a/src/intent-client-lib/intentclient.cpp b/src/intent-client-lib/intentclient.cpp
index 17f2ac25..4078108f 100644
--- a/src/intent-client-lib/intentclient.cpp
+++ b/src/intent-client-lib/intentclient.cpp
@@ -71,6 +71,20 @@ IntentClient *IntentClient::instance()
return s_instance;
}
+/*! \qmlproperty string IntentClient::systemUiId
+
+ The hardcoded, special application id for targeting the System UI with an intent request.
+*/
+QString IntentClient::systemUiId() const
+{
+ return qSL(":sysui:");
+}
+
+int IntentClient::replyFromSystemTimeout() const
+{
+ return m_replyFromSystemTimeout;
+}
+
void IntentClient::setReplyFromSystemTimeout(int timeout)
{
m_replyFromSystemTimeout = timeout;
@@ -145,6 +159,9 @@ IntentClientRequest *IntentClient::sendIntentRequest(const QString &intentId, co
it. The request will fail, if this specified application doesn't exist or can't handle this
specific request, even though other applications would be able to do it.
+ There is the special application id \c IntentClient.systemUiId which can be used to target the
+ System UI.
+
\sa sendIntentRequest
*/
IntentClientRequest *IntentClient::sendIntentRequest(const QString &intentId, const QString &applicationId,
@@ -152,6 +169,8 @@ IntentClientRequest *IntentClient::sendIntentRequest(const QString &intentId, co
{
if (intentId.isEmpty())
return nullptr;
+ if (applicationId == qSL(":broadcast:")) // reserved
+ return nullptr;
//TODO: check that parameters only contains basic datatypes. convertFromJSVariant() does most of
// this already, but doesn't bail out on unconvertible types (yet)
@@ -162,13 +181,37 @@ IntentClientRequest *IntentClient::sendIntentRequest(const QString &intentId, co
return icr;
}
+/*! \qmlmethod bool IntentClient::broadcastIntentRequest(string intentId, var parameters)
+ \since 6.5
+
+ Broadcasts an intent request with the given \a intentId to the system. The additional
+ \a parameters are specific to the requested \a intentId, but the format is always the same: a
+ standard JavaScript object, which can also be just empty if the requested intent doesn't
+ require any parameters.
+
+ Broadcast requests do not generate replies. The return value is only ever \c false, if you
+ call this function with invalid arguments.
+*/
+bool IntentClient::broadcastIntentRequest(const QString &intentId, const QVariantMap &parameters)
+{
+ if (intentId.isEmpty())
+ return false;
+
+ //TODO: check that parameters only contains basic datatypes. convertFromJSVariant() does most of
+ // this already, but doesn't bail out on unconvertible types (yet)
+
+ requestToSystem(m_systemInterface->currentApplicationId(this), intentId, qSL(":broadcast:"), parameters);
+ return true;
+}
+
IntentClientRequest *IntentClient::requestToSystem(const QString &requestingApplicationId,
const QString &intentId, const QString &applicationId,
const QVariantMap &parameters)
{
IntentClientRequest *ir = new IntentClientRequest(IntentClientRequest::Direction::ToSystem,
requestingApplicationId, QUuid(),
- intentId, applicationId, parameters);
+ intentId, applicationId, parameters,
+ applicationId == qSL(":broadcast:"));
qCDebug(LogIntents) << "Application" << requestingApplicationId << "created an intent request for"
<< intentId << "(application:" << applicationId << ")";
@@ -181,6 +224,11 @@ void IntentClient::requestToSystemFinished(IntentClientRequest *icr, const QUuid
if (!icr)
return;
+ if (icr->isBroadcast()) {
+ icr->deleteLater();
+ return;
+ }
+
if (error) {
icr->setErrorMessage(errorMessage);
} else if (newRequestId.isNull()) {
@@ -228,17 +276,20 @@ void IntentClient::requestToApplication(const QUuid &requestId, const QString &i
const QString &requestingApplicationId,
const QString &applicationId, const QVariantMap &parameters)
{
+ bool broadcast = (requestingApplicationId == qSL(":broadcast:"));
+
qCDebug(LogIntents) << "Client: Incoming intent request" << requestId << "to application" << applicationId
- << "for intent" << intentId << "parameters" << parameters;
+ << "for intent" << intentId << (broadcast ? "(broadcast)" : "") << "parameters" << parameters;
IntentClientRequest *icr = new IntentClientRequest(IntentClientRequest::Direction::ToApplication,
requestingApplicationId, requestId, intentId,
- applicationId, parameters);
+ applicationId, parameters, broadcast);
IntentHandler *handler = m_handlers.value(qMakePair(intentId, applicationId));
if (handler) {
QQmlEngine::setObjectOwnership(icr, QQmlEngine::JavaScriptOwnership);
- icr->startTimeout(m_replyFromApplicationTimeout);
+ if (!broadcast)
+ icr->startTimeout(m_replyFromApplicationTimeout);
emit handler->requestReceived(icr);
} else {
diff --git a/src/intent-client-lib/intentclient.h b/src/intent-client-lib/intentclient.h
index ecc224e8..a405b8c3 100644
--- a/src/intent-client-lib/intentclient.h
+++ b/src/intent-client-lib/intentclient.h
@@ -26,13 +26,17 @@ class IntentClientSystemInterface;
class IntentClient : public QObject
{
Q_OBJECT
- Q_CLASSINFO("AM-QmlType", "QtApplicationManager/IntentClient 2.0 SINGLETON")
+ Q_CLASSINFO("AM-QmlType", "QtApplicationManager/IntentClient 2.1 SINGLETON")
+ Q_PROPERTY(QString systemUiId READ systemUiId CONSTANT REVISION 1)
public:
~IntentClient() override;
static IntentClient *createInstance(IntentClientSystemInterface *systemInterface);
static IntentClient *instance();
+ QString systemUiId() const;
+
+ int replyFromSystemTimeout() const;
void setReplyFromSystemTimeout(int timeout);
void setReplyFromApplicationTimeout(int timeout);
@@ -50,6 +54,9 @@ public:
const QString &applicationId,
const QVariantMap &parameters);
+ Q_REVISION(1) Q_INVOKABLE bool broadcastIntentRequest(const QString &intentId,
+ const QVariantMap &parameters);
+
private:
void requestToSystemFinished(IntentClientRequest *icr, const QUuid &newRequestId,
bool error, const QString &errorMessage);
diff --git a/src/intent-client-lib/intentclientrequest.cpp b/src/intent-client-lib/intentclientrequest.cpp
index 09836df2..e077fe35 100644
--- a/src/intent-client-lib/intentclientrequest.cpp
+++ b/src/intent-client-lib/intentclientrequest.cpp
@@ -126,6 +126,15 @@ QT_BEGIN_NAMESPACE_AM
\sa succeeded
*/
+/*! \qmlproperty bool IntentRequest::broadcast
+ \readonly
+ \since 6.5
+
+ Only Set to \c true, if the received request is a broadcast.
+
+ \note Valid only on received requests.
+*/
+
/*! \qmlsignal IntentRequest::replyReceived()
This signal gets emitted when a reply to an intent request is available. The signal handler
@@ -145,7 +154,7 @@ IntentClientRequest::Direction IntentClientRequest::direction() const
IntentClientRequest::~IntentClientRequest()
{
// the incoming request was gc'ed on the JavaScript side, but no reply was sent yet
- if ((direction() == Direction::ToApplication) && !m_finished)
+ if ((direction() == Direction::ToApplication) && !m_finished && !m_broadcast)
sendErrorReply(qSL("Request not handled"));
}
@@ -174,6 +183,11 @@ QVariantMap IntentClientRequest::parameters() const
return m_parameters;
}
+bool IntentClientRequest::isBroadcast() const
+{
+ return m_broadcast;
+}
+
bool IntentClientRequest::succeeded() const
{
return m_succeeded;
@@ -210,6 +224,11 @@ void IntentClientRequest::sendReply(const QVariantMap &result)
qmlWarning(this) << "Calling IntentRequest::sendReply on requests originating from this application is a no-op.";
return;
}
+ if (m_broadcast) {
+ qmlWarning(this) << "Calling IntentRequest::sendReply on broadcast requests is a no-op.";
+ return;
+ }
+
IntentClient *ic = IntentClient::instance();
if (QThread::currentThread() != ic->thread()) {
@@ -241,6 +260,10 @@ void IntentClientRequest::sendErrorReply(const QString &errorMessage)
qmlWarning(this) << "Calling IntentRequest::sendErrorReply on requests originating from this application is a no-op.";
return;
}
+ if (m_broadcast) {
+ qmlWarning(this) << "Calling IntentRequest::sendErrorReply on broadcast requests is a no-op.";
+ return;
+ }
IntentClient *ic = IntentClient::instance();
if (QThread::currentThread() != ic->thread()) {
@@ -286,7 +309,8 @@ void IntentClientRequest::connectNotify(const QMetaMethod &signal)
IntentClientRequest::IntentClientRequest(Direction direction, const QString &requestingApplicationId,
const QUuid &id, const QString &intentId,
- const QString &applicationId, const QVariantMap &parameters)
+ const QString &applicationId, const QVariantMap &parameters,
+ bool broadcast)
: QObject()
, m_direction(direction)
, m_id(id)
@@ -294,6 +318,7 @@ IntentClientRequest::IntentClientRequest(Direction direction, const QString &req
, m_requestingApplicationId(requestingApplicationId)
, m_applicationId(applicationId)
, m_parameters(parameters)
+ , m_broadcast(broadcast)
{ }
void IntentClientRequest::setRequestId(const QUuid &requestId)
diff --git a/src/intent-client-lib/intentclientrequest.h b/src/intent-client-lib/intentclientrequest.h
index 02d7b39c..016ab349 100644
--- a/src/intent-client-lib/intentclientrequest.h
+++ b/src/intent-client-lib/intentclientrequest.h
@@ -19,7 +19,7 @@ class IntentClient;
class IntentClientRequest : public QObject
{
Q_OBJECT
- Q_CLASSINFO("AM-QmlType", "QtApplicationManager/IntentRequest 2.0 UNCREATABLE")
+ Q_CLASSINFO("AM-QmlType", "QtApplicationManager/IntentRequest 2.1 UNCREATABLE")
Q_PROPERTY(QUuid requestId READ requestId NOTIFY requestIdChanged)
Q_PROPERTY(Direction direction READ direction CONSTANT)
@@ -30,6 +30,7 @@ class IntentClientRequest : public QObject
Q_PROPERTY(bool succeeded READ succeeded NOTIFY replyReceived)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY replyReceived)
Q_PROPERTY(QVariantMap result READ result NOTIFY replyReceived)
+ Q_PROPERTY(bool broadcast READ isBroadcast CONSTANT REVISION 1)
public:
enum class Direction { ToSystem, ToApplication };
@@ -43,6 +44,7 @@ public:
QString applicationId() const;
QString requestingApplicationId() const;
QVariantMap parameters() const;
+ bool isBroadcast() const;
const QVariantMap result() const;
bool succeeded() const;
@@ -62,7 +64,8 @@ protected:
private:
IntentClientRequest(Direction direction, const QString &requestingApplicationId, const QUuid &id,
- const QString &intentId, const QString &applicationId, const QVariantMap &parameters);
+ const QString &intentId, const QString &applicationId, const QVariantMap &parameters,
+ bool broadcast);
void setRequestId(const QUuid &requestId);
void setResult(const QVariantMap &result);
@@ -83,6 +86,7 @@ private:
// ToSystem: we have received the final result or errorMessage via replyReceived()
// ToApplication: the request was handled and send(Error)Reply was called
bool m_finished = false;
+ bool m_broadcast = false;
Q_DISABLE_COPY(IntentClientRequest)
diff --git a/src/intent-server-lib/intent.cpp b/src/intent-server-lib/intent.cpp
index af4a4549..95992a8b 100644
--- a/src/intent-server-lib/intent.cpp
+++ b/src/intent-server-lib/intent.cpp
@@ -142,6 +142,18 @@ QT_BEGIN_NAMESPACE_AM
If the intent does not specify a \c categories list, this will return the same as the
containing PackageObject::categories.
*/
+/*!
+ \qmlproperty bool IntentObject::handleOnlyWhenRunning
+ \readonly
+ \since 6.5
+
+ By default, applications are automatically started when a request is targeted at them, but
+ they are not currently running. If this property is set to \c true, then any requests for this
+ intent will only be forwarded to its handling application, if the application is actuallly
+ running.
+ This is useful for system-wide broadcasts that are only relevant if an application is active
+ (e.g. changes in internet availability).
+*/
Intent::Intent()
@@ -151,7 +163,7 @@ Intent::Intent(const QString &id, const QString &packageId, const QString &appli
const QStringList &capabilities, Intent::Visibility visibility,
const QVariantMap &parameterMatch, const QMap<QString, QString> &names,
const QMap<QString, QString> &descriptions, const QUrl &icon,
- const QStringList &categories)
+ const QStringList &categories, bool handleOnlyWhenRunning)
: m_intentId(id)
, m_visibility(visibility)
, m_requiredCapabilities(capabilities)
@@ -162,6 +174,7 @@ Intent::Intent(const QString &id, const QString &packageId, const QString &appli
, m_descriptions(descriptions)
, m_categories(categories)
, m_icon(icon)
+ , m_handleOnlyWhenRunning(handleOnlyWhenRunning)
{
}
@@ -273,6 +286,11 @@ QStringList Intent::categories() const
return m_categories;
}
+bool Intent::handleOnlyWhenRunning() const
+{
+ return m_handleOnlyWhenRunning;
+}
+
QT_END_NAMESPACE_AM
#include "moc_intent.cpp"
diff --git a/src/intent-server-lib/intent.h b/src/intent-server-lib/intent.h
index 5ccdc6d6..dc9a7350 100644
--- a/src/intent-server-lib/intent.h
+++ b/src/intent-server-lib/intent.h
@@ -17,7 +17,7 @@ QT_BEGIN_NAMESPACE_AM
class Intent : public QObject
{
Q_OBJECT
- Q_CLASSINFO("AM-QmlType", "QtApplicationManager.SystemUI/IntentObject 2.0 UNCREATABLE")
+ Q_CLASSINFO("AM-QmlType", "QtApplicationManager.SystemUI/IntentObject 2.1 UNCREATABLE")
Q_PROPERTY(QString intentId READ intentId CONSTANT)
Q_PROPERTY(QString packageId READ packageId CONSTANT)
@@ -33,6 +33,8 @@ class Intent : public QObject
Q_PROPERTY(QVariantMap descriptions READ descriptions CONSTANT)
Q_PROPERTY(QStringList categories READ categories CONSTANT)
+ Q_PROPERTY(bool handleOnlyWhenRunning READ handleOnlyWhenRunning CONSTANT REVISION 1)
+
public:
enum Visibility {
Public,
@@ -59,12 +61,14 @@ public:
QVariantMap descriptions() const;
QStringList categories() const;
+ bool handleOnlyWhenRunning() const;
+
private:
Intent(const QString &intentId, const QString &packageId, const QString &applicationId,
const QStringList &capabilities, Intent::Visibility visibility,
const QVariantMap &parameterMatch, const QMap<QString, QString> &names,
const QMap<QString, QString> &descriptions, const QUrl &icon,
- const QStringList &categories);
+ const QStringList &categories, bool handleOnlyWhenRunning);
QString m_intentId;
Visibility m_visibility = Private;
@@ -79,6 +83,8 @@ private:
QStringList m_categories;
QUrl m_icon;
+ bool m_handleOnlyWhenRunning = false;
+
friend class IntentServer;
friend class IntentServerHandler;
friend class TestPackageLoader; // for auto tests only
diff --git a/src/intent-server-lib/intentserver.cpp b/src/intent-server-lib/intentserver.cpp
index 3813e5db..476e0c77 100644
--- a/src/intent-server-lib/intentserver.cpp
+++ b/src/intent-server-lib/intentserver.cpp
@@ -122,6 +122,7 @@ IntentServer *IntentServer::createInstance(IntentServerSystemInterface *systemIn
systemInterface->initialize(is.get());
qmlRegisterType<Intent>("QtApplicationManager.SystemUI", 2, 0, "IntentObject");
+ qmlRegisterType<Intent, 1>("QtApplicationManager.SystemUI", 2, 1, "IntentObject");
qmlRegisterType<IntentModel>("QtApplicationManager.SystemUI", 2, 0, "IntentModel");
qmlRegisterSingletonType<IntentServer>("QtApplicationManager.SystemUI", 2, 0, "IntentServer",
@@ -211,7 +212,7 @@ Intent *IntentServer::addIntent(const QString &id, const QString &packageId,
const QStringList &capabilities, Intent::Visibility visibility,
const QVariantMap &parameterMatch, const QMap<QString, QString> &names,
const QMap<QString, QString> &descriptions, const QUrl &icon,
- const QStringList &categories)
+ const QStringList &categories, bool handleOnlyWhenRunning)
{
try {
if (id.isEmpty())
@@ -233,7 +234,8 @@ Intent *IntentServer::addIntent(const QString &id, const QString &packageId,
}
auto intent = new Intent(id, packageId, handlingApplicationId, capabilities, visibility,
- parameterMatch, names, descriptions, icon, categories);
+ parameterMatch, names, descriptions, icon, categories,
+ handleOnlyWhenRunning);
QQmlEngine::setObjectOwnership(intent, QQmlEngine::CppOwnership);
beginInsertRows(QModelIndex(), rowCount(), rowCount());
@@ -502,12 +504,12 @@ void IntentServer::processRequestQueue()
qCDebug(LogIntents) << "Processing intent request" << isr << isr->requestId() << "in state" << isr->state();
if (isr->state() == IntentServerRequest::State::ReceivedRequest) { // step 1) disambiguate
- if (isr->handlingApplicationId().isEmpty()) {
+ if (!isr->isBroadcast() && !isr->selectedIntent()) {
// not disambiguated yet
if (!isSignalConnected(QMetaMethod::fromSignal(&IntentServer::disambiguationRequest))) {
// If the System UI does not react to the signal, then just use the first match.
- isr->setHandlingApplicationId(isr->potentialIntents().constFirst()->packageId());
+ isr->setSelectedIntent(isr->potentialIntents().constFirst());
} else {
m_disambiguationQueue.enqueue(isr);
isr->setState(IntentServerRequest::State::WaitingForDisambiguation);
@@ -524,67 +526,80 @@ void IntentServer::processRequestQueue()
isr->parameters());
}
}
- if (!isr->handlingApplicationId().isEmpty()) {
+ if (isr->isBroadcast() || isr->selectedIntent()) {
qCDebug(LogIntents) << "No disambiguation necessary/required for intent" << isr->intentId();
isr->setState(IntentServerRequest::State::Disambiguated);
}
}
if (isr->state() == IntentServerRequest::State::Disambiguated) { // step 2) start app
- auto handlerIPC = m_systemInterface->findClientIpc(isr->handlingApplicationId());
+ auto handlerIPC = m_systemInterface->findClientIpc(isr->selectedIntent()->applicationId());
if (!handlerIPC) {
- qCDebug(LogIntents) << "Intent handler" << isr->handlingApplicationId() << "is not running";
- m_startingAppQueue.enqueue(isr);
- isr->setState(IntentServerRequest::State::WaitingForApplicationStart);
- if (m_startingAppTimeout > 0) {
- QTimer::singleShot(m_startingAppTimeout, this, [this, isr]() {
- if (m_startingAppQueue.removeOne(isr)) {
- isr->setRequestFailed(qSL("Starting handler application timed out after %1 ms").arg(m_startingAppTimeout));
- enqueueRequest(isr);
- }
- });
+ qCDebug(LogIntents) << "Intent handler" << isr->selectedIntent()->applicationId() << "is not running";
+
+ if (isr->potentialIntents().constFirst()->handleOnlyWhenRunning()) {
+ qCDebug(LogIntents) << " * skipping, because 'handleOnlyWhenRunning' is set";
+ isr->setRequestFailed(qSL("Skipping delivery due to handleOnlyWhenRunning"));
+ } else {
+ m_startingAppQueue.enqueue(isr);
+ isr->setState(IntentServerRequest::State::WaitingForApplicationStart);
+ if (m_startingAppTimeout > 0) {
+ QTimer::singleShot(m_startingAppTimeout, this, [this, isr]() {
+ if (m_startingAppQueue.removeOne(isr)) {
+ isr->setRequestFailed(qSL("Starting handler application timed out after %1 ms").arg(m_startingAppTimeout));
+ enqueueRequest(isr);
+ }
+ });
+ }
+ m_systemInterface->startApplication(isr->selectedIntent()->applicationId());
}
- m_systemInterface->startApplication(isr->handlingApplicationId());
} else {
- qCDebug(LogIntents) << "Intent handler" << isr->handlingApplicationId() << "is already running";
+ qCDebug(LogIntents) << "Intent handler" << isr->selectedIntent()->applicationId() << "is already running";
isr->setState(IntentServerRequest::State::StartedApplication);
}
}
if (isr->state() == IntentServerRequest::State::StartedApplication) { // step 3) send request out
- auto clientIPC = m_systemInterface->findClientIpc(isr->handlingApplicationId());
+ auto clientIPC = m_systemInterface->findClientIpc(isr->selectedIntent()->applicationId());
if (!clientIPC) {
qCWarning(LogIntents) << "Could not find an IPC connection for application"
- << isr->handlingApplicationId() << "to forward the intent request"
+ << isr->selectedIntent()->applicationId() << "to forward the intent request"
<< isr->requestId();
isr->setRequestFailed(qSL("No IPC channel to reach target application."));
} else {
qCDebug(LogIntents) << "Sending intent request to handler application"
- << isr->handlingApplicationId();
- m_sentToAppQueue.enqueue(isr);
- isr->setState(IntentServerRequest::State::WaitingForReplyFromApplication);
- if (m_sentToAppTimeout > 0) {
- QTimer::singleShot(m_sentToAppTimeout, this, [this, isr]() {
- if (m_sentToAppQueue.removeOne(isr)) {
- isr->setRequestFailed(qSL("Waiting for reply from handler application timed out after %1 ms").arg(m_sentToAppTimeout));
- enqueueRequest(isr);
- }
- });
+ << isr->selectedIntent()->applicationId();
+ if (!isr->isBroadcast()) {
+ m_sentToAppQueue.enqueue(isr);
+ isr->setState(IntentServerRequest::State::WaitingForReplyFromApplication);
+ if (m_sentToAppTimeout > 0) {
+ QTimer::singleShot(m_sentToAppTimeout, this, [this, isr]() {
+ if (m_sentToAppQueue.removeOne(isr)) {
+ isr->setRequestFailed(qSL("Waiting for reply from handler application timed out after %1 ms").arg(m_sentToAppTimeout));
+ enqueueRequest(isr);
+ }
+ });
+ }
+ } else {
+ // there are no replies for broadcasts, so we simply skip this step
+ isr->setState(IntentServerRequest::State::ReceivedReplyFromApplication);
}
m_systemInterface->requestToApplication(clientIPC, isr);
}
}
if (isr->state() == IntentServerRequest::State::ReceivedReplyFromApplication) { // step 5) send reply to requesting app
- auto clientIPC = m_systemInterface->findClientIpc(isr->requestingApplicationId());
- if (!clientIPC) {
- qCWarning(LogIntents) << "Could not find an IPC connection for application"
- << isr->requestingApplicationId() << "to forward the Intent reply"
- << isr->requestId();
- } else {
- qCDebug(LogIntents) << "Forwarding intent reply" << isr->requestId()
- << "to requesting application" << isr->requestingApplicationId();
- m_systemInterface->replyFromSystem(clientIPC, isr);
+ if (!isr->isBroadcast()) {
+ auto clientIPC = m_systemInterface->findClientIpc(isr->requestingApplicationId());
+ if (!clientIPC) {
+ qCWarning(LogIntents) << "Could not find an IPC connection for application"
+ << isr->requestingApplicationId() << "to forward the Intent reply"
+ << isr->requestId();
+ } else {
+ qCDebug(LogIntents) << "Forwarding intent reply" << isr->requestId()
+ << "to requesting application" << isr->requestingApplicationId();
+ m_systemInterface->replyFromSystem(clientIPC, isr);
+ }
}
QMetaObject::invokeMethod(this, [isr]() { delete isr; }, Qt::QueuedConnection); // aka deleteLater for non-QObject
isr = nullptr;
@@ -595,6 +610,9 @@ void IntentServer::processRequestQueue()
QList<QObject *> IntentServer::convertToQml(const QVector<Intent *> &intents)
{
+ //TODO: we really should copy here, because the Intent pointers may die: a disambiguation might
+ // be active, while one of the apps involved is removed or updated
+
QList<QObject *> ol;
for (auto intent : intents)
ol << intent;
@@ -675,7 +693,7 @@ void IntentServer::internalDisambiguateRequest(const QUuid &requestId, bool reje
if (reject) {
isr->setRequestFailed(qSL("Disambiguation was rejected"));
} else if (isr->potentialIntents().contains(selectedIntent)) {
- isr->setHandlingApplicationId(selectedIntent->applicationId());
+ isr->setSelectedIntent(selectedIntent);
isr->setState(IntentServerRequest::State::Disambiguated);
} else {
qCWarning(LogIntents) << "IntentServer::acknowledgeDisambiguationRequest for intent"
@@ -694,7 +712,7 @@ void IntentServer::applicationWasStarted(const QString &applicationId)
bool foundOne = false;
for (auto it = m_startingAppQueue.cbegin(); it != m_startingAppQueue.cend(); ) {
auto isr = *it;
- if (isr->handlingApplicationId() == applicationId) {
+ if (isr->selectedIntent()->applicationId() == applicationId) {
qCDebug(LogIntents) << "Intent request" << isr->intentId()
<< "can now be forwarded to application" << applicationId;
@@ -726,10 +744,10 @@ void IntentServer::replyFromApplication(const QString &replyingApplicationId, co
qCWarning(LogIntents) << "Got a reply for intent" << requestId << "from application"
<< replyingApplicationId << "but no reply was expected for this intent";
} else {
- if (isr->handlingApplicationId() != replyingApplicationId) {
+ if (isr->selectedIntent() && (isr->selectedIntent()->applicationId() != replyingApplicationId)) {
qCWarning(LogIntents) << "Got a reply for intent" << isr->requestId() << "from application"
<< replyingApplicationId << "but expected a reply from"
- << isr->handlingApplicationId() << "instead";
+ << isr->selectedIntent()->applicationId() << "instead";
isr->setRequestFailed(qSL("Request reply received from wrong application"));
} else {
QString errorMessage;
@@ -762,7 +780,8 @@ IntentServerRequest *IntentServer::requestToSystem(const QString &requestingAppl
}
QVector<Intent *> intents;
- if (applicationId.isEmpty()) {
+ bool broadcast = (applicationId == qSL(":broadcast:"));
+ if (applicationId.isEmpty() || broadcast) {
intents = filterByIntentId(m_intents, intentId, parameters);
} else {
if (Intent *intent = this->applicationIntent(intentId, applicationId, parameters))
@@ -783,9 +802,17 @@ IntentServerRequest *IntentServer::requestToSystem(const QString &requestingAppl
return nullptr;
}
- auto isr = new IntentServerRequest(requestingApplicationId, intentId, intents, parameters);
- enqueueRequest(isr);
- return isr;
+ if (broadcast) {
+ for (auto intent : qAsConst(intents)) {
+ auto isr = new IntentServerRequest(requestingApplicationId, intentId, { intent }, parameters, broadcast);
+ enqueueRequest(isr);
+ }
+ return nullptr; // this is not an error condition for broadcasts - there simply is no return value for the sender
+ } else {
+ auto isr = new IntentServerRequest(requestingApplicationId, intentId, intents, parameters, broadcast);
+ enqueueRequest(isr);
+ return isr;
+ }
}
QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserver.h b/src/intent-server-lib/intentserver.h
index cd0828ec..5419f175 100644
--- a/src/intent-server-lib/intentserver.h
+++ b/src/intent-server-lib/intentserver.h
@@ -47,7 +47,7 @@ public:
const QStringList &capabilities, Intent::Visibility visibility,
const QVariantMap &parameterMatch, const QMap<QString, QString> &names,
const QMap<QString, QString> &descriptions, const QUrl &icon,
- const QStringList &categories);
+ const QStringList &categories, bool handleOnlyWhenRunning);
void removeIntent(Intent *intent);
diff --git a/src/intent-server-lib/intentserverrequest.cpp b/src/intent-server-lib/intentserverrequest.cpp
index 7880007e..9641ccbb 100644
--- a/src/intent-server-lib/intentserverrequest.cpp
+++ b/src/intent-server-lib/intentserverrequest.cpp
@@ -9,18 +9,22 @@ QT_BEGIN_NAMESPACE_AM
IntentServerRequest::IntentServerRequest(const QString &requestingApplicationId, const QString &intentId,
const QVector<Intent *> &potentialIntents,
- const QVariantMap &parameters)
+ const QVariantMap &parameters, bool broadcast)
: m_id(QUuid::createUuid())
, m_state(State::ReceivedRequest)
+ , m_broadcast(broadcast)
, m_intentId(intentId)
, m_requestingApplicationId(requestingApplicationId)
- , m_potentialIntents(potentialIntents)
, m_parameters(parameters)
{
Q_ASSERT(!potentialIntents.isEmpty());
+ Q_ASSERT(!broadcast || (potentialIntents.size() == 1));
+
+ for (auto *intent : potentialIntents)
+ m_potentialIntents << intent;
if (potentialIntents.size() == 1)
- setHandlingApplicationId(potentialIntents.first()->applicationId());
+ setSelectedIntent(potentialIntents.constFirst());
}
IntentServerRequest::State IntentServerRequest::state() const
@@ -43,14 +47,19 @@ QString IntentServerRequest::requestingApplicationId() const
return m_requestingApplicationId;
}
-QString IntentServerRequest::handlingApplicationId() const
+Intent *IntentServerRequest::selectedIntent() const
{
- return m_handlingApplicationId;
+ return m_selectedIntent.get();
}
QVector<Intent *> IntentServerRequest::potentialIntents() const
{
- return m_potentialIntents;
+ QVector<Intent *> out;
+ for (auto &intent : m_potentialIntents) {
+ if (intent)
+ out << intent.get();
+ }
+ return out;
}
QVariantMap IntentServerRequest::parameters() const
@@ -68,6 +77,11 @@ QVariantMap IntentServerRequest::result() const
return m_result;
}
+bool IntentServerRequest::isBroadcast() const
+{
+ return m_broadcast;
+}
+
void IntentServerRequest::setRequestFailed(const QString &errorMessage)
{
m_succeeded = false;
@@ -88,9 +102,10 @@ void IntentServerRequest::setState(IntentServerRequest::State newState)
m_state = newState;
}
-void IntentServerRequest::setHandlingApplicationId(const QString &applicationId)
+void IntentServerRequest::setSelectedIntent(Intent *intent)
{
- m_handlingApplicationId = applicationId;
+ if (m_potentialIntents.contains(intent))
+ m_selectedIntent = intent;
}
QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserverrequest.h b/src/intent-server-lib/intentserverrequest.h
index eb8004f9..2b2bd27b 100644
--- a/src/intent-server-lib/intentserverrequest.h
+++ b/src/intent-server-lib/intentserverrequest.h
@@ -10,6 +10,7 @@
#include <QVariantMap>
#include <QUuid>
#include <QVector>
+#include <QPointer>
#include <QtAppManCommon/global.h>
#include <QtAppManIntentServer/intent.h>
@@ -23,7 +24,8 @@ class IntentServerRequest
public:
IntentServerRequest(const QString &requestingApplicationId, const QString &intentId,
- const QVector<Intent *> &potentialIntents, const QVariantMap &parameters);
+ const QVector<Intent *> &potentialIntents, const QVariantMap &parameters,
+ bool broadcast);
enum class State {
ReceivedRequest,
@@ -41,14 +43,15 @@ public:
QUuid requestId() const;
QString intentId() const;
QString requestingApplicationId() const;
- QString handlingApplicationId() const;
+ Intent *selectedIntent() const;
QVector<Intent *> potentialIntents() const;
QVariantMap parameters() const;
bool succeeded() const;
QVariantMap result() const;
+ bool isBroadcast() const;
void setState(State newState);
- void setHandlingApplicationId(const QString &applicationId);
+ void setSelectedIntent(Intent *intent);
void setRequestFailed(const QString &errorMessage);
void setRequestSucceeded(const QVariantMap &result);
@@ -57,10 +60,11 @@ private:
QUuid m_id;
State m_state;
bool m_succeeded = false;
+ bool m_broadcast = false;
QString m_intentId;
QString m_requestingApplicationId;
- QString m_handlingApplicationId;
- QVector<Intent *> m_potentialIntents;
+ QPointer<Intent> m_selectedIntent;
+ QVector<QPointer<Intent>> m_potentialIntents;
QVariantMap m_parameters;
QVariantMap m_result;
};
diff --git a/src/intent-server-lib/intentserversysteminterface.cpp b/src/intent-server-lib/intentserversysteminterface.cpp
index f17038d5..08945c12 100644
--- a/src/intent-server-lib/intentserversysteminterface.cpp
+++ b/src/intent-server-lib/intentserversysteminterface.cpp
@@ -14,8 +14,11 @@ void IntentServerSystemInterface::initialize(IntentServer *intentServer)
connect(this, &IntentServerSystemInterface::replyFromApplication,
m_is, &IntentServer::replyFromApplication);
+
+ // we need to explicitly decouple here via QueuedConnection. Otherwise the request queue
+ // handling in IntentServer can get out of sync
connect(this, &IntentServerSystemInterface::applicationWasStarted,
- m_is, &IntentServer::applicationWasStarted);
+ m_is, &IntentServer::applicationWasStarted, Qt::QueuedConnection);
}
IntentServer *IntentServerSystemInterface::intentServer() const
diff --git a/src/intent-server-lib/intentserversysteminterface.h b/src/intent-server-lib/intentserversysteminterface.h
index d317cc96..8b2f299c 100644
--- a/src/intent-server-lib/intentserversysteminterface.h
+++ b/src/intent-server-lib/intentserversysteminterface.h
@@ -37,9 +37,9 @@ public:
IntentServerRequest *requestToSystem(const QString &requestingApplicationId, const QString &intentId,
const QString &applicationId, const QVariantMap &parameters);
- virtual void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *irs) = 0;
+ virtual void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *isr) = 0;
- virtual void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *irs) = 0;
+ virtual void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *isr) = 0;
signals:
void applicationWasStarted(const QString &appId);
diff --git a/src/launcher-lib/intentclientdbusimplementation.cpp b/src/launcher-lib/intentclientdbusimplementation.cpp
index c4056a31..57451388 100644
--- a/src/launcher-lib/intentclientdbusimplementation.cpp
+++ b/src/launcher-lib/intentclientdbusimplementation.cpp
@@ -40,9 +40,12 @@ void IntentClientDBusImplementation::initialize(IntentClient *intentClient) Q_DE
QQmlEngine::setObjectOwnership(IntentClient::instance(), QQmlEngine::CppOwnership);
return IntentClient::instance();
});
+ qmlRegisterRevision<IntentClient, 1>("QtApplicationManager", 2, 1);
qmlRegisterUncreatableType<IntentClientRequest>("QtApplicationManager", 2, 0, "IntentRequest",
qSL("Cannot create objects of type IntentRequest"));
+ qmlRegisterUncreatableType<IntentClientRequest, 1>("QtApplicationManager", 2, 1, "IntentRequest",
+ qSL("Cannot create objects of type IntentRequest"));
qmlRegisterType<IntentHandler>("QtApplicationManager.Application", 2, 0, "IntentHandler");
diff --git a/src/manager-lib/intentaminterface.cpp b/src/manager-lib/intentaminterface.cpp
index 3aeb5b1d..28bc19ae 100644
--- a/src/manager-lib/intentaminterface.cpp
+++ b/src/manager-lib/intentaminterface.cpp
@@ -41,9 +41,6 @@
QT_BEGIN_NAMESPACE_AM
-static QString sysUiId() { return qSL(":sysui:"); }
-
-
//////////////////////////////////////////////////////////////////////////
// vvv IntentAMImplementation vvv
@@ -105,7 +102,7 @@ IntentServer *IntentAMImplementation::createIntentServerAndClientInstance(Packag
intentInfo->parameterMatch(), intentInfo->names(),
intentInfo->descriptions(),
QUrl::fromLocalFile(package->info()->baseDir().absoluteFilePath(intentInfo->icon())),
- intentInfo->categories())) {
+ intentInfo->categories(), intentInfo->handleOnlyWhenRunning())) {
throw Exception(Error::Intents, "could not add intent %1 for package %2")
.arg(intentInfo->id()).arg(package->id());
}
@@ -196,7 +193,7 @@ void IntentServerAMImplementation::initialize(IntentServer *server)
bool IntentServerAMImplementation::checkApplicationCapabilities(const QString &applicationId,
const QStringList &requiredCapabilities)
{
- if (applicationId == sysUiId()) // The System UI bypasses the capabilities check
+ if (applicationId == IntentClient::instance()->systemUiId()) // The System UI bypasses the capabilities check
return true;
const auto app = ApplicationManager::instance()->application(applicationId);
@@ -223,15 +220,15 @@ void IntentServerAMImplementation::startApplication(const QString &appId)
}
void IntentServerAMImplementation::requestToApplication(IntentServerSystemInterface::IpcConnection *clientIPC,
- IntentServerRequest *irs)
+ IntentServerRequest *isr)
{
- reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->requestToApplication(irs);
+ reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->requestToApplication(isr);
}
void IntentServerAMImplementation::replyFromSystem(IntentServerSystemInterface::IpcConnection *clientIPC,
- IntentServerRequest *irs)
+ IntentServerRequest *isr)
{
- reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->replyFromSystem(irs);
+ reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->replyFromSystem(isr);
}
@@ -251,7 +248,7 @@ QString IntentClientAMImplementation::currentApplicationId(QObject *hint)
{
QmlInProcessRuntime *runtime = QmlInProcessRuntime::determineRuntime(hint);
- return runtime ? runtime->application()->info()->id() : sysUiId();
+ return runtime ? runtime->application()->info()->id() : IntentClient::instance()->systemUiId();
}
void IntentClientAMImplementation::initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false)
@@ -263,9 +260,12 @@ void IntentClientAMImplementation::initialize(IntentClient *intentClient) Q_DECL
QQmlEngine::setObjectOwnership(IntentClient::instance(), QQmlEngine::CppOwnership);
return IntentClient::instance();
});
+ qmlRegisterRevision<IntentClient, 1>("QtApplicationManager", 2, 1);
qmlRegisterUncreatableType<IntentClientRequest>("QtApplicationManager", 2, 0, "IntentRequest",
qSL("Cannot create objects of type IntentRequest"));
+ qmlRegisterUncreatableType<IntentClientRequest, 1>("QtApplicationManager", 2, 1, "IntentRequest",
+ qSL("Cannot create objects of type IntentRequest"));
qmlRegisterType<IntentHandler>("QtApplicationManager.Application", 2, 0, "IntentHandler");
qmlRegisterType<IntentServerHandler>("QtApplicationManager.SystemUI", 2, 0, "IntentServerHandler");
@@ -334,7 +334,8 @@ void IntentServerIpcConnection::setReady(Application *application)
return;
m_application = application;
m_ready = true;
- emit applicationIsReady((isInProcess() && !application) ? sysUiId() : application->id());
+ emit applicationIsReady((isInProcess() && !application) ? IntentClient::instance()->systemUiId()
+ : application->id());
}
IntentServerIpcConnection *IntentServerIpcConnection::find(const QString &appId)
@@ -395,28 +396,29 @@ IntentServerInProcessIpcConnection *IntentServerInProcessIpcConnection::createSy
QString IntentServerInProcessIpcConnection::applicationId() const
{
- return m_isSystemUi ? sysUiId() : IntentServerIpcConnection::applicationId();
+ return m_isSystemUi ? IntentClient::instance()->systemUiId()
+ : IntentServerIpcConnection::applicationId();
}
-void IntentServerInProcessIpcConnection::requestToApplication(IntentServerRequest *irs)
+void IntentServerInProcessIpcConnection::requestToApplication(IntentServerRequest *isr)
{
// we need decouple the server/client interface at this point to have a consistent
// behavior in single- and multi-process mode
- QMetaObject::invokeMethod(this, [this, irs]() {
+ QMetaObject::invokeMethod(this, [this, isr]() {
auto clientInterface = m_interface->intentClientSystemInterface();
- emit clientInterface->requestToApplication(irs->requestId(), irs->intentId(),
- irs->requestingApplicationId(),
- irs->handlingApplicationId(), irs->parameters());
+ emit clientInterface->requestToApplication(isr->requestId(), isr->intentId(),
+ isr->isBroadcast() ? qSL(":broadcast:") : isr->requestingApplicationId(),
+ isr->selectedIntent()->applicationId(), isr->parameters());
}, Qt::QueuedConnection);
}
-void IntentServerInProcessIpcConnection::replyFromSystem(IntentServerRequest *irs)
+void IntentServerInProcessIpcConnection::replyFromSystem(IntentServerRequest *isr)
{
// we need decouple the server/client interface at this point to have a consistent
// behavior in single- and multi-process mode
- QMetaObject::invokeMethod(this, [this, irs]() {
+ QMetaObject::invokeMethod(this, [this, isr]() {
auto clientInterface = m_interface->intentClientSystemInterface();
- emit clientInterface->replyFromSystem(irs->requestId(), !irs->succeeded(), irs->result());
+ emit clientInterface->replyFromSystem(isr->requestId(), !isr->succeeded(), isr->result());
}, Qt::QueuedConnection);
}
@@ -465,17 +467,21 @@ IntentServerDBusIpcConnection *IntentServerDBusIpcConnection::find(QDBusConnecti
return nullptr;
}
-void IntentServerDBusIpcConnection::requestToApplication(IntentServerRequest *irs)
+void IntentServerDBusIpcConnection::requestToApplication(IntentServerRequest *isr)
{
- emit m_adaptor->requestToApplication(irs->requestId().toString(), irs->intentId(),
- irs->handlingApplicationId(),
- convertFromJSVariant(irs->parameters()).toMap());
+ Q_ASSERT(isr && isr->selectedIntent());
+
+ emit m_adaptor->requestToApplication(isr->requestId().toString(), isr->intentId(),
+ isr->selectedIntent()->applicationId(),
+ convertFromJSVariant(isr->parameters()).toMap());
}
-void IntentServerDBusIpcConnection::replyFromSystem(IntentServerRequest *irs)
+void IntentServerDBusIpcConnection::replyFromSystem(IntentServerRequest *isr)
{
- emit m_adaptor->replyFromSystem(irs->requestId().toString(), !irs->succeeded(),
- convertFromJSVariant(irs->result()).toMap());
+ Q_ASSERT(isr);
+
+ emit m_adaptor->replyFromSystem(isr->requestId().toString(), !isr->succeeded(),
+ convertFromJSVariant(isr->result()).toMap());
}
QString IntentServerDBusIpcConnection::requestToSystem(const QString &intentId,
@@ -483,13 +489,13 @@ QString IntentServerDBusIpcConnection::requestToSystem(const QString &intentId,
const QVariantMap &parameters)
{
auto requestingApplicationId = application() ? application()->id() : QString();
- auto irs = m_interface->requestToSystem(requestingApplicationId, intentId, applicationId,
+ auto isr = m_interface->requestToSystem(requestingApplicationId, intentId, applicationId,
convertFromDBusVariant(parameters).toMap());
- if (!irs) {
+ if (!isr) {
sendErrorReply(QDBusError::NotSupported, qL1S("No matching intent handler registered."));
return QString();
} else {
- return irs->requestId().toString();
+ return isr->requestId().toString();
}
}
@@ -733,9 +739,11 @@ void IntentServerHandler::componentComplete()
return;
}
+ QString sysUiId = IntentClient::instance()->systemUiId();
+
IntentServer *is = IntentServer::instance();
- is->addPackage(sysUiId());
- is->addApplication(sysUiId(), sysUiId());
+ is->addPackage(sysUiId);
+ is->addApplication(sysUiId, sysUiId);
const auto ids = intentIds();
for (const auto &intentId : ids) {
@@ -748,9 +756,10 @@ void IntentServerHandler::componentComplete()
for (auto it = qvm_descriptions.cbegin(); it != qvm_descriptions.cend(); ++it)
descriptions.insert(it.key(), it.value().toString());
- auto intent = is->addIntent(intentId, sysUiId(), sysUiId(), m_intent->requiredCapabilities(),
+ auto intent = is->addIntent(intentId, sysUiId, sysUiId, m_intent->requiredCapabilities(),
m_intent->visibility(), m_intent->parameterMatch(), names,
- descriptions, m_intent->icon(), m_intent->categories());
+ descriptions, m_intent->icon(), m_intent->categories(),
+ m_intent->handleOnlyWhenRunning());
if (intent)
m_registeredIntents << intent;
else
diff --git a/src/manager-lib/intentaminterface.h b/src/manager-lib/intentaminterface.h
index 81d7fd3f..f72418b3 100644
--- a/src/manager-lib/intentaminterface.h
+++ b/src/manager-lib/intentaminterface.h
@@ -54,8 +54,8 @@ public:
IpcConnection *findClientIpc(const QString &appId) override;
void startApplication(const QString &appId) override;
- void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *irs) override;
- void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *irs) override;
+ void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *isr) override;
+ void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *isr) override;
private:
IntentClientSystemInterface *m_icsi = nullptr;
@@ -94,8 +94,8 @@ public:
bool isReady() const;
void setReady(Application *application);
- virtual void replyFromSystem(IntentServerRequest *irs) = 0;
- virtual void requestToApplication(IntentServerRequest *irs) = 0;
+ virtual void replyFromSystem(IntentServerRequest *isr) = 0;
+ virtual void requestToApplication(IntentServerRequest *isr) = 0;
signals:
void applicationIsReady(const QString &applicationId);
@@ -122,8 +122,8 @@ public:
QString applicationId() const override;
- void replyFromSystem(IntentServerRequest *irs) override;
- void requestToApplication(IntentServerRequest *irs) override;
+ void replyFromSystem(IntentServerRequest *isr) override;
+ void requestToApplication(IntentServerRequest *isr) override;
private:
IntentServerInProcessIpcConnection(Application *application, IntentServerAMImplementation *iface);
@@ -146,8 +146,8 @@ public:
~IntentServerDBusIpcConnection() override;
QString requestToSystem(const QString &intentId, const QString &applicationId, const QVariantMap &parameters);
- void replyFromSystem(IntentServerRequest *irs) override;
- void requestToApplication(IntentServerRequest *irs) override;
+ void replyFromSystem(IntentServerRequest *isr) override;
+ void requestToApplication(IntentServerRequest *isr) override;
void replyFromApplication(const QString &requestId, bool error, const QVariantMap &result);
diff --git a/tests/auto/applicationinfo/tst_applicationinfo.cpp b/tests/auto/applicationinfo/tst_applicationinfo.cpp
index f66caf78..25ab28b1 100644
--- a/tests/auto/applicationinfo/tst_applicationinfo.cpp
+++ b/tests/auto/applicationinfo/tst_applicationinfo.cpp
@@ -61,7 +61,7 @@ public:
ii->parameterMatch(), ii->names(),
ii->descriptions(),
QUrl::fromLocalFile(m_pi->baseDir().absoluteFilePath(ii->icon())),
- ii->categories());
+ ii->categories(), ii->handleOnlyWhenRunning());
}
}
} catch (const Exception &e) {
diff --git a/tests/auto/qml/intents/apps/intents1/info.yaml b/tests/auto/qml/intents/apps/intents1/info.yaml
index d5f72561..ea825a54 100644
--- a/tests/auto/qml/intents/apps/intents1/info.yaml
+++ b/tests/auto/qml/intents/apps/intents1/info.yaml
@@ -18,3 +18,4 @@ intents:
string: "^foo_.*_bar$"
complex: { 'a': 1 }
- id: custom-error
+- id: broadcast/ping
diff --git a/tests/auto/qml/intents/apps/intents1/intents1.qml b/tests/auto/qml/intents/apps/intents1/intents1.qml
index 7816ed38..e67f35d3 100644
--- a/tests/auto/qml/intents/apps/intents1/intents1.qml
+++ b/tests/auto/qml/intents/apps/intents1/intents1.qml
@@ -30,4 +30,11 @@ QtObject {
request.sendErrorReply("custom error")
}
}
+
+ property var broadcastHandler: IntentHandler {
+ intentIds: [ "broadcast/ping" ]
+ onRequestReceived: (request) => {
+ IntentClient.broadcastIntentRequest("broadcast/pong", { "from": "intents1" })
+ }
+ }
}
diff --git a/tests/auto/qml/intents/apps/intents2/info.yaml b/tests/auto/qml/intents/apps/intents2/info.yaml
index 209ab30c..fa178f10 100644
--- a/tests/auto/qml/intents/apps/intents2/info.yaml
+++ b/tests/auto/qml/intents/apps/intents2/info.yaml
@@ -14,3 +14,5 @@ intents:
- id: only2
- id: both
- id: only1 # declared here, but no handler to provoke an error
+- id: broadcast/ping
+ handleOnlyWhenRunning: yes
diff --git a/tests/auto/qml/intents/apps/intents2/intents2.qml b/tests/auto/qml/intents/apps/intents2/intents2.qml
index 435c9cb1..ef274a9d 100644
--- a/tests/auto/qml/intents/apps/intents2/intents2.qml
+++ b/tests/auto/qml/intents/apps/intents2/intents2.qml
@@ -25,4 +25,11 @@ QtObject {
})
}
}
+
+ property var broadcastHandler: IntentHandler {
+ intentIds: [ "broadcast/ping" ]
+ onRequestReceived: (request) => {
+ IntentClient.broadcastIntentRequest("broadcast/pong", { "from": "intents2" })
+ }
+ }
}
diff --git a/tests/auto/qml/intents/tst_intents.qml b/tests/auto/qml/intents/tst_intents.qml
index 95091e3b..b4ae71dc 100644
--- a/tests/auto/qml/intents/tst_intents.qml
+++ b/tests/auto/qml/intents/tst_intents.qml
@@ -5,8 +5,8 @@
import QtQuick 2.4
import QtTest 1.0
-import QtApplicationManager 2.0
-import QtApplicationManager.SystemUI 2.0
+import QtApplicationManager 2.1
+import QtApplicationManager.SystemUI 2.1
TestCase {
id: testCase
@@ -217,4 +217,46 @@ TestCase {
}
requestSpy.clear()
}
+
+ IntentServerHandler {
+ id: broadcastReceiver
+ intentIds: [ "broadcast/pong" ]
+ visibility: IntentObject.Public
+ onRequestReceived: (request) => {
+ pongsReceived.push(request.parameters.from)
+ }
+ property var pongsReceived: []
+ }
+
+
+ function test_zbroadcast() { // 'z' to make it run as the last test
+ let am = ApplicationManager
+
+ // stop all running applications
+ am.stopAllApplications(true)
+ tryVerify(() => { return am.applicationRunState("intents1") === Am.NotRunning })
+ tryVerify(() => { return am.applicationRunState("intents2.1") === Am.NotRunning })
+
+ broadcastReceiver.pongsReceived = []
+
+ // broadcast ping -> only intents1 should be auto-started
+ verify(IntentClient.broadcastIntentRequest("broadcast/ping", { }))
+
+ tryVerify(() => { return am.applicationRunState("intents1") === Am.Running })
+ tryVerify(() => { return am.applicationRunState("intents2.1") === Am.NotRunning })
+
+ tryVerify(() => { return broadcastReceiver.pongsReceived.join() === "intents1"})
+
+ // manually start intents2.1 and ping again -> both apps should receive it
+
+ verify(am.startApplication("intents2.1"))
+ tryVerify(() => { return am.applicationRunState("intents2.1") === Am.Running })
+ wait(250 * AmTest.timeoutFactor) // it takes a bit for the intents IPC to get initialized
+
+ broadcastReceiver.pongsReceived = []
+
+ verify(IntentClient.broadcastIntentRequest("broadcast/ping", { }))
+
+ tryVerify(() => { return broadcastReceiver.pongsReceived.sort().join() === "intents1,intents2" })
+ }
}