Plug-in Bio Control API: Using Plugin BIO Control API

Before using this API, it is important to understand the concept of BIO messaging. A brief introduction was given in the section BIO Messages but more extensive information can be found in the SDK documentation and at Forum Nokia.

Crucial to the use of BIO messaging, is the definition of a BIO Information File (BIF). This defines the UID of the BIO message and contains information about the parser and BIO control to be used for the particular BIO message type. The BIF is created from a resource during the normal build process, that is using the standard resource compiler. An example resource file is shown here:

#include <biftool2.rh>
RESOURCE BIO_INFO_FILE
    {
    message_type_UID=0x1000FFFF; 			 						// Unique UID for the message type
    message_parser_name="BIOControlApiParser.dll"; // Parser name (a parser is not necessary for using the plugin control)
    message_app_UID=KUidUseDefaultApp;  					// This defines that the message is opened in the default app (SMS editor)
    message_appctrl_name="BIOControlApi.dll";     	// this is the name of our own BIOcontrol

    description="BioExample";  // Displayed in the Inbox

    ids=
        {
        ID
            {
            type=ENbs;         // Narrow band (SMS)
            confidence=EPossible;
            character_set=KCharSetDefault;
            text="//MYTK";     // Initial characters to define our bio message	
            }
        }; 
    }
 

The example BIF resource above defines both a BIO parser and a BIO control. To use Plugin BIO control API, the parser is optional but it is recommended that a parser should be used, as a minimum, to set the parsed flag to read. The Symbian Generic File Parser (gfp.dll) can be used for this purpose.

A polymorphic DLL is created for the BIO control (CMsgBioControl derived class) and this is referenced in the BIF: message_appctrl_name="BIOControlApi.dll". Since the control is used as a plug-in to the messaging UI, the message_app_UID should be defined as KUidUseDefaultApp - that is, the SMS editor.

While it is possible to define a specific port number for BIO messages to be received on, this example just uses a simple text string as an identifier. The type ENbs indicates that, in this instance, the bearer is SMS. The string is written as the first characters of the BIO message.

On the target device, the BIF file created should reside in the directory \resource\messaging\bif (The parser DLL and control DLL both reside in sys\bin)

In this document, we are concerned with BIO messages that are received as an SMS and which are displayed using the SMS UI. It is also possible that a BIO message can be opened from a file, for example an email attachment, and that they are display using an independent application.

The capabilities required to use Plugin BIO control API are LocalServices, ReadDeviceData, ReadUserData, UserEnvironment, WriteUserData WriteDeviceData, NetworkControl Location, SwEvent, and NetworkServices. Note some of these capabilities are not user-grantable and therefore applications implementing the Plugin BIO Control API must be granted approval to use them through the Symbian Signed process. (Note that a BIO parser requires further capabilities.)

The most important use cases of the Plugin BIO control are the following:

Launching the BIO control

If a BIO message arrives in Inbox on the device it is marked as a BIO message. This is done by the messaging framework as a result of the message arriving on a certain port or being identified by the defined character string.

Behavior on opening the message is dependent upon the values defined in the BIF file. If the message_app_UID was KUidUseDefaultApp then the SMS editor is opened showing the control specified by message_appctrl_name. The framework calls NewL() on the BIO control and the control will then be drawn. The SMS editor serves as a container for the BIO control.

As stated earlier, the BIO control itself is a polymorphic DLL; its NewL() function should be its first (only) exported function.

The BIO control must derive from CMsgBioControl. As CMsgBioControl is derived from the mixin MMsgBioControl, it is necessary for the BIO control to implement any of its pure virtual functions that have not been implemented by CMsgBioControl. To ensure that the control is sized appropriately and that scrolling works correctly, it is also necessary to implement various UI functions.

A header file for an example BIO control (CBioControlExample) is shown below:

#include <msgbiocontrol.h>
class CRichBio;

