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.
xiaomibianqian/src-annotation/net/micode/notes/tool/BackupUtils.java

411 lines
20 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);
}
// 如果sInstance为null则创建一个新的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;
// 声明一个TextExport对象mTextExport实现对象信息的导出
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
// 创建一个TextExport对象并将其赋值给mTextExport
}
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
// 检查外存是否可用如果可用则返回true否则返回false
}
public int exportToText() {
return mTextExport.exportToText();
// 调用TextExport对象的exportToText方法并返回结果
}
public String getExportedTextFileName() {
return mTextExport.mFileName;
// 获取导出对象的名字
}
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
// 获取导出对象的目录
}
private static class TextExport {
// 定义一个常量数组用于从ContentProvider中获取特定的便签信息
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID, //便签标识符
NoteColumns.MODIFIED_DATE, //便签的修改日期
NoteColumns.SNIPPET, //便签摘要
NoteColumns.TYPE //便签类型
};
private static final int NOTE_COLUMN_ID = 0;
// 定义一个常量表示便签ID的列索引
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 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);
// 从资源中获取字符串数组并将其赋值给TEXT_FORMAT成员变量
mContext = context;
// 将传入的Context对象赋值给mContext成员变量
mFileName = "";
// 将mFileName成员变量初始化为空字符串
mFileDirectory = "";
// 将mFileDirectory成员变量初始化为空字符串
}
private String getFormat(int id) {
return TEXT_FORMAT[id];
} // 根据传入的id参数从TEXT_FORMAT数组中获取对应索引位置的字符串格式化模板
/////////////////////////////////////////
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder通过查询parent id是文件夹id的note来选出制定ID文件夹下的Note
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
/*
这行太复杂了,给出详细解释
创建一个名为notesCursor的游标对象用于存储查询结果。
使用mContext.getContentResolver()获取ContentResolver对象它用于与系统中的内容提供者进行交互。
调用ContentResolver的query()方法执行查询操作,传入以下参数:
Notes.CONTENT_NOTE_URI表示要查询的数据的URI即"Notes"表格的URI。
NOTE_PROJECTION表示查询结果中要返回的列。
NoteColumns.PARENT_ID + "=?":表示查询条件,即根据"NoteColumns.PARENT_ID"字段的值进行查询。
new String[] { folderId }:表示查询条件的参数值,即具体的"folderId"值。
null表示查询结果排序方式此处为默认排序方式。
查询结果将会存储在notesCursor游标对象中可以通过该对象获取查询结果。
*/
if (notesCursor != null) { // 如果查询结果不为空
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date// 打印笔记的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note// 查询属于该笔记的数据
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext()); // 遍历结果集中的每一行数据
}
notesCursor.close();
}
}
/**
* Export note identified by id to a print stream
* 将以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);
/*
创建一个名为exportNoteToText的方法该方法接收noteId和PrintStream对象ps作为参数用于导出笔记内容到文本文件。
创建一个名为dataCursor的游标对象用于存储查询结果。
使用mContext.getContentResolver()获取ContentResolver对象它用于与系统中的内容提供者进行交互。
调用ContentResolver的query()方法执行查询操作,传入以下参数:
Notes.CONTENT_DATA_URI表示要查询的数据的URI即"Notes"表格的DATA_URI。
DATA_PROJECTION表示查询结果中要返回的列。
DataColumns.NOTE_ID + "=?":表示查询条件,即根据"DataColumns.NOTE_ID"字段的值进行查询。
new String[] { noteId }:表示查询条件的参数值,即具体的"noteId"值。
null表示查询结果排序方式此处为默认排序方式。
查询结果将会存储在dataCursor游标对象中可以通过该对象遍历获取查询结果的每条记录。
*/
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// Print phone number
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));
}
// Print call date打印电话号码
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location打印附件位置
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();
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString()); //日志输出打印异常信息
}
}
/**
* Note will be exported as text which is user readable
* 该方法的功能是将应用程序中的笔记导出为文本格式,包括文件夹和笔记的信息。导出的文本可以被用户阅读和查看。
*/
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;
}
// First export folder and its notes首先导出文件夹及其笔记
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 {
// Print folder's name打印文件夹名称
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();
}
// Export notes in root's folder导出根文件夹中的笔记
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
/*
创建一个名为noteCursor的游标对象用于存储查询结果。
使用mContext.getContentResolver()获取ContentResolver对象它用于与系统中的内容提供者进行交互。
调用ContentResolver的query()方法执行查询操作,传入以下参数:
Notes.CONTENT_NOTE_URI表示要查询的数据的URI即"Notes"表格的NOTE_URI。
NOTE_PROJECTION表示查询结果中要返回的列。
NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0":表示查询条件,即根据"NoteColumns.TYPE"字段的值等于"Notes.TYPE_NOTE"且"NoteColumns.PARENT_ID"字段的值等于0。
null表示查询条件的参数值此处不需要参数。
null表示查询结果排序方式此处为默认排序方式。
查询条件是将两个条件用"AND"运算符连接起来,其中:
NoteColumns.TYPE + "=" + Notes.TYPE_NOTE 表示筛选出类型为"Notes.TYPE_NOTE"(可能是常量)的笔记。
NoteColumns.PARENT_ID + "=0" 表示筛选出父文件夹ID为0的笔记可能是代表根目录
查询结果将会存储在noteCursor游标对象中可以通过该对象遍历获取查询结果的每条记录。
*/
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))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
* 生成导出的文本文件,并返回一个指向该文件的打印流。
*/
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;
}
}
/**
* Generate the text file to store imported data
* 该方法的功能是根据提供的资源ID和当前时间戳生成一个存储导入数据的文本文件并返回该文件对象。
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 创建一个StringBuilder来构建文件路径
StringBuilder sb = new StringBuilder();
// 将外部存储路径添加到StringBuilder中
sb.append(Environment.getExternalStorageDirectory());
// 将文件路径的资源ID转换为实际路径添加到StringBuilder中
sb.append(context.getString(filePathResId));
// 创建表示文件夹的File对象
File filedir = new File(sb.toString());
// 添加文件名和格式到StringBuilder中
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) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}