这样一个需求,手机第一次启动的时候,需要内置一个群组,并且里面有给定的联系人信息,

本来打算写双进程守护的,结果昨天接到一个这样的任务,就先把它做了,发现里面有些操作数据库的东西还是值得看一下。

首先接到这样一个需求第一眼就是懵逼,然后还是得硬着头皮搞,接下来分析下这个需求需要怎么搞:

1、首先第一次启动 Android 有一个开机启动的广播;

2、启动后去内置一个群组;

3、内置客户给的联系人;

4、把联系人加到群组里面。

根据这四个步骤我们来一步一步搞定(透漏一下这篇文章的精华所在 就是最后一步把联系人加到群组里面 这个网上找了好久都没找到)

1、开机启动广播

由于我是基于源码开发的即在android原生的contacts进行修改

android源码中应用层关于contacts的代码位于

packages\apps\Contacts

packages\apps\ContactsCommon

这两个地方

基于源码开发一般先要看看源码中有没有我们需要的开机广播有的话我们就不需要重新去写这些代码了,一方面省事,另一方面既然源码里面已经有

了,我们在去重新写 会显得代码比较混乱。

通过搜索发现有这样一个类BootCmpReceiver.java

中有接受开机广播的地方

else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
            // fix ALPS01003520,when boot complete,remove the contacts if the
            // card of a slot has been removed
            if (!isPhbReady()) {
                processBootComplete(context);
            }
            // [START] add for Preset service number

            presetServiceNumber(context);

            // [END]
        }

  private void presetServiceNumber(Context context) {
        SIMProcessorBase processor = null;
        LogUtils.d(TAG, "presetServiceNumber");
       // processor = new PresetContactsImportProcessor(context, -1, null, null);

        startSimService(context, -1, SIMServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS);

    }

private void startSimService(Context context, int subId, int workType) {
        Intent intent = null;
        intent = new Intent(context, SIMProcessorService.class);
        intent.putExtra(SIMServiceUtils.SERVICE_SUBSCRIPTION_KEY, subId);
        intent.putExtra(SIMServiceUtils.SERVICE_WORK_TYPE, workType);
        LogUtils.d(TAG, "[startSimService]subId:" + subId + "|workType:" + workType);
        context.startService(intent);
    }

所以我在其中加了这几段启动服务的代码 看这代码结构也许跟google原生的不一样这是我们公司修修改改后的结果 我们主要分析原理

首先找到开机广播 在里面加入我们的函数presetServiceNumber(context) 然后开启服务去执行我们的内置群组、联系人的操作

2、 内置群组

private void addDefaultGroups() {
            Uri uri = Groups.CONTENT_URI;
            StringBuilder selection = new StringBuilder();
            selection.append(Groups.DELETED + "=0");
            selection.append(" AND " + Groups.TITLE + "='" + "ROAMING" + "'");
            Cursor groupCursor = mContext.getContentResolver().query(uri,
                 new String[] { Groups._ID, Groups.TITLE },   selection.toString(), null, null);
            try {
                if (groupCursor != null && groupCursor.getCount() > 0) {
                    return;
                } else {
                    String defaultGroups[] = "ROAMING";
                    final ArrayList operationList = new ArrayList();
                    ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri);
                    ContentValues groupValues = new ContentValues();

                        groupValues.put(Groups.ACCOUNT_NAME,AccountType.ACCOUNT_NAME_LOCAL_PHONE);
                        groupValues.put(Groups.ACCOUNT_TYPE,AccountType.ACCOUNT_TYPE_LOCAL_PHONE);
                        groupValues.put(Groups.TITLE, defaultGroups);
                        groupValues.put(Groups.GROUP_IS_READ_ONLY, 1);
                        builder.withValues(groupValues);
                        operationList.add(builder.build());
                        try {
                            mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
                            groupValues.clear();
                            operationList.clear();
                        } catch (RemoteException e) {
                           Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
                        } catch (OperationApplicationException e) {
                            Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
                        }

                }
            } finally {
                if (groupCursor != null) {
                    groupCursor.close();
                }
            }
        }

去掉不相关的代码这就是我们内置群组的核心代码,

看上面代码 首先我们去查询群组名为“ROAMING”的群组是否存在。

当Groups.DELETED=0的时候, 是 查询没有被删除的联系人分组

当Groups.DELETED=1的时候,是查询删除的分组

