/*! Parses SIM contacts in TLV format. * * \param rawData SIM contacts in TLV format. * \return List of contacts. */ QList<QContact> CntSimStorePrivate::decodeSimContactsL(TDes8& rawData) const { PbkPrintToLog(_L("CntSymbianSimEngine::decodeSimContactsL() - IN")); QList<QContact> fetchedContacts; QContact currentContact; TBuf16<KDataClientBuf> buffer; TPtrC16 bufPtr(buffer); TUint8 tagValue(0); CPhoneBookBuffer::TPhBkTagType dataType; bool isAdditionalNumber = false; CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer(); CleanupStack::PushL(pbBuffer); pbBuffer->Set(&rawData); pbBuffer->StartRead(); while (pbBuffer->GetTagAndType(tagValue, dataType) == KErrNone) { switch (tagValue) { case RMobilePhoneBookStore::ETagPBAdnIndex: { //save contact's id (SIM card index) and manager's name TUint16 index; if (pbBuffer->GetValue(index) == KErrNone) { QScopedPointer<QContactId> contactId(new QContactId()); contactId->setLocalId(index); contactId->setManagerUri(m_managerUri); currentContact.setId(*contactId); } isAdditionalNumber = false; break; } case RMobilePhoneBookStore::ETagPBTonNpi: { // Note, that TON info can be incorporated into the phone number by Etel // implementation (TSY). E.g. this is the case with Nokia TSY. // Here general case is implemented. // Check number type, we are only interested if it's international or not. // We assume here that ETagPBTonNpi always comes after ETagPBNumber, not before. TUint8 tonNpi; if (pbBuffer->GetValue(tonNpi) == KErrNone) { TUint8 intFlag = (tonNpi & KEtsiTonPosition) >> 4; if (intFlag == 1) { //international number format, append "+" to the last //saved number QList<QContactDetail> phoneNumbers = currentContact.details( QContactPhoneNumber::DefinitionName); if (phoneNumbers.count() > 0) { QContactPhoneNumber lastNumber = static_cast<QContactPhoneNumber>( phoneNumbers.at(phoneNumbers.count() - 1)); QString number = lastNumber.number(); number.insert(0, "+"); lastNumber.setNumber(number); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&lastNumber); currentContact.saveDetail(&lastNumber); } } } // We have rearched to the end of the number, // invalidate additional number flag. isAdditionalNumber = false; break; } case RMobilePhoneBookStore::ETagPBText: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { if (isAdditionalNumber) { // For additional number bufPtr contains number alpha string, // this is ignored currently } else { // Contact name otherwise QContactName name; QString nameString = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); name.setCustomLabel(nameString); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&name); currentContact.saveDetail(&name); QContactManager::Error error(QContactManager::NoError); m_engine.setContactDisplayLabel(¤tContact, m_engine.synthesizedDisplayLabel(currentContact, &error)); } } break; } case RMobilePhoneBookStore::ETagPBSecondName: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactNickname nickName; QString name = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); nickName.setNickname(name); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&nickName); currentContact.saveDetail(&nickName); } break; } case RMobilePhoneBookStore::ETagPBNumber: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactPhoneNumber phoneNumber; QString number = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); phoneNumber.setNumber(number); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&phoneNumber); currentContact.saveDetail(&phoneNumber); } break; } case RMobilePhoneBookStore::ETagPBAnrStart: { // This tag should precede every additional number entry isAdditionalNumber = true; break; } case RMobilePhoneBookStore::ETagPBEmailAddress: { if (pbBuffer->GetValue(bufPtr) == KErrNone) { QContactEmailAddress email; QString emailAddress = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length()); email.setEmailAddress(emailAddress); if (m_storeInfo.m_readOnlyAccess) m_engine.setReadOnlyAccessConstraint(&email); currentContact.saveDetail(&email); } break; } case RMobilePhoneBookStore::ETagPBNewEntry: { // This signals the end of the entry and is a special case // which will be handled below. break; } default: { // An unsupported field type - just skip this value pbBuffer->SkipValue(dataType); break; } } //switch // save contact to the array of contact to be returned if the whole entry was extracted if ((tagValue == RMobilePhoneBookStore::ETagPBNewEntry && currentContact.localId() > 0) || (pbBuffer->RemainingReadLength() == 0 && currentContact.localId() > 0)) { fetchedContacts.append(currentContact); //clear current contact currentContact.clearDetails(); QScopedPointer<QContactId> contactId(new QContactId()); contactId->setLocalId(0); contactId->setManagerUri(QString()); currentContact.setId(*contactId); } } //while