emailservices/emailserver/cmailhandlerplugin/src/fsnotificationhandlerbaseimpl.cpp
author Simon Howkins <simonh@symbian.org>
Thu, 25 Nov 2010 12:13:04 +0000
branchRCL_3
changeset 83 31a5fbf5db1d
parent 80 726fba06891a
permissions -rw-r--r--
Adjusted to avoid exports, etc, from a top-level bld.inf

/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: This file implements class CFSNotificationHandlerBase.
*
*/

#include <centralrepository.h>
#include "emailtrace.h"
#include "cfsmailclient.h"

#include "fsnotificationhandlermgr.h"
#include "fsnotificationhandlerbase.h"
#include "cmailhandlerpluginpanic.h"
#include "commonemailcrkeys.h"
#include "freestyleemailcenrepkeys.h"
#include "FreestyleEmailUiConstants.h"


const TInt KTimerDelay = 20;

// ======== MEMBER FUNCTIONS ========

CFSNotificationHandlerBase::CFSNotificationHandlerBase(
    MFSNotificationHandlerMgr& aOwner ) :
    iOwner( aOwner ),
    iObserving( EFalse )
    {
    FUNC_LOG;
    }

void CFSNotificationHandlerBase::ConstructL()
    {
    FUNC_LOG;
    
    iTimer = CNewMailNotificationTimer::NewL( *this );
    }

CFSNotificationHandlerBase::~CFSNotificationHandlerBase()
    {
    FUNC_LOG;
    REComSession::DestroyedImplementation( iDestructorKey );
    
    delete iTimer;
    iNewInboxEntries.Reset(); 
    }

CFSMailClient& CFSNotificationHandlerBase::MailClient() const
    {
    FUNC_LOG;
    return iOwner.MailClient();
    }
void CFSNotificationHandlerBase::EventL( TFSMailEvent aEvent, TFSMailMsgId aMailbox,
                                         TAny* aParam1, TAny* aParam2, TAny* aParam3 )
    {
    FUNC_LOG;
    if ( !iObserving )
        {
        return;
        }   
    
    HandleEventL( aEvent, aMailbox, aParam1, aParam2, aParam3 );
    }


    
void CFSNotificationHandlerBase::SetObserving( TBool aNewValue )
    {
    FUNC_LOG;
    iObserving = aNewValue;
    }

TBool CFSNotificationHandlerBase::MsgIsUnread( CFSMailMessage& aMessage ) const
    {
    FUNC_LOG;
    TBool read( aMessage.IsFlagSet( EFSMsgFlag_Read ) );
    TBool read_locally( aMessage.IsFlagSet( EFSMsgFlag_Read_Locally ) );
    
    if ( !read && !read_locally )
        {
        return ETrue;
        }
    else
        {
        return EFalse;
        }
    }

TBool CFSNotificationHandlerBase::MessagesCauseNotificationL( TFSMailMsgId aMailboxId,
                                                              TFSMailMsgId aParentFolderId,
                                                              const RArray<TFSMailMsgId>& aMsgIdList )
    {
    FUNC_LOG;

    CFSMailFolder* parentFolder(
        MailClient().GetFolderByUidL( aMailboxId, aParentFolderId ) );
    if ( !parentFolder )
        {
        // by some reason the folder could not be found..
        return EFalse;
        }
    CleanupStack::PushL( parentFolder );
    
    CFSMailMessage* newestMsg( NULL );
    TRAPD( notFoundError,
           newestMsg =
               NewestMsgInFolderL( *parentFolder ) );
    if ( notFoundError == KErrNotFound )
        {
        // For some odd reason we are not able to get the newest
        // message from the folder. This should not be possible
        // as we just received notification of a new message.
        // Possibly something has moved/deleted the message?
        return EFalse;
        }
    User::LeaveIfError( notFoundError );
    
    TTime dateOfNewest( newestMsg->GetDate() );

    delete newestMsg;
    newestMsg = NULL;

    CleanupStack::PopAndDestroy( parentFolder );
    
    const TInt entriesCount( aMsgIdList.Count() );
    TInt index( entriesCount-1 );
    // go from back of list, as messages are coming from earliest to latest..
    while ( index >= 0 ) 
        {
        // Let's get the message. We need to check from it that
        // it is really unread. This info is stored in the
        // flags. Also check that the message is newest.
        // EFSMsgDataEnvelope is used as TFSMailDetails 
        // so that we get enough data.
        CFSMailMessage*
            currentMessage( MailClient().GetMessageByUidL(
                aMailboxId, 
                aParentFolderId,
                aMsgIdList[index], 
                EFSMsgDataEnvelope ) );
        if ( currentMessage )
            {
            const TTime dateOfCurrentMsg( currentMessage->GetDate() );
            
            const TBool msgIsUnread( MsgIsUnread( *currentMessage ) );
            delete currentMessage;
            currentMessage = NULL;
            
            if ( msgIsUnread &&
                 ( dateOfCurrentMsg >= dateOfNewest ) )
                {
                // At least one of the messages is unread and newest.
                return ETrue;
                }
            }
            
        --index;
        }
    
    return EFalse;
    }

