package net.micode.notes.tool; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; import android.os.RemoteException; import android.util.Log; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.CallNote; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; // DataUtils类提供了一系列用于操作笔记数据的实用方法,例如批量删除笔记、移动笔记到文件夹、查询各种数据相关的存在性等功能 public class DataUtils { public static final String TAG = "DataUtils"; // 批量删除指定ID集合对应的笔记 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { // 如果传入的要删除的笔记ID集合为null,记录日志并返回true(表示无需进行删除操作,直接认为成功) if (ids == null) { Log.d(TAG, "the ids is null"); return true; } // 如果传入的要删除的笔记ID集合大小为0,即没有要删除的笔记,记录日志并返回true(表示无需进行删除操作,直接认为成功) if (ids.size() == 0) { Log.d(TAG, "no id is in the hashset"); return true; } // 创建一个用于存储内容提供器操作的列表,后续会将批量删除操作添加到这个列表中 ArrayList operationList = new ArrayList(); // 遍历要删除的笔记ID集合 for (long id : ids) { // 如果当前笔记的ID是系统根文件夹的ID(通常系统根文件夹不允许删除),记录错误日志并跳过本次循环,不添加到删除操作列表中 if (id == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Don't delete system folder root"); continue; } // 创建一个用于删除指定笔记的内容提供器操作构建器,指定要删除的笔记的URI(通过将笔记ID附加到基础的笔记内容URI上) ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 将构建好的删除操作添加到操作列表中 operationList.add(builder.build()); } try { // 执行批量的内容提供器操作,传入对应的权限和操作列表,获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 如果操作结果数组为null,或者长度为0,或者第一个结果为null(表示操作可能没有正确执行),记录日志并返回false(表示删除笔记失败) 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; } // 将指定ID的笔记移动到目标文件夹,同时记录笔记的原始文件夹ID,并标记本地已修改 public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { // 创建一个用于存储要更新的列和对应值的ContentValues对象 ContentValues values = new ContentValues(); // 设置笔记的新父文件夹ID(即目标文件夹ID) values.put(NoteColumns.PARENT_ID, desFolderId); // 设置笔记的原始父文件夹ID(即来源文件夹ID) values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); // 设置本地已修改的标记为1,表示该笔记有本地修改操作 values.put(NoteColumns.LOCAL_MODIFIED, 1); // 通过内容解析器更新指定ID的笔记,使用上面设置好的ContentValues对象来更新相应列的值,后面两个参数为null表示不使用条件筛选和不传入额外的参数 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } // 批量将指定ID集合的笔记移动到指定文件夹 public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { // 如果传入的要移动的笔记ID集合为null,记录日志并返回true(表示无需进行移动操作,直接认为成功) if (ids == null) { Log.d(TAG, "the ids is null"); return true; } // 创建一个用于存储内容提供器操作的列表,后续会将批量移动操作添加到这个列表中 ArrayList operationList = new ArrayList(); // 遍历要移动的笔记ID集合 for (long id : ids) { // 创建一个用于更新指定笔记的内容提供器操作构建器,指定要更新的笔记的URI(通过将笔记ID附加到基础的笔记内容URI上) ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 设置笔记的新父文件夹ID(即目标文件夹ID) builder.withValue(NoteColumns.PARENT_ID, folderId); // 设置本地已修改的标记为1,表示该笔记有本地修改操作 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); // 将构建好的更新操作添加到操作列表中 operationList.add(builder.build()); } try { // 执行批量的内容提供器操作,传入对应的权限和操作列表,获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 如果操作结果数组为null,或者长度为0,或者第一个结果为null(表示操作可能没有正确执行),记录日志并返回false(表示移动笔记失败) 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; } /** * 获取除系统文件夹({@link Notes#TYPE_SYSTEM} 标记的文件夹)之外的所有用户文件夹的数量 */ public static int getUserFolderCount(ContentResolver resolver) { // 通过内容解析器查询满足条件的文件夹数量,查询条件是类型为文件夹且父文件夹ID不是回收站文件夹ID,只查询记录数量(通过 COUNT(*) 聚合函数) Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String[]{"COUNT(*)"}, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", new String[]{String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, null); int count = 0; // 如果查询结果游标不为空 if (cursor!= null) { // 将游标移动到第一条数据(虽然这里只有一条数据,就是查询到的数量结果) if (cursor.moveToFirst()) { try { // 尝试从游标中获取第一列(也就是查询到的数量值)并赋值给count变量 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { // 如果发生越界异常(比如查询结果格式不符合预期等情况),记录详细的异常信息到日志中 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { // 无论是否发生异常,都关闭游标,释放相关资源 cursor.close(); } } } return count; } // 判断指定ID的笔记是否在笔记数据库中可见(即不在回收站且类型符合要求) public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { // 通过内容解析器查询指定笔记是否存在且满足可见条件(类型符合要求且父文件夹ID不是回收站文件夹ID) Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, new String[]{String.valueOf(type)}, null); boolean exist = false; // 如果查询结果游标不为空 if (cursor!= null) { // 如果游标中的记录数量大于0,表示笔记存在且满足可见条件,将exist设置为true if (cursor.getCount() > 0) { exist = true; } // 关闭游标,释放相关资源 cursor.close(); } return exist; } // 判断指定ID的笔记是否存在于笔记数据库中(简单查询是否存在该记录) public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { // 通过内容解析器查询指定笔记是否存在,不添加额外的查询条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); boolean exist = false; // 如果查询结果游标不为空 if (cursor!= null) { // 如果游标中的记录数量大于0,表示笔记存在,将exist设置为true if (cursor.getCount() > 0) { exist = true; } // 关闭游标,释放相关资源 cursor.close(); } return exist; } // 判断指定ID的数据记录是否存在于数据数据库中(简单查询是否存在该记录) public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { // 通过内容解析器查询指定数据记录是否存在,不添加额外的查询条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); boolean exist = false; // 如果查询结果游标不为空 if (cursor!= null) { // 如果游标中的记录数量大于0,表示数据记录存在,将exist设置为true if (cursor.getCount() > 0) { exist = true; } // 关闭游标,释放相关资源 cursor.close(); } return exist; } // 检查指定名称的文件夹是否在笔记数据库中可见(即不在回收站且类型为文件夹、名称匹配) public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { // 通过内容解析器查询满足条件的文件夹是否存在,条件是类型为文件夹、父文件夹ID不是回收站文件夹ID且摘要(名称)匹配传入的名称 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.SNIPPET + "=?", new String[]{name}, null); boolean exist = false; // 如果查询结果游标不为空 if (cursor!= null) { // 如果游标中的记录数量大于0,表示文件夹存在且满足可见条件,将exist设置为true if (cursor.getCount() > 0) { exist = true; } // 关闭游标,释放相关资源 cursor.close(); } return exist; } // 获取指定文件夹下所有笔记对应的小部件相关属性集合(包括小部件ID和小部件类型) public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { // 通过内容解析器查询指定文件夹下所有笔记的小部件ID和小部件类型信息 Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[]{NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE}, NoteColumns.PARENT_ID + "=?", new String[]{String.valueOf(folderId)}, null); HashSet set = null; // 如果查询结果游标不为空 if (c!= null) { // 将游标移动到第一条数据位置(开始遍历数据) if (c.moveToFirst()) { // 创建一个用于存储小部件相关属性的HashSet集合 set = new HashSet(); do { try { // 创建一个AppWidgetAttribute对象,用于存储单个小部件的属性 AppWidgetAttribute widget = new AppWidgetAttribute(); // 从游标中获取小部件ID并赋值给AppWidgetAttribute对象的对应属性 widget.widgetId = c.getInt(0); // 从游标中获取小部件类型并赋值给AppWidgetAttribute对象的对应属性 widget.widgetType = c.getInt(1); // 将小部件属性对象添加到HashSet集合中 set.add(widget); } catch (IndexOutOfBoundsException e) { // 如果发生越界异常(比如查询结果格式不符合预期等情况),记录详细的异常信息到日志中 Log.e(TAG, e.toString()); } } while (c.moveToNext()); // 循环遍历游标中的下一条数据,直到全部遍历完 } // 关闭游标,释放相关资源 c.close(); } return set; } // 根据笔记ID获取对应的通话号码(前提是该笔记是通话相关笔记) public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { // 通过内容解析器查询指定笔记对应的通话号码,查询条件是笔记ID匹配且MIME类型为通话笔记的类型 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String[]{CallNote.PHONE_NUMBER}, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", new String[]{String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE}, null); if (cursor!= null && cursor.moveToFirst()) { try { // 如果查询到数据且游标移动到第一条数据位置,尝试从游标中获取通话号码并返回 return cursor.getString(0); } catch (IndexOutOfBoundsException e) { // 如果发生越界异常(比如查询结果格式不符合预期等情况),记录详细的异常信息到日志中 Log.e(TAG, "Get call number fails " + e.toString()); } finally { // 无论是否发生异常,都关闭游标,释放相关资源 cursor.close(); } } return ""; // 如果没有查询到通话号码或者发生异常,返回空字符串 } // 根据电话号码和通话日期获取对应的笔记ID(前提是存在关联的通话笔记) public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { // 通过内容解析器查询满足条件的笔记ID,条件是通话日期匹配、MIME类型为通话笔记的类型且电话号码匹配(通过自定义函数PHONE_NUMBERS_EQUAL判断) Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String[]{CallNote.NOTE_ID}, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + CallNote.PHONE_NUMBER + ",?)", new String[]{String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber}, null); if (cursor!= null) { if (cursor.moveToFirst()) { try { // 如果查询到数据且游标移动到第一条数据位置,尝试从游标中获取笔记ID并返回 return cursor.getLong(0); } catch (IndexOutOfBoundsException e) { // 如果发生越界异常(比如查询结果格式不符合预期等情况),记录详细的异常信息到日志中 Log.e(TAG, "Get call note id fails " + e.toString()); } } // 关闭游标,释放相关资源