Symbian OS通訊錄

Symbian OS通訊錄模型
Symbian OS手機的通訊錄採用文件方式存儲,用symbian自己的說法就是通訊錄數據庫。每個Symbian OS手機都有一個默認的通訊錄數據庫,這個通訊錄數據庫在2nd和3rd兩個版本手機中的位置是不同的,前者是c:/ system/data/Contacts.cdb,後者是c:/private/100012a5/DBS_100065FF_Contacts.cdb。不管怎麼說兩者都在內部閃存中,也就是跟優盤差不多的介質,由於symbian OS的文件系統暫時還沒有仔細拜讀過,所以具體差別暫不知,總之掉電不會失去就是了。
Symbian OS的手機通訊錄在開發上的操作依靠Symbian OS通訊錄模型(Contacts Model)來實現。通訊錄模型由通訊錄數據庫、通訊錄條目(項)和通信錄域三者組成,他們之間的關係是:一個手機除了系統自帶的默認通訊錄數據庫外還可以帶多個通訊錄數據庫;一個通訊錄數據庫有多個通訊錄條目組成,這裏每個條目就是每個聯繫人,具體數量限制各個手機應該不一樣;而一個通訊錄條目又有多個通訊錄域組成,好比有姓名、工作手機號碼、家庭手機號碼等等,每個項就是一個域。
在這裏Symbian爲了統一通訊錄格式,所以將通訊錄條目採用vCard格式MIME規範(RFC 1521)所定義的明碼文本來定義域,具體的vCard簡單介紹見附錄。
 
Symbian OS通訊錄操作API類
知道了Contacts Model的概念,Symbian OS將很多系統API操作封裝爲幾個類:
CContactDatabase(數據庫類):負責新建、打開、關閉等基本數據庫操作外,還負責數據庫更新(通訊錄條目的新建、修改、刪除需要通過CContactDatabase類的操作才能實現)、排序和查找,另外還有一些建立快速撥號之類的操作也是通過它來實現。
CContactItem(通訊錄條目類):由唯一的一個TContactItemId(一個TInt32類型的宏定義)標識,負責具體一個通訊錄條目的創建、修改,其直接管理每一個通訊錄域
CContactItemField(域類):每一個域就是一個真實單一的數據,該數據的類型具有存儲類型(TStorageType)和域類型(TFieldType)同時決定,具體的四種存儲類型和多種域類型定義見系統頭文件cntdef.h內的定義。
當然還有很多其它的類,比如CContactItemFieldSet(域集類)、CContactFieldStorage(與存儲基類)、CContactTextFields(文本存儲域類)、MContactDbObserver(通訊數據庫觀察類)等等,涉及面太大,具體也不能憑空說清楚,代碼中出現就知道了。
 
Symbian OS通訊錄操作實例
例1、       打開和關閉數據庫
CContactDatabase::OpenL()函數有兩個重載函數。如果該函數沒有給出一個參數,就打開默認的數據庫。另一種情況是,應用軟件設計師也可以傳遞一個有關數據庫的路徑和文件名,規定打開一個指定數據庫。
//打開默認數據庫
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
//取得當前數據庫所有通訊條目數
TInt numberOfContacts = contactsDb->CountL();
//釋放數據庫
CleanupStack::PopAndDestroy(contactsDb);
要注意的是:某個通信錄數據庫並不具有Close()函數或類似的函數,否則我們壓入清除棧時就得用CleanupClosePushL()函數了。
 
例2、       創建數據庫
CContactDatabase::CreateL()函數與CContactDatabase::ReplaceL()函數之間的唯一差別就是:如果該數據庫已經存在,前者會以KErrAlreadyExists退出。如前所述,如果沒有定義參數,這些函數將創建一個默認的數據庫。CContactDatabase::FindContactFile()函數給出了一個描述符,如果不存在默認數據庫的話,該描述符就會返回該默認數據庫的位置。
// If one is found, replace it with a new empty default database.
// If no default database is found, create a new one.
TFileName contactDbFilePath;
CContactDatabase* newDefaultContactDb;
//是否存在默認數據庫
if(CContactDatabase::FindContactFile(contactDbFilePath))
{
 newDefaultContactDb = CContactDatabase::ReplaceL();
}
else
{
 newDefaultContactDb = CContactDatabase::CreateL();
}
CleanupStack::PushL(newDefaultContactDb);
// 添加自己功能代碼
CleanupStack::PopAndDestroy(newDefaultContactDb);
注:以上代碼負責創建一個空的默認數據庫。
 
