You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MiNote/tool/DataUtils.java

407 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 版权声明本文件由MiCode开源社区开发遵循Apache License, Version 2.0协议;
* 您仅在遵守协议的前提下使用本文件,完整协议可通过以下链接获取:
* http://www.apache.org/licenses/LICENSE-2.0
* 注:未书面明确要求时,本软件按"原样"提供,不附带任何明示或暗示的保证。
*/
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;
/**
* 数据操作工具类
* 提供笔记数据的批量删除、移动、存在性检查、文件夹管理等功能,封装数据库操作细节
*/
public class DataUtils {
public static final String TAG = "DataUtils";
/**
* 批量删除笔记(支持多条记录一次性删除)
* @param resolver ContentResolver实例用于访问内容提供者
* @param ids 待删除的笔记ID集合HashSet<Long>类型)
* @return 删除成功返回true失败返回false
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) { // 空集合校验
Log.d(TAG, "待删除的笔记ID集合为空");
return true;
}
if (ids.size() == 0) { // 无有效ID校验
Log.d(TAG, "笔记ID集合中无元素");
return true;
}
// 构建批量操作列表使用ContentProviderOperation提高效率
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
for (long id : ids) {
if (id == Notes.ID_ROOT_FOLDER) { // 系统根文件夹保护逻辑
Log.e(TAG, "禁止删除系统根文件夹");
continue;
}
// 构建删除操作针对note表中指定ID的记录
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try {
// 执行批量删除操作通过ContentResolver提交
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 结果校验(任意操作失败则整体失败)
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "笔记删除失败ID集合" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) { // 跨进程调用异常(如内容提供者崩溃)
Log.e(TAG, "远程调用异常: " + e.toString() + " - " + e.getMessage());
} catch (OperationApplicationException e) { // 操作应用异常(如数据冲突)
Log.e(TAG, "操作应用异常: " + e.toString() + " - " + e.getMessage());
}
return false;
}
/**
* 将单个笔记移动到目标文件夹
* @param resolver ContentResolver实例
* @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); // 更新父文件夹ID为目标文件夹
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); // 记录原始父文件夹ID
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记本地修改(用于同步)
// 执行更新操作针对note表中指定ID的记录
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
/**
* 批量移动笔记到目标文件夹
* @param resolver ContentResolver实例
* @param ids 待移动的笔记ID集合
* @param folderId 目标文件夹ID
* @return 移动成功返回true失败返回false
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, long folderId) {
if (ids == null) { // 空集合校验
Log.d(TAG, "待移动的笔记ID集合为空");
return true;
}
// 构建批量操作列表使用ContentProviderOperation提高效率
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
for (long id : ids) {
// 构建更新操作设置父文件夹ID并标记本地修改
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId); // 目标文件夹ID
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, "笔记移动失败ID集合" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) { // 跨进程调用异常
Log.e(TAG, "远程调用异常: " + e.toString() + " - " + e.getMessage());
} catch (OperationApplicationException e) { // 操作应用异常
Log.e(TAG, "操作应用异常: " + e.toString() + " - " + e.getMessage());
}
return false;
}
/**
* 获取用户创建的文件夹数量(排除系统文件夹和回收站)
* @param resolver ContentResolver实例
* @return 用户文件夹数量(整数)
*/
public static int getUserFolderCount(ContentResolver resolver) {
// 查询条件:类型为文件夹 且 父ID不等于回收站ID
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 = cursor.getInt(0); // 获取统计结果
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "获取文件夹数量失败: " + e.toString());
} finally {
cursor.close(); // 关闭游标释放资源
}
}
}
return count;
}
/**
* 检查笔记在数据库中是否可见(非回收站且类型匹配)
* @param resolver ContentResolver实例
* @param noteId 待检查的笔记ID
* @param type 笔记类型如Notes.TYPE_NOTE、Notes.TYPE_FOLDER
* @return 可见返回true不可见返回false
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 查询条件指定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) {
exist = cursor.getCount() > 0; // 存在记录则可见
cursor.close(); // 关闭游标
}
return exist;
}
/**
* 检查笔记是否存在于数据库中(无论状态)
* @param resolver ContentResolver实例
* @param noteId 待检查的笔记ID
* @return 存在返回true不存在返回false
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 查询note表中是否存在指定ID的记录
Cursor cursor = resolver.query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
null,
null,
null
);
boolean exist = false;
if (cursor != null) {
exist = cursor.getCount() > 0; // 存在记录则返回true
cursor.close(); // 关闭游标
}
return exist;
}
/**
* 检查关联数据(如文本、通话记录)是否存在于数据库中
* @param resolver ContentResolver实例
* @param dataId 待检查的关联数据IDdata表中的记录ID
* @return 存在返回true不存在返回false
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 查询data表中是否存在指定ID的记录
Cursor cursor = resolver.query(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null,
null,
null,
null
);
boolean exist = false;
if (cursor != null) {
exist = cursor.getCount() > 0; // 存在记录则返回true
cursor.close(); // 关闭游标
}
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 +
" AND " + NoteColumns.SNIPPET + "=?",
new String[]{name},
null
);
boolean exist = false;
if (cursor != null) {
exist = cursor.getCount() > 0; // 存在记录则名称已存在
cursor.close(); // 关闭游标
}
return exist;
}
/**
* 获取指定文件夹下的所有桌面小部件属性(用于小部件管理)
* @param resolver ContentResolver实例
* @param folderId 目标文件夹ID
* @return 小部件属性集合HashSet<AppWidgetAttribute>无数据返回null
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 查询note表中属于该文件夹的小部件记录只取widget_id和widget_type字段
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<AppWidgetAttribute> set = null;
if (c != null) {
if (c.moveToFirst()) {
set = new HashSet<>(); // 初始化集合
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0); // 小部件ID
widget.widgetType = c.getInt(1); // 小部件类型
set.add(widget); // 添加到集合
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "解析小部件属性失败: " + e.toString());
}
} while (c.moveToNext()); // 遍历所有记录
}
c.close(); // 关闭游标
}
return set;
}
/**
* 根据笔记ID获取通话记录的电话号码仅适用于通话类型笔记
* @param resolver ContentResolver实例
* @param noteId 通话类型笔记的ID
* @return 电话号码字符串(无数据返回空字符串)
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 查询data表中关联的通话记录MIME类型为CallNote.CONTENT_ITEM_TYPE
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, "获取通话号码失败: " + e.toString());
} finally {
cursor.close(); // 关闭游标
}
}
return ""; // 无数据返回空字符串
}
/**
* 根据电话号码和通话时间查找关联的笔记ID用于通话记录匹配
* @param resolver ContentResolver实例
* @param phoneNumber 电话号码
* @param callDate 通话时间戳(毫秒级)
* @return 匹配的笔记ID无匹配返回0
*/
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 查询条件通话时间匹配、MIME类型正确、电话号码匹配使用PHONE_NUMBERS_EQUAL处理号码格式
Cursor cursor = resolver.query(
Notes.CONTENT_DATA_URI,
new String[]{CallNote.NOTE_ID}, // 只取笔记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 {
return cursor.getLong(0); // 返回匹配的笔记ID
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "获取通话笔记ID失败: " + e.toString());
}
}
cursor.close(); // 关闭游标
}
return 0; // 无匹配返回0
}
/**
* 根据笔记ID获取摘要内容用于显示预览
* @param resolver ContentResolver实例
* @param noteId 笔记ID
* @return 摘要字符串(无数据抛出异常)
*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 查询note表中的摘要字段
Cursor cursor = resolver.query(
Notes.CONTENT_NOTE_URI,
new String[]{NoteColumns.SNIPPET}, // 只取摘要字段
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)},
null
);
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0); // 获取摘要内容
}
cursor.close(); // 关闭游标
return snippet;
}
// 未找到笔记时抛出异常(参数非法)
throw new IllegalArgumentException("未找到ID为" + noteId + "的笔记");
}
/**
* 格式化摘要内容(用于显示优化)
* @param snippet 原始摘要字符串
* @return 格式化后的摘要(去除首尾空格,截断换行符前的内容)
*/
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim(); // 去除首尾空格
int index = snippet.indexOf('\n'); // 查找换行符位置
if (index != -1) { // 存在换行符时截断
snippet = snippet.substring(0, index);
}
}
return snippet;
}
}