Showing posts with label QML. Show all posts
Showing posts with label QML. Show all posts

Saturday, December 27, 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, 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);
}

Saturday, June 7, 2014

Geting Call notification from Cascades QML code in BB10

I was updating my Audiobook Reader application. I wanted to listen to our going or incoming call and pause book accordingly.

BB10 provides nice API to control call and listen to it.

This post will show how we can listen to call from Cascades QML code. First we need to ask for access_phone and control_phone permission in our bar descriptor file.
    <permission>access_phone</permission>
    <permission>control_phone</permission>
Now we need to link system lib to our app.
LIBS += -lbbsystem 
Once this is done, we need to register Phone type to QML system so we can access it from QML.

#include <bb/system/phone/Phone>
...
using namespace bb::system;

int main(int argc, char **argv) {
    qmlRegisterType("bb.system.phone", 1, 0, "Phone");
    ...
}
Now we are ready to use Phone API from QML side. Following is how QML code will look like. In code we are creating Phone object. On any call related event callUpdated signal will be called. So we are handling that signal in onCallUpdated event. Please note that we can not access Call's properties with QML, as its not derived from QObject, so if you need to access properties of Call then you need to forward call to C++ and handle it there.

import bb.system 1.0
import bb.system.phone 1.0

Page {
    attachedObjects: [
        Phone {
            id: phone
            onCallUpdated: {
                console.log(" ############## Phone::onCallUpdated ") ; 
                //do the needful
            }
        }
    ]
}
Thant's it. Hope it will be helpful.

Sunday, April 13, 2014

Creating Custom Swipe handler in QML

I was often request for sample that shows how custom swipe handler can be created in QML. In this post I will show how same can be achieved.

Please note that code is just prototype level code and is not tested well with actual use. It has also lots of hard coded value that assume certain size of application.

But, you should be able to change those according to your use and can try sample with your app.

This sample implement three QML views and you can swipe on that to change view from one to next. This code also implement some parallax effect on view and view transition animation. In addition to swipe you can also change view using keyboard Left/Right arrow key. Following is demo for the sample app.



So let's start with code.

Following code is from SwipeHandler.qml, it extends MouseArea and try to detect swipe based on mouse's x position change. Swipe can be generated by two way, by flicking on view or dragging it.
Flick is detected, if there is large change in mouse x position in less time. In case of drag, if mouse travel certain distance then code consider it as a swipe.

import QtQuick 2.0

MouseArea{
    id: root

    property int oldX: mouseX;
    property int swipeOffset: 100;
    property int originX:mouseX;

    property var gestureStartTime;
    property bool gestureStarted: false;

    signal swipeEnded(var diff);
    signal swipeContinues(var diff);

    anchors.fill: parent

    onReleased: {
        if( gestureStarted ) {
            //swipe canceled
            root.swipeEnded(0);
            resetGesture();
        }
        //else swipe is already ended
    }

    onPressed: {
        gestureStarted =  true;
        gestureStartTime = new Date();
    }

    onMouseXChanged: {
        if( mouseX < parent.x
        || mouseX > parent.width || gestureStarted == false )
            return;

        if( originX == 0 ) {
            originX = mouseX; oldX = mouseX;
            return;
        }

        var diff = (oldX - mouseX);
        if(handleFlick(diff)){
            return;
        }

        if( haldleDrag(mouseX, diff)){
            return;
        }

        oldX = mouseX;
        root.swipeContinues(diff);
    }

    function resetGesture() {
        originX = 0; oldX = 0;
        gestureStarted =  false;
    }

    function haldleDrag(xPos,xPosDiff){
        if(xPosDiff < 0) {
            if( Math.abs(originX-xPos)  > swipeOffset ){
                root.swipeEnded(xPosDiff);
                resetGesture();
                return true;
            }
        } else {
            if( Math.abs(originX-xPos) >  swipeOffset ){
                root.swipeEnded(xPosDiff);
                resetGesture();
                return true;
            }
        }
        return false;
    }

    function handleFlick(xPosDiff){
        var now = new Date();
        var timeDiff = now - gestureStartTime;

        //high velocity and large diff between start end point
        if(timeDiff < 40 && Math.abs(xPosDiff) > 10 ){
            if(xPosDiff < 0) {
                root.swipeEnded(xPosDiff);
                resetGesture();
                return true;
            } else {
                root.swipeEnded(xPosDiff);
                resetGesture();
                return true;
            }
        }
        return false;
    }
}
So, this was SwipeHandler which can detect if swipe is generated or not. To demonstrate its use, I created a small View Management component, that create's three views. On swipe, view changes form one to another base on direction of swipe movement. Here is code for the same.
import QtQuick 2.0

