From 60aed4dd897ba0fc7845bb315a9ab08c755aea4f Mon Sep 17 00:00:00 2001 From: pc7tynsj8 Date: Sat, 11 Jan 2025 21:19:29 +0800 Subject: [PATCH] ADD file via upload --- src/BackupUtils.txt | 516 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 src/BackupUtils.txt diff --git a/src/BackupUtils.txt b/src/BackupUtils.txt new file mode 100644 index 0000000..23bf9ee --- /dev/null +++ b/src/BackupUtils.txt @@ -0,0 +1,516 @@ +package net.micode.notes.tool; // 包名,表示该类属于net.micode.notes应用的tool包 + +import android.content.Context; // 导入Context类,用于获取应用上下文 +import android.database.Cursor; // 导入Cursor类,用于操作数据库查询结果 +import android.os.Environment; // 导入Environment类,用于访问外部存储状态 +import android.text.TextUtils; // 导入TextUtils类,用于文本操作 +import android.text.format.DateFormat; // 导入DateFormat类,用于日期格式化 +import android.util.Log; // 导入Log类,用于日志输出 + +import net.micode.notes.R; // 导入R类,用于访问应用资源 +import net.micode.notes.data.Notes; // 导入Notes类,用于操作笔记数据 +import net.micode.notes.data.Notes.DataColumns; // 导入DataColumns类,用于访问笔记数据的列名 +import net.micode.notes.data.Notes.DataConstants; // 导入DataConstants类,用于访问笔记数据的常量 +import net.micode.notes.data.Notes.NoteColumns; // 导入NoteColumns类,用于访问笔记数据的列名 + +import java.io.File; // 导入File类,用于文件操作 +import java.io.FileNotFoundException; // 导入FileNotFoundException类,用于处理文件未找到异常 +import java.io.FileOutputStream; // 导入FileOutputStream类,用于文件输出 +import java.io.IOException; // 导入IOException类,用于处理I/O异常 +import java.io.PrintStream; // 导入PrintStream类,用于输出流操作 + +/** + * BackupUtils类用于处理笔记应用的备份和恢复功能。 + */ +public class BackupUtils { + private static final String TAG = "BackupUtils"; // 日志标签,用于Log输出 + // Singleton instance + private static BackupUtils sInstance; + + // 获取BackupUtils的单例实例 + public static synchronized BackupUtils getInstance(Context context) { + if (sInstance == null) { + sInstance = new BackupUtils(context); // 创建实例 + } + return sInstance; + } + + /** + * 以下状态表示备份或恢复的状态 + */ + // 当前SD卡未挂载 + public static final int STATE_SD_CARD_UNMOUONTED = 0; + // 备份文件不存在 + 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); // 初始化文本导出工具 + } + + // 检查外部存储是否可写 + public boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); // 获取外部存储状态 + return Environment.MEDIA_MOUNTED.equals(state); // 判断是否可写 + } + + // 检查外部存储是否可读 + public boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); // 获取外部存储状态 + return Environment.MEDIA_MOUNTED.equals(state) || // 判断是否可读 + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); + } + + // 备份笔记数据到SD卡 + public int backupNotes() { + if (!isExternalStorageWritable()) { // 检查外部存储是否可写 + Log.e(TAG, "External storage is not writable."); // 输出错误日志 + return STATE_SD_CARD_UNMOUONTED; // 返回SD卡未挂载状态 + } + + File backupDir = new File(Environment.getExternalStorageDirectory(), "NotesBackup"); // 创建备份文件夹 + if (!backupDir.exists()) { + backupDir.mkdirs(); // 如果文件夹不存在,则创建 + } + + File backupFile = new File(backupDir, "notes_backup.txt"); // 创建备份文件 + try (PrintStream out = new PrintStream(new FileOutputStream(backupFile))) { // 创建输出流 + Cursor cursor = Notes.getNotesCursor(); // 获取笔记数据的Cursor + if (cursor == null) { + Log.e(TAG, "Failed to get notes cursor."); // 输出错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 + } + + while (cursor.moveToNext()) { // 遍历笔记数据 + String title = cursor.getString(cursor.getColumnIndex(NoteColumns.TITLE)); // 获取笔记标题 + String content = cursor.getString(cursor.getColumnIndex(NoteColumns.CONTENT)); // 获取笔记内容 + String date = DateFormat.format("yyyy-MM-dd HH:mm:ss", cursor.getLong(cursor.getColumnIndex(NoteColumns.DATE))).toString(); // 获取笔记日期 + out.println(title); // 输出笔记标题 + out.println(content); // 输出笔记内容 + out.println(date); // 输出笔记日期 + out.println("----------"); // 输出分隔符 + } + cursor.close(); // 关闭Cursor + } catch (FileNotFoundException e) { + Log.e(TAG, "Failed to create backup file.", e); // 输出错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 + } catch (IOException e) { + Log.e(TAG, "Failed to write backup file.", e); // 输出错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 + } + + return STATE_SUCCESS; // 返回备份成功状态 + } + + // 从SD卡恢复笔记数据 + public int restoreNotes() { + if (!isExternalStorageReadable()) { // 检查外部存储是否可读 + Log.e(TAG, "External storage is not readable."); // 输出错误日志 + return STATE_SD_CARD_UNMOUONTED; // 返回SD卡未挂载状态 + } + + File backupDir = new File(Environment.getExternalStorageDirectory(), "NotesBackup"); // 创建备份文件夹 + File backupFile = new File(backupDir, "notes_backup.txt"); // 创建备份文件 + + if (!backupFile.exists()) { // 检查备份文件是否存在 + Log.e(TAG, "Backup file does not exist."); // 输出错误日志 + return STATE_BACKUP_FILE_NOT_EXIST; // 返回备份文件不存在状态 + } + + try (PrintStream out = new PrintStream(new FileOutputStream(backupFile))) { // 创建输出流 + // 读取备份文件并恢复笔记数据 + // 这里需要实现具体的恢复逻辑 + } catch (FileNotFoundException e) { + Log.e(TAG, "Failed to open backup file.", e); // 输出错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 + } catch (IOException e) { + Log.e(TAG, "Failed to read backup file.", e); // 输出错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 + } + + return STATE_SUCCESS; // 返回恢复成功状态 + } +} + // 检查外部存储是否可用 + 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 { + private static final String[] NOTE_PROJECTION = { // 笔记数据查询的列 + NoteColumns.ID, // 笔记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, // MIME类型 + DataColumns.DATA1, // 数据1 + DataColumns.DATA2, // 数据2 + DataColumns.DATA3, // 数据3 + DataColumns.DATA4, // 数据4 + }; + + private static final int DATA_COLUMN_CONTENT = 0; // 内容列索引 + private static final int DATA_COLUMN_MIME_TYPE = 1; // MIME类型列索引 + 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 = "exported_notes.txt"; // 示例文件名 + mFileDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/NotesBackup"; // 示例文件目录 + } + + // 导出到文本文件的方法 + public int exportToText() { + // 实现导出逻辑 + // 示例代码,具体实现根据需求进行 + return BackupUtils.STATE_SUCCESS; // 返回导出成功状态 + } + } +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]; // 根据传入的id返回对应的文本格式字符串 +} + +/** + * 将指定文件夹的笔记导出为文本 + */ +private void exportFolderToText(String folderId, PrintStream ps) { + // 查询属于该文件夹的笔记 + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, // 笔记内容URI + NOTE_PROJECTION, // 查询的列 + NoteColumns.PARENT_ID + "=?", // 查询条件,筛选出父ID等于folderId的笔记 + 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); // 获取笔记ID + exportNoteToText(noteId, ps); // 导出笔记数据 + } while (notesCursor.moveToNext()); // 遍历所有笔记 + } + notesCursor.close(); // 关闭游标,释放资源 + } +} + +/** + * 将指定ID的笔记导出到打印流 + */ +private void exportNoteToText(String noteId, PrintStream ps) { + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, // 笔记数据内容URI + DATA_PROJECTION, // 查询的列 + DataColumns.NOTE_ID + "=?", // 查询条件,筛选出笔记ID等于noteId的数据 + new String[] { noteId }, // 查询条件的参数 + null); // 排序参数,这里不指定排序 + + if (dataCursor != null) { + if (dataCursor.moveToFirst()) { + do { + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); // 获取数据的MIME类型 + 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)); // 打印电话号码 + } + } + // 可以根据需要添加其他类型的笔记导出逻辑 + } while (dataCursor.moveToNext()); // 遍历所有数据 + } + dataCursor.close(); // 关闭游标,释放资源 + } +} +private void exportNoteToText(String noteId, PrintStream ps) { + // 查询指定笔记ID的数据 + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, // 笔记数据内容URI + DATA_PROJECTION, // 查询的列 + DataColumns.NOTE_ID + "=?", // 查询条件,筛选出笔记ID等于noteId的数据 + 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; // 返回SD卡未挂载的状态码 + } + + // 获取用于导出文本的打印流 + 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, // 笔记内容URI + NOTE_PROJECTION, // 查询的列 + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " // 笔记类型为文件夹 + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " // 并且父ID不等于垃圾桶文件夹ID + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, // 或者笔记ID等于通话记录文件夹ID + 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); // 获取文件夹ID + exportFolderToText(folderId, ps); // 导出文件夹中的笔记 + } while (folderCursor.moveToNext()); // 遍历所有文件夹 + } + folderCursor.close(); // 关闭游标,释放资源 + } +} +Cursor noteCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, // 笔记内容URI + NOTE_PROJECTION, // 查询的列 + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0", // 查询条件,筛选出类型为笔记且父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); // 获取笔记ID + exportNoteToText(noteId, ps); // 导出笔记数据 + } while (noteCursor.moveToNext()); // 遍历所有笔记 + } + noteCursor.close(); // 关闭游标,释放资源 +} +ps.close(); // 关闭打印流 + +return STATE_SUCCESS; // 返回成功状态 +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; // 返回null,表示无法获取打印流 + } + mFileName = file.getName(); // 获取并保存文件名 + mFileDirectory = mContext.getString(R.string.file_path); // 获取并保存文件目录 + PrintStream ps = null; // 初始化打印流为null + try { + FileOutputStream fos = new FileOutputStream(file); // 创建文件输出流 + ps = new PrintStream(fos); // 创建打印流 + } catch (FileNotFoundException e) { + e.printStackTrace(); // 打印文件未找到异常的堆栈信息 + return null; // 返回null,表示无法获取打印流 + } catch (NullPointerException e) { + e.printStackTrace(); // 打印空指针异常的堆栈信息 + return null; // 返回null,表示无法获取打印流 + } + return ps; // 返回打印流 +} +private PrintStream getExportToTextPrintStream() { + // 调用generateFileMountedOnSDcard方法生成导出文件 + File file = generateFileMountedOnSDcard(mContext, R.string.file_path, R.string.file_name_txt_format); + // 如果文件生成失败,返回null + if (file == null) { + Log.e(TAG, "create file to exported failed"); // 记录错误日志 + return null; + } + // 获取文件名并赋值给mFileName + mFileName = file.getName(); + // 获取文件目录并赋值给mFileDirectory + mFileDirectory = mContext.getString(R.string.file_path); + // 初始化PrintStream对象为null + PrintStream ps = null; + try { + // 创建FileOutputStream对象,用于写入文件 + FileOutputStream fos = new FileOutputStream(file); + // 创建PrintStream对象,用于将数据输出到文件 + ps = new PrintStream(fos); + } catch (FileNotFoundException e) { + // 如果文件未找到,打印异常信息并返回null + e.printStackTrace(); + return null; + } catch (NullPointerException e) { + // 如果发生空指针异常,打印异常信息并返回null + e.printStackTrace(); + return null; + } + // 返回PrintStream对象 + return ps; +} +private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + // 创建StringBuilder对象用于构建文件路径 + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); // 获取外部存储目录并添加到路径中 + sb.append(context.getString(filePathResId)); // 添加文件路径(从资源文件中获取) + File filedir = new File(sb.toString()); // 根据路径创建文件夹对象 + sb.append(context.getString( + fileNameFormatResId, // 文件名格式资源ID + 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(); // 打印IO异常信息 + } + + return null; // 如果发生异常或文件创建失败,则返回null +} +/** + * Generate the text file to store imported data + */ +private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + // 创建StringBuilder用于构建文件路径 + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); // 获取外部存储目录并添加到路径中 + sb.append(context.getString(filePathResId)); // 添加文件路径(从资源文件中获取) + File filedir = new File(sb.toString()); // 根据路径创建文件夹对象 + sb.append(context.getString( + fileNameFormatResId, // 文件名格式资源ID + 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(); // 打印IO异常信息 + } + + return null; // 如果发生异常或文件创建失败,则返回null +} +