Showing posts with label Z10. Show all posts
Showing posts with label Z10. 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.

Saturday, February 1, 2014

Handling GamePad events in BB10 Cascades App

I added GamePad support to my CrazyFlight game for BB10. You can see demo here.



In this post I will describe, how we can add GamePad support to BB10 cascades or Qt app.

First we should add use_gamepad permission to bar-descriptor.xml file. This is not necessary to enable gamepad support but its necessary for AppWorld to detect that your game supports GamePad, this helps in app discovery process.
 <permission>use_gamepad</permission>
We should also add libscreen dependency to our .pro file.
LIBS += -lscreen
As far as I know there is no Cascades API for handling GamePad events, we need to rely on native API to get GamePad events. I created a Helper class that handles Native API call back and send events to Cascades QML items.

Here is definition of my helper class's (GamePadObserver.h) header file.
#ifndef GAMEPADOBSERVER_H_
#define GAMEPADOBSERVER_H_

class GamePadObserver: public QObject {
 Q_OBJECT
 Q_ENUMS(GamePadButton)

 // Structure representing a game controller.
 struct GameController {
  // Static device info.
  screen_device_t handle;
  int type;
  int analogCount;
  int buttonCount;
  char id[64];

  // Current state.
  int buttons;
  int analog0[3];
  int analog1[3];

  // Text to display to the user about this controller.
  char deviceString[256];
 };

public:
 //Enum which we will use to send signal when GamePad event is detected
 enum GamePadButton{
   A_BUTTON=0,
   B_BUTTON,
   C_BUTTON,
   X_BUTTON,
   Y_BUTTON,
   Z_BUTTON,
   MENU1_BUTTON,
   MENU2_BUTTON,
   MENU3_BUTTON,
   MENU4_BUTTON,
   L1_BUTTON,
   L2_BUTTON,
   L3_BUTTON,
   R1_BUTTON,
   R2_BUTTON,
   R3_BUTTON,
   DPAD_UP_BUTTON,
   DPAD_DOWN_BUTTON,
   DPAD_LEFT_BUTTON,
   DPAD_RIGHT_BUTTON,
   NO_BUTTON
 };

public:
 GamePadObserver(QObject* parent = 0);
 virtual ~GamePadObserver();

 //Main event loop should send event to this handler if it can not handle event by itself
 //This handler will try to handle event if its related to GamePad
 void handleScreenEvent(bps_event_t *event);

signals:
        //Signals will be emitted when Gamepad events is detected
 void buttonReleased(int button);
 void buttonPressed(int button);

private:
 //Helper methods to discover the GamePad and device connection
 void discoverControllers();
 void initController(GameController* controller, int player);
 void loadController(GameController* controller);
 void handleDeviceConnection(screen_event_t screen_event);
       
 // Methods to handle gamepad events
 void handleGamePadInput(screen_event_t screen_event);
 QString gamePadButtonAsString(GamePadButton button);

private:
 screen_context_t _screen_ctx;
 GameController _controllers[2];
 bool _conneted;
 GamePadButton _lastButton;
};
#endif /* GAMEPADOBSERVER_H_ */
Now let's see source file.
In constructor we are creating screen context and then trying to discover if there is GamePad connected to device already.
#define SCREEN_API(x, y) rc = x; \
    if (rc)  printf("\n%s in %s: %d, %d", y, __FUNCTION__,__LINE__, errno)

GamePadObserver::GamePadObserver( QObject* parent)
: QObject(parent),_screen_ctx(0),_conneted(false)
{
 // Create a screen context that will be used to create an EGL surface to receive libscreen events.
 SCREEN_API(screen_create_context(&_screen_ctx, SCREEN_APPLICATION_CONTEXT), "create_context");
 discoverControllers();
}

