Showing posts with label audiobookreader. Show all posts
Showing posts with label audiobookreader. Show all posts

Saturday, June 28, 2014

Resolving Folder reading issue from SD Card in BB10

There was a tricky bug in Audiobook Reader BB10 version for quite a some time which I was trying to resolve. Issue was that, for some folder in SD card, I was not able to browse it using QDir Qt API or Posix API. Though API was showing the folder, but when I try to find out folder's content, it reports folder as empty.

This was causing serious usability issue with Audiobook Reader application, as user were not able to add book from SD card. I debugged it long and tried to use various approach to resolve the issue but could not find any satisfactory solution.

I read it somewhere it causes issue if SD card was formatted with PC software and issue is resolved if you format SD card with BB10 setting. But this also looks hassle to user.

Finally, I decided to see how directory structure looks like and what is folders permission using SSH to device. When I used linux command like ls and cd, I was able to see folder's content fine. And I decide to use those command to resolve directory browsing issue.

Following is my code, if someone else is also facing similar issue. First I am escaping special character in filepath, else linux command will not work. Then I am creating "ls" command with sorting and with output which list directory content in absolute file path. Once we have proper command I am using popen API to run command and then using IO api to read command's output.
QStringList BookListModel::getFileList(const QString& dir) {

 QString filePath(dir);
 filePath.replace(" ","\\ ");
 filePath.replace("'","\\'");
 filePath.replace("`","\\`");
 filePath.replace("!","\\!");
 filePath.replace("$","\\$");
 filePath.replace("&","\\&");
 filePath.replace("*","\\*");
 filePath.replace("\"","\\\"");
 filePath.replace("(","\\(");
 filePath.replace(")","\\)");
 filePath.replace("[","\\[");
 filePath.replace("]","\\]");

 qDebug() << filePath;
 QString cmd = QString("ls -1 %1/*").arg(filePath);
 qDebug() << cmd;

 QStringList files;
 char path[1035];
 FILE* fp = popen(cmd.toStdString().c_str(), "r");
 if (fp == NULL) {
  qDebug() << "Failed to run command";
  return files;
 }

 while (fgets(path, sizeof(path)-1, fp) != NULL) {
  QString file = QString(path).trimmed();
  QFileInfo fileInfo(file);
  if (fileInfo.isFile() && FileModel::isSupportedMedia(fileInfo.fileName()) ){
   files << file;
  }
 }

 qDebug() << files;

 pclose(fp);
}
That's all, hope it will be helpful to someone.

Thursday, June 26, 2014

Audiobook Reader for BB10 now got Built for Blackberry status

Recently I upgraded my Audiobook Reader application pro version for BB10.

This version contains many UI related changes and now it is more aligned to Blackberry design guideline and now it is also awarded Built for Blackberry status.

Apart form UI related changes, this version contains two major changes.

Now it Book auto pauses in case it detect phone call activity.

And there was a very critical bug in application which prevents book to be added from SD card. Though issue was related to BB10 OS, I found one work around and now adding books from SD card works as expected.

Full list of features in current version is as below.
  • Allows adding single file or whole folder with book audio data
  • Supports adding custom bookmark
  • Supports browsing mp3 chapter files and play selected mp3 chapter file
  • Supports chapter skip
  • Supports 2 min, 1 min, 30 sec skip option
  • Supports auto pause on call
  • Supports Play/Pause using HW buttons
  • Supports custom cover art download
  • Advance file browser
  • Supports sleep timer
  • Supports shuffle
  • and some more...
Following is demo for current version.


Hope you will like the updates.

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.

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, January 4, 2013

Creating custom listview with BB10 Cascades API

As I indicated in my previous post, I am trying to port my Audiobook Reader application to BB10. I needed to make few changes to use BB10 Cascades API.

Previously I posted about how to create custom dialog box using Cascades QML API.

In this post I will talk about how to create custom list view.

For most case StandardListItem should be enough for use. Its looks like below.


But I wanted to learn creating custom component and also wanted to add some dynamic behavior to my list item.

I wanted to add Delete button on my list item. By default delete button is invisible, its get visible on long press event. It you tap on button the delete event is fired, touching anywhere else in item makes it invisible again. Also touching List item in normal state fire the selection event.

