DEVELOPERS BLOG

Pocket integration via ReadItNow! on BlackBerry 10

HOW-TO / 10.06.15 / svenziegler77

ReadItNow_Featured_NEW

Sometimes you don’t have the time to read everything as soon as you discover it. It’s the same for users of your apps as well. For that reason it could be a good fit to add Pocket integration to your apps. For those of you who don’t know what Pocket is, it’s a so called “read it later” service, available cross platform for multiple devices. Pocket enables you to save everything you want to read for later, and access it on multiple devices and platforms. For more informations please have a look at GetPocket.com.

Pocket itself doesn’t offer a native BlackBerry 10 app. You can sideload the official android app, but that version does not integrate well with the BlackBerry 10 user interface and flow. But Pocket has a public API. ReadItNow! is probably the most popular and complete Pocket app for BlackBerry 10. It has been developed by myself over the past few years and it get’s continually updates. I’ve designed the app to be invoked by other BlackBerry 10 apps. For that reason it doesn’t need much effort at all, to integrate Pocket support for your own BlackBerry 10 app.

I’ve put together a few sniplets to show you how to integrate it with C++ and QML. It’s really easy, you will see.

Let’s start with the header file of the class, where you want to add the invocation. You should add the following lines to your header file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public:
    Q_INVOKABLE void invokeReadItNowWithURL(const QString &url);
    Q_INVOKABLE void invokeReadItNowWithText(const QString &text);
 
signals:
    void readitnowNotFound();
 
private slots:
    void onCheck3rdPartyAppsResponse();
 
private:
    void check3rdPartyApps();
 
    bool m_readitnowAvailable;

In line 12 we’re defining a new function called check3rdPartyApps(). We’ll use it to detect automatically if the user has ReadItNow! installed on his device. As this check will be asynchronously, we need a slot for handling the response as well. The slot has been already defined in line 9 (onCheck3rdPartyAppsResponse()).

Now let’s see how both functions looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void ApplicationUI::check3rdPartyApps()
{
    qDebug() << "check3rdPartyApps()";
    m_readitnowAvailable = false;
    InvokeQueryTargetsRequest request;
    request.setMimeType("text/plain");
    const InvokeReply *reply = m_invokeManager->queryTargets(request);
    // Listen for the results
    bool ok = QObject::connect(reply, SIGNAL(finished()), this, SLOT(onCheck3rdPartyAppsResponse()));
    Q_ASSERT(ok);
    Q_UNUSED(ok);
}
 
void ApplicationUI::onCheck3rdPartyAppsResponse()
{
    qDebug() << "onCheck3rdPartyAppsResponse()";
 
    // Get the reply from the sender object
    InvokeQueryTargetsReply *reply = qobject_cast<InvokeQueryTargetsReply*>(sender());
    if (reply) {
        if (reply->error() == InvokeReplyError::None) {
            QList<InvokeAction> targetsByAction = reply->actions();
            foreach (const InvokeAction &action, reply->actions()) {
                foreach (const InvokeTarget &target, action.targets()) {
                    //qDebug() << target.name();
                    if (target.name().indexOf("com.svzi.rin") != -1) {
                        m_readitnowAvailable = true;
                    }
                }
            }
        } else if (reply->error() != InvokeReplyError::None) {
            qDebug() << "ERROR:" << reply->error();
        }
 
        // Clean up the results
        reply->deleteLater();
    }
    qDebug() << "m_readitnowAvailable:" << m_readitnowAvailable;
}

We’re using a InvokeQueryTargetsRequest in line 5 to search for apps that could handle “text/plain” mime type (line 6). We query the targets in line 7 and connect the finished() signal of that request with our onCheck3rdPartyAppsResponse() slot.

In our slot we iterate over all actions (line 23) and the corresponding targets (line 24). If the target name is “com.svzi.rin”, we found the app installed on that device. We need to remember that for later, so we set our class variable m_readitnowAvailable to true (line 27).

I usually call the check3rdPartyApps() in the constructor of my class, as well as every time the app got foregrounded. This way the user can start the app without having ReadItNow! installed, minimize your app, download ReadItNow! from the BlackBerry World and can use it right away, because you already checked and found it as your app got maximized again.

Now let’s share some content to ReadItNow!

There are two different ways to share content to ReadItNow:

  1. Send an URL / URI
  2. Send plain text, that will get parsed for any containing URLs!

For that reason we had defined two different methods in our header file (lines 2 and 3).

1) Send an URL / URI

Sending an URL to ReadItNow! is really easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ApplicationUI::invokeReadItNowWithURL(const QString &url)
{
    if (m_readitnowAvailable) {
        bb::system::InvokeManager *invokeManager = new bb::system::InvokeManager(this);
        bb::system::InvokeRequest request;
 
        request.setTarget("com.svzi.rin");
        request.setAction("bb.action.SHARE");
        request.setUri(url);
 
        invokeManager->invoke(request);
    } else {
        emit readitnowNotFound();
    }
 
}

Before you start the invocation you should check if ReadItNow! was found on the device. If the app isn’t installed you could emit a signal, as shown in our example in line 13, to show a dialog or popup to the user and provide a link to the BlackBerry World to let him easily grab his copy of ReadItNow in order to use it with your app.

Back to the function. Lines 7 – 9 are defining the invocation request. As you can see the url parameter will be set as request URI. That’s all you need to do, to add Pocket support to your own app.

2) Send plain text, that will get parsed for any containing URLs!

But if you don’t want to share a simple URL, but a text containing one or more URLs, you need to add another function. But it doesn’t get complicated, it’s just as easy as sharing a single URL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ApplicationUI::invokeReadItNowWithText(const QString &text)
{
    if (m_readitnowAvailable) {
        bb::system::InvokeManager *invokeManager = new bb::system::InvokeManager(this);
        bb::system::InvokeRequest request;
 
        request.setTarget("com.svzi.rin");
        request.setAction("bb.action.SHARE");
        request.setData(text.toUtf8());
 
        invokeManager->invoke(request);
    } else {
        emit readitnowNotFound();
    }
}

The only difference compared with our previous invocation is, despite the param name, the missing request.setUri(..). That call has been replaced with request.setData(…) in line 9. That’s all you need to do here as well. ReadItNow! will parse the text for any URLs and store every one of them to the users Pocket account.

What’s next?

Now you can can call either invokeReadItNowWithURL(…) or invokeReadItNowWithText(…). Whatever you want to save for later reading. You can call it from C++:

1
invokeReadItNowWithText("This string contains links like http://bit.ly/1VDf28P and http://blck.by/1MX2uHg ! Both will be stored to Pocket without any issues. :-)");

And from QML as well:

1
_app.invokeReadItNowWithURL("http://bit.ly/1VDf28P");

You can find a working example app here: https://github.com/svzi/ShareToReadItNow

Please let me know if you have any issues with the integration, I’m happy to help.

Have you added ReadItNow to your app? Share your app below and I will review it for inclusion in my list of 3rd Party Apps.

That’s it. 🙂 Happy integrating!

Be sure to check out my applications on BlackBerry World!

About svenziegler77