Qt-based BB10 API Examples Documentation

Contents

BBM Profile Box Example

Files:

Description

The BBM Profile Box example demonstrates how to create and update the ProfileBox for the application shown in the user's BlackBerry Messenger Profile using the Qt style BBM SP library.

Overview

In this example we'll learn how to use the ProfileBox and ProfileBoxItem class of the bb::platform::bbm module to create or delete the application's ProfileBox.

To use the BBM Social Platform, the application must register first. How this is done is explained in bbmregistration and this sample is based on that code. We will describe here everything that needs to be done after the registration was successful.

The business logic of this application is encapsulated in the ProfileBoxManager class, which is exported to QML under the name '_profileBoxManager'.

UI

The main UI of this application is shown after a successful registration. It consists of the main page, which lists the existing ProfileBox items of the application, and a second page which let the user create a new ProfileBox item.

The profile box list page

The page contains a ListView as central component, which uses the data model that is provided by the ProfileBoxManager through its 'model' property. This model contains entries that describe the available ProfileBox items. The items are visualized by a custom ListItemComponent, that is implemented in ProfileBoxItem.qml, which displays the icon and label of the item side by side. Whenever the user selects an item, the item's ID is stored in the custom property 'selectedItemId'.

    ListView {
        id: listView

        property string selectedItemId

        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill

        dataModel: _profileBoxManager.model

        listItemComponents: [
            ListItemComponent {
                ProfileBoxItem {
                }
            }
        ]

        onTriggered: {
            clearSelection()
            select(indexPath)

            selectedItemId = dataModel.data(indexPath).id
        }
    }

The ID is used by the DeleteActionItem in the action bar to remove the currently selected ProfileBox item from the application's ProfileBox.

    actions: [
        ActionItem {
            title: qsTr("New Profile Box")

            onTriggered: {
                navigationPane.push(createBoxPage.createObject())
            }
        },
        DeleteActionItem {
            onTriggered: {
                _profileBoxManager.removeProfileBoxItem(listView.selectedItemId)
            }
        }
    ]

The second ActionItem in the action bar allows the user to open the page for creating new ProfileBox items. The UI is loaded from NewProfileBoxPage.qml via a ComponentDefinition.

    attachedObjects: [
        ComponentDefinition {
            id: createBoxPage
            source: "NewProfileBoxPage.qml"
        }
    ]

The new profile box page

The UI of the new profile box page basically consists of a TextField to enter the text of the ProfileBox item, a RadioGroup to select the icon of the item and a Button to trigger the actual creation of the ProfileBox item.

    TextField {
        id: profileBoxText
        hintText: qsTr("Type profile box text here")
    }
    RadioGroup {
        id: profileBoxIcon

        Option {
            imageSource: "images/apple.png"
            value: 1
            selected: true
        }

        Option {
            imageSource: "images/pear.png"
            value: 2
        }

        Option {
            imageSource: "images/orange.png"
            value: 3
        }
    }

We call the addProfileBoxItem() method of the ProfileBoxManager and pass the text and the icon ID. The icon ID is a numeric value that must match with the IDs that we register for the ProfileBox (see below).

    Button {
        horizontalAlignment: HorizontalAlignment.Right

        text: qsTr("Save Profile Box")

        onClicked: {
            _profileBoxManager.addProfileBoxItem(profileBoxText.text, profileBoxIcon.selectedValue)
            navigationPane.pop()
        }
    }

The ProfileBoxManager class

