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

434 lines
21 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.
*/
package net.micode.notes.data;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
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;
// 该类是一个内容提供者,用于在不同的应用组件之间共享数据
public class NotesProvider extends ContentProvider {
// 用于匹配Uri的UriMatcher对象通过它可以根据不同的Uri执行不同的操作
private static final UriMatcher mMatcher;
// 用于管理数据库的帮助类实例,通过它可以获取数据库连接进行数据操作
private NotesDatabaseHelper mHelper;
// 用于日志记录的标签,方便在日志中识别该类相关的信息
private static final String TAG = "NotesProvider";
// 定义不同的Uri匹配码用于标识不同类型的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对象初始匹配码为UriMatcher.NO_MATCH表示不匹配任何模式
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加一个匹配规则,匹配以"note"结尾的Uri匹配码为URI_NOTE
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 添加一个匹配规则,匹配以"note/"开头并后跟一个数字的Uri匹配码为URI_NOTE_ITEM
// 这里的"#"是UriMatcher中的通配符表示一个数字
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 添加一个匹配规则,匹配以"data"结尾的Uri匹配码为URI_DATA
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 添加一个匹配规则,匹配以"data/"开头并后跟一个数字的Uri匹配码为URI_DATA_ITEM
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 添加一个匹配规则,匹配以"search"结尾的Uri匹配码为URI_SEARCH
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 添加一个匹配规则匹配以SearchManager.SUGGEST_URI_PATH_QUERY结尾的Uri匹配码为URI_SEARCH_SUGGEST
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
// 添加一个匹配规则匹配以SearchManager.SUGGEST_URI_PATH_QUERY开头并后跟任意字符的Uri匹配码为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;
// 定义搜索片段的查询语句,用于在数据库中查询符合搜索条件的笔记
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;
// 当内容提供者创建时调用的方法
@Override
public boolean onCreate() {
// 获取数据库帮助类的单例实例,该实例用于管理数据库连接和操作
mHelper = NotesDatabaseHelper.getInstance(getContext());
// 返回true表示内容提供者创建成功。此方法只需要成功获取数据库帮助类实例即可视为创建成功
return true;
}
// 处理查询请求的方法根据传入的Uri、投影、选择条件、选择参数和排序顺序返回相应的查询结果
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// 用于存储查询结果的游标初始值为null
Cursor c = null;
// 获取可读的数据库实例,用于执行查询操作
SQLiteDatabase db = mHelper.getReadableDatabase();
// 用于存储从Uri中解析出的ID
String id = null;
// 根据Uri匹配结果进行不同的查询操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 对笔记表进行查询。
// 参数说明:
// - TABLE.NOTE要查询的表名
// - projection要返回的列如果为null则返回所有列
// - selection查询条件如 "column1 =?"
// - selectionArgs查询条件中的参数值对应 "?" 的实际值
// - null分组条件这里不进行分组
// - null过滤分组条件这里不进行过滤
// - sortOrder排序顺序如 "column1 ASC"
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
// 从Uri的路径段中获取笔记的ID
id = uri.getPathSegments().get(1);
// 对笔记表进行查询查询指定ID的笔记。
// 这里将解析出的ID添加到查询条件中并调用parseSelection方法处理额外的选择条件
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;
// 如果是搜索建议的Uri匹配
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
// 如果Uri的路径段数量大于1获取第二个路径段作为搜索字符串
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
} else {
// 对于普通搜索查询从Uri的查询参数中获取搜索字符串
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不匹配任何已知的模式抛出非法参数异常提示未知的Uri
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果查询结果游标不为null设置通知Uri
// 这样当数据发生变化时内容解析器可以通知注册监听该Uri的观察者
if (c!= null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
// 返回查询结果游标
return c;
}
// 处理插入数据请求的方法根据传入的Uri和ContentValues向相应的表中插入数据
@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:
// 向笔记表中插入数据返回插入行的ID
// 这里插入的ID同时赋值给insertedId和noteId
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA:
// 如果ContentValues中包含笔记ID
if (values.containsKey(DataColumns.NOTE_ID)) {
// 获取笔记ID
noteId = values.getAsLong(DataColumns.NOTE_ID);
} else {
// 如果没有包含笔记ID记录日志提示数据格式错误
Log.d(TAG, "Wrong data format without note id:" + values.toString());
}
// 向数据表中插入数据返回插入行的ID
// 这里插入的ID同时赋值给insertedId和dataId
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
default:
// 如果Uri不匹配任何已知模式抛出非法参数异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果插入了笔记ID通知内容解析器该笔记的Uri已改变
// 这样注册监听该笔记Uri的观察者可以收到数据变化的通知
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 如果插入了数据ID通知内容解析器该数据的Uri已改变
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
// 返回包含插入ID的Uri以便调用者可以获取插入数据的具体位置
return ContentUris.withAppendedId(uri, insertedId);
}
// 处理删除数据请求的方法根据传入的Uri、选择条件和选择参数删除相应的数据
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 用于记录删除的行数初始值为0
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的笔记
// 将传入的选择条件用括号括起来,并添加额外条件 "NoteColumns.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转换为long类型
long noteId = Long.valueOf(id);
// 如果ID小于等于0说明是系统文件夹不允许删除直接跳出
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);
}
// 如果删除了数据
if (count > 0) {
// 如果删除的是数据通知内容解析器笔记的Uri已改变
// 因为数据的删除可能会影响到笔记相关的信息
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知内容解析器当前Uri已改变让注册监听该Uri的观察者知道数据变化
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回删除的行数
return count;
}
// 更新数据的方法根据传入的Uri、ContentValues、选择条件和选择参数来更新数据库中的数据
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 用于记录更新的行数初始值为0
int count = 0;
// 用于存储从Uri中解析出的ID
String id = null;
// 获取可写的数据库实例,用于执行更新操作
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否是更新数据的操作
boolean updateData = false;
// 根据Uri匹配结果进行不同的更新操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 增加符合条件的笔记的版本号
// -1 表示针对所有符合选择条件的笔记,而不是特定的某个笔记
increaseNoteVersion(-1, selection, selectionArgs);
// 更新笔记表中的数据
// 参数说明:
// - TABLE.NOTE要更新的表名
// - values包含要更新的列名和对应值的ContentValues对象
// - selection更新条件如 "column1 =?"
// - selectionArgs更新条件中的参数值对应 "?" 的实际值
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 从Uri的路径段中获取笔记的ID
id = uri.getPathSegments().get(1);
// 增加指定ID笔记的版本号
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) {
// 通知内容解析器笔记的Uri已改变
// 因为数据的更新可能会影响到笔记相关的信息
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知内容解析器当前Uri已改变
// 这样注册监听该Uri的观察者可以收到数据变化的通知
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回更新的行数
return count;
}
// 解析选择条件的方法,用于格式化选择条件
private String parseSelection(String selection) {
// 如果选择条件不为空
if (!TextUtils.isEmpty(selection)) {
// 在选择条件前后添加括号,并加上 " AND "
return " AND (" + selection + ')';
} else {
// 如果选择条件为空,返回空字符串
return "";
}
}
// 增加笔记版本号的方法根据传入的ID、选择条件和选择参数更新笔记的版本号
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
// 创建一个StringBuilder用于构建SQL语句
StringBuilder sql = new StringBuilder(120);
// 添加更新的表名
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
// 添加更新的字段和操作将版本号加1
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
// 如果ID大于0或者选择条件不为空添加WHERE子句
if (id > 0 ||!TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
// 如果ID大于0添加ID条件
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
// 如果选择条件不为空
if (!TextUtils.isEmpty(selection)) {
// 根据ID是否大于0来格式化选择条件
String selectString = id > 0? parseSelection(selection) : selection;
// 替换选择条件中的占位符为实际参数
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
// 将格式化后的选择条件添加到SQL语句中
sql.append(selectString);
}
// 执行构建好的SQL语句更新笔记的版本号
mHelper.getWritableDatabase().execSQL(sql.toString());
}
// 获取Uri对应的MIME类型的方法目前该方法未实现
@Override
public String getType(Uri uri) {
// 这是一个待办事项,提示需要实现该方法
// 目前只是简单返回null
// 实际应用中应根据Uri返回正确的MIME类型字符串
return null;
}