例3、       讀取(遍歷)通訊錄條目
可以用TContactIter類(該類起到數據庫操作中類似遊標的作用)來遍歷一個通信錄數據庫。這個類提供了一整套的函數,用於遍歷所有的通信錄項。所有的函數都用通信錄項ID (TContactItemId) 進行操作,該ID 用於訪問某個特定的通信錄項。
// Open the default contacts database:
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
TContactIter iter(*contactsDb);
TContactItemId cardId;
//循環遍歷
while( ( cardId = iter.NextL() ) != KNullContactId )
{
//讀取相應項,這裏之所以稱其card,就是其實際相當於讀一個完整的vCard條目
CContactItem* card = contactsDb->ReadContactL(cardId);
CleanupStack::PushL(card);
 
//添加自己功能代碼
//……
contactsDb->CloseContactL(card->Id());
CleanupStack::PopAndDestroy(); // card
}
CleanupStack::PopAndDestroy(); // contactsDb
 
 
例4、       新建通訊錄條目
// 字符串聲明
_LIT(KForenameLabel,"Forename");//中文“名”
_LIT(KSurnameLabel,"Surname"); //中文“姓”
_LIT(KWorkPhoneLabel,"Work Phone");
_LIT(KForename,"Steve");
_LIT(KOtherForename,"Bob");
_LIT(KSurname,"Wilkinson");
_LIT(KWorkPhone,"+441617779700");
//以上定義的字符串,在以後例子中將直接使用,不再重新進行定義了
//打開默認數據庫
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
// 建立一個新條目
CContactItem* contact = CContactCard::NewLC();
//創建一個新的文本存儲類型的姓域
CContactItemField* field =
CContactItemField::NewLC(KStorageTypeText, KUidContactFieldFamilyName);
//將姓域建立與vCard的映射
field->SetMapping(KUidContactFieldVCardMapUnusedN);
//設置域標籤
field->SetLabelL(KSurnameLabel);
//設置域值
field->TextStorage()->SetTextL(KSurname);
//把該域加入到新建的條目中
contact->AddFieldL(*field);    
CleanupStack::Pop();
 
//添加文本存儲類型的名域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldGivenName);
field->SetMapping(KUidContactFieldVCardMapUnusedN);
field->SetLabelL(KForenameLabel);
field->TextStorage()->SetTextL(KForename);
contact->AddFieldL(*field);
CleanupStack::Pop();
 
//添加文本存儲類型的手機號碼域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldPhoneNumber);
field->SetMapping(KUidContactFieldVCardMapTEL);
field->SetLabelL(KWorkPhoneLabel);
field->TextStorage()->SetTextL(KWorkPhone);
contact->AddFieldL(*field);
CleanupStack::Pop();
 
//把建立的新記錄添加到數據庫中
contactsDb->AddNewContactL(*contact);
contactsDb->SetOwnCardL(*contact);
CleanupStack::PopAndDestroy(2); // contact contactsDb
 