如果存在就return了,不存在我们就是新建,新建群组的大部分都都相似主要是

groupValues.put(Groups.TITLE, defaultGroups); //群组的TITLE

groupValues.put(Groups.GROUP_IS_READ_ONLY, 1); //是否只读

3、内置联系人

private void importDefaultReadonlyContact(){
       for(int i = 0;i < INSERT_PRESET_NAME.length; i++) {
           Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(INSERT_PRESET_NUMBER[i]));
           Cursor contactCursor = mContext.getContentResolver().query(uri,
                              new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_ID}, null, null, null);
               try {
               if (contactCursor != null && contactCursor.getCount() > 0) {
                return;
               } else {
                final ArrayList operationList = new ArrayList();
                ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
                ContentValues contactvalues = new ContentValues();
                contactvalues.put(RawContacts.ACCOUNT_NAME, AccountType.ACCOUNT_NAME_LOCAL_PHONE);
                contactvalues.put(RawContacts.ACCOUNT_TYPE, AccountType.ACCOUNT_TYPE_LOCAL_PHONE);
                contactvalues.put(RawContacts.INDICATE_PHONE_SIM, ContactsContract.RawContacts.INDICATE_PHONE);
                contactvalues.put(RawContacts.IS_SDN_CONTACT, -2);
                builder.withValues(contactvalues);
                builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
                operationList.add(builder.build());
                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
                builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
                builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
                builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
                builder.withValue(Phone.NUMBER, INSERT_PRESET_NUMBER[i]);
                builder.withValue(Data.IS_PRIMARY, 1);
                operationList.add(builder.build());  

                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
                builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
                builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
                builder.withValue(StructuredName.DISPLAY_NAME, INSERT_PRESET_NAME[i]);
                operationList.add(builder.build());

                try {
                    mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
                } catch (RemoteException e) {
                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
                } catch (OperationApplicationException e) {
                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
                }
           }
       } finally {
           if (contactCursor != null) {
               contactCursor.close();
           }
       }
    }
  }

去掉不相关的代码这就是我们内置联系人的核心代码 其中INSERT_PRESET_NUMBER是我们的联系人号码数组可以写 如{“185…..”,”158…..”,”137….”}等

INSERT_PRESET_NAME是联系人姓名数组可以写 如{“张三”,“李四”,“王五”}等

首先也是查询数据库看看我们的联系人号码是否存在,存在的话就 return

不存在的话就去创建

联系人的操作大部分也都是一样的

主要是

builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); //MIME类型

builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);//phone类型可以是 号码 姓名之类的

builder.withValue(Phone.NUMBER, INSERT_PRESET_NUMBER[i]);//号码

builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); //MIME类型

builder.withValue(StructuredName.DISPLAY_NAME, INSERT_PRESET_NAME[i]);//姓名

还可以加入邮箱之类的,

builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); //MIME类型

builder.withValue(Email.TYPE, Email.TYPE_MOBILE);//Email类型

builder.withValue(Email.DATA, email);//EMail

4、把联系人加到群组里面。

怎么把联系人加入到群组呢

有两种方法 第一种是内置联系人是 在联系人中加入群组标示;

第二中是内置群组后,把联系人批量导入群组

我们今天用第一种方式实现:

首先看联系人的操作

builder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);//MIME类型

builder.withValue(GroupMembership.GROUP_ROW_ID, grpId);//群组

我们可以通过这样把联系人加如群组标签

所以这里我们关键的操作就是找到grpId即我们要加入群组ID

 Uri urigroup = Groups.CONTENT_URI;
       StringBuilder selection = new StringBuilder();
       selection.append(Groups.DELETED + "=0");
       selection.append(" AND " + Groups.TITLE + "='" + "ROAMING CUSTOMER CARE" + "'");
       Cursor groupCursor = mContext.getContentResolver().query(urigroup,
           new String[] { Groups._ID, Groups.TITLE }, selection.toString(), null, null);
       try {
           if(groupCursor!=null && groupCursor.getCount()>0 ){
               while (groupCursor.moveToNext()) {
                   grpId =   groupCursor.getLong(groupCursor.getColumnIndex(Groups._ID));
            }
           }else{
               addDefaultGroups();
           }
       }finally {
           if (groupCursor != null) {
               groupCursor.close();
           }
       }

跟内置群组操作差不多一样但是这么我们如果查询到的话就取出这个群组的id如果不存在就是内置群组,

经过上面这四步我们就能顺利的吧联系人内置到群组里面了。

Android 内置群组,联系人的更多相关文章

  1. 写一个android内置android程序

    当我们编译完毕android源代码之后,就须要对他做点什么事情,我如今正在看老罗的"Android源代码情景分析"一书.在这里主要是记录一些书中没有说清楚的地方. 相同.我们创建一 ...

  2. Android内置和外置SD卡的位置获取

    public class StorageUtils { private static String TAG="123"; // 获取主存储卡路径 内置内存卡路径 public st ...

  3. android 内置视频目录

    在做引导界面的时候有一个视频文件, 把它放在res/raw目录下面. 引用方法 如下: videoView = (VideoView) findViewById(R.id.video_view); v ...

  4. Android之Socket群组聊天

    在这只做了一个简单的例子,没有用到数据库,思路就是客户端发送信息到服务器端,服务器端转发所有数据到客户端,校验服务器端发来消息是否是自己发出的,如果是自己发出的,则不显示自己的消息 贴一下Androi ...

  5. Android内置下拉刷新组件SwipeRefreshLayout

    也许下拉刷新之前,你可能会使用一些第三方的开源库,例如PullToRefresh, ActionBar-PullToRefresh等待,但现在有的正式组成部分---SwipeRefreshLayout ...

  6. 【Android】16.5 Android内置的系统服务

    分类:C#.Android.VS2015: 创建日期:2016-03-01 一.简介 实际上,在Android.Content.Context类中,Android已经提供了多种类型的系统服务,这些服务 ...

  7. 【Android】18.1 利用安卓内置的定位服务实现位置跟踪

    分类:C#.Android.VS2015: 创建日期:2016-03-04 一.安卓内置的定位服务简介 通常将各种不同的定位技术称为位置服务或定位服务.这种服务是通过电信运营商的无线电通信网络(如GS ...

  8. 【Android】9.2 内置行视图的分类和呈现效果

    分类:C#.Android.VS2015: 创建日期:2016-02-18 一.简介 Android内置了很多行视图模板,在应用程序中可直接使用这些内置的视图来呈现列表项. 要在ListView中使用 ...

  9. adb将Apk内置到系统中(system/priv-app)

    https://blog.csdn.net/starhosea/article/details/78697007 so文件的处理是目前遇到过的问题.文章中解释了. 正文: 有时候我们在Android ...

随机推荐

  1. 1018关于MySQL复制搭建[异步复制和半同步复制]

    转自:http://www.cnblogs.com/ivictor/p/5735580.html 搭建MySQL数据库的主从架构,还是蛮简单的.重要的几个命令整理一下. 主从服务器上: SHOW VA ...

  2. js高阶函数应用—函数防抖和节流

    高阶函数指的是至少满足下列两个条件之一的函数: 1. 函数可以作为参数被传递:2.函数可以作为返回值输出: javaScript中的函数显然具备高级函数的特征,这使得函数运用更灵活,作为学习js必定会 ...

  3. PostgreSQL的insert注入

    写这篇文是在昨夜的ctf中遇到的. ctf地址:bloody-feedback.quals.2017.volgactf.ru email存在注入,在ctf中发现注入就很好办了,只要找到能绕过的方法就行 ...

  4. [UOJ 282]长度测量鸡

    Description

  5. [SHOI2008]堵塞的交通

    Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可 以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有 ...

  6. 计蒜客NOIP模拟赛4 D1T1 小X的质数

    小 X 是一位热爱数学的男孩子,在茫茫的数字中,他对质数更有一种独特的情感.小 X 认为,质数是一切自然数起源的地方. 在小 X 的认知里,质数是除了本身和 1以外,没有其他因数的数字. 但由于小 X ...

  7. NOIP 2011 观光公交

    题目描述 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第 0 分钟出现在 1号景点,随后依次前往 2 ...

  8. 2015 多校联赛 ——HDU5349(水)

    Problem Description A simple problem Problem Description You have a multiple set,and now there are t ...

  9. bzoj3685普通van Emde Boas树 线段树

    3685: 普通van Emde Boas树 Time Limit: 9 Sec  Memory Limit: 128 MBSubmit: 1932  Solved: 626[Submit][Stat ...

  10. bzoj3894

    转载自http://www.cnblogs.com/rausen 3894: 文理分科 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1338  So ...