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.

364 lines
13 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 的单例实例。
*
* @param context 上下文
* @return 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; // 成功
private TextExport mTextExport;
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
/**
* 检查外部存储是否可用。
*
* @return 如果外部存储可用则返回 true否则返回 false
*/
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
* 将笔记导出为文本文件。
*
* @return 导出结果的状态码
*/
public int exportToText() {
return mTextExport.exportToText();
}
/**
* 获取导出的文本文件名。
*
* @return 文件名
*/
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
/**
* 获取导出的文本文件目录。
*
* @return 文件目录
*/
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
/**
* 内部类,用于处理笔记导出到文本文件的具体逻辑。
*/
private static class TextExport {
// 查询笔记时使用的列
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 查询数据时使用的列
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
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 = "";
}
/**
* 根据 ID 获取格式化字符串。
*
* @param id 格式 ID
* @return 格式化字符串
*/
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* 将指定文件夹中的笔记导出为文本。
*
* @param folderId 文件夹 ID
* @param ps PrintStream 对象
*/
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(1), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(1))));
String noteId = notesCursor.getString(0);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* 将指定笔记导出为文本。
*
* @param noteId 笔记 ID
* @param ps PrintStream 对象
*/
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(1);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
String phoneNumber = dataCursor.getString(4);
long callDate = dataCursor.getLong(2);
String location = dataCursor.getString(0);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(2), phoneNumber));
}
ps.println(String.format(getFormat(2), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
callDate)));
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(2), location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(0);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(2), 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());
}
}
/**
* 将笔记导出为用户可读的文本文件。
*
* @return 导出结果的状态码
*/
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(0) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(2);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(0), folderName));
}
String folderId = folderCursor.getString(0);
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(1), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(1))));
String noteId = noteCursor.getString(0);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* 获取指向导出文件的 PrintStream 对象。
*
* @return 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);
ps = new PrintStream(fos);
} catch (FileNotFoundException | NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* 在 SD 卡上生成用于存储导出数据的文件。
*
* @param context 上下文
* @param filePathResId 文件路径资源 ID
* @param fileNameFormatResId 文件名格式资源 ID
* @return 生成的文件对象
*/
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());
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 | IOException e) {
e.printStackTrace();
}
return null;
}
}