例5、       查找並更新通訊錄條目
這個例子比較複雜,涉及的查找函數爲FindAsyncL,該類函數實例有:
CContactIdArray * CContactDatabase::FindLC(const TDesC &aText, const CContactItemFieldDef *aFieldDef);
CIdleFinder * CContactDatabase::FindAsyncL(const TDesC &aText, const CContactItemFieldDef *aFieldDef, MIdleFindObserver *aObserver);
還有對應的FindInTextDefLC()和FindInTextDefAsyncL()各兩組,具體參見sdk
下面是具體代碼實例:
CContactDatabase*  iContactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(iContactsDb);
CContactItemFieldDef*  iFieldDef = new (ELeave)CContactItemFieldDef();
CleanupStack::PushL(iFieldDef);
iFieldDef->AppendL(KUidContactFieldGivenName);
iFieldDef->AppendL(KUidContactFieldFamilyName);
_LIT(KFindToken, "Bond");
CIdleFinder * iFinder = iContactsDb->FindAsyncL( KFindToken, iFieldDef, this);
CleanupStack::PushL(iFinder);
if(iFinder->IsComplete())
   {
if(iFinder->Error() == KErrNone)
        {
CContactIdArray* result = iFinder->TakeContactIds();
CleanupStack:: PushL(result);
for(TInt i=0; i<result->Count(); i++)
   {
TContactItemId cardId = (*result)[i];
CContactItem* ownCard = iContactsDb ->OpenContactL(cardId);
CleanupStack::PushL(ownCard);
TInt index =
ownCard->CardFields().Find(KUidContactFieldGivenName);
ownCard->CardFields()[index].TextStorage()->SetTextL(KOtherForename);
//提交所做的修改,如果這裏不做更改可以調用CloseContactL直接關閉
//但是一旦用OpenContactL或OpenContactLX打開就必須調用兩者之一關閉
iContactsDb ->CommitContactL(*ownCard);
CleanupStack::PopAndDestroy();// ownCard
}
CleanupStack::PopAndDestroy();//result;
          }
}
CleanupStack::PopAndDestroy(3);// iContactsDb、iFieldDef、iFinder
 
例6、       導出所選通訊錄條目到文件(vCard)
在這裏,主要使用CContactDatabase類中ExportSelectedContactsL函數,關於該函數的定義可以查看SDK文檔;而且在這裏與前次遍歷不一樣的是,加了一個過濾器CCntFilter類,雖然取法仍然是所有通訊條目,但做法不一樣,具體例程如下:
RFs fileSession;
//連接文件服務器
User::LeaveIfError(fileSession.Connect());
CleanupClosePushL(fileSession); //1
//打開默認數據庫
CContactDatabase* contactDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactDb); //2
//新建過濾器
CCntFilter* filter = CCntFilter::NewLC(); //3
filter->SetContactFilterTypeALL(EFalse);
//按vCard格式導出
filter->SetContactFilterTypeCard(ETrue);
//安裝filter
contactDb->FilterDatabaseL(*filter);
//取出滿足條件的記錄數據項數組
CContactIdArray* exportContact = CContactIdArray::NewL(filter->iIds);
CleanupStack::PushL(exportContact); //4
 
RFile file;
//新建文件,aFileName是文件名字
file.Replace(fileSession,aFileName,EFileWrite);
CleanupClosePushL(file); //5
//聲明文件流
RFileWriteStream outputStream(file);
CleanupClosePushL(outputStream); //6
 
TUid id;
id.iUid = KVersitEntityUidVCard;
//導出到文件
contactDb->ExportSelectedContactsL(id,*exportContact, aWriteStream, CContactDatabase::EExcludeUid);
CleanupStack::PopAndDestroy(6,contactDb);
 
