Qt-based BB10 API Examples Documentation

Contents

Social Invocation Example

Files:

Description

The Social Invocation example demonstrates how to integrate your application with various social networks through the BB10 platform.

Overview

In this example we'll learn how to share texts and images with your friends in social networks like Twitter, Facebook or LinkedIn by using the InvokeActionItem or InvokeManager classes of the BB10 framework.

In opposite to the InvokeActionItems, the InvokeManager is created and used inside the C++ class SocialInvocation, which provides a convenience API to invoke external applications and is exported in main() to the QML environment under the name '_socialInvocation'. Additionally the full paths for the assets directory and the shared camera directory are exported under the name '_dirPaths'.

    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);
    qml->setContextProperty("_socialInvocation", new SocialInvocation(&app));

    // Retrieve the path to the app's working directory
    const QString workDir = QDir::currentPath();

    // Build the path, add it as a context property,
    // and expose it to QML
    QVariantMap dirPaths;
    dirPaths["camera"] = QString::fromLatin1("file://%1/shared/camera/").arg(workDir);
    dirPaths["asset"] = QString::fromLatin1("file://%1/app/native/assets/").arg(workDir);
    qml->documentContext()->setContextProperty("_dirPaths", dirPaths);

    AbstractPane *root = qml->createRootObject<AbstractPane>();
    app.setScene(root);

The UI

The UI of this sample application consists of a TabbedPane with four Pages. Each page demonstrates how to interact with one of the social networks.

The pages share some common UI elements, which have been externalized into the files GeneralBlock.qml and ImageSelectionBlock.qml.

The GeneralBlock is a Container with a header Label and a TextField. The text of the Label can be overwritten through the 'statusLabel' property and the content of the TextField can be accessed through the 'statusText' property.

    Container {

        property alias statusLabel: labelStatus.text
        property alias statusText: textStatus.text

        leftPadding: 20.0
        rightPadding: 20.0
        topPadding: 20.0
        bottomPadding: 20.0

        Label {
            id: labelStatus
            text: qsTr("What do you want to share?")
            textStyle.fontWeight: FontWeight.Bold
        }

        TextField {
            id: textStatus
        }
    }

The ImageSelectionBlock provides a Button to select an image from the local file system. To select a specific image, the FilePicker class is used, which stores the selected file in its custom 'selectedFile' property. Once a valid image file has been selected, it is previewed in an ImageView and a 'Post' Button becomes visible. When the user clicks on it, the 'invoke()' slot of the SocialInvocation object is called to share the image file. The target of the invocation can be configured through the 'invokeTarget' property of the ImageSelectionBlock.

    Container {
        topPadding: 40.0

        property string invokeTarget

        layout: StackLayout {
            orientation: LayoutOrientation.LeftToRight
        }

        Container {
            rightPadding: 20.0

            Button {
                horizontalAlignment: HorizontalAlignment.Center
                text: qsTr("Select Image")
                onClicked: {
                    picker.open()
                }
            }

            Button {
                id: btnPostText
                text: qsTr("Post")
                visible: (picker.selectedFile != "")
                onClicked: {
                    _socialInvocation.invoke(invokeTarget, "bb.action.SHARE",
                                             "image/jpeg",
                                             "file://" + picker.selectedFile)
                }
            }
        }

        ImageView {
            imageSource: "file://" + picker.selectedFile
            maxHeight: 475
            maxWidth: 325
            scalingMethod: ScalingMethod.AspectFit
            verticalAlignment: VerticalAlignment.Top
        }

        attachedObjects: [
            FilePicker {
                id: picker
                property string selectedFile
                title: qsTr("File Picker")
                mode: FilePickerMode.Picker
                type: FileType.Picture
                viewMode: FilePickerViewMode.GridView
                onFileSelected: {
                    selectedFile = selectedFiles[0]
                }
            }
        ]
    }

The 'All Targets' page

The 'All Targets' page just uses a GeneralBlock as UI element and contains a InvokeActionItem to share the text the user has entered into the TextField of the GeneralBlock. Since the InvokeActionItem only specifies the mime type and the action ID, but no invokeTarget, triggering this action opens a dialog where the user can select the target application he wants to invoke. Such an invocation is also called "unbound invocation".

    actions: [
        // General SHARE Framework call
        // Will display all the SHARE Targets available
        InvokeActionItem {
            ActionBar.placement: ActionBarPlacement.OnBar
            query {
                mimeType: "text/plain"
                invokeActionId: "bb.action.SHARE"
            }
            onTriggered: {
                data = _socialInvocation.encodeQString(generalShare.statusText);
                console.log(data);
            }
        }
    ]
    GeneralBlock {
        id: generalShare
    }

The 'Facebook' page

The 'Facebook' page has a more complex UI than the 'All Targets' page. Except from changing your Facebook status, it also allows you to view the profile of a Facebook contact or share an image with a friend via Facebook.

Changing the Facebook status is done through an InvokeActionItem again. This time the 'invokeTargetId' is specified as 'Facebook', which has two consequences:

Such an invocation is also called "bound invocation". Depending on the configured mime type, action ID and URI, the Facebook application can be invoked in different ways to execute different actions. If the mime type is 'text/plain' and the action ID 'bb.action.SHARE', the status message of your profile is updated with the passed data. However if no mime type is set but a URI is specified, the file the URI points to is be shared with some friend on Facebook. See Facebook Invocation for more information.

    // Facebook - update status
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Update Status")
        query {
            mimeType: "text/plain"
            invokeTargetId: "Facebook"
            invokeActionId: "bb.action.SHARE"
        }
        onTriggered: {
            data = _socialInvocation.encodeQString(fbShare.statusText);
            console.log(data);
        }
    },

    // Facebook - share URL
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Post URL")
        query {
            invokeTargetId: "Facebook"
            invokeActionId: "bb.action.SHARE"
            uri: "http://www.blackberry.com"
        }
    },

    // Facebook - share an image from the asset directory
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Post Image")
        query {
            mimeType: "image/jpeg"
            invokeTargetId: "Facebook"
            invokeActionId: "bb.action.SHARE"
            // Share an image that is packaged with the app
            uri: _dirPaths.asset + "images/MissScarlett.png"
        }
    }

If you don't want to rely on InvokeActionItem to invoke the functionality from the Facebook application, you can also invoke it explicitly by using the InvokeManager class. In this example the convenience method invoke() of the SocialInvocation object is called with the target ID, the action ID, the mime type and the URI passed as parameters.

    Button {
        text: qsTr("GO!")
        onClicked: {
            _socialInvocation.invoke("com.rim.bb.app.facebook", "bb.action.OPEN", fbGroup.selectedValue, txtFBProfile.text)
        }
        verticalAlignment: VerticalAlignment.Center
        layoutProperties: StackLayoutProperties {
            spaceQuota: 0.25
        }
    }

At the bottom of the page the ImageSelectionBlock is used to let the user select an image from the local file system and share it with a friend on Facebook.

    ImageSelectionBlock {
        invokeTarget: "Facebook"
    }

The 'Twitter' page

The 'Twitter' page has a similar UI like the 'Facebook' page. It contains a GeneralBlock at the top to enter a Tweet, followed by two custom input fields to either enter a Twitter handle to show its profile, or enter a term to search for on Twitter.

To show the profile of a Twitter user, the invocation URI must follow the pattern 'twitter:connect:<twitter_handle>'.

    TextField {
        id: txtProfile
        hintText: qsTr("Enter Twitter handle")
        verticalAlignment: VerticalAlignment.Center
        layoutProperties: StackLayoutProperties {
            spaceQuota: 0.75
        }
    }
    Button {
        text: qsTr("GO!")
        onClicked: {
            _socialInvocation.invoke("com.twitter.urihandler", "bb.action.VIEW", "", "twitter:connect:" + txtProfile.text)
        }
        verticalAlignment: VerticalAlignment.Center
        layoutProperties: StackLayoutProperties {
            spaceQuota: 0.25
        }
    }

To search for a given term on Twitter, the invocation URI must follow the pattern 'twitter:search:<search_term>'.

    TextField {
        id: txtSearch
        hintText: qsTr("Enter search term")
        verticalAlignment: VerticalAlignment.Center
        layoutProperties: StackLayoutProperties {
            spaceQuota: 0.75
        }
    }
    Button {
        text: qsTr("GO!")
        onClicked: {
            _socialInvocation.invoke("com.twitter.urihandler", "bb.action.VIEW", "", "twitter:search:" + txtSearch.text)
        }
        verticalAlignment: VerticalAlignment.Center
        layoutProperties: StackLayoutProperties {
            spaceQuota: 0.25
        }
    }

Additionally to the input fields the 'Twitter' page contains various InvokeActionItems. This time screenshot has been taken on a device where the local Twitter application has an configured account, therefor the actions show up on the action bar at the bottom.