I was not sure how I can achieve this behavior with StandardListItem and I create my custom list item like below.

Normal condition, it looks like below.

On long press event on list item, it shows the delete button.

Now lets see how I created list item to satisfy above behavior.

First lets create list view, which uses the custom list item named ListDelegate and also have necessary function (selected and delete )to handle events from list item.
            ListView {
                id: listView
                dataModel: listModel.model
                listItemComponents: [
                    ListDelegate {
                    }
                ]

                //called by listdelegate manually
                function selected(index) {
                    console.log("###### Index selected:" + index);
                }

                //called by listdelegate manually
                function delete(index) {
                    console.log("###### Delete index:" + index);
                }
            }
Following is code for ListDelete component. I removed some code to keep code short and relevant.

First we need to set touchPropogationMode to Full on root container, so the all sub component get chance to handle their event. Then I added to sub container to main container to hold actual list item content and one to hold delete button and added gesture handler to both of them.

Unfortunately I was not able to find easy way to emit signal from ListItem to ListView, so I ended up calling ListView function manually to indicate select and delete event. By calling list view function
 root.ListItem.view.selected() and root.ListItem.view.delete()
.
To access current list items's index, we can use root.ListItem.indexPath property.
ListItemComponent {  
    
    Container{
        id: root
        layout: DockLayout {}
        touchPropagationMode: TouchPropagationMode.Full;
          
        Container{
            id: itemRoot
            layout: DockLayout {}
            preferredWidth: 768; preferredHeight: 120  
            
            gestureHandlers:[
                LongPressHandler {
                    onLongPressed: {
                        //make delete button visible
                        deleteBtn.opacity = 1;
                        itemRoot.opacity = 0.3 
                    }
                },
                TapHandler {
                    onTapped: {   
                        if( deleteBtn.opacity == 1 ) {   
                            //make delete button hide                
                            deleteBtn.opacity = 0;
                            itemRoot.opacity = 1; 
                        } else {
                            //fire selected event
                            root.ListItem.view.selected(root.ListItem.indexPath); 
                        }
                    }
                }
            ]  
                        
            Divider {
                verticalAlignment: VerticalAlignment.Bottom
                horizontalAlignment: HorizontalAlignment.Center  
            }
            
            Container {
                
                verticalAlignment: VerticalAlignment.Center
                layout:StackLayout {
                    orientation: LayoutOrientation.LeftToRight;                
                }
                                
                ImageView{
                    id: image
                    preferredHeight: 110; preferredWidth: 110
                    imageSource: ListItemData.image;
                    verticalAlignment: VerticalAlignment.Center
                }
                
                Container {             
                    layout:StackLayout {}
                    bottomPadding: 10
                    Label{
                        text: ListItemData.title;            
                    }
                    
                    Label{
                        text: ListItemData.subTitle;                            
                    } 
                }
            }
        
        } 
        
        Container {
            id: deleteBtn
            
            opacity: 0; preferredWidth: 150; preferredHeight: 100
            verticalAlignment: VerticalAlignment.Center
            horizontalAlignment: HorizontalAlignment.Right  
          
            ImageView{
                imageSource: "delete.png";
                verticalAlignment: VerticalAlignment.Center
                horizontalAlignment: HorizontalAlignment.Center
            } 
            
            gestureHandlers:[
                TapHandler {
                    onTapped: {                              
                        if( deleteBtn.opacity == 1 ) {                    
                            deleteBtn.opacity = 0;
                            itemRoot.opacity = 1;
                            root.ListItem.view.delete(root.ListItem.indexPath); 
                        } 
                    }
                }
            ]           
        }
    }
}
Thought it seems lot of code, its quite easy to create custom list item. Now lets see how we can provide data to ListView. For my case I created Data model in C++ and exported it to QML. You can export c++ Data model to QML using setContextProperty. listModel is instance of class derived from QObject.

  QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app);
  ListModel listModel;
 qml->setContextProperty("listModel", &listModel);
We are setting model to list view like dataModel: listModel.model, it means that listModel has property called model. Following is code from the same.

class ListModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bb::cascades::DataModel* model READ model);

public:
    void loadData();
    bb::cascades::DataModel* model(return mListModel;);