class CBioControlExample : public CMsgBioControl 
    {
public:
        IMPORT_C static CMsgBioControl* NewL(
            MMsgBioControlObserver& aObserver,
            CMsvSession* aSession,
            TMsvId aId,
            TMsgBioMode aEditorOrViewerMode,
            const RFile* aFile);

         // Destructor
        ~CBioControlExample();

public:  // From MMsgBioControl
        void SetAndGetSizeL(TSize& aSize);
        void SetMenuCommandSetL(CEikMenuPane& aMenuPane);
        TBool HandleBioCommandL(TInt aCommand);
        TRect CurrentLineRect() const;
        TBool IsFocusChangePossible(TMsgFocusDirection aDirection) const;
        HBufC* HeaderTextL(void) const;
        TInt VirtualHeight();
        TInt VirtualVisibleTop();
        TBool IsCursorLocation(TMsgCursorLocation aLocation) const;

protected: // From CCoeControl
        TInt CountComponentControls() const;
        CCoeControl* ComponentControl(TInt aIndex) const;
        void SizeChanged();
        void FocusChanged(TDrawNow aDrawNow);
        void SetContainerWindowL(const CCoeControl& aContainer);

private: // Construction
        CBioBCTest(
            MMsgBioControlObserver& aObserver,
            CMsvSession* aSession,
            TMsvId aId,
            TMsgBioMode aEditorOrViewerMode,
            const RFile* aFile);

        // Second phase constructor.
        void ConstructL();

private:
        // The viewer control
        CRichBio* iViewer;
};

In the example shown, the BIO control owns a CRichBio object. This is another CCoeControl that is designed specifically for use with the BIO control and should be used to display the data. Many of the functions relating to size and scrolling can then simply call the equivalent CRichBio function. (It is possible to define another control rather than use CRichBio but the BIO control interface requirements must be fulfilled.)

An example implementation of this class is shown below:

CBioControlExample::CBioControlExample(MMsgBioControlObserver& aObserver, CMsvSession* aSession, TMsvId aId,
     TMsgBioMode aEditorOrViewerMode, const RFile* aFile):         
     CMsgBioControl(aObserver, aSession, aId, aEditorOrViewerMode, aFile)     
    {          
    }  

CBioControlExample::~CBioControlExample()
    {
    delete iViewer;
    }

EXPORT_C CMsgBioControl* CBioControlExample::NewL(
    MMsgBioControlObserver& aObserver,
    CMsvSession* aSession,
    TMsvId aId,
    TMsgBioMode aEditorOrViewerMode,
    const RFile* aFile)
    {
    CBioBCTest* self = new(ELeave) CBioBCTest(
        aObserver,
        aSession,
        aId,
        aEditorOrViewerMode,
        aFile);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }

void CBioControlExample::ConstructL()     
    { 
    // Load ui resource files
    _LIT(KAvkonResourceFile, "avkon.rsc");

    LoadResourceL(KAvkonResourceFile);     
    LoadStandardBioResourceL();      
    }

TInt CBioControlExample::CountComponentControls() const
    {
    return 1; // the viewer component
    }

CCoeControl* CBioControlExample::ComponentControl(TInt aIndex) const
    {
    if (aIndex == 0 && iViewer)
        {
        return iViewer;
        }
    return NULL;
    }


void CBioControlExample::SetContainerWindowL(const CCoeControl& aContainer)
    {
    CCoeControl::SetContainerWindowL(aContainer);

    // The reason for creating the viewer control here is that the
    // construction of the viewer requires a parent with a window. 
    // So it cannot be done in ConstructL().
    //
    // The type (TRichBioMode) of CRichBio object is needed for construction
    // but is not used meaningfully. Therefore either ERichBioModeEditorBase
    // or ERichBioModeStandard are equivalent in terms of their effect on the CRichBio object
    iViewer = CRichBio::NewL(this, ERichBioModeEditorBase);
    iViewer->ActivateL();
    }

void CBioControlExample::SetAndGetSizeL(TSize& aSize)
    {
    iViewer->SetAndGetSizeL(aSize);
    SetSizeWithoutNotification(aSize);
    }

void CBioControlExample::SetMenuCommandSetL(CEikMenuPane& aMenuPane)
    {
    }

TBool CBioControlExample::HandleBioCommandL(TInt aCommand)
    {
    return EFalse;
    }

TInt CBioControlExample::VirtualHeight()
    {
    return iViewer->VirtualHeight();
    }

TInt CBioControlExample::VirtualVisibleTop()
    {
    return iViewer->VirtualVisibleTop();
    }

TBool CBioControlExample::IsCursorLocation(TMsgCursorLocation aLocation) const
    {
    return iViewer->IsCursorLocation(aLocation);
    }

