Sunday, January 25, 2015

Notification on property change in iOS SDK

I have used QML quite a lot in my projects and there is one quite nice feature in QML that makes implementation quite easy. The feature is Property change notification.

Apple iOS SDK also similar mechanism named Key-Value Observing mechanism. Here is Apple's documentation for the same.

Following is simple code which demonstrate how we can use Key-Value Observer API to get notification on property changes. First we need to register observer and in which property observer is interested in. Code shows, class "self" is interested in property change event from propertyOwner for "isPlaying" property.
[propertyOwner addObserver:self
    forKeyPath:@"isPlaying"
       options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
       context:NULL];
Now, we have registered observer for property change, we need to implement method which will be called in case of property is changed. Following code shows the same. We need to check which object sent event and for which property, then we can take appropriate action.
- (void) observeValueForKeyPath:(NSString *)path ofObject:(id) object change:(NSDictionary *) change context:(void *)context
{
    // this method is used for all observations, so you need to make sure
    // you are responding to the right one.
    if (object == propertyOwner && [path isEqualToString:@"isPlaying"]) {
        // now we know which property is modified
    }
}
When we are done and don't need notification any more we can remove observer. Below is code for the same. It's safe to use try and catch as we might get exception if we try to remove observer when none is registered.
    @try{
        [propertyOwner removeObserver:self forKeyPath:@"isPlaying"];
    }@catch(id anException){
        //do nothing, obviously it wasn't attached because an exception was thrown
    }
Now whenever "propertyOwner" changes's "isPlaying" property then "self" will get notification and "observeValueForKeyPath" method will get called.

Sunday, December 28, 2014

Showing Remote Image using ImageView in BB10 Cascades

While working on my BB10 App update, I required to show Remote Image using ImageView BB10 cascades API. ImageView or Image component by default does not support loading image from URL on internet. I implemented a custom component which serve the purpose and also easily integrated with ImageView.

Following is implementation for the same, I hope it will be useful to someone.

Let's start by showing how my custom component works. Following code shows how to use ImageDownloader custom component along with ImageView to display Grid of images from internet. Data Model contains two kind of URL, url for low resolution image and url for high resolution image.
import my.library 1.0

ListView {
  id: listView
  
  layout: GridListLayout {}
  dataModel: model;
  
  listItemComponents: [
      ListItemComponent {                        
          ImageView {
              id: imgView
              imageSource: "default_image.jpg"  
                                        
              attachedObjects: [
                  ImageDownloader {
                      id: imageLoader
                      url: ListItemData.mediumImage
                      onImageChanged: {                 
                          if (imageLoader.isVaid) {
                              imgView.image = imageLoader.image
                          }
                      }
                  }
              ]
          }
      }
  ]
}
ImageDownloader component is available in QML because we imported "my.library". We can make any C++ code available to QML by registering it to QML System, folloiwing snippet shows how we can register C++ component to QML system.

#include "ImageDownloader.h"

int main(int argc, char **argv) 
{
 Application app(argc, argv);

 qmlRegisterType<ImageDownloader>("my.library",1, 0, "ImageDownloader");

 QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);

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

 return Application::exec();
}
Now that custom component is ready to be used with QML, let's see how its implemented. Below if header file for ImageDownloader class. We are defining few properties like url, image and isValid. By setting "url" we can initiate download of image, when "image" download is finished downloaded image can be accessed by using "image" property. We can check if image is valid or not by checking "isValid" property. we are also defining few signal like "urlChanged" and "imageChanged", which are emited when url is changed or image is downloaded. And we are using QNetworkAccessManager to download image from internet.
#ifndef IMAGEDOWNLOADER_H_
#define IMAGEDOWNLOADER_H_

#include 
#include 

class QNetworkAccessManager;

class ImageDownloader: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QVariant image READ image NOTIFY imageChanged)
    Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
    Q_PROPERTY(bool isVaid READ isValid CONSTANT);
public:
    ImageDownloader( QObject* parent = 0);
    virtual ~ImageDownloader();

signals:
    void urlChanged();
    void imageChanged();

private slots:
    QString url() const;
    void setUrl( const QString& url);

    QVariant image() const;
    bool isValid() const;

    void startDownload();
    void onReplyFinished();

private:
    QNetworkAccessManager mNetManager;
    QString mImageUrl;
    bb::cascades::Image mImage;
    bool mIsValid;
};