Symbian S60獨有通訊錄操作API引擎
以上是適用於任何Symbian OS通訊錄操作的方法,在S60平臺SDK中nokia專門爲我們建立了一個操作通訊錄的引擎,以及相對應的產生了一些封裝的類:
CPbkContactEngine(通訊錄引擎類):如果已經存在一個缺省數據庫,CPbkContactEngine::NewL()就連接到該數據庫,否則創建該數據庫。當然也可以傳入文件名,打開一個指定的通訊錄數據庫,根據頭文件cpbkcontactengine.h,他就是對CContactDatabase和觀察器類MContactDbObserver封裝了下並進行了一些優化,簡便了我們操作時的一些代碼,爲此操作起來比較方便。
CPbkContactItem(通訊錄條目類):該類頭文件是CPbkContactItem.h,主要對通訊錄條目類CContactItem的封裝和優化,可以看出很多導出函數都是一致的。
TPbkContactItemField(域類):不用想也知道這個類是怎麼來的了,該類的頭文件tpbkcontactitemfield.h。有興趣的可以去研究比照一下。
當然也有其他一些封裝的類,只不過離通訊錄模型比較遠的,我們就不多展開了,在例子中看其使用。
例7、       新建通訊錄條目
_LIT(KFName,"King");
_LIT(KLName,"Chai");
_LIT(KNumber,"13777777777");
//運用引擎打開默認通訊錄
CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
CleanupStack::PushL(PbkContactEngine);//1
//新建一空通信錄項
CPbkContactItem* contact = iPbkContactEngine->CreateEmptyContactL();
CleanupStack::PushL(contact); //2
//設置first name 域
TPbkContactItemField* field = contact->FindField(EPbkFieldIdFirstName);
CleanupStack::PushL(contact); //3
field->TextStorage()->SetTextL(KFName);
//設置last name 域
field = contact->FindField(EPbkFieldIdLastName);
field->TextStorage()->SetTextL(KLName);
//設置手機號碼域
field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
field->TextStorage()->SetTextL(KNumber);
//可以添加其他值域
//...
//修改後結果添加到數據庫中,並返回這個通信錄項的id,該id可以以後使用
TContactItemId Id = iPbkContactEngine->AddNewContactL(*contact);
CleanupStack::PopAndDestroy(3);
以上代碼是否比例4的代碼相對來說更簡單些啊?
 
例8、       修改通訊錄條目
實現修改和新建的代碼類似,不同是你需要找到你要修改的通訊錄條目aContactId,然後找到要修改的域進行修改,最後導入數據庫。
_LIT(number,"13500000000");
TBuf<11> phonenumber(number);
CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
CleanupStack::PushL(PbkContactEngine);//1
//這裏打開條目後加鎖,以防其它客戶端打開
CPbkContactItem* contact = iPbkContactEngine->OpenContactLCX(aContactId);
CleanupStack::PushL(contact); //2
//找到需要修改的field
TPbkContactItemField* field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
CleanupStack::PushL(field); //3
//設置並確認修改
field->TextStorage()->SetTextL(phonenumber);
iPbkContactEngine ->CommitContactL(*contact);
CleanupStack::PopAndDestroy(2)
 附錄vCard:
手機應用開發中經常會遇到有關OBEX協議的問題,其實在通信錄開發中也遵循這個協議,通信錄中的數據是存在一個名叫vCard的載體裏。vCard是一類電子名片,得到許多電子設備(如PDA和移動電話等)的支持。vCard的目的是:在這些設備之間用某些協議實現方便的通信錄數據傳遞。可以將vCard編碼成MIME規範(RFC 1521)所定義的明碼文本。這種編碼確保了各種vCard與限制爲7位字符集(如在SMS消息中使用的編碼)的傳遞編碼的完全兼容。
一張vCard被格式化如下(說實話,下面這個vCard我也沒看懂,有看懂的幫忙解釋下):
BEGIN:VCARD
VERSION:2.1
N:Wilkinson;Steve
FN:Steve Wilkinson
ORG:EMCC Software Ltd.
TEL;WORK;VOICE:01617779700
ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;108 Manchester
d.=0D=0ACarrington;Manchester;UK;M31 4BD;United Kingdom LABEL;WORK;ENCODING=QUOTED-PRINTABLE:108 Manchester Rd.=0D=0ACarrington=0D=0AManchester, UK M31 4BD=0D=0AUnited K= ingdom
EMAIL;PREF;INTERNET:[email protected]
REV:20030909T164330Z
END:VCARD
通信錄模型中的許多功能都與vCard的處理有關,以保證Symbian應用開發夥伴們能方便地編制符合電子名片及通訊錄交換方面的工業標準的代碼。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/Max__Payne/archive/2008/07/08/2624776.aspx

發佈了81 篇原創文章 · 獲贊 6 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章