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

358 lines
14 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 的单例实例
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); // 初始化 TextExport 实例
}
// 检查外部存储是否可用
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
// 导出笔记到文本文件
public int exportToText() {
return mTextExport.exportToText(); // 调用 TextExport 类的 exportToText 方法
}
// 获取导出的文本文件名
public String getExportedTextFileName() {
return mTextExport.mFileName; // 返回 TextExport 类中的文件名
}
// 获取导出的文本文件目录
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory; // 返回 TextExport 类中的文件目录
}
private static class TextExport {
// 定义查询笔记时的列信息
private static final String[] NOTE_PROJECTION = {
NoteColumns.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,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 定义每列的索引
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 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 = ""; // 初始化文件目录为空字符串
}
// 获取指定格式的字符串
private String getFormat(int id) {
return TEXT_FORMAT[id]; // 返回格式化字符串数组中的指定元素
}
/**
* 导出指定文件夹ID的笔记到文本文件
*/
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的数据到文本流
*/
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()); // 记录异常信息
}
}
/**
* 将笔记导出为用户可读的文本文件
*/
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted"); // 记录调试信息SD卡未挂载
return STATE_SD_CARD_UNMOUONTED; // 返回 SD卡未挂载 的状态码
}
PrintStream ps = getExportToTextPrintStream(); // 获取 PrintStream 实例
if (ps == null) {
Log.e(TAG, "get print stream error"); // 记录错误信息,获取 PrintStream 失败
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(); // 关闭 PrintStream
return STATE_SUCCESS; // 返回 成功 的状态码
}
/**
* 获取指向导出文件的 PrintStream
*/
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); // 创建 FileOutputStream 实例
ps = new PrintStream(fos); // 创建 PrintStream 实例
} catch (FileNotFoundException e) {
e.printStackTrace(); // 打印异常堆栈信息
return null;
} catch (NullPointerException e) {
e.printStackTrace(); // 打印异常堆栈信息
return null;
}
return ps; // 返回 PrintStream 实例
}
}
/**
* 生成用于存储导入数据的文本文件
*/
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()); // 创建文件目录对象
// 如果目录不存在,则创建目录
if (!filedir.exists()) {
filedir.mkdir();
}
// 构建文件名
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis()))); // 追加格式化的日期
File file = new File(sb.toString()); // 创建文件对象
try {
// 如果文件不存在,则创建文件
if (!file.exists()) {
file.createNewFile();
}
return file; // 返回文件对象
} catch (SecurityException e) {
e.printStackTrace(); // 打印安全异常堆栈信息
} catch (IOException e) {
e.printStackTrace(); // 打印IO异常堆栈信息
}
return null; // 返回 null表示文件创建失败
}
}