--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingapp/msgappfw/server/src/ccsconversationcachehelper.cpp Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,733 @@
+/*
+ * 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 class helps CS Cache in processing the data.
+ *
+ */
+
+// INCLUDE FILES
+// SYSTEM INCLUDE FILES
+#include <ccsdefs.h>
+#include <ccsconversationentry.h>
+#include <ccsclientconversation.h>
+
+// USER INCLUDE FILES
+#include "ccsconversationcachehelper.h"
+#include "ccsconversationcache.h"
+#include "ccsconversation.h"
+#include "ccsconversationentry.h"
+#include "ccsconversationevent.h"
+#include "ccscontactsmanager.h"
+#include "ccscontactsresolver.h"
+#include "s60qconversions.h"
+#include "ccsdebug.h"
+
+// ============================== MEMBER FUNCTIONS ============================
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::NewL
+// Two Phase Construction
+// ----------------------------------------------------------------------------
+CCsConversationCacheHelper*
+CCsConversationCacheHelper::NewL(CCsConversationCache* aConversationCache)
+{
+ PRINT ( _L("Enter CCsConversationCacheHelper::NewL") );
+
+ CCsConversationCacheHelper* self =
+ new (ELeave) CCsConversationCacheHelper(aConversationCache);
+
+ CActiveScheduler::Add(self);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+
+ PRINT ( _L("End CCsConversationCacheHelper::NewL") );
+
+ return self;
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::CCsConversationCacheHelper
+// Construtor
+// ----------------------------------------------------------------------------
+CCsConversationCacheHelper::CCsConversationCacheHelper(
+ CCsConversationCache* aConversationCache) :
+ CActive(EPriorityLow), iConversationCache(aConversationCache)
+{
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::ConstructL
+// Second phase constructor
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::ConstructL()
+{
+ iConversationEvent = NULL;
+ iConversationIndex = KErrNotFound;
+ iCurrentConversationIndex = KErrNotFound;
+
+ //initialize the event list
+ iConversationEventList
+ = new (ELeave) RPointerArray<CCsConversationEvent> ();
+
+ // now create all special type of conversations
+ // like BT, IRDA, Unknown Drafts etc.
+ TInt loop = 0;
+ for (loop = 0; loop < KMaxSpecialConversations; loop++)
+ {
+ CreateSpecialConversationL();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCache::CreateSpecialConversationL
+// Second part of constructL
+// This shall create all special type of messages like BT, Unknown Draft, IRDA etc.
+// Pls note this shall be placed by order at starting of cache
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::CreateSpecialConversationL()
+{
+ // Create a conversation for Drafts Unknown case and set it in
+ // 0th position and the conversation Id for drafts unknown case is
+ // KDratsUnknownConversationId
+ CCsConversation* conversation = CCsConversation::NewL();
+
+ conversation->SetConversationId(GetNextRowId());
+
+ // add into the list
+ iConversationCache->ConversationList()->AppendL(conversation);
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::CCsConversationCacheHelper
+// Destructor
+// ----------------------------------------------------------------------------
+CCsConversationCacheHelper::~CCsConversationCacheHelper()
+{
+ Cancel();
+
+ // delete the event list
+ if (iConversationEventList)
+ {
+ iConversationEventList->ResetAndDestroy();
+ iConversationEventList->Close();
+ delete iConversationEventList;
+ iConversationEventList = NULL;
+ }
+}
+// -----------------------------------------------------------------------------
+// CCsConversationCacheHelper::DoCancel()
+// DoCancel implementation from CActive Object
+// -----------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::DoCancel()
+{
+ // do nothing
+}
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::Run
+// Called from the scheduler
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::RunL()
+{
+ switch (iState)
+ {
+ case ECsProcessConversation:
+ {
+ HandleProcessConversationL();
+ }
+ break;
+
+ case ECsSpecialConversation:
+ {
+ HandleSpecialConversationL();
+ }
+ break;
+
+ case ECsConversationFoundInCache:
+ {
+ HandleConversationInCacheL();
+ }
+ break;
+
+ case ECsResolveConversation:
+ {
+ ResolveContact(iConversationEvent);
+ }
+ break;
+ }
+}
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::StartCacheUpdate
+// this shall start looking at cache data and update if entry already exist
+// shall put request to phonebook in case new data
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::StartCacheUpdate()
+{
+ iState = ECsProcessConversation;
+ IssueRequest();
+}
+// ---------------------------------------------------------------------------
+// CCsConversationCacheHelper::HandleProcessConversationL()
+// Process Conversation and update cache
+// ---------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::HandleProcessConversationL()
+{
+ iConversationEvent = GetNextEvent();
+ // check to see if all the events are consumed
+ // then mark cache as caching done..
+ if (!iConversationEvent)
+ {
+ iConversationCache->CachingCompletedL();
+ return;
+ }
+
+ CCsConversationEntry* conEntry =
+ iConversationEvent->ClientConversation()->GetConversationEntry();
+
+ //handle add, modify events
+ if (iConversationEvent->Event() != KConversationEventDelete)
+ {
+ TUint8 SpecialId = NeedsSpecialProcessing(conEntry);
+
+ if (SpecialId)
+ {
+ // this is for draft(unlnown)/BT/IR processing
+ iState = ECsSpecialConversation;
+ }
+ // check only if contact is a valid address
+ else if (conEntry->Contact())
+ {
+ iCurrentConversationIndex
+ = iConversationCache->FindConversation(* (conEntry->Contact()));
+ if (iCurrentConversationIndex != KErrNotFound)
+ {
+ iState = ECsConversationFoundInCache;
+ }
+ else
+ {
+ iState = ECsResolveConversation;
+ }
+ }
+ else
+ {
+ // this is when contact number is NULL
+ // delete the entry from temp list
+ DeleteEvent(*iConversationEvent);
+ iState = ECsProcessConversation;
+ }
+ }
+ else
+ {
+ //handle delete event
+ DeleteConversationEntryL(conEntry);
+ DeleteEvent(*iConversationEvent);
+ iState = ECsProcessConversation;
+ }
+ //move to other state
+ IssueRequest();
+}
+// ---------------------------------------------------------------------------
+// CCsConversationCacheHelper::HandleSpecialConversationL()
+// Hnadle the case when the conversation has no contact,
+// BlueTooth/IrDa messages
+// ---------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::HandleSpecialConversationL()
+{
+ CCsConversationEntry* conEntry =
+ iConversationEvent->ClientConversation()->GetConversationEntry();
+
+ if (ECsBlueTooth == conEntry->GetType())
+ {
+ AddConversationEntryL(conEntry, KBluetoothMsgsConversationId);
+ }
+ else
+ {
+ AddConversationEntryL(conEntry, KUnknownConversationId);
+ }
+ DeleteEvent(*iConversationEvent);
+ iState = ECsProcessConversation;
+ IssueRequest();
+
+ PRINT ( _L("CCsConversationCacheHelper::HandleSpecialConversationL") );
+}
+// ---------------------------------------------------------------------------
+// CCsConversationCacheHelper::HandleConversationInCacheL()
+// Handle the case when the conversation is found in the cache
+// so there is no need for resolving the contact
+// ---------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::HandleConversationInCacheL()
+{
+ CCsConversationEntry* conEntry =
+ iConversationEvent->ClientConversation()->GetConversationEntry();
+
+ AddConversationEntryL(conEntry, iCurrentConversationIndex);
+
+ DeleteEvent(*iConversationEvent);
+
+ // reset back the conversation index
+ iCurrentConversationIndex = KErrNotFound;
+ iState = ECsProcessConversation;
+ IssueRequest();
+}
+// ----------------------------------------------------------------------------
+// This function identifies if the conentry needs special processing
+// Drafts message with NULL contact/Bluetooth/IRDA etc
+// ----------------------------------------------------------------------------
+TUint8 CCsConversationCacheHelper::NeedsSpecialProcessing(
+ CCsConversationEntry* aConversationEntry)
+{
+ if (ECsBlueTooth == aConversationEntry->GetType())
+ {
+ return 1;
+ }
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+// Move to next state
+// ---------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::IssueRequest()
+{
+ if (!IsActive())
+ {
+ iStatus = KRequestPending;
+ TRequestStatus* status = &iStatus;
+ SetActive();
+ User::RequestComplete(status, KErrNone);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::ContactResolved
+// Handle a contact resolved in the contact database
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::ResolveContact(
+ CCsConversationEvent* aConverastionEvent)
+{
+ CCsContactDetail contactDetail;
+
+ //get contact from event
+ CCsConversationEntry* ConvEntry =
+ aConverastionEvent->ClientConversation()->GetConversationEntry();
+
+ if (ConvEntry)
+ {
+ HBufC* Contact = ConvEntry->Contact();
+ if (Contact)
+ {
+ QString contactAddress =
+ S60QConversions::s60DescToQString(Contact->Des());
+
+ iConversationCache->ContactsManager()->resolver()->resolveContact(contactAddress,
+ contactDetail);
+
+ int contactId = contactDetail.contactId;
+ TInt cIndex = iConversationCache->FindConversation(contactId);
+
+ if (cIndex == KErrNotFound)
+ {
+ // Add as new conversation
+ HBufC* displayName = NULL;
+ if(!contactDetail.displayName.isEmpty())
+ {
+ displayName=S60QConversions::qStringToS60Desc(contactDetail.displayName);
+ }
+ TRAPD(error, AddNewConversationL( aConverastionEvent->
+ ClientConversation()->GetConversationEntry(),
+ contactId,
+ displayName));
+
+ if (displayName) delete displayName;
+
+ if (error != KErrNone)
+ {
+ // handle error
+ }
+ }
+ else
+ {
+ // this is when two contacts are having same contact Id,
+ // in that case it should add into an existing conversation
+ TRAPD(error, AddConversationEntryL(
+ aConverastionEvent->ClientConversation(
+ )->GetConversationEntry(), cIndex));
+ if (error != KErrNone)
+ {
+ // handle error
+ }
+ }
+
+ // now remove the entry from event list
+ DeleteEvent(*aConverastionEvent);
+
+ // call start update cache
+ StartCacheUpdate();
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Handles the case where conversation is already in cache
+// This will add new conversation entry into the corresponding conversation
+// pls note there is already a conversation exist for this entry
+// hence this entry gets appended inside conversation list
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::AddConversationEntryL(
+ CCsConversationEntry *aConEntry,
+ TInt aConversationIndex)
+{
+ RPointerArray<CCsConversation>* conversationList =
+ iConversationCache->ConversationList();
+
+ CCsConversation* conversation = (*conversationList)[aConversationIndex];
+
+ CCsConversationEntry* prevLatestEntry = conversation->GetLatestEntryL();
+
+ if (prevLatestEntry)
+ {
+ prevLatestEntry = prevLatestEntry->CloneL();
+ }
+ else
+ {
+ prevLatestEntry = aConEntry->CloneL();
+ }
+
+ CleanupStack::PushL(prevLatestEntry);
+
+ // save the previous unread message count
+ TUint16 prevUnreadCount = conversation->GetUnreadMessageCount();
+
+ TUint32 event;
+ conversation->UpdateEntryL(aConEntry, event);
+
+ // Send update notify to conversation list
+ if (IsNotifyRequiredL(conversation,
+ prevLatestEntry,
+ KConversationListEventUpdate,
+ prevUnreadCount))
+ {
+ CCsConversationEntry* convEntry = conversation->GetLatestEntryL();
+ CCsClientConversation* clientConvLatest =
+ iConversationCache->CreateClientConvLC(conversation, convEntry);
+
+ // THIS IS USED FOR SHOWING NOTIFICATIONS. NOTIFICATION IS SHOWN ONLY
+ // WHEN ECsAttributeNewEntryAdded IS SET.
+ if(iConversationEvent->IsUpdateConversationEventSet()
+ && event == KConversationEventNew)
+ {
+ clientConvLatest->GetConversationEntry()->
+ ChangeAttributes(ECsAttributeNewEntryAdded,ECsAttributeNone);
+ }
+
+ iConversationCache->NotifyL(clientConvLatest,
+ KConversationListEventUpdate);
+ CleanupStack::PopAndDestroy(clientConvLatest);
+ }
+
+ // Send update notify to conversations
+ CCsClientConversation* clientConv =
+ iConversationCache->CreateClientConvLC(conversation, aConEntry);
+ iConversationCache->NotifyL(clientConv, event);
+
+ // Cleanup
+ CleanupStack::PopAndDestroy(clientConv);
+ CleanupStack::PopAndDestroy(prevLatestEntry);
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCache::DeleteConversationL
+// Delete conversation entry from conversation list
+// there are few checks of how to notify the UI about the changes
+// for the various use cases.
+// Delete requires the conversation Id and type (SMS, MMS etc.)
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::DeleteConversationEntryL(
+ CCsConversationEntry* aConversationEntry)
+{
+ // For MMS, search the entry inside all conversations and delete.
+ // For SMS, stop on first match.
+ TBool stopOnFirstMatch = ETrue;
+
+ RPointerArray<CCsConversation>* conversationList =
+ iConversationCache->ConversationList();
+
+ // the deletion needs search the entryId inside whole conversation
+ // search the entry id and delete the entry
+ for (TInt loop = 0; loop < iConversationCache->ConversationList()->Count(); loop++)
+ {
+ CCsConversation* conversation =
+ static_cast<CCsConversation*> ( (*conversationList)[loop]);
+
+ TInt indexDeletion = conversation->FindEntry(aConversationEntry);
+
+ if (indexDeletion != KErrNotFound)
+ {
+ // Get the entry from cache to check the type
+ CCsConversationEntry* cacheEntry =
+ conversation->GetEntryL(indexDeletion);
+
+ if (cacheEntry->GetType() == ECsMMS || cacheEntry->GetType()
+ == ECsAudio)
+ {
+ stopOnFirstMatch = EFalse;
+ }
+
+ TUint16 prevUnreadCount = conversation->GetUnreadMessageCount();
+
+ // Delete the conversation entry from this conversation
+ conversation->DeleteEntryL(indexDeletion);
+
+ // Notify client of conversation list change.
+ if (IsNotifyRequiredL(conversation,
+ aConversationEntry,
+ KConversationListEventDelete,
+ prevUnreadCount))
+ {
+ // Delete
+ CCsClientConversation
+ * clientConv =
+ iConversationCache->CreateClientConvLC(conversation,
+ aConversationEntry);
+ iConversationCache->NotifyL(clientConv,
+ KConversationListEventDelete);
+ CleanupStack::PopAndDestroy(clientConv);
+
+ // Update with latest entry
+ CCsConversationEntry* conEntry =
+ conversation->GetLatestEntryL();
+ if (conEntry)
+ {
+ CCsClientConversation
+ * clientConv =
+ iConversationCache->CreateClientConvLC(conversation,
+ conEntry);
+ iConversationCache->NotifyL(clientConv,
+ KConversationListEventNew);
+ CleanupStack::PopAndDestroy(clientConv);
+ }
+ }
+
+ // Notify client of conversation change
+ if (!conversation->IsDeleted())
+ {
+ CCsClientConversation
+ * clientConv =
+ iConversationCache->CreateClientConvLC(conversation,
+ aConversationEntry);
+ iConversationCache->NotifyL(clientConv,
+ KConversationEventDelete);
+ CleanupStack::PopAndDestroy(clientConv);
+ }
+
+ // check if all entries are deleted then
+ // delete the conversation from cache
+ if (conversation->GetEntryCount() == 0
+ && !conversation->IsSpecialConversation())
+ {
+ conversationList->Remove(loop);
+ delete conversation;
+ //reset the counters
+ loop -= 1;
+ }
+
+ // Stop searching
+ if (stopOnFirstMatch) break;
+ }
+ } // for
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::AddNewConversationL
+// add new conversation into cache
+// this shall be called after resolving entry from phonebook
+// ----------------------------------------------------------------------------
+void CCsConversationCacheHelper::AddNewConversationL(
+ CCsConversationEntry* aConversationEntry,
+ TInt32 aContactId,
+ const HBufC* aDisplayName)
+{
+ CCsConversation* conversation = CCsConversation::NewL();
+ CleanupStack::PushL(conversation);
+
+ //set conversation entry id
+ // by default just set the count of list as of now, as it will be unique
+ conversation->SetConversationId(GetNextRowId());
+
+ // add the conversation entry
+ conversation->AddEntryL(aConversationEntry);
+
+ // fill firstname and lastname and contact Id
+ conversation->AddContactDetailsL(aContactId,
+ *aDisplayName);
+
+ // fill the phone number
+ if (aConversationEntry->Contact())
+ {
+ conversation->AddContactDetailsL(* (aConversationEntry->Contact()));
+ }
+
+ // add into the list
+ iConversationCache->ConversationList()->AppendL(conversation);
+
+ // Send add notify to conversation list
+ CCsClientConversation* clientConv =
+ iConversationCache->CreateClientConvLC(conversation,
+ aConversationEntry);
+
+ // THIS IS USED FOR SHOWING NOTIFICATIONS. NOTIFICATION IS SHOWN ONLY
+ // WHEN ECsAttributeNewEntryAdded IS SET.
+ if(iConversationEvent->IsUpdateConversationEventSet())
+ {
+ clientConv->GetConversationEntry()->
+ ChangeAttributes(ECsAttributeNewEntryAdded,ECsAttributeNone);
+ }
+
+ iConversationCache->NotifyL(clientConv, KConversationListEventNew);
+ CleanupStack::PopAndDestroy(clientConv);
+
+ CleanupStack::Pop(conversation);
+
+ PRINT ( _L("CCsConversationCacheHelper::AddNewConversationL - Conversation Added") );
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCache::GetPendingEventCount
+// Pending event count
+// ----------------------------------------------------------------------------
+TInt CCsConversationCacheHelper::GetPendingEventCount()
+{
+ return iConversationEventList->Count();
+}
+
+// ---------------------------------------------------------------------------
+// Get next event from event list
+// ---------------------------------------------------------------------------
+//
+CCsConversationEvent* CCsConversationCacheHelper::GetNextEvent()
+{
+ // check in case there are no events in cache
+ if (iConversationEventList->Count() > 0)
+ {
+ return (*iConversationEventList)[0];
+ }
+ return NULL;
+}
+
+// ---------------------------------------------------------------------------
+// Find CCsConversationEvent from event Q based on ConversationEntryId.
+// ---------------------------------------------------------------------------
+TInt CCsConversationCacheHelper::FindEvent(
+ const CCsConversationEvent& aConvEvent)
+{
+ // find the event inside event list and send the index
+ // KErrNotFound in case None
+ TInt
+ index =
+ iConversationEventList->Find(&aConvEvent,
+ CCsConversationEvent::CompareByEntryId);
+
+ return index;
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCache::GetNextRowId
+// Get the conversation list count where to add next conversation
+// ----------------------------------------------------------------------------
+TInt16 CCsConversationCacheHelper::GetNextRowId()
+{
+ // increment the index and return
+ iConversationIndex++;
+ return iConversationIndex;
+}
+
+// ---------------------------------------------------------------------------
+// Find CCsConversationEvent based on ConversationEntryId and delete it.
+// ---------------------------------------------------------------------------
+//
+void CCsConversationCacheHelper::DeleteEvent(
+ const CCsConversationEvent& aConvEvent)
+{
+ TInt index = FindEvent(aConvEvent);
+
+ if (KErrNotFound != index)
+ {
+ CCsConversationEvent* conversationEvent =
+ (*iConversationEventList)[index];
+ iConversationEventList->Remove(index);
+ delete conversationEvent;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCache::IsNotifyRequiredL
+// checks whether notification for conversation list is required
+// ----------------------------------------------------------------------------
+TBool CCsConversationCacheHelper::IsNotifyRequiredL(
+ CCsConversation* aConversation,
+ CCsConversationEntry* aConversationEntry,
+ TUint32 aEvent,
+ TUint16 aPreviousUnreadMsgsCount)
+{
+ TUint16 presentUnreadMessageCount = aConversation->GetUnreadMessageCount();
+ CCsConversationEntry* latestEntry = aConversation->GetLatestEntryL();
+
+ // Add & Update
+ if (aEvent & KConversationListEventNew || aEvent
+ & KConversationListEventUpdate)
+ {
+ // Check whether deleted is ongoing. Don't Notify.
+ if (aConversation->IsDeleted())
+ {
+ return EFalse;
+ }
+
+ // Check whether latest entry is same and unread count has not changed. Don't Notify.
+ if (CCsConversationEntry::Compare(*latestEntry, *aConversationEntry)
+ == 0 && (presentUnreadMessageCount == aPreviousUnreadMsgsCount)
+ && (latestEntry->GetSendState()
+ == aConversationEntry->GetSendState()))
+ {
+ return EFalse;
+ }
+ }
+
+ // Delete
+ if (aEvent & KConversationListEventDelete)
+ {
+ // Not Last entry during delete. Don't Notify.
+ if (aConversation->IsDeleted() && aConversation->GetEntryCount() > 0)
+ {
+ return EFalse;
+ }
+ }
+
+ // Notify.
+ return ETrue;
+}
+
+// ----------------------------------------------------------------------------
+// CCsConversationCacheHelper::ConversationEventList
+// Pls refer to .h file
+// ----------------------------------------------------------------------------
+RPointerArray<CCsConversationEvent>*
+CCsConversationCacheHelper::ConversationEventList()
+{
+ return iConversationEventList;
+}
+
+//end of file