- Qt 5.0
- Qt Contacts Action API
- Qt 5.0.0 Reference Documentation

# Qt Contacts Action API

The Qt Contacts API supports the concept of a generic action which may be invoked upon an action target (e.g., a contact) or list thereof. The API allows clients to invoke an action upon a target (for example, to send an email to a contact) in a cross-platform manner, and allows third-party developers to provide platform-specific action plugins which may be used by clients.

## Invoking Actions upon Targets

The client interface to actions consists of three classes: QContactAction, QContactActionTarget and QContactActionDescriptor. A descriptor uniquely identifies a particular implementation of an action, and allows the client to query meta-data about the action. An action target consists of either a contact, a detail of a contact, or a list of details of a contact.

The available actions may be queried by calling QContactAction::availableActions(). This function returns the list of names of actions which are provided by the given service name, or by any service if the parameter is omitted.

There may be multiple implementations of any given action identified by a particular action name, since multiple third-party action providers could provide (for example) a "call" action, using various proprietary protocols and techologies. Once the client knows which action they wish to perform on a contact, they can retrieve the list of action descriptors for that action by calling QContactAction::actionDescriptors() which takes the action name as a parameter.

Note that there are several predefined action names including QContactAction::ActionCall, QContactAction::ActionEmail, QContactAction::ActionSms etc, however there is no guarantee that all of these actions are implemented on any given platform.

Finally, once the client has selected a particular implementation of the action, by inspecting the action descriptor (from which they can retrieve meta-data and check that it supports the contact that they wish to perform the action on), the client may request a pointer to the action implementation by calling QContactAction::action() and passing the action descriptor as a parameter. Note that the client takes ownership of the returned QContactAction pointer and must delete it to avoid leaking memory. The caller is able to delete the action at any time, however doing so prior to when the action transitions to a finished state may have an undefined outcome depending on the implementation of the action.

## Implementing Actions

If you are a third-party developer who wants to provide an action for other clients to use, you must do four things:

- Implement a QServicePluginInterface-derived class
- Implement a QContactActionFactory-derived class
- Implement (one or more) QContactAction-derived classes
- Write an XML file which describes your service plugin

For more information on the QServicePluginInterface and the format of the service description XML, see the Qt Service Framework documentation. An example action plugin is provided later in this document.

Note: While the plugins are loaded by the Qt Service Framework, clients of the Qt Contacts Action API are entirely shielded from this implementation detail.

The QContactActionDescriptor class is actually a client-facing interface to an action factory, which allows the factory to provide meta-data and other implementation-specific information to clients on demand.

### Other Considerations

We recommend that action implementors provide values for the default meta-data keys (including icons and labels) documented in QContactActionDescriptor, to allow client applications to provide meaningful user interface elements to represent the action.

We recommend that action implementors read the documentation of the Qt Service Framework carefully, to better understand how their implementation plugin may be updated with patch releases or major releases, and how these considerations affect the implementation of the plugin.

### Example Implementation

