qtmobility/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp
changeset 14 6fbed849b4f4
parent 5 453da2cfceef
--- a/qtmobility/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp	Fri Jun 11 14:26:25 2010 +0300
+++ b/qtmobility/plugins/contacts/symbiansim/src/cntsimstoreprivate.cpp	Wed Jun 23 19:08:38 2010 +0300
@@ -67,13 +67,26 @@
      m_state(InactiveState),
      m_engine(engine),
      m_simStore(simStore),
-     m_storeName(storeName),
-     m_readOnlyAccess(false),
-     m_storeInfoPckg(m_storeInfo),
-     m_listener(0)
+     m_listener(0),
+     m_extraDetailsChecked(false)
 {
     CActiveScheduler::Add(this);
     m_managerUri = engine.managerUri();
+    
+    // Initialize store info
+    m_storeInfo.m_storeName                 = storeName;
+    m_storeInfo.m_totalEntries              = -1;
+    m_storeInfo.m_usedEntries               = -1;
+    m_storeInfo.m_readOnlyAccess            = false; 
+    m_storeInfo.m_numberSupported           = true; // allways supported
+    m_storeInfo.m_nameSupported             = true; // allways supported
+    m_storeInfo.m_secondNameSupported       = false;
+    m_storeInfo.m_additionalNumberSupported = false;
+    m_storeInfo.m_emailSupported            = false;
+    
+    // SDN store is allways read only
+    if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn)
+        m_storeInfo.m_readOnlyAccess = true;
 }
 
 void CntSimStorePrivate::ConstructL()
@@ -81,10 +94,6 @@
     TBuf<RMobilePhoneBookStore::KMaxPBIDSize> storeName;
     convertStoreNameL(storeName);
     
-    // SDN store is always read only
-    if (m_storeName == KParameterValueSimStoreNameSdn)
-        m_readOnlyAccess = true;
-
     // Open etel server
     User::LeaveIfError(m_etelServer.Connect());
     User::LeaveIfError(m_etelServer.LoadPhoneModule(KMmTsyModuleName));
@@ -94,26 +103,13 @@
     User::LeaveIfError(m_etelServer.GetPhoneInfo(0, info));
     User::LeaveIfError(m_etelPhone.Open(m_etelServer, info.iName));
     
-    // open Etel store
+    // Open Etel store
     User::LeaveIfError(m_etelStore.Open(m_etelPhone, storeName));
+
+    // Update store info
+    updateStoreInfoL();
     
-    //check what information can be saved to the Etel store
-    TRequestStatus requestStatus;
-    m_etelStore.GetInfo(requestStatus, (TDes8&)m_storeInfoPckg);
-    User::WaitForRequest(requestStatus);
-    PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - GetInfo err = %d"),
-        requestStatus.Int());
-    User::LeaveIfError(requestStatus.Int());
-
-    PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - TotalEntries = %d"),
-            m_storeInfo.iTotalEntries);
-    PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - UsedEntries = %d"),
-            m_storeInfo.iUsedEntries);
-    PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - MaxNumLength = %d"),
-            m_storeInfo.iMaxNumLength);
-    PbkPrintToLog(_L("CntSymbianSimEngine::getEtelStoreInfoL() - MaxTextLength = %d"),
-            m_storeInfo.iMaxTextLength);
-    
+    // Start listening for events
     m_listener = new (ELeave) CntSimStoreEventListener(m_engine, m_etelStore);
     m_listener->start();
 }
@@ -133,15 +129,15 @@
         User::Leave(KErrArgument);
     }
 
