Sunday, April 21, 2013

Un-boxing BlackBerry 10 Dev Alpha C

After a long wait, finally today I received my BlackBerry 10 Dev Alpha C device. As you know it developer version of BlackBerry 10 Qwerty device.

Following are few pics from ub-boxing, if you are curious about how device looks and what comes with it.

It comes in nice little box, same as Dev Alpha B, with Micro USB cable and Power cable.



Build quality wise its also boxy and made from plastic.



On one side , there is micro USB port for data transfer and charging and micro HDMI for TV out, on other side there are media keys, on top there is audio jack and power button.


Here are few snaps after power on.




Wednesday, April 17, 2013

Creating QML ListView with Search support

In last post I posted initial implementation for my Audiobook Reader app for Ubuntu-Touch, I was able to add some more features to it then after. I added support for Adding and removing custom bookmark and play the custom bookmark.

I also added support for searching the books by title in book list view. Similar to email application in Nokia N9. In this post I will show how similar feature can be implemented in QML application.

In my implementation if you pull down the book list, then it will show the search box. You can type in that search box and it will try to show books that matches that typed text. If you don't type for some time, search box will gets disappear.

Here is demo,



Following is my code. First I created TextField where user can type search text. If user type some text then I am applying the filter to my list's data model using typed text and also resetting the timer, which is responsible in hiding the search field.
    ....
    TextField{
        id: searchField
        width: parent.width
        visible: false

        onTextChanged: {
            timer.restart();
            if(text.length > 0 ) {
                model.applyFilter(text);
            } else {
                model.reload();
            }
        }

        onVisibleChanged: {
            if( visible) focus = true
        }

        Behavior on visible {
            NumberAnimation{ duration: 200 }
        }
    }
Following is my list view, Here I am setting its y property based on visibility of search field. Data model implements method for showing filtered data or all the data. Other important function is onContentYChanged, this function makes search field visible if user pull down the book list view.

    ListView {
        id:listView
        clip: true
        width: parent.width
        height: parent.height
        y: searchField.visible ? searchField.height : 0

        Behavior on y {
            NumberAnimation{ duration: 200 }
        }

        model: ListModel {
            id: model
            Component.onCompleted: {
                reload();
            }

            function reload() {
                var bookList = DB.getAllBooks();
                model.clear();
                for( var i=0; i < bookList.length ; ++i ) {
                    model.append(bookList[i]);
                }
            }

            function applyFilter(bookName) {
                var bookList = DB.getBooksByName(bookName);
                model.clear();
                for( var i=0; i < bookList.length ; ++i ) {
                    model.append(bookList[i]);
                }
            }
        }

        delegate: listDelegate

        onContentYChanged: {
            if( contentY < -100 ) {
                searchField.visible = true;
                timer.running = true;
            }
        }
    }

Lastly the timer, which is responsible for hiding the search field if user don't type anything for certain duration.
    Timer{
        id: timer; running: false; interval: 7000; repeat: false
        onTriggered: {
            searchField.visible = false;
        }
    }
That's all needed to add search support to QML ListView.

Sunday, April 14, 2013

Audiobook Reader for Ubuntu-Touch

I have been exploring and working on Ubuntu-Touch for some time now. I have wrote some QML code for official Ubuntu-Touch calendar app and mean time I was also working on porting my Audiobook Reader application. This porting exercise helped me to understand how mature SDK is and also helped me to learn the SDK.

There were some minor issue with Ubuntu-Touch SDK, but nothing serious that can affect the work. As per Ubuntu-Touch development guideline, they prefer QML/Javascript and suggest to avoid C++ code as much as possible. This will help to make sure most of application is compatible for most Ubuntu platform. I tried to follow guideline and tried to not use C++ till now for app. I found myself missing C++ now and then, sometimes there are plugins that we can use instead and sometime there is no alternative. Mostly I missed the File IO, surely SDK team will come up with something, but till now there is no solution.

Anyway, I did not faced any major issue and was able to create initial version of Audiobook Reader quite easily without much problem. Following is demo.


Emitting signal from Javascript in QML application

While I was working on Ubuntu-Touch core app calendar, I wanted to emit signal from java script to QML code.

As such there is no official way provided by Qt framework, but we can try some workaround for this. Initially I used observer pattern to notify QML code from javascript. but I needed to wrote quite handful of code, and I did not liked the solution, I asked my team mates in calendar team and Frank suggested quite neat solution for this.

His suggestion was to create a temporary QtObject in javascript and use it for notification. Following is code for the same.

In following code, I created a QtObject in javascript with dataChanged signal defined in it.

