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.
READMI/src/notes/tool/BackupUtils.java

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

/*
* 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.
*/
//BackupUtils 是一个备份工具类,用于数据备份读取和显示
package net.micode.notes.tool;//定义小米便签类:功能类
//调用了Android的包
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;
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
};
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,
};
//标识设定数据内容标识为0媒体类型标识为1访问日期标识为2电话号码标识为4
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;
//文档格式标识名称为0;日期为1;内容为2
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) {//从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返回文件的格式信息
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {//通过文件夹ID将目录导出后成文件
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {//利用光标来扫描内容区别为callnote和note两种靠ps.printline输出
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);
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,
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));
}
// 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// 在Note下方输出一条线
});
} catch (IOException e) {
Log.e(TAG, e.toString());//检测异常如果有异常输出红色TAG
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {//以TEXT形式输出到外部设备
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);// 通过便签ID得到folderID
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);//找到这块数据的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,//初始化存储在SD卡的文件
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 {//将ps输出流输出到特定的文件目的就是导出到文件而不是直接输出
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
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {//生成存储文件安装在SD卡上
StringBuilder sb = new StringBuilder();///构建一个动态字符串将外部存储器路径、文件路径、编辑时间加入到其中
sb.append(Environment.getExternalStorageDirectory());//外部SD卡的存储路径
sb.append(context.getString(filePathResId));//文件的存储路径
File filedir = new File(sb.toString());
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();
}
return null;
}
}