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 Nokia Developer.
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 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.
Related APIs
-
CBioControlExample
-
CCoeControl
-
CMsgBioControl
-
CRichBio
-
KUidUseDefaultApp
-
MMsgBioControl
-
NewL()
-
message_app_UID
-
message_appctrl_name
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
}
Related APIs
-
CMsgBioControl
-
CMsvEntry
-
CRichBio
-
MSvSession()
-
TMsvId
-
iId
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.)
Related APIs
-
AddItemL()
-
CCoeControl
-
CRichBio
-
CRichBio::Editor()
-
SetContainerWindowL()
-
iViewer
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;
}
Related APIs
-
AddMenuItemL()
-
CEikMenuPane
-
CMsgBioControl::AddMenuItemL()
-
EFalse
-
FirstFreeCommand()
-
HandleBioCommandL()
-
MMsgBioControl::OptionMenuPermissionsL()
-
SetMenuCommandSetL()
-
TMsgBioOptionMenu
-
TUint32
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
.
Related APIs
-
CMsgBioControl
-
CMsgBioControl::ConfirmationQueryL()
-
CMsgBioControl::LoadResourceL()
-
CMsgBioControl::LoadStandardBioResourceL()
-
TDesC&
Related APIs
-
CMsgBioControl
-
ENbs
-
KUidUseDefaultApp
-
LocalServices,
-
Location
-
NetworkControl
-
NetworkServices
-
ReadDeviceData,
-
ReadUserData,
-
SwEvent
-
UserEnvironment,
-
WriteDeviceData,
-
WriteUserData
-
gfp.dll
-
message_app_UID
-
message_appctrl_name="BIOControlApi.dll"