eventsNotifier = Qt.createQmlObject('import QtQuick 2.0; QtObject { signal dataChanged }', Qt.application, 'EventsNotifier');
Now, we can use this object to emit signal to QML code, like this.

function someFunc() {
 ...
 ...
 eventsNotifier.dataChanged();
}
In QML code, we need to attach slot to dataChanged signal,like below.

    import "test.js" as TestJS

    function reload() {
        ...
        ...
    }
    Component.onCompleted: {
        TestJS.eventsNotifier.dataChanged.connect(reload);
    }
This is all, simple and neat.

Thursday, April 4, 2013

Accessing Amazon AWS service from QML/Javascript

Sometime back I published a post that shows how to access Amazon Web Service using Qt. You can find original post here. Now that trend is to code everything from QML and Javascript ( at-least Ubuntu Touch is following that path to make it as device independent as possible), I tired to access AWS service from using pure QML and Javascript.

I required to use external Crypto library for HMACSHA1 algorithm but everything else can easily be done with pure QML/Javascript.

Following is code i used for downloading Book cover image from Amazon AWS.

First all to communicate with AWS you required to have AWS access key id and Secret Access key. You can get it from here. I am also importing the Crypto library, for using HmacSHA1 implementation. I am using crypto-js Library. You can download required the same from here.

.import "JSLib/rollups/hmac-sha1.js" as Crypto

//key and password, required to sign the request using HMACSHA1
var AWS_KEY = "KEY";
var AWS_PASS = "PASSWORD";
var END_POINT = "http://ecs.amazonaws.com/onca/xml";
var AWS_TAG = "TAG"

Now we have required information, we can are ready to create request. In following code, I am putting all required parameter in a map and then using it to create signature and URL. We also need to encode parameter, I am using encodeURIComponent for encoding required parameter.
function downloadCover( author,bookName,callback) {

    var queryItems = {};
    queryItems["AWSAccessKeyId"] = AWS_KEY;
    queryItems["AssociateTag"] = AWS_TAG;
    queryItems["Author"] = encodeURIComponent(author);
    queryItems["Keywords"] = encodeURIComponent(bookName);
    queryItems["Operation"] = "ItemSearch";
    queryItems["ResponseGroup"] = "Images";       
    queryItems["SearchIndex"] = "Books";
    queryItems["Service"] = "AWSECommerceService";
    queryItems["SignatureMethod"] = "HmacSHA1";
    queryItems["Timestamp"] = encodeURIComponent( new Date().toISOString());
    queryItems["Signature"] = createSignature(queryItems);


    var downloadUrl = createUrl(queryItems);
    sendNetworkRequest(downloadUrl,callback);
}
Following is code for creating signature. Creating signature is the only tricky part to make code works as expected. I am using HmacSHA1 from Crypto-JS lib. This function return WordArray object, which can be converted to HEX string, binary string or base64. Once we have HmacSHA1 hash of request, we need to convert it to Base64. I was not able to use base64 function from Crypto-JS library due to some error. I decided to copy base64 function from library directly to my js file and use it. Then finally we need to encode this base64 output, which we can be used as signature while sending request.
function createSignature(queryItems) {
    var strToSign = "GET\n";
    strToSign += "ecs.amazonaws.com\n";
    strToSign += "/onca/xml\n"

    for( var prop in queryItems ) {
        if( prop === "Signature") {
            continue;
        }

        strToSign += ( prop+"="+ queryItems[prop]);
        strToSign += "&"
    }
    //removing last &
    strToSign = strToSign.slice(0,strToSign.length-1)


    var signature = Crypto.CryptoJS.HmacSHA1(strToSign, AWS_PASS);
    signature = base64(signature);

    return encodeURIComponent(signature);
}

Now we are almost done, all we need to do is to use created signature and make HTTP request. Following code shows how. There is nothing new here. I am creating complete URL from all required parameter and making request using this URL by XMLHttpRequest object.

function createUrl(queryItems )
{
    var url = END_POINT+"?";

    for( var prop in queryItems ) {
        url += ( prop+"="+ queryItems[prop]);
        url += "&"
    }
    //removing last &
    url = url.slice(0,url.length-1);
    return url;
}

function sendNetworkRequest(url,callback) {
    var http = new XMLHttpRequest();
    http.onreadystatechange = function() {
        if (http.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
            console.log("Headers -->");
            console.log(http.getAllResponseHeaders ());
            console.log("Last modified -->");
            console.log(http.getResponseHeader ("Last-Modified"));

        } else if (http.readyState === XMLHttpRequest.DONE) {
            console.log(http.responseText);
            callback(http.responseText);
        }
    }

    http.open("GET", url);
    http.send();
}