Notes API: Using AVKON UI Notes API

Using a basic note

The following code creates a waiting confirmation note, displays it, and after a short delay dismisses it. Delay timer is predefined for all basic notes in avkon.hrh. Since the note is created as a waiting note, the ExecuteLD() function blocks the application execution. ExecuteLD() also deletes the note automatically.

// creating basic note
#include <aknnotewrappers.h>

_LIT( mConfText, "Picture deleted.");
CAknConfirmationNote *mConfNote;
mConfNote = new (ELeave) CAknConfirmationNote( ETrue );  // waiting
mConfNote->ExecuteLD( mConfText );  // display the note and delete it

Using a non-waiting basic note with self pointer

When a non-waiting note is used, ExecuteLD() returns immediately not blocking the application to do further processes. When a pointer reference is used, the note is not only deleted after execution but the pointer is also set to NULL.

class CNoteExampleAppUi : public CAknAppUi
    {
    private:
        CAknInformationNote *iInfNote;
    };

void CNoteExampleAppUi::HandleCommandL( TInt aCommand )
    {
    switch( aCommand )
        {
        case ECommandCreateInformationNote :
            {
            iInfNote = new (ELeave) CAknInformationNote( &iInfNote );
            iInfNote->ExecuteLD( _L("Not an integer. Try again.") );
            // note will be deleted automatically 
            // and pointer will be set to NULL after execution
            break;
            }
        default:
            break;
        }
    }

Do not create and execute another note until this one has completed, ActiveScheduler cannot handle this situation:

void CNoteExampleAppUi::ShowErrorNoteL()
    {
    if( iInfNote )  // note is on the screen
        {
        return;
        }
    // no note is shown
    CAknErrorNote* mErrorNote;
    mErrorNote = new (ELeave) CAknErrorNote( ETrue );
    mErrorNote->ExecuteLD( _L("General error.") );
    }

Using the Wait note

To use WaitNoteWrapper you have to define a note resource. Its structure is defined in eikon.rh and avkon.rh.

STRUCT DIALOG
    {
    LONG   flags = 0;
    LLINK  buttons = 0;
    STRUCT items[];     // STRUCT DLG_LINE
    }

STRUCT DLG_LINE
    {
    WORD   type;
    WORD   id = 0;
    STRUCT control;     // STRUCT AVKON_NOTE
    }

STRUCT AVKON_NOTE
    {
    WORD  layout;
    LTEXT singular_label;     // e.g. You have missed a call.
    LTEXT plural_label = "";  // e.g. You have %d missed calls.
    LTEXT imagefile = "";
    WORD  imageid   = 0xffff;
    WORD  imagemask = 0xffff;
    LTEXT iconfile  = "";
    WORD  iconid    = 0xffff;
    WORD  iconmask  = 0xffff;
    LLINK animation = 0;
    }

RESOURCE DIALOG r_myapp_waitnote
    {
    flags = EAknWaitNoteFlags;
    buttons = R_AVKON_SOFTKEYS_CANCEL;
    items =
        {
        DLG_LINE
            {
            type = EAknCtNote;
            id = EMyAppWaitNote;
            control = AVKON_NOTE
                {
                layout = EWaitLayout;
                singular_label = "Connecting, please wait.";
                imagefile = "z:\\system\data\avkon.mbm";
                imageid = EMbmAvkonQgn_note_progress;
                imagemask = EMbmAvkonQgn_note_progress_mask;
                animation = R_QGN_GRAF_WAIT_BAR_ANIM;
                };
            }
        };
    }

Wait note's background process is controlled by a MAknBackgroundProcess class. Its callback functions are called by the WaitNoteWrapper:

If the user does not interrupt the Wait note by pressing a key, ProcessFinishedL() is called before DialogDismissedL().

#include <aknwaitnotewrapper.h>

class CMyProcess : public CBase, public MAknBackgroundProcess
    {
    public:
        TBool IsProcessDone() const;
        void StepL();
        // TInt CycleError( TInt aError );
        // void DialogDismissedL( TInt aButtonId );
        // void ProcessFinishedL();

    private:
        TInt iCount;
    };