private:

    QMapListDataModel* mListModel;
};
And I am populating the ListModel like below. I am packaging my List ItemData inside QVariantMap, so that ListItem can easily access the data.

void ListModel::loadData()
{
    mListModel->clear();

    foreach( ... )
    {
  QVariantMap map;
 map["image"] = "image path";
 map["title"] = "title";
 map["subtitle"] = "subtitle";
 (*mListModel) << map;
   }
}
If you see ListItem's code, We can access the list item data like below.
                ImageView{
                    imageSource: ListItemData.image;
                }
                
                Container {             
                    Label{
                        text: ListItemData.title;
                    }            
                    Label{
                        text: ListItemData.subTitle;                            
                    } 
                }
Now finally its done. I hope it will be helpful to someone.

Saturday, February 11, 2012

Qml FileDialog for symbian anna and belle

I recently ported my Audiobook Reader app for symbian using symbian components. As there is no common QML based file dialog, I need to create my own.

I uploaded my implementation to Gitorious repository here. Currently this code works for only symbian as its used symbian components, but it can be easily ported to work on meego as well.

In this post I will try to explain how this file dialog can be used. Repository also include full working sample.

Lets start with main.cpp file. We need to create instance of FileModel and share it with QML and rest of code is to launch our mainl Qml file.

int main( int argc, char* argv[] ) {
    QApplication app(argc,argv);

    QDeclarativeView view;

    FileModel fileModel;
    QDeclarativeContext *ctxt = view.rootContext();
    ctxt->setContextProperty("fileModel", &fileModel);

    view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    view.setResizeMode( QDeclarativeView::SizeRootObjectToView );
    view.setSource(QUrl("qrc:/main.qml"));
    view.show();

    return app.exec();
}
Now QML file which needs to use FileDialog, following code can be used. Here in openFile function, if you want to select Folder/Directory then dirMode should be set to true, if you want to select File than set it to false.
    Page{
        id:page

        function openFile( dirMode ) {
            var component = Qt.createComponent("FileDialog.qml");
            var dialog = component.createObject(page);
            if( dialog !== null ) {
                if( dirMode) {
                    dialog.dirMode = true;
                }
                dialog.fileSelected.connect(fileSelected);
                dialog.directorySelected.connect(directorySelected);
                dialog.open();
            }
        }

        function fileSelected( filePath ) {
            console.debug("File selected:" + filePath);
        }

        function directorySelected( dirPath ) {
            console.debug("Folder selected:" + dirPath);
        }

        tools: ToolBarLayout {
            ToolButton {
                iconSource: "toolbar-back";
                onClicked: {
                    Qt.quit();
                }
            }
            ToolButton {
                text: "File Selection"
                onClicked: {
                    page.openFile(false);
                }
            }
            ToolButton {
                text: "Folder Selection"
                onClicked: {
                    page.openFile(true);
                }
            }
        }
    }
Following are few snaps of FileDialog component running from my Audiobook reader application.

Here is demo of file dialog.

Audiobook Reader for symbian Belle and Anna



I recently ported my Audiobook Reader application to Symbian (supports Anna and Belle).

I struggled a lot while porting this to symbian. For example phonon has some problem with Qt 4.7 on symbian and I wested almost one day to find out solution. If you are also facing similar problem. Then visit this link and this.

Capturing volume key was another problem. I required to use native symbian remote control api to resolve this issue. Visit this link for more info.

And as I use ubuntu machine as development machine, debugging and compilation was another problem.

But finally I was able to port it successfully and now its available on Nokia Store.
Please download it from below.
- Fixed web browser: http://store.ovi.com/content/120217
- Nokia mobile browser: http://store.ovi.mobi/content/120217
Following is demo. Hope you will like it.


Saturday, December 24, 2011

Creating custom list view and adaptor in Android

I recently got new toy for myself a imuz TX72 7" android tablet to satisfy my book reading hobby.

I will write a seperate post for tablet, but as I got this tablet, I thought to port my Audiobook reader app to Android so I can here my audiobook on this tablet as well.

As I started to port, First thing I need to learn is how to create view and specially list view. I required to create custom Listview so I can show thumnail and other audiobook details.

So to start, First you need to create is activity. I also wanted to put button on main activity so I did not derived my activity from ListActivity but derived it from Activity. For new people to android like me, Activity is something that we see on screen , a view.