TBool CFSNotificationHandlerBase::Observing() const
    {
    FUNC_LOG;
    return iObserving;
    }

TBool CFSNotificationHandlerBase::CapabilitiesToContinueL(
    TFSMailEvent aEvent,
    TFSMailMsgId aMailbox,
    TAny* /*aParam1*/,
    TAny* /*aParam2*/,
    TAny* /*aParam3*/ ) const
    {
    FUNC_LOG;
    if ( aEvent != TFSEventMailboxDeleted )
        {
        CFSMailBox* mailBox( MailClient().GetMailBoxByUidL( aMailbox ) );
        if ( !mailBox )
            {
            User::Leave( KErrArgument );
            }

        if ( mailBox->HasCapability( EFSMBoxCapaNewEmailNotifications ) )
            {
            delete mailBox;
            return EFalse;
            }
        else
            {
            delete mailBox;
            return ETrue;
            }
        }
    else
        {
        return ETrue;
        }
    }

void CFSNotificationHandlerBase::HandleEventL(
    TFSMailEvent aEvent,
    TFSMailMsgId aMailbox,
    TAny* aParam1,
    TAny* aParam2,
    TAny* /*aParam3*/ )
    {
    FUNC_LOG;
    // Only event TFSEventNewMail means that the mail is completely new.
    if ( aEvent == TFSEventNewMail )
        {
        // In case of TFSEventNewMail we have parent folder id
        // in aParam2
        TFSMailMsgId* parentFolderId( NULL );
        parentFolderId = static_cast< TFSMailMsgId* >( aParam2 );
        if ( !parentFolderId )
            {
            User::Leave( KErrArgument );
            }

        // Set the notification on only in cases that the new mail is
        // in folder of type EFSInbox
        if ( iOwner.GetFolderTypeL( aMailbox, parentFolderId ) == EFSInbox )
            {
            RArray<TFSMailMsgId>* newEntries(
                static_cast< RArray<TFSMailMsgId>* >( aParam1 ) );

            TInt count = newEntries->Count();
            for ( TInt i = 0; i<count;i++ )
                {
                TFSMailMsgId msgId = newEntries->operator []( i );
                TNewMailInfo info( msgId, aMailbox, *parentFolderId ); 
                iNewInboxEntries.AppendL( info );
                }

            if (iTimer->IsActive() )
                {
                iTimer->Cancel();
                }
            iTimer->After( KTimerDelay );
            }
         else
            {
            // If messages are in some other folder than in inbox
            // they have no effect on the notification
            }
        }
    else
        {
        // No other events than new mail are handled. For example
        // moving of messages and changing message status has no
        // effect on the notification.
        }
    }

