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.
XiaoMiNotes/src/data/NotesProvider.java

370 lines
15 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
*
* 功能为便签应用提供数据访问接口通过ContentProvider机制实现数据共享
* 继承自ContentProvider实现对note表和data表的CRUD操作并支持搜索功能
*/
public class NotesProvider extends ContentProvider {
private static final UriMatcher mMatcher; // URI匹配器用于匹配不同的URI请求
private NotesDatabaseHelper mHelper; // 数据库帮助类实例
private static final String TAG = "NotesProvider"; // 日志标签
// URI匹配常量定义
private static final int URI_NOTE = 1; // 匹配note表所有记录
private static final int URI_NOTE_ITEM = 2; // 匹配note表单条记录
private static final int URI_DATA = 3; // 匹配data表所有记录
private static final int URI_DATA_ITEM = 4; // 匹配data表单条记录
private static final int URI_SEARCH = 5; // 匹配搜索请求
private static final int URI_SEARCH_SUGGEST = 6; // 匹配搜索建议请求
// 静态初始化块初始化URI匹配器添加各种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'和空格以显示更多信息
*/
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; // 只搜索普通便签类型
/**
* ContentProvider创建时调用初始化数据库帮助类
* @return true表示初始化成功
*/
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* 查询方法根据URI匹配不同的查询逻辑
* @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: // 查询note表所有记录
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM: // 查询note表单条记录
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: // 查询data表所有记录
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_DATA_ITEM: // 查询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: // 处理搜索和搜索建议请求
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"); // 从查询参数获取搜索模式
}
if (TextUtils.isEmpty(searchString)) {
return null; // 搜索字符串为空则返回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);
}
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;
switch (mMatcher.match(uri)) {
case URI_NOTE: // 插入note表记录
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA: // 插入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);
}
// 通知note URI的数据变化
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 通知data 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 删除条件
* @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; // 标记是否删除data表数据
switch (mMatcher.match(uri)) {
case URI_NOTE: // 删除note表记录系统文件夹不允许删除
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM: // 删除note表单条记录
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: // 删除data表记录
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case URI_DATA_ITEM: // 删除data表单条记录
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) {
// 删除data表数据时需要通知note URI
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 更新数据方法
* @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; // 标记是否更新data表数据
switch (mMatcher.match(uri)) {
case URI_NOTE: // 更新note表记录
increaseNoteVersion(-1, selection, selectionArgs); // 增加版本号
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM: // 更新note表单条记录
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: // 更新data表记录
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM: // 更新data表单条记录
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) {
// 更新data表数据时需要通知note URI
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 解析选择条件将传入的条件转换为AND连接的条件
* @param selection 原始选择条件
* @return 解析后的选择条件字符串
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 增加便签版本号
* @param id 便签ID大于0或-1表示更新所有匹配的记录
* @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 "); // 版本号加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 请求的URI
* @return MIME类型字符串
*/
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}