You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

440 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 文件: BackupUtils.java
* 描述: 便签数据备份工具类,提供将便签导出为文本文件的功能
* 作用: 负责便签数据的备份操作支持将便签内容导出到SD卡中的文本文件
*/
package net.micode.notes.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
/**
* 便签备份工具类
*
* 提供便签数据备份相关功能,主要用于将便签导出为文本文件
* 采用单例模式设计,确保全局只有一个实例
*/
public class BackupUtils {
private static final String TAG = "BackupUtils";
// 单例实例
private static BackupUtils sInstance;
/**
* 获取BackupUtils单例实例
*
* @param context 应用上下文
* @return BackupUtils实例
*/
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
* 备份或恢复状态常量定义
*/
// SD卡未挂载状态
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// 备份文件不存在状态
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// 数据已损坏状态(可能被其他程序修改)
public static final int STATE_DATA_DESTROIED = 2;
// 系统错误状态(运行时异常导致备份或恢复失败)
public static final int STATE_SYSTEM_ERROR = 3;
// 操作成功状态
public static final int STATE_SUCCESS = 4;
// 文本导出工具实例
private TextExport mTextExport;
/**
* 私有构造函数,初始化文本导出工具
*
* @param context 应用上下文
*/
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
/**
* 检查外部存储是否可用
*
* @return 如果外部存储可用返回true否则返回false
*/
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
* 将便签导出为文本文件
*
* @return 操作状态码参见STATE_*常量
*/
public int exportToText() {
return mTextExport.exportToText();
}
/**
* 获取导出的文本文件名
*
* @return 导出的文本文件名
*/
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
/**
* 获取导出的文本文件目录
*
* @return 导出的文本文件目录
*/
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
/**
* 文本导出内部工具类
*
* 负责将便签数据导出为格式化的文本文件
*/
private static class TextExport {
// 便签查询投影:定义查询便签时需要获取的列
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 便签ID列索引
private static final int NOTE_COLUMN_ID = 0;
// 便签修改日期列索引
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
// 便签摘要列索引
private static final int NOTE_COLUMN_SNIPPET = 2;
// 便签数据查询投影:定义查询便签数据时需要获取的列
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 便签内容列索引
private static final int DATA_COLUMN_CONTENT = 0;
// 便签MIME类型列索引
private static final int DATA_COLUMN_MIME_TYPE = 1;
// 通话日期列索引
private static final int DATA_COLUMN_CALL_DATE = 2;
// 电话号码列索引
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
// 文本格式化模板数组
private final String [] TEXT_FORMAT;
// 文件夹名称格式索引
private static final int FORMAT_FOLDER_NAME = 0;
// 便签日期格式索引
private static final int FORMAT_NOTE_DATE = 1;
// 便签内容格式索引
private static final int FORMAT_NOTE_CONTENT = 2;
// 上下文环境
private Context mContext;
// 导出文件名
private String mFileName;
// 导出文件目录
private String mFileDirectory;
/**
* 构造函数,初始化文本导出工具
*
* @param context 应用上下文
*/
public TextExport(Context context) {
// 从资源文件加载文本格式化模板
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
/**
* 获取指定索引的格式化模板
*
* @param id 格式化模板索引
* @return 格式化模板字符串
*/
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* 将指定文件夹下的便签导出为文本
*
* @param folderId 文件夹ID
* @param ps 输出流
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// 查询属于此文件夹的便签
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// 输出便签的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询并导出此便签的数据
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* 将指定ID的便签导出到输出流
*
* @param noteId 便签ID
* @param ps 输出流
*/
private void exportNoteToText(String noteId, PrintStream ps) {
// 查询便签数据
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
// 处理通话记录便签
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// 输出电话号码
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// 输出通话日期
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// 输出通话附件位置
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
}
// 处理普通文本便签
else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// 在便签之间打印一个分隔行
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
* 将便签导出为文本文件
*
* @return 操作状态码参见STATE_*常量
*/
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取输出流
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出文件夹及其便签
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// 输出文件夹名称
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* 获取导出文本的打印流
*
* @return 打印流对象如果创建失败则返回null
*/
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* 在SD卡上生成文件
*
* @param context 应用上下文
* @param filePathResId 文件路径资源ID
* @param fileNameFormatResId 文件名格式资源ID
* @return 生成的文件对象如果创建失败则返回null
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
File file = new File(sb.toString());
try {
if (!filedir.exists()) {
filedir.mkdir();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}