The following snippet provides an example of an action plugin. As previously described, the action plugin consists of a QServicePluginInterface, a QContactActionFactory, and one or more QContactAction derived classes. The QServicePluginInterface-derived class merely instantiates the QContactActionFactory-derived class on request for the Qt Service Framework. The QContactActionFactory-derived class then instantiates the actions when required.

     /*
        This action plugin is capable of producing two actions which each have the
        same action name, service name, interface name and implementation (minor) version,
        but internally use a different implementation.  This difference is reported via the
        meta data function of the factory (which is exposed to clients via the descriptor
        which provides a "front end" to the factory).

        Example use case:
        Company "Example VoIP Solutions" wants to provide a "Call" action with different implementations.
            -> it provides a SINGLE plugin which provides two actions, both of which are:
                - ServiceName = "Example VoIP Solution"
                - InterfaceName = "org.qt-project.Qt.SampleContactsActionPlugin" (QContactActionFactory::InterfaceName)
                - Major Version = "1"
                - Minor Version = "1"
                - ActionName = "call" (this is a custom property in the service interface xml)
            -> BUT one of the actions has the custom property:
                - Provider = "sip"
            -> where the other action has the custom property:
                - Provider = "example proprietary protocol"
            -> the custom properties are available to clients via the QContactActionDescriptor::metaData() function.
      */

     class QContactMultiActionPlugin : public QObject, public QServicePluginInterface
     {
         Q_OBJECT
         Q_INTERFACES(QtMobility::QServicePluginInterface)

     public:
         QObject* createInstance(const QServiceInterfaceDescriptor& descriptor,
                                 QServiceContext* context,
                                 QAbstractSecuritySession* session);
     };

     class QContactMultiActionFactory : public QContactActionFactory
     {
         Q_OBJECT

     public:
         QContactMultiActionFactory();
         ~QContactMultiActionFactory();

         QList<QContactActionDescriptor> actionDescriptors() const;
         QContactAction* create(const QContactActionDescriptor& which) const;

         QSet<QContactActionTarget> supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const;
         QContactFilter contactFilter(const QContactActionDescriptor& which) const;
         QVariant metaData(const QString& key, const QList<QContactActionTarget>& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const;

         bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const;

     private:
         QContactActionDescriptor m_actionOneDescriptor;
         QContactActionDescriptor m_actionTwoDescriptor;
     };

     class QContactActionOne : public QContactAction
     {
         Q_OBJECT

     public:
         QContactActionOne();
         ~QContactActionOne();

         bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap());
         bool invokeAction(const QList<QContactActionTarget>& targets, const QVariantMap& params = QVariantMap());
         QVariantMap results() const;
         State state() const;

     private slots:
         void performAction();
     };

     class QContactActionTwo : public QContactAction
     {
         Q_OBJECT

     public:
         QContactActionTwo();
         ~QContactActionTwo();

         bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap());
         bool invokeAction(const QList<QContactActionTarget>& targets, const QVariantMap& params = QVariantMap());
         QVariantMap results() const;
         State state() const;

     private slots:
         void performAction();
     };