-    if (m_storeName.isEmpty()) {
+    if (m_storeInfo.m_storeName.isEmpty()) {
         // Default to ADN store
-        m_storeName = (QLatin1String) KParameterValueSimStoreNameAdn;
+        m_storeInfo.m_storeName = (QLatin1String) KParameterValueSimStoreNameAdn;
         storeName.Copy(KETelIccAdnPhoneBook);
-    } else if (m_storeName == KParameterValueSimStoreNameFdn) {
+    } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameFdn) {
         storeName.Copy(KETelIccFdnPhoneBook);
-    } else if (m_storeName == KParameterValueSimStoreNameAdn) {
+    } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameAdn) {
         storeName.Copy(KETelIccAdnPhoneBook);
-    } else if (m_storeName == KParameterValueSimStoreNameSdn) {
+    } else if (m_storeInfo.m_storeName == KParameterValueSimStoreNameSdn) {
         storeName.Copy(KETelIccSdnPhoneBook);
     }
 
@@ -233,8 +229,8 @@
     
     // start reading
     m_buffer.Zero();
-    m_buffer.ReAlloc(KOneSimContactBufferSize*m_storeInfo.iTotalEntries);
-    m_etelStore.Read(iStatus, 1, m_storeInfo.iTotalEntries, m_buffer);
+    m_buffer.ReAlloc(KOneSimContactBufferSize*m_storeInfo.m_totalEntries);
+    m_etelStore.Read(iStatus, 1, m_storeInfo.m_totalEntries, m_buffer);
     SetActive();
     m_state = ReadReservedSlotsState;
     
@@ -271,7 +267,7 @@
         {
             QList<QContact> contacts = decodeSimContactsL(m_buffer);
 
-            // set sync target and set type as read only
+            // set sync target and read only access constraint and display label
             QList<QContact>::iterator i;
             for (i = contacts.begin(); i != contacts.end(); ++i) {
                 QContactSyncTarget syncTarget;
@@ -281,6 +277,7 @@
                 QContactType contactType = i->detail(QContactType::DefinitionName);
                 m_engine.setReadOnlyAccessConstraint(&contactType);
                 i->saveDetail(&contactType);
+                m_engine.updateDisplayLabel(*i);
             }
             
             emit m_simStore.readComplete(contacts, QContactManager::NoError);
@@ -417,7 +414,7 @@
                             QString number = lastNumber.number();
                             number.insert(0, "+");
                             lastNumber.setNumber(number);
-                            if (m_readOnlyAccess)
+                            if (m_storeInfo.m_readOnlyAccess)
                                 m_engine.setReadOnlyAccessConstraint(&lastNumber);
                             currentContact.saveDetail(&lastNumber);
                         }
@@ -441,7 +438,7 @@
                         QContactName name;
                         QString nameString = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
                         name.setCustomLabel(nameString);
-                        if (m_readOnlyAccess)
+                        if (m_storeInfo.m_readOnlyAccess)
                             m_engine.setReadOnlyAccessConstraint(&name);
                         currentContact.saveDetail(&name);
                         QContactManager::Error error(QContactManager::NoError);
@@ -456,7 +453,7 @@
                     QContactNickname nickName;
                     QString name = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
                     nickName.setNickname(name);
-                    if (m_readOnlyAccess)
+                    if (m_storeInfo.m_readOnlyAccess)
                         m_engine.setReadOnlyAccessConstraint(&nickName);
                     currentContact.saveDetail(&nickName);
                 }
@@ -468,7 +465,7 @@
                     QContactPhoneNumber phoneNumber;
                     QString number = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
                     phoneNumber.setNumber(number);
-                    if (m_readOnlyAccess)
+                    if (m_storeInfo.m_readOnlyAccess)
                         m_engine.setReadOnlyAccessConstraint(&phoneNumber);
                     currentContact.saveDetail(&phoneNumber);
                 }
@@ -486,7 +483,7 @@
                     QContactEmailAddress email;
                     QString emailAddress = QString::fromUtf16(bufPtr.Ptr(), bufPtr.Length());
                     email.setEmailAddress(emailAddress);
-                    if (m_readOnlyAccess)
+                    if (m_storeInfo.m_readOnlyAccess)
                         m_engine.setReadOnlyAccessConstraint(&email);
                     currentContact.saveDetail(&email);
                 }