The ProfileBoxManager class encapsulates the creation and deletion of the single ProfileBox items. It has a 'model' property of type bb::cascades::DataModel, which makes the available ProfileBox items available to the UI. Furthermore it provides two slots to add and remove a ProfileBox item. The remaining slots and methods are used to update the model or do initialization work.

    class ProfileBoxManager : public QObject
    {
        Q_OBJECT

        Q_PROPERTY(bb::cascades::DataModel* model READ model CONSTANT)

    public:
        ProfileBoxManager(bb::platform::bbm::Context &context, QObject *parent = 0);

    public Q_SLOTS:
        void show();

        void addProfileBoxItem(const QString &text, int iconId);
        void removeProfileBoxItem(const QString &itemId);

    private Q_SLOTS:
        void loadProfileBoxes();
        void itemAdded(const QString &itemId);
        void itemRemoved(const QString &itemId);
        void iconRetrieved(int iconId, bb::platform::bbm::ImageType::Type type, const QByteArray &imageData);

    private:
        void registerIcons();
        void registerIcon(const QString &path, int iconId);

        bb::cascades::DataModel* model() const;

        bb::platform::bbm::ProfileBox* m_profileBox;
        bb::cascades::QVariantListDataModel* m_model;
        bb::platform::bbm::Context *m_context;
    };

Inside the constructor we just initialize the member variables and create the model instance.

    ProfileBoxManager::ProfileBoxManager(bb::platform::bbm::Context &context, QObject *parent)
        : QObject(parent)
        , m_profileBox(0)
        , m_context(&context)
        , m_model(new QVariantListDataModel())
    {
        m_model->setParent(this);
    }

After a successful registration to the BBM service, the show() slot of the ProfileBoxManager class is invoked. The signal/slot connection for this is established in main.cpp

        RegistrationHandler *registrationHandler = new RegistrationHandler(uuid, &app);

        ProfileBoxManager *profileBoxManager = new ProfileBoxManager(registrationHandler->context(), &app);

        // Whenever the registration has finished successfully, we continue to the main UI
        bool ok = QObject::connect(registrationHandler, SIGNAL(registered()), profileBoxManager, SLOT(show()));
        Q_ASSERT(ok);
        Q_UNUSED(ok);

Inside the show() slot the UI is loaded from main.qml file and the loadProfileBoxes() slot is invoked in a delayed way.

    void ProfileBoxManager::show()
    {
        // Create the UI
        QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);

        qml->setContextProperty("_profileBoxManager", this);

        AbstractPane *root = qml->createRootObject<AbstractPane>();
        Application::instance()->setScene(root);

        // Delay the loading of ProfileBox a bit
        QMetaObject::invokeMethod(this, "loadProfileBoxes", Qt::QueuedConnection);
    }

In loadProfileBoxes() the bb::platform::bbm::ProfileBox object is created, which does the low-level communication with the BBM service. We connect our custom slots against the itemAdded() and itemRemoved() signals to get informed whenever new ProfileBox items are added or removed. Additionally we connect against the iconRetrieved() signal to handle the response for icon requests (see below).

Since the ProfileBox does not store the actual image data for the icons but just numeric IDs, we have to register the image data with the icon IDs, which we do in the helper method registerIcons().

Now that everything is prepared, we request all available ProfileBoxItem objects from the ProfileBox object and fill the model with entries that describe them. Instead of the actual icon, we first store the icon ID inside the model entry and trigger a lookup by calling requestRetrieveIcon().

    void ProfileBoxManager::loadProfileBoxes()
    {
        // Create the profile box object
        m_profileBox = new bb::platform::bbm::ProfileBox(m_context, this);

        // Connect all signals to get informed about updates to the profile box
        bool ok = connect(m_profileBox, SIGNAL(itemAdded(QString)), this, SLOT(itemAdded(QString)));
        Q_ASSERT(ok);
        ok = connect(m_profileBox, SIGNAL(itemRemoved(QString)), this, SLOT(itemRemoved(QString)));
        Q_ASSERT(ok);
        ok = connect(m_profileBox, SIGNAL(iconRetrieved(int, bb::platform::bbm::ImageType::Type, QByteArray)),
                     this, SLOT(iconRetrieved(int, bb::platform::bbm::ImageType::Type, QByteArray)));
        Q_ASSERT(ok);

        registerIcons();

        // Fill the model with the initial data
        foreach (const bb::platform::bbm::ProfileBoxItem &item, m_profileBox->items()) {
            // Create a model entry
            const int iconId = item.iconId();

            QVariantMap entry;
            entry["id"] = item.id();
            entry["text"] = item.text();
            entry["iconId"] = iconId;

            // Append the entry to the model
            m_model->append(entry);

            // Request the icon for this icon ID asynchronously
            m_profileBox->requestRetrieveIcon(iconId);
        }
    }

