DEVELOPERS BLOG

Cloudy Pics Part 4: Cards and Enterprise

Cloud Pics Part 4_1

Photo by Adam Stanley

We’re just about done with the current version of CloudyPics showcase app. So far we’ve used it introduce the Showcase app concept, discussed how it helps you manage the camera roll, and also how it helps save and restore camera settings. There are two more topics that I will discuss in today’s post before I leave you for a while as I work on the next version. These two topics are invocation and enterprise use.

Invocation

CloudyPics is a great example of an app that can be invoked. You can invoke it the same way as the built in Camera app (though it only does photos). For example:

       InvokeRequest invokeRequest;
       invokeRequest.setTarget("bb.community.cloudypics.card");
       invokeRequest.setAction("bb.action.CAPTURE");
       invokeRequest.setMimeType("image/jpeg");
       InvokeTargetReply *invokeReply = _invokeManager->invoke(invokeRequest);

As you can see, the only difference between this and the Camera app is the target. We’ll talk more about that near the end.

Setting invocation up in CloudyPics is pretty straightforward. First, I added two invoke targets (one for a Card, and one for an App) in the bar-descriptor.xml file:

       <invoke-target id="bb.community.cloudypics.app">
              <invoke-target-type>application</invoke-target-type>
              <filter>
                     <action>bb.action.CAPTURE</action>
                     <mime-type>image/jpeg</mime-type>
              </filter>
       </invoke-target>

       <invoke-target id="bb.community.cloudypics.card">
              <invoke-target-type>card.composer</invoke-target-type>
              <filter>
                     <action>bb.action.CAPTURE</action>
                     <mime-type>image/jpeg</mime-type>
              </filter>
       </invoke-target>

Cloud Pics Part 4_2

Photo by Luca Filigheddu

To handle the invocation process itself, I created a class called CardManager that is used when the app is launched. It sets things up so that the card closes when a picture is taken, and camera resources are managed if the card is pooled.

To get the card to close at the right time, we connect the camera’s photoSaved signal to our triggerCardDone slot, like this:

void CardManager::setCamera(Camera *camera) {
       _camera = camera;
       connect(camera, SIGNAL(photoSaved (const QString&, quint64)), this,
                     SLOT(triggerCardDone(const QString&)));
}

That also gives us a reference to the Camera object, which we’ll need later.

In triggerCardDone, we build the CardDoneMessage and give it to the calling app:

void CardManager::triggerCardDone(const QString& data) {

       // Send a signal containing the image path
       if (_invoked) {
              qDebug() << "+++++++++ Sending done message" << endl;
              QFileInfo fileInfo (data);

              CardDoneMessage message;
              message.setData(fileInfo.canonicalFilePath());
              message.setDataType("text/plain");
              message.setReason("Photo captured");
              _camera->stopViewfinder();
              _camera->close();
              _invokeManager->sendCardDone(message);
       }
}

Easy-peasy.

Cloud Pics Part 4_3

Photo by Larry McDonough

If the system thinks it may need your card again soon, it can “pool” it. Basically this means it the card is kept in memory and not really closed. If you are pooled, you want to let go of any resources with limited availability and acquire them again the next time the card is invoked. In our case, we need to close the camera when we’re pooled, and open it again if we’re invoked later:

void CardManager::onCardPooled(const bb::system::CardDoneMessage&) {
       _pooled=true;
       _camera->stopViewfinder();
       _camera->close();
}
void CardManager::onInvoked(const bb::system::InvokeRequest&) {
       if(_pooled) {
              // TODO: Consider connecting this into CameraSettingsManager
              _camera->open(CameraUnit::Rear);
              _camera->startViewfinder();
              _pooled = false;
       }
}

While simple, this can be easy to forget, and errors may not immediately show with only superficial testing.

Cloud Pics Part 4_4

Photo by Tim Windsor

Enterprise

Why is CloudyPics invokable? Because the Camera app is actually tied into the personal perimeter and doesn’t exist in a BlackBerry Balance user’s work perimeter. However, the camera service still functions, so all you need is an app that uses it. Luckily, if you are a BES admin you can just compile and deploy CloudyPics. It won’t see the SD card, but everything else will work.

Why do you care if you’re a developer? Well, if you’ve hardcoded the

sys.camera.card

target, that’s not going to fly in the work perimeter. You don’t need it though. What I recommend is this:

       InvokeRequest invokeRequest;
       invokeRequest.setAction("bb.action.CAPTURE");
       invokeRequest.setMimeType("image/jpeg");
       InvokeTargetReply *invokeReply = _invokeManager->invoke(invokeRequest);

If you don’t specify the target, you will just get the default card. This will generally be the built-in camera app in the personal space, but leaves the door open for the user to select another default. In the work perimeter, this lets you hit CloudyPics or some other custom camera app that may have been deployed.

Less code, more power. Love it.

Anyway, that’s it for this version of CloudyPics! I hope you’ve enjoyed this series on our first showcase app. As always, for questions, comments or feature requests hit me up in the comments below, or on Twitter.

Thanks, and happy coding!

Cloud Pics Part 4_5

Photo by Paul Bernhardt

About paulbe1