LCOV - code coverage report
Current view: top level - intent-client-lib - intentclientrequest.cpp (source / functions) Coverage Total Hit
Test: QtApplicationManager Lines: 75.0 % 100 75
Test Date: 2024-06-15 10:05:34 Functions: 82.6 % 23 19
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 37.5 % 88 33

             Branch data     Line data    Source code
       1                 :             : // Copyright (C) 2021 The Qt Company Ltd.
       2                 :             : // Copyright (C) 2019 Luxoft Sweden AB
       3                 :             : // Copyright (C) 2018 Pelagicore AG
       4                 :             : // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
       5                 :             : 
       6                 :             : #include "intentclientrequest.h"
       7                 :             : #include "intentclient.h"
       8                 :             : 
       9                 :             : #include <QQmlEngine>
      10                 :             : #include <QQmlInfo>
      11                 :             : #include <QThread>
      12                 :             : #include <QPointer>
      13                 :             : #include <QTimer>
      14                 :             : 
      15                 :             : using namespace Qt::StringLiterals;
      16                 :             : 
      17                 :             : 
      18                 :             : QT_BEGIN_NAMESPACE_AM
      19                 :             : 
      20                 :             : /*! \qmltype IntentRequest
      21                 :             :     \inqmlmodule QtApplicationManager
      22                 :             :     \ingroup common-non-instantiable
      23                 :             :     \brief Each instance represents an outgoing or incoming intent request.
      24                 :             : 
      25                 :             :     This type is used both in applications as well as within the System UI to represent an intent
      26                 :             :     request. This type can not be instantiated directly, but will be returned by
      27                 :             :     IntentClient::sendIntentRequest() (for outgoing requests to the system) and
      28                 :             :     IntentHandler::requestReceived() (for incoming requests to the application)
      29                 :             : 
      30                 :             :     See the IntentClient type for a short example on how to send intent requests to the system.
      31                 :             : 
      32                 :             :     The IntentHandler documenatation provides an example showing the use of this type when receiving
      33                 :             :     requests from the system.
      34                 :             : */
      35                 :             : 
      36                 :             : /*! \qmlproperty uuid IntentRequest::requestId
      37                 :             :     \readonly
      38                 :             : 
      39                 :             :     Every intent request in the system gets an unique requestId assigned by the server that will be
      40                 :             :     used throughout the life-time of the request in every context (requesting application, handling
      41                 :             :     application and intent server).
      42                 :             :     \note Since this requestId is generated by the server, any IntentRequest object generated by
      43                 :             :           IntentClient::sendIntentRequest() will start with a null requestId. The property will
      44                 :             :           be updated asynchronously once the server has assigned a new requestId to the
      45                 :             :           incoming request.
      46                 :             : 
      47                 :             :     \note Constant for requests received by IntentHandlers, valid on both sent and received requests.
      48                 :             : */
      49                 :             : 
      50                 :             : /*! \qmlproperty IntentRequest::Direction IntentRequest::direction
      51                 :             :     \readonly
      52                 :             : 
      53                 :             :     This property describes if this instance is an outgoing or incoming intent request:
      54                 :             : 
      55                 :             :     \list
      56                 :             :     \li IntentRequest.ToSystem - The request object was generated by IntentClient::sendIntentRequest(),
      57                 :             :                                  i.e. this request is sent out to the system side for handling.
      58                 :             :     \li IntentRequest.ToApplication - The request object was received by IntentHandler::requestReceived(),
      59                 :             :                                       i.e. this request was sent from the system side to the
      60                 :             :                                       application for handling.
      61                 :             :     \endlist
      62                 :             : 
      63                 :             :     \note Constant, valid on both sent and received requests.
      64                 :             : */
      65                 :             : 
      66                 :             : /*! \qmlproperty string IntentRequest::intentId
      67                 :             :     \readonly
      68                 :             : 
      69                 :             :     The requested intent id.
      70                 :             : 
      71                 :             :     \note Constant, valid on both sent and received requests.
      72                 :             : */
      73                 :             : 
      74                 :             : /*! \qmlproperty string IntentRequest::applicationId
      75                 :             :     \readonly
      76                 :             : 
      77                 :             :     The id of the application which should be handling this request. Returns an empty string if
      78                 :             :     no specific application was requested.
      79                 :             : 
      80                 :             :     \note Constant, valid on both sent and received requests.
      81                 :             : */
      82                 :             : 
      83                 :             : /*! \qmlproperty string IntentRequest::requestingApplicationId
      84                 :             :     \readonly
      85                 :             : 
      86                 :             :     The id of the application which created this intent request. Returns an empty string if called
      87                 :             :     from within an application context - only the server side has access to this information in
      88                 :             :     IntentServerHandler::requestReceived.
      89                 :             : 
      90                 :             :     \note Constant, valid on both sent and received requests.
      91                 :             : */
      92                 :             : 
      93                 :             : /*! \qmlproperty var IntentRequest::parameters
      94                 :             :     \readonly
      95                 :             : 
      96                 :             :     All parameters attached to the request as a JavaScript object.
      97                 :             : 
      98                 :             :     \note Constant, valid on both sent and received requests.
      99                 :             : */
     100                 :             : 
     101                 :             : /*! \qmlproperty bool IntentRequest::succeeded
     102                 :             :     \readonly
     103                 :             : 
     104                 :             :     As soon as the replyReceived() signal has been emitted, this property will show if the
     105                 :             :     intent request was actually successful.
     106                 :             : 
     107                 :             :     \note Valid only on sent requests.
     108                 :             : */
     109                 :             : 
     110                 :             : /*! \qmlproperty string IntentRequest::errorMessage
     111                 :             :     \readonly
     112                 :             : 
     113                 :             :     As soon as the replyReceived() signal has been emitted, this property will hold a potential
     114                 :             :     error message in case the request failed.
     115                 :             : 
     116                 :             :     \note Valid only on sent requests.
     117                 :             : 
     118                 :             :     \sa succeeded
     119                 :             : */
     120                 :             : 
     121                 :             : /*! \qmlproperty var IntentRequest::result
     122                 :             :     \readonly
     123                 :             : 
     124                 :             :     As soon as the replyReceived() signal has been emitted, this property will hold the result in
     125                 :             :     form of a JavaScript object in case the request succeeded.
     126                 :             : 
     127                 :             :     \note Valid only on sent requests.
     128                 :             : 
     129                 :             :     \sa succeeded
     130                 :             : */
     131                 :             : 
     132                 :             : /*! \qmlproperty bool IntentRequest::broadcast
     133                 :             :     \readonly
     134                 :             :     \since 6.5
     135                 :             : 
     136                 :             :     Only Set to \c true, if the received request is a broadcast.
     137                 :             : 
     138                 :             :     \note Valid only on received requests.
     139                 :             : */
     140                 :             : 
     141                 :             : /*! \qmlsignal IntentRequest::replyReceived()
     142                 :             : 
     143                 :             :     This signal gets emitted when a reply to an intent request is available. The signal handler
     144                 :             :     needs to check the succeeded property to decided whether errorMessage or result are actually
     145                 :             :     valid.
     146                 :             : 
     147                 :             :     \note This signal will only ever by emitted for request objects created by
     148                 :             :           IntentClient::sendIntentRequest().
     149                 :             : */
     150                 :             : 
     151                 :             : 
     152                 :         240 : IntentClientRequest::Direction IntentClientRequest::direction() const
     153                 :             : {
     154                 :           0 :     return m_direction;
     155                 :             : }
     156                 :             : 
     157                 :         374 : IntentClientRequest::~IntentClientRequest()
     158                 :             : {
     159                 :             :     // the incoming request was gc'ed on the JavaScript side, but no reply was sent yet
     160   [ +  +  +  +  :         187 :     if ((direction() == Direction::ToApplication) && !m_finished && !m_broadcast)
                   +  + ]
     161                 :          10 :         sendErrorReply(u"Request not handled"_s);
     162                 :         374 : }
     163                 :             : 
     164                 :         125 : QUuid IntentClientRequest::requestId() const
     165                 :             : {
     166                 :           0 :     return m_id;
     167                 :             : }
     168                 :             : 
     169                 :         112 : QString IntentClientRequest::intentId() const
     170                 :             : {
     171                 :         112 :     return m_intentId;
     172                 :             : }
     173                 :             : 
     174                 :         152 : QString IntentClientRequest::applicationId() const
     175                 :             : {
     176                 :         152 :     return m_applicationId;
     177                 :             : }
     178                 :             : 
     179                 :          94 : QString IntentClientRequest::requestingApplicationId() const
     180                 :             : {
     181                 :          94 :     return m_requestingApplicationId;
     182                 :             : }
     183                 :             : 
     184                 :         183 : QVariantMap IntentClientRequest::parameters() const
     185                 :             : {
     186   [ +  +  +  + ]:         243 :     return m_parameters;
     187                 :             : }
     188                 :             : 
     189                 :         111 : bool IntentClientRequest::isBroadcast() const
     190                 :             : {
     191                 :           0 :     return m_broadcast;
     192                 :             : }
     193                 :             : 
     194                 :         228 : bool IntentClientRequest::succeeded() const
     195                 :             : {
     196                 :         171 :     return m_succeeded;
     197                 :             : }
     198                 :             : 
     199                 :         115 : const QVariantMap IntentClientRequest::result() const
     200                 :             : {
     201   [ +  +  +  + ]:         164 :     return m_result;
     202                 :             : }
     203                 :             : 
     204                 :          39 : QString IntentClientRequest::errorMessage() const
     205                 :             : {
     206                 :          39 :     return m_errorMessage;
     207                 :             : }
     208                 :             : 
     209                 :             : /*! \qmlmethod IntentRequest::sendReply(var result)
     210                 :             : 
     211                 :             :     An IntentHandler needs to call this function to send its \a result back to the system in reply
     212                 :             :     to an request received via IntentHandler::requestReceived().
     213                 :             : 
     214                 :             :     Only either sendReply() or sendErrorReply() can be used on a single IntentRequest.
     215                 :             : 
     216                 :             :     \note This function only works for request objects received from IntentHandler::requestReceived().
     217                 :             :           It will simply do nothing on requests created by IntentClient::sendIntentRequest().
     218                 :             : 
     219                 :             :     \sa sendErrorReply
     220                 :             : */
     221                 :          44 : void IntentClientRequest::sendReply(const QVariantMap &result)
     222                 :             : {
     223                 :             :     //TODO: check that result only contains basic datatypes. convertFromJSVariant() does most of
     224                 :             :     //      this already, but doesn't bail out on unconvertible types (yet)
     225                 :             : 
     226         [ -  + ]:          44 :     if (m_direction == Direction::ToSystem) {
     227         [ #  # ]:           0 :         qmlWarning(this) << "Calling IntentRequest::sendReply on requests originating from this application is a no-op.";
     228                 :           0 :         return;
     229                 :             :     }
     230         [ -  + ]:          44 :     if (m_broadcast) {
     231         [ #  # ]:           0 :         qmlWarning(this) << "Calling IntentRequest::sendReply on broadcast requests is a no-op.";
     232                 :           0 :         return;
     233                 :             :     }
     234                 :             : 
     235                 :          44 :     IntentClient *ic = IntentClient::instance();
     236                 :             : 
     237         [ -  + ]:          44 :     if (QThread::currentThread() != ic->thread()) {
     238                 :           0 :         QPointer<IntentClientRequest> that(this);
     239                 :             : 
     240   [ #  #  #  #  :           0 :         ic->metaObject()->invokeMethod(ic, [that, ic, result]()
             #  #  #  # ]
     241         [ #  # ]:           0 :         { if (that) ic->replyFromApplication(that.data(), result); },
     242                 :             :         Qt::QueuedConnection);
     243                 :           0 :     } else {
     244                 :          44 :         ic->replyFromApplication(this, result);
     245                 :             :     }
     246                 :             : }
     247                 :             : 
     248                 :             : /*! \qmlmethod IntentRequest::sendErrorReply(string errorMessage)
     249                 :             : 
     250                 :             :     IntentHandlers can use this function to indicate that they are unable to handle a request that
     251                 :             :     they received via IntentHandler::requestReceived(), stating the reason in \a errorMessage.
     252                 :             : 
     253                 :             :     Only either sendReply() or sendErrorReply() can be used on a single IntentRequest.
     254                 :             : 
     255                 :             :     \note This function only works for request objects received from IntentHandler::requestReceived().
     256                 :             :           It will simply do nothing on requests created by IntentClient::sendIntentRequest().
     257                 :             : 
     258                 :             :     \sa sendReply
     259                 :             : */
     260                 :          11 : void IntentClientRequest::sendErrorReply(const QString &errorMessage)
     261                 :             : {
     262         [ -  + ]:          11 :     if (m_direction == Direction::ToSystem) {
     263         [ #  # ]:           0 :         qmlWarning(this) << "Calling IntentRequest::sendErrorReply on requests originating from this application is a no-op.";
     264                 :           0 :         return;
     265                 :             :     }
     266         [ -  + ]:          11 :     if (m_broadcast) {
     267         [ #  # ]:           0 :         qmlWarning(this) << "Calling IntentRequest::sendErrorReply on broadcast requests is a no-op.";
     268                 :           0 :         return;
     269                 :             :     }
     270                 :          11 :     IntentClient *ic = IntentClient::instance();
     271                 :             : 
     272         [ -  + ]:          11 :     if (QThread::currentThread() != ic->thread()) {
     273                 :           0 :         QPointer<IntentClientRequest> that(this);
     274                 :             : 
     275   [ #  #  #  #  :           0 :         ic->metaObject()->invokeMethod(ic, [that, ic, errorMessage]()
                   #  # ]
     276         [ #  # ]:           0 :         { if (that) ic->errorReplyFromApplication(that.data(), errorMessage); },
     277                 :             :         Qt::QueuedConnection);
     278                 :           0 :     } else {
     279                 :          11 :         ic->errorReplyFromApplication(this, errorMessage);
     280                 :             :     }
     281                 :             : }
     282                 :             : 
     283                 :         149 : void IntentClientRequest::startTimeout(int timeout)
     284                 :             : {
     285         [ +  + ]:         149 :     if (timeout <= 0)
     286                 :             :         return;
     287                 :             : 
     288   [ -  +  -  -  :         246 :     QTimer::singleShot(timeout, this, [this, timeout]() {
                   -  - ]
     289         [ -  + ]:          15 :         if (!m_finished) {
     290         [ #  # ]:           0 :             if (direction() == Direction::ToApplication)
     291   [ #  #  #  # ]:           0 :                 sendErrorReply(u"Intent request to application timed out after %1 ms"_s.arg(timeout));
     292                 :             :             else
     293   [ #  #  #  # ]:           0 :                 setErrorMessage(u"No reply received from Intent server after %1 ms"_s.arg(timeout));
     294                 :             :         }
     295                 :          15 :     });
     296                 :             : }
     297                 :             : 
     298                 :          53 : void IntentClientRequest::connectNotify(const QMetaMethod &signal)
     299                 :             : {
     300                 :             :     // take care of connects happening after the request is already finished:
     301                 :             :     // re-emit the finished signal in this case (this shouldn't happen in practice, but better be
     302                 :             :     // safe than sorry)
     303         [ +  - ]:          53 :     if (signal == QMetaMethod::fromSignal(&IntentClientRequest::replyReceived)) {
     304         [ -  + ]:          53 :         if (direction() == Direction::ToApplication) {
     305         [ #  # ]:           0 :             qmlWarning(this) << "Connecting to IntentRequest::replyReceived on requests received "
     306                 :           0 :                                 "by IntentHandlers is a no-op.";
     307         [ -  + ]:          53 :         } else if (m_finished) {
     308                 :           0 :             QMetaObject::invokeMethod(this, &IntentClientRequest::doFinish, Qt::QueuedConnection);
     309                 :             :         }
     310                 :             :     }
     311                 :          53 : }
     312                 :             : 
     313                 :         187 : IntentClientRequest::IntentClientRequest(Direction direction, const QString &requestingApplicationId,
     314                 :             :                                          const QUuid &id, const QString &intentId,
     315                 :             :                                          const QString &applicationId, const QVariantMap &parameters,
     316                 :         187 :                                          bool broadcast)
     317                 :             :     : QObject()
     318                 :         187 :     , m_direction(direction)
     319                 :         187 :     , m_id(id)
     320                 :         374 :     , m_intentId(intentId)
     321                 :         187 :     , m_requestingApplicationId(requestingApplicationId)
     322                 :         187 :     , m_applicationId(applicationId)
     323         [ +  + ]:         187 :     , m_parameters(parameters)
     324                 :         187 :     , m_broadcast(broadcast)
     325                 :         187 : { }
     326                 :             : 
     327                 :          79 : void IntentClientRequest::setRequestId(const QUuid &requestId)
     328                 :             : {
     329         [ +  - ]:          79 :     if (m_id != requestId) {
     330                 :          79 :         m_id = requestId;
     331                 :          79 :         emit requestIdChanged();
     332                 :             :     }
     333                 :          79 : }
     334                 :             : 
     335                 :          51 : void IntentClientRequest::setResult(const QVariantMap &result)
     336                 :             : {
     337         [ +  + ]:          51 :     if (m_result != result)
     338                 :          36 :         m_result = result;
     339                 :          51 :     m_succeeded = true;
     340                 :          51 :     doFinish();
     341                 :          51 : }
     342                 :             : 
     343                 :          33 : void IntentClientRequest::setErrorMessage(const QString &errorMessage)
     344                 :             : {
     345         [ +  - ]:          33 :     if (m_errorMessage != errorMessage)
     346                 :          33 :         m_errorMessage = errorMessage;
     347                 :          33 :     m_succeeded = false;
     348                 :          33 :     doFinish();
     349                 :          33 : }
     350                 :             : 
     351                 :          84 : void IntentClientRequest::doFinish()
     352                 :             : {
     353                 :          84 :     m_finished = true;
     354                 :          84 :     emit replyReceived();
     355                 :             :     // We need to disconnect all JS handlers now, because otherwise the request object would
     356                 :             :     // never be garbage collected (the signal connections increase the use-counter).
     357                 :          84 :     disconnect(this, &IntentClientRequest::replyReceived, nullptr, nullptr);
     358                 :          84 : }
     359                 :             : 
     360                 :             : QT_END_NAMESPACE_AM
     361                 :             : 
     362                 :             : #include "moc_intentclientrequest.cpp"
        

Generated by: LCOV version 2.0-1