Android contacts 的详解

一、包结构分析

相关联的的projects

1、Contacts相关

联系人分为了Contacts和ContactsCommon,与sim卡联系人相关的是在Telephony中,数据库是在ContactsProvider,apk要push到/system/priv-app/Contacts下

2、Contacts的包结构


3、ContactsCommon的包结构


二、功能分析

主要介绍一下和联系人相关的几个主要的功能,其他简单的就略过了,具体问题还是需要在code中看。

1、数据库分析

系统联系人数据库存放在如下位置data\data\com.android.providers.contacts\data bases\,正常的联系人都保存在contacts2.db中,个人信息保存在profile.db,但是两个数据库的基本结构都是一样的

表结构


其中有比较重要的三个表:account、mimetype、data、raw_contacts、contacts,其他的表结构用的较少,遇到问题再去ContactsProvider中查看code。

几张表直接的主要关联关系


Account是和账号有关


data表中存放的是联系人具体信息,每行存储一位联系人的某一类信息(如电话,姓名,邮箱、社交账号、地址等)


其中数据存放在dataX(x为数字)中,根据这行的mimetype_id来对应是什么类的信息数据,mimetype表中存放的是几种数据类型和_id,如图所示


对应到code中,可以通过下面的一些类去源码中具体看mimetype和dataX是对应关系

例如:


其他的类可以参考下面这段定义

[java]  view plain copy
  1. String MIMETYPE_EMAIL_V2 = Email.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/email_v2";  
  2. String MIMETYPE_IM = Im.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/im";  
  3. String MIMETYPE_NICKNAME = Nickname.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/nickname";  
  4. String MIMETYPE_ORGANIZATION = Organization.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/organization";  
  5. String MIMETYPE_PHONE_V2 = Phone.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/phone_v2";  
  6. String MIMETYPE_SIP_ADDRESS = SipAddress.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/sip_address";  
  7. String MIMETYPE_NAME = StructuredName.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/name";  
  8. String MIMETYPE_POSTAL_ADDRESS_V2 = StructuredPostal.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/postal-address_v2";  
  9. String MIMETYPE_IDENTITY = Identity.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/identity";  
  10. String MIMETYPE_PHOTO = Photo.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/photo";  
  11. String MIMETYPE_GROUP_MEMBERSHIP = GroupMembership.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/group_membership";  
  12. String MIMETYPE_NOTE = Note.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/note";  
  13. String MIMETYPE_WEBSITE = Website.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/website";  

下面是数据库中的视图(一些联合查询的封装),其中重要的几个已经标出。



以上都是一些常用到的数据表和视图,详细的数据表结构以及各个字段的数据类型和意义还需要在code中详细查看。

ContactsProvider中最主要的两个类:

com.android.providers.contacts.ContactsDatabaseHelper

com.android.providers.contacts.ContactsProvider2

ContactsDatabaseHelper继承自SqliteOpenHelper,是对数据库的创建和更新的操作,该类中有详细的每个表的字段和数据类型的定义,以及数据库根据version更新和对联系人部分数据CURD操作封装的声明,提供给ContactsProvider2调用

ContactsProvider2继承自AbstractContactsProvider,是apps和数据库之间数据传递的协议,通过Uri来进行访问,定义了apps需要使用到的CURD方法

2、联系人中的数据操作流程

数据查询

以主界面的DefaultContactBrowseListFragment为例,继承关系如图

如下图所示:


继承自ContactEntryListFragment的List界面的数据加载流程如下:


在Fragment中通过LoaderManager来对CursorLoader的创建和管理, Fragment中持有一个ContactEntryListAdapter对象,CursorLoader的参数配置等都放在ContactEntryListAdapter中处理, CursorLoader数据加载完成之后回调到Fragment中来,然后通过ContactEntryListAdapter来控制数据的刷新,以及UI的控制

DefaultContactBrowseListFragment对应的adapter为DefaultContactListAdapter,对应的cursorloader为ProfileAndContactsLoader。

其他操作

对于联系人的创建、更新、删除一般情况下都封装在ContactSaveService类中,如图


ContactSaveService类继承自IntentService,是一个用于异步操作联系人数据的service,在完成数据操作之后,通过请求操作的时候传递来intent(intent中包含需要回调的Activity和action)回调到Activity中来通知UIThread进行UI update

以create contact为例:

ContactEditorFragment.save()方法中


ContactSaveService中createSaveContactIntent(),传递了回调需要的参数


ContactSaveService中saveContact()中