Rectangle {
    id: root
    width: 200
    height: 300

    property var delegate: comp;

    property var centralView;
    property var nextView;
    property var prevView;

    focus: true

    Component.onCompleted: {
        var colors = ["red","blue","green"];
        var objs = [];
        for(var i =0; i < 3; ++i){
            var obj = comp.createObject(root);
            obj.text = i+1;
            obj.color = colors[i];
            objs.push(obj);
        }

        centralView = objs[0]
        nextView = objs[1]
        prevView = objs[2]

        setViewPos();
    }

    function setViewPos(oldX){
        centralView.animate(50,0);
        nextView.animate(50,root.width);
        prevView.animate(50,-root.width);

        centralView.z = 1;
        nextView.z = 0;
        prevView.z = 0;
    }

    Keys.onRightPressed: {
        var tempView = centralView;
        centralView = prevView;
        prevView = nextView;
        nextView = tempView;

        centralView.animate(150,0);
        nextView.animate(150,root.width);
        prevView.x = -width
    }

    Keys.onLeftPressed: {
        var tempView = centralView;
        centralView = nextView;
        nextView = prevView;
        prevView = tempView;

        centralView.animate(150,0);
        prevView.animate(150,-root.width);
        nextView.x = width
    }

    SwipeArea{
        onSwipeEnded: {
            if(diff === 0) {
                root.setViewPos();
                return;
            }

            var tempView = centralView;
            if(diff < 0) {
                centralView = prevView;
                prevView = nextView;
                nextView = tempView;
            } else {
                centralView = nextView;
                nextView = prevView;
                prevView = tempView;
            }
            root.setViewPos();
        }

        onSwipeContinues: {
            centralView.x = centralView.x - diff;
            if(diff < 0) {
                prevView.x = prevView.x  + Math.abs(diff*1.6);
                prevView.z = 1
                centralView.z = 0;
            } else {
                nextView.x = nextView.x - Math.abs(diff*1.6) ;
                nextView.z = 1
                centralView.z = 0;
            }
        }
    }

    Component{
        id: comp
        Rectangle{
            id: rect
            property alias text: label.text

            width: parent.width; height: parent.height
            Text{
                id: label; anchors.centerIn: parent
            }

            function animate(duration, to){
                anim.to = to; anim.duration = duration
                anim.running = true
            }

            PropertyAnimation{
                id: anim; target:rect; property: "x";duration: 50
            }
        }
    }
}

Sunday, December 1, 2013

Exposing C++ ENUM to QML

I was working on update of one of my game and I while re-factoring I was required to expose C++ Enum to QML code.

Following is how header looks like with definition of Enum GamePadButton. I am registering Enum with Qt's Metaobject system using Q_ENUMS macro.
class GamePadObserver: public QObject {
 Q_OBJECT
 Q_ENUMS(GamePadButton)
public:

 enum GamePadButton{
   A_BUTTON=0,
   B_BUTTON,
   C_BUTTON,
   X_BUTTON,
   Y_BUTTON,
   Z_BUTTON,
                 ...
   NO_BUTTON
 };

public:
 GamePadObserver(QObject* parent = 0);
 virtual ~GamePadObserver();
        ...
};
Now to be able to see this Enum to QML code we need to register GamePadObserver class to Qt Metaobject system.If you want to be able to create instance of class then you can use qmlRegisterType() macro.

