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.

Thursday, June 12, 2014

Using WorkerScript from QML

As you might know, I am contributing to Ubuntu Touch Calendar core application. Those application are pure QML application with no C++ backend or we aim to avoid it as much as possible. This is done to make sure application are portable across device.

For simple use-case you can just use javascript code as backend logic and it should work fine without any problem. but if you need to some intensive calculation in application then its better to move those calculation to WorkerScript.

While working on one calendar feature, Calculating layout for overlapping events, I also needed to use WorkerScript. But one major problem with WorkerScript is that, it does not have access to QML Engine. So you will not be able to send your Declarative object to worker script to offload intensive task to threads.

In calendar application I am getting Events declarative object from model and I needed to process events time to decide position and size of events in view.

As such I can not send those Events to WorkerScript, but to overcome that limitation, I created temporary Javascript object which has required data like start time, end time and id, which I can use to decide position, size and Id can be used to map temp object to original object from model and rest of logic then can be implemented in WorkerScript.

Code wise using WorkerScript is quite easy. Following code creates WorkerScript object and sends array of events to script.
    WorkerScript {
        id: eventLayoutHelper
        source: "EventLayoutHelper.js"

        onMessage: {
            //recevied process data, which we can use to layout events
            layoutEvents(messageObject.schedules);
        }
    }

    function createEvents() {

        var eventMap = {};
        var allSchs = [];

        var startDate = new Date().midnight();
        var endDate = new Date().endOfDay();
        var items = model.getItems(startDate,endDate);
        for(var i = 0; i < items.length; ++i) {
            var event = items[i];
            var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId };
            allSchs.push(schedule);
            eventMap[event.itemId] = event;
        }

        intern.eventMap = eventMap;
        //here we are asking worker script to process all the events
        eventLayoutHelper.sendMessage(allSchs);
    }


Following is script code, it will process these events and will send back location and size information back.
WorkerScript.onMessage = function(events) {

    //do some pre-processing like sorting and converting the data type more comfortable to algorithm
    var allSchs = processEvents(events);

    while( allSchs.length > 0) {
        var sch = allSchs.shift();

        //finds all schedules overlapping with current schedule and remove from original array
        var schs = findOverlappingSchedules(sch, allSchs);

        //insert original schedule first, so array remain sorted
        schs.unshift(sch);

        //send processed events back to GUI thread so we can render it
        WorkerScript.sendMessage({ 'schedules': schs});
    }
}

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.