/* * 版权所有 (c) 2010-2011,MiCode 开源社区 (www.micode.net) * 根据 Apache 许可证 2.0 版本("许可证")授权; * 除非符合许可证的规定,否则不得使用本文件。 * 您可以从以下网址获取许可证副本: * http://www.apache.org/licenses/LICENSE-2.0 * 除非适用法律要求或书面同意,本软件按"原样"分发, * 没有任何明示或暗示的保证或条件。 * 详见许可证中规定的权限和限制。 * (注:这是一份标准的Apache许可证2.0版本的开源声明) */ // 笔记内容提供者的实现类,提供对笔记数据的CRUD操作 package net.micode.notes.data; // 导入所需的Android类 import android.app.SearchManager; // 搜索相关功能 import android.content.ContentProvider; // 内容提供者基类 import android.content.ContentUris; // URI工具类 import android.content.ContentValues; // 键值对存储类 import android.content.Intent; // 意图相关 import android.content.UriMatcher; // URI匹配器 import android.database.Cursor; // 数据库查询结果 import android.database.sqlite.SQLiteDatabase; // SQLite数据库 import android.net.Uri; // URI处理 import android.text.TextUtils; // 文本处理工具 import android.util.Log; // 日志工具 // 导入项目资源 import androidx.annotation.NonNull; import net.micode.notes.R; // 资源文件 import net.micode.notes.data.Notes.DataColumns; // 数据列常量 import net.micode.notes.data.Notes.NoteColumns; // 笔记列常量 import net.micode.notes.data.NotesDatabaseHelper.TABLE; // 数据库表名 import java.util.Objects; // 内容提供者实现类 public class NotesProvider extends ContentProvider { private static final UriMatcher mMatcher; // URI匹配器,用于匹配不同的URI请求 private NotesDatabaseHelper mHelper; // 数据库帮助类 private static final String TAG = "NotesProvider"; // 日志标签 // URI类型常量 private static final int URI_NOTE = 1; // 笔记表操作 private static final int URI_NOTE_ITEM = 2; // 单条笔记操作 private static final int URI_DATA = 3; // 数据表操作 private static final int URI_DATA_ITEM = 4; // 单条数据操作 private static final int URI_SEARCH = 5; // 搜索操作 private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议 // 静态初始化块,配置URI匹配规则 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 创建URI匹配器 mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); // 匹配笔记表URI mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); // 匹配单条笔记URI mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); // 匹配数据表URI mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); // 匹配单条数据URI mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); // 匹配搜索URI mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); // 匹配搜索建议URI mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); // 匹配带参数的搜索建议URI } /** * x'0A'代表SQLite中的换行符。对于搜索结果中的标题和内容, * 我们将删除换行符和空格以显示更多信息。 */ // 搜索结果的投影(列)定义 private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," // 笔记ID + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," // 建议项的额外数据 + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," // 建议项主文本 + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," // 建议项次文本 + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," // 建议项图标 + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," // 建议项点击动作 + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; // 建议项数据 // 搜索查询SQL语句 private static final String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + " FROM " + TABLE.NOTE // 从笔记表查询 + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" // 按内容片段模糊匹配 + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER // 排除回收站中的笔记 + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; // 只查询普通笔记 // 内容提供者创建时调用 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); // 获取数据库帮助类实例 return true; } // 查询方法 @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; // 查询结果游标 SQLiteDatabase db = mHelper.getReadableDatabase(); // 获取可读数据库 String id = null; // ID变量 switch (mMatcher.match(uri)) { // 根据URI类型处理不同查询 case URI_NOTE: // 查询笔记表 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: // 查询单条笔记 id = uri.getPathSegments().get(1); // 从URI中获取笔记ID c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: // 查询数据表 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: // 查询单条数据 id = uri.getPathSegments().get(1); // 从URI中获取数据ID c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_SEARCH: // 搜索请求 case URI_SEARCH_SUGGEST: // 搜索建议请求 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } String searchString = null; // 搜索关键字 if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { // 处理搜索建议请求 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); // 从URI获取搜索词 } } else { // 处理普通搜索请求 searchString = uri.getQueryParameter("pattern"); // 从查询参数获取搜索模式 } if (TextUtils.isEmpty(searchString)) { // 搜索词为空则返回空 return null; } try { searchString = String.format("%%%s%%", searchString); // 添加通配符 c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, // 执行原始查询 new String[] { searchString }); } catch (IllegalStateException ex) { Log.e(TAG, "got exception: " + ex.toString()); // 记录异常 } break; default: throw new IllegalArgumentException("Unknown URI " + uri); // 未知URI异常 } if (c != null) { c.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri); // 设置通知URI } return c; // 返回查询结果 } // 插入方法 @Override public Uri insert(@NonNull Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写数据库 long dataId = 0, noteId = 0, insertedId = 0; // ID变量 insertedId = switch (mMatcher.match(uri)) { // 根据URI类型处理不同插入 case URI_NOTE -> // 插入笔记 noteId = db.insert(TABLE.NOTE, null, values); case URI_DATA -> { if (values.containsKey(DataColumns.NOTE_ID)) { // 检查是否包含笔记ID noteId = values.getAsLong(DataColumns.NOTE_ID); } else { Log.d(TAG, "Wrong data format without note id:" + values.toString()); } yield dataId = db.insert(TABLE.DATA, null, values); } default -> throw new IllegalArgumentException("Unknown URI " + uri); // 未知URI异常 }; // 通知笔记URI变更 if (noteId > 0) { Objects.requireNonNull(getContext()).getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } // 通知数据URI变更 if (dataId > 0) { Objects.requireNonNull(getContext()).getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } return ContentUris.withAppendedId(uri, insertedId); // 返回插入数据的URI } // 删除方法 @Override public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { int count = 0; // 删除计数 String id = null; // ID变量 SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写数据库 boolean deleteData = false; // 数据删除标志 switch (mMatcher.match(uri)) { // 根据URI类型处理不同删除 case URI_NOTE: // 删除笔记 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; // 只删除ID大于0的笔记 count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: // 删除单条笔记 id = uri.getPathSegments().get(1); // 从URI获取笔记ID /* * 小于0的ID是系统文件夹,不允许删除 */ long noteId = Long.parseLong(id); if (noteId <= 0) { // 系统文件夹不删除 break; } count = db.delete(TABLE.NOTE, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: // 删除数据 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; // 标记为数据删除 break; case URI_DATA_ITEM: // 删除单条数据 id = uri.getPathSegments().get(1); // 从URI获取数据ID count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); deleteData = true; // 标记为数据删除 break; default: throw new IllegalArgumentException("Unknown URI " + uri); // 未知URI异常 } if (count > 0) { // 如果有数据被删除 if (deleteData) { // 如果是数据删除,通知笔记URI变更 Objects.requireNonNull(getContext()).getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null); // 通知当前URI变更 } return count; // 返回删除数量 } // 更新方法 @Override public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; // 更新计数 String id = null; // ID变量 SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写数据库 boolean updateData = false; // 数据更新标志 switch (mMatcher.match(uri)) { // 根据URI类型处理不同更新 case URI_NOTE: // 更新笔记表 increaseNoteVersion(-1, selection, selectionArgs); // 增加笔记版本 count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: // 更新单条笔记 id = uri.getPathSegments().get(1); // 从URI获取笔记ID increaseNoteVersion(Long.parseLong(id), selection, selectionArgs); // 增加笔记版本 count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: // 更新数据表 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; // 标记为数据更新 break; case URI_DATA_ITEM: // 更新单条数据 id = uri.getPathSegments().get(1); // 从URI获取数据ID count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); updateData = true; // 标记为数据更新 break; default: throw new IllegalArgumentException("Unknown URI " + uri); // 未知URI异常 } if (count > 0) { // 如果有数据被更新 if (updateData) { // 如果是数据更新,通知笔记URI变更 Objects.requireNonNull(getContext()).getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null); // 通知当前URI变更 } return count; // 返回更新数量 } // 解析选择条件,添加AND连接 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } // 增加笔记版本号 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); // SQL语句构建器 sql.append("UPDATE "); sql.append(TABLE.NOTE); sql.append(" SET "); sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); // 版本号+1 if (id > 0 || !TextUtils.isEmpty(selection)) { // 有条件时添加WHERE sql.append(" WHERE "); } if (id > 0) { // 指定ID的条件 sql.append(NoteColumns.ID + "=").append(id); } if (!TextUtils.isEmpty(selection)) { // 处理其他条件 String selectString = id > 0 ? parseSelection(selection) : selection; for (String args : selectionArgs) { // 替换参数占位符 selectString = selectString.replaceFirst("\\?", args); } sql.append(selectString); } mHelper.getWritableDatabase().execSQL(sql.toString()); // 执行SQL } // 获取URI类型(未实现) @Override public String getType(@NonNull Uri uri) { // TODO Auto-generated method stub return null; } }