void GamePadObserver::discoverControllers()
{
    // Get an array of all available devices.
    int deviceCount = 0;
    SCREEN_API(screen_get_context_property_iv(_screen_ctx, SCREEN_PROPERTY_DEVICE_COUNT, &deviceCount), "SCREEN_PROPERTY_DEVICE_COUNT");
    screen_device_t* devices = (screen_device_t*) calloc(deviceCount, sizeof(screen_device_t));
    SCREEN_API(screen_get_context_property_pv(_screen_ctx, SCREEN_PROPERTY_DEVICES, (void**)devices), "SCREEN_PROPERTY_DEVICES");

    // Scan the list for gamepad and joystick devices.
    int controllerIndex = 0;
    for (int i = 0; i < deviceCount; i++) {
        int type;
        SCREEN_API(screen_get_device_property_iv(devices[i], SCREEN_PROPERTY_TYPE, &type), "SCREEN_PROPERTY_TYPE");

        if ( !rc && (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)) {
            // Assign this device to control Player 1 or Player 2.
            GameController* controller = &_controllers[controllerIndex];
            controller->handle = devices[i];
            loadController(controller);

            // We'll just use the first compatible devices we find.
            controllerIndex++;
            if (controllerIndex == MAX_CONTROLLERS) {
                break;
            }
        }
    }
    free(devices);
}
On destructor we should release screen context.
GamePadObserver::~GamePadObserver()
{
 screen_destroy_context(_screen_ctx);
}
loadController setup our GameController structure, that we will use to store currently pressed buttons while handling the events.
void GamePadObserver::loadController(GameController* controller)
{
    // Query libscreen for information about this device.
    SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_TYPE, &controller->type), "SCREEN_PROPERTY_TYPE");
    SCREEN_API(screen_get_device_property_cv(controller->handle, SCREEN_PROPERTY_ID_STRING, sizeof(controller->id), controller->id), "SCREEN_PROPERTY_ID_STRING");
    SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_BUTTON_COUNT, &controller->buttonCount), "SCREEN_PROPERTY_BUTTON_COUNT");

    // Check for the existence of analog sticks.
    if (!screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG0, controller->analog0)) {
     ++controller->analogCount;
    }

    if (!screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG1, controller->analog1)) {
     ++controller->analogCount;
    }

    if (controller->type == SCREEN_EVENT_GAMEPAD) {
        sprintf( controller->deviceString, "Gamepad device ID: %s", controller->id);
        qDebug() << "Gamepad device ID" <<  controller->id;
    } else {
        sprintf( controller->deviceString, "Joystick device: %s", controller->id);
        qDebug() << "Joystick device ID" <<  controller->id;
    }
}
handleScreenEvent function should be called from main event loop when event is related to screen domain. This function check if event type is GamePad device connection or its GamePad button event and handle it accordingly.
void GamePadObserver::handleScreenEvent(bps_event_t *event)
{
    int eventType;

    screen_event_t screen_event = screen_event_get_event(event);
    screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType);

    switch (eventType) {
        case SCREEN_EVENT_GAMEPAD:
        case SCREEN_EVENT_JOYSTICK:
        {
         handleGamePadInput(screen_event);
         break;
        }
        case SCREEN_EVENT_DEVICE:
        {
         // A device was attached or removed.
         handleDeviceConnection(screen_event);
         break;
        }
    }
}
handleDeviceConnection handles GamePad device connection. If it detects new connection then it loads new device, in case of device disconnection it remove device.
void GamePadObserver::handleDeviceConnection(screen_event_t screen_event)
{
    // A device was attached or removed.
    screen_device_t device;
    int attached;
    int type;

    SCREEN_API(screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_DEVICE, (void**)&device), "SCREEN_PROPERTY_DEVICE");
    SCREEN_API(screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ATTACHED, &attached), "SCREEN_PROPERTY_ATTACHED");

    if ( attached ) {
        SCREEN_API(screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type), "SCREEN_PROPERTY_TYPE");
    }

    int i;
    if (attached && (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)) {
        for (i = 0; i < MAX_CONTROLLERS; ++i) {
            if (!_controllers[i].handle) {
                _controllers[i].handle = device;
                loadController(&_controllers[i]);
                break;
            }
        }
    } else {
        for (i = 0; i < MAX_CONTROLLERS; ++i) {
            if (device == _controllers[i].handle) {
                initController(&_controllers[i], i);
                break;
            }
        }
    }
}

