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.
king/data/NotesProvider.java

431 lines
18 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)
*
* 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.
*/
// 包声明,表明该类属于 net.micode.notes.data 包
package net.micode.notes.data;
// 导入 Android 搜索管理相关类,用于处理搜索功能
import android.app.SearchManager;
// 导入 Android 内容提供者基类,用于与外部应用共享数据
import android.content.ContentProvider;
// 导入 Android 用于处理 Content URI 并添加 ID 的工具类
import android.content.ContentUris;
// 导入 Android 用于存储键值对,通常用于数据库操作中插入或更新数据
import android.content.ContentValues;
// 导入 Android 用于在不同组件间传递消息的类
import android.content.Intent;
// 导入 Android 用于匹配 URI 的工具类
import android.content.UriMatcher;
// 导入 Android 数据库游标类,用于处理数据库查询结果
import android.database.Cursor;
// 导入 Android SQLite 数据库操作类,用于对数据库进行读写操作
import android.database.sqlite.SQLiteDatabase;
// 导入 Android 用于表示统一资源标识符的类
import android.net.Uri;
// 导入 Android 用于处理文本相关操作的工具类
import android.text.TextUtils;
// 导入 Android 日志工具类,用于在开发和调试过程中记录信息
import android.util.Log;
// 导入应用资源类,用于引用应用内的资源
import net.micode.notes.R;
// 导入 Notes 类中的 DataColumns 接口,用于引用数据列的常量
import net.micode.notes.data.Notes.DataColumns;
// 导入 Notes 类中的 NoteColumns 接口,用于引用笔记列的常量
import net.micode.notes.data.Notes.NoteColumns;
// 导入 NotesDatabaseHelper 类中的 TABLE 接口,用于引用数据库表名常量
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
/**
* NotesProvider 类继承自 ContentProvider用于提供对笔记数据的增删改查操作。
* 它作为数据的提供者,允许其他应用通过 ContentResolver 访问笔记数据。
*/
public class NotesProvider extends ContentProvider {
// 用于匹配 URI 的对象
private static final UriMatcher mMatcher;
// 数据库帮助类的实例
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;
// 静态代码块,在类加载时初始化 UriMatcher
static {
// 创建一个 UriMatcher 实例,初始匹配值为 NO_MATCH
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加不同的 URI 匹配规则
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
/**
* x'0A' 表示 SQLite 中的 '\n' 字符。对于搜索结果中的标题和内容,
* 我们将去除 '\n' 和空格以显示更多信息。
*/
// 笔记搜索的投影,定义了搜索结果的列
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.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 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;
/**
* 当 ContentProvider 创建时调用,初始化数据库帮助类
* @return 若创建成功返回 true否则返回 false
*/
@Override
public boolean onCreate() {
// 获取 NotesDatabaseHelper 的单例实例
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* 根据给定的 URI 进行查询操作
* @param uri 查询的 URI
* @param projection 查询的列
* @param selection 查询条件
* @param selectionArgs 查询条件的参数
* @param sortOrder 排序规则
* @return 查询结果的游标
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// 初始化游标
Cursor c = null;
// 获取可读的数据库实例
SQLiteDatabase db = mHelper.getReadableDatabase();
// 用于存储从 URI 中提取的 ID
String id = null;
// 根据 URI 匹配结果执行不同的查询操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 查询笔记表
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
// 从 URI 中提取笔记 ID
id = uri.getPathSegments().get(1);
// 根据笔记 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:
// 从 URI 中提取数据 ID
id = uri.getPathSegments().get(1);
// 根据数据 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) {
// 从 URI 中提取搜索字符串
searchString = uri.getPathSegments().get(1);
}
} else {
// 从查询参数中获取搜索字符串
searchString = uri.getQueryParameter("pattern");
}
// 如果搜索字符串为空,返回 null
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:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果游标不为空,设置通知 URI
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
/**
* 向指定 URI 插入数据
* @param uri 插入数据的 URI
* @param values 要插入的数据
* @return 插入数据后的 URI
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
// 获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 初始化数据 ID、笔记 ID 和插入的 ID
long dataId = 0, noteId = 0, insertedId = 0;
// 根据 URI 匹配结果执行不同的插入操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 插入笔记数据
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
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());
}
// 插入数据
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
default:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 通知笔记 URI 数据已更改
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 通知数据 URI 数据已更改
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
// 返回插入数据后的 URI
return ContentUris.withAppendedId(uri, insertedId);
}
/**
* 根据给定的 URI 删除数据
* @param uri 要删除数据的 URI
* @param selection 删除条件
* @param selectionArgs 删除条件的参数
* @return 删除的行数
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 初始化删除的行数
int count = 0;
// 用于存储从 URI 中提取的 ID
String id = null;
// 获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否删除数据
boolean deleteData = false;
// 根据 URI 匹配结果执行不同的删除操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 确保删除的笔记 ID 大于 0
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
// 删除笔记数据
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 从 URI 中提取笔记 ID
id = uri.getPathSegments().get(1);
/**
* ID 小于 0 的是系统文件夹,不允许删除
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
}
// 根据笔记 ID 删除笔记数据
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:
// 从 URI 中提取数据 ID
id = uri.getPathSegments().get(1);
// 根据数据 ID 删除数据
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果删除行数大于 0通知数据已更改
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 根据给定的 URI 更新数据
* @param uri 要更新数据的 URI
* @param values 要更新的数据
* @param selection 更新条件
* @param selectionArgs 更新条件的参数
* @return 更新的行数
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 初始化更新的行数
int count = 0;
// 用于存储从 URI 中提取的 ID
String id = null;
// 获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否更新数据
boolean updateData = false;
// 根据 URI 匹配结果执行不同的更新操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 增加笔记版本号
increaseNoteVersion(-1, selection, selectionArgs);
// 更新笔记数据
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 从 URI 中提取笔记 ID
id = uri.getPathSegments().get(1);
// 增加笔记版本号
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
// 根据笔记 ID 更新笔记数据
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:
// 从 URI 中提取数据 ID
id = uri.getPathSegments().get(1);
// 根据数据 ID 更新数据
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果更新行数大于 0通知数据已更改
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 解析查询条件,添加 AND 连接符
* @param selection 查询条件
* @return 解析后的查询条件
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 增加笔记的版本号
* @param id 笔记 ID
* @param selection 更新条件
* @param selectionArgs 更新条件的参数
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
// 用于构建 SQL 语句
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
if (!TextUtils.isEmpty(selection)) {
// 处理更新条件
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
sql.append(selectString);
}
// 执行 SQL 语句
mHelper.getWritableDatabase().execSQL(sql.toString());
}
/**
* 获取给定 URI 的 MIME 类型,当前方法未实现,返回 null
* @param uri 要查询 MIME 类型的 URI
* @return MIME 类型字符串
*/
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}