Depending on the configured mime type, target ID and URI different actions can be executed through the Twitter application. See Twitter Invocation for more information.

    // Twitter - tweet
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Tweet")
        query {
            mimeType: "text/plain"
            invokeTargetId: "Twitter"
            invokeActionId: "bb.action.SHARE"
        }
        onTriggered: {
            data = _socialInvocation.encodeQString(twShare.statusText);
            console.log(data);
        }
    },

    // Twitter - tweet URL
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Post URL")
        query {
            invokeTargetId: "Twitter"
            invokeActionId: "bb.action.SHARE"
            uri: "http://www.blackberry.com"
        }
    },

    // Twitter - tweet image
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Post Image")
        query {
            mimeType: "image/png"
            invokeTargetId: "Twitter"
            invokeActionId: "bb.action.SHARE"
            // Share an image from the user's shared folder
            uri: _dirPaths.camera + "IMG_00000004.jpg"
        }
    }

At the bottom of the page the ImageSelectionBlock is used to let the user select an image from the local file system and post it as Tweet.

    ImageSelectionBlock {
        invokeTarget: "Twitter"
    }

The 'LinkedIn' page

The 'LinkedIn' page contains only one GeneralBlock, which is provides a TextField to enter some text. The text is used by the InvokeActionItem to update the status message. See LinkedIn Invocation for more information about possible parameters of the InvokeActionItem.

    // LinkedIn - update status
    InvokeActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("Update Status")
        query {
            mimeType: "text/plain"
            invokeTargetId: "LinkedIn"
            invokeActionId: "bb.action.SHARE"
        }
        onTriggered: {
            data = _socialInvocation.encodeQString(liShare.statusText);
            console.log(data);
        }
    },

    // LinkedIn - invoke to profile
    ActionItem {
        ActionBar.placement: ActionBarPlacement.OnBar
        title: qsTr("View Profile")
        onTriggered: {
            // URI format is "linkedin:contact:" + public URL that you can find
            // on someone's LinkedIn Profile Page, generally of the form:
            // www.linkedin.com/pub/firstname-lastname/xx/yyy/zzz/
            //
            // Please note that currently (10.0.0.31) the LinkedIn app is only
            // able to view the profile of a 1st, 2nd, or 3rd degree connection
            // of the signed-in user.  For connections beyond that distance,
            // LinkedIn will display a permission error.
            //
            // Add your LinkedIn URL to the fourth parameter below
            _socialInvocation.invoke("com.linkedin.urihandler", "bb.action.VIEW", "", "linkedin:contact:" + "")
        }
    }

Additionally to the InvokeActionItem the page also contains a normal ActionItem which invokes the LinkedIn URI handler to view the profile of a LinkedIn user. You have to modify the source code of the QML file here and put in the public LinkedIn profile URL of the user.

The SocialInvocation class

The SocialInvocation class is just a small convenience wrapper around the bb::system::InvokeManager class. It provides the public slot 'invoke()' to allow access to the InvokeManager from the QML file.

    class SocialInvocation : public QObject
    {
        Q_OBJECT

    public:
        // Creates a new SocialInvocation object with the given @p parent object
        SocialInvocation(QObject *parent = 0);

        // Converts the passed QString to an UTF-8 encoded QByteArray
        Q_INVOKABLE QByteArray encodeQString(const QString& toEncode) const;

    public Q_SLOTS:
        // This method is called to invoke another application with the current configuration
        void invoke(const QString &target, const QString &action,
                    const QString &mimetype, const QString &uri);

        void invokeFoursquareVenueCard (const QString &venue);
        void childCardDone(const bb::system::CardDoneMessage &doneMessage);
        void onSSO();

    private:
        // The central object to manage invocations
        bb::system::InvokeManager* m_invokeManager;
    };

Inside the constructor a new instance of the InvokeManager is created, which is used for subsequent invocation requests.

    SocialInvocation::SocialInvocation(QObject *parent) :
                    QObject(parent), m_invokeManager(new InvokeManager(this)) {

            bool ok = connect(m_invokeManager,
                                          SIGNAL(childCardDone(const bb::system::CardDoneMessage&)), this,
                                          SLOT(childCardDone(const bb::system::CardDoneMessage&)));
            Q_ASSERT(ok);
            Q_UNUSED(ok);
    }

