意图系统UI和应用程序示例

三个应用程序和一个通过意图进行通信的系统UI。

The Intents example with all applications running.

简介

本示例演示了系统UI和应用程序如何发送和接收意图。类似于"Hello World!"系统UI示例,最小化了窗口管理方面,以便仅关注意图:右侧的2x2网格将始终显示系统UI部分(灰色)位于左上角,而三个应用程序(红色、绿色和蓝色)将动态地占据其他角落,其占用顺序与它们启动的顺序相同。您可以在左侧看到可用的应用程序的名称和图标;单击相应的图标以启动和停止这些应用程序。

每个应用程序以及系统UI都外观相似,并具有UI中相同的功能:您可以从顶部的组合框中选择可用的意图ID(标注为意图),并可任选指定应处理所选请求的相应应用程序(标注为应用程序)。单击请求按钮将创建并发送相应的意图请求到应用程序管理器的IntentServer进行处理

  • 意图应用程序的组合有效,且目标应用程序能够处理该请求;此请求的结果将作为JSON文本显示在标注为请求的底部输出字段中。
  • 或者意图应用程序的组合无效,或者目标应用程序无法正确处理请求;此请求的错误消息将作为纯文本显示在标注为请求的底部输出字段中。
  • 未指定应用程序,且选择的意图可以被多个应用程序处理;在这种情况下,示例的系统UI将显示一个对话框,提示用户澄清请求

文件和文件夹结构

本示例包括一个系统UI和三个样本应用程序("红色意图"、"绿色意图"和"蓝色意图"),总共四个独立的QML应用程序。系统UI最终也是一个QML应用程序,尽管是特殊的。

每个应用程序都按照以下内容放置在其自己的单独目录中。由于所有应用程序和系统UI都使用基于QtQuickControls 2的UI,因此其组件位于一个共享目录中。

  • system-ui.qml
  • apps
    • intents.blue
      • icon.png
      • info.yaml
      • main.qml
    • intents.red
      • icon.png
      • info.yaml
      • main.qml
    • intents.green
      • icon.png
      • info.yaml
      • main.qml
  • shared
    • IntentsApplicationWindow.qml
    • IntentsUIPage.qml

如上图所示,每个应用程序(除主QML文件外)还有一个图标和一个包含应用程序元数据的info.yaml文件,后者还包括了此应用程序可以处理的意图的定义。

运行示例

假设appman可执行文件在您的路径上,您可以通过以下方式运行系统UI

examples/applicationmanager/intents$ appman --builtin-apps-manifest-dir ./apps system-ui.qml

添加-o "ui: { style: material }"将使示例看起来和感觉更好。

您应该看到的是这个

关于这些和其他命令行选项的信息,您可以运行appman --help或查看配置文档。

应用实现

所有应用程序(红色、绿色和蓝色)都是相同的,它们的main.qml只是实例化了共享的IntentsApplicationWindow组件。

import "../../shared"

IntentsApplicationWindow { }

IntentsApplicationWindow组件实际上是一个顶级ApplicationManagerWindow,其UI内容是通过实例化共享的IntentsUIPage组件来定义的。这个UI组件没有特定的Intent代码,所以实际的发送是在绑定到IntentsUIPage请求信号的事件处理程序中完成的

onRequest: {
    var request = IntentClient.sendIntentRequest(intentId, applicationId, parameters)
    request.onReplyReceived.connect(function() {
        intentPage.setResult(request.requestId, request.succeeded,
                             request.succeeded ? request.result : request.errorMessage)
    })
}

在调用IntentClient::sendIntentRequest并使用UI中选择的参数之后,示例代码会将一个函数对象连接到< request's replyReceived请求的回复已接收信号。结果是放置在UI的请求字段中。

此外,它定义了所有必要的IntentHandlers,例如

IntentHandler {
    intentIds: "rotate-window"
    onRequestReceived: {
        rotationAnimation.start()
        request.sendReply({ "done": true })
    }
}

这些IntentHandlers并不复杂,每个都只是触发在文件本中定义的基本动画,所以对于rotate-window intent,就会有:

RotationAnimation on rotation {
    id: rotationAnimation
    running: false
    duration: 500; from: 0; to: 360
}

