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.
xiaomi/tool/BackupUtils.java

421 lines
17 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"; // 日志标签,用于在日志输出中标识该类的相关信息
// Singleton stuff
private static BackupUtils sInstance; // 单例模式下的唯一实例确保整个应用中只有一个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;
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
/**
* 检查外部存储SD卡是否可用
*
* @return 如果SD卡已挂载则返回true否则返回false
*/
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
* 调用TextExport实例的exportToText方法将笔记数据导出为文本文件
*
* @return 导出操作的状态码如STATE_SUCCESS、STATE_SD_CARD_UNMOUONTED等
*/
public int exportToText() {
return mTextExport.exportToText();
}
//获取导出的文本文件的文件名
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
//获取导出的文本文件所在的目录路径
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 // 笔记的类型
};
// 笔记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, // 数据的MIME类型
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;
//构造函数,初始化文本格式数组和上下文
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);
f (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))));
// 获取笔记的ID
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
// 导出该笔记的详细内容
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
// 关闭游标,释放资源
notesCursor.close();
}
/**
* 将指定ID的笔记详细内容导出到文本文件
*/
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 {
// 获取数据的MIME类型
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());
}
}
/**
* 将所有笔记数据导出为文本文件
*/
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));
}
// 获取文件夹ID
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))));
// 获取笔记的ID
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;
}
}
/**
* 生成用于存储导出数据的文件
*
* @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) {
// 处理IO异常
e.printStackTrace();
}
return null;
}
}