In my case I dont want to create instace of GamePadObserver in QML, I just want to expose it enum to QML and for that purpose we can use qmlRegisterUncreatableType macro. Its useful for exposing enum and attached property. Following how we can use this macro.
#include 

int main(int argc, char **argv)
{
 qmlRegisterUncreatableType("GamePadObserver", 1, 0,"GamePadObserver", "");
        ...
}
Now we can use Enum from QML, following how we can do that.
...
import GamePadObserver 1.0
...

Rectangle {
    id:main
    ...

    Connections{
        target: GamePad
        onButtonPressed: {
  
            if( button == GamePadObserver.X_BUTTON
            || button == GamePadObserver.Y_BUTTON 
            || button == GamePadObserver.A_BUTTON 
            || button == GamePadObserver.B_BUTTON ) {
                ...
            } 
        }        
    }

}

Friday, September 20, 2013

Sharing on Facebook, Twitter with BB10 Cascades app

I am currently adding sharing feature to my BB10 Audiobook Reader app. I wanted to allow sharing on Facebook, Twitter, BBM  and other supported platform.

BB10 support this via Invocation framework, Using Invocation framework you can share things using native application.

Using Invokation Framework is easy, you just need use InvokeActionItem and set some property and all is done.

Following is my code that share comment via Facebook or Twitter. Following code will allow to share using any application which support plain text invocation target.

By default it will also have sharing icon, you can use Image property to change it to custom icon.

Pressing share button on toolbar will show all application which can share the data specified by mimeType, User can select one of those and share data using that application.

Page {
    ...
    actions[
        InvokeActionItem {
            title: "Share"
            query {
                mimeType: "text/plain"           
                invokeActionId: "bb.action.SHARE"
            }
            onTriggered: {
                data = "Comments to share";
            }
        }
        ...
    ]
    ...
}
You can find more about, how to share with individual application from here. And here is documentation for InvokeActionItem.

Here is how it looks and works on device.




Friday, August 30, 2013

Ubuntu Touch Calendar prototype for new design


Lately I am working on Ubuntu Touch's Calendar core app. Recently I tried to create a prototype for calendar to show how new design looks for the app.

Here if demo on youtube.



And following are few snaps from prototype.






Monday, August 26, 2013

Using Invocation API with BB10 Cascades

I was working on update of my application Audiobook Reader. Currently my application has custom file browser that allow user to add file but I wanted to add allow user to add book/file directly from native BB10 file browser, like shown in below picture.



BB10 supports this usecase by Invocation API, your application can receive invocation from other application by using it. Your application can also use other application by the same way.

In my app, I just wanted to receive invocation from default File Browser. To do this we need to register our application as invocation target by entering details in to .bar file.

Following is entry for my app.

Here Target-Type mean can be Card or Application, Application will launch app as separate process, Card will launch app in scope of calling app.

Icon is your app's icon, that will be used by other app to display it in menu.

Action Open mean, suggested file can be opened by other app. Other possible value is View.

