Android通话记录备份实现代码

(一) 前言

Android默认提供了联系人备份到sd卡的功能(代码在com.android.vcard包里面),我们可以把联系人导出成.vcf文件存在sd卡中;如果换手机了,我们又可以把联系人从sd卡文件中导入进来。那么,通话记录我们也能不能做出类似的功能呢?答案是肯定的!

(二) 导出通话记录

既然是备份通话记录,那就肯定包括导出和导入的功能,这里我们先讲导出通话记录。

1. 根据通话记录导出的规范,导出的文件一般以.vcl后缀结尾,中间的内容是


代码如下:

BEGIN:VCALL
SLOT:0 //卡槽号 0:单卡手机 1: 双卡手机卡槽1 2: 双卡手机卡槽2
TYPE:1 //电话类型 1:接入电话,2: 呼出电话 3: 未接电话
Date: 2013/02/12 14:11:12 GMT //来电或者去点的时间 备份时以GMT时间记录,恢复时显示手机时区对应时间
NUMBER:+86134xxxxx //对方号码
DURATION:5 //持续时间,秒数
END:VCALL

那么这里就是一条通话记录的存储格式了,以BEGIN:VCALL 开始 END:VCALL结束。 //表示的是该字段的含义,只是为了让大家理解,不会导入到实际的文件中去。那么我们来看实际怎么导出的。

2. 查询通话记录列表

ok.. 既然是保存通话记录,那么首先要查询通话记录
Android里面提供了一个CallLogProvider来满足大家的这个需求,它在系统中配置的名字是“call_log”, 所以大家只要提供一个这样的Uri就可以查询了,比如:

代码如下:

Uri uri = Uri.parse("context://call_log/calls");
Cursor c = mContext.getContentResolver().query(uri, xxx, xxx );

这样就可以查询出所有的通话记录,得到游标。。

3. 从游标中剥离出想要保存的字段和数据,写入文件

既然找到了游标,那么接下来就是从游标中找到我们想要写入文件的字段数据,比如,基本如下:

代码如下:

protected Object doInBackground(Object... params) { //后台异步Task,后台查询数据和写入文件,每导出一条记录,更新一次进度条
super.doInBackground(params);
String path = (String)params[0];

Uri queryUri = Uri.parse("content://call_log/calls");
Cursor queryedCursor = mContext.getContentResolver().query(queryUri,
null,
null,
null,
null);
if (queryedCursor == null || queryedCursor.getCount() == 0){
return -1;
}

Object[] message = new Object[1];
message[0] = queryedCursor.getCount();
publishProgress(message);

StringBuilder sb = new StringBuilder();
OutputStream outputStream = null;
Writer writer = null;
try {
outputStream = new FileOutputStream(path);
writer = new BufferedWriter(new OutputStreamWriter(outputStream));

for (queryedCursor.moveToFirst(); !queryedCursor.isAfterLast();
queryedCursor.moveToNext()) {
if (mCancel){
break;
}
sb.setLength(0);
sb.append("BEGIN:VCALL").append("\n");
int subId = queryedCursor.getInt(queryedCursor.getColumnIndex("sub_id"));
int callType = queryedCursor.getInt(
queryedCursor.getColumnIndex("type")); //incall/outcall/missed call
long date = queryedCursor.getLong(queryedCursor.getColumnIndex("date"));
String gmtData = getGTMDatetimeString(date);
String number = queryedCursor.getString(queryedCursor.getColumnIndex("formatted_number"));
String duration = queryedCursor.getString(queryedCursor.getColumnIndex("duration"));

sb.append("SLOT:").append(subId).append("\n");
sb.append("TYPE:").append(callType).append("\n");
sb.append("DATE:").append(gmtData).append("\n");
sb.append("NUMBER:").append(number).append("\n");
sb.append("DURATION:").append(duration).append("\n");
sb.append("END:VCALL").append("\n");
writer.write(sb.toString()); //写入一条记录到文件中
message[0] = -1;
publishProgress(message); //发布消息,让主线程更新进度条
}

} catch (Exception e) {
Log.d(TAG, "", e);
return 0;
}finally{
try {
if (writer != null){
writer.close();
}
if (outputStream != null){
outputStream.close();
}
} catch (Exception e2) {
Log.d(TAG, "", e2);
return 0;
}
}
return 1;
}

这个只是大体代码,大家如果以后有需求,可以在上面任意修改而无需知会作者。。无需版权的哈~~~

(三) 导入通话记录到数据库

1. 嗯,导入的话,首先得搜索sd卡里面以.vcl后缀结尾的文件,嗯!起个线程吧,迭代搜索。如下:


代码如下:

private class VCLScanThread extends Thread implements OnCancelListener, OnClickListener { //启动线程进行搜索,同时弹出进度条给用户
private boolean mCanceled; //变量标志用户是否已经cancel这个搜索过程
private boolean mGotIOException;
private File mRootDirectory;
private static final String LOG_TAG = "VCLScanThread";

// To avoid recursive link.
private Set<String> mCheckedPaths;

private class CanceledException extends Exception {
}

public VCLScanThread(File sdcardDirectory, String scanType) {
mCanceled = false;
mGotIOException = false;
mRootDirectory = sdcardDirectory;
mCheckedPaths = new HashSet<String>();
mProgressDialogForScanVCard = new ProgressDialog(Main.this);
mProgressDialogForScanVCard.setTitle(R.string.dialog_scan_calllist_progress_title);
mProgressDialogForScanVCard.show(); //弹出搜索进度条
}

@Override
public void run() {
if (mAllVclFileList == null){
mAllVclFileList = new Vector<VCLFile>(); //开始搜索,首先清空list,这个list用来保存找到的.vcl文件(包括文件名,文件路径,等等)
}else{
mAllVclFileList.clear();
}

try {
getVCardFileRecursively(mRootDirectory); //迭代搜索sd卡中所有的.vcl文件
} catch (CanceledException e) {
mCanceled = true;
} catch (IOException e) {
mGotIOException = true;
}

if (mCanceled) {
mAllVclFileList = null;
}

mProgressDialogForScanVCard.dismiss();
mProgressDialogForScanVCard = null;

if (mGotIOException) {
// runOnUiThread(new DialogDisplayer(R.id.dialog_io_exception));
} else if (mCanceled) {
// finish();
} else {
int size = mAllVclFileList.size();
if (size == 0) {
Toast.makeText(Main.this, R.string.error_scan_vcl_not_found,
Toast.LENGTH_SHORT).show();
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
startVCardSelectAndImport(); //搜索完毕,弹出对话框让用户选择导入那些文件
}
});

}
}
}

private void getVCardFileRecursively(File directory)
throws CanceledException, IOException {
if (mCanceled) {
throw new CanceledException();
}

// e.g. secured directory may return null toward listFiles().
final File[] files = directory.listFiles();
if (files == null) {
final String currentDirectoryPath = directory.getCanonicalPath();
final String secureDirectoryPath =
mRootDirectory.getCanonicalPath().concat(SECURE_DIRECTORY_NAME);
if (!TextUtils.equals(currentDirectoryPath, secureDirectoryPath)) {
Log.w(LOG_TAG, "listFiles() returned null (directory: " + directory + ")");
}
return;
}
for (File file : directory.listFiles()) {
if (mCanceled) {
throw new CanceledException();
}
String canonicalPath = file.getCanonicalPath();
if (mCheckedPaths.contains(canonicalPath)) {
continue;
}

mCheckedPaths.add(canonicalPath);

String endFix = ".vcl";
if (file.isDirectory()) {
getVCardFileRecursively(file); //如果是目录,就继续迭代搜索
} else if (canonicalPath.toLowerCase().endsWith(endFix) && //如果是文件,就判断文件名是否以.vcl结尾,如果是,而且可读,则放入搜索的list里面。
file.canRead()){
String fileName = file.getName();
VCLFile vclFile = new VCLFile(
fileName, canonicalPath, file.lastModified());
mAllVclFileList.add(vclFile);

}
}
}

public void onCancel(DialogInterface dialog) {
mCanceled = true;
}

public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
mCanceled = true;
}
}
}

2. 选择好要导入的文件之后,就是解析该文件,解析完一个BEGIN:VCALL和END:VCALL之后,就存入数据库(你也可以解析多条之后一次性存入数据库)

代码如下:

private void parseItemInter(String name, String value) throws Exception{
if ("SLOT".equalsIgnoreCase(name)){
mValues.put("sub_id", value);
}else if ("TYPE".equalsIgnoreCase(name)){
mValues.put("type", value);
}else if ("DATE".equalsIgnoreCase(name)){
mValues.put("date", getGTMDatetime(value));
}else if ("NUMBER".equalsIgnoreCase(name)){
mValues.put("formatted_number", value);
mValues.put("number", value);
}else if ("DURATION".equalsIgnoreCase(name)){
mValues.put("duration", value);
}else{
throw new Exception("Unknown type, name: " + name + " value: " + value);
}
}

//提交一次通话记录信息到数据库
Uri uri = Uri.parse("content://call_log");
mContext.getContentResolver().insert(uri, mValues);

大体就是这个意思了,只是具体细节,还要控制。比如文件非法啦,不是以BEGIN:VCALL开头啦,之类的。还需要大家控制。

大体就这么多了,希望能对大家以后做这块的时候稍微有所参考。。。

(0)