When the ProfileBox has finished the lookup, it will emit the iconRetrieved() signal, which we bound against the custom iconRetrieved() slot. Inside this slot we convert the raw image data, that are passed as parameter, into a bb::cascades::Image object, so that a ImageView can use it directly. Then we iterate over the entries in the model and add an 'icon' property with the Image object as value if the 'iconId' matches.

    void ProfileBoxManager::iconRetrieved(int iconId, bb::platform::bbm::ImageType::Type, const QByteArray &imageData)
    {
        const bb::cascades::Image image(imageData);

        for (int pos = 0; pos < m_model->size(); ++pos) {
            QVariantMap entry = m_model->value(pos).toMap();
            if (entry.value("iconId").toInt() == iconId) {
                entry["icon"] = QVariant::fromValue(image);
                m_model->replace(pos, entry);
            }
        }
    }

Whenever some component adds a new ProfileBoxItem to the application's ProfileBox, our custom slot itemAdded() is invoked. Inside there we fetch the new ProfileBoxItem object from the ProfileBox and add a new entry to the model.

    void ProfileBoxManager::itemAdded(const QString &itemId)
    {
        // Retrieve the new item
        const bb::platform::bbm::ProfileBoxItem item = m_profileBox->item(itemId);

        // Create a model entry for it
        const int iconId = item.iconId();

        QVariantMap entry;
        entry["id"] = itemId;
        entry["text"] = item.text();
        entry["iconId"] = iconId;

        // Append the entry to the model
        m_model->append(entry);

        // Request the icon for this icon ID asynchronously
        m_profileBox->requestRetrieveIcon(iconId);
    }

Whenever some component removes a ProfileBoxItem from the application's ProfileBox, our custom slot itemRemoved() is invoked. Inside there we iterate over the entries of the model and remove the one with the matching ID.

    void ProfileBoxManager::itemRemoved(const QString &itemId)
    {
        // Search the corresponding entry in the model
        for (int pos = 0; pos < m_model->size(); ++pos) {
            if (m_model->value(pos).toMap().value("id").toString() == itemId) {
                // Remove the corresponding entry from the model
                m_model->removeAt(pos);
                return;
            }
        }
    }

If the user wants to creates a new ProfileBox item and clicks on the 'Save Profile Box' button in the UI, addProfileBoxItem() is called. This method simply forwards the call to the ProfileBox object.

    void ProfileBoxManager::addProfileBoxItem(const QString &text, int iconId)
    {
        m_profileBox->requestAddItem(text, iconId);
    }

If the user wants to delete the currently selected ProfileBox item and triggers the 'Delete' action in the UI, removeProfileBoxItem() is called. This method also forwards the call to the ProfileBox object.

    void ProfileBoxManager::removeProfileBoxItem(const QString &itemId)
    {
        m_profileBox->requestRemoveItem(itemId);
    }

The registerIcons() method, which is called in the initialization code above, registers the three icons add the BBM service with the same numeric IDs that we use in the RadioGroup in the UI.

    void ProfileBoxManager::registerIcons()
    {
        const QString imageDir(QDir::currentPath() + QLatin1String("/app/native/assets/images/"));
        registerIcon(imageDir + QLatin1String("apple.png"), 1);
        registerIcon(imageDir + QLatin1String("pear.png"), 2);
        registerIcon(imageDir + QLatin1String("orange.png"), 3);
    }

For each icon it calls the helper method registerIcon(), which reads out the raw image data from the file and calls requestRegisterIcon() on the ProfileBox object with the image type and image data as parameters.

    void ProfileBoxManager::registerIcon(const QString& path, int iconId)
    {
        // Read the icon from the file
        QFile file(path);
        if (!file.open(QIODevice::ReadOnly))
            return;

        const QByteArray imageData = file.readAll();

        // Create the icon object and register the icon
        m_profileBox->requestRegisterIcon(iconId, bb::platform::bbm::ImageType::Png, imageData);
    }