The invoke() method is called from within the QML file to trigger the invocation of the various social network applications. Inside this method an InvokeRequest is created, configured with the parameters as passed to the method and in a last last step set on the InvokeManager.

    void SocialInvocation::invoke(const QString &target, const QString &action,
                    const QString &mimetype, const QString &uri) {
            // Create a new invocation request
            InvokeRequest request;

            request.setTarget(target);
            request.setAction(action);

            if (target == QLatin1String("com.rim.bb.app.facebook")) {
                    QVariantMap payload;

                    if (!uri.isEmpty()) {
                            payload["object_type"] = mimetype;
                            payload["object_id"] = uri;
                    } else {
                            // Open the BlackBerry North America page by default
                            payload["object_type"] = "page";
                            payload["object_id"] = "328506290597521";
                    }

                    request.setMetadata(payload);
            } else {
                    request.setUri(uri);
            }

            m_invokeManager->invoke(request);
    }
    // The Foursquare invocation calls are based on the sample available here:
    // https://github.com/kylefowler/foursquare-bb10-sdk
    //
    // Launches a native Foursquare venue search in your app.
    // This card will call back to your childCardDone slot with the appropriate
    // response for the actions taken by the user.
    //
    // URI Params:
    // query: (optional) prime the venue search with a query
    // client_id: (required) the client id from your oauth consumer
    // client_secret: (required) the client secret from your oauth consumer
    // oauth_token: (required if no client_id/client_secret) pass this if you
    //              already have an authenticated user token, this way venue
    //              search results will be fitted to the user requesting them
    //              for a higher quality queryless search
    //
    // Response:
    // When the user selects a venue, you will get the venue information in
    // JSON format back through the childCardDone slot in the data object.
    // The venue format will match what is listed here in the core venue fields:
    // https://developer.foursquare.com/docs/responses/venue
    //
    // If the user cancels the search without any action: the reason message will be "canceled"
    // If any of the parameters are missing you will get a reason message of "invalid_credentials"
    void SocialInvocation::invokeFoursquareVenueCard(const QString &venue) {
            InvokeRequest cardRequest;
            cardRequest.setTarget("com.foursquare.blackberry.venuesearch.card");
            cardRequest.setAction("bb.action.VIEW");
            cardRequest.setMimeType("venuesearch/foursquare");

            // The client_id and client_secret are the Foursquare API credentials
            // that you receive when registering your app with Foursquare.
            //
            // You can register your app with Foursquare here:
            // https://foursquare.com/developers/apps
            //
            // For more information on Foursquare API credentials, see here:
            // https://developer.foursquare.com/overview/auth

            QUrl uri = QUrl("foursquare://venues/search");

            // Replace the following values with your app's client ID and secret
            uri.addQueryItem("client_id", "UFVANV2FBBFRPXSBXHCCKECVUDANDKP5KQFKICRCA1VAFV4V");
            uri.addQueryItem("client_secret","11AY4DWL0A2CV1NXPKDMS2PJTEACRZJP0BMFXORNCKBSNVMH");

            uri.addQueryItem("query", venue);
            cardRequest.setUri(uri);

            m_invokeManager->invoke(cardRequest);
    }

    // Login/Connect with Foursquare
    // This card will call back to your childCardDone slot with the appropriate
    // response for the actions taken by the user.
    //
    // Pass in your client_id from your Foursquare API consumer.
    //
    // If the user authorizes your app or has already authorized you:
    // The response reason will be "success" and the data block will have a
    // json encoded access token which you can use for authenticated Foursquare
    // requests. That response string looks something like this:
    // { access_token: "masdfvasvawefafawvwef90we0900990092012" }
    //
    // If the user denies the authentication: the response reason will be "denied".
    // If the user cancels the login without any action: the reason message will be "canceled"
    void SocialInvocation::onSSO() {
            InvokeRequest cardRequest;
            cardRequest.setTarget("com.foursquare.blackberry.sso.card");
            cardRequest.setAction("bb.action.VIEW");
            cardRequest.setMimeType("sso/foursquare");

            // Pass in the client_id that you receive when registering
            // your app with Foursquare.
            //
            // You can register your app with Foursquare here:
            // https://foursquare.com/developers/apps
            //
            // For more information on Foursquare API credentials, see here:
            // https://developer.foursquare.com/overview/auth
            cardRequest.setData(QString("UFVANV2FBBFRPXSBXHCCKECVUDANDKP5KQFKICRCA1VAFV4V").toUtf8());

            m_invokeManager->invoke(cardRequest);
    }