In order to use IM API, the client has to create a connection using an own unique application ID and log in to the remote IM server. After creating the interface objects, the user application has to register its observers using the corresponding method from each interface class. This is necessary because the methods of this API are non-blocking and the notification when the operation is complete is delivered through the observer methods. Each asynchronous operation returns an operation ID when the request is issued. When the operation ends, the observer method is called with the operation ID to map it back to the corresponding request.
Creating CImConnection
involves the ECom framework. ECom
loads imclient.dll, which contains the API implementation. If the implementation
is not found the NewL()
method leaves with KErrNotFound
.
After the object is created, the user has to register its implementation of
the MImConnectionObserver
interface.
The client application has to have an application ID, which is supposed
to be unique in the domain of the application using the instant messaging
services. The application ID is given in the NewL()
method.
That ID is used in routing the instant messages to the correct application
in case the session to the IM server is shared between two or more applications.
Registering can be done only once, a subsequent registration without unregistration
leads to an error.
An application ID can be defined as a literal, e.g.:
_LIT(KMyApplicationId, “MySeries60IMApplication”);
Figure 3: Creating the connection and registering the observer
Example code:
void CSDKExample1AppUi::ConstructL() { BaseConstructL( ); iAppContainer = new (ELeave) CSDKExample1Container; iAppContainer->SetMopParent( this ); iAppContainer->ConstructL( ClientRect( ) ); AddToStackL( iAppContainer ); // Create an instance of the connection. // If the plugin is not found the eCom will leave with KErrNotFound iMyImConnection = CImConnection::NewL( KMyApplicationId ); }
After the CImConnection
object is created the client application
has to log in. Logging in is done using the IM server URL, user ID, password
and the access point ID that is intended to be used. In case there already
is an existing session with the same user parameters that session is shared.
Figure 4: Logging in
Example code:
void CSDKExample1AppUi::TestConnectL ( ) { _LIT(KServer, “http://impsdomain.com:8080/wv”); _LIT(KUserId, “wv:[email protected]”); _LIT(KPassword, ”secret”); TRAPD(err,iMyImConnection->LoginL( KServer, KUserId, KPassowrd, 0)); // use existing AP!!! if ( err != KErrNone ) { // Failed to start the registration CAknErrorNote* note = new(ELeave) CAknErrorNote(); TBuf<0x100> buf; buf.Format( _L("Login failed:( %i )"), err ); note->ExecuteLD( buf ); } }
The HandleLoginL
method is implemented by the client.
It is called when LoginL()
is completed.
void CSDKExample1AppUi::HandleLoginL( const TInt aErrorCode ) { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::ELongTimeout ); TBuf<0x100> buf; buf.Format( _L("HandleLoginL( %i )"), aErrorCode ); note->ExecuteLD( buf ); }
If the login operation is not yet finished, there is a possibility to cancel it. If the login was not yet started or it is finished already the method will leave with an appropriate error code. The method is asynchronous and will complete by calling the corresponding observer method.
Figure 5: Canceling the ongoing login
The HandleCancelLoginL
method is implemented by the client.
It is called when CancelLoginL
is completed.
Example code:
void CSDKExample1AppUi::HandleCancelLoginL( const TInt aErrorCode ) { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::ELongTimeout ); TBuf<0x100> buf; buf.Format( _L("HandleCancelLoginL( %i )"), aErrorCode ); note->ExecuteLD( buf ); }
After a successful login the connection can be closed by starting a logout procedure. If calling the method when not logged in it will leave with an appropriate error code. The method is asynchronous and will complete by calling the corresponding observer method.
Figure 6: Logging out
HandleLogoutL
method is implemented by the client. It
is called when LogoutL
is completed.
Example code:
void CSDKExample1AppUi::HandleLogoutL( const TInt aErrorCode ) { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::EShortTimeout ); TBuf<0x100> buf; buf.Format( _L("HandleLogoutL( %i )"), aErrorCode); note->ExecuteLD( buf ); }
By unregistering the connection observer all the connection related ongoing tasks are cancelled. It is advised that when login is ongoing or already logged in, first cancel the login, or logout before unregistering the connection observer.
Figure 7: Unregistering the connection observer
Creating the MImClient
interface is needed when the user
wants to send and receive instant messages. After the creation is successful
the user has to register its IM observer. Without registering that observer
it is not even possible to send a point-to-point message because the notification
about the sending is delivered through the HandleMessageSentL()
observer
method.
Figure 8: Creating IM client and registering the observer
The CreateImClientL
method creates the IM Client interface.
A pointer is returned to the object. It is the client’s responsibility to
destroy the object.
Example code:
void CSDKExample1AppUi::TestCreacteImClientL() { TRAPD(err,iMyImClient = iMyImConnection->CreateImClientL()); // Failed to create IM Interface if ( err != KErrNone ) { CAknErrorNote* note = new(ELeave) CAknErrorNote(); note->ExecuteLD(_L("No IM Interface!")); } }
Before using the methods in the MImClient
interface, the
observer must be registered successfully to IM API using the RegisterObserverL
method.
Example code:
void CSDKExample1AppUi::TestRegisterImObserverL() { TRAPD(err, iMyImClient->RegisterObserverL( this ) ); if( err != KErrNone ) { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::ELongTimeout ); TBuf<0x100> buf; buf.Format( _L("Registering failed with %d"),ret ); note->ExecuteLD( buf ); } }
Sending a point-to-point message is possible by using a user ID (e.g. WV
user ID) or contact model ID from the contacts DB. In the latter case the
IM API translates the recipient’s address to the corresponding protocol user
ID. In case the contact ID given in the method does not have a user ID in
its fields, the error code KImApiErrInvalidContactId
is returned
by a leave mechanism.
In case there are more contact IDs in the recipient list and for example
one does not have a user ID, this one is silently removed from the list and
a message sending is attempted to the other recipients. In case the recipient
was addressed directly with a user ID the validity of the ID is checked only
in the remote IMPS server. Therefore the error code can be delivered only
to the observer object by the HandleSendErrorL()
method.
For more information on error handling, see Section Error
handling
It is not possible to send several point-to-point messages in the same
active object scheduling round, e.g. calling SendPToPMessageL()
several
times one after another in the same function, due to some limitations in the
protocol stack. That kind of usage leads to the error KErrServerBusy
.
Figure 9: Sending a point-to-point message
The SendPToPMessageL
method has two prototypes. Textual
instant messages can be sent using contact model IDs or directly a user ID.
This method is asynchronous and HandleMessageSentL()
or HandleSendErrorL()
is
called when completed.
This method returns an operation ID. Please note that only one message
can be sent during an active object scheduling round, which means for example
that the SendPToPMessageL
method cannot be called subsequently
in one function.
Example code:
void CSDKExample1AppUi::TestSendPToPMessageContactL() { // Select contact from the Contacts Model _LIT(KWVStart, ""); // Specify in which fields search should be performed CContactItemFieldDef* fieldToSearchIn = new (ELeave) CContactItemFieldDef(); CleanupStack::PushL( fieldToSearchIn ); fieldToSearchIn->AppendL( KUidContactFieldVCardMapWV ); // search in contact database CContactIdArray *myContactArray = iMyContacts->FindLC(KWVStart, fieldToSearchIn ); // Call the method TRAPD(err, iMyImClient->SendPToPMessageL(*myContactArray, _L("Hi there!")); if ( err != KErrNone ) { CAknErrorNote* note = new(ELeave) CAknErrorNote(); note->ExecuteLD(_L("Sending failed")); } CleanupStack::PopAndDestroy( 2 ); // fieldToSearchn, myContactArray }Example code of sending with user IDs:
void CSDKExample1AppUi::TestSendPToPMessageContactL() { CDesCArray* userIds = new(ELeave) CDesCArrayFlat(4); CleanupStack::PushL(userIds); userIds->AppendL(_L("wv:[email protected]")); userIds->AppendL(_L("wv:[email protected]")); userIds->AppendL(_L("wv:[email protected]")); userIds->AppendL(_L("wv:[email protected]")); // Call the method TRAPD(err, iMyImClient->SendPToPMessageL(*userIds, _L("Hi there!")); if ( err != KErrNone ) { CAknErrorNote* note = new(ELeave) CAknErrorNote(); note->ExecuteLD(_L("Sending failed")); } CleanupStack::PopAndDestroy( ); // userIds }
HandleMessageSentL()
is implemented by client and it is
called when SendPToPMessageL
is completed.
Example code:
void CSDKExample1AppUi:: HandleMessageSentL(const TInt aOpCode, const TInt aErrorCode) { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::EShortTimeout ); TBuf<0x100> buf; buf.Format( _L("HandleMessageSentL( %i )"), aErrorCode ); note->ExecuteLD( buf ); }
HandleSendErrorL
method is implemented by the client.
It is called when SendPToPMessageL
is completed with an error.
If the error is KImApiErrPartialSuccess
the detailed list
of failed user IDs can be fetched using the MImClientDetailedError
interface
.
Example code:
void CSDKExample1AppUi:: HandleSendErrorL(const TInt aOpCode, const TInt aErrorCode, MImClientDetailedError* aDetailedError); { CAknInformationNote* note = new (ELeave) CAknInformationNote( EFalse ); note->SetTimeout( CAknNoteDialog::EShortTimeout ); TBuf<0x100> buf; buf.Format( _L("HandleSendErrorL( %i )"), aErrorCode ); note->ExecuteLD( buf ); if ( aDetailedError ) { _LIT(KMessage, "Failed contacts:"); TPtrC ptr(KMessage); CAknMessageQueryDialog* dlg = CAknMessageQueryDialog::NewL(ptr); CleanupStack::PushL(dlg); // << dlg dlg->PrepareLC(R_MESSAGE_QUERY); dlg->QueryHeading()->SetTextL(_L("Failed contacts")); buf.FillZ(); // iterate through the failed users list for(TInt i(0); i < aDetailedError->Count(); ++i) { // read the ith user buf.AppendFormat(_L("%S %d:\n"), & aDetailedError>UserId(i), aDetailedError->ErrorCode(i)); } dlg->SetMessageTextL(buf); dlg->RunLD(); CleanupStack::Pop(); // >> dlg }
New point-to-point messages are delivered to the IM observer method HandleNewPToPMessage()
.
Figure 10: Receiving a point-to-point message
Example code:
void CSDKExample1AppUi::HandleNewPToPMessageL( const TInt aErrorCode, const TContactItemId aContactId, const TDesC& aUserId, const TDesC& aMessageType, const TDesC16& aContent ) { TBuf<0x100> buf; _LIT(KMessage, "New Message"); TPtrC ptr(KMessage); CAknMessageQueryDialog* dlg = CAknMessageQueryDialog::NewL(ptr); dlg->PrepareLC(R_MESSAGE_QUERY); // Show the sender user buf.AppendFormat(_L("from %S"), & aUserId); dlg->QueryHeading()->SetTextL(buf); buf.FillZ(); // show the message itself if( aMessageType.Compare(_L("text/plain")) == 0 ) dlg->SetMessageTextL(aContent); else { buf.Append(_L("Message content cannot be showed!")); dlg->SetMessageTextL(buf); } dlg->RunLD(); }
In case that one or more user IDs were incorrect for some reason KImApiErrPartialSuccess
is
returned in the HandleSendErrorL()
observer method. In that
case the user is able to fetch the failure reasons for each failed user ID.
The user IDs are not converted back to contact IDs if the contact IDs were
used in sending the instant message.
Figure 11: Receiving partial success
By unregistering the observer all the pending requests are cancelled.
Figure 12: Unregistering the IM observer