diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index 39f6ec4..6c30e18 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -35,12 +35,15 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; - +// BackupUtils 类用于实现笔记数据的备份和导出为文本文件的功能 public class BackupUtils { + // 用于日志记录的标签 private static final String TAG = "BackupUtils"; // Singleton stuff + // 单例模式相关 + // 保存单例实例 private static BackupUtils sInstance; - + // 获取单例实例的方法,如果实例不存在则创建一个新的实例 public static synchronized BackupUtils getInstance(Context context) { if (sInstance == null) { sInstance = new BackupUtils(context); @@ -53,52 +56,60 @@ public class BackupUtils { * 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 实例 private BackupUtils(Context context) { mTextExport = new TextExport(context); } - + // 检查外部存储是否可用的方法 private static boolean externalStorageAvailable() { + // 比较外部存储状态是否为已挂载 return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - + // 调用 TextExport 的 exportToText 方法进行数据导出为文本,并返回结果状态 public int exportToText() { return mTextExport.exportToText(); } - + // 获取导出的文本文件名 public String getExportedTextFileName() { return mTextExport.mFileName; } - + // 获取导出的文本文件目录 public String getExportedTextFileDir() { return mTextExport.mFileDirectory; } - + // 内部类 TextExport,用于实现具体的文本导出功能 private static class TextExport { + // 用于查询笔记的投影,包含笔记的 ID、修改日期、摘要和类型 private static final String[] NOTE_PROJECTION = { NoteColumns.ID, NoteColumns.MODIFIED_DATE, NoteColumns.SNIPPET, NoteColumns.TYPE }; - + // 笔记投影中 ID 列的索引 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; - + // 用于查询数据的投影,包含数据的内容、MIME 类型等多个字段 private static final String[] DATA_PROJECTION = { DataColumns.CONTENT, DataColumns.MIME_TYPE, @@ -107,31 +118,36 @@ public class BackupUtils { DataColumns.DATA3, DataColumns.DATA4, }; - + // 数据投影中内容列的索引 private static final int DATA_COLUMN_CONTENT = 0; - + // 数据投影中 MIME 类型列的索引 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); mContext = context; mFileName = ""; mFileDirectory = ""; } - + // 根据索引获取对应的文本格式字符串 private String getFormat(int id) { return TEXT_FORMAT[id]; } @@ -139,8 +155,10 @@ public class BackupUtils { /** * Export the folder identified by folder id to text */ + // 将指定文件夹及其下的笔记导出到 PrintStream private void exportFolderToText(String folderId, PrintStream ps) { // Query notes belong to this folder + // 查询属于该文件夹的笔记 Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { folderId @@ -150,14 +168,18 @@ public class BackupUtils { 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 + // 获取笔记 ID String noteId = notesCursor.getString(NOTE_COLUMN_ID); + // 导出该笔记 exportNoteToText(noteId, ps); } while (notesCursor.moveToNext()); } + // 关闭游标 notesCursor.close(); } } @@ -165,7 +187,9 @@ public class BackupUtils { /** * Export note identified by id to a print stream */ - private void exportNoteToText(String noteId, PrintStream 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 @@ -177,6 +201,7 @@ public class BackupUtils { 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); @@ -186,15 +211,18 @@ public class BackupUtils { 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), @@ -203,9 +231,11 @@ public class BackupUtils { } } while (dataCursor.moveToNext()); } + // 关闭游标 dataCursor.close(); } // print a line separator between note + // 在笔记之间打印换行符 try { ps.write(new byte[] { Character.LINE_SEPARATOR, Character.LETTER_NUMBER @@ -218,18 +248,22 @@ public class BackupUtils { /** * Note will be exported as text which is user readable */ - public int exportToText() { + // 将笔记数据导出为用户可读的文本文件 + public int exportToText() { + // 检查外部存储是否可用 if (!externalStorageAvailable()) { Log.d(TAG, "Media was not mounted"); return STATE_SD_CARD_UNMOUONTED; } + // 获取用于导出的 PrintStream 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, @@ -241,6 +275,7 @@ public class BackupUtils { 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); @@ -254,10 +289,12 @@ public class BackupUtils { 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, @@ -271,12 +308,15 @@ public class BackupUtils { mContext.getString(R.string.format_datetime_mdhm), noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); // Query data belong to this note + // 获取笔记 ID String noteId = noteCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (noteCursor.moveToNext()); } + // 关闭游标 noteCursor.close(); } + // 关闭 PrintStream ps.close(); return STATE_SUCCESS; @@ -285,7 +325,9 @@ public class BackupUtils { /** * Get a print stream pointed to the file {@generateExportedTextFile} */ + // 获取指向导出文件的 PrintStream private PrintStream getExportToTextPrintStream() { + // 在 SD 卡上生成导出文件 File file = generateFileMountedOnSDcard(mContext, R.string.file_path, R.string.file_name_txt_format); if (file == null) { @@ -296,6 +338,7 @@ public class BackupUtils { mFileDirectory = mContext.getString(R.string.file_path); PrintStream ps = null; try { + // 创建文件输出流和 PrintStream FileOutputStream fos = new FileOutputStream(file); ps = new PrintStream(fos); } catch (FileNotFoundException e) { @@ -312,6 +355,7 @@ public class BackupUtils { /** * Generate the text file to store imported data */ + // 在 SD 卡上生成用于存储导入数据的文本文件 private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { StringBuilder sb = new StringBuilder(); sb.append(Environment.getExternalStorageDirectory()); @@ -324,9 +368,11 @@ public class BackupUtils { File file = new File(sb.toString()); try { + // 如果目录不存在则创建目录 if (!filedir.exists()) { filedir.mkdir(); } + // 如果文件不存在则创建文件 if (!file.exists()) { file.createNewFile(); } @@ -340,5 +386,13 @@ public class BackupUtils { return null; } } +/* + * 这个类主要实现了以下功能: +采用单例模式,确保整个应用中只有一个BackupUtils实例。 +定义了一系列表示备份和恢复状态的常量,方便在操作过程中返回状态信息。 +通过内部类TextExport实现了将笔记数据按照特定格式导出到文本文件的功能,包括文件夹、笔记及其相关数据的导出,并且在导出过程中会根据外部存储的状态进行相应处理,如果外部存储不可用或获取打印流失败等情况会返回相应的错误状态。同时,还提供了生成导出文件的方法,确保文件在 SD 卡上的正确创建和存储路径的设置。 +在使用过程中,可以通过getInstance方法获取BackupUtils实例,然后调用exportToText方法进行数据导出,并根据返回的状态判断导出是否成功,还可以获取导出文件的名称和目录信息。 +请注意,代码中的Notes类及其内部的常量(如NoteColumns、DataColumns、DataConstants等)和资源字符串(如R.string中的相关字符串)应该是在其他地方定义的,这里假设它们已经正确导入和定义。同时,在实际应用中,可能需要进一步处理异常情况和优化代码逻辑。 + */ diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index ca9e1d9..396bc6b 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -103,47 +103,75 @@ public class DataUtils { */ public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { ContentValues values = new ContentValues(); + // 设置目标文件夹 ID values.put(NoteColumns.PARENT_ID, desFolderId); + // 设置源文件夹 ID values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + // 设置本地已修改标志 values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 执行更新操作,将笔记移动到目标文件夹 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - + /** + * 批量将笔记移动到指定文件夹 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param ids 要移动的笔记 ID 的 HashSet + * @param folderId 目标文件夹 ID + * @return 如果移动成功返回 true,否则返回 false + */ public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { + // 检查 ids 是否为 null if (ids == null) { Log.d(TAG, "the ids is null"); return true; } ArrayList operationList = new ArrayList(); + // 遍历要移动的笔记 ID 集合 for (long id : ids) { + // 创建一个更新操作的构建器,指定要更新的笔记 URI ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 设置目标文件夹 ID builder.withValue(NoteColumns.PARENT_ID, folderId); + // 设置本地已修改标志 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); operationList.add(builder.build()); } try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - if (results == null || results.length == 0 || results[0] == null) { + // 执行批量操作 + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + // 检查结果是否为空或长度为 0 或第一个结果为 null + if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } + // 如果操作成功,返回 true return true; } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } + // 如果发生异常,返回 false return false; } /** * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + */ + + /** + * 获取除系统文件夹({@link Notes#TYPE_SYSTEM})之外的所有文件夹数量 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @return 文件夹数量 */ public static int getUserFolderCount(ContentResolver resolver) { + // 查询数据库,计算符合条件的文件夹数量 Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -154,18 +182,28 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { + // 获取查询结果中的数量 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "get folder count failed:" + e.toString()); } finally { + // 关闭游标 cursor.close(); } } } return count; } - + /** + * 检查笔记在笔记数据库中是否可见(不在回收站且类型匹配) + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param noteId 笔记 ID + * @param type 笔记类型 + * @return 如果可见返回 true,否则返回 false + */ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + // 查询数据库,检查笔记是否存在且符合可见条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -181,8 +219,15 @@ public class DataUtils { } return exist; } - + /** + * 检查笔记在笔记数据库中是否存在 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param noteId 笔记 ID + * @return 如果存在返回 true,否则返回 false + */ public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + // 查询数据库,检查笔记是否存在 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); @@ -195,8 +240,15 @@ public class DataUtils { } return exist; } - +/** + * 检查数据在数据数据库中是否存在 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param dataId 数据 ID + * @return 如果存在返回 true,否则返回 false + */ public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + // 查询数据库,检查数据是否存在 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); @@ -209,8 +261,15 @@ public class DataUtils { } return exist; } - + /** + * 检查指定名称的文件夹在数据库中是否可见(不在回收站且名称匹配) + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param name 文件夹名称 + * @return 如果可见返回 true,否则返回 false + */ public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { + // 查询数据库,检查文件夹是否存在且符合可见条件 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + @@ -225,8 +284,15 @@ public class DataUtils { } return exist; } - + /** + * 获取指定文件夹下的笔记小部件信息 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param folderId 文件夹 ID + * @return 包含笔记小部件信息的 HashSet + */ public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + // 查询数据库,获取指定文件夹下的笔记小部件信息 Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", @@ -252,8 +318,15 @@ public class DataUtils { } return set; } - + /** + * 根据笔记 ID 获取对应的电话号码 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param noteId 笔记 ID + * @return 电话号码,如果获取失败返回空字符串 + */ public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + // 查询数据库,获取指定笔记对应的电话号码 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", @@ -271,8 +344,16 @@ public class DataUtils { } return ""; } - + /** + * 根据电话号码和通话日期获取对应的笔记 ID + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param phoneNumber 电话号码 + * @param callDate 通话日期 + * @return 笔记 ID,如果获取失败返回 0 + */ public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + // 查询数据库,获取指定电话号码和通话日期对应的笔记 ID Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" @@ -292,8 +373,15 @@ public class DataUtils { } return 0; } - +/** + * 根据笔记 ID 获取对应的摘要信息 + * + * @param resolver 用于与内容提供器交互的 ContentResolver + * @param noteId 笔记 ID + * @return 摘要信息,如果笔记不存在则抛出异常 + */ public static String getSnippetById(ContentResolver resolver, long noteId) { + // 查询数据库,获取指定笔记的摘要信息 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -310,7 +398,12 @@ public class DataUtils { } throw new IllegalArgumentException("Note is not found with id: " + noteId); } - +/** + * 格式化摘要信息,去除换行符并截断 + * + * @param snippet 要格式化的摘要信息 + * @return 格式化后的摘要信息 + */ public static String getFormattedSnippet(String snippet) { if (snippet != null) { snippet = snippet.trim();