#endif /* IMAGEDOWNLOADER_H_ */
Implementation is also quite simple, let's see how its implemented. On URL change, we are initiating the image download using QNetworkAccessManager. When download is finished, we are reading image data in to buffer and creating Image using BB10 Image API.
#include "ImageDownloader.h"

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>

ImageDownloader::ImageDownloader(QObject* parent):
    QObject(parent), mIsValid(false) {}

ImageDownloader::~ImageDownloader() {}

QString ImageDownloader::url() const {
    return mImageUrl;
}

void ImageDownloader::setUrl( const QString& url)
{
    if(url != mImageUrl) {
        mImageUrl = url;
        mIsValid = false;
        mImage = bb::cascades::Image();
        emit urlChanged();
        emit imageChanged();
        startDownload();
    }
}

QVariant ImageDownloader::image() const {
    return QVariant::fromValue(mImage);
}

bool ImageDownloader::isValid() const {
    return mIsValid;
}

void ImageDownloader::startDownload() {
    QNetworkRequest request(mImageUrl);
    QNetworkReply* reply = mNetManager.get(request);
    connect(reply, SIGNAL(finished()), this, SLOT(onReplyFinished()));
}

void ImageDownloader::onReplyFinished() {
    QNetworkReply* reply = qobject_cast(sender());
    QString response;
    if (reply) {
        if (reply->error() == QNetworkReply::NoError) {
            const int available = reply->bytesAvailable();
            if (available > 0) {
                const QByteArray data(reply->readAll());
                mImage = bb::cascades::Image(data);
                mIsValid = true;
                emit imageChanged();
            }
        }
        reply->deleteLater();
    }
}
That's all we have to do to do display remote image in BB10, hope you liked it.

Sunday, December 21, 2014

Resolving "_clock$UNIX2003", referenced from" error

I was trying to build Unity 3D project for simulator SDK. I wanted to get different screen size screen shot. But when I tried to build it I got following build error.
_clock$UNIX2003", referenced from
I found one work around to resolve this issue. We need to add following patch to main.mm file.
#include <time.h>

// "_clock$UNIX2003", referenced from:
//Temporary hack for building Simulator Project for Unity
extern "C"
{
    clock_t
    clock$UNIX2003(void)
    {
        return clock();
    }
}
Once I added above patch build started working fine for me and I was able to run project on Simulator.

Sunday, November 23, 2014

Your Shot Ubuntu Touch Scope

You might have heard about Ubuntu Touch, and one of unique feature of Ubuntu Touch is Scope. Recently they also announced Ubuntu Touch Scope competition. This got me interested, I wanted to learn about Scope development, so I thought to take part as well in the competition.

You can get more information about competition here.

Recently I discovered Your Shot photo community, I like pictures uploaded there and I visit site every day to checkout newly updated photos, so I thought it is good candidate for Scope development and I decided to write score for Your Shot photo community.

Your Shot has nice Jason based web API and I was able to create scope quite easily using Ubuntu Touch socpe's Jason template. In fact I was able to get decent working scope in few hours using template. Documentation is also quite good and rich with API Reference, Guide and Tutorials.

Here is demo for the same running on my desktop.


And Few snapshot.





It looks like now I am ready to write more complex Scope but may be later.

Thursday, October 30, 2014

Creating dynamic QML object asynchronously

We can create QML object dynamically by using createComponent and createObject API.

Like follow,
var component = Qt.createComponent("Button.qml");
if (component.status == Component.Ready)
    component.createObject(parent, {"x": 100, "y": 100});
You can find more information regarding same here.

Latest Qt release added new API named incubateObject. This allows object to be created in asynchronously. You can find more information about this API here.

Following code I written for Ubuntu touch calendar application, which uses the same. Here thing to remember is that incubateObject returns the incubator and not created object itself. You need to get object from incubator by incubator.object call.
var incubator = delegate.incubateObject(bubbleOverLay);
if (incubator.status !== Component.Ready) {
    incubator.onStatusChanged = function(status) {
        if (status === Component.Ready) {
            incubator.object.objectName = children.length;
            assignBubbleProperties( incubator.object, event);
        }
    }
} else {
    incubator.object.objectName = children.length;
    assignBubbleProperties(incubator.object, event);
}