IM API: Using IM API

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 the connection and registering the observer

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 );
    }

Logging in

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 );
    }

Canceling the ongoing login

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 );
    }

Logging out

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 );
    }

Unregistering the connection observer

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 IM client and registering the 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

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:

Example code of sending with contact IDs
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
        }

Receiving a point-to-point message

New point-to-point messages are delivered to the IM observer method HandleNewPToPMessage().

Figure 10: Receiving a point-to-point message

Example code:

Example code for textual messages
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();
}

Receiving partial success

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

Unregistering the IM observer

By unregistering the observer all the pending requests are cancelled.

Figure 12: Unregistering the IM observer


Copyright © Nokia Corporation 2001-2008
Back to top