能够回调到Activity是因为,Contact中有关联系人操作的Activity的基类ContactsActivity 实现了ContactSaveService.Listener,然后在onCreate中ContactSaveService.registerListener(this),然后通过上面的代码逻辑最终会调用到



3、主界面

主界面为PeopleActivity界面,分为两个tab,分别为DefaultContactBrowseListFragment和ContactTileListFragment,通过viewpager进行切换

其中DefaultContactBrowseListFragment为联系人列表页面,主要用来处理联系人列表的加载展示和联系人搜索,数据加载流程上面已经说明了,列表itemview为ContactListItemView

ContactTileListFragment为收藏联系人及常用联系人,该类直接继承自Fragment,但是和之前说的流程基本一致,方式略微不同,数据分为收藏联系人和最近常联系的联系人,通过ContactTileLoaderFactory来提供加载不同数据的cursorloader


在UI显示上,通过adapter来控制显示stared和frequent联系人,stared为3个一行,frequent一行一个

主界面最上层右下角有一个create联系人的按钮,点击直接进入create contact界面

最上面是toolbar,有search按钮,和menu键

主界面还监听了provider数据变化

4、Detail界面

AndroidL的code中,detail包中没有了之前的联系人详情,只留下了一些辅助工具类,转而使用QuickContactActivity来代替ContactDetailActivity,而且UI上也有较大改动,界面是可滑动的,最外层是自定义view:com.android.contacts.widget.MultiShrinkScroller,如果最近和该联系人联系过,那么在联系人号码下面紧接着就会列出最近联系情况,包括通话和短信,然后才是其他信息,每一类数据多条时默认显示一条,点击查看全部的时候会展开。

z

从联系人列表进入的时候是全屏状态,从其他途径(目前已知的有从短信、桌面快捷方式)进入的时候是非全屏状态,是通过MultiShrinkScroller控制(runEntranceAnimation)

数据加载和上面的加载流程类似。

5、Edit界面

编辑界面在ContactEditorActivity->ContactEditorFragment中,界面中数据加载流程都和上面类似,比较复杂的是编辑界面的UI,是一个自定义的RawContactEditorView,下图是数据传递和绑定的流程图


Fragment中持有自定义view对象RawContactEditorView和数据对象RawContactDeltaList,从数据库中查询出数据Contact之后,将数据封装到RawContactDeltaList中,然后通过bindeditors、editor.setState方法将RawContactDeltaList数据传递给自定义view,然后RawContactEditorView就会将数据拆分,然后传递各个子view,同时也将RawContactDeltaList传递过去,然后数据更新就会在子view中完成,当Fragment中需要进行保存联系人的时候,就可以直接使用RawContactDeltaList;保存联系人的操作和上面的数据交互流程类似。

6、Pick界面

Pick界面为ContactSelectionActivity,会根据不同的action加载不同的Fragment,包括ContactPickerFragment

PhoneNumberPickerFragment

EmailAddressPickerFragment

PostalAddressPickerFragment

JoinContactListFragment

比较常用的是前3个Fragment

ContactSelectionActivity一般都是通过startActivityForResult的方式启动的,所以在它finish之前需要返回一个结果回去。

数据加载流程略过。

7、导入导出功能

这个功能是从主界面上的menu键触发

从SIM卡导入

会调用到Telephony的com.android.phone.SimContacts extends ADNList,ADNList中负责数据加载流程,SimContacts负责UI逻辑处理,都很简单。

 

从存储空间导入

导入vcard文件,是在ImportVCardActivity中,首先会通过VCardScanThread获取到存储空间中的.vcf文件,然后提示用户来选择要导入的vcf文件,通过VCardCacheThread进行缓存数据,封装导入数据需要的数据类型,具体的导入过程是通过bind VCardService进行,service中通过ExecutorService (初始化的为一个单线程的线程池)来执行ImportProcessor(implements Runable)线程,ImportProcessor中执行具体解析导入,ImportProcessor在初始化的时候会传一个NotificationImportExportListener来进行导入完成之后的接口回调。

导出到存储空间卡

导出联系人到vcf文件,存储到存储空间中,导出过程和导入过程类似,也是使用VCardService进行。

8、sim卡联系人

Android L上没有专门针对SIM联系人的编辑,显示等处理,只有上面的导入操作,据说5.1支持双卡,还不清楚对于sim联系人有什么影响

9、dialer中用到联系人

Dialer中有一个tab是全部联系人的展示和搜索,都是用的ContactsCommon中的东西,所以数据加载流程基本类似,不多复述。

10、其他

1、拷贝到剪贴板