TRect CBioControlExample::CurrentLineRect() const
    {
    return iViewer->CurrentLineRect();
    }

void CBioControlExample::SizeChanged()
    {
    iViewer->SetExtent(Position(), iViewer->Size());
    }

void CBioControlExample::FocusChanged(TDrawNow aDrawNow)
    {
    iViewer->SetFocus(IsFocused());
    }

TBool CBioControlExample::IsFocusChangePossible(
    TMsgFocusDirection aDirection) const
    {
    if (aDirection == EMsgFocusUp)
        {
        return iViewer->IsCursorLocation(EMsgTop);
        }
    return EFalse;    
    }

HBufC* CBioControlExample::HeaderTextL() const
    {
    _LIT(KHeader, "My Bio control");
    HBufC* buf = KHeader().AllocL();
    return buf; 
    }

This code provides a skeleton implementation of a BIO control plug-in to the messaging UI.

Retrieving data from the BIO message

When a BIO message is received by a device, its data is stored by Message Server. If a BIO parser has been installed for the particular BIO message type, then the message will have been parsed and maybe modified by the parser. It is likely that any BIO control implementation requires access to the BIO message that has been opened. Fortunately, when the BIO control constructor is called by the framework, the ID(TMsvId) of the appropriate message is passed through and also a handle to a session with the Message Server. These are maintained by the base class (CMsgBioControl) and can be accessed through iId and MSvSession() respectively. The usual messaging APIs can be used to access the message.

In the example below, access to the data is through creation of a CMsvEntry object loaded with the appropriate message details. Its store is then read and the message body data passed to the viewer's (CRichBio) rich text editor. This strategy is a bit simplistic and it is likely that the data needs to be manipulated in some way, for example removing the BIO identifier string, but it illustrates the basic concept.

void CBioControlExample::ReadMessageL() 	
    { 	
    CMsvEntry* entry = MsvSession().GetEntryL(iId);
    CleanupStack::PushL(entry);
    CMsvStore* store = entry->ReadStoreL(); 	
    CleanupStack::PushL(store); 	 	
    // If the message has a message body then copy the 
    // the text to the viewer (CRichBio)
    if (store->HasBodyTextL()) 	
        { 	    
        CEikRichTextEditor& ed = iViewer->Editor();  		
        store->RestoreBodyTextL(*(ed.RichText())); 	
        } 	
    CleanupStack::PopAndDestroy(2, entry); 	// Entry & store
    }

Displaying data in the BIO control

The BIO control is a CCoeControl and therefore has the functionality of any normal control. However, it is usual for the BIO control to create a CRichBio object (also a CCoeControl) and use this to display the data. The CRichBio object needs a parent window before it can be fully constructed and hence care must be taken not to construct it until the parent window can be set. A suitable place is in the BIO control's SetContainerWindowL() function. A typical implementation is shown:

void CBioControlExample::SetContainerWindowL(const CCoeControl& aContainer)
    {
    // Call the base class
    CCoeControl::SetContainerWindowL(aContainer);

    // The reason for creating the viewer control here is that the
    // construction of the viewer requires a parent with a window. So it
    // cannot be done in ConstructL().
    //
    iViewer = CRichBio::NewL(this, ERichBioModeStandard); // iViewer is a CRichBio* (member data)
    iViewer->ActivateL();
    }

CRichBio owns an editor that is used to display rich text; it is accessed through CRichBio::Editor(). It also has a function, AddItemL() , that can be used to add labels to the control. This actually appends the label and its value to the text contained in the rich text editor. An example is shown below, where iViewer is the CRichBio object:

CEikRichTextEditor& ed = iViewer->Editor();  
if (store->HasBodyTextL()) 	
    {
    store->RestoreBodyTextL(*(ed.RichText())); 	
    } 	 	

// Define strings used for labels. Note, these should usually be defined
// in a resource file (not using _LIT). 
_LIT(KLabel1, 		"1st label desc"); 	
_LIT(KLabel1Text, 	"text for label 1"); 	
_LIT(KLabel2, 		"2nd label desc"); 	
_LIT(KLabel2Text, 	"this is the text for label 2"); 	     

iViewer->AddItemL(KLabel1, KLabel1Text);     
iViewer->AddItemL(KLabel2, KLabel2Text);

