|
|
|
@ -34,53 +34,90 @@ import java.io.FileNotFoundException;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.PrintStream;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @aPackage : net.micode.notes.tool
|
|
|
|
|
* @aClassName: BackupUtils
|
|
|
|
|
* @Description:
|
|
|
|
|
* 主要功能:备份工具类,用于数据备份读取、显示
|
|
|
|
|
* 使用单例模式,确保在整个应用程序只有一个实例
|
|
|
|
|
* @aAuthor: Li Qiushi
|
|
|
|
|
* @createdate: 12/23/2023 10:31 AM
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
public class BackupUtils {
|
|
|
|
|
private static final String TAG = "BackupUtils";
|
|
|
|
|
// Singleton stuff
|
|
|
|
|
private static BackupUtils sInstance;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:用于获取BackupUtils类的实例
|
|
|
|
|
* 使用synchronized关键字确保在多线程环境下的安全性
|
|
|
|
|
* @param context
|
|
|
|
|
* @return sInstance
|
|
|
|
|
*/
|
|
|
|
|
public static synchronized BackupUtils getInstance(Context context) {
|
|
|
|
|
if (sInstance == null) {
|
|
|
|
|
sInstance = new BackupUtils(context);
|
|
|
|
|
sInstance = new BackupUtils(context);//创建一个新的BackupUtils实例并将其赋值给sInstance
|
|
|
|
|
}
|
|
|
|
|
return sInstance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Following states are signs to represents backup or restore
|
|
|
|
|
* status
|
|
|
|
|
* 下列状态是表示备份或还原状态的状态
|
|
|
|
|
*/
|
|
|
|
|
// Currently, the sdcard is not mounted
|
|
|
|
|
// 当前SD卡未挂载
|
|
|
|
|
public static final int STATE_SD_CARD_UNMOUONTED = 0;
|
|
|
|
|
// The backup file not exist
|
|
|
|
|
|
|
|
|
|
// 备份文件不存在
|
|
|
|
|
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
|
|
|
|
|
// The data is not well formated, may be changed by other programs
|
|
|
|
|
|
|
|
|
|
//数据格式错误
|
|
|
|
|
public static final int STATE_DATA_DESTROIED = 2;
|
|
|
|
|
// Some run-time exception which causes restore or backup fails
|
|
|
|
|
|
|
|
|
|
// 系统错误
|
|
|
|
|
public static final int STATE_SYSTEM_ERROR = 3;
|
|
|
|
|
// Backup or restore success
|
|
|
|
|
|
|
|
|
|
// 备份或恢复成功
|
|
|
|
|
public static final int STATE_SUCCESS = 4;
|
|
|
|
|
|
|
|
|
|
private TextExport mTextExport;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:接收一个Context参数,并使用该参数创建一个TextExport实例
|
|
|
|
|
* @param context
|
|
|
|
|
*/
|
|
|
|
|
private BackupUtils(Context context) {
|
|
|
|
|
mTextExport = new TextExport(context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用: 用于检查外部存储是否可用
|
|
|
|
|
* @return boolean
|
|
|
|
|
*/
|
|
|
|
|
private static boolean externalStorageAvailable() {
|
|
|
|
|
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:用于将数据导出到文本文件。
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public int exportToText() {
|
|
|
|
|
return mTextExport.exportToText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:用于获取导出的文本文件名
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
|
|
|
|
public String getExportedTextFileName() {
|
|
|
|
|
return mTextExport.mFileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:用于获取导出的文本文件目录
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
|
|
|
|
public String getExportedTextFileDir() {
|
|
|
|
|
return mTextExport.mFileDirectory;
|
|
|
|
|
}
|
|
|
|
@ -125,6 +162,10 @@ public class BackupUtils {
|
|
|
|
|
private String mFileName;
|
|
|
|
|
private String mFileDirectory;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:接收一个Context对象作为参数,并初始化一些成员变量,如TEXT_FORMAT、mContext、mFileName和mFileDirectory
|
|
|
|
|
* @param context
|
|
|
|
|
*/
|
|
|
|
|
public TextExport(Context context) {
|
|
|
|
|
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
|
|
|
|
|
mContext = context;
|
|
|
|
@ -132,12 +173,19 @@ public class BackupUtils {
|
|
|
|
|
mFileDirectory = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 作用:根据传入的id返回对应的文本格式
|
|
|
|
|
* @param id
|
|
|
|
|
* @return String
|
|
|
|
|
*/
|
|
|
|
|
private String getFormat(int id) {
|
|
|
|
|
return TEXT_FORMAT[id];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Export the folder identified by folder id to text
|
|
|
|
|
* 作用:用于将指定文件夹下的所有笔记导出为文本文件
|
|
|
|
|
* @param folderId
|
|
|
|
|
* @param ps
|
|
|
|
|
*/
|
|
|
|
|
private void exportFolderToText(String folderId, PrintStream ps) {
|
|
|
|
|
// Query notes belong to this folder
|
|
|
|
@ -146,6 +194,9 @@ public class BackupUtils {
|
|
|
|
|
folderId
|
|
|
|
|
}, null);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
查询该文件夹下的所有笔记,然后遍历每个笔记,打印其最后修改日期和内容
|
|
|
|
|
*/
|
|
|
|
|
if (notesCursor != null) {
|
|
|
|
|
if (notesCursor.moveToFirst()) {
|
|
|
|
|
do {
|
|
|
|
@ -155,7 +206,7 @@ public class BackupUtils {
|
|
|
|
|
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
|
|
|
|
|
// Query data belong to this note
|
|
|
|
|
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
|
|
|
|
|
exportNoteToText(noteId, ps);
|
|
|
|
|
exportNoteToText(noteId, ps);//将笔记的内容导出为文本
|
|
|
|
|
} while (notesCursor.moveToNext());
|
|
|
|
|
}
|
|
|
|
|
notesCursor.close();
|
|
|
|
@ -163,9 +214,12 @@ public class BackupUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Export note identified by id to a print stream
|
|
|
|
|
* 作用:将指定ID的笔记导出到文本文件中,包括电话记录和普通笔记的内容
|
|
|
|
|
* @param noteId
|
|
|
|
|
* @param ps
|
|
|
|
|
*/
|
|
|
|
|
private void exportNoteToText(String noteId, PrintStream ps) {
|
|
|
|
|
//查询与给定noteId相关的数据,并将结果存储在Cursor对象dataCursor中
|
|
|
|
|
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
|
|
|
|
|
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
|
|
|
|
|
noteId
|
|
|
|
@ -175,6 +229,9 @@ public class BackupUtils {
|
|
|
|
|
if (dataCursor.moveToFirst()) {
|
|
|
|
|
do {
|
|
|
|
|
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
|
|
|
|
|
/*
|
|
|
|
|
表示该记录是一个电话记录,此时,获取电话记录中的电话号码、通话日期和位置信息,并将其格式化后输出到PrintStream对象ps中
|
|
|
|
|
*/
|
|
|
|
|
if (DataConstants.CALL_NOTE.equals(mimeType)) {
|
|
|
|
|
// Print phone number
|
|
|
|
|
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
|
|
|
|
@ -194,7 +251,7 @@ public class BackupUtils {
|
|
|
|
|
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
|
|
|
|
|
location));
|
|
|
|
|
}
|
|
|
|
|
} else if (DataConstants.NOTE.equals(mimeType)) {
|
|
|
|
|
} else if (DataConstants.NOTE.equals(mimeType)) {//表示该记录是一个普通笔记,此时,获取笔记的内容,并将其格式化后输出到PrintStream对象ps中
|
|
|
|
|
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
|
|
|
|
|
if (!TextUtils.isEmpty(content)) {
|
|
|
|
|
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
|
|
|
|
@ -205,7 +262,7 @@ public class BackupUtils {
|
|
|
|
|
}
|
|
|
|
|
dataCursor.close();
|
|
|
|
|
}
|
|
|
|
|
// print a line separator between note
|
|
|
|
|
// 向PrintStream对象ps中写入一个换行符,用于分隔不同笔记之间的内容
|
|
|
|
|
try {
|
|
|
|
|
ps.write(new byte[] {
|
|
|
|
|
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
|
|
|
|
@ -216,14 +273,21 @@ public class BackupUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Note will be exported as text which is user readable
|
|
|
|
|
* 作用:将笔记导出到文本文件中
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public int exportToText() {
|
|
|
|
|
/*
|
|
|
|
|
如果外部存储未挂载,则记录一条日志并返回一个表示外部存储未挂载的状态码(STATE_SD_CARD_UNMOUONTED)
|
|
|
|
|
*/
|
|
|
|
|
if (!externalStorageAvailable()) {
|
|
|
|
|
Log.d(TAG, "Media was not mounted");
|
|
|
|
|
return STATE_SD_CARD_UNMOUONTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
获取一个用于输出到文本文件的打印流(PrintStream),并将其赋值给变量ps
|
|
|
|
|
如果获取打印流失败,则记录一条错误日志并返回一个表示系统错误的状态码(STATE_SYSTEM_ERROR)
|
|
|
|
|
*/
|
|
|
|
|
PrintStream ps = getExportToTextPrintStream();
|
|
|
|
|
if (ps == null) {
|
|
|
|
|
Log.e(TAG, "get print stream error");
|
|
|
|
@ -237,7 +301,9 @@ public class BackupUtils {
|
|
|
|
|
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
|
|
|
|
|
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (folderCursor != null) {
|
|
|
|
|
|
|
|
|
|
if (folderCursor.moveToFirst()) {
|
|
|
|
|
do {
|
|
|
|
|
// Print folder's name
|
|
|
|
@ -247,6 +313,7 @@ public class BackupUtils {
|
|
|
|
|
} else {
|
|
|
|
|
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
|
|
|
|
|
}
|
|
|
|
|
//将格式化后的文件夹名称写入打印流ps
|
|
|
|
|
if (!TextUtils.isEmpty(folderName)) {
|
|
|
|
|
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
|
|
|
|
|
}
|
|
|
|
@ -264,6 +331,10 @@ public class BackupUtils {
|
|
|
|
|
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
|
|
|
|
|
+ "=0", null, null);
|
|
|
|
|
|
|
|
|
|
/*如果noteCursor不为空,则遍历查询结果
|
|
|
|
|
将格式化后的笔记修改日期写入打印流ps
|
|
|
|
|
获取笔记的ID,并调用exportNoteToText方法将笔记的内容导出到文本文件中
|
|
|
|
|
*/
|
|
|
|
|
if (noteCursor != null) {
|
|
|
|
|
if (noteCursor.moveToFirst()) {
|
|
|
|
|
do {
|
|
|
|
@ -283,16 +354,21 @@ public class BackupUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a print stream pointed to the file {@generateExportedTextFile}
|
|
|
|
|
* 作用:建一个用于导出文本的PrintStream对象
|
|
|
|
|
* @return PrintStream
|
|
|
|
|
*/
|
|
|
|
|
private PrintStream getExportToTextPrintStream() {
|
|
|
|
|
//生成一个文件,该文件位于SD卡上
|
|
|
|
|
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
|
|
|
|
|
R.string.file_name_txt_format);
|
|
|
|
|
/*
|
|
|
|
|
表示创建文件失败,此时会记录一条错误日志(Log.e(TAG, "create file to exported failed"))
|
|
|
|
|
*/
|
|
|
|
|
if (file == null) {
|
|
|
|
|
Log.e(TAG, "create file to exported failed");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
mFileName = file.getName();
|
|
|
|
|
mFileName = file.getName();//文件名赋值给成员变量mFileName
|
|
|
|
|
mFileDirectory = mContext.getString(R.string.file_path);
|
|
|
|
|
PrintStream ps = null;
|
|
|
|
|
try {
|
|
|
|
@ -310,19 +386,27 @@ public class BackupUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate the text file to store imported data
|
|
|
|
|
* 作用:用于在Android设备上生成一个文件
|
|
|
|
|
* @param context
|
|
|
|
|
* @param filePathResId
|
|
|
|
|
* @param fileNameFormatResId
|
|
|
|
|
* @return File
|
|
|
|
|
*/
|
|
|
|
|
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
sb.append(Environment.getExternalStorageDirectory());
|
|
|
|
|
sb.append(context.getString(filePathResId));
|
|
|
|
|
sb.append(Environment.getExternalStorageDirectory()); //获取外部存储设备的根目录
|
|
|
|
|
sb.append(context.getString(filePathResId)); //根据传入的filePathResId参数,将指定的路径添加到根目录下
|
|
|
|
|
File filedir = new File(sb.toString());
|
|
|
|
|
//根据传入的fileNameFormatResId参数和当前日期时间,生成文件名,并将其添加到目录路径中
|
|
|
|
|
sb.append(context.getString(
|
|
|
|
|
fileNameFormatResId,
|
|
|
|
|
DateFormat.format(context.getString(R.string.format_date_ymd),
|
|
|
|
|
System.currentTimeMillis())));
|
|
|
|
|
File file = new File(sb.toString());
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
尝试创建文件所在的目录(如果不存在),并创建文件本身(如果不存在)
|
|
|
|
|
如果成功创建了文件,返回该文件的引用;否则,返回null
|
|
|
|
|
*/
|
|
|
|
|
try {
|
|
|
|
|
if (!filedir.exists()) {
|
|
|
|
|
filedir.mkdir();
|
|
|
|
|