diff -r 84d9eb65b26f -r 518b245aa84c messagingapp/msgui/appengine/src/conversationsmodel.cpp --- a/messagingapp/msgui/appengine/src/conversationsmodel.cpp Mon May 03 12:29:07 2010 +0300 +++ b/messagingapp/msgui/appengine/src/conversationsmodel.cpp Fri Jun 25 15:47:40 2010 +0530 @@ -19,24 +19,57 @@ #include "conversationsenginedefines.h" #include "conversationmsgstorehandler.h" #include "convergedmessage.h" -#include "s60qconversions.h" +#include #include "conversationsengineutility.h" #include "unidatamodelloader.h" #include "unidatamodelplugininterface.h" +#include "ringbc.h" +#include "msgcontacthandler.h" +#include "mmsconformancecheck.h" #include +#include + +#include "debugtraces.h" + #include #include +#include +#include +#include +#include -// LOCAL CONSTANTS +//CONSTANTS +_LIT(KDbFileName, "c:[2002A542]conversations.db"); + +// preview sql query +_LIT(KSelectConvMsgsStmt, "SELECT message_id, msg_processingstate, subject, body_text, preview_path, msg_property, preview_icon FROM conversation_messages WHERE message_id=:message_id "); +//selecet preview-icon query +_LIT(KSelectPreviewIconStmt,"SELECT message_id, preview_icon FROM conversation_messages WHERE message_id = :message_id "); + +// preview-cache max cost (items) +const int CACHE_COST = 50; +//Preview thumbnail size +const int KWidth = 9.5 * 6.7; +const int KHeight = 9.5 * 6.7; //--------------------------------------------------------------- // ConversationsModel::ConversationsModel // Constructor //--------------------------------------------------------------- ConversationsModel::ConversationsModel(ConversationMsgStoreHandler* msgStoreHandler, QObject* parent) : - QStandardItemModel(parent), mMsgStoreHandler(msgStoreHandler) + QStandardItemModel(parent), mMsgStoreHandler(msgStoreHandler), iSqlDbOpen(EFalse) { + //Open SQL DB + if (KErrNone == iSqlDb.Open(KDbFileName)) + { + iSqlDbOpen = ETrue; + } + previewIconCache.setMaxCost(CACHE_COST); + + int err = connect(this, SIGNAL(retrievePreviewIcon(int, QString&)), this, + SLOT(updatePreviewIcon(int, QString&))); + QCRITICAL_WRITE_FORMAT("Error from connect()", err) iDataModelPluginLoader = new UniDataModelLoader; iMmsDataPlugin = iDataModelPluginLoader->getDataModelPlugin(ConvergedMessage::Mms); iBioMsgPlugin = iDataModelPluginLoader->getDataModelPlugin(ConvergedMessage::BioMsg); @@ -48,6 +81,12 @@ //--------------------------------------------------------------- ConversationsModel::~ConversationsModel() { + //Close SQL-DB + iSqlDb.Close(); + + //clear preview-cache + previewIconCache.clear(); + if (iDataModelPluginLoader) { delete iDataModelPluginLoader; iDataModelPluginLoader = NULL; @@ -128,6 +167,13 @@ value = item->data(SendingState); break; } + case PreviewIcon: + { + QString filepath(item->data(Attachments).toString()); + int msgId = item->data(ConvergedMsgId).toInt(); + HbIcon *icon = getPreviewIconItem(msgId, filepath); + return *icon; + } case MessagePriority: { value = item->data(MessagePriority); @@ -160,7 +206,7 @@ } case DisplayName: // Fall through start value = item->data(DisplayName); - break; + break; case Avatar: // Fall througn end value = item->data(Avatar); break; @@ -244,6 +290,8 @@ //--------------------------------------------------------------- void ConversationsModel::populateItem(QStandardItem& item, const CCsConversationEntry& entry) { + QCRITICAL_WRITE("ConversationsModel::populateItem start."); + int msgId = entry.EntryId(); // id item.setData(msgId, ConvergedMsgId); @@ -252,10 +300,10 @@ HBufC* description = entry.Description(); QString subject(""); if (description && description->Length()) { - subject = (S60QConversions::s60DescToQString(*description)); + subject = (XQConversions::s60DescToQString(*description)); } - // time stamp + // time stamp TTime unixEpoch(KUnixEpoch); TTimeIntervalSeconds seconds; TTime timeStamp(entry.TimeStamp()); @@ -265,7 +313,7 @@ //contact details HBufC* contact = entry.Contact(); if (contact && contact->Length()) { - item.setData(S60QConversions::s60DescToQString(*contact), ConversationAddress); + item.setData(XQConversions::s60DescToQString(*contact), ConversationAddress); } // message type. @@ -302,9 +350,11 @@ item.setData(ConvergedMessage::Outbox, MessageLocation); } - //message specific handling + //message specific handling if (msgType == ConvergedMessage::Mms) { + QCRITICAL_WRITE("ConversationsModel::populateItem MMS start.") handleMMS(item, entry); + QCRITICAL_WRITE("ConversationsModel::populateItem MMS end.") } else if(msgType == ConvergedMessage::MmsNotification) { item.setData(subject, Subject); @@ -320,54 +370,282 @@ // sms bodytext item.setData(subject, BodyText); } + + QCRITICAL_WRITE("ConversationsModel::populateItem end."); } //--------------------------------------------------------------- +// ConversationsModel::validateMsgForForward +// @see header file +//--------------------------------------------------------------- +bool ConversationsModel::validateMsgForForward(qint32 messageId) +{ + bool retValue = true; + //Validate if the mms msg can be forwarded or not + MmsConformanceCheck* mmsConformanceCheck = new MmsConformanceCheck; + retValue = mmsConformanceCheck->validateMsgForForward(messageId); + + delete mmsConformanceCheck; + return retValue; +} + + +//--------------------------------------------------------------- // ConversationsModel::handleMMS // @see header //--------------------------------------------------------------- void ConversationsModel::handleMMS(QStandardItem& item, const CCsConversationEntry& entry) { - iMmsDataPlugin->setMessageId(entry.EntryId()); - if (iMmsDataPlugin->attachmentCount() > 0) { - item.setData(ConvergedMessage::Attachment, MessageProperty); - } + //msg_id + int msgId = entry.EntryId(); + + bool isEntryInDb = false; + TInt err = KErrNone; + + //check if db is open and query db + if (iSqlDbOpen) + { + RSqlStatement sqlSelectStmt; + err = sqlSelectStmt.Prepare(iSqlDb, KSelectConvMsgsStmt); + + // move to fallback option + if (KErrNone == err) + { + TInt msgIdIndex = sqlSelectStmt.ParameterIndex(_L(":message_id")); + TInt msgProcessingStateIndex = sqlSelectStmt.ColumnIndex(_L("msg_processingstate")); + TInt subjectIndex = sqlSelectStmt.ColumnIndex(_L("subject")); + TInt bodyIndex = sqlSelectStmt.ColumnIndex(_L("body_text")); + TInt previewPathIndex = sqlSelectStmt.ColumnIndex( + _L("preview_path")); + TInt msgpropertyIndex = sqlSelectStmt.ColumnIndex( + _L("msg_property")); + TInt previewIconIndex = sqlSelectStmt.ColumnIndex( + _L("preview_icon")); + + err = sqlSelectStmt.BindInt(msgIdIndex, msgId); + + // populate item + if ((KErrNone == err) && (sqlSelectStmt.Next() == KSqlAtRow)) + { + int msgProcessingState = 0; + msgProcessingState = sqlSelectStmt.ColumnInt( + msgProcessingStateIndex); + if (msgProcessingState == EPreviewMsgProcessed) + { + // use entry to populate model only when, + // entry is present in DB and its processing is over. + RBuf subjectBuffer; + subjectBuffer.Create(sqlSelectStmt.ColumnSize( + subjectIndex)); + sqlSelectStmt.ColumnText(subjectIndex, subjectBuffer); + + item.setData(XQConversions::s60DescToQString( + subjectBuffer), Subject); + subjectBuffer.Close(); + + RBuf bodyBuffer; + bodyBuffer.Create(sqlSelectStmt.ColumnSize(bodyIndex)); + sqlSelectStmt.ColumnText(bodyIndex, bodyBuffer); + + item.setData( + XQConversions::s60DescToQString(bodyBuffer), + BodyText); + bodyBuffer.Close(); + + RBuf previewPathBuffer; + previewPathBuffer.Create(sqlSelectStmt.ColumnSize( + previewPathIndex)); + sqlSelectStmt.ColumnText(previewPathIndex, + previewPathBuffer); + + //Rightnow set inside attachments + QString attachmentPath(XQConversions::s60DescToQString( + previewPathBuffer)); + + item.setData(attachmentPath, Attachments); + previewPathBuffer.Close(); + + int msgProperty = 0; + msgProperty = sqlSelectStmt.ColumnInt(msgpropertyIndex); + item.setData(msgProperty, MessageProperty); + + RSqlColumnReadStream stream; + //Get data from binary column BLOB + TInt err = stream.ColumnBinary(sqlSelectStmt, + previewIconIndex); + + QCRITICAL_WRITE_FORMAT("Error from ColumnBinary()", err) - int slideCount = iMmsDataPlugin->slideCount(); - bool isBodyTextSet = false; - QString textContent; - QStringList attachmentPaths; + if (err == KErrNone) + { + CFbsBitmap *bitmap = new CFbsBitmap; + TRAPD(err,bitmap->InternalizeL(stream)); + QCRITICAL_WRITE_FORMAT("Error from bitmap InternalizeL()", err) + + //convert bitmap to pixmap + if (err == KErrNone) + { + TSize size = bitmap->SizeInPixels(); + int bytesPerLine = bitmap->ScanLineLength( + size.iWidth, bitmap->DisplayMode()); + const uchar* dataPtr = + (const uchar*) bitmap->DataAddress(); + + QPixmap pixmap = QPixmap::fromImage(QImage( + dataPtr, size.iWidth, size.iHeight, + bytesPerLine, QImage::Format_RGB16)); + + setPreviewIcon(pixmap, attachmentPath, msgId, + true); + + } + //remove bitmap + delete bitmap; + } + + //set flag to disable fallback option + isEntryInDb = true; + } + } + } + sqlSelectStmt.Close(); + } - for (int i = 0; i < slideCount; ++i) { - UniMessageInfoList objectList = iMmsDataPlugin->slideContent(i); - for (int index = 0; index < objectList.count(); ++index) { - attachmentPaths.append(objectList[index]->path()); - if (!isBodyTextSet && objectList[index]->mimetype().contains("text")) { - QFile file(objectList[index]->path()); - if(file.open(QIODevice::ReadOnly)) + //fallback option incase of db operation failure or enry not found in DB + //populate from data plugins + if (!isEntryInDb || err != KErrNone) + { + int id = iMmsDataPlugin->setMessageId(entry.EntryId()); + if(id <= 0) + { + // skip all + return; + } + int msgProperty = 0; + + if (iMmsDataPlugin->attachmentCount() > 0) + { + msgProperty |= EPreviewAttachment; + } + + if(validateMsgForForward(entry.EntryId())) + { + msgProperty |= EPreviewForward; + } + + //subject + item.setData(iMmsDataPlugin->subject(), Subject); + + int slideCount = iMmsDataPlugin->slideCount(); + bool isBodyTextSet = false; + bool isAudioSet = false; + bool isImageSet = false; + bool isVideoSet = false; + QString textContent; + QString videoPath; + QString imagePath; + + for (int i = 0; i < slideCount; ++i) + { + UniMessageInfoList objectList = iMmsDataPlugin->slideContent(i); + for (int index = 0; index < objectList.count(); ++index) + { + if (!isBodyTextSet && objectList[index]->mimetype().contains( + "text")) { + QFile file(objectList[index]->path()); + file.open(QIODevice::ReadOnly); textContent = file.readAll(); item.setData(textContent, BodyText); isBodyTextSet = true; file.close(); } + if (!isVideoSet && !isImageSet && objectList[index]->mimetype().contains( + "image")) + { + isImageSet = true; + msgProperty |= EPreviewImage; + if (objectList[index]->isProtected()) + { + msgProperty |= EPreviewProtectedImage; + } + if (objectList[index]->isCorrupted()) + { + msgProperty |= EPreviewCorruptedImage; + } + imagePath = objectList[index]->path(); + } + if (!isVideoSet && !isAudioSet && objectList[index]->mimetype().contains( + "audio")) + { + msgProperty |= EPreviewAudio; + if (objectList[index]->isProtected()) + { + msgProperty |= EPreviewProtectedAudio; + } + if (objectList[index]->isCorrupted()) + { + msgProperty |= EPreviewCorruptedAudio; + } + isAudioSet = true; + } + if (!( isImageSet || isAudioSet) && !isVideoSet && objectList[index]->mimetype().contains( + "video")) + { + isVideoSet = true; + msgProperty |= EPreviewVideo; + if (objectList[index]->isProtected()) + { + msgProperty |= EPreviewProtectedVideo; + } + if (objectList[index]->isCorrupted()) + { + msgProperty |= EPreviewCorruptedVideo; + } + videoPath = objectList[index]->path(); + } + } + foreach(UniMessageInfo* slide,objectList) + { + delete slide; + } + } + QPixmap pixmap; + //populate item with the attachment list + //TODO: This code is not required bcoz video icon is show and not preview + if (isVideoSet) + { + item.setData(videoPath, Attachments); + // Store thumbnail only for non protected, non corrupted content. + if (!(EPreviewProtectedVideo & msgProperty) && + !(EPreviewCorruptedVideo & msgProperty)) + { + setPreviewIcon(pixmap, videoPath, msgId, false); } } - foreach(UniMessageInfo* slide,objectList) + else if (isImageSet) + { + item.setData(imagePath, Attachments); + // Store thumbnail only for non protected, non corrupted content. + if (!(EPreviewProtectedImage & msgProperty) && + !(EPreviewCorruptedImage & msgProperty)) { - delete slide; + setPreviewIcon(pixmap, imagePath, msgId, false); } + } + //populate msgProperty + item.setData(msgProperty, MessageProperty); } - //populate item with the attachment list - item.setData(attachmentPaths.join("|"), Attachments); - if (entry.IsAttributeSet(ECsAttributeHighPriority)) { + + // fill other attributes + if (entry.IsAttributeSet(ECsAttributeHighPriority)) + { item.setData(ConvergedMessage::High, MessagePriority); } - else if (entry.IsAttributeSet(ECsAttributeLowPriority)) { + else if (entry.IsAttributeSet(ECsAttributeLowPriority)) + { item.setData(ConvergedMessage::Low, MessagePriority); } - //subject - item.setData(iMmsDataPlugin->subject(), Subject); } //--------------------------------------------------------------- @@ -378,27 +656,33 @@ const CCsConversationEntry& entry) { // set context to current entry - mMsgStoreHandler->setNotificationMessageId(entry.EntryId()); - + TRAPD(err, mMsgStoreHandler->setNotificationMessageIdL(entry.EntryId())); + if(err != KErrNone) + { + return; + } + // fetch relevent info to show in CV // msg size - QString estimatedMsgSizeStr = - mMsgStoreHandler->NotificationMsgSize(); - + QString estimatedMsgSizeStr = QString("%1").arg(0); + estimatedMsgSizeStr.append(" Kb"); + TRAP_IGNORE(estimatedMsgSizeStr = + mMsgStoreHandler->NotificationMsgSizeL()); + // msg class type QString classInfoStr = mMsgStoreHandler->NotificationClass(); - + // notification expiry date //TODO: Need to do localization of digits used to show expiry time TTime expiryTime; QString expiryTimeStr; mMsgStoreHandler->NotificationExpiryDate(expiryTime, expiryTimeStr); - + // notification state e.g. waiting, retrieving etc QString statusStr; int status; mMsgStoreHandler->NotificationStatus(status, statusStr); - + // create data for bodytext role QString dataText; dataText.append("Size: "); // TODO: use logical str name @@ -437,7 +721,7 @@ { //TODO, needs to be revisited again, once BT team provides the solution for //BT received as Biomsg issue. - QString description = S60QConversions::s60DescToQString(*(entry.Description())); + QString description = XQConversions::s60DescToQString(*(entry.Description())); if (description.contains(".vcf") || description.contains(".ics")) // "vCard" { @@ -445,10 +729,11 @@ item.setData(ConvergedMessage::VCard, MessageSubType); //parse vcf file to get the details - QString displayName = ConversationsEngineUtility::getVcardDisplayName(description); + QString displayName = MsgContactHandler::getVCardDisplayName( + description); item.setData(displayName, BodyText); - } - else + } + else { if (description.contains(".vcs")) // "vCalendar" { @@ -482,8 +767,11 @@ QString attachmentPath = attList[0]->path(); //get display-name and set as bodytext - QString displayName = ConversationsEngineUtility::getVcardDisplayName(attachmentPath); + QString displayName = + MsgContactHandler::getVCardDisplayName( + attachmentPath); item.setData(displayName, BodyText); + item.setData(attachmentPath, Attachments); // clear attachement list : its allocated at data model while (!attList.isEmpty()) { @@ -494,15 +782,198 @@ else if (ConvergedMessage::VCal == msgSubType) { //not supported } + else if (ConvergedMessage::RingingTone == msgSubType) { + if (iBioMsgPlugin->attachmentCount() > 0) { + UniMessageInfoList attList = iBioMsgPlugin->attachmentList(); + QString attachmentPath = attList[0]->path(); + + //get tone title, and set as bodytext + RingBc ringBc; + item.setData(ringBc.toneTitle(attachmentPath), BodyText); + while (!attList.isEmpty()) { + delete attList.takeFirst(); + } + } + + } else { // description HBufC* description = entry.Description(); QString subject(""); if (description && description->Length()) { - subject = (S60QConversions::s60DescToQString(*description)); + subject = (XQConversions::s60DescToQString(*description)); item.setData(subject, BodyText); } } } +//--------------------------------------------------------------- +// ConversationsModel::getDBHandle() +// @see header +//--------------------------------------------------------------- +RSqlDatabase& ConversationsModel::getDBHandle(TBool& isOpen) +{ + isOpen = iSqlDbOpen; + return iSqlDb; +} + +//--------------------------------------------------------------- +// ConversationsModel::setPreviewIcon() +// @see header +//--------------------------------------------------------------- +void ConversationsModel::setPreviewIcon(QPixmap& pixmap, QString& filePath, + int msgId, bool inDb) +{ + + //Since the population happens in reverse this check is needed so that + //most recent items have their icons present in cache + if (previewIconCache.totalCost() >= previewIconCache.maxCost()) + return; + + // if not found in db, set from file path + if (!inDb) + { + QPixmap pixmap(filePath); + QPixmap scaledPixmap = pixmap.scaled(KWidth, KHeight, Qt::IgnoreAspectRatio); + HbIcon *previewIcon = new HbIcon(scaledPixmap); + + previewIconCache.insert(msgId, previewIcon); + + } + else + { + HbIcon *previewIcon = new HbIcon(pixmap); + previewIconCache.insert(msgId, previewIcon); + } +} + +//--------------------------------------------------------------- +// ConversationsModel::getPreviewIconItem() +// @see header +//--------------------------------------------------------------- +HbIcon* ConversationsModel::getPreviewIconItem(int msgId, + QString& filepath) const +{ + QCRITICAL_WRITE("ConversationsModel::getPreviewIconItem start.") + + //Initialize icon from the Cache will be NULL if Item not present + HbIcon* previewIcon = previewIconCache[msgId]; + if (!previewIcon) + { + //This is done in this way as non-const function call cant be done here + emit retrievePreviewIcon(msgId, filepath); + + previewIcon = previewIconCache[msgId]; + } + + QCRITICAL_WRITE("ConversationsModel::getPreviewIconItem start.") + + return previewIcon; +} + +//--------------------------------------------------------------- +// ConversationsModel::updatePreviewIcon() +// @see header +//--------------------------------------------------------------- +void ConversationsModel::updatePreviewIcon(int msgId, QString& filePath) +{ + QCRITICAL_WRITE("ConversationsModel::updatePreviewIcon start.") + + //sql query to get preview-icon from DB + bool imagePreviewed = false; + QPixmap pixmap; + + if (iSqlDbOpen) + { + RSqlStatement sqlSelectPreviewIconStmt; + TInt err = sqlSelectPreviewIconStmt.Prepare(iSqlDb, + KSelectPreviewIconStmt); + + QCRITICAL_WRITE_FORMAT("Error from Prepare()", err) + + if (err == KErrNone) + { + //msg_id + TInt msgIdIndex = sqlSelectPreviewIconStmt.ParameterIndex( + _L(":message_id")); + sqlSelectPreviewIconStmt.BindInt(msgIdIndex, msgId); + + // get preview-icon from DB + err = sqlSelectPreviewIconStmt.Next(); + QCRITICAL_WRITE_FORMAT("Error from Next()", err) + + if (err == KSqlAtRow) + { + TInt previewIconIndex = sqlSelectPreviewIconStmt.ColumnIndex( + _L("preview_icon")); + + RSqlColumnReadStream stream; + + //Get data from binary column BLOB + err = stream.ColumnBinary(sqlSelectPreviewIconStmt, + previewIconIndex); + + QCRITICAL_WRITE_FORMAT("Error from ColumnBinary()", err) + + if (err == KErrNone) + { + CFbsBitmap *bitmap = new CFbsBitmap; + TRAPD(err,bitmap->InternalizeL(stream)); + QCRITICAL_WRITE_FORMAT("Error from bitmap InternalizeL()", err) + + //convert bitmap to pixmap + if (err == KErrNone) + { + TSize size = bitmap->SizeInPixels(); + int bytesPerLine = bitmap->ScanLineLength(size.iWidth, + bitmap->DisplayMode()); + const uchar* dataPtr = + (const uchar*) bitmap->DataAddress(); + + pixmap = QPixmap::fromImage(QImage(dataPtr, + size.iWidth, size.iHeight, bytesPerLine, + QImage::Format_RGB16)); + + imagePreviewed = true; + + QCRITICAL_WRITE("Bitmap Conversion completed") + } + //remove bitmap + delete bitmap; + } + //close stream + stream.Close(); + } + } + sqlSelectPreviewIconStmt.Close(); + } + + // if not found in db, set from file path + if (!imagePreviewed) + { + QPixmap orgPixmap(filePath); + pixmap = orgPixmap.scaled(63.65, 63.65, Qt::IgnoreAspectRatio); + } + HbIcon * previewIcon = new HbIcon(pixmap); + + previewIconCache.insert(msgId, previewIcon); + + QCRITICAL_WRITE("ConversationsModel::updatePreviewIcon end.") + +} + +//--------------------------------------------------------------- +// ConversationsModel::clearModel() +// @see header +//--------------------------------------------------------------- +void ConversationsModel::clearModel() +{ + clear(); + previewIconCache.clear(); +} + +void ConversationsModel:: emitConversationViewEmpty() +{ + emit conversationViewEmpty(); +} //EOF