[java]  view plain copy
  1. /** 
  2.      * Copy a text to clipboard.拷贝到剪贴板 
  3.      * 
  4.      * @param context Context 
  5.      * @param label Label to show to the user describing this clip. 
  6.      * @param text Text to copy. 
  7.      * @param showToast If {@code true}, a toast is shown to the user. 
  8.      */  
  9.     public static void copyText(Context context, CharSequence label, CharSequence text,  
  10.             boolean showToast) {  
  11.         if (TextUtils.isEmpty(text)) return;  
  12.   
  13.   
  14.         ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(  
  15.                 Context.CLIPBOARD_SERVICE);  
  16.         ClipData clipData = ClipData.newPlainText(label == null ? "" : label, text);  
  17.         clipboardManager.setPrimaryClip(clipData);  
  18.   
  19.   
  20.         if (showToast) {  
  21.             String toastText = context.getString(R.string.toast_text_copied);  
  22.             Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show();  
  23.         }  
  24.     }  

2、Android L上用到的水波纹效果的类:ViewAnimationUtils

3、修改statusbar颜色(Android L上的,其他version未验证)

[java]  view plain copy
  1. private void updateStatusBarColor() {  
  2.         if (mScroller == null) {  
  3.             return;  
  4.         }  
  5.         final int desiredStatusBarColor;  
  6.         // Only use a custom status bar color if QuickContacts touches the top of the viewport.  
  7.         if (mScroller.getScrollNeededToBeFullScreen() <= 0) {  
  8.             desiredStatusBarColor = mStatusBarColor;  
  9.         } else {  
  10.             desiredStatusBarColor = Color.TRANSPARENT;  
  11.         }  
  12.         // Animate to the new color.  
  13.         final ObjectAnimator animation = ObjectAnimator.ofInt(getWindow(), "statusBarColor",  
  14.                 getWindow().getStatusBarColor(), desiredStatusBarColor);  
  15.         animation.setDuration(ANIMATION_STATUS_BAR_COLOR_CHANGE_DURATION);  
  16.         animation.setEvaluator(new ArgbEvaluator());  
  17.         animation.start();  
  18.     }  

总体来看,和我们之前在mtk4.4.4上做的区别很大,移植工作量比较大。

一、包结构分析

相关联的的projects

1、Contacts相关

联系人分为了Contacts和ContactsCommon,与sim卡联系人相关的是在Telephony中,数据库是在ContactsProvider,apk要push到/system/priv-app/Contacts下

2、Contacts的包结构


3、ContactsCommon的包结构


二、功能分析

主要介绍一下和联系人相关的几个主要的功能,其他简单的就略过了,具体问题还是需要在code中看。

1、数据库分析

系统联系人数据库存放在如下位置data\data\com.android.providers.contacts\data bases\,正常的联系人都保存在contacts2.db中,个人信息保存在profile.db,但是两个数据库的基本结构都是一样的

表结构


其中有比较重要的三个表:account、mimetype、data、raw_contacts、contacts,其他的表结构用的较少,遇到问题再去ContactsProvider中查看code。

几张表直接的主要关联关系


Account是和账号有关


data表中存放的是联系人具体信息,每行存储一位联系人的某一类信息(如电话,姓名,邮箱、社交账号、地址等)


其中数据存放在dataX(x为数字)中,根据这行的mimetype_id来对应是什么类的信息数据,mimetype表中存放的是几种数据类型和_id,如图所示


对应到code中,可以通过下面的一些类去源码中具体看mimetype和dataX是对应关系

例如:


其他的类可以参考下面这段定义

[java]  view plain copy
  1. String MIMETYPE_EMAIL_V2 = Email.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/email_v2";  
  2. String MIMETYPE_IM = Im.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/im";  
  3. String MIMETYPE_NICKNAME = Nickname.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/nickname";  
  4. String MIMETYPE_ORGANIZATION = Organization.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/organization";  
  5. String MIMETYPE_PHONE_V2 = Phone.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/phone_v2";  
  6. String MIMETYPE_SIP_ADDRESS = SipAddress.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/sip_address";  
  7. String MIMETYPE_NAME = StructuredName.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/name";  
  8. String MIMETYPE_POSTAL_ADDRESS_V2 = StructuredPostal.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/postal-address_v2";  
  9. String MIMETYPE_IDENTITY = Identity.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/identity";  
  10. String MIMETYPE_PHOTO = Photo.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/photo";  
  11. String MIMETYPE_GROUP_MEMBERSHIP = GroupMembership.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/group_membership";  
  12. String MIMETYPE_NOTE = Note.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/note";  
  13. String MIMETYPE_WEBSITE = Website.CONTENT_ITEM_TYPE;//"vnd.android.cursor.item/website";  

