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.

409 lines
15 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.
*/
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 导出状态码
*/
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, // 便签ID
NoteColumns.MODIFIED_DATE, // 便签修改日期
NoteColumns.SNIPPET, // 便签摘要
NoteColumns.TYPE // 便签类型
};
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, // MIME类型
DataColumns.DATA1, // 数据字段1
DataColumns.DATA2, // 数据字段2
DataColumns.DATA3, // 数据字段3
DataColumns.DATA4, // 数据字段4
};
private static final int DATA_COLUMN_CONTENT = 0;
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 格式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();
}
}
/**
* 导出单个便签到文本
* @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 导出状态码
*/
public int exportToText() {
// 检查SD卡是否挂载
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();
}
// 导出根文件夹中的便签
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))));
// 导出便签内容
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* 获取用于导出文本的打印流
* @return 打印流
*/
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 生成的文件对象
*/
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;
}
}