diff -r 000000000000 -r 094583676ce7 wvuing/wvuiprocess/Src/CCAMessageFlowHandlerPC.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wvuing/wvuiprocess/Src/CCAMessageFlowHandlerPC.cpp Thu Dec 17 08:41:52 2009 +0200 @@ -0,0 +1,512 @@ +/* +* Copyright (c) 2006 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: Flow controller for messages +* +*/ + + +// INCLUDE FILES +#include + +#include "CCAMessageFlowHandlerPC.h" +#include "ChatDebugAssert.h" +#include "MCAMessageContainer.h" +#include "MCAMessagesReadInterface.h" +#include "MCASettings.h" +#include "ChatDebugPrint.h" +#include "imutils.h" +#include "imnginternalcrkeys.h" +#include "imprivatecrkeys.h" +#include "imvariant.hrh" +#include "CCARecordedChatsPC.h" +#include "CCAProcessManagerFactory.h" +#include "CCAConversationMessage.h" +#include "CCAEngine.h" +#include "MCAProcessManager.h" +#include "MCARecordedChatsPC.h" + + +// CONSTANTS +const TInt KMilliToMicro = 1000; // multiplier for converting milliseconds to microseconds + +const TInt KTimeIntervalSlow = 2000; // slowest message speed (in milliseconds) +const TInt KTimeIntervalFast = 200; // fastest message speed (in milliseconds) + +const TInt KTimeIntervalOpen = 0; // message fetching speed when opening the view +const TInt KInitialMessages = 3; // fetch n messages at once when opening the view + +const TInt KSettingValueMin = 1; // minimum value for flow control setting +const TInt KSettingValueMax = 3; // maximum value for flow control setting + + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::CCAMessageFlowHandlerPC +// C++ default constructor can NOT contain any code, that +// might leave. +// ----------------------------------------------------------------------------- +// +CCAMessageFlowHandlerPC::CCAMessageFlowHandlerPC( MCAMessageContainer& aMessageContainer, + MCAMessagesReadInterface& aReadInterface, + MCASettings& aSettings ) + : CTimer( EPriorityStandard ), + iMessages( aMessageContainer ), + iReadInterface( aReadInterface ), + iSettings( aSettings ), + iFetchMessages( ETrue ) + + { + CActiveScheduler::Add( this ); + } + + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandler::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CCAMessageFlowHandlerPC* CCAMessageFlowHandlerPC::NewL( + MCAMessageContainer& aMessageContainer, + MCAMessagesReadInterface& aReadInterface, + MCASettings& aSettings, + TBool aRecordedChatHandler ) + { + CCAMessageFlowHandlerPC* self = new( ELeave ) CCAMessageFlowHandlerPC( + aMessageContainer, + aReadInterface, + aSettings ); + + CleanupStack::PushL( self ); + self->ConstructL( aRecordedChatHandler ); + CleanupStack::Pop( self ); + + return self; + } +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::ConstructL( TBool aRecordedChatHandler ) + { + // construct base class + CTimer::ConstructL(); + + // and observe changes + iReadInterface.SetObserver( this ); + + // fetch flow control value from settings + UpdateTimeIntervalL(); + + // and observe + iSettings.AddObserverL( this ); + + // Check CR variation + if ( aRecordedChatHandler ) + { + iBgOpeningMode = EFalse; + } + else + { + iBgOpeningMode = IMUtils::CRKeyL( KCRUidIMVariation, KIMVariationKey ) + & EIMFeatBackgroundGroupOpening; + } + + // start the timer if there are messages + iInitialMsgCount = iReadInterface.MessageCount(); + if ( iInitialMsgCount + iReadInterface.UnreadCount() > 0 ) + { + // lock the buffer + iReadInterface.Lock( ETrue ); + + // use faster timer when constructing + iTimeInterval = KTimeIntervalOpen * KMilliToMicro; + After( iTimeInterval ); + } + + + } + + +// Destructor +CCAMessageFlowHandlerPC::~CCAMessageFlowHandlerPC() + { + + if ( !iChatDeleted ) + { + iReadInterface.SetObserver( NULL ); + iReadInterface.Lock( EFalse ); + } + iSettings.RemoveObserver( this ); + Cancel(); + + } + + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::FetchMessages +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::FetchMessages( TBool aFetch ) + { + TBool reallyInBg = + CCoeEnv::Static()->RootWin().OrdinalPosition() == 0 ? EFalse : ETrue; + + if ( !reallyInBg && !aFetch ) + { + // Application is not really in background, this can happen + // if key lock is activated while application is in foreground + aFetch = ETrue; + } + + iFetchMessages = aFetch; + if ( iFetchMessages && !iChatDeleted ) + { + // we're allowed to fetch messages again + if ( iReadInterface.UnreadCount() > 0 && !IsActive() ) + { + // there are some unread messages + // => start the timer (if not active already) + After( iTimeInterval ); + } + } + else if ( iMsgIndex < iInitialMsgCount ) + { + Cancel(); + After( iTimeInterval ); + return; + } + else + { + // we're not allowed to fetch new messages any more, so cancel the timer + Cancel(); + } + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::HandleMessageEvent +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::HandleMessageEvent( TMessageEventType aEvent, + TInt aIndex ) + { + CHAT_DP( D_CHAT_LIT( "CCAMessageFlowHandlerPC::HandleMessageEvent event %d, \ + index %d" ), aEvent, aIndex ); + switch ( aEvent ) + { + case ENewMessage: + { + if ( !IsActive() && iFetchMessages ) + { + // if not active, start timer + After( iTimeInterval ); + } + + if ( IsOpening() && iBgOpeningMode ) + { + // New message during opening phase + iNewMsgWhileOpening = ETrue; + } + + // otherwise do nothing as timer fetches the messages + break; + } + case EMessageDeleted: + { + TInt corIndex = MessageIndexCorrection( aIndex ); + iMessages.RemoveMessage( corIndex ); + break; + } + case EMessageChanged: + { + TInt corIndex = MessageIndexCorrection( aIndex ); + TRAPD( err, iMessages.MessageChangedL( corIndex ) ); + if ( err != KErrNone ) + { + CActiveScheduler::Current()->Error( err ); + } + break; + } + case EChatDeleted: + { + iChatDeleted = ETrue; + iMessages.MarkDeleted(); + break; + } + default: + { + // not supported + __CHAT_ASSERT_DEBUG( EFalse ); + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::RunL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::RunL() + { + TBool messages( ETrue ); + TBool opening = IsOpening(); + + // If we're opening the view, fetch KInitialMessages at a time, otherwise just one + TInt count( opening ? KInitialMessages : 1 ); + + while ( count-- > 0 && messages ) + { + if ( iBgOpeningMode && opening ) + { + // Opening the chat with background message fetching. + // Perform steps in following order: + // 1. Add possible new messages that are received during + // the opening phase to the end of chat normally + // 2. Insert unread messages to the beginning of chat + // 3. Insert read messages to the beginning + if ( iNewMsgWhileOpening && iFetchMessages ) + { + // Add the new message now + CCAConversationMessage* msg = CCAConversationMessage::NewL( + iReadInterface.ReadNextUnread() ); + + CleanupStack::PushL( msg ); + iMessages.AddMessageL( *msg ); + CleanupStack::Pop( msg ); + + iNewMsgWhileOpening = EFalse; + } + else if ( iReadInterface.UnreadCount() > 0 && iFetchMessages ) + { + // Insert unread messages, insert in last-to-first order + iAddedUnreadMsgs++; + + CCAConversationMessage* msg = CCAConversationMessage::NewL( + iReadInterface.ReadUnreadFromIndex( + iReadInterface.MessageCount() + + iReadInterface.UnreadCount() + - iAddedUnreadMsgs ) ); + + CleanupStack::PushL( msg ); + iMessages.InsertMessageL( *msg ); + CleanupStack::Pop( msg ); + + } + else if ( iMsgIndex < iInitialMsgCount ) + { + // Insert unread messages, in last-to-first order + iAddedUnreadMsgs = 0; // Init to zero + TInt index = iInitialMsgCount - 1 - iMsgIndex; + CCAConversationMessage* msg = CCAConversationMessage::NewL( + iReadInterface.Message( index ) ); + + CleanupStack::PushL( msg ); + iMessages.InsertMessageL( *msg ); + CleanupStack::Pop( msg ); + + iMsgIndex++; + } + else + { + // Check if the initial speed was active + if ( opening ) + { + UpdateTimeIntervalL(); + } + messages = EFalse; + iReadInterface.Lock( EFalse ); + } + } + else + { + // Functionality in opening in releases 3.1 and earlier + // and normal functionality when the chat is already fully opened + // and new messages are received. + // 1. Add read messages in first-to-last order + // 2. Add unread messages in first-to-last order + if ( iMsgIndex < iInitialMsgCount ) + { + // Add read messages + CCAConversationMessage* msg = CCAConversationMessage::NewL( + iReadInterface.Message( iMsgIndex++ ) ); + CleanupStack::PushL( msg ); + iMessages.AddMessageL( *msg ); + CleanupStack::Pop( msg ); + + } + else if ( iReadInterface.UnreadCount() > 0 ) + { + // Add unread messages + CCAConversationMessage* msg = CCAConversationMessage::NewL( + iReadInterface.ReadNextUnread() ); + CleanupStack::PushL( msg ); + iMessages.AddMessageL( *msg ); + CleanupStack::Pop( msg ); + } + else + { + // Check if the initial speed was active + if ( opening ) + { + UpdateTimeIntervalL(); + } + messages = EFalse; + iReadInterface.Lock( EFalse ); + } + } + } + + // And restart timer if needed + if ( messages ) + { + Cancel(); + After( iTimeInterval ); + } + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::RunError +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TInt CCAMessageFlowHandlerPC::RunError( TInt aError ) + { + CHAT_DP( D_CHAT_LIT( "CCAMessageFlowHandlerPC::RunError (%d)" ), aError ); + + // Something leaved in RunL + if ( aError == KErrNoMemory ) + { + // inform user about low memory + CActiveScheduler::Current()->Error( aError ); + } + + if ( IsActive() ) + { + // stop processing messages + Cancel(); + } + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::HandleSettingsChangeL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::HandleSettingsChangeL( TInt aChangedSettingEnum ) + { + // code scanner warning can be ignored + // non-leaving function in leavining function + switch ( aChangedSettingEnum ) + { + case MCASettings::EMessageFlowSettingLevel: + { + UpdateTimeIntervalL(); + // no need to restart the timer even if we're running, + // because it will get updated after next message + break; + } + default: + { + // no need to react to other changes + break; + } + } + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::UpdateTimeIntervalL +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCAMessageFlowHandlerPC::UpdateTimeIntervalL() + { + // codescanner warning can be ignored + TInt flowSetting( iSettings.Value( + MCASettings::EMessageFlowSettingLevel ) ); + + // flowSetting is from KSettingValueMin to KSettingValueMax + TInt range( KSettingValueMax - KSettingValueMin ); + TInt newRange( KTimeIntervalFast - KTimeIntervalSlow ); + + // convert it to a range from KTimeIntervalSlow to KTimeIntervalFast + TInt flowSpeed( ( flowSetting - KSettingValueMin )*newRange / range ); + + // update the end point + flowSpeed += KTimeIntervalSlow; + + // validate the result + if ( flowSpeed > KTimeIntervalSlow ) + { + flowSpeed = KTimeIntervalSlow; + } + + if ( flowSpeed < KTimeIntervalFast ) + { + flowSpeed = KTimeIntervalFast; + } + + // and convert from milliseconds to microseconds + iTimeInterval = flowSpeed * KMilliToMicro; + } + +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandlerPC::IsOpening +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCAMessageFlowHandlerPC::IsOpening() const + { + return iTimeInterval == + TTimeIntervalMicroSeconds32( KTimeIntervalOpen * KMilliToMicro ); + } +// ----------------------------------------------------------------------------- +// CCAMessageFlowHandler::MessageIndexCorrection +// ----------------------------------------------------------------------------- +// +TInt CCAMessageFlowHandlerPC::MessageIndexCorrection( TInt aIndex ) const + { + if ( !IsOpening() || !iBgOpeningMode ) + { + // if not opening chat or message insertion is not used + // => then index is already correct + return aIndex; + } + + // We need to calculate correct index + TInt index = -1; + if ( iAddedUnreadMsgs > 0 ) + { + // we have only added some unread messages + TInt totalCount = iReadInterface.MessageCount() + + iReadInterface.UnreadCount(); + index = aIndex - ( totalCount - iAddedUnreadMsgs ); + } + else + { + // added some read messages + index = aIndex - ( iInitialMsgCount - 1 - iMsgIndex ); + } + + CHAT_DP( D_CHAT_LIT( "CCAMessageFlowHandler::MessageIndexCorrection \ + old index: %d, corrected index: %d" ), aIndex, index ); + return index; + } + +// End of File