@@ -531,46 +528,42 @@
 */
 void CntSimStorePrivate::encodeSimContactL(QContact* contact, TDes8& rawData) const
 {
-    // TODO: get detail definition schema and verify (unique)
-
     // Keep track of the count of phone numbers added
     int phoneNumberCount(0);
-    int emailCount(0);
 
+    // Create buffer
     CPhoneBookBuffer* pbBuffer = new(ELeave) CPhoneBookBuffer();
     CleanupStack::PushL(pbBuffer);
     pbBuffer->Set(&rawData);
     User::LeaveIfError(pbBuffer->AddNewEntryTag());
 
-    foreach(QContactDetail detail, contact->details()) {
+    // Loop through details
+    foreach(QContactDetail detail, contact->details()) 
+    {
         QString definitionName = detail.definitionName();
+        
+        // NOTE: If the detail is too long let the etel store return
+        // an error about it. We could check the maximum lenghts for each
+        // detail but then we would need to read it before every write operation
+        // bacause it seems to change depending on how full the sim card is.
 
-        if (definitionName == QContactName::DefinitionName) {
-            // Name
+        // Name
+        if (definitionName == QContactName::DefinitionName) 
+        {
             QContactName nameDetail = static_cast<QContactName>(detail);
-            // Trim to the max possible length
-            QString name = nameDetail.customLabel().left(m_storeInfo.iMaxTextLength);
-            if (name.isEmpty()) {
-                name = "Unnamed";
-            }
-            putTagAndValueL(
-                pbBuffer,
-                RMobilePhoneBookStore::ETagPBText,
-                name);
-            // Replace detail value with the trimmed one
-            nameDetail.setCustomLabel(name);
-            contact->saveDetail(&nameDetail);
-        } else if (definitionName == QContactPhoneNumber::DefinitionName
-#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1
-            && (phoneNumberCount == 0
-            || phoneNumberCount <= m_storeInfo.iMaxAdditionalNumbers)) {
-#else
-            && phoneNumberCount == 0) {
-#endif
-            // Phone number
+            QString name = nameDetail.customLabel();
+            putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBText, name);
+        }
+        // Phone number
+        else if (definitionName == QContactPhoneNumber::DefinitionName)
+        {
+            if (m_storeInfo.m_additionalNumberSupported == false && phoneNumberCount>0)
+                User::Leave(KErrNotSupported);
+
             phoneNumberCount++;
-            QContactPhoneNumber numberDetail = static_cast<QContactPhoneNumber>(detail);
-            QString number = numberDetail.number();
+            QString number = static_cast<QContactPhoneNumber>(detail).number();
+            if (number.isEmpty())
+                continue;
 
             // Verify the number only contains legal digits
             foreach (const QChar character, number) {
@@ -585,58 +578,48 @@
                 }
             }
 
-            // TODO: check if the number is empty (do we have a test case for that?)
-
-            // Verify the number length
-            PbkPrintToLog(_L("CntSymbianSimEngine::encodeSimContactL() - phone number length = %d"),
-                numberDetail.number().length());
-            if (numberDetail.number().length() > m_storeInfo.iMaxNumLength) {
-                User::Leave(KErrTooBig);
-            }
-
             if (phoneNumberCount > 1) {
                 // Mark the beginning of an additional number
                 User::LeaveIfError(pbBuffer->AddNewNumberTag());
             }
 
             // The number itself
-            putTagAndValueL(
-                pbBuffer,
-                RMobilePhoneBookStore::ETagPBNumber,
-                number);
-#ifndef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1
-        // Phonebook info version 1 does not support nick name or e-mail
-        } else if (definitionName == QContactNickname::DefinitionName
-            && m_storeInfo.iMaxSecondNames > 0) {
-            // nickname
-            QContactNickname nicknameDetail = static_cast<QContactNickname>(detail);
-            // Trim to the max possible length;
-            QString nickname = nicknameDetail.nickname().left(m_storeInfo.iMaxTextLengthSecondName);
-            putTagAndValueL(
-                pbBuffer,
-                RMobilePhoneBookStore::ETagPBSecondName,
-                nickname);
-            // Replace detail value with the trimmed one
-            nicknameDetail.setNickname(nickname);
-            contact->saveDetail(&nicknameDetail);
-        } else if (definitionName == QContactEmailAddress::DefinitionName
-            && emailCount < m_storeInfo.iMaxEmailAddr) {
-            emailCount++;
-            QContactEmailAddress emailDetail = static_cast<QContactEmailAddress>(detail);
-            if (emailDetail.emailAddress().length() > m_storeInfo.iMaxTextLengthEmailAddr) {
-                User::Leave(KErrTooBig);
-            }
-            putTagAndValueL(
-                pbBuffer,
-                RMobilePhoneBookStore::ETagPBEmailAddress,
-                emailDetail.emailAddress());
-#endif
+            putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBNumber, number);
+        }
+        // Nickname
+        else if (definitionName == QContactNickname::DefinitionName)
+        {
+            if (m_storeInfo.m_secondNameSupported == false)
+                User::Leave(KErrNotSupported);
+            
+            QString nickname = static_cast<QContactNickname>(detail).nickname();
+            if (nickname.isEmpty())
+                continue;
+            
+            putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBSecondName, nickname);
+        } 
+        // email
+        else if (definitionName == QContactEmailAddress::DefinitionName)
+        {
+            if (m_storeInfo.m_emailSupported == false)
+                User::Leave(KErrNotSupported);
+            
+            QString email = static_cast<QContactEmailAddress>(detail).emailAddress();
+            if (email.isEmpty())
+                continue;            
+            
+            putTagAndValueL(pbBuffer, RMobilePhoneBookStore::ETagPBEmailAddress, email);
+        }
         // These are ignored in the conversion