void GamePadObserver::initController(GameController* controller, int player)
{
    // Initialize controller values.
    controller->handle = 0;
    controller->type = 0;
    controller->analogCount = 0;
    controller->buttonCount = 0;
    controller->buttons = 0;
    controller->analog0[0] = controller->analog0[1] = controller->analog0[2] = 0;
    controller->analog1[0] = controller->analog1[1] = controller->analog1[2] = 0;
    sprintf(controller->deviceString, "Player %d: No device detected.", player + 1);
}
handleGamePadInput function that handles GamePad events.
void GamePadObserver::handleGamePadInput(screen_event_t /*screen_event*/)
{
    int i;
    for (i = 0; i < MAX_CONTROLLERS; i++) {
        GameController* controller = &_controllers[i];

        if ( controller->handle ) {
            GamePadButton gamePadButton = NO_BUTTON;

            // Get the current state of a gamepad device.
            SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_BUTTONS, &controller->buttons), "SCREEN_PROPERTY_BUTTONS");

            if (controller->analogCount > 0) {
             SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG0, controller->analog0), "SCREEN_PROPERTY_ANALOG0");
            }

            if (controller->analogCount == 2) {
             SCREEN_API(screen_get_device_property_iv(controller->handle, SCREEN_PROPERTY_ANALOG1, controller->analog1), "SCREEN_PROPERTY_ANALOG1");
            }

            for(int i = A_BUTTON ; i < NO_BUTTON ; ++i) {
             if( controller->buttons & (1 << i) ) {
              gamePadButton = (GamePadButton)(i);
              break;
             }
            }

            if( gamePadButton == NO_BUTTON ) {
             emit buttonReleased( _lastButton );
             _lastButton = NO_BUTTON;
            }
            else if( _lastButton != gamePadButton ) {
             emit buttonReleased(_lastButton);
             emit buttonPressed(gamePadButton);
             _lastButton = gamePadButton;
            }
        }
    }
}
Now we have all necessary implementation to handle the GamePad connection and GamePad button events. But we should pass events related to GamePad to our helper class when we receive it in our main event handler. To do that, In our main function we should register main bps event loop or filter as below and can use GamePadObserver as described.
#include "GamePadObserver.h"

static QAbstractEventDispatcher::EventFilter previousEventFilter = 0;
static GamePadObserver _gamePadObserver;

static bool bpsEventFilter(void *message)
{
    bps_event_t * const event = static_cast(message);

    if( event && bps_event_get_domain(event) == screen_get_domain()) {
     _gamePadObserver.handleScreenEvent(event);
    }

    if (previousEventFilter)
        return previousEventFilter(message);
    else
        return false;
}

int main(int argc, char **argv)
{
    // Register GamePadObserver so we can use it in QML code
    qmlRegisterUncreatableType("GamePadObserver", 1, 0,"GamePadObserver", "");

    QApplication app(argc, argv);

    previousEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(bpsEventFilter);

    QScopedPointer view(new QDeclarativeView());
    view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    view->setResizeMode( QDeclarativeView::SizeRootObjectToView );

    QDeclarativeContext *ctxt = view->rootContext();
    view->setSource(QUrl("app/native/assets/main.qml"));

    ctxt->setContextProperty("GamePad",&_gamePadObserver);
    view->showFullScreen();

    return app.exec();
}
And finally, out GamePadObserver class is ready to deliver GamePad events. To handle GamePad events in QML you can use GamePadObaserver class as below.
        
        Connections{
            target: GamePad
            onButtonPressed: {
                                
                if(button == GamePadObserver.DPAD_UP_BUTTON
                || button == GamePadObserver.DPAD_DOWN_BUTTON ){
                    handleUpButton();
                }else if(button ==  GamePadObserver.DPAD_LEFT_BUTTON 
                || button ==  GamePadObserver.DPAD_RIGHT_BUTTON) {
                    handleLeftButton();
                }          
                else if(button == GamePadObserver.MENU1_BUTTON                 
                || button == GamePadObserver.MENU2_BUTTON
                || button == GamePadObserver.X_BUTTON
                || button == GamePadObserver.Y_BUTTON 
                || button == GamePadObserver.A_BUTTON 
                || button == GamePadObserver.B_BUTTON ) {
                    handleButtonSelection();
                } 
            }        
        }
