read/src/net/micode/notes/data/NotesProvider.java

343 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;
public class NotesProvider extends ContentProvider {
// UriMatcher用于匹配不同的URI请求帮助识别是请求笔记还是笔记数据或是搜索请求等并返回相应的请求类型代码
private static final UriMatcher mMatcher;
// 数据库辅助类,用于创建和管理数据库
private NotesDatabaseHelper mHelper;
// 日志标签,便于调试和日志记录
private static final String TAG = "NotesProvider";
// 定义URI请求类型常量
private static final int URI_NOTE = 1; // 匹配所有笔记的URI
private static final int URI_NOTE_ITEM = 2; // 匹配特定笔记ID的URI
private static final int URI_DATA = 3; // 匹配所有笔记数据的URI
private static final int URI_DATA_ITEM = 4; // 匹配特定笔记数据ID的URI
private static final int URI_SEARCH = 5; // 匹配搜索请求的URI
private static final int URI_SEARCH_SUGGEST = 6; // 匹配搜索建议请求的URI
// 静态代码块在类加载时初始化UriMatcher添加各种URI模式及其对应的代码
static {
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'字符。对于搜索结果中的标题和内容,
* 我们将修剪'\n'和空白字符,以便显示更多信息。
* 此投影用于搜索结果将笔记ID和片段内容格式化以便显示。
*/
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时调用初始化NotesDatabaseHelper以准备数据库操作
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext()); // 获取数据库帮助器的单例实例
return true;
}
// 根据不同的URI请求类型执行查询操作返回查询结果的游标
@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的笔记返回符合条件的单条笔记游标
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的笔记数据返回符合条件的单条笔记数据游标
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(
"不允许在搜索时指定排序条件、选择条件、选择参数或投影");
}
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"); // 从URI查询参数中获取搜索字符串
}
// 如果搜索字符串为空返回null
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
// 格式化搜索字符串使其可以用于LIKE查询。例如如果搜索字符串是"abc",则格式化后的字符串是"%abc%"
searchString = String.format("%%%s%%", searchString);
// 执行搜索查询,返回搜索结果游标
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "发生异常: " + ex.toString()); // 记录异常日志
}
break;
default:
// 如果URI请求类型未知抛出异常以提示错误
throw new IllegalArgumentException("未知的URI " + uri);
}
// 设置游标的通知URI以便内容变化时通知监听者。这有助于UI更新和数据同步。
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c; // 返回查询结果游标
}
// 根据不同的URI请求类型插入数据返回插入数据的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:
// 插入新的笔记返回插入笔记的ID
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA:
// 插入新的笔记数据返回插入数据的ID
if (values.containsKey(DataColumns.NOTE_ID)) {
noteId = values.getAsLong(DataColumns.NOTE_ID); // 获取关联的笔记ID
} else {
Log.d(TAG, "数据格式错误缺少笔记ID:" + values.toString()); // 记录日志,表示数据格式错误
}
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
default:
// 如果URI请求类型未知抛出异常以提示错误
throw new IllegalArgumentException("未知的URI " + uri);
}
// 通知笔记URI变化以便UI更新和数据同步
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 通知数据URI变化以便UI更新和数据同步
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
return ContentUris.withAppendedId(uri, insertedId); // 返回插入数据的URI
}
// 根据不同的URI请求类型删除数据返回删除的数据条目数
@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:
// 删除笔记时确保ID大于0以防止删除系统文件夹
selection = " AND (" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1); // 获取URI路径中的笔记ID
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:
id = uri.getPathSegments().get(1); // 获取URI路径中的笔记数据ID
// 删除特定ID的笔记数据
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
// 如果URI请求类型未知抛出异常以提示错误
throw new IllegalArgumentException("未知的URI " + uri);
}
// 如果有数据被删除通知URI变化以便UI更新和数据同步
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记URI变化
}
getContext().getContentResolver().notifyChange(uri, null); // 通知特定数据URI变化
}
return count; // 返回删除的数据条目数
}
// 根据不同的URI请求类型更新数据返回更新的数据条目数
@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
long noteId = Long.valueOf(id);
// 更新特定ID的笔记并增加笔记版本号
increaseNoteVersion(noteId, 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
// 更新特定ID的笔记数据
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
// 如果URI请求类型未知抛出异常以提示错误
throw new IllegalArgumentException("未知的URI " + uri);
}
// 如果有数据被更新通知URI变化以便UI更新和数据同步
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记URI变化
}
getContext().getContentResolver().notifyChange(uri, null); // 通知特定数据URI变化
}
return count; // 返回更新的数据条目数
}
// 解析选择条件字符串如果存在选择条件则添加到SQL查询中。用于处理动态SQL查询条件
private String parseSelection(String selection) {
// 如果选择条件不为空则将其添加到SQL查询中
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
// 增加笔记版本号用于记录笔记的修改历史。每当笔记被更新时版本号会自动加1
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 "); // 将笔记版本号加1
// 构建WHERE子句以指定更新哪个笔记
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());
}
// 返回URI对应的数据类型。这里未实现返回null。通常需要根据URI返回相应的MIME类型。
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null; // 未实现返回null
}
}