Mime-Type is type of file your application can support and exts mean, the extension of file that your app can handle.

    <invoke-target id="com.example.AudiobookReader">
      <invoke-target-type>application</invoke-target-type>
      <invoke-target-name>Audiobook Reader</invoke-target-name>
      <icon><image>icon.png</image></icon>
      <filter>
        <action>bb.action.OPEN</action>
        <mime-type>audio/*</mime-type>
        <property var="exts" value="mp3,..,..,..."/>
      </filter>
    </invoke-target>

Once this is done, Then we need to add handler code that will be called when other application invoke our app. To do that, we need to connect InvokeManager's onInvoke signal to our slot.

like below.

 bb::system::InvokeManager invokeManager;

 QObject::connect(&invokeManager, SIGNAL(invoked(const bb::system::InvokeRequest&)),
     &helper, SLOT(onInvoke(const bb::system::InvokeRequest&)));

When our slot is called, we can retrieve URL and other information from InvokeRequest and do the further processing.

void Helper::onInvoke(const bb::system::InvokeRequest& request) {

 mInvokationUrl = request.uri().toString(QUrl::RemoveScheme);

 QFile file( mInvokationUrl );
 QFileInfo fileInfo( file );

 if( !QFile::exists(mInvokationUrl) ) {
  showErrorDialog("Error Locating file!!");
  return;
 }

 if( !FileModel::isSupportedMedia(mInvokationUrl)) {
  showErrorDialog("Not valid Audio file!!");
  return;
 }

 showAddBookDialog(fileInfo);
}
This is it, you can learn more about invocation framework from here.

Tuesday, July 23, 2013

Getting Size of control in BB10 Cascades API

I was working on update for my Audiobook Reader application for BB10. I wanted to create a custom control like shown below and I required to use AbsoluteLayout to create it.



In this case layouting needs to be done manually and to do that we need to consider width and height of the controls and position controls using those. But unlike Qt Quick, BB10 Cascades controls does not provide access to width, height and x,y properties.

However Cascades API offers LayoutUpdateHandler control, this control gets notification when layout is complete and with notification it offers actual location and size of control and it also store these values in it's layoutFrame property. I used that to find out size of control and did required layouting.

Following code shows how LayoutUpdateHandler can be used to find out size of control.
    Container {
        id: indicators
        layout: AbsoluteLayout {}
        
        touchPropagationMode: TouchPropagationMode.Full
        
        background: Color.DarkGray

        attachedObjects: [
            LayoutUpdateHandler {
                id: handler
            }
        ]

        property alias width: handler.layoutFrame.width;
        property alias height: handler.layoutFrame.height;
    }
Now you can use width and height property to know control's actual size.

Friday, June 21, 2013

Inviting Application Review from Application in BB10 using Cascades API

Most time people leave a review for application when they are unhappy but if they are happy with your app, Chances are high that they will ignore the review. And this might leave impression on App World that your application if not working fine and is of low quality.

To overcome this we might offer user a option to review application from within application and some user might be happy enough to provide review.

BB10 has nice QML Cascades API, which makes the task very easy. Only few lines of code and you can have option to offer review from application.

Following is the code which I am using. You can use InvokeActionItem, with actions property of Page. "sys.appworld" this is invocation target id for App World. And in URI you need to mention your application's ID, which you can find from App World.
Page {
    ...
    actions: [
        ...
        InvokeActionItem {
            title: "Rate Application"
            ActionBar.placement: ActionBarPlacement.InOverflow
            imageSource: "rate.png"
            query {
                invokeTargetId: "sys.appworld"
                invokeActionId: "bb.action.OPEN"
                uri: "appworld://content/20200034"
            }
        }
        ...
    ]
    ...
}


Saturday, June 8, 2013

Using QML Camera and passing image to C++ code

I tried to compile one my application with Qt5. Application was using QML camera and sharing image to C++ code for further processing.

Following is sample code, it works with Qt5 and Qt Multimedia 5.

Lets start with ImageProcessor class, the C++ class which is called from QML to further image processing.

Following is header file for ImageProcessor class, it declares processImage() slot which can be invoked from QML code.
#ifndef IMAGEPROCESSOR_H
#define IMAGEPROCESSOR_H

#include <QObject>

class ImageProcessor : public QObject
{
    Q_OBJECT
public:
    explicit ImageProcessor(QObject *parent = 0);
 
public slots:
    void processImage( const QString& image);   
};
#endif // IMAGEPROCESSOR_H
Following is cpp file for ImageProcessor class. processImage() function retrieves Image from camera image provider. Once we have valid image, we can process it further.
#include "imageprocessor.h"
#include <QtQml/QmlEngine>
#include <QtQml/QmlContext>
#include <QtQuick/QQuickImageProvider>
#include <QDebug>

ImageProcessor::ImageProcessor(QObject *parent)
    : QObject(parent)
{}

void ImageProcessor::processImage( const QString& path)
{
    QUrl imageUrl(path);
    QQmlEngine* engine = QQmlEngine::contextForObject(this)->engine();
    QQmlImageProviderBase* imageProviderBase = engine->imageProvider(
     imageUrl.host());
    QQuickImageProvider* imageProvider = static_cast<QQuickImageProvider>
     (imageProviderBase);
    
    QSize imageSize;
    QString imageId = imageUrl.path().remove(0,1);
    QImage image = imageProvider->requestImage(imageId, &imageSize, imageSize);
    if( !image.isNull()) {
        //process image
    }
}
Now we need to register ImageProcessor class with QML. so that we can use it from QML code. This can be done by using qmlRegisterType global function.
#include  <QtGui/QGuiApplication>
#include  <QQmlEngine>
#include  <QQmlComponent>
#include  <QtQuick/QQuickView>

#include "imageprocessor.h"

int main(int argc, char *argv[])
{
    qmlRegisterType<ImageProcessor>("ImageProcessor", 1, 0, "ImageProcessor");

    QGuiApplication app(argc, argv);

    QQuickView view;

    QObject::connect(view.engine(),SIGNAL(quit()),&app,SLOT(quit()));    
    view.setSource(QUrl::fromLocalFile("qml/main.qml"));
    view.show();

    return app.exec();
}
That's all from C++ side, QML code is event easier. Following how you can use ImageProcess class from QML code.
import QtQuick 2.0
import QtMultimedia 5.0
import ImageProcessor 1.0

Rectangle {
    width: 360
    height: 360

    //shows live preview from camera
    VideoOutput {
        source: camera
        anchors.fill: parent
        focus : visible
    }

    //shows captured image
    Image {
        id: photoPreview
        anchors.fill: parent
        fillMode: Image.PreserveAspectFit
    }

    Camera {
        id: camera
        imageProcessing.whiteBalanceMode: CameraImageProcessing.WhiteBalanceFlash
        captureMode: Camera.CaptureStillImage

        exposure {
            exposureCompensation: -1.0
            exposureMode: Camera.ExposurePortrait
        }

        flash.mode: Camera.FlashRedEyeReduction

        imageCapture {
            onImageCaptured: {
                photoPreview.source = preview
                imageProcessor.processImage(preview);
            }
        }
    }

    MouseArea{
        anchors.fill: parent
        onClicked: {
            camera.imageCapture.capture();
        }
    }

    //image processor for further image processing
    ImageProcessor{
        id: imageProcessor
    }
}

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();
}

Friday, March 1, 2013

Using LocalStorage API from javascipt in QML application

Recently I needed to create database from QML application on which I am working. I wanted to separate database related code from QML and created javascript file to handle database related work. I had to struggle a little to make storage API work from javascript, below are my findings.

As you might already know to use sqllite database in QML you will need to import LocalStorage API.
import QtQuick.LocalStorage 2.0 as Sql
This will work for QML but if you want to use it from javascript then you will need to use following syntax.
.import QtQuick.LocalStorage 2.0 as Sql
Now you should be able to use LocalStorage API in javascript. LocalStorage related API can be accessed by using LocalStorage object and we imported LocalStorage module as Sql, we will need to use access LocalStorage object from Sql scope. Like below.
var db = 
   Sql.LocalStorage.openDatabaseSync("TestDB", "1.0", "Description", 100000);
Now you can use this db object to create table and select values from it just like you do in QML. Following is sample example.
function getDatabase() {
     var db = 
     Sql.LocalStorage.openDatabaseSync("TestDB", "1.0", "Description", 100000);

     //create table
     db.transaction(
        function(tx) {    
var query="CREATE TABLE IF NOT EXISTS TEST(Id INTEGER PRIMARY KEY, Title TEXT)";                   
            tx.executeSql(query);
      });

     return db;
}

function printValues() {

    var db = getDatabase();
    db.transaction( function(tx) {
        var rs = tx.executeSql("SELECT * FROM TEST");
        for(var i = 0; i < rs.rows.length; i++) {
            var dbItem = rs.rows.item(i);
            console.log("ID:"+ dbItem.Id + ", TITLE:"+dbItem.Title);
        }
    });
}

Sunday, February 3, 2013

Audiobook Reader for BlackBerry BB10

For past some time I was working on porting my Audiobook Reader application to BlackBerry BB10 platform.

Application is quite similar to Meego version, though there are few feature missing in BB10 which are available on Meego version. I thought I will work on that once I have some feedback from existing version. So if you have installed and have some feedback kindly provide those.

Application is available both as free version and paid version. Feature wise both version are same, only difference is free version is ad sponsored.

Here is live demo on BB10 dev alpha device.


Here are few snapshot of application for BB10.








Hope you will like the application.






Friday, February 1, 2013

Preventing display dimming in BB10 with Qt

Recently I was creating an application for BB10, where I need to disable display dimming. This same approach also works to avoid auto lock.

There is already API for this if you are using cascades API.

Following should do for Cascades Application
//for C++
Application::mainWindow->screenIdleMode = ScreenIdleMode::KeepAwake;

//for QML, we need to use constant as it seems required enum is not exported properly
Application.mainWindow.screenIdleMode = 1

But my application was pure Qt application. For this I required to use native API. Following is code which i used to achieve desired behavior.
        QScopedPointer view(new QDeclarativeView());
        view->setSource(QUrl("app/native/assets/main.qml"));
        view->showFullScreen();

 int idle_mode = SCREEN_IDLE_MODE_KEEP_AWAKE;
 WId winId = view->winId();
 screen_set_window_property_iv(screen_window_t(winId), SCREEN_PROPERTY_IDLE_MODE,&idle_mode);
You will need to add following headers.
#include <bps/bps.h>
#include <bps/navigator.h>
#include <bps/screen.h>
And need to link screen library.
LIBS += -lscreen

Saturday, January 12, 2013

Signal Slot connection with C++ object and Cascades QML

In this blog post I shown how to achieve signal slot connection with QML and C++ object using Connections QML element.

In BB10 Cascades there are two ways to achieve the same.

First using java script connect method, like blow. Following code is from my previous post of Sprite animation.

This is preferred method as we are using only Cascades API.

Here timer is exported c++ object. By using timer.timeout.connect(renderNextFrame) statement. We are connecting the timeout signal of timer C++ object to renderNextFrame function.

import bb.cascades 1.0
...
        Container {
            id: sprite;
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Center
            layout: AbsoluteLayout {}  
            
            onCreationCompleted: {
                timer.timeout.connect(renderNextFrame);
                timer.start(200);
            }
                      
            function renderNextFrame() {
                ...
            }
       }
...

Second, Using Connections element like below.
import bb.cascades 1.0
import QtQuick 1.0
...
        Container {
            id: sprite;
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Center
            
            onCreationCompleted: {
                timer.start(200);
            }
            
            attachedObjects: [
                Connections {
                    target:timer;
                    onTimeout:{
                        sprite.renderNextFrame();
                    }
                }
            ]
            
            function renderNextFrame() {
             ...
            }
        }
...
To use Connections element, we need to import QtQuick components, as Connections element is part of QtQuick.

Once its imported, you will need to declare Connections element as part of attachedObjects. Then you can use it as usual in QML.

And just for reference, I create Timer object like below for above example.
    QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);

    QTimer* timer = new QTimer(app);
    qml->setContextProperty("timer",timer);