void CFSNotificationHandlerBase::TimerExpiredL()
    {
    // process collected insert requests
    RArray<TFSMailMsgId> msgIds;
    CleanupClosePushL( msgIds );

    TFSMailMsgId mailBoxId;
    TFSMailMsgId parentFolderId;
    for ( TInt i = 0; i< iNewInboxEntries.Count(); i++ )
        {
        TNewMailInfo& info = iNewInboxEntries[ i ];
        if ( mailBoxId.IsNullId() && parentFolderId.IsNullId() )
            {
            // starting new group is starting to collect
            mailBoxId = info.iMailBox;
            parentFolderId = info.iParentFolderId;
            }
        if ( mailBoxId == info.iMailBox && parentFolderId == info.iParentFolderId )
            {
            // collect message ids for the same mailbox and parent folder
            msgIds.Append( info.iMsgId );
            }
        else
            {
            // process collected message ids for the same mailbox and parent folder
            if ( msgIds.Count()&& MessagesCauseNotificationL( mailBoxId, parentFolderId, msgIds ) )
                {
                TurnNotificationOn();
                }
            // clear data and start collecting again
            msgIds.Reset();
            mailBoxId = TFSMailMsgId();
            parentFolderId = TFSMailMsgId();
            }
        }
    // process collected message ids for the same mailbox and parent folder
    if ( msgIds.Count() && MessagesCauseNotificationL( mailBoxId, parentFolderId, msgIds ) )
        {
        TurnNotificationOn();
        }
    // clear processed entries
    msgIds.Reset();
    CleanupStack::PopAndDestroy( &msgIds );
    iNewInboxEntries.Reset();    
    }

CFSMailMessage* CFSNotificationHandlerBase::NewestMsgInFolderL(
    CFSMailFolder& aFolder ) const
    {
    FUNC_LOG;
    // Load info only necessary for sorting by date into the messages.
    TFSMailDetails details( EFSMsgDataDate );

    // Want to sort mails so that the newest is in the beginning
    TFSMailSortCriteria criteriaDate;
    criteriaDate.iField = EFSMailSortByDate;
    criteriaDate.iOrder = EFSMailDescending;

    RArray<TFSMailSortCriteria> sorting;
    CleanupClosePushL( sorting );
    // First criteria appended would be the primary criteria
    // but here we don't have any other criteria
    sorting.Append( criteriaDate );
    MFSMailIterator* iterator = aFolder.ListMessagesL( details, sorting );
    
    // Resetting array of sort criteria already here because
    // the iterator does not need it anymore.
    CleanupStack::PopAndDestroy( &sorting );
                                    
    // CleanupStack::PushL doesn't work for M-class
    CleanupDeletePushL( iterator ); 
    
    RPointerArray<CFSMailMessage> messages;
    CleanupClosePushL( messages );
    // Let's get only the first and therefore the newest message.
    TInt amount( 1 );
    iterator->NextL( TFSMailMsgId(), amount, messages );    
    if ( messages.Count() < 1 )
        {
        messages.ResetAndDestroy();
        User::Leave( KErrNotFound );
        }
            
    CFSMailMessage* outcome = messages[0];
    messages.Remove( 0 ); // remove from array to prevent destruction of element
    messages.ResetAndDestroy();
    CleanupStack::PopAndDestroy( &messages ); 
    CleanupStack::PopAndDestroy( iterator );
    return outcome;
    }

CNewMailNotificationTimer::CNewMailNotificationTimer( MFSTimerObserver& aObserver ) :
    CTimer( EPriorityIdle ), iObserver( aObserver )
    {
    FUNC_LOG;
    }

void CNewMailNotificationTimer::ConstructL()
    {
    FUNC_LOG;
    CTimer::ConstructL();
    CActiveScheduler::Add( this );
    }

CNewMailNotificationTimer* CNewMailNotificationTimer::NewL(
        MFSTimerObserver& aObserver )
    {
    FUNC_LOG;
    CNewMailNotificationTimer* self =
        new( ELeave ) CNewMailNotificationTimer( aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


CNewMailNotificationTimer::~CNewMailNotificationTimer()
    {
    FUNC_LOG;
    Cancel();
    }

void CNewMailNotificationTimer::DoCancel()
    {
    FUNC_LOG;
    // Cancel Base class
    CTimer::DoCancel(); 
    }

void CNewMailNotificationTimer::RunL()
    {
    FUNC_LOG;
    iObserver.TimerExpiredL();
    }

TInt CNewMailNotificationTimer::RunError(TInt aError)
    {
    if( aError )
        {
        INFO_1( "CNewMailNotificationTimer::RunError( aError: %d )", aError );
        }
    
    return KErrNone;
    }

void Panic( TCmailhandlerPanic aPanic )
    {
    _LIT( KPanicText, "emailhandlerplugin" );
    User::Panic( KPanicText, aPanic );
    }

// End of file