Files:
The BBM Contacts example demonstrates how to retrieve your list of contacts(that have the same application installed) from the BBM Social Platform.
In this example we'll learn how to use the ContactService class of the bb::platform::bbm module to retrieve your contacs, that have the same application installed, from the BBM Social Platform.
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 ContactsDisplay class, and the Contact class, which is exposed to QML as a metatype.
The main UI of this application is shown after a successful registration.
Field { id: contactCount objectName: "contactCount" title: qsTr("BBM Contacts") value: "0" }
It consists of one page with a single field to display a single value of the number of contacts found to have your application installed.
// The list of contacts ListView { objectName: "contactListView" listItemComponents: [ ListItemComponent { ContactItem { } } ] // Create Contact page upon the user selecting a contact from the list onTriggered: { //This gives the selected contact. var page = contactPage.createObject(); page.contact = dataModel.data(indexPath); navigationPane.push(page); } }
Following this field is the ListView containing those contacts. Whenever the user triggers one of the ContactItem's, an instance of the ContactPage page is created through a ComponentDefinition and pushed on the navigation pane.
attachedObjects: ComponentDefinition { id: contactPage source: "ContactPage.qml" }
The list items are represented by custom ListItemComponent called ContactItem. Which consists of two labels to display the Contact's brief summary (display name, personal message), and his/her avatar in a LeftToRight order.
// Container that represents a Contact in the ListView Container { topPadding: 40 leftPadding: 40 rightPadding: 40 Container { Container { layout: StackLayout { orientation: LayoutOrientation.LeftToRight } Container { layoutProperties: StackLayoutProperties { spaceQuota: 1 } // Contacts display name Label { verticalAlignment: VerticalAlignment.Center text: ListItemData.displayName textStyle { color: Color.White } } // Contacts personal message Label { verticalAlignment: VerticalAlignment.Center leftMargin: 50.0 multiline: true text: ListItemData.personalMessage textStyle { color: Color.White fontSize: FontSize.Small } } } // The Contacts display image ImageView { verticalAlignment: VerticalAlignment.Center image: ListItemData.avatar scalingMethod: ScalingMethod.None preferredHeight: 100 preferredWidth: 100 } } Container { background: Color.White Divider { } } } }
ImageView { preferredHeight: 300 preferredWidth: 300 image: contact ? contact.avatar : null }
At the top an ImageView is located to show the Contact avatar. The image is provided through the 'avatar' property of the Contact object and uses a placeholder image by default. Only when the call to the ContactService::requestDisplayPicture() method is successful, the real avatar image replaces the placeholder image in the property.
ImageView { verticalAlignment: VerticalAlignment.Center imageSource: "images/busy.png" visible: contact ? contact.busy : false } Label { layoutProperties: StackLayoutProperties { spaceQuota: 1 } text: contact ? contact.displayName : "n/a" textStyle { color: Color.White fontWeight: FontWeight.Bold } }
Below the avatar, an ImageView and a Label are located that show the current busy status and the name of the Contact. Depending on the value of the Contact's 'busy' property, the busy indicator is visible or hidden.
Field { title: qsTr("status message") value: contact ? contact.statusMessage : "n/a" }
For all the other data of the Contact, a Field is created, which is a custom component (implemented in Field.qml) with two Labels side by side. The left Label displays the title of the field and the right Label the corresponding value. All the Contact data are provided by Contact through properties, which we simply bind against the Field's 'value' property.
The Contact class encapsulates the information about the users contact and makes the data available through properties.
class Contact: public QObject { Q_OBJECT // Property to display contacts handle Q_PROPERTY(QString handle READ handle NOTIFY contactChanged) // Property to indicate users ppId Q_PROPERTY(QString ppid READ ppid NOTIFY contactChanged) // Property that indicates this app version running on the contacts device Q_PROPERTY(QString appVersion READ appVersion NOTIFY contactChanged) // BBM Social Platform version that is running on a contact's device Q_PROPERTY(QString platformVersion READ platformVersion NOTIFY contactChanged) // The contacts display name Q_PROPERTY(QString displayName READ displayName NOTIFY contactChanged) // The contacts personal message Q_PROPERTY(QString personalMessage READ personalMessage NOTIFY contactChanged) // The contacts status Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY contactChanged) // Property to indicate contacts availability Q_PROPERTY(bool busy READ busy NOTIFY contactChanged) // The contacts avatar image Q_PROPERTY(QVariant avatar READ avatar NOTIFY avatarChanged) public: Contact() { } /** * Default constructor. */ Contact(const bb::platform::bbm::Contact &contact); ~Contact(); QString handle(); QString ppid(); QString appVersion(); QString platformVersion(); QString displayName(); QString personalMessage(); QString statusMessage(); // Returns true if the contact's status is busy; otherwise, returns false. bool busy() const; QVariant avatar(); Q_SIGNALS: // Emitted when contact info changes void contactChanged(); // Emitted when contact avatar changes void avatarChanged(); public Q_SLOTS: // Corresponds to the bb::platform::bbm::ContactService::displayPictureUpdate() signal void avatarUpdated (const QString &handle, const bb::platform::bbm::ImageType::Type imageType, const QByteArray &displayPicture); private: void setAvatar(const QByteArray &imageData); const bb::platform::bbm::Contact m_bbmspContact; bb::cascades::Image m_avatar; };
Inside the constructor the member variables are initialized.
Contact::Contact(const bb::platform::bbm::Contact &contact) : m_bbmspContact(contact) { // Load the place holder for the display image (avatar) // Image by Decosigner: http://openclipart.org/detail/104977/help-orb-button-by-decosigner m_avatar = bb::cascades::Image(QUrl("asset:///images/avatarPlaceholder.png")); }
Whenever the ContactsDisplay class creates or updates a Contact instance in the ListView model the requestDisplayPicture() method is invoked, this in turn updates the Contact avatar image. This is the result of having the displayPictureUpdate signal of the ContactService object connected to our own avatarUpdated slot, so that whenever the contact changes we retrieve it's image right away. Inside this method we verify that the avatarUpdate is for this Contact using handle comparison, afterwards we take the raw image data that is provided, create an Image object from it, since ImageView can only use that one as input, and emit the change notification signal to let the ImageView update its representation.
void Contact::avatarUpdated(const QString& handle, const bb::platform::bbm::ImageType::Type imageType, const QByteArray& displayPicture) { Q_UNUSED(imageType); //Verify the update handle corresponds to this contact handle if (QString::compare(m_bbmspContact.handle(), handle) == 0) { // Verify that there is an image to be set. if(displayPicture.size() != 0) { setAvatar(displayPicture); } } } void Contact::setAvatar(const QByteArray &imageData) { m_avatar = bb::cascades::Image(imageData); Q_EMIT avatarChanged(); }
In all the other property accessor methods, we simply return the values as we get them from the bb::platform::bbm::Contact object.
QString Contact::displayName() { return m_bbmspContact.displayName(); } QString Contact::personalMessage() { return m_bbmspContact.personalMessage(); }
The ContactsDisplay class encapsulates the code that populates and updates the user contact list in the ListView.
class ContactsDisplay: public QObject { Q_OBJECT public: /** * Default Constructor. */ ContactsDisplay(bb::platform::bbm::Context &context, QObject *parent = 0); public Q_SLOTS: /** * Creates the ContactList qml document and sets * the application scene to it. */ void show(); /** * Updates the ListView model with your bbm contacts. */ void updateModel(); /** * Updates the contact in the datamodel with the * contact changes. */ void contactUpdated(const QString &handle); private: bb::platform::bbm::Context *m_context; bb::cascades::QListDataModel<Contact*>* m_contactsDataModel; bb::platform::bbm::ContactService * m_contactService; };
The class takes a reference to the Context object in the constructor, that instance is used later to create the ContactService. Additionally it provides three slots to create the ContactList.qml UI, update ListView model, and update a single contact.
ContactsDisplay::ContactsDisplay(bb::platform::bbm::Context &context, QObject *parent) : QObject(parent) , m_context(&context) , m_contactsDataModel(new QListDataModel<Contact*>()) , m_contactService(0) { m_contactsDataModel->setParent(this); }
Inside the constructor we just initialize the member variable.
After a successful registration to the BBM service, the show() slot of the ContactsDisplay class is invoked. The signal/slot connection for this is established in main.cpp
RegistrationHandler *registrationHandler = new RegistrationHandler(uuid, &app); ContactsDisplay *contactsDisplay = new ContactsDisplay(registrationHandler->context(), &app); // Whenever the registration has finished successfully, we continue to the main UI bool ok = QObject::connect(registrationHandler, SIGNAL(registered()), contactsDisplay, SLOT(show())); Q_ASSERT(ok); Q_UNUSED(ok);
Inside the show() slot a new ContactService object is created using the supplied Context. This does the low-level communication with the BBM service. We connect all update signals of the ContactService object to our own update slots, so that our DataModel notify modifications as soon as any underlying Contact object is added or changed. We than retrieve the number of bbm contacts that have this application installed, update the qml field with this value and populate the QListDataModel with the Contact's.
At the end of this method, the UI is loaded from ContactList.qml
void ContactsDisplay::show() { // Create the UI QmlDocument *qml = QmlDocument::create("asset:///ContactList.qml").parent(this); AbstractPane *root = qml->createRootObject<AbstractPane>(); Application::instance()->setScene(root); m_contactService = new bb::platform::bbm::ContactService(m_context, this); // Connect the update signals to the model update slot bool ok = QObject::connect(m_contactService, SIGNAL(contactListUpdated()), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(applicationEnabled(const QString&)), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(applicationDisabled(const QString&)), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(contactUpdated(const QString&)), this, SLOT(contactUpdated(const QString&))); Q_ASSERT(ok); if (m_contactService->isValid()) { updateModel(); } // Retrieve the Field.qml custom component and set it's value QObject* contactCountField = root->findChild<QObject*>("contactCount"); QString contCount = QString("%1").arg(m_contactService->contactCount()); contactCountField->setProperty("value", contCount); // Retrieve the ListView and set its datamodel ListView* contactListView = root->findChild<ListView*>("contactListView"); contactListView->setDataModel(m_contactsDataModel); qDebug() << contCount; }
The updateModel() method retrieves a list of Contact's, that have your application installed, using the ContactService. Afterwards, it creates a Contact instance for each one of them and connects the displayPictureUpdated signal to our avatarUpdated slot in order to receive the contact display image when requestDisplayPicture() is made via ContactService.
void ContactsDisplay::show() { // Create the UI QmlDocument *qml = QmlDocument::create("asset:///ContactList.qml").parent(this); AbstractPane *root = qml->createRootObject<AbstractPane>(); Application::instance()->setScene(root); m_contactService = new bb::platform::bbm::ContactService(m_context, this); // Connect the update signals to the model update slot bool ok = QObject::connect(m_contactService, SIGNAL(contactListUpdated()), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(applicationEnabled(const QString&)), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(applicationDisabled(const QString&)), this, SLOT (updateModel())); Q_ASSERT(ok); ok = QObject::connect(m_contactService, SIGNAL(contactUpdated(const QString&)), this, SLOT(contactUpdated(const QString&))); Q_ASSERT(ok); if (m_contactService->isValid()) { updateModel(); } // Retrieve the Field.qml custom component and set it's value QObject* contactCountField = root->findChild<QObject*>("contactCount"); QString contCount = QString("%1").arg(m_contactService->contactCount()); contactCountField->setProperty("value", contCount); // Retrieve the ListView and set its datamodel ListView* contactListView = root->findChild<ListView*>("contactListView"); contactListView->setDataModel(m_contactsDataModel); qDebug() << contCount; }
The contactUpdated() method searches through the ListView data model to find the Contact with the specified handle, once it is found it is replaced with the new Contact instance and the appropriate connections are made in order to get the contact display image update.