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