在QML中,只实现IntentHandlers是不够的,因为应用程序管理器需要有关哪些应用程序支持哪些 intents 的信息。这些信息必须在应用程序运行之前提供给应用程序管理器,以便便于在意图请求上自动启动应用程序。正如应用程序管理器中的其他所有应用程序配置一样,这是通过应用程序的清单文件 info.yaml 来完成的

红色应用程序定义了三个可用的uri

intents:
- id: rotate-window
  name:
    en: Rotate Red
- id: scale-window
  name:
    en: Scale Red
- id: blink-window
  name:
    en: Blink Red

此外,红色应用程序获得了call-blue功能,这是蓝色应用程序中某些uri所需要的。

capabilities: 'call-blue'

绿色应用程序只定义了两个可用的uri。注意,尽管这个应用程序在共享的IntentsApplicationWindow组件中有一个用于blink-window uri的IntentHandler,但由于此uri ID未通过info.yaml清单在系统中注册,因此此处理程序永远不会被调用。

intents:
- id: rotate-window
  name:
    en: Rotate Green
- id: scale-window
  name:
    en: Scale Green

蓝色应用程序的uri定义最为复杂。除了处理与红色应用程序相同的三个uri外,它还注册了具有visibility: private属性的blue-window-private uri。私有uri只能由注册它们的同一应用程序请求,所以在这种情况下,只有蓝色能够成功请求blue-window-private uri。此外,rotate-window uri只能由具有call-blue功能的应用程序请求:在这里,红色应用程序包含所需的功能,而绿色则不具备。

intents:
- id: rotate-window
  name:
    en: Rotate Blue
  requiredCapabilities: [ 'call-blue' ]
- id: scale-window
  name:
    en: Scale Blue
- id: blink-window
  name:
    en: Blink Blue
- id: blue-window-private
  name:
    en: Blue Private Intent
  visibility: private

系统UI实现

除了处理启动和停止应用程序的左侧栏之外,系统UI还有两个特殊功能,用于处理意图机制

  • 系统UI中的意图处理和
  • 意图请求的消除歧义
系统UI中的意图处理

意图不仅可以在应用程序中处理,还可以在系统用户界面中处理。由于系统用户界面始终运行,我们不需要依赖info.yaml清单文件来定义支持的场景,而是可以直接将需要的元数据作为IntentServerHandler组件的属性声明。实际上,IntentServerHandler是从IntentHandler派生出来的,因此它的工作方式与应用方面的对应组件相同:它只是在顶部添加了定义所有元数据(例如namesicon等)所需的所有属性。

IntentServerHandler {
    intentIds: "rotate-window"
    names: { "en": "Rotate System UI" }
    visibility: IntentObject.Public

    onRequestReceived: {
        rotationAnimation.start()
        request.sendReply({ "wasRequestedBy": request.requestingApplicationId })
    }
}

处理程序的回调几乎与应用程序中的相同。这里唯一值得注意的区别是,我们有访问requestingApplicationId的权限,以便识别请求的来源;出于安全原因,此数据在应用程序中的场景处理程序中不可用。

意图请求的消除歧义

以下示例实现了一个用户界面,允许用户选择如何消除意向请求的歧义。在此处注册对IntentServer的消除歧义请求

Connections {
    target: IntentServer
    function onDisambiguationRequest(requestId, potentialIntents) {
        disambiguationDialog.add(requestId, potentialIntents)
    }
}

由于我们希望系统中的并行意向请求数量保持灵活,我们只需将传入的请求添加到队列(allRequests)中。消费此队列的Dialog是一个相当标准的QtQuickControls 2对话框,它提供了一个漂亮的用户界面,显示了IntentServer::disambiguationRequested()信号可能来自的远程应用选择。在按下确定取消后,对话框将通知IntentServer关于用户的决策

onAccepted: {
    IntentServer.acknowledgeDisambiguationRequest(currentRequest.requestId,
                                                  currentRequest.intents[handlingApplications.currentIndex]);
    showNext()
}
onRejected: {
    IntentServer.rejectDisambiguationRequest(currentRequest.requestId)
    showNext()
}

示例项目 @ code.qt.io

©2019 Luxoft Sweden AB。文档中的贡献权属于各自的所有者。在此提供的文档受自由软件基金会发布的GNU自由文档许可协议(版本1.3)的条款约束。