So, I hope this will help to utilize GamePad events in your games.

Sunday, December 1, 2013

BlackBerry gamepad offer

As you might know BB10 support gamepad, that mean you can connect gamepad to Device via Bluetooth and play games using it. And to promote gamepad enabled game, Blackberry also announced an offer. You can get more details about offer here.



For the same offer I got mine GamePad recently. I also enabled my CrazyFlight game to support GamePad. Here is demo.

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.

Saturday, August 10, 2013

Using VibrationController API wit BB10 Cascades QML

I tried to add vibration support to my BB10 application Counter. Application is simple, which just increment the count when you press add button and decrements count on minus button. Simple but useful, I use it quite often to count the stuffs.

But you need to see at screen to see if it button is really pressed, so I thought to add vibration support, so I know button is pressed even without looking at screen. This is first time I am adding Vibration support to any of BB10 application. So I thought to share code.

Code is much simple, only trick is that you need to register VibrationController c++ class to QML system.

Here is how it goes.

First we need to link device lib which has VibrationController API. Add following line to your .pro file and you are good to go.

LIBS += -lbbdevice

Now in main.cpp you need to add proper header to locate VibrarionController API and the register it to QML registry.
#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

#include <bb/device/VibrationController>

using namespace bb::cascades;
using namespace bb::device;

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

    qmlRegisterType<vibrationcontroller>
        ("bb.vibrationController", 1, 0, "VibrationController");

    new Counter(&app);

    return Application::exec();
}

Now we are ready to use VibrarionController with QML code. First you need to import VibraionController QML by import statement. Then we are creating it by defining it in attachedObjects. Once this is done, we can use it's start API to vibrate phone with certain intensity for certain durarion.

import bb.cascades 1.0
import bb.vibrationController 1.0

Page {
    id: page
    Container {
        id: root
        layout: DockLayout {}
        preferredHeight: 1280
        preferredWidth: 768
        touchPropagationMode: TouchPropagationMode.Full;
        
        attachedObjects: [
            VibrationController {
                id: vib
            }
        ]

        function vibrate() {
            //first parameter intensity, second parameter duration for vibration
            vib.start(80, 100);
        }

        Button{
            verticalAlignment: VerticalAlignment.Center
            horizontalAlignment: HorizontalAlignment.Center 
            onClicked: {
                root.vibrate();
            }
        }
    }
}

Thant's all, hope this will be helpful.

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"
            }
        }
        ...
    ]
    ...
}


Friday, May 3, 2013

Un-boxing BlackBerry Limited Edition Red Z10

Last week I received Limited Edition Red Z10 device. I was waiting for it for quite long time and finally received it last week.

Following are Un-boxing of the device. The box is quite different than box in which Dev Alpha device came.



Box came with a Device, an USB cable, Power adapters compatible with various countries' standard and a headphone.


Behind the battery there is slot for micro SIM and There is also slot for micro SD card, you don't need to remove battery to change micro SD card.


It has hardware button to control volume and play pause key in middle and on other side there are slots for micro USB and micro HDMI port.




Its take some time for complete boot up, once it boots up and initial set-up is complete it offers a introductory tutorial for its gestures based interface.