The implementation of these classes might be something like the following (example only):

     QObject* QContactMultiActionPlugin::createInstance(const QServiceInterfaceDescriptor& descriptor,
                             QServiceContext* context,
                             QAbstractSecuritySession* session)
     {
         Q_UNUSED(context);
         Q_UNUSED(session);

         if (descriptor.interfaceName() == QContactActionFactory::InterfaceName
                 && descriptor.serviceName() == QString(QLatin1String("tst_qcontactactions:multiaction"))
                 && descriptor.majorVersion() == 1
                 && descriptor.minorVersion() == 1
                 && descriptor.customAttribute("ActionName") == QString(QLatin1String("call"))) {
             return new QContactMultiActionFactory;
         } else {
             return 0;
         }
     }

     Q_EXPORT_PLUGIN2(contacts_multiaction, QContactMultiActionPlugin);

     QContactMultiActionFactory::QContactMultiActionFactory()
         : QContactActionFactory()
     {
         m_actionOneDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "sip", 1);
         m_actionTwoDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "prop", 1);
     }

     QContactMultiActionFactory::~QContactMultiActionFactory()
     {
     }

     QList<QContactActionDescriptor> QContactMultiActionFactory::actionDescriptors() const
     {
         QList<QContactActionDescriptor> retn;
         retn << m_actionOneDescriptor << m_actionTwoDescriptor;
         return retn;
     }

     QContactAction* QContactMultiActionFactory::create(const QContactActionDescriptor& which) const
     {
         if (which == m_actionOneDescriptor)
             return new QContactActionOne;
         else if (which == m_actionTwoDescriptor)
             return new QContactActionTwo;
         else
             return 0;
     }

     QSet<QContactActionTarget> QContactMultiActionFactory::supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const
     {
         QSet<QContactActionTarget> retn;
         if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) {
             // in this example, they support the same targets.
             QList<QContactPhoneNumber> pndets = contact.details<QContactPhoneNumber>();
             for (int i = 0; i < pndets.size(); ++i) {
                 QContactActionTarget curr;
                 curr.setContact(contact);
                 curr.setDetails(QList<QContactDetail>() << pndets.at(i));
                 retn << curr;
             }
         }

         return retn;
     }

     QContactFilter QContactMultiActionFactory::contactFilter(const QContactActionDescriptor& which) const
     {
         if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) {
             QContactDetailFilter retn;
             retn.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber);
             return retn;
         }

         return QContactFilter();
     }

     QVariant QContactMultiActionFactory::metaData(const QString& key, const QList<QContactActionTarget>& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const
     {
         Q_UNUSED(targets);
         Q_UNUSED(parameters);

         if (key == QContactActionDescriptor::MetaDataLabel)
             return QString("Call with VoIP");
         // Label etc

         if (key == QLatin1String("Provider")) {// our custom metadata - just return which.actionIdentifier
             return which.actionIdentifier();
         }

         return QVariant();
     }

     bool QContactMultiActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const
     {
         if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor)
             return !contact.details<QContactPhoneNumber>().isEmpty();
         return false;
     }

     QContactActionOne::QContactActionOne()
     {

     }

     QContactActionOne::~QContactActionOne()
     {

     }

     bool QContactActionOne::invokeAction(const QContactActionTarget& target, const QVariantMap& params)
     {
         Q_UNUSED(params);
         // this action only works on (contact + phone number) targets.
         if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName)
             return false;

         QTimer::singleShot(1, this, SLOT(performAction()));
         return true;
     }

     bool QContactActionOne::invokeAction(const QList<QContactActionTarget>& targets, const QVariantMap& params)
     {
         Q_UNUSED(params);
         foreach (const QContactActionTarget& target, targets) {
             if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) {
                 return false;
             }
         }

         QTimer::singleShot(1, this, SLOT(performAction()));
         return true;
     }

     QVariantMap QContactActionOne::results() const
     {
         return QVariantMap();
     }

     QContactAction::State QContactActionOne::state() const
     {
         return QContactAction::FinishedState;
     }

     void QContactActionOne::performAction()
     {
         QMessageBox::information(0, "ActionOne", "This is action one!");
         emit stateChanged(QContactAction::FinishedState);
     }

     QContactActionTwo::QContactActionTwo()
     {

     }

     QContactActionTwo::~QContactActionTwo()
     {

     }

     bool QContactActionTwo::invokeAction(const QContactActionTarget& target, const QVariantMap& params)
     {
         Q_UNUSED(params);
         // this action only works on (contact + phone number) targets.  Note that it doesn't
         // have to be the same as QContactActionOne -- it could have an entirely different implementation!
         if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName)
             return false;

         QTimer::singleShot(1, this, SLOT(performAction()));
         return true;
     }

     bool QContactActionTwo::invokeAction(const QList<QContactActionTarget>& targets, const QVariantMap& params)
     {
         Q_UNUSED(params);
         foreach (const QContactActionTarget& target, targets) {
             if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) {
                 return false;
             }
         }

         QTimer::singleShot(1, this, SLOT(performAction()));
         return true;
     }

     QVariantMap QContactActionTwo::results() const
     {
         return QVariantMap();
     }

     QContactAction::State QContactActionTwo::state() const
     {
         return QContactAction::FinishedState;
     }

     void QContactActionTwo::performAction()
     {
         QMessageBox::information(0, "ActionTwo", "This is action two!");
         emit stateChanged(QContactAction::FinishedState);
     }

Once implemented, the plugin must be described by an XML file and installed in an appropriate location. For more information, see the Qt Service Framework documentation.

     <?xml version="1.0" encoding="utf-8" ?>
     <service>
         <name>tst_qcontactactions:multiaction</name>
         <filepath>plugins/contacts/libcontacts_multiaction</filepath>
         <description>This service provides two test QContactAction implementations for testing purposes.  It is also an example of a single plugin providing multiple actions whose descriptors are identical except for their meta data.</description>
         <interface>
             <name>org.qt-project.Qt.SampleContactsActionPlugin</name>
             <version>1.1</version>
             <capabilities></capabilities>
             <customproperty key="ActionName">call</customproperty>
             <description>This plugin can instantiate two different QContactAction instances; one which provides the "call" action via the "sip" provider, the other which provides the "call" action via the "example proprietary protocol" provider.</description>
         </interface>
     </service>

### Deploying Services

Depending on the platform, the service which provides the action must be deployed in a certain way.