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/other/06_210340011金泊成_代码标注/BackupUtils.java

355 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.

package net.micode.notes.tool;
//定义小米便签类:功能类
import android.content.Context;//导入小米便签需要的各项函数
import android.database.Cursor;//android查询数据库类
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; // 类里面为什么可以定义自身类的对象?
public static synchronized BackupUtils getInstance(Context context) {
// ynchronized 关键字,代表这个方法加锁,相当于不管哪一个线程例如线程A
// 运行到这个方法时,都要检查有没有其它线程B或者C、
// D等正在用这个方法(或者该类的其他同步方法)有的话要等正在使用synchronized方法的线程B或者C
// 、D运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。
// 它包括两种用法synchronized 方法和 synchronized 块。
if (sInstance == null) {
// 如果当前备份不存在,则新声明一个
sInstance = new BackupUtils(context);
}
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;
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;
}
private static class TextExport {
// 内部类文本输出定义笔记ID、修改日期等笔记、内容、日期、电话号码的数据和格式等常量
private static final String[] NOTE_PROJECTION = {
// 定义了一个数组储存便签的信息
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 标识设定笔记ID标识为0笔记的修改日期标识为1笔记的数据标识为2.
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,
};
// 标识设定数据内容标识为0媒体类型标识为1访问日期标识为2电话号码标识为4
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
// 数据媒体类型标识为1
private static final int DATA_COLUMN_CALL_DATE = 2;
// 日期标识为2
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
// 初始化上述一些参数
private final String[] TEXT_FORMAT;// 文档格式标识:名称=>0;日期=>1;内容=>2
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) {
// 从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];
}
/**
* 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);
if (notesCursor != null) {
// ps里面保存有这份note的日期
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date ps里面保存有这份note的日期
// 函数:打印最后修改日期
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);
// 代码块通过将便签内容打印到屏幕上检查是不是这个note的数据
exportNoteToText(noteId, ps); // 将文件导出到text
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
// 函数:将便签的内容以文本的形式显示在屏幕上。比如电话号码、打电话的日期等。
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
// 利用光标来扫描内容区别为callnote和note两种靠ps.printline输出
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) { // 利用光标来扫描内容区别为callnote和note两种靠ps.printline输出
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));// 输出电话号码phoneNumber
}
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
// 输出calldate
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
// 输出位置location
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
// print a line separator between note
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[] { // 语句块在note下面输出一条线
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() { // 总函数调用上面的exportFolder和exportNote
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);
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); // 将ps输出流输出到特定的文件目的就是导出到文件而不是直接输出
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory()); // 外部SD卡的存储路径
sb.append(context.getString(filePathResId)); // 文件的存储路径
File filedir = new File(sb.toString()); // filedir应该就是用来存储路径信息
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) {
e.printStackTrace();
}
// try catch 异常处理
return null;
}
}