My main activity goes like below.

    
public class AudiobookReaderActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       
       setContentView(R.layout.main);
       
       ListView list = (ListView) findViewById(R.id.BookList);
       list.setAdapter( new BookListAdaptor(this));  
    }  
}

Now we have list view, but how invidiual list item looks is decided by item's xml file, which provide layout and views details to be drawn as item.

Following is my list item, with one image and two text view.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=
       "http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
    
    <ImageView
        android:id="@+id/AlbumArt"        
      android:layout_width="55dip"
  android:layout_height="55dip"/>
    
 <LinearLayout xmlns:android=
              "http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical" >    
 
  <TextView xmlns:android=
                "http://schemas.android.com/apk/res/android"
      android:id="@+id/BookName"     
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textStyle="bold" 
      android:padding="3dp"
      android:textColor="#FFFF00" 
      android:textSize="16sp" >
  </TextView>
  
  <TextView xmlns:android=
                      "http://schemas.android.com/apk/res/android"
      android:id="@+id/TrackName"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:padding="3dp" >
  </TextView>
  
 </LinearLayout>
</LinearLayout>

Now look is set for list view, now we need to provide data to list, by creating adaptor class. Adaptor class act as model and provide data to list view.

Following is my custom list adaptor class to support Book data structure. Here getView method is importat as it uses our xml file to create liste item and decide our list view's look.

import java.util.ArrayList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.content.Context;

public class BookListAdaptor extends BaseAdapter {
 
 public BookListAdaptor( Context context) {
  mContext = context;
  
  //load book list from file or database
  mBookList = new ArrayList();

  mBookList.add(new AudioBook("Harry Potter"));
  mBookList.add(new AudioBook("The Hobbit"));
 }

 @Override
 public int getCount() { 
  return mBookList.size();
 }

 @Override
 public Object getItem(int position) {
  return mBookList.get(position);
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {  
  
        if (convertView == null) {
   LayoutInflater layoutInflator = 
                             LayoutInflater.from(mContext);
   convertView = layoutInflator.
                             inflate(R.layout.list_item, null);
        }
     
        TextView bookName = (TextView) convertView.
                                      findViewById(R.id.BookName);
        TextView trackName = (TextView)convertView.
                                      findViewById(R.id.TrackName);
        ImageView imageView = (ImageView)convertView.
                                      findViewById(R.id.AlbumArt);
  
  
  AudioBook book = mBookList.get(position);
  bookName.setText(book.bookName());
  trackName.setText(book.bookName());
  imageView.setBackgroundResource(android.R.drawable.btn_plus);
  
  return convertView;
  
 }

    private Context mContext;
    private ArrayList mBookList;
}

Book class looks something like following.

package com.niku;

import java.util.ArrayList;


public class AudioBook {
 
    public AudioBook( String aBookName) {
 mBookName = aBookName;
    }
 
    public String bookName() 
    {
        return mBookName;
    }