The rich text editor is first populated with the BIO message data. (See Retrieving data from the BIO message for further information.) Two labels are then added immediately after the end of the text. The displayed control (depending on the message data) would look similar to Figure 2. (Adding a new line before the labels is the responsibility of the developer.)

Figure 2: Example BIO control display

Defining and handling menu options

Menu options can be made available to the user through the left (Options) softkey. Certain menu options are available as standard on a BIO control but, if required, these can be suppressed. In addition, control specific options can be defined.

The function MMsgBioControl::OptionMenuPermissionsL() should be implemented by the control if the standard menu options are not acceptable. The function should return a TUint32 representing permissible options. Options are defined by the enumeration TMsgBioOptionMenu in mmsgbiocontrol.h.

TUint32 CBioControlExample::OptionMenuPermissionsL() const
	{
	// Don't implement this function if the defaults are ok
	// implement to define which of the standard options are presented
	return (TUint32)(EMsgBioMessInfo);
 	}

If further options are required, they can be added to the menu through the SetMenuCommandSetL(), which is called by the framework. This function receives a reference to the menu pane (CEikMenuPane) of the BIO control. CMsgBioControl::AddMenuItemL() can then be used to insert/append options to the menu. As menu options are added, they need to be assigned a command ID so that they can be handled when selected. The mechanism used to do this is for the control to enquire what the next free command ID is and then add each item as an offset against this value. Enquiry about the next available command ID is achieved with a call to FirstFreeCommand() on the control's observer. AddMenuItemL() then takes an offset value as a parameter.

const TInt KItemOneCommandOffset = 0;
const TInt KItemTwoCommandOffset = 1;
const TInt KItemMenuPositionOne = 0;
const TInt KItemMenuPositionTwo = 1;

void CBioControlExample::SetMenuCommandSetL(CEikMenuPane& aMenuPane)     
    {     
    // This is called when menu is activated     
    // so menu pane is passed to control and items can be added         
    LoadResourceL(KBCTestResourceFile); 	// Resource file resides in /resource directory
    iMyDefinedCommand = iBioControlObserver.FirstFreeCommand(); 	

    // Add Menu items
    // Value of first command added will be iMyDefinedCommand + KItemOneCommandOffset
    // It will be placed at the position in the menu defined by KItemOneMenuPosition
    AddMenuItemL(aMenuPane, R_MENU_ITEM_ONE, KItemOneCommandOffset, KItemOneMenuPosition); 

    // Value of second command added will be iMyDefinedCommand + KItemTwoCommandOffset
    // It will be placed at the position in the menu defined by KItemTwoeMenuPosition
    AddMenuItemL(aMenuPane, R_MENU_ITEM_TWO, KItemTwoCommandOffset, KItemTwoMenuPosition); 		
    }

    

When any menu item is selected, HandleBioCommandL() is called on the control. The command can either be dealt with by the derived class or passed on to the base class by returning EFalse.

TBool MMsgBioControl::HandleBioCommandL(TInt aCommand)     
    {
    TBool commandHandled = EFalse;
    if (aCommand == iMyDefinedCommand + KItemOneCommandOffset) 	    
        {
        // Handle the first command here
        ...
        commandHandled = ETrue;
        } 	
    else if (aCommand == iMyDefinedCommand + KItemTwoCommandOffset) 	    
        {
        // Handle the second command here
        ...
        commandHandled = ETrue;
        } 	

    return commandHandled;     
    }

Using a query note for user confirmation

The base class, CMsgBioControl, provides a function that can be used to present a question to the user that has a Yes/No answer. This is useful if you need to confirm an action with the user before carrying it out. CMsgBioControl::ConfirmationQueryL() has two overloads - one that takes a TDesC&, the other a resource ID of a string. Before using the query, CMsgBioControl::LoadStandardBioResourceL() must be called to ensure the resource is available. Any further resources should be loaded using CMsgBioControl::LoadResourceL(). It is usual to call this function during construction of the control.

// _LIT used for demonstration purposes only
_LIT(KQueryText, "Confirm action");
TBool response = ConfirmationQueryL(KQueryText);
if (response)
    {
    // Carry out action
    // ...
    }

The resulting query dialog is shown in Figure 3.

Figure 3: Example confirmation query note


Copyright © Nokia Corporation 2001-2008
Back to top