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

377 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;
// 导入必要的Android和Java类
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; // 导入Notes数据访问类
import net.micode.notes.data.Notes.DataColumns; // 导入Notes数据表的列定义
import net.micode.notes.data.Notes.DataConstants; // 导入Notes数据表的常量定义
import net.micode.notes.data.Notes.NoteColumns; // 导入Notes数据表的列定义
import java.io.File; // 提供文件和目录路径名的抽象表示形式
import java.io.FileNotFoundException; // 当试图打开文件失败时抛出的异常
import java.io.FileOutputStream; // 文件输出流,用于将数据写入文件
import java.io.IOException; // 当发生某种I/O异常时抛出的异常
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;
}
// 定义表示备份或恢复状态的常量
public static final int STATE_SD_CARD_UNMOUONTED = 0; // SD卡未挂载
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;
// 私有构造函数,用于单例模式
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
// 检查外部存储是否可用的方法
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
// 导出数据到文本文件的方法
public int exportToText() {
return mTextExport.exportToText();
}
// 获取导出的文本文件名的方法
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
// 获取导出的文本文件目录的方法
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;
// 注意DATA_COLUMN_CALL_DATE是错误的索引应该是DATA_COLUMN_DATA1或其他
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 = "";
}
// 根据索引获取文本格式的方法
private String getFormat(int id) {
return TEXT_FORMAT[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();
}
}
// ... 省略了exportNoteToText方法的实现该方法应该负责将指定笔记的内容导出到文本
}
}
//定义一个方法,用于将笔记内容导出为文本格式
private void exportNoteToText(String noteId, PrintStream ps) {
// 使用内容解析器查询特定笔记ID的笔记数据
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() {
// 检查外部存储是否可用
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;
}
/**
* 获取一个指向导出文本文件的打印流。
* 注意:此方法的实现未在代码中给出,但基于上下文,该方法应该负责创建或打开一个文件,
* 并返回一个可以向该文件写入数据的PrintStream对象。
*/
//定义一个私有方法用于获取一个指向导出文本文件的PrintStream对象
private PrintStream getExportToTextPrintStream() {
// 调用generateFileMountedOnSDcard方法生成文件该方法需要上下文、文件路径资源ID和文件名格式资源ID
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
// 如果文件创建失败即file为null
if (file == null) {
// 打印错误日志
Log.e(TAG, "create file to exported failed");
// 返回null表示无法获取PrintStream
return null;
}
// 保存文件名
mFileName = file.getName();
// 保存文件目录路径
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
// 创建一个指向文件的FileOutputStream
FileOutputStream fos = new FileOutputStream(file);
// 使用FileOutputStream创建一个PrintStream对象
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
// 如果文件未找到打印堆栈跟踪并返回null
e.printStackTrace();
return null;
} catch (NullPointerException e) {
// 如果发生空指针异常打印堆栈跟踪并返回null
e.printStackTrace();
return null;
}
// 返回创建的PrintStream对象
return ps;
}
/**
* 生成一个位于SD卡上的文本文件用于存储导入的数据
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 创建一个StringBuilder对象用于拼接文件路径
StringBuilder sb = new StringBuilder();
// 追加SD卡根目录路径
sb.append(Environment.getExternalStorageDirectory());
// 追加从资源文件中获取的文件路径
sb.append(context.getString(filePathResId));
// 创建文件目录的File对象
File filedir = new File(sb.toString());
// 追加从资源文件中获取的文件名格式,并格式化当前日期作为文件名的一部分
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// 创建文件的File对象
File file = new File(sb.toString());
try {
// 如果文件目录不存在,则创建它
if (!filedir.exists()) {
filedir.mkdir();
}
// 如果文件不存在,则创建它
if (!file.exists()) {
file.createNewFile();
}
// 返回创建的文件对象
return file;
} catch (SecurityException e) {
// 如果发生安全异常如没有SD卡写入权限打印堆栈跟踪
e.printStackTrace();
} catch (IOException e) {
// 如果发生IO异常打印堆栈跟踪
e.printStackTrace();
}
// 如果发生异常返回null
return null;
}