相关推荐

  • Android实现通讯录效果——获取手机号码和姓名

    首先给大家展示下运行效果图: 由于通讯录在手机里是以数据库贮存的 所以我们可以通过一个方法 context.getContentResolver().query(Phone.CONTENT_URI, null, null, null, null); 来获得通讯录 ,这个方法返回一个游标的数据类型,通过moveToNext()方法来获取所有的手机号码信息 当然读取手机通讯录需要权限 在adnroidManifest文件中声明即可 由于我也实现了打电话的功能 所以也要声明权限 <uses-permi

  • Android手机联系人快速索引(手机通讯录)

    最近需要实现一个手机通讯录的快速索引功能.根据姓名首字母快速索引功能.下面是一个手机联系人快速索引的效果,总体来说代码不算难,拼音转换的地方略有复杂.下面上源码:源码中有注释. 下面是效果图: MainActivity: import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; imp

  • Android跳转到通讯录获取用户名称和手机号码的实现思路

    效果图如下所示: 先给大家说下实现android 跳转到通讯录的实现思路: 1.点击跳转到通讯录界面 2.获取通讯录姓名和手机号码 3.回调显示姓名和手机号码 1首先是跳转到通讯录界面 Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, 0); 通过设置通讯录url跳转,可

  • Android获取手机通讯录、sim卡联系人及调用拨号界面方法

    android获取手机通讯录联系人信息 复制代码 代码如下: private void getPhoneContacts() {        ContentResolver resolver = this.getContentResolver();                // 获取手机联系人       Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,                  new String[] { Phone

  • Android实现仿通讯录侧边栏滑动SiderBar效果代码

    本文实例讲述了Android实现仿通讯录侧边栏滑动SiderBar效果代码.分享给大家供大家参考,具体如下: 之前看到某些应用的侧边栏做得不错,想想自己也弄一个出来,现在分享出来,当然里面还有不足的地方,请大家多多包涵. 先上图: 具体实现的代码如下: package com.freesonfish.listview_index; import android.content.Context; import android.graphics.Canvas; import android.grap

  • Android破解微信获取聊天记录和通讯录信息(静态方式)

    一.猜想数据存放路径 微信现在是老少皆宜,大街小巷都在使用,已经替代了传统的短信聊天方式了,只要涉及到聊天就肯定有隐私消息,那么本文就来讲解如何获取微信的聊天记录以及通讯录信息. 首先我们在没有网络的时候,打开微信同样可以查看聊天记录,说明微信会把聊天记录保存到本地,那么这么多信息肯定会保存在数据库中,所以我们可以去查看微信的databases目录看看内容: 可惜的是,我们在这个里面并没有发现一些有用的数据,所以这时候就了解到了微信因为把重要信息的数据库存在其他目录下面,我们可以直接把微信的整个

  • Android利用Intent读取和更新通讯录

    一.简介 本节演示如何在安卓系统中通过用户配置文件(user profile)读取和更新该手机的所有联系人信息,以及如何导航到用户配置文件中的这些联系人. 二.基本概念  1.什么是 User Profile 用户配置文件(user profile)保存的是机主信息以及该手机中所有联系人的信息. 假定手机所有者的名字为"Mao mao yu",那么,user profile保存的就是"Mao mao yu"的通讯录(即机主所有联系人的姓名.电话.邮箱.--等信息).

  • Android通讯录开发之删除功能的实现方法

    无论是Android开发或者是其他移动平台的开发,ListView肯定是一个大咖,那么对ListView的操作肯定是不会少的,上一篇博客介绍了如何实现全选和反选的功能,本篇博客介绍删除功能,删除列表中的项无谓就是及时刷新列表,这又跟UI线程扯上关系了,还是那句话,数据的更新通知一定要在UI线程上做,不然会出现各种错误,比如出现adapter数据源改变,但没有及时收到通知的情况.在执行遍历删除的时候,最好不要每删一个就直接通知,下面是我的实现方法,将需要删除的contact保存到一个List然后通

  • Android基于BaseExpandableListAdapter实现的二级列表仿通话记录功能详解

    本文实例讲述了Android基于BaseExpandableListAdapter实现的二级列表仿通话记录功能.分享给大家供大家参考,具体如下: android SDK中带有这样类似的例子,但是那个还是静态数据,没有实际应用价值,参考意义不大. 网上找了很多,还是那样的情况,几乎是同一篇文章,大家转来转去.况且,那篇例子也是静态的数据. 还是自己试试,自己写一个吧.程序读取手机系统的通话记录,按联系人分组,显示到列表. 开发工具:eclipse 运行环境:htc G9 android2.3.3

  • Android开发实现删除联系人通话记录的方法

    本文实例讲述了Android开发实现删除联系人通话记录的方法.分享给大家供大家参考,具体如下: 1. 负责存放呼叫记录的内容提供者源码在 ContactsProvider 项目下: 源码路径: com/Android/providers/contacts/CallLogProvider.Java 使用到的数据库在: /data/data/com.android.providers.contacts/databases/contacts2.db 表名:calls 呼叫记录有三种类型: 来电:Cal

  • Android获取手机通话记录的方法

    Android如何获取手机通话记录,本文为大家揭晓. 获取手机通话记录流程: 1. 获取ContentResolver; ContentResolver resolver = getContentResolver(); 2.resolver.query(*); 需要传入通话记录的URI:CallLog.Calls.CONTENT_URI 3.对查询得到的Cursor进行数据获取. 主要代码如下: MainActivity.java package com.noonecode.contentres

随机推荐