wvuing/wvuiprocess/Src/CCAMessageFlowHandlerPC.cpp
changeset 0 094583676ce7
--- /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    <coemain.h>
+
+#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