The messaging architecture relies on the client/server framework. This
means that a session (an instance of CMsvSession
) is required
as a channel of communication between the client thread (Client MTM) and the
Message Server thread. This session must exist before Client MTM can be constructed
and its functionality used.
The most important use cases are described in the sections below. Note
that these use cases do not constitute an exhaustive list of the MMS Client
MTM's functionality. CMmsClientMtm
contains numerous functions
documented in the header file mmsclient.h.
Note that a prerequisite of using MMS Client MTM is a good understanding of the key messaging concepts.
Capabilities required to use MMS Client MTM API are ReadUserData
, ReadDeviceData
and NetworkServices
The most important use cases of MMS Client API are the following:
The first step for using MMS Client MTM API is creating a session with
Message Server. To do this, a class which implements the mixin MMsvSessionObserver
is
required. An example of such a class is shown below.
class CMtmExample : public CActive, public MMsvSessionObserver { ... private: // from MMsvSessionObserver void HandleSessionEventL(TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* aArg3); ... private: CMsvOperation* iOp; CMsvSession* iSession; // Session to the messaging server CClientMtmRegistry* iMtmReg; CMmsClientMtm* iMmsMtm; TMsvId iNewMessageId; };
A session with the Message Server can be created either synchronously (using CMsvSession::OpenSyncL
)
or asynchronously (using CMsvSession::OpenAsyncL()
). In each
case, an instance of the MMSvSessionObserver
derived class
is passed, by reference to the function. (Use of the asynchronous implementation
is recommended to ensure responsiveness of the device.)
void CMtmExample::ConstructL() { CActiveScheduler::Add(this); // Create CMsvSession // New session is opened asynchronously iSession = CMsvSession::OpenAsyncL(*this); }
For asynchronous connections, notification of success is through a call
to the implemented HandleSessionEventL()
function, with the
argument aEvent = EMsvServerReady
being passed through. Once
this notification has been received (or the synchronous function has returned),
other messaging objects can be created.
void CMtmExample::HandleSessionEventL(TMsvSessionEvent aEvent, TAny* /*aArg1*/, TAny* /*aArg2*/, TAny* /*aArg3*/) { switch (aEvent) { // This event tells us that the session has been opened case EMsvServerReady: CompleteConstructL(); break; ... } }
Accessing MMS Client MTM is done through the Client MTM registry (CClientMtmRegistry
).
This holds details of all registered client MTMs available on the device.
The registry is constructed with a handle to the session. MMS Client MTM can
then be created through a call to CClientMtmRegistry::NewMtmL()
,
passing through KUidMsgTypeMultiMedia
(defined in mmsconst.h)
as the MTM UID. NewMtmL()
returns a CBaseMtm
pointer
which needs to be cast into the correct type, that is CMmsClientMtm
.
void CMtmsExampleEngine::CompleteConstructL() { // Construct the Client MTM registry iMtmReg = CClientMtmRegistry::NewL(*iSession); // Obtain MMS Client MTM from the MTM registry iMmsMtm = static_cast<CMmsClientMtm*>(iMtmReg->NewMtmL(KUidMsgTypeMultimedia)); }
MMS Client MTM can now be used to access Message Server and create, manipulate, and send multimedia messages.
Once the MMS Client MTM instance has been constructed (see Constructing
MMS Client MTM), it can be used to create an MMS entry in Message Server.
There is more than one way to create an MMS message entry but the most obvious
way is to use CMmsClientMtm::CreateMessageL()
. This function
creates an empty message entry as the child of the current context. (The context
is simply the entry on which the MTM actions are performed.) The entry is
created with its visible flag set to false and its in-preparation flag set
to true since at this stage it is empty.
As the entry is created as a child to the current context , it is important
to first set the context appropriately before CreateMessageL()
is
called. It is usual to create new messages in the drafts folder. To set the
context , SwitchCurrentEntryL()
is called with the parent
folder ID (KMsvDraftEntryId
) passed through. CMmsClientMtm::CreateMessageL()
is
then called with the required service ID as a parameter. The service ID can
be retrieved by calling DefaultServiceL()
on the MTM. After
successfully creating the new message entry, CreateMessageL()
then
sets the new entry as the current context.
TBool CMtmsExampleEngine::CreateNewMessageL() { // Set context to the parent folder (Drafts folder) iMmsMtm->SwitchCurrentEntryL(KMsvDraftEntryId); iMmsMtm->CreateMessageL(iMmsMtm->DefaultServiceL()) ...
At this stage, the created message is empty. To be useful, the intended message recipient(s) of the message must be set. For MMS entries, it is also useful to give the message a subject attribute.
CMmsClientMtm::AddAddresseeL()
has a number of overloads
that can be used to set the To, Cc, and Bcc fields of an MMS entry. CMMsClientMtmL::SetSubjectL()
takes
a descriptor (TDesC&
) and is used to set the subject.
// Set the recipients (_LIT used only for demonstration purposes) // "To" recipient. _LIT(KSamsNumber, "07738123456"); _LIT(KSamsAlias, "Sam"); // Cc recipient _LIT(KPetesNumber, "07812654321"); iMmsMtm->AddAddresseeL(EMsvRecipientTo, KSamsNumber, KSamsAlias); // To field, alias set iMmsMtm->AddAddresseeL(EMsvRecipientCc, KPetesNumber); // Cc field, no alias // Set a message subject _LIT(KMessageSubject, "MMS Example"); iMmsMtm->SetSubjectL(KMessageSubject);
The next step at this point is to add some text and multimedia object to the message. How this is done is shown in Adding an attachment to an MMS message.
Here, the message is just saved to the drafts folder. To indicate that
the message entry is no longer being amended, TMsvEntry::SetInPreparation()
can
be set to EFalse
while TMsvEntry::SetVisible()
is
set to ETrue
to ensure the message is displayed in the Messaging
application. Changes to the entry should be committed using CMsvEntry::ChangeL()
and
the message saved using CMmsClientMtm::SaveMessageL()
.
SetInPreparation()
and SetVisible()
need
to be called on the TMsvEntry
that represents the message
in Message Server. To access this, Entry()
is called on Client
MTM and then Entry()
is called on the returned CMsvEntry
.
TMsvEntry ent = iMmsMtm->Entry().Entry(); // Set InPreparation to false ent.SetInPreparation(EFalse); ent.SetVisible(ETrue); // Mark as visible, so this the message can be seen in Drafts iMmsMtm->Entry().ChangeL(ent); // Commit changes //Save the changes iMmsMtm->SaveMessageL();
There are a number of attributes on a message entry than can be set or amended. These attributes are properties of the message header but can be set through Client MTM and include the expiry date on the message, the delivery time, and priority. As long as the context of the MTM is set to the appropriate message, setting the values for the properties is simply a case of calling the correct function. The functions are detailed in the Client MTM header file (mmsclient.h) and enumeration values required by some of the functions can be found in mmsconst.h.
An example showing the message priority being set to high is shown below.
iMmsMtm->SetMessagePriority(EMmsPriorityHigh); //Save the changes iMmsMtm->SaveMessageL();
Although CMmsClientMtm
has a Body()
function
defined, it should not be used. Body text for an MMS message is treated as
a message attachment and therefore should be set using CMmsClientMtm::CreateTextAttachmentL()
.
See Adding attachments to an MMS message for
further information.
All MMS message data (text, images, sounds, and so on) should be sent as
attachments to the message with the required presentation being defined by
a SMIL file (also sent as an attachment). Note, although CMmsClientMtm
has
a Body()
function defined, it should not be used - the message
body text is treated as an attachment. Therefore, text should be added using CMmsClientMtm::CreateTextAttachmentL()
.
virtual void CMmsClientMtm::CreateTextAttachmentL( CMsvStore& aStore, TMsvAttachmentId & aAttachmentId, const TDesC& aText, const TDesC& aFile, TBool aConvertParagraphSeparator = ETrue);
The function requires that the message store for the message entry has
been opened ready for editing. A pointer to the open store is then passed
as a parameter. The text required for the message is passed as a descriptor
(aText
) and a suggestion for a suitable file name is also
needed (SMIL references this filename). If required, the unicode paragraph
separator (character 0x2029
) can be replaced with a line
feed (character 0x000a
). On completion, aAttachmentId
is
set.
To use CreateTextAttachmentL()
, the message entry's store
must first be opened.
// Open the entry's store - assume message context set correctly CMsvStore* store = iMmsMtm->Entry().EditStoreL(); CleanupStack::PushL(store); // Add the text attachment. _LIT used for demonstration purposes _LIT(KMMSText, "Thought you'd like to see where I'm staying"); _LIT(KMMSTextFilename, "hello.txt"); iMmsMtm->CreateTextAttachmentL(*store, attachId, KMMSText, KMMSTextFilename); // Add more attachments or save the message // Remember to do the appropriate clean up at the end ...
For multimedia attachments, CMmsClientMtm::CreateAttachment2L()
is
used.
virtual void CreateAttachment2L( CMsvStore& aStore, RFile& aFile, TDesC8& aMimeType, CMsvMimeHeaders& aMimeHeaders, CMsvAttachment* aAttachmentInfo, TMsvAttachmentId& aAttaId);
The function creates an attachment entry and copies the file specified
as a parameter (aFile
) to an open message store (aStore
).
The attachment should be passed as a handle to an open file. The MIME type,
for example "image/jpeg"
for a JPEG image, is passed as a
descriptor. A MIME header (CMsvMimeHeaders
) for the attachment
is created. A reference to the header is passed to the function so it can
be updated. A CMsvAttachment
object is created with the parameter CMsvAttachment::EMsvFile
to
represent the attachment. This is also updated by the CreateAttachment2L
function.
// Continuation of code above which set a text attachment // Add an image attachment // Set the filename for the image #define KDirPictures PathInfo::ImagesPath() #define KPhoneRootPath PathInfo::PhoneMemoryRootPath() _LIT(KFileName, "mmsexample.jpg"); TFileName attachmentFile(KPhoneRootPath); attachmentFile.Append(KDirPictures); attachmentFile.Append(KFileName); // Open the attachment file RFile attachment; TInt error = attachment.Open(CCoeEnv::Static()->FsSession(), attachmentFile, EFileShareReadersOnly | EFileRead); CleanupClosePushL(attachment); // Check that the attachment file exists. if(error != KErrNone) { _LIT(KFileNotFound, "Attachment file not found!"); attachment.Close(); CleanupStack::PopAndDestroy(attachment); CleanupStack::PopAndDestroy(store); return EFalse; } else { // Mime header CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL(); CleanupStack::PushL(mimeHeaders); mimeHeaders->SetSuggestedFilenameL(KFileName); // Represents a single attachment and information about the attachment CMsvAttachment* attaInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); CleanupStack::PushL(attaInfo); // Mime Type _LIT8(KMimeType, "image/jpeg"); TBufC8<10> mimeType(KMimeType); // Attachment file must be in a public folder (e.g. c:\Data\images) iMmsMtm->CreateAttachment2L( *store, attachment, mimeType, *mimeHeaders, attaInfo, attachId ); CleanupStack::Pop(attaInfo); // attaInfo CleanupStack::PopAndDestroy(mimeHeaders); // mimeHeaders // If a SMIL file is attached for presentation layout purposes // then it should be done here before the // the changes are committed // Commit the changes and perform the appropriate cleanup store->CommitL(); attachment.Close(); CleanupStack::PopAndDestroy(attachment); CleanupStack::PopAndDestroy(store); // Save the changes iMmsMtm->SaveMessageL(); }
This example shows how to attach some text and an image to an MMS message. However, the layout of the message is left to the receiving device. To dictate how the message should be presented to the recipient, a SMIL file is needed. An example file is shown below.
<smil xmlns="http://www.w3.org/2000/SMIL20/CR/Language"> <head> <layout> <root-layout width="160" height="140"/> <region id="Image" width="160" height="120" left="0" top="0"/> <region id = "Text" width="160" height="20" left="100" top="120"/> </layout> </head> <body> <par dur="10s"> <img src="mmsexample.jpg" region="Image" /> <text src="hello.txt" region="Text" /> </par> </body> </smil>
This file indicates that the image should be displayed before the text in the message.
The SMIL file should be added as an attachment in the same way that the
image was added, that is using CMmsClientMtm::CreateAttachment2L()
.
In this case, the MIME type should be set to KMmsApplicationSmil
(defined
in mmsconst.h). The SMIL attachment should then be set as the root
attachment using CMmsClientMtm::SetMessageRootL()
and passing
through the ID (TMsvAttachmentId
) of the SMIL attachment
set by the Createattachment2L()
function.
iMmsMtm->SetMessageRootL(smilAttachId);
Note that after setting the root, the changes to the store should be committed and the message saved.
Before sending an MMS message, it is advisable to validate it to ensure
the added data is compliant with the message type. The base class (CBaseMtm
)
defines the function ValidateMessage()
and this is implemented
by each MTM to provide type-specific validation. The function takes a TMsvPartList
parameter
which is a bit-set (flags for this bit-set are defined in Mmtdef.h)
indicating the parts to be checked. Not all flags are supported by MMS MTM.
Supported flags are KMsvMessagePartRecipient
, KMsvMessagePartOriginator
and KMsvMessagePartAttachments
. The return value of ValidateMessage()
is also a TMsvPartList
bit
set that indicates which parts of the message were invalid.
TBool CMtmsExampleEngine::ValidateMMS() { // Context should be set to appropriate message TMsvPartList msgCheckParts = KMsvMessagePartRecipient | KMsvMessagePartOriginator | KMsvMessagePartAttachments; TMsvPartList msgFailParts = iMmsMtm->ValidateMessage(msgCheckParts); return msgFailParts == KMsvMessagePartNone; }
Assuming that the MMS message has been created and validated, sending a
message using MMS Client MTM is simply a case of using its SendL()
function.
The important point to realize is that the ID of the message entry that should
be sent must be set as the context of the MTM. If required, a time can be
set for the sending of the message. The SendL()
function
is asynchronous and returns a CMsvOperation
pointer. This
can be used to obtain progress information about the operation before the
active object completes (that is, before the RunL()
is called).
void CMtmsExampleEngine::SendMMSL() { iOp = iMmsMtm->SendL(iStatus); SetActive(); }
There is also an overload of CMmsClientMtm::SendL()
that
enables the sending of multiple messages with one operation. An array of message
IDs (TMsvId
s) is created as a CMsvEntrySelection
object
and this is passed as a parameter to the SendL()
function.
Again the function is asynchronous and returns a CMsvOperation
pointer
to allow progress monitoring.
MMS Client MTM can be used to retrieve information from received MMS messages.
Functions such as Sender()
, MessageReceiveTime()
, NumberOfPreviousSenders()
are
available to use. In each case, the important point is that the context of
the MTM is set correctly using the ID of the message.
The example below shows an incoming message, caught by MMsvSessionObserver::HandleSessionEventL()
.
The message ID (
) is
stored as the entry is created in Inbox and then, once the message has been
stored (aEvent == EMsvEntriesChanged
), the MTM context is
set and the message loaded. Information about the message can then be queried,
for example the sender.
void CMtmsExampleEngine::HandleSessionEventL(TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* aArg3) { switch (aEvent) { ... case EMsvEntriesCreated: if(*(static_cast<TMsvId*>(aArg2)) == KMsvGlobalInBoxIndexEntryId) { CMsvEntrySelection* entries = static_cast<CMsvEntrySelection*>(aArg1); iNewMessageId = entries->At(0); } break; case EMsvEntriesChanged: if(*(static_cast<TMsvId*>(aArg2)) == KMsvGlobalInBoxIndexEntryId) { CMsvEntrySelection* entries = static_cast<CMsvEntrySelection*>(aArg1); if(iNewMessageId == entries->At(0)) { iMmsMtm->SwitchCurrentEntryL(iNewMessageId); iMmsMtm->LoadMessageL(); TPtrC sender = iMmsMtm->Sender(); // Use the sender information ... } } break; ... } }
Further information about the general messaging concepts that are needed to understand this example can be found in the SDK documentation and at Forum Nokia.
It is important to note that an MMS entry does not have a message body
as the message text is sent as an attachment and therefore the Client MTM's Body()
function
should not be used. However, further information about the message text and
attachments can be obtained through the message entry's store. The general
procedure is to use CMsvStore::AttachmentManagerL()
. The
example below shows how the message text could be retrieved.
CMsvEntry* entry = iSession->GetEntryL(iNewMessageId); CleanupStack::PushL(entry); CMsvStore* store = entry->ReadStoreL(); if(store!= NULL) { CleanupStack::PushL(store); MMsvAttachmentManager& attManager = store->AttachmentManagerL(); _LIT8(KMimeBuf, "text/plain"); TBuf8<10>mimeBuf(KMimeBuf); // Cycle through the attachments for(TInt i=0; i<attManager.AttachmentCount(); i++) { CMsvAttachment* attachment = attManager.GetAttachmentInfoL(i); CleanupStack::PushL(attachment); // Test to see if we have a text file if(mimeBuf.CompareF(attachment->MimeType())== 0) { RFile file = attManager.GetAttachmentFileL(i); // The file can then be read using the normal file functionality // After reading, the file should be closed ... } CleanupStack::PopandDestroy(attachment); } Cleanupstack::PopAndDestroy(store); } CleanupStack::PopAndDestroy(entry);
This aspect of message querying falls outside the scope of MMS Client MTM and therefore is not covered in detail here but is included for completeness.
An obvious requirement of a messaging client is to be able to reply to
or forward received messages. MMS Client MTM provides CMmsClientMtm::ReplyL()
and CMmsClientMtm::ForwardL()
for
this purpose. Both functions are asynchronous and return a CMsvOperation
pointer
to allow the monitoring of the progress of the function and ultimately to
provide the ID(TMsvId
) of the newly created message.
To use the functions, the message to be replied to/forwarded must be set
as the context. The functions take as parameters the ID (TMsvId
)
of the folder in which the new message should be created, a list (TMsvPartList
)
of the elements of the original message that should be copied to the new message,
and a TRequestStatus
reference. (Note that not all elements
of TMsvPartList
are supported, see mmsclient.h for
further information.) An example of using CClientMtm::ReplyL()
is
shown here but CMmsClientMtm::ForwardL()
can be used similarly.
void CMtmsExampleEngine::ReplyL() { iMmsMtm->SwitchCurrentEntryL(iMessageToReplyTo); iMmsMtm->LoadMessageL(); // Add the description of the received message to the new (reply) message // Note the Recipient address is automatically set to the sender // KMsvMessagePartRecipient is only needed for reply to all TMsvPartList msgCheckParts = KMsvMessagePartDescription; iOp = iMmsMtm->ReplyL(KMsvDraftEntryId,msgCheckParts,iStatus); SetActive(); }
On completion (RunL()
being called), the ID of the newly
created message is retrieved using CMsvOperation::Progress()
.
A package buffer is used to transfer this information. If the creation of
the reply/forward message was not successful, the ID will be set to KMsvNullIndexEntryId
.
Once the operation has completed, the message can be modified and saved, or
sent, using the usual functions.
void CMtmsExampleEngine::RunL() { if(iStatus.Int() == KErrNone) { // Retrieve the id of the created message // Id is returned in a package buffer TPckgBuf<TMsvId> pkg; pkg.Copy( iOp->ProgressL()); TMsvId indexEntry = pkg(); if(indexEntry != KMsvNullIndexEntryId) { // Set the context to the new message iMmsMtm->SwitchCurrentEntryL(indexEntry); iMmsMtm->LoadMessageL(); // Here add further recipients, subject, attachments // to the message ... // Commit the changes and save the Message TMsvEntry ent = iMmsMtm->Entry().Entry(); ent.SetReadOnly(EFalse); // Set InPreparation to false ent.SetInPreparation(EFalse); ent.SetVisible(ETrue); // mark as visible iMmsMtm->Entry().ChangeL(ent); // Commit changes //Save the changes iMmsMtm->SaveMessageL(); } } }
Once all of the required operations have been carried out, the appropriate destruction of resources should be carried out. Obviously the resources used are application specific but in general require at least Client MTM, the MTM registry and the session. They should be deleted in the reverse order to which they were created.
CMtmsExampleEngine::~CMtmsExampleEngine() { // For this particular example cleanup Cancel(); // Active Object delete iOp; // For general use of the MMS Client MTM delete iMmsMtm; // CMmsClientMtm delete iMtmReg; // CClientMtmRegistry delete iSession; // CMsvSession }
The leave mechanism of Symbian OS is used to handle error conditions on method calls. In addition, messaging defines a series of panics; these are detailed in Symbian OS reference » System panic reference » MSGS.