Android通話記錄備份實(shí)現(xiàn)代碼
Android默認(rèn)提供了聯(lián)系人備份到sd卡的功能(代碼在com.android.vcard包里面),我們可以把聯(lián)系人導(dǎo)出成.vcf文件存在sd卡中;如果換手機(jī)了,我們又可以把聯(lián)系人從sd卡文件中導(dǎo)入進(jìn)來(lái)。那么,通話記錄我們也能不能做出類似的功能呢?答案是肯定的!
(二) 導(dǎo)出通話記錄
既然是備份通話記錄,那就肯定包括導(dǎo)出和導(dǎo)入的功能,這里我們先講導(dǎo)出通話記錄。
1. 根據(jù)通話記錄導(dǎo)出的規(guī)范,導(dǎo)出的文件一般以.vcl后綴結(jié)尾,中間的內(nèi)容是
BEGIN:VCALL
SLOT:0 //卡槽號(hào) 0:單卡手機(jī) 1: 雙卡手機(jī)卡槽1 2: 雙卡手機(jī)卡槽2
TYPE:1 //電話類型 1:接入電話,2: 呼出電話 3: 未接電話
Date: 2013/02/12 14:11:12 GMT //來(lái)電或者去點(diǎn)的時(shí)間 備份時(shí)以GMT時(shí)間記錄,恢復(fù)時(shí)顯示手機(jī)時(shí)區(qū)對(duì)應(yīng)時(shí)間
NUMBER:+86134xxxxx //對(duì)方號(hào)碼
DURATION:5 //持續(xù)時(shí)間,秒數(shù)
END:VCALL
那么這里就是一條通話記錄的存儲(chǔ)格式了,以BEGIN:VCALL 開始 END:VCALL結(jié)束。 //表示的是該字段的含義,只是為了讓大家理解,不會(huì)導(dǎo)入到實(shí)際的文件中去。那么我們來(lái)看實(shí)際怎么導(dǎo)出的。
2. 查詢通話記錄列表
ok.. 既然是保存通話記錄,那么首先要查詢通話記錄
Android里面提供了一個(gè)CallLogProvider來(lái)滿足大家的這個(gè)需求,它在系統(tǒng)中配置的名字是“call_log”, 所以大家只要提供一個(gè)這樣的Uri就可以查詢了,比如:
Uri uri = Uri.parse("context://call_log/calls");
Cursor c = mContext.getContentResolver().query(uri, xxx, xxx );
這樣就可以查詢出所有的通話記錄,得到游標(biāo)。。
3. 從游標(biāo)中剝離出想要保存的字段和數(shù)據(jù),寫入文件
既然找到了游標(biāo),那么接下來(lái)就是從游標(biāo)中找到我們想要寫入文件的字段數(shù)據(jù),比如,基本如下:
protected Object doInBackground(Object... params) { //后臺(tái)異步Task,后臺(tái)查詢數(shù)據(jù)和寫入文件,每導(dǎo)出一條記錄,更新一次進(jìn)度條
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); //發(fā)布消息,讓主線程更新進(jìn)度條
}
} 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;
}
這個(gè)只是大體代碼,大家如果以后有需求,可以在上面任意修改而無(wú)需知會(huì)作者。。無(wú)需版權(quán)的哈~~~
(三) 導(dǎo)入通話記錄到數(shù)據(jù)庫(kù)
1. 嗯,導(dǎo)入的話,首先得搜索sd卡里面以.vcl后綴結(jié)尾的文件,嗯!起個(gè)線程吧,迭代搜索。如下:
private class VCLScanThread extends Thread implements OnCancelListener, OnClickListener { //啟動(dòng)線程進(jìn)行搜索,同時(shí)彈出進(jìn)度條給用戶
private boolean mCanceled; //變量標(biāo)志用戶是否已經(jīng)cancel這個(gè)搜索過(guò)程
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(); //彈出搜索進(jìn)度條
}
@Override
public void run() {
if (mAllVclFileList == null){
mAllVclFileList = new Vector<VCLFile>(); //開始搜索,首先清空l(shuí)ist,這個(gè)list用來(lái)保存找到的.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(); //搜索完畢,彈出對(duì)話框讓用戶選擇導(dǎo)入那些文件
}
});
}
}
}
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); //如果是目錄,就繼續(xù)迭代搜索
} else if (canonicalPath.toLowerCase().endsWith(endFix) && //如果是文件,就判斷文件名是否以.vcl結(jié)尾,如果是,而且可讀,則放入搜索的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. 選擇好要導(dǎo)入的文件之后,就是解析該文件,解析完一個(gè)BEGIN:VCALL和END:VCALL之后,就存入數(shù)據(jù)庫(kù)(你也可以解析多條之后一次性存入數(shù)據(jù)庫(kù))
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);
}
}
//提交一次通話記錄信息到數(shù)據(jù)庫(kù)
Uri uri = Uri.parse("content://call_log");
mContext.getContentResolver().insert(uri, mValues);
大體就是這個(gè)意思了,只是具體細(xì)節(jié),還要控制。比如文件非法啦,不是以BEGIN:VCALL開頭啦,之類的。還需要大家控制。
大體就這么多了,希望能對(duì)大家以后做這塊的時(shí)候稍微有所參考。。。
- Android開發(fā)實(shí)現(xiàn)刪除聯(lián)系人通話記錄的方法
- Android獲取手機(jī)通話記錄的方法
- Android破解微信獲取聊天記錄和通訊錄信息(靜態(tài)方式)
- Android通訊錄開發(fā)之刪除功能的實(shí)現(xiàn)方法
- Android獲取手機(jī)通訊錄、sim卡聯(lián)系人及調(diào)用撥號(hào)界面方法
- Android實(shí)現(xiàn)通訊錄效果——獲取手機(jī)號(hào)碼和姓名
- Android實(shí)現(xiàn)仿通訊錄側(cè)邊欄滑動(dòng)SiderBar效果代碼
- Android手機(jī)聯(lián)系人快速索引(手機(jī)通訊錄)
- Android跳轉(zhuǎn)到通訊錄獲取用戶名稱和手機(jī)號(hào)碼的實(shí)現(xiàn)思路
- Android利用Intent讀取和更新通訊錄
- Android基于BaseExpandableListAdapter實(shí)現(xiàn)的二級(jí)列表仿通話記錄功能詳解
相關(guān)文章
Android通過(guò)RemoteViews實(shí)現(xiàn)跨進(jìn)程更新UI示例
本篇文章主要介紹了Android通過(guò)RemoteViews實(shí)現(xiàn)跨進(jìn)程更新UI示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02Android那兩個(gè)你碰不到但是很重要的類之ViewRootImpl
這兩個(gè)類就是ActivityThread和ViewRootImpl,之所以說(shuō)碰不到是因?yàn)槲覀儫o(wú)法通過(guò)正常的方式引用這兩個(gè)類或者其類的對(duì)象,本文就嘗試從幾個(gè)我們經(jīng)常接觸的方面先談?wù)刅iewRootImpl,感興趣的可以參考閱讀下2023-05-05Android實(shí)現(xiàn)單項(xiàng)、多項(xiàng)選擇操作
這篇文章主要介紹了Android實(shí)現(xiàn)單項(xiàng)、多項(xiàng)選擇操作的相關(guān)資料,單項(xiàng)選擇、多項(xiàng)選擇操作在項(xiàng)目開發(fā)中經(jīng)常應(yīng)用,感興趣的小伙伴們可以參考一下2016-04-04Android?App設(shè)計(jì)規(guī)范深入講解
隨著安卓智能手機(jī)不停的更新?lián)Q代,安卓手機(jī)系統(tǒng)越來(lái)越完美,屏幕尺寸也越來(lái)越大啦,下面這篇文章主要給大家介紹了關(guān)于Android?App設(shè)計(jì)規(guī)范的相關(guān)資料,需要的朋友可以參考下2022-10-10android listview實(shí)現(xiàn)新聞列表展示效果
這篇文章主要為大家詳細(xì)介紹了android listview實(shí)現(xiàn)新聞列表展示效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android編程實(shí)現(xiàn)使用webView打開本地html文件的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)使用webView打開本地html文件的方法,結(jié)合實(shí)例形式分析了Android中webview布局及打開HTML文件的功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-02-02Android自定義View實(shí)現(xiàn)球形動(dòng)態(tài)加速球
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)球形動(dòng)態(tài)加速球,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Android中ViewPager的PagerTabStrip與PagerTitleStrip用法實(shí)例
這篇文章主要介紹了Android中ViewPager的PagerTabStrip與PagerTitleStrip用法實(shí)例,這兩個(gè)子控件一般被用作添加標(biāo)題,在實(shí)際效果上并不是那么好控制,使用的時(shí)候需要謹(jǐn)慎,需要的朋友可以參考下2016-06-06