-        } else if (definitionName == QContactSyncTarget::DefinitionName
+        else if (definitionName == QContactSyncTarget::DefinitionName
             || definitionName == QContactDisplayLabel::DefinitionName
-            || definitionName == QContactType::DefinitionName) {
+            || definitionName == QContactType::DefinitionName) 
+        {
             // Do nothing
-        } else {
+        } 
+        else 
+        {
+            // Unknown detail
             User::Leave(KErrArgument);
         }
     }
@@ -675,3 +658,154 @@
     CleanupStack::PopAndDestroy(pbBuffer);
     return reservedSlots;
 }
+
+void CntSimStorePrivate::writeL(QContact *contact)
+{
+    if (IsActive())
+        User::Leave(KErrLocked);
+    
+    // get index
+    int index = KErrNotFound;
+    if (contact->id().managerUri() == m_managerUri &&
+        contact->localId() > 0) {
+        index = contact->localId();
+    }
+    
+    // encode
+    m_buffer.Zero();
+    m_buffer.ReAlloc(KOneSimContactBufferSize);
+    encodeSimContactL(contact, m_buffer);
+
+    // write
+    TRequestStatus status;
+    m_etelStore.Write(status, m_buffer, index);
+    User::WaitForRequest(status);
+    User::LeaveIfError(status.Int());
+    
+    // update id
+    QContactId id;
+    id.setLocalId(index);
+    id.setManagerUri(m_managerUri);
+    contact->setId(id);
+}
+
+void CntSimStorePrivate::removeL(int index)
+{
+    if (IsActive())
+        User::Leave(KErrLocked);
+    
+    // NOTE:
+    // If index points to an empty slot and running in hardware the 
+    // delete operation will not return any error.
+    
+    TRequestStatus status;
+    m_etelStore.Delete(status, index);
+    User::WaitForRequest(status);
+    User::LeaveIfError(status.Int());
+}
+
+void CntSimStorePrivate::updateStoreInfoL()
+{
+#ifdef SYMBIANSIM_BACKEND_PHONEBOOKINFOV1
+    RMobilePhoneBookStore::TMobilePhoneBookInfoV1 info;
+    RMobilePhoneBookStore::TMobilePhoneBookInfoV1Pckg infoPckg(info);
+#else
+    RMobilePhoneBookStore::TMobilePhoneBookInfoV5 info;
+    RMobilePhoneBookStore::TMobilePhoneBookInfoV5Pckg infoPckg(info);
+#endif
+
+    // Get info
+    TRequestStatus status;
+    m_etelStore.GetInfo(status, infoPckg);
+    User::WaitForRequest(status);
+    User::LeaveIfError(status.Int());
+
+    // Update entry counts
+    m_storeInfo.m_totalEntries = info.iTotalEntries;
+    m_storeInfo.m_usedEntries  = info.iUsedEntries;
+    
+#ifdef SYMBIANSIM_BACKEND_TEST_EXTRADETAILS
+    // Check if store supports the extra details
+    //
+    // NOTE: 
+    // We cannot rely on TMobilePhoneBookInfoV5 to check if we support
+    // these details. For example iMaxSecondNames is allways -1 even if the sim
+    // card supports a second name.
+    //
+    // There is an API for checking these but it's Nokia internal so we must
+    // do it this way - by checking if saving these details is possible.
+    
+    // Have we checked these already?
+    if (m_extraDetailsChecked == false)
+    {
+        // Cannot test extra details if sim card is full 
+        if (m_storeInfo.m_usedEntries == m_storeInfo.m_totalEntries)
+            return;
+
+        // Cancel store event listener temporarily
+        if (m_listener)
+            m_listener->Cancel();
+
+        // Test writing nickname
+        QContact contact;
+        QContactNickname nick;
+        nick.setNickname("simbackend test");
+        contact.saveDetail(&nick);
+        TRAPD(err, {
+            m_storeInfo.m_secondNameSupported = true; // enable to pass encodeSimContactL()
+            writeL(&contact);
+            removeL(contact.localId());
+        } );
+        if (err)
+            m_storeInfo.m_secondNameSupported = false;
+
+        // Test writing additional number
+        contact = QContact();
+        QContactPhoneNumber num1;
+        num1.setNumber("1111111111");
+        contact.saveDetail(&num1);
+        QContactPhoneNumber num2;
+        num2.setNumber("2222222222");
+        contact.saveDetail(&num2);
+        TRAP(err, {
+            m_storeInfo.m_additionalNumberSupported = true; // enable to pass encodeSimContactL()
+            writeL(&contact); 
+            removeL(contact.localId()); 
+        } );
+        if (err)
+            m_storeInfo.m_additionalNumberSupported = false;
+
+        // Test writing email
+        contact = QContact();
+        QContactEmailAddress email;
+        email.setEmailAddress("simbackend@test.com");
+        contact.saveDetail(&email);
+        TRAP(err, {
+            m_storeInfo.m_emailSupported = true; // enable to pass encodeSimContactL()
+            writeL(&contact); 
+            removeL(contact.localId()); 
+        } );
+        if (err)
+            m_storeInfo.m_emailSupported = false;
+
+        // Start store event listener again
+        if (m_listener)
+            m_listener->start();
+
+        m_extraDetailsChecked = true;
+    }
+#endif
+
+    /*
+    qDebug() << "Store info:"
+        << "\nStore name                :" << m_storeInfo.m_storeName
+        << "\nTotal entries             :" << m_storeInfo.m_totalEntries
+        << "\nUsed entries              :" << m_storeInfo.m_usedEntries
+        << "\nRead only access          :" << m_storeInfo.m_readOnlyAccess
+        << "\nNumber supported          :" << m_storeInfo.m_numberSupported
+        << "\nName supported            :" << m_storeInfo.m_nameSupported
+        << "\nSecond name supported     :" << m_storeInfo.m_secondNameSupported
+        << "\nAdditional name supported :" << m_storeInfo.m_additionalNumberSupported
+        << "\nEmail supported           :" << m_storeInfo.m_emailSupported;
+    */
+}