Sending invocation

In the previous section you learned that an invocation message contains a target, an action, and data parameters. In this section, you'll learn how to send invocation messages from your application. You can configure your application to invoke target applications by using bound or unbound invocations. Be sure to check the InvokeManager class, which provides a convenient way to build invocations.

Bound invocation

Bound invocation occurs when a client application explicitly identifies the target application to handle the invocation. You can specify a target application's name in the target parameter of InvokeRequest class. For example, a client application that wants to open a .png file invokes a target application for viewing images using the bound invocation.

The following image show how a client application can use the bound invocation to invoke a target application of its choice. In this case, App 2 opens the document contained in the client application.

Diagram showing how a client application can use the bound invocation to invoke a target application of its choice

Here's an example that shows how to send an invocation request:

InvokeManager invokeManager;
InvokeRequest request;
// Who do we want to send the invoke request to?
request.setTarget("com.example.image.view"); 
// What do we want the target application to do with it?
request.setAction("bb.action.OPEN");
// What are we sending?         
request.setMimeType("image/png");
// Where is the data?            
request.setUri(QUrl("file:///path/to/image.png"));  
InvokeTargetReply *reply = invokeManager.invoke(request);

To verify whether your invoke request is successful, connect to the signal emitted by the reply, to listen for the response:

...
InvokeTargetReply reply) {
    // Remember to take ownership of the reply object.
    reply->setParent(this);
    // Listen for the invoke response.  
    
    // If any Q_ASSERT statement(s) indicate that the slot failed to connect to 
    // the signal, make sure you know exactly why this has happened. This is not
    // normal, and will cause your app to stop working!!
    bool connectResult;
    
    // Since the variable is not used in the app, this is added to avoid a 
    // compiler warning.
    Q_UNUSED(connectResult);
    
    connectResult =  QObject::connect(reply, SIGNAL(finished()), 
                                      this,  SLOT(onInvokeResult()));

    // This is only available in Debug builds.
     Q_ASSERT(connectResult);
     
    // Store reply somewhere, so you can 
    // access it when onInvokeResult fires.
    _invokeTargetReply = reply;    
}


// Because we connected this method to the finished() SIGNAL on the 
// reply object, this method will be called when the application
// receives an invoke response.
void MyApp::onInvokeResult()
{
    // Check for errors
    switch(_invokeTargetReply->error()) {
        // Invocation could not find the target 
        // did we use the right target ID?
    case InvokeReplyError::NoTarget: {
            cout << "invokeFinished(): Error: no target" << endl;
            break;
        }
        // There was a problem with the invoke request
        // did we set all the values correctly?
    case InvokeReplyError::BadRequest: {
            cout << "invokeFinished(): Error: bad request" << endl;
            break;
        }
        // Something went completely 
        // wrong inside the invocation request 
        // Find an alternate route :(
    case InvokeReplyError::Internal: {
            cout << "invokeFinished(): Error: internal" << endl;
            break;
        }
        //Message received if the invoke request is successful
    default:
        cout << "invokeFinished(): Invoke Succeeded" << endl;
        break;
    }

    // A little house keeping never hurts...
    delete _invokeTargetReply;
}

Target discovery

After learning about how to send an invocation, you must be wondering how you can determine what targets are available on the device. This section deals with exactly that. You can hard code the target application that you want to send an invocation message to, or you can query the invocation framework to find out what targets are installed on the BlackBerry device. The invocation framework uses a target filter to allow the target applications to declare the types of invocation they support. When a client application queries the invocation framework, the framework uses the target app's filter to describe the kind of target it is looking for and the data it has to offer.

The quality of the results is generally dependent on how specific the target query request is. A target query request should consist of the URI and the MIME type. If the MIME type is left out, the framework tries to determine the type using the URI.

A client application can include the action, to be performed on the data, in its target query request. If the action is not specified in the target query request, the query result will contain a list of all the actions that can be performed on the specified data by the available targets.

The invocation framework returns a query result which includes the targets grouped by the action that these targets support. For each target, the query result provides the target ID, the target type (an application or a card), and other information about the target, such as icons and labels, that can be used to display the target in the client application's screen.

To determine what targets are suitable candidates, the invocation framework applies a set of brokering rules. For more information, see Target selection and brokering process.

The following code demonstrates a target query request in which a client application searches for targets that support the bb.action.OPEN action on image/png images, which are sent as a file:// URI.

InvokeManager invokeManager;
InvokeQueryTargetsRequest request;
// Find the targets that support bb.action.OPEN
request.setAction("bb.action.OPEN");  
// Find the targets that can handle image/png data   
request.setMimeType("image/png");    

// Find the targets that are sent as a file from this path 
request.setUri(QUrl("file:///path/to/image.png"));
InvokeQueryTargetsReply* results = invokeManager.queryTargets(request);

if(results) {
// Remember to take ownership of the results
results->setParent(this);   
// Listen for the results  
QObject::connect(results, SIGNAL(finished()), 
this, SLOT(onQueryResponse()));  
// Best to store the reply
// so you can access the results when onQueryResponse fires
_queryResults = results;                                      
}

Here's how you can handle the results:

Q_SLOT void onQueryResponse()
{
   switch(_queryResults->error()) {
   case InvokeReplyError::BadRequest:
     // Resolve the error
     break;
   case InvokeReplyError::Internal:
     // Resolve the error
     break;
   default:
     // If the error is resolved..
     QList<InvokeAction> targetsByAction = _queryResults->actions();
     for(int i = 0; i < targetsByAction.size(); i++) {
      // See the API documentation for the full set of action attributes
       QString actionName = targetsByAction[i].name();    
       QList<InvokeTarget> targets = targetsByAction[i].targets();
       for(int j = 0; j < targets.size(); j++) {
      // See the API documentation for the full set of target attributes
           QString targetId = targets[j].name();          
       }
     }
     break;
   }
  // Clean up the results
  delete _queryResults; 

Unbound invocation

In this section, you will learn how the invocation framework can make invoking a target application easy, by removing the need to query. Instead of sending a target query request to determine the available targets, the client application can simply send an invocation request to the framework without specifying a target. This kind of invocation is called unbound invocation. If you send an unbound invocation, the framework searches for and invokes a suitable target application for you.

The following image shows how a client application (App 1) lets the invocation framework return the most suitable target app for a particular action. In this case, App 2 is the most suitable application to open the document contained in the client application.

Diagram showing how a client app lets the invocation framework return the most suitable target app for a particular action.

To find the best candidate, the framework first uses a brokering process. It applies a set of rules to determine which target applications support the client application's invocation request. If there is more than one suitable target available, the framework applies a set of selection rules to choose the most appropriate target. For more information, see Target selection and brokering.

You can also send an invocation request without specifying an action item, since only the data is sent. The framework determines the type of target and the action to be performed.

If you don't define the action parameter, the invocation framework tries to find an appropriate target application for the bb.action.VIEW action. If no suitable target application is found for bb.action.VIEW, the invocation framework falls back to determine an appropriate target application that supports bb.action.OPEN. If still no suitable target application is available for bb.action.OPEN, the invoke request is unsuccessful.

The following sample code shows how you can easily create an unbound invocation. The sample code invokes the most suitable target application to handle the image.png file, specified by the URI.

InvokeManager invokeManager;
InvokeRequest request;
// Set the URI
request.setUri(QUrl("file:///path/to/image.png"));  
// Send the invoke 
InvokeTargetReply *reply = invokeManager.invoke(request);       

Last modified: 2014-01-23

comments powered by Disqus