Here we implement a simple strategy to maintain the wait note. It will be shown for 5 seconds, so 5 rounds of stepping is done:

TBool CMyProcess::IsProcessDone() const
    {
    return ( iCount == 5 );
    }

void CMyProcess::StepL()
    {
    sleep( 1 );  // do nothing for 1 second
    iCount++;    // next cycle
    }

Using the note:

// create background process
CMyProcess *myProcess = new (ELeave) CMyProcess();
CleanupStack::PushL( myProcess );

// create WaitNoteWrapper
CAknWaitNoteWrapper *waitNote = CAknWaitNoteWrapper::NewL();
// CAknWaitNoteWrapper is privately derived from CActive/CBase
// therefore a reinterpret_cast must be performed here
CleanupStack::PushL( reinterpret_cast<CBase*> ( waitNote ) );

// shows the note immediately
TBool success = 
    waitNote->ExecuteL( R_MYAPP_WAITNOTE, *myProcess, ETrue ) )

CleanupStack::PopAndDestroy( waitNote );
CleanupStack::PopAndDestroy( myProcess );

// further processes based on the note's exit code
if( success )
    {
    InitializeConnection();
    }
else  // note was cancelled
    {
    CancelConnection();
    }

Using the Progress note

A similar resource can be used to create a Progress note:

RESOURCE DIALOG r_myapp_progressnote
    {
    flags = EAknProgressNoteFlags;
    buttons = R_AVKON_SOFTKEYS_CANCEL;
    items =
        {
        DLG_LINE
            {
            type = EAknCtNote;
            id = EMyAppProgressNote;
            control = AVKON_NOTE
                {
                layout = EProgressLayout;
                singular_label = “Resizing image”;
                imagefile = “\\system\data\avkon.mbm”;
                imageid = EMbmAvkonQgn_note_progress;
                imagemask = EMbmAvkonQgn_note_progress_mask;
                };
            }
        };
    }

Progress note does not have a wrapper class around the note object as Wait note does. Therefore the note has to be controlled directly by the client. This means two things: when and how the progress bar is updated. In most cases it means a data-based operation calculation, e.g. uploading a file is a connection dependent process, converting an image to another format might be a hardware chipset-variant operation, etc.

Here we use a simple timer-based active object for the progress bar calculation. It is a safe design decision to encapsulate the active object, the dialog and the callback provide mechanism (MProgressDialogCallback) into one class. Another design issue is the usage of the class. Here we used the "fire and forget" method:

#include <myapp.rsg>
// create a waiting note
CProgressProcessor* iProcessor = CProgressProcessor::NewL(
    &iProcessor, R_MYAPP_PROGRESSNOTE, ETrue );
// run
iProcessor->ExecuteLD();
// do nothing, ExecuteLD() deletes the objects automatically

Based on this the class definition looks like this:

#include <aknprogressdialog.h>

class CProgressProcessor: public CActive, public MProgressDialogCallback
    {
    public:  // constructors, destructor

        // aResource: the progress note's resource id
        // aWaiting: waiting note or non-waiting
        // aVisible: ETrue if note has to be shown immediately
        static CProgressProcessor* NewL(
            CProgressProcessor** aSelfPtr,
            TInt aResource,
            TBool aWaiting,
            TBool aVisible = EFalse );

        virtual ~CProgressProcessor();

    public:  // new functions
        void ExecuteLD();

    private:  // from CActive
        void RunL();
        void DoCancel();
        TInt RunError( TInt aError );

    public:  // from MProgressDialogCallback
        void DialogDismissedL( TInt aButtonId );

    protected:  // construction
        CProgressProcessor();
        void ConstructL( TInt aResource, TBool aWaiting, TBool aVisible );

    private:  // new functions
        TBool IsProcessDone();  // finished?
        void DoProcess();  // perform next step
        void DoDelete();  // delete the object

    private:  // data
        CAknProgressDialog* iDialog;
        TInt iCounter;
        CProgressProcessor** iSelfPtr;
        RTimer iTimer;
        TBool iDestroy;
        CActiveSchedulerWait* iWait;  // for waiting note
    };

There is nothing special in the constructors and destructor. The second phase constructor creates the dialog and initializes the progress bar.

