Symbian OS應(yīng)用開發(fā)學(xué)習(xí)筆記之通訊錄(電話薄Contacts)
Symbian OS通訊錄模型
Symbian OS手機(jī)的通訊錄采用文件方式存儲(chǔ),用symbian自己的說法就是通訊錄數(shù)據(jù)庫(kù)。每個(gè)Symbian OS手機(jī)都有一個(gè)默認(rèn)的通訊錄數(shù)據(jù)庫(kù),這個(gè)通訊錄數(shù)據(jù)庫(kù)在2nd和3rd兩個(gè)版本手機(jī)中的位置是不同的,前者是c:\ system\data\Contacts.cdb,后者是c:\private\100012a5\DBS_100065FF_Contacts.cdb。不管怎么說兩者都在內(nèi)部閃存中,也就是跟優(yōu)盤差不多的介質(zhì),由于symbian OS的文件系統(tǒng)暫時(shí)還沒有仔細(xì)拜讀過,所以具體差別暫不知,總之掉電不會(huì)失去就是了。
Symbian OS的手機(jī)通訊錄在開發(fā)上的操作依靠Symbian OS通訊錄模型(Contacts Model)來實(shí)現(xiàn)。通訊錄模型由通訊錄數(shù)據(jù)庫(kù)、通訊錄條目(項(xiàng))和通信錄域三者組成,他們之間的關(guān)系是:一個(gè)手機(jī)除了系統(tǒng)自帶的默認(rèn)通訊錄數(shù)據(jù)庫(kù)外還可以帶多個(gè)通訊錄數(shù)據(jù)庫(kù);一個(gè)通訊錄數(shù)據(jù)庫(kù)有多個(gè)通訊錄條目組成,這里每個(gè)條目就是每個(gè)聯(lián)系人,具體數(shù)量限制各個(gè)手機(jī)應(yīng)該不一樣;而一個(gè)通訊錄條目又有多個(gè)通訊錄域組成,好比有姓名、工作手機(jī)號(hào)碼、家庭手機(jī)號(hào)碼等等,每個(gè)項(xiàng)就是一個(gè)域。
在這里Symbian為了統(tǒng)一通訊錄格式,所以將通訊錄條目采用vCard格式MIME規(guī)范(RFC 1521)所定義的明碼文本來定義域,具體的vCard簡(jiǎn)單介紹見附錄。
Symbian OS通訊錄操作API類
知道了Contacts Model的概念,Symbian OS將很多系統(tǒng)API操作封裝為幾個(gè)類:
CContactDatabase(數(shù)據(jù)庫(kù)類):負(fù)責(zé)新建、打開、關(guān)閉等基本數(shù)據(jù)庫(kù)操作外,還負(fù)責(zé)數(shù)據(jù)庫(kù)更新(通訊錄條目的新建、修改、刪除需要通過CContactDatabase類的操作才能實(shí)現(xiàn))、排序和查找,另外還有一些建立快速撥號(hào)之類的操作也是通過它來實(shí)現(xiàn)。
CContactItem(通訊錄條目類):由唯一的一個(gè)TContactItemId(一個(gè)TInt32類型的宏定義)標(biāo)識(shí),負(fù)責(zé)具體一個(gè)通訊錄條目的創(chuàng)建、修改,其直接管理每一個(gè)通訊錄域
CContactItemField(域類):每一個(gè)域就是一個(gè)真實(shí)單一的數(shù)據(jù),該數(shù)據(jù)的類型具有存儲(chǔ)類型(TStorageType)和域類型(TFieldType)同時(shí)決定,具體的四種存儲(chǔ)類型和多種域類型定義見系統(tǒng)頭文件cntdef.h內(nèi)的定義。
當(dāng)然還有很多其它的類,比如CContactItemFieldSet(域集類)、CContactFieldStorage(與存儲(chǔ)基類)、CContactTextFields(文本存儲(chǔ)域類)、MContactDbObserver(通訊數(shù)據(jù)庫(kù)觀察類)等等,涉及面太大,具體也不能憑空說清楚,代碼中出現(xiàn)就知道了。
Symbian OS通訊錄操作實(shí)例
例1、 打開和關(guān)閉數(shù)據(jù)庫(kù)
CContactDatabase::OpenL()函數(shù)有兩個(gè)重載函數(shù)。如果該函數(shù)沒有給出一個(gè)參數(shù),就打開默認(rèn)的數(shù)據(jù)庫(kù)。另一種情況是,應(yīng)用軟件設(shè)計(jì)師也可以傳遞一個(gè)有關(guān)數(shù)據(jù)庫(kù)的路徑和文件名,規(guī)定打開一個(gè)指定數(shù)據(jù)庫(kù)。
//打開默認(rèn)數(shù)據(jù)庫(kù)
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
//取得當(dāng)前數(shù)據(jù)庫(kù)所有通訊條目數(shù)
TInt numberOfContacts = contactsDb->CountL();
//釋放數(shù)據(jù)庫(kù)
CleanupStack::PopAndDestroy(contactsDb);
要注意的是:某個(gè)通信錄數(shù)據(jù)庫(kù)并不具有Close()函數(shù)或類似的函數(shù),否則我們壓入清除棧時(shí)就得用CleanupClosePushL()函數(shù)了。
例2、 創(chuàng)建數(shù)據(jù)庫(kù)
CContactDatabase::CreateL()函數(shù)與CContactDatabase::ReplaceL()函數(shù)之間的唯一差別就是:如果該數(shù)據(jù)庫(kù)已經(jīng)存在,前者會(huì)以KErrAlreadyExists退出。如前所述,如果沒有定義參數(shù),這些函數(shù)將創(chuàng)建一個(gè)默認(rèn)的數(shù)據(jù)庫(kù)。CContactDatabase::FindContactFile()函數(shù)給出了一個(gè)描述符,如果不存在默認(rèn)數(shù)據(jù)庫(kù)的話,該描述符就會(huì)返回該默認(rèn)數(shù)據(jù)庫(kù)的位置。
// 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;
//是否存在默認(rèn)數(shù)據(jù)庫(kù)
if(CContactDatabase::FindContactFile(contactDbFilePath))
{
newDefaultContactDb = CContactDatabase::ReplaceL();
}
else
{
newDefaultContactDb = CContactDatabase::CreateL();
}
CleanupStack::PushL(newDefaultContactDb);
// 添加自己功能代碼
CleanupStack::PopAndDestroy(newDefaultContactDb);
注:以上代碼負(fù)責(zé)創(chuàng)建一個(gè)空的默認(rèn)數(shù)據(jù)庫(kù)。
例3、 讀取(遍歷)通訊錄條目
可以用TContactIter類(該類起到數(shù)據(jù)庫(kù)操作中類似游標(biāo)的作用)來遍歷一個(gè)通信錄數(shù)據(jù)庫(kù)。這個(gè)類提供了一整套的函數(shù),用于遍歷所有的通信錄項(xiàng)。所有的函數(shù)都用通信錄項(xiàng)ID (TContactItemId) 進(jìn)行操作,該ID 用于訪問某個(gè)特定的通信錄項(xiàng)。
// Open the default contacts database:
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
TContactIter iter(*contactsDb);
TContactItemId cardId;
//循環(huán)遍歷
while( ( cardId = iter.NextL() ) != KNullContactId )
{
//讀取相應(yīng)項(xiàng),這里之所以稱其card,就是其實(shí)際相當(dāng)于讀一個(gè)完整的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");
//以上定義的字符串,在以后例子中將直接使用,不再重新進(jìn)行定義了
//打開默認(rèn)數(shù)據(jù)庫(kù)
CContactDatabase* contactsDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactsDb);
// 建立一個(gè)新條目
CContactItem* contact = CContactCard::NewLC();
//創(chuàng)建一個(gè)新的文本存儲(chǔ)類型的姓域
CContactItemField* field =
CContactItemField::NewLC(KStorageTypeText, KUidContactFieldFamilyName);
//將姓域建立與vCard的映射
field->SetMapping(KUidContactFieldVCardMapUnusedN);
//設(shè)置域標(biāo)簽
field->SetLabelL(KSurnameLabel);
//設(shè)置域值
field->TextStorage()->SetTextL(KSurname);
//把該域加入到新建的條目中
contact->AddFieldL(*field);
CleanupStack::Pop();
//添加文本存儲(chǔ)類型的名域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldGivenName);
field->SetMapping(KUidContactFieldVCardMapUnusedN);
field->SetLabelL(KForenameLabel);
field->TextStorage()->SetTextL(KForename);
contact->AddFieldL(*field);
CleanupStack::Pop();
//添加文本存儲(chǔ)類型的手機(jī)號(hào)碼域
field=CContactItemField::NewLC(KStorageTypeText, KUidContactFieldPhoneNumber);
field->SetMapping(KUidContactFieldVCardMapTEL);
field->SetLabelL(KWorkPhoneLabel);
field->TextStorage()->SetTextL(KWorkPhone);
contact->AddFieldL(*field);
CleanupStack::Pop();
//把建立的新記錄添加到數(shù)據(jù)庫(kù)中
contactsDb->AddNewContactL(*contact);
contactsDb->SetOwnCardL(*contact);
CleanupStack::PopAndDestroy(2); // contact contactsDb
例5、 查找并更新通訊錄條目
這個(gè)例子比較復(fù)雜,涉及的查找函數(shù)為FindAsyncL,該類函數(shù)實(shí)例有:
CContactIdArray * CContactDatabase::FindLC(const TDesC &aText, const CContactItemFieldDef *aFieldDef);
CIdleFinder * CContactDatabase::FindAsyncL(const TDesC &aText, const CContactItemFieldDef *aFieldDef, MIdleFindObserver *aObserver);
還有對(duì)應(yīng)的FindInTextDefLC()和FindInTextDefAsyncL()各兩組,具體參見sdk
下面是具體代碼實(shí)例:
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);
//提交所做的修改,如果這里不做更改可以調(diào)用CloseContactL直接關(guān)閉
//但是一旦用OpenContactL或OpenContactLX打開就必須調(diào)用兩者之一關(guān)閉
iContactsDb ->CommitContactL(*ownCard);
CleanupStack::PopAndDestroy();// ownCard
}
CleanupStack::PopAndDestroy();//result;
}
}
CleanupStack::PopAndDestroy(3);// iContactsDb、iFieldDef、iFinder
例6、 導(dǎo)出所選通訊錄條目到文件(vCard)
在這里,主要使用CContactDatabase類中ExportSelectedContactsL函數(shù),關(guān)于該函數(shù)的定義可以查看SDK文檔;而且在這里與前次遍歷不一樣的是,加了一個(gè)過濾器CCntFilter類,雖然取法仍然是所有通訊條目,但做法不一樣,具體例程如下:
RFs fileSession;
//連接文件服務(wù)器
User::LeaveIfError(fileSession.Connect());
CleanupClosePushL(fileSession); //1
//打開默認(rèn)數(shù)據(jù)庫(kù)
CContactDatabase* contactDb = CContactDatabase::OpenL();
CleanupStack::PushL(contactDb); //2
//新建過濾器
CCntFilter* filter = CCntFilter::NewLC(); //3
filter->SetContactFilterTypeALL(EFalse);
//按vCard格式導(dǎo)出
filter->SetContactFilterTypeCard(ETrue);
//安裝filter
contactDb->FilterDatabaseL(*filter);
//取出滿足條件的記錄數(shù)據(jù)項(xiàng)數(shù)組
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;
//導(dǎo)出到文件
contactDb->ExportSelectedContactsL(id,*exportContact, aWriteStream,
CContactDatabase::EExcludeUid);
CleanupStack::PopAndDestroy(6,contactDb);
Symbian S60獨(dú)有通訊錄操作API引擎
以上是適用于任何Symbian OS通訊錄操作的方法,在S60平臺(tái)SDK中nokia專門為我們建立了一個(gè)操作通訊錄的引擎,以及相對(duì)應(yīng)的產(chǎn)生了一些封裝的類:
CPbkContactEngine(通訊錄引擎類):如果已經(jīng)存在一個(gè)缺省數(shù)據(jù)庫(kù),CPbkContactEngine::NewL()就連接到該數(shù)據(jù)庫(kù),否則創(chuàng)建該數(shù)據(jù)庫(kù)。當(dāng)然也可以傳入文件名,打開一個(gè)指定的通訊錄數(shù)據(jù)庫(kù),根據(jù)頭文件cpbkcontactengine.h,他就是對(duì)CContactDatabase和觀察器類MContactDbObserver封裝了下并進(jìn)行了一些優(yōu)化,簡(jiǎn)便了我們操作時(shí)的一些代碼,為此操作起來比較方便。
CPbkContactItem(通訊錄條目類):該類頭文件是CPbkContactItem.h,主要對(duì)通訊錄條目類CContactItem的封裝和優(yōu)化,可以看出很多導(dǎo)出函數(shù)都是一致的。
TPbkContactItemField(域類):不用想也知道這個(gè)類是怎么來的了,該類的頭文件tpbkcontactitemfield.h。有興趣的可以去研究比照一下。
當(dāng)然也有其他一些封裝的類,只不過離通訊錄模型比較遠(yuǎn)的,我們就不多展開了,在例子中看其使用。
例7、 新建通訊錄條目
_LIT(KFName,"King");
_LIT(KLName,"Chai");
_LIT(KNumber,"13777777777");
//運(yùn)用引擎打開默認(rèn)通訊錄
CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
CleanupStack::PushL(PbkContactEngine);//1
//新建一空通信錄項(xiàng)
CPbkContactItem* contact = iPbkContactEngine->CreateEmptyContactL();
CleanupStack::PushL(contact); //2
//設(shè)置first name 域
TPbkContactItemField* field = contact->FindField(EPbkFieldIdFirstName);
CleanupStack::PushL(contact); //3
field->TextStorage()->SetTextL(KFName);
//設(shè)置last name 域
field = contact->FindField(EPbkFieldIdLastName);
field->TextStorage()->SetTextL(KLName);
//設(shè)置手機(jī)號(hào)碼域
field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
field->TextStorage()->SetTextL(KNumber);
//可以添加其他值域
//...
//修改后結(jié)果添加到數(shù)據(jù)庫(kù)中,并返回這個(gè)通信錄項(xiàng)的id,該id可以以后使用
TContactItemId Id = iPbkContactEngine->AddNewContactL(*contact);
CleanupStack::PopAndDestroy(3);
以上代碼是否比例4的代碼相對(duì)來說更簡(jiǎn)單些啊?
例8、 修改通訊錄條目
實(shí)現(xiàn)修改和新建的代碼類似,不同是你需要找到你要修改的通訊錄條目aContactId,然后找到要修改的域進(jìn)行修改,最后導(dǎo)入數(shù)據(jù)庫(kù)。
_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
//設(shè)置并確認(rèn)修改
field->TextStorage()->SetTextL(phonenumber);
iPbkContactEngine ->CommitContactL(*contact);
CleanupStack::PopAndDestroy(2)
附錄vCard:
手機(jī)應(yīng)用開發(fā)中經(jīng)常會(huì)遇到有關(guān)OBEX協(xié)議的問題,其實(shí)在通信錄開發(fā)中也遵循這個(gè)協(xié)議,通信錄中的數(shù)據(jù)是存在一個(gè)名叫vCard的載體里。vCard是一類電子名片,得到許多電子設(shè)備(如PDA和移動(dòng)電話等)的支持。vCard的目的是:在這些設(shè)備之間用某些協(xié)議實(shí)現(xiàn)方便的通信錄數(shù)據(jù)傳遞。可以將vCard編碼成MIME規(guī)范(RFC 1521)所定義的明碼文本。這種編碼確保了各種vCard與限制為7位字符集(如在SMS消息中使用的編碼)的傳遞編碼的完全兼容。
一張vCard被格式化如下(說實(shí)話,下面這個(gè)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:steve.wilkinson@emccsoft.com
REV:20030909T164330Z
END:VCARD
通信錄模型中的許多功能都與vCard的處理有關(guān),以保證Symbian應(yīng)用開發(fā)伙伴們能方便地編制符合電子名片及通訊錄交換方面的工業(yè)標(biāo)準(zhǔn)的代碼。
致謝:
全文暫告一個(gè)段落,應(yīng)該說自己書都沒有看過,主要參考了網(wǎng)上一些人整理的文章,感謝csdn的風(fēng)之云的玩轉(zhuǎn)通訊錄(http://blog.csdn.net/welcome_ck/archive/2005/01/06/242686.aspx),本文原型主要來自于他,另外還參考了兩篇代碼文檔,一篇用C++實(shí)現(xiàn)的訪問Symbian手機(jī)電話薄(出處http://jcszjswkzhou.itpub.net/post/32804/369208),另一篇忘了出處。由于很多例子本人只是走讀改進(jìn)了下,還未來得及親測(cè),所以如果有網(wǎng)友親測(cè)了某些實(shí)例,通不過還望能告知下,當(dāng)然小筆在用到的時(shí)候也會(huì)親測(cè)下,在此謝過以上三人,當(dāng)然還要感謝公司和兩位同事,并先感謝有時(shí)間來親測(cè)的網(wǎng)友。
另:轉(zhuǎn)載煩請(qǐng)注明
http://www.shnenglu.com/franksunny/,以便更多的學(xué)習(xí)交流
posted on 2008-06-27 08:05
frank.sunny 閱讀(6421)
評(píng)論(8) 編輯 收藏 引用 所屬分類:
symbian 開發(fā)