    private String mBookName;
}  
That's all. Final list view will looks like followign snap. Thanks fo reading my blog.






Saturday, December 17, 2011

Handling bluetooth headphone event with QmKeys in Harmattan

Currently I am working on upgrade of Audiobook reader application. As part of upgrade I am planing to support bluthooth headset support. In this upgrade I am planning to support only play/pause event.

Harmattan has QmSystem Qt library that provide events for various system activity. I used QmKeys API from QmSystem to capture and handle bluetooth headset event.

This is first time I am using QmSystem Library so I thought to put whatever I learned in blogpost in hope that it might help someone.

Following is my code to capture bluetooth headset event. It can be used to capture other system key event as well like Powekey, Volume key, Camera , keyboard slider and others.

First add qmsystem2 to CONFIG Qt project file.
CONFIG += qmsystem2
And also you need to include qmkeys.h.
#include <qmkeys.h> 
. Then need to create instace of QmKeys and connect its keyEvent SIGNAL to our SLOT.
mQmkeys = new MeeGo::QmKeys(this);

connect(mQmkeys, SIGNAL(keyEvent(MeeGo::QmKeys::Key, MeeGo::QmKeys::State)),
       this, SLOT(keyEvent(MeeGo::QmKeys::Key, MeeGo::QmKeys::State)));
Now our slot keyEvent will get called when ever there is key event. I am instrested only in play/pause event so following code list only those event.
void HeadsetKeyListener::keyEvent(MeeGo::QmKeys::Key key, 
    MeeGo::QmKeys::State state) {

    if( state != MeeGo::QmKeys::KeyDown ) {
        return;
    }

    switch( key) {
    case MeeGo::QmKeys::Play:
        emit playPauseClicked();
        break;
    case MeeGo::QmKeys::Pause:
        emit playPauseClicked();
        break;
    }
}
Well this is it, its vary simple to use.

Friday, November 4, 2011

Taking backup of Audiobook Reader on N9/N950

Recently I upgraded my N950 to PR 1.1 and realized that its camera is not working any longer.

After googling a bit I found that issue can be solved by flashing "Linux_OCF_39-5_RM680-RM680-OEM1.bin" image. But that means loosing all data, you can use backup and restore app but unfortunately my Audiobook Reader currently dose not support backup restore.  I need to  implement this feature in next release.

But if you need this support currently like me, then you can try following work around.

Audiobook reader setting file is store at "/home/user/.config/kunal/audiobookreader.conf". You can copy this file to your pc using ssh and then restore it at same place when you need.

I am not sure but same trick can be used for N900 as well.

Hope this helps.

Saturday, October 15, 2011

Audiobook Reader now available for N9 / N950 ( Harmattan ) on Nokia Store

I recently updated my Audiobook reader application for N9 Harmattan platform. It is also supported on N950. You can download it from Nokia Store from here.





For those who dont know about this application, Audiobook reader is simple Audiobook player which lets you add audiobook with single large audio file or folder with audio files for each chapter. It automatically bookmark the last position of audiobook and resume playback from that position. It also allow to add custom bookmark and playback from certain chapter from Audiobook.

For N9, I completely reimplemented UI layer to give native look and feel. For this version I am also showing book cover image, downloaded from AWS to give more feel of book.




Same as N900 version, this version also support custom bookmark support and chapter selection support. Volume can be changed by using Hardware key.


Saturday, September 10, 2011

Accessing Amazon Web service from Qt

Currently I am trying to port my Audiobook reader application to Harmattan. I decided to improve its UI and as a change I decided to show book cover page in background.

Initially I thought to use taglib to fetch album art from audio file, but then I decided to download cover art from internet as some audio file might not contain album art information.

To download cover art from internet, I decided to use Amazon web service. Initially I struggled to successfully communicate with AWS.

AWS required to send signature with every request you send and there is certain procedure to create that signature. Creating signature with Qt is quite easy, but still I was facing problem with retrieving data from AWS because I did not read QUrl documentation carefully.

While generating QUrl, it expect human redable URL string with no percentage encoding. But I was trying to generate QUrl from already encoded parameters.

And as a result I was getting such error message.
"Value 2011-09-11T05%3A18%3A42 for parameter Timestamp is invalid. Reason: Must be in ISO8601 format".

Double encoding was causing above problem.

I am posting my code here, in hope that will be helpful to someone.

First all to communicate with AWS you required to have AWS access key id and Secret Access key. You can get it from here.

Here is AWS doc that explain how to generate signature. And here utility which you can use to verify if you generated correct signature.

Now we are ready for implementation. Following is my code.

Here in below code, I am trying to create AWS request to get all image for mentioned book. In request I am also specifying SignatureMetod to HmacSHA1, by default AWS use HmacSHA256 signature method for comparison.

const char AWS_KEY[] = "YOU ACCESS KEY";
const char AWS_PASS[] = "YOUR SECRET KEY";
const char END_POINT[] = "http://ecs.amazonaws.com/onca/xml";

AwsAlbumArtHelper::AwsAlbumArtHelper(QObject *parent) :
    QObject(parent)
{
    _netManager = new QNetworkAccessManager(this);    
}

QByteArray AwsAlbumArtHelper::getTimeStamp()
{
    QDateTime dateTime = QDateTime::currentDateTimeUtc();
    return dateTime.toString(Qt::ISODate).toUtf8();
}

void AwsAlbumArtHelper::downloadAlbumArt(const QString& bookname)
{
    _bookName = bookname;
    QMap< QString,QString > queryItems;
    queryItems["AWSAccessKeyId"] = AWS_KEY;
    queryItems["ResponseGroup"] = "Images";
    queryItems["Keywords"] = QUrl::toPercentEncoding(bookname);
    queryItems["Operation"] = "ItemSearch";
    queryItems["SearchIndex"] = "Books";
    queryItems["Service"] = "AWSECommerceService";
    queryItems["SignatureMethod"] = "HmacSHA1";
    queryItems["Timestamp"] = QUrl::toPercentEncoding(getTimeStamp());
    queryItems["Signature"] = createSignature(queryItems);

    QUrl downloadUrl = createUrl(queryItems);
    QNetworkRequest request(downloadUrl);

    disconnect(_netManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(imageDownloaded(QNetworkReply*)));
    connect(_netManager,SIGNAL(finished(QNetworkReply*)),this,SLOT(xmlDownloaded(QNetworkReply*)));
    _netManager->get(request);
}
Following code create signature from query parameters. I used code pasted here for signing string with hmacSha1.
QByteArray AwsAlbumArtHelper::createSignature(const QMap< QString,QString >& queryItems)
{
    QUrl url(END_POINT);
    QString stringToSign = "GET\n";
    stringToSign.append(url.host() + "\n");
    stringToSign.append(url.path() + "\n");

    QList<qstring> keys = queryItems.keys();
    for( int i=0; i < keys.count() ; ++i ) {
        stringToSign.append(keys[i]+"="+queryItems[keys[i]]);
        if( i != keys.count() -1  ) {
            stringToSign.append("&");
        }
    }
    QString signature = hmacSha1(AWS_PASS,stringToSign.toUtf8());
    return QUrl::toPercentEncoding(signature);
}

QUrl AwsAlbumArtHelper::createUrl( const QMap< QString,QString >& queryItems )
{
    QUrl url(END_POINT);
    QMapIterator<QString, QString> it(queryItems);
    while (it.hasNext()) {            
        it.next();
        //everything is already URL encoded
        url.addEncodedQueryItem(it.key().toUtf8(), it.value().toUtf8());
    }
    return url;
}
Now we are ready to submit request using downloadAlbumArt method with required book name and AWS should return xml data with various image link. We need to parse xml and use mentioned link to download image.

Saturday, April 30, 2011

Audiobook Reader for N900 using QML and Phonon

Now days I am trying to learn Qt Quick (QML) and as exercise I decided to upgraded my Audiobook reader application to use QML.

Outcome is pretty good I think, GUI looks much more impressive than earlier though feature set remain same as before.

Currently new version is waiting to be approved by Ovi Store QA. As soon as its cleared you can download it for free from Ovi Store. But its only available for N900 users.

Following is demo video on Qt SDK simulator, Please note that File Dialog will be different on actual hardware.



Following are screen capture from application.



Sunday, December 5, 2010

Audiobook Reader for N900 usign Qt and Phonon

So after long time I found enough time to complete my Audiobook Reader application. This application was in extra-devel repository for long time but there were few problem with it, now finally I worked on those bugs and I am satisfied enough to share information about it.

Now days almost all novels are available in audio format and I am huge fan of those audio book. Previously I was using n900's music player to hear those audio books. But music player dose not preserve last playing position and this create problem when phone restart.

So I created this Audiobook Reader application.Key features are listed below.
  • You can add multiple audiobook.
  • It support both individual audio file which contain whole book, or can add folder which contain books audio file.
  • You can bookmark position in audiobook and can resume from same position.
  • It remember bookmark for all audiobook.This mean you can play multiple audiobook simultaneously.
Following are snaps from  application.


Above snaps is of Book list view. This view list currently added books, 
  • Add Audio button is used to add single audio file which contain whole book content.
  • Add Folder button is used to add folder, which contain multiple audio file for book. Note that you need to select folder which contain audio files, not the individual files.
  • Pokemon icon is used to start playing individual audio book.
  • Cross mark icon is used to remove this book from book list.





In above snaps are from  Reading view.This view list currently played book's title, track name and artist's name.
  • > button is used to play book
  • || button is used to pause book
  • x button is used to stop book, it will put reading position to beginning.
  • <)) button is used to increase/ decrease volume
Currently you can download this application to your n900 from extra-devel repository.