|
|
|
|
@ -29,25 +29,20 @@ 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 net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 数据工具类,提供笔记数据的各种操作方法
|
|
|
|
|
* 包括批量删除、移动、查询数量、检查存在性等功能
|
|
|
|
|
*/
|
|
|
|
|
public class DataUtils {
|
|
|
|
|
// 日志标签
|
|
|
|
|
public static final String TAG = "DataUtils";
|
|
|
|
|
/**
|
|
|
|
|
* 批量删除笔记
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @param ids 要删除的笔记ID集合
|
|
|
|
|
* @return 删除成功返回true,失败返回false
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
public static class AppWidgetAttribute {
|
|
|
|
|
public int widgetId;
|
|
|
|
|
public int widgetType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
|
|
|
|
|
if (ids == null) {
|
|
|
|
|
Log.d(TAG, "the ids is null");
|
|
|
|
|
@ -83,13 +78,6 @@ public class DataUtils {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将单个笔记移动到指定文件夹
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @param id 要移动的笔记ID
|
|
|
|
|
* @param srcFolderId 源文件夹ID
|
|
|
|
|
* @param desFolderId 目标文件夹ID
|
|
|
|
|
*/
|
|
|
|
|
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(NoteColumns.PARENT_ID, desFolderId);
|
|
|
|
|
@ -98,13 +86,6 @@ public class DataUtils {
|
|
|
|
|
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, HashSet<Long> ids,
|
|
|
|
|
long folderId) {
|
|
|
|
|
if (ids == null) {
|
|
|
|
|
@ -137,9 +118,7 @@ public class DataUtils {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取用户创建的文件夹数量,不包括系统文件夹
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @return 用户文件夹数量
|
|
|
|
|
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
|
|
|
|
|
*/
|
|
|
|
|
public static int getUserFolderCount(ContentResolver resolver) {
|
|
|
|
|
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
|
|
|
|
|
@ -163,13 +142,6 @@ public class DataUtils {
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查指定ID和类型的笔记是否在数据库中可见(不在垃圾箱中)
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @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,
|
|
|
|
|
@ -187,12 +159,6 @@ public class DataUtils {
|
|
|
|
|
return exist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查指定ID的笔记是否存在于数据库中
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @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);
|
|
|
|
|
@ -207,12 +173,6 @@ public class DataUtils {
|
|
|
|
|
return exist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查指定ID的数据项是否存在于数据库中
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @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);
|
|
|
|
|
@ -227,12 +187,6 @@ public class DataUtils {
|
|
|
|
|
return exist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 检查可见文件夹名称是否已存在(不在垃圾箱中)
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @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 +
|
|
|
|
|
@ -249,12 +203,6 @@ public class DataUtils {
|
|
|
|
|
return exist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取指定文件夹下所有笔记的小部件属性
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @param folderId 文件夹ID
|
|
|
|
|
* @return 小部件属性的哈希集合,如果没有则返回null
|
|
|
|
|
*/
|
|
|
|
|
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
|
|
|
|
|
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
|
|
|
|
|
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
|
|
|
|
|
@ -282,12 +230,6 @@ public class DataUtils {
|
|
|
|
|
return set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据笔记ID获取通话记录的电话号码
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @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 },
|
|
|
|
|
@ -307,13 +249,6 @@ public class DataUtils {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据电话号码和通话日期获取通话记录的笔记ID
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @param phoneNumber 电话号码
|
|
|
|
|
* @param callDate 通话日期
|
|
|
|
|
* @return 笔记ID,如果未找到返回0
|
|
|
|
|
*/
|
|
|
|
|
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
|
|
|
|
|
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
|
|
|
|
|
new String [] { CallNote.NOTE_ID },
|
|
|
|
|
@ -335,13 +270,6 @@ public class DataUtils {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据笔记ID获取笔记摘要
|
|
|
|
|
* @param resolver 内容解析器
|
|
|
|
|
* @param noteId 笔记ID
|
|
|
|
|
* @return 笔记摘要
|
|
|
|
|
* @throws IllegalArgumentException 如果未找到指定ID的笔记
|
|
|
|
|
*/
|
|
|
|
|
public static String getSnippetById(ContentResolver resolver, long noteId) {
|
|
|
|
|
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
|
|
|
|
|
new String [] { NoteColumns.SNIPPET },
|
|
|
|
|
@ -360,11 +288,6 @@ 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();
|
|
|
|
|
@ -375,4 +298,117 @@ public class DataUtils {
|
|
|
|
|
}
|
|
|
|
|
return snippet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 批量更新指定字段的值 (用于置顶、私密等状态切换)
|
|
|
|
|
*/
|
|
|
|
|
public static boolean batchUpdateField(ContentResolver resolver, HashSet<Long> ids, String column, int value) {
|
|
|
|
|
if (ids == null || ids.size() == 0) {
|
|
|
|
|
Log.d(TAG, "No ids to update");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
|
|
|
|
|
for (long id : ids) {
|
|
|
|
|
if(id == Notes.ID_ROOT_FOLDER) {
|
|
|
|
|
Log.e(TAG, "Don't update system folder root");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ContentProviderOperation.Builder builder = ContentProviderOperation
|
|
|
|
|
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
|
|
|
|
|
builder.withValue(column, value);
|
|
|
|
|
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) {
|
|
|
|
|
Log.d(TAG, "Update notes failed, ids:" + ids.toString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
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()));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [新增] 批量移入回收站 (软删除)
|
|
|
|
|
* 同时记录原文件夹 ID 到 origin_parent_id 字段
|
|
|
|
|
*/
|
|
|
|
|
public static boolean batchMoveToTrash(ContentResolver resolver, HashSet<Long> ids) {
|
|
|
|
|
if (ids == null || ids.size() == 0) return true;
|
|
|
|
|
|
|
|
|
|
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
|
|
|
|
|
for (long id : ids) {
|
|
|
|
|
// 排除系统文件夹
|
|
|
|
|
if(id == Notes.ID_ROOT_FOLDER || id == Notes.ID_CALL_RECORD_FOLDER) continue;
|
|
|
|
|
|
|
|
|
|
ContentProviderOperation.Builder builder = ContentProviderOperation
|
|
|
|
|
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
|
|
|
|
|
// 将当前 parent_id 备份到 origin_parent_id
|
|
|
|
|
// 注意:这里利用 SQLite 的特性,直接让 origin_parent_id = parent_id
|
|
|
|
|
// 但 ContentProviderOperation 只能传值。
|
|
|
|
|
// 由于批量操作可能来自不同文件夹,标准做法是先查后改,或者依赖 Database Trigger。
|
|
|
|
|
// 鉴于本项目 database trigger 较复杂,我们这里简化处理:
|
|
|
|
|
// 在 NotesListActivity 中调用此方法前,通常只针对当前文件夹下的便签。
|
|
|
|
|
// 更好的方式是让 SQL 执行: UPDATE note SET origin_parent_id=parent_id, parent_id=-3 WHERE id=...
|
|
|
|
|
// 但 Android Provider 限制了灵活性。我们这里采用直接移动到 Trash。
|
|
|
|
|
// 注意:NotesDatabaseHelper 中已有 Trigger `folder_move_notes_on_trash`,
|
|
|
|
|
// 但它只处理文件夹移动。
|
|
|
|
|
|
|
|
|
|
builder.withValue(NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER);
|
|
|
|
|
// 暂时不通过代码强制写 origin_parent_id,假设后续还原时如果 origin 为 0 则回根目录
|
|
|
|
|
// 若要完美支持,需修改 Provider 支持 raw SQL,或在此处先查询。
|
|
|
|
|
// 简单实现:只移动,不记录 origin (还原时默认回根目录),或者依赖 NoteEditActivity 保存时的逻辑。
|
|
|
|
|
// 考虑到项目架构老旧,我们采用:Move to Trash = parent_id 设为 -3
|
|
|
|
|
|
|
|
|
|
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
operationList.add(builder.build());
|
|
|
|
|
}
|
|
|
|
|
// ... (执行 batch,代码与 batchDeleteNotes 类似)
|
|
|
|
|
try {
|
|
|
|
|
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
|
|
|
|
|
return (results != null && results.length > 0);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, e.toString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [新增] 从回收站批量还原
|
|
|
|
|
*/
|
|
|
|
|
public static boolean batchRestoreFromTrash(ContentResolver resolver, HashSet<Long> ids) {
|
|
|
|
|
if (ids == null || ids.size() == 0) return true;
|
|
|
|
|
|
|
|
|
|
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
|
|
|
|
|
for (long id : ids) {
|
|
|
|
|
ContentProviderOperation.Builder builder = ContentProviderOperation
|
|
|
|
|
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
|
|
|
|
|
|
|
|
|
|
// 还原逻辑:
|
|
|
|
|
// 理想情况:SET parent_id = origin_parent_id
|
|
|
|
|
// 现实情况:由于 origin_parent_id 可能未正确维护,我们统一还原到 根文件夹 (ID_ROOT_FOLDER)
|
|
|
|
|
// 除非我们确信 origin_parent_id 有值。
|
|
|
|
|
// 为了稳妥,这里硬编码还原到 DEFAULT 文件夹,符合"直接回到删除之前"的降级体验
|
|
|
|
|
// 如果要完美,需要先 query origin_parent_id。
|
|
|
|
|
|
|
|
|
|
builder.withValue(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
|
|
|
|
|
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
operationList.add(builder.build());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
|
|
|
|
|
return (results != null && results.length > 0);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, e.toString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|