CProgressProcessor::ConstructL( TInt aResource, TBool aWaiting, TBool aVisible )
    {
    User::LeaveIfError( iTimer.CreateLocal() );

    // length of the process is unknown (in time) but it can be calculated
    iDialog = new (ELeave) CAknProgressDialog( 
        reinterpret_cast<CEikDialog**>(&iDialog), aVisible );
    iDialog->PrepareLC( aResource );
    CleanupStack::Pop();
    iDialog->SetCallback( this );

    // how "long" is the process in steps
    // here we use a percentage value
    CEikProgressInfo* progressInfo = iDialog->GetProgressInfoL();
    progressInfo->SetFinalValue( 100 );

    if( aWaiting )
        iWait = new (ELeave) CActiveSchedulerWait();
    CActiveScheduler::Add( this );
    }

CProgressProcessor::~CProgressProcessor()
    {
    iTimer.Close();
    // if note is not deleted yet
    if( iDialog )
        {
        iDialog->SetCallback( NULL );
        TRAP_IGNORE( iDialog->ProcessFinishedL() );
        }
    *iSelfPtr = NULL;  // set self pointer in parent object to NULL
    }

The process status functions are very simple:

TBool CProgressProcessor::IsProcessDone()
    {
    // 100 percent reached?
    return iCounter>=100;
    }

void CProgressProcessor::DoProcess()
    {
    iCounter++;
    iTimer.After( iStatus, 300000 );  // wait 0.3 seconds
    SetActive();  // and call RunL()
    }

The public function to activate the note:

void CProgressProcessor::ExecuteLD()
    {
    CleanupStack::PushL( this );
    iDialog->RunLD();  // shows the dialog, returns immediately
    DoProcess();  // start background process
    if( iWait )
        {
        // start waiting process, it blocks further execution here
        iWait->Start();
        // after it finishes, cleanup everything
        CleanupStack::PopAndDestroy();
        }
    else
        {
        // non-waiting dialog, so return immediately
        // delete will be performed by RunL() or DoCancel()
        CleanupStack::Pop();
        }
    }

Safe object delete:

void CProgressProcessor::DoDelete()
    {
    if( iWait )  // waiting note?
        CAknEnv::StopSchedulerWaitWithBusyMessage( *iWait );
    else
        delete this;
    }

Active object deletes itself if it was requested (via an error) or process has been finished; or perform next step.

// one step was done
void CProgressProcessor::RunL()
    {
    // any delete operation requested meanwhile?
    if( iDestroy )
        {
        DoDelete();
        }

    if( IsProcessDone() )
        {
        iDialog->SetCallback( NULL );
        iDialog->ProcessFinishedL();
        DoDelete();
        }
    else  // do next step, update progress bar
        {
        DoProcess();
        iDialog->GetProgressInfoL()->IncrementAndDarw( 1 );
        }
    }

// RunL() did leave
TInt CProgressProcessor::RunError( TInt /* aError */ )
    {
    iDialog->SetCallback( NULL );
    TRAP_IGNORE( iDialog->ProcessFinishedL() );

    iDestroy = ETrue;
    iTimer.Cancel();
    iTimer.Start( iStatus, 10 );  // call RunL() immediately
    SetActive();

    // all errors are swallowed
    return KErrNone;
    }

// someone uses the object in a not proper way?
void CProgressProcessor::DoCancel()
    {
    if( iDialog )
        {
        // no function callback needed for ProcessFinishedL()
        iDialog->SetCallback( NULL );
        TRAP_IGNORE( iDialog->ProcessFinishedL() );
        }
    iDestroy = ETrue;  // just in case
    iTimer.Cancel();
    // object is NOT deleted here
    }

If the dialog was dismissed, delete this object too:

void CProgressProcessor::DialogDismissedL( TInt /* aButton */ )
    {
    iDialog = NULL;
    DoDelete();
    }

Using Global notes

Following code shows the simplest way of using the Global note:

// resource definition
RESOURCE TBUF r_myapp_globalnote_searching { buf = "Searching..."; }

#include <aknnotifystd.h>  // note type
#include <myapp.rsg>  // application resource

const TInt KGlobalNoteMaxLength = 32;

// allocate TBuf with constant length, and fill it with a resource text
TBuf<KGlobalNoteMaxLength> text( NULL );
CEikonEnv::Static()->ReadResourceL( text, R_MYAPP_GLOBALNOTE_SEARCHING );
TPtrC noteText( text );

// create GlobalNote instance and show it.
CAknGlobalNote* globalNote = CAknGlobalNote::NewLC();
TInt noteId = globalNote->ShowNoteL( EAknGlobalInformationNote, noteText );

// do anything here
sleep( 5 );  // waiting for 5 seconds

// discard note
globalNote->CancelNoteL( noteId );
CleanupStack::PopAndDestroy();

Using Global note with user interaction

It is also possible to wait for user action when softkeys are attached to the Global note. This note is an asynchronous UI element, so an active object has to be implemented to receive the notification about user's choice.

Here we define an interface class as well to handle user interactions and errors.

// callback interface for global note handler
class MGlobalNoteClient
    {
    public:
        virtual void HandleCommandL( TInt aCommand ) = 0;
        virtual TInt NoteError( TInt aError ) = 0;
    };

// define the global note handler as an active object
class CGlobalNoteHandler: public CActive
    {
    public:  // constructors, destructor, etc.

    public:  // new function
        // aResource: resource identifier to shown
        void ShowNoteL(
            TInt aResource, 
            const TDesC& aText, 
            MGlobalNoteClient*& aClient );

    protected:  // from CActive
        void RunL();
        void DoCancel();
        TInt RunError( TInt aError );

    private:  // data
        CAknGlobalNote *iGlobalNote;
        TInt iNoteId;
        MGlobalNoteClient* iNoteClient;
    };

// active object initialization
CGlobalNoteHandler::CGlobalNoteHandler() : CActive( EPriorityNormal )
    {
    CActiveScheduler::Add( this );
    }

// showing the note
void CGlobalNoteHandler::ShowNoteL(
        TInt aResource,
        const TDesC& aText, 
        MGlobalNoteClient*& aClient )
    {
    if( iGlobalNote )
        return;  // do not do anything if a note is already shown
    iGlobalNote = CAknGlobalNote::NewL();
    iGlobalNote->SetSoftkeys( aResource );
    iNoteId = iGlobalNote->ShowNoteL( iStatus, 
        EAknGlobalConfirmationNote, aText );
    SetActive();
    iNoteClient = aNoteClient;
    }

// handling user interaction
void CGlobalNoteHandler::RunL()
    {
    // cancel note operation
    iGlobalNote->CancelNoteL( iNoteId );
    delete iGlobalNote;
    iGlobalNote = NULL;
    iNoteId = 0;

    TInt value( iStatus.Int() );  // user response
    User::LeaveIfError( value );

    // handle user response
    iNoteClient->HandleCommandL( value );
    }

// error occurs in RunL()
TInt CGlobalNoteHandler::RunError( TInt aError )
    {
    // cancel note operation
    TRAP_IGNORE( iGlobalNote->CancelNoteL( iNoteId ) );
    delete iGlobalNote;
    iGlobalNote = NULL;
    iNoteId = 0;

    return iNoteClient->NoteError( aError );  // all errors are passed to client
    }

// system cancelled the note
void CGlobalNoteHandler::DoCancel()
    {
    // cancel note operation
    TRAP_IGNORE( iGlobalNote->CancelNoteL( iNoteId ) );
    delete iGlobalNote;
    iGlobalNote = NULL;
    iNoteId = 0;
    }

Using custom notes

There are cases when the predefined notes just are not enough. You might, for example, want to change the icon displayed in the note, use a different kind of sound, or change the note duration. All of the properties can be changed via CAknNoteDialog's setter functions: SetTimeout, SetTone, SetTextWrapping, SetTextProcessing, SetImageL, SetIconL.

Error handling

AVKON UI Notes API uses standard Symbian OS error reporting mechanism. Leaves and system wide error codes as function return values are used if the error is recoverable. A client application can handle these errors similarly as a normal Symbian platform application.

Limitations of the API

None.


Copyright © Nokia Corporation 2001-2008
Back to top