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.
git-test/src/main/java/net/micode/notes/data/NotesProvider.java

420 lines
16 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;
/**
* 便签应用的内容提供者ContentProvider
* 负责管理便签数据的CRUD操作包括搜索功能
*/
public class NotesProvider extends ContentProvider {
/**
* URI匹配器用于匹配不同的URI请求根据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; // 搜索建议操作
/**
* 静态初始化块配置URI匹配规则
* 为不同类型的URI请求配置对应的匹配类型
*/
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 匹配便签表的URI
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 匹配数据表的URI
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 匹配搜索相关的URI
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;
/**
* 初始化内容提供者
* @return 如果初始化成功返回true否则返回false
*/
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* 查询数据
* @param uri 要查询的URI
* @param projection 要返回的列null表示所有列
* @param selection 查询条件null表示无条件
* @param selectionArgs 查询条件参数
* @param sortOrder 排序方式null表示默认排序
* @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;
// 根据URI类型执行不同的查询操作
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);
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);
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) {
if (uri.getPathSegments().size() > 1) {
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:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 设置内容观察者,以便在数据变化时通知相关组件
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
/**
* 插入数据
* @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;
// 根据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)) {
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:
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);
}
/**
* 删除数据
* @param uri 要删除数据的URI
* @param selection 删除条件null表示无条件
* @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;
// 根据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:
// 删除单条便签记录
id = uri.getPathSegments().get(1);
/**
* 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);
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) {
// 如果删除的是数据表记录通知便签URI数据变化
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知当前URI数据变化
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 更新数据
* @param uri 要更新数据的URI
* @param values 更新的数据值
* @param selection 更新条件null表示无条件
* @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;
// 根据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:
// 更新单条便签记录,增加版本号
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) {
// 如果更新的是数据表记录通知便签URI数据变化
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知当前URI数据变化
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 解析查询/更新条件
* 将原始条件转换为SQL语句中的AND条件
* @param selection 原始条件字符串
* @return 格式化后的条件字符串
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 增加便签版本号
* 当便签被修改时,调用此方法增加版本号
* @param id 便签ID如果id>0则更新指定便签否则更新所有匹配条件的便签
* @param selection 更新条件
* @param selectionArgs 更新条件参数
*/
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 ");
// 构造WHERE子句
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类型
* @param uri 要获取MIME类型的URI
* @return MIME类型字符串
*/
@Override
public String getType(Uri uri) {
// TODO: 实现此方法返回合适的MIME类型
return null;
}
}