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.
software/NotesProvider.txt

376 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)
*
* 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;
/*
* NotesProvider 是一个 ContentProvider用于管理笔记数据的增删改查操作。
* 它通过 UriMatcher 来匹配不同的 URI并根据匹配结果执行相应的数据库操作。
*/
public class NotesProvider extends ContentProvider {
private static final UriMatcher mMatcher;
// UriMatcher 用于匹配不同的 URI
private NotesDatabaseHelper mHelper;
// 数据库帮助类实例
private static final String TAG = "NotesProvider";
// 日志标签
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 { // 初始化 UriMatcher
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 匹配笔记表
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'。
* 在搜索结果中,标题和内容会去除换行符和空白字符,以便显示更多信息。
*/
/**
* x'0A' represents the '\n' character in sqlite. For title and content in the search result,
* we will trim '\n' and white space in order to show more information.
*/
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 查询语句。
* 查询条件包括:笔记摘要包含搜索关键字、父 ID 不是回收站文件夹、笔记类型为普通笔记。
*/
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 表示初始化成功
*/
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext()); // 获取数据库帮助类的单例实例
return true; // 返回 true 表示初始化成功
}
/*
* 查询操作。
* 该方法根据传入的 URI 执行相应的查询操作,并返回查询结果的 Cursor。
* @param uri 查询的 URI。
* @param projection 查询的列。
* @param selection 查询条件。
* @param selectionArgs 查询条件的参数。
* @param sortOrder 排序方式。
* @return 返回查询结果的 Cursor。
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
SQLiteDatabase db = mHelper.getReadableDatabase(); // 获取可读的数据库实例
String id = null;
switch (mMatcher.match(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); // 获取搜索建议的搜索字符串
}
} 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(getContext().getContentResolver(), uri); // 设置 Cursor 的通知 URI
}
return c; // 返回查询结果的 Cursor
}
/*
* 插入操作。
* 该方法根据传入的 URI 执行相应的插入操作,并返回插入后的 URI。
* @param uri 插入的 URI。
* @param values 插入的数据。
* @return 返回插入后的 URI。
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的数据库实例
long dataId = 0, noteId = 0, insertedId = 0;
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)) {
noteId = values.getAsLong(DataColumns.NOTE_ID); // 获取笔记 ID
} else {
Log.d(TAG, "Wrong data format without note id:" + values.toString()); // 记录日志,数据格式错误
}
insertedId = dataId = db.insert(TABLE.DATA, null, values); // 插入数据
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri); // 抛出未知 URI 异常
}
// Notify the note uri
// 通知笔记 URI 变化
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// Notify the data uri
// 通知数据 URI 变化
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
return ContentUris.withAppendedId(uri, insertedId); // 返回插入后的 URI
}
/*
* 删除操作。
* 该方法根据传入的 URI 执行相应的删除操作,并返回删除的行数。
* @param uri 删除的 URI。
* @param selection 删除条件。
* @param selectionArgs 删除条件的参数。
* @return 返回删除的行数。
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的数据库实例
boolean deleteData = false;
switch (mMatcher.match(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
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
/*
* ID 小于等于 0 的是系统文件夹,不允许删除
*/
long noteId = Long.valueOf(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) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记 URI 变化
}
getContext().getContentResolver().notifyChange(uri, null); // 通知 URI 变化
}
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;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的数据库实例
boolean updateData = false;
switch (mMatcher.match(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.valueOf(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) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记 URI 变化
}
getContext().getContentResolver().notifyChange(uri, null); // 通知 URI 变化
}
return count; // 返回更新的行数
}
/*
* 解析选择条件,用于在查询或更新时附加额外的条件。
* @param selection 原始选择条件。
* @return 返回附加了额外条件的字符串。
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/*
* 增加笔记版本号。
*该方法用于增加指定笔记的版本号,或者根据选择条件增加符合条件的笔记的版本号。
* @param id 笔记 ID如果为 -1 则表示更新所有符合条件的笔记。
* @param selection 更新条件。
* @param selectionArgs 更新条件的参数。
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120); // 创建 StringBuilder 对象,用于构建 SQL 语句
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)); // 根据笔记 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 语句
}
/*
* 获取 MIME 类型。
* @param uri 查询的 URI。
* @return 返回 MIME 类型。
*/
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}