|  |  | /*
 | 
						
						
						
							|  |  |  * 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.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;
 | 
						
						
						
							|  |  | import java.util.Set;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | /**
 | 
						
						
						
							|  |  |  * 数据工具类 - 提供批量数据库操作和查询功能
 | 
						
						
						
							|  |  |  * 功能包括:批量删除/移动笔记、文件夹计数、存在性检查、小部件查询等
 | 
						
						
						
							|  |  |  */
 | 
						
						
						
							|  |  | public class DataUtils {
 | 
						
						
						
							|  |  |     private static final String TAG = "DataUtils";
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 批量删除笔记
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param ids      要删除的笔记ID集合
 | 
						
						
						
							|  |  |      * @return 删除成功返回true,否则返回false
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean batchDeleteNotes(ContentResolver resolver, Set<Long> ids) {
 | 
						
						
						
							|  |  |         // 参数有效性检查
 | 
						
						
						
							|  |  |         if (resolver == null) {
 | 
						
						
						
							|  |  |             Log.w(TAG, "ContentResolver is null");
 | 
						
						
						
							|  |  |             return false;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         if (ids == null || ids.isEmpty()) {
 | 
						
						
						
							|  |  |             Log.d(TAG, "No notes to delete");
 | 
						
						
						
							|  |  |             return true;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         ArrayList<ContentProviderOperation> operations = new ArrayList<>(ids.size());
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         for (long id : ids) {
 | 
						
						
						
							|  |  |             // 防止删除系统根文件夹
 | 
						
						
						
							|  |  |             if (id == Notes.ID_ROOT_FOLDER) {
 | 
						
						
						
							|  |  |                 Log.w(TAG, "Skipping system root folder deletion");
 | 
						
						
						
							|  |  |                 continue;
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |             // 添加删除操作
 | 
						
						
						
							|  |  |             operations.add(ContentProviderOperation
 | 
						
						
						
							|  |  |                     .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id))
 | 
						
						
						
							|  |  |                     .build());
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         if (operations.isEmpty()) {
 | 
						
						
						
							|  |  |             Log.d(TAG, "No valid operations to execute");
 | 
						
						
						
							|  |  |             return true;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try {
 | 
						
						
						
							|  |  |             ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operations);
 | 
						
						
						
							|  |  |             return results != null && results.length > 0;
 | 
						
						
						
							|  |  |         } catch (RemoteException | OperationApplicationException e) {
 | 
						
						
						
							|  |  |             Log.e(TAG, "Batch delete failed", e);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return false;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 移动笔记到新文件夹
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver    内容解析器
 | 
						
						
						
							|  |  |      * @param id          笔记ID
 | 
						
						
						
							|  |  |      * @param srcFolderId 源文件夹ID
 | 
						
						
						
							|  |  |      * @param desFolderId 目标文件夹ID
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static void moveNoteToFolder(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
 | 
						
						
						
							|  |  |         if (resolver == null) {
 | 
						
						
						
							|  |  |             Log.w(TAG, "ContentResolver is null");
 | 
						
						
						
							|  |  |             return;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         ContentValues values = new ContentValues();
 | 
						
						
						
							|  |  |         values.put(NoteColumns.PARENT_ID, desFolderId);
 | 
						
						
						
							|  |  |         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 内容解析器
 | 
						
						
						
							|  |  |      * @param ids      笔记ID集合
 | 
						
						
						
							|  |  |      * @param folderId 目标文件夹ID
 | 
						
						
						
							|  |  |      * @return 移动成功返回true,否则返回false
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean batchMoveToFolder(ContentResolver resolver, Set<Long> ids, long folderId) {
 | 
						
						
						
							|  |  |         if (resolver == null) {
 | 
						
						
						
							|  |  |             Log.w(TAG, "ContentResolver is null");
 | 
						
						
						
							|  |  |             return false;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         if (ids == null || ids.isEmpty()) {
 | 
						
						
						
							|  |  |             Log.d(TAG, "No notes to move");
 | 
						
						
						
							|  |  |             return true;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         ArrayList<ContentProviderOperation> operations = new ArrayList<>(ids.size());
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         for (long id : ids) {
 | 
						
						
						
							|  |  |             operations.add(ContentProviderOperation
 | 
						
						
						
							|  |  |                     .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id))
 | 
						
						
						
							|  |  |                     .withValue(NoteColumns.PARENT_ID, folderId)
 | 
						
						
						
							|  |  |                     .withValue(NoteColumns.LOCAL_MODIFIED, 1)
 | 
						
						
						
							|  |  |                     .build());
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try {
 | 
						
						
						
							|  |  |             ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operations);
 | 
						
						
						
							|  |  |             return results != null && results.length > 0;
 | 
						
						
						
							|  |  |         } catch (RemoteException | OperationApplicationException e) {
 | 
						
						
						
							|  |  |             Log.e(TAG, "Batch move failed", e);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return false;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 获取用户创建的文件夹数量(排除系统文件夹)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @return 用户文件夹数量
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static int getUserFolderCount(ContentResolver resolver) {
 | 
						
						
						
							|  |  |         if (resolver == null) {
 | 
						
						
						
							|  |  |             Log.w(TAG, "ContentResolver is null");
 | 
						
						
						
							|  |  |             return 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String[] projection = {NoteColumns.ID};
 | 
						
						
						
							|  |  |         String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = {
 | 
						
						
						
							|  |  |                 String.valueOf(Notes.TYPE_FOLDER),
 | 
						
						
						
							|  |  |                 String.valueOf(Notes.ID_TRASH_FOLDER) // 修正拼写错误:FOLER -> FOLDER
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
 | 
						
						
						
							|  |  |                 projection, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  |             return cursor != null ? cursor.getCount() : 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 检查指定类型笔记是否可见(不在回收站中)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param noteId   笔记ID
 | 
						
						
						
							|  |  |      * @param type     笔记类型
 | 
						
						
						
							|  |  |      * @return 笔记存在且可见返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean isNoteVisible(ContentResolver resolver, long noteId, int type) {
 | 
						
						
						
							|  |  |         if (resolver == null) return false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = {
 | 
						
						
						
							|  |  |                 String.valueOf(type),
 | 
						
						
						
							|  |  |                 String.valueOf(Notes.ID_TRASH_FOLDER)
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
 | 
						
						
						
							|  |  |                 null, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  |             return cursor != null && cursor.getCount() > 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 检查笔记是否存在
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param noteId   笔记ID
 | 
						
						
						
							|  |  |      * @return 笔记存在返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean doesNoteExist(ContentResolver resolver, long noteId) {
 | 
						
						
						
							|  |  |         if (resolver == null) return false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
 | 
						
						
						
							|  |  |                 null, null, null, null)) {
 | 
						
						
						
							|  |  |             return cursor != null && cursor.getCount() > 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 检查笔记数据是否存在
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param dataId   数据ID
 | 
						
						
						
							|  |  |      * @return 数据存在返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean doesDataExist(ContentResolver resolver, long dataId) {
 | 
						
						
						
							|  |  |         if (resolver == null) return false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
 | 
						
						
						
							|  |  |                 null, null, null, null)) {
 | 
						
						
						
							|  |  |             return cursor != null && cursor.getCount() > 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 检查文件夹名称是否已存在(可见文件夹)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param name     文件夹名称
 | 
						
						
						
							|  |  |      * @return 名称已存在返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean isFolderNameExist(ContentResolver resolver, String name) {
 | 
						
						
						
							|  |  |         if (resolver == null || TextUtils.isEmpty(name)) return false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String selection = NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
 | 
						
						
						
							|  |  |                 " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER +
 | 
						
						
						
							|  |  |                 " AND " + NoteColumns.SNIPPET + "=?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = { name };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
 | 
						
						
						
							|  |  |                 null, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  |             return cursor != null && cursor.getCount() > 0;
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 获取文件夹关联的小部件属性
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param folderId 文件夹ID
 | 
						
						
						
							|  |  |      * @return 小部件属性集合
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static Set<AppWidgetAttribute> getFolderWidgets(ContentResolver resolver, long folderId) {
 | 
						
						
						
							|  |  |         Set<AppWidgetAttribute> widgets = new HashSet<>();
 | 
						
						
						
							|  |  |         if (resolver == null) return widgets;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String[] projection = { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE };
 | 
						
						
						
							|  |  |         String selection = NoteColumns.PARENT_ID + "=?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = { String.valueOf(folderId) };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
 | 
						
						
						
							|  |  |                 projection, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |             if (cursor != null && cursor.moveToFirst()) {
 | 
						
						
						
							|  |  |                 do {
 | 
						
						
						
							|  |  |                     try {
 | 
						
						
						
							|  |  |                         AppWidgetAttribute widget = new AppWidgetAttribute();
 | 
						
						
						
							|  |  |                         widget.widgetId = cursor.getInt(0);
 | 
						
						
						
							|  |  |                         widget.widgetType = cursor.getInt(1);
 | 
						
						
						
							|  |  |                         widgets.add(widget);
 | 
						
						
						
							|  |  |                     } catch (Exception e) {
 | 
						
						
						
							|  |  |                         Log.e(TAG, "Error reading widget attributes", e);
 | 
						
						
						
							|  |  |                     }
 | 
						
						
						
							|  |  |                 } while (cursor.moveToNext());
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return widgets;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 通过笔记ID获取电话号码(通话记录笔记)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param noteId   笔记ID
 | 
						
						
						
							|  |  |      * @return 电话号码,查询失败返回空字符串
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static String getCallNumber(ContentResolver resolver, long noteId) {
 | 
						
						
						
							|  |  |         if (resolver == null) return "";
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String[] projection = { CallNote.PHONE_NUMBER };
 | 
						
						
						
							|  |  |         String selection = CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = {
 | 
						
						
						
							|  |  |                 String.valueOf(noteId),
 | 
						
						
						
							|  |  |                 CallNote.CONTENT_ITEM_TYPE
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
 | 
						
						
						
							|  |  |                 projection, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |             if (cursor != null && cursor.moveToFirst()) {
 | 
						
						
						
							|  |  |                 return cursor.getString(0);
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |         } catch (Exception e) {
 | 
						
						
						
							|  |  |             Log.e(TAG, "Error getting call number", e);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return "";
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 通过电话号码和呼叫日期查找笔记ID
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver    内容解析器
 | 
						
						
						
							|  |  |      * @param phoneNumber 电话号码
 | 
						
						
						
							|  |  |      * @param callDate    通话日期
 | 
						
						
						
							|  |  |      * @return 笔记ID,未找到返回0
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static long findNoteByCallDetails(ContentResolver resolver, String phoneNumber, long callDate) {
 | 
						
						
						
							|  |  |         if (resolver == null || TextUtils.isEmpty(phoneNumber)) return 0;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String[] projection = { CallNote.NOTE_ID };
 | 
						
						
						
							|  |  |         String selection = CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND " +
 | 
						
						
						
							|  |  |                 "PHONE_NUMBERS_EQUAL(" + CallNote.PHONE_NUMBER + ", ?)";
 | 
						
						
						
							|  |  |         String[] selectionArgs = {
 | 
						
						
						
							|  |  |                 String.valueOf(callDate),
 | 
						
						
						
							|  |  |                 CallNote.CONTENT_ITEM_TYPE,
 | 
						
						
						
							|  |  |                 phoneNumber
 | 
						
						
						
							|  |  |         };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
 | 
						
						
						
							|  |  |                 projection, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |             if (cursor != null && cursor.moveToFirst()) {
 | 
						
						
						
							|  |  |                 return cursor.getLong(0);
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |         } catch (Exception e) {
 | 
						
						
						
							|  |  |             Log.e(TAG, "Error finding note by call details", e);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return 0;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 获取笔记摘要
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param noteId   笔记ID
 | 
						
						
						
							|  |  |      * @return 笔记摘要
 | 
						
						
						
							|  |  |      * @throws IllegalArgumentException 笔记不存在时抛出异常
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static String getNoteSnippet(ContentResolver resolver, long noteId) {
 | 
						
						
						
							|  |  |         if (resolver == null) throw new IllegalArgumentException("Resolver is null");
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String[] projection = { NoteColumns.SNIPPET };
 | 
						
						
						
							|  |  |         String selection = NoteColumns.ID + "=?";
 | 
						
						
						
							|  |  |         String[] selectionArgs = { String.valueOf(noteId) };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try (Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
 | 
						
						
						
							|  |  |                 projection, selection, selectionArgs, null)) {
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |             if (cursor != null && cursor.moveToFirst()) {
 | 
						
						
						
							|  |  |                 return cursor.getString(0);
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |             throw new IllegalArgumentException("Note not found with id: " + noteId);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 格式化笔记摘要(移除换行符)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param snippet 原始摘要
 | 
						
						
						
							|  |  |      * @return 格式化后的摘要
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static String formatSnippet(String snippet) {
 | 
						
						
						
							|  |  |         if (snippet == null) return null;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         String formatted = snippet.trim();
 | 
						
						
						
							|  |  |         int newlineIndex = formatted.indexOf('\n');
 | 
						
						
						
							|  |  |         return (newlineIndex != -1) ? formatted.substring(0, newlineIndex) : formatted;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /* ----------------- 新增功能 ----------------- */
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 安全删除笔记(防止系统文件夹被删除)
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param noteId   笔记ID
 | 
						
						
						
							|  |  |      * @return 删除成功返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean safeDeleteNote(ContentResolver resolver, long noteId) {
 | 
						
						
						
							|  |  |         Set<Long> ids = new HashSet<>(1);
 | 
						
						
						
							|  |  |         ids.add(noteId);
 | 
						
						
						
							|  |  |         return batchDeleteNotes(resolver, ids);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     /**
 | 
						
						
						
							|  |  |      * 批量更新笔记的本地修改标志
 | 
						
						
						
							|  |  |      *
 | 
						
						
						
							|  |  |      * @param resolver 内容解析器
 | 
						
						
						
							|  |  |      * @param ids      笔记ID集合
 | 
						
						
						
							|  |  |      * @param modified 修改标志值 (1-已修改, 0-未修改)
 | 
						
						
						
							|  |  |      * @return 更新成功返回true
 | 
						
						
						
							|  |  |      */
 | 
						
						
						
							|  |  |     public static boolean batchMarkModified(ContentResolver resolver, Set<Long> ids, int modified) {
 | 
						
						
						
							|  |  |         if (resolver == null) return false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         ArrayList<ContentProviderOperation> operations = new ArrayList<>(ids.size());
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         for (long id : ids) {
 | 
						
						
						
							|  |  |             operations.add(ContentProviderOperation
 | 
						
						
						
							|  |  |                     .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id))
 | 
						
						
						
							|  |  |                     .withValue(NoteColumns.LOCAL_MODIFIED, modified)
 | 
						
						
						
							|  |  |                     .build());
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |         try {
 | 
						
						
						
							|  |  |             resolver.applyBatch(Notes.AUTHORITY, operations);
 | 
						
						
						
							|  |  |             return true;
 | 
						
						
						
							|  |  |         } catch (Exception e) {
 | 
						
						
						
							|  |  |             Log.e(TAG, "Batch mark modified failed", e);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         return false;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | } |