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

396 lines
19 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 {
private static final UriMatcher mMatcher;
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;
static {
// 创建一个UriMatcher实例用于匹配URI。NO_MATCH是初始值表示没有匹配的URI。
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加一个URI匹配规则用于匹配单个note的URI。
// 例如content://com.example.notes.provider/note 会匹配到URI_NOTE。
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 添加一个URI匹配规则用于匹配特定ID的note的URI。
// 例如content://com.example.notes.provider/note/1 会匹配到URI_NOTE_ITEM。
// 这里的#是一个通配符表示任意数字即note的ID
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 添加一个URI匹配规则用于匹配data表的所有数据。
// 例如content://com.example.notes.provider/data 会匹配到URI_DATA。
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 添加一个URI匹配规则用于匹配特定ID的data项的URI。
// 例如content://com.example.notes.provider/data/1 会匹配到URI_DATA_ITEM。
// 同样,#是一个通配符表示data项的ID。
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 添加一个URI匹配规则用于处理搜索请求。
// 例如content://com.example.notes.provider/search 可能会触发搜索操作。
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 添加两个URI匹配规则用于处理搜索建议。
// 第一个规则匹配基本的搜索建议URI如 content://com.example.notes.provider/search_suggest_query。
// 第二个规则匹配带有额外路径段的搜索建议URI这通常用于处理查询参数或特定类型的建议。
// 例如content://com.example.notes.provider/search_suggest_query/some_query_parameters
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
// 注意:在实际应用中,第二个规则可能需要根据具体需求进行调整,
// 因为"/*"会匹配任何路径段,这可能导致意外的匹配。
// 如果搜索建议URI不需要额外的路径段则可能不需要第二个规则。
}
/**
* 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 =
// 返回note的ID通常用于在点击搜索建议时定位具体的note
NoteColumns.ID + "," +
// 将note的ID作为Intent的额外数据返回以便在点击搜索建议时传递
NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," +
// 去除snippet中的换行符\n在SQL中用x'0A'表示),并修剪空格,作为第一条搜索建议文本返回
"TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," +
// 注意这里重复了与TEXT_1相同的逻辑通常TEXT_2用于提供额外的文本信息但在这个例子中似乎是多余的
"TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," +
// 将一个固定的drawable资源ID作为搜索建议的图标返回
// 注意这里使用R.drawable.search_result可能不是最佳实践因为SQL查询通常不直接处理资源ID
// 实际上图标可能需要在搜索建议的适配器中根据返回的ID动态设置
R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," +
// 指定点击搜索建议时要执行的Intent动作这里是查看note
"'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," +
// 指定点击搜索建议时要传递给Intent的数据类型这里是note的内容类型
"'" + 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());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null; // 初始化Cursor为null
SQLiteDatabase db = mHelper.getReadableDatabase(); // 获取可读数据库实例
String id = null; // 用于存储从URI中提取的ID
// 根据URI匹配情况执行不同的查询
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 查询整个Note表
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder);
break;
case URI_NOTE_ITEM:
// 根据URI中的ID查询特定的Note项
id = uri.getPathSegments().get(1);
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection),
selectionArgs, null, null, sortOrder);
break;
case URI_DATA:
// 查询整个Data表
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder);
break;
case URI_DATA_ITEM:
// 根据URI中的ID查询特定的Data项
id = uri.getPathSegments().get(1);
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection),
selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
// 对于搜索URI不允许指定sortOrder, selection, selectionArgs, 或 projection
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException("不支持的查询参数对于搜索URI不应指定sortOrder, selection, selectionArgs, 或 projection");
}
// 从URI中提取搜索字符串
String searchString = null;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
// 对于搜索建议URI从路径段中获取搜索词
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, "执行搜索查询时发生异常: " + ex.toString());
}
break;
default:
// 如果URI不匹配任何已知模式则抛出异常
throw new IllegalArgumentException("未知的URI: " + uri);
}
// 如果查询成功则为Cursor设置通知URI
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
// 返回查询结果的Cursor
return c;
}
@Override
/**
* 向数据库中插入数据。
* 根据提供的URI决定向哪个表中插入数据并使用ContentValues对象中的值作为插入的数据。
*
* @param uri 指定要插入数据的表的URI。
* @param values 包含要插入数据的ContentValues对象。
* @return 返回包含新插入数据ID的Uri但实际上这个方法的当前实现并没有返回Uri需要补充。
* @throws IllegalArgumentException 如果提供的URI未知。
*/
public Uri insert(Uri uri, ContentValues values) {
// 获取数据库的可写实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 初始化ID变量这些变量可能用于记录不同表中插入的ID但在这个例子中有些冗余
long dataId = 0, noteId = 0, insertedId = 0;
// 使用URI匹配器来确定URI对应的表
switch (mMatcher.match(uri)) {
// 如果URI匹配到的是笔记表的URI
case URI_NOTE:
// 向笔记表中插入数据并获取新插入行的ID
// 假设TABLE.NOTE是数据库中笔记表的名称
// values包含了要插入的列和值
// null表示没有特定的列名列表将插入values中所有的列
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
// 注意这里noteId和insertedId被设置为相同的值但在这个方法的上下文中noteId可能并不需要
break;
// 如果URI匹配到的是数据表的URI
case URI_DATA:
// 检查values中是否包含笔记IDDataColumns.NOTE_ID因为数据通常与某个笔记相关联
if (values.containsKey(DataColumns.NOTE_ID)) {
// 从values中获取笔记ID
noteId = values.getAsLong(DataColumns.NOTE_ID);
// 注意尽管获取了noteId但在本例中它并未被用于插入操作除非有额外的逻辑需要它
} else {
// 如果没有提供笔记ID则记录一条日志指出数据格式错误
Log.d(TAG, "Wrong data format without note id:" + values.toString());
}
// 向数据表中插入数据并获取新插入行的ID
// 假设TABLE.DATA是数据库中数据表的名称
insertedId = dataId = db.insert(TABLE.DATA, null, values);
// 同样dataId和insertedId被设置为相同的值但在这个上下文中dataId可能并不需要
break;
// 如果URI不匹配任何已知类型则抛出异常
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 注释这里实际上应该返回一个Uri该Uri包含新插入数据的ID以便客户端可以查询或更新这条数据。
// 然而当前的实现并没有这样做。为了遵循ContentResolver的约定你需要构建并返回一个Uri。
// 例如你可以使用Uri.withAppendedPath()方法结合一个基础URI和新插入的ID来构建返回的Uri。
// 注意由于这个方法的当前签名是返回Uri但实现中并没有返回任何Uri因此你需要在switch语句之后添加相应的逻辑来返回Uri。
// 这里只是提供了注释说明实际实现将取决于你的应用程序的URI设计和需求。
// 假设这里我们返回一个null这通常不是一个好主意因为它违反了方法的签名
// 但你应该用实际的Uri替换它。
return null; // 临时返回null实际应返回包含新ID的Uri
}
// Notify the note uri
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// Notify the data uri
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
return ContentUris.withAppendedId(uri, insertedId);
}
@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 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
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);
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@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);
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);
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
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.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);
}
mHelper.getWritableDatabase().execSQL(sql.toString());
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}