下面是数据库中的视图(一些联合查询的封装),其中重要的几个已经标出。



以上都是一些常用到的数据表和视图,详细的数据表结构以及各个字段的数据类型和意义还需要在code中详细查看。

ContactsProvider中最主要的两个类:

com.android.providers.contacts.ContactsDatabaseHelper

com.android.providers.contacts.ContactsProvider2

ContactsDatabaseHelper继承自SqliteOpenHelper,是对数据库的创建和更新的操作,该类中有详细的每个表的字段和数据类型的定义,以及数据库根据version更新和对联系人部分数据CURD操作封装的声明,提供给ContactsProvider2调用

ContactsProvider2继承自AbstractContactsProvider,是apps和数据库之间数据传递的协议,通过Uri来进行访问,定义了apps需要使用到的CURD方法

2、联系人中的数据操作流程

数据查询

以主界面的DefaultContactBrowseListFragment为例,继承关系如图

如下图所示:


继承自ContactEntryListFragment的List界面的数据加载流程如下:


在Fragment中通过LoaderManager来对CursorLoader的创建和管理, Fragment中持有一个ContactEntryListAdapter对象,CursorLoader的参数配置等都放在ContactEntryListAdapter中处理, CursorLoader数据加载完成之后回调到Fragment中来,然后通过ContactEntryListAdapter来控制数据的刷新,以及UI的控制

DefaultContactBrowseListFragment对应的adapter为DefaultContactListAdapter,对应的cursorloader为ProfileAndContactsLoader。

其他操作

对于联系人的创建、更新、删除一般情况下都封装在ContactSaveService类中,如图


ContactSaveService类继承自IntentService,是一个用于异步操作联系人数据的service,在完成数据操作之后,通过请求操作的时候传递来intent(intent中包含需要回调的Activity和action)回调到Activity中来通知UIThread进行UI update

以create contact为例:

ContactEditorFragment.save()方法中


ContactSaveService中createSaveContactIntent(),传递了回调需要的参数


ContactSaveService中saveContact()中



能够回调到Activity是因为,Contact中有关联系人操作的Activity的基类ContactsActivity 实现了ContactSaveService.Listener,然后在onCreate中ContactSaveService.registerListener(this),然后通过上面的代码逻辑最终会调用到



3、主界面

主界面为PeopleActivity界面,分为两个tab,分别为DefaultContactBrowseListFragment和ContactTileListFragment,通过viewpager进行切换

其中DefaultContactBrowseListFragment为联系人列表页面,主要用来处理联系人列表的加载展示和联系人搜索,数据加载流程上面已经说明了,列表itemview为ContactListItemView

ContactTileListFragment为收藏联系人及常用联系人,该类直接继承自Fragment,但是和之前说的流程基本一致,方式略微不同,数据分为收藏联系人和最近常联系的联系人,通过ContactTileLoaderFactory来提供加载不同数据的cursorloader


在UI显示上,通过adapter来控制显示stared和frequent联系人,stared为3个一行,frequent一行一个

主界面最上层右下角有一个create联系人的按钮,点击直接进入create contact界面

最上面是toolbar,有search按钮,和menu键

主界面还监听了provider数据变化

4、Detail界面

AndroidL的code中,detail包中没有了之前的联系人详情,只留下了一些辅助工具类,转而使用QuickContactActivity来代替ContactDetailActivity,而且UI上也有较大改动,界面是可滑动的,最外层是自定义view:com.android.contacts.widget.MultiShrinkScroller,如果最近和该联系人联系过,那么在联系人号码下面紧接着就会列出最近联系情况,包括通话和短信,然后才是其他信息,每一类数据多条时默认显示一条,点击查看全部的时候会展开。

z

从联系人列表进入的时候是全屏状态,从其他途径(目前已知的有从短信、桌面快捷方式)进入的时候是非全屏状态,是通过MultiShrinkScroller控制(runEntranceAnimation)

数据加载和上面的加载流程类似。

5、Edit界面

编辑界面在ContactEditorActivity->ContactEditorFragment中,界面中数据加载流程都和上面类似,比较复杂的是编辑界面的UI,是一个自定义的RawContactEditorView,下图是数据传递和绑定的流程图


