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

417 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.
*/
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;
/**
* BackupUtils 类提供了将应用程序中的笔记数据备份到文本文件的功能。
*/
public class BackupUtils {
/**
* 日志标签用于Logcat中识别日志来源。
*/
private static final String TAG = "BackupUtils";
// 单例模式相关变量
private static BackupUtils sInstance;
/**
* 获取BackupUtils的单例实例。
* @param context 上下文对象通常是一个Activity或Service。
* @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;
// TextExport对象用于将数据导出到文本文件
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;
}
/**
* TextExport内部类用于将笔记数据导出到文本文件。
*/
private static class TextExport {
// NOTE_PROJECTION数组定义了查询笔记数据时需要的列
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// NOTE_COLUMN_常量定义了NOTE_PROJECTION数组中各列的索引
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;
// DATA_PROJECTION数组定义了查询笔记数据时需要的列
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// DATA_COLUMN_常量定义了DATA_PROJECTION数组中各列的索引
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;
// TEXT_FORMAT数组包含了导出文本的格式字符串
private final String[] TEXT_FORMAT;
// FORMAT_常量定义了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;
/**
* TextExport类的构造函数。
* @param context 上下文对象通常是一个Activity或Service。
*/
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
/**
* 根据id获取对应的格式字符串。
* @param id 格式字符串的索引。
* @return 格式字符串。
*/
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* 将指定文件夹ID的笔记导出到文本。
* @param folderId 文件夹ID。
* @param ps PrintStream对象用于写入文本数据。
*/
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的笔记导出到PrintStream。
* @param noteId 笔记ID。
* @param ps PrintStream对象用于写入文本数据。
*/
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());
}
}
/**
* 将笔记数据导出为用户可读的文本格式。
* @return 操作结果状态码。
*/
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取PrintStream对象用于写入文本数据
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_FOLDER + ") 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;
}
/**
* 获取指向导出文本文件的PrintStream对象。
* @return PrintStream对象用于写入文本数据。如果创建文件失败则返回null。
*/
private PrintStream getExportToTextPrintStream() {
// 在SD卡上生成用于存储导出数据的文本文件
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 {
// 创建文件输出流并将其封装为PrintStream对象
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 上下文对象通常是一个Activity或Service。
* @param filePathResId 文件夹路径的资源ID。
* @param fileNameFormatResId 文件名格式的资源ID。
* @return 创建的文件对象如果创建失败则返回null。
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 定义一个StringBuilder对象用于构建文件路径
StringBuilder sb = new StringBuilder();
// 追加SD卡的根目录到路径
sb.append(Environment.getExternalStorageDirectory());
// 追加资源文件中定义的文件夹路径到SD卡根目录
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();
}
// 如果文件创建失败则返回null
return null;
}
}