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

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

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

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. HTML5新增的标签及使用

    HTML5和HTML其实是很相似的,但是有些内容有发生了改变,今天我学习了一下HTML5发现还是挺好学的,只要有html+css基础就可以,今天知识看了下新的标签. 一.定义文档类型 在文件的开头总是 ...

  2. [原创]手把手教你写网络爬虫(4):Scrapy入门

    手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...

  3. postman学习笔记(一)——最简单的postman入门

    昨天开始正式接触postman的操作,最简单的操作是根据接口文档一个个测试接口. 例如: 测试环境地址:http://111.2.198.4(项目组自己的测试环境,要测试的项目组肯定会给你的) //以 ...

  4. “百度杯”CTF比赛 九月场_SQL

    题目在i春秋ctf大本营 题目一开始就提醒我们是注入,查看源码还给出了查询语句 输入测试语句发现服务器端做了过滤,一些语句被过滤了 试了一下/**/.+都不行,后来才发现可以用<>绕过 接 ...

  5. chall.tasteless.eu 中的注入题

    第一题好像就很难,看了payload,算是涨见识了,感觉有点为了猜而猜. 题目给我们的时候是这样的:http://chall.tasteless.eu/level1/index.php?dir=ASC ...

  6. [Codeforces 933B]A Determined Cleanup

    Description 题库链接 给你两个正整数 \(p,k\) ,询问是否能够构造多项式 \(f(x)=\sum\limits_{i=0}^{d-1}a_ix^i\) ,使得存在多项式 \(q(x) ...

  7. ●POJ 2828 Buy Tickets

    题链: http://poj.org/problem?id=2828 题解: 线段树. 逆向考虑这个过程.最后的序列S共有n个元素. 先看最后一个人,如果他插入到第i位,那么他最终的位置就是当前序列S ...

  8. ●POJ 1195 Mobile phones

    题链: http://poj.org/problem?id=1195 题解: 二维树状数组 #include<cstdio> #include<cstring> #includ ...

  9. ●POJ 1509 Glass Beads

    题链: http://poj.org/problem?id=1509 题解: 给出一个字符串,有一个操作:把首字符放到末尾,形成新的串.求任意次操作后,字典序最小的串的首字母在原串中的位置.(这就是最 ...

  10. 2015 多校联赛 ——HDU5348(搜索)

    Problem Description As we all kown, MZL hates the endless loop deeply, and he commands you to solve ...