Fragment中持有自定义view对象RawContactEditorView和数据对象RawContactDeltaList,从数据库中查询出数据Contact之后,将数据封装到RawContactDeltaList中,然后通过bindeditors、editor.setState方法将RawContactDeltaList数据传递给自定义view,然后RawContactEditorView就会将数据拆分,然后传递各个子view,同时也将RawContactDeltaList传递过去,然后数据更新就会在子view中完成,当Fragment中需要进行保存联系人的时候,就可以直接使用RawContactDeltaList;保存联系人的操作和上面的数据交互流程类似。

6、Pick界面

Pick界面为ContactSelectionActivity,会根据不同的action加载不同的Fragment,包括ContactPickerFragment

PhoneNumberPickerFragment

EmailAddressPickerFragment

PostalAddressPickerFragment

JoinContactListFragment

比较常用的是前3个Fragment

ContactSelectionActivity一般都是通过startActivityForResult的方式启动的,所以在它finish之前需要返回一个结果回去。

数据加载流程略过。

7、导入导出功能

这个功能是从主界面上的menu键触发

从SIM卡导入

会调用到Telephony的com.android.phone.SimContacts extends ADNList,ADNList中负责数据加载流程,SimContacts负责UI逻辑处理,都很简单。

 

从存储空间导入

导入vcard文件,是在ImportVCardActivity中,首先会通过VCardScanThread获取到存储空间中的.vcf文件,然后提示用户来选择要导入的vcf文件,通过VCardCacheThread进行缓存数据,封装导入数据需要的数据类型,具体的导入过程是通过bind VCardService进行,service中通过ExecutorService (初始化的为一个单线程的线程池)来执行ImportProcessor(implements Runable)线程,ImportProcessor中执行具体解析导入,ImportProcessor在初始化的时候会传一个NotificationImportExportListener来进行导入完成之后的接口回调。

导出到存储空间卡

导出联系人到vcf文件,存储到存储空间中,导出过程和导入过程类似,也是使用VCardService进行。

8、sim卡联系人

Android L上没有专门针对SIM联系人的编辑,显示等处理,只有上面的导入操作,据说5.1支持双卡,还不清楚对于sim联系人有什么影响

9、dialer中用到联系人

Dialer中有一个tab是全部联系人的展示和搜索,都是用的ContactsCommon中的东西,所以数据加载流程基本类似,不多复述。

10、其他

1、拷贝到剪贴板

[java]  view plain copy
  1. /** 
  2.      * Copy a text to clipboard.拷贝到剪贴板 
  3.      * 
  4.      * @param context Context 
  5.      * @param label Label to show to the user describing this clip. 
  6.      * @param text Text to copy. 
  7.      * @param showToast If {@code true}, a toast is shown to the user. 
  8.      */  
  9.     public static void copyText(Context context, CharSequence label, CharSequence text,  
  10.             boolean showToast) {  
  11.         if (TextUtils.isEmpty(text)) return;  
  12.   
  13.   
  14.         ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(  
  15.                 Context.CLIPBOARD_SERVICE);  
  16.         ClipData clipData = ClipData.newPlainText(label == null ? "" : label, text);  
  17.         clipboardManager.setPrimaryClip(clipData);  
  18.   
  19.   
  20.         if (showToast) {  
  21.             String toastText = context.getString(R.string.toast_text_copied);  
  22.             Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show();  
  23.         }  
  24.     }  

2、Android L上用到的水波纹效果的类:ViewAnimationUtils

3、修改statusbar颜色(Android L上的,其他version未验证)

[java]  view plain copy
  1. private void updateStatusBarColor() {  
  2.         if (mScroller == null) {  
  3.             return;  
  4.         }  
  5.         final int desiredStatusBarColor;  
  6.         // Only use a custom status bar color if QuickContacts touches the top of the viewport.  
  7.         if (mScroller.getScrollNeededToBeFullScreen() <= 0) {  
  8.             desiredStatusBarColor = mStatusBarColor;  
  9.         } else {  
  10.             desiredStatusBarColor = Color.TRANSPARENT;  
  11.         }  
  12.         // Animate to the new color.  
  13.         final ObjectAnimator animation = ObjectAnimator.ofInt(getWindow(), "statusBarColor",  
  14.                 getWindow().getStatusBarColor(), desiredStatusBarColor);  
  15.         animation.setDuration(ANIMATION_STATUS_BAR_COLOR_CHANGE_DURATION);  
  16.         animation.setEvaluator(new ArgbEvaluator());  
  17.         animation.start();  
  18.     }  

总体来看,和我们之前在mtk4.4.4上做的区别很大,移植工作量比较大。

猜你喜欢

转载自blog.csdn.net/qq_43186463/article/details/85236902