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.

400 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
* 负责管理便签数据的增删改查操作,对外提供统一的数据访问接口
* 通过UriMatcher匹配不同的URI请求实现数据的分层访问控制
*/
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; // 单条便签URI
private static final int URI_DATA = 3; // 便签数据列表URI
private static final int URI_DATA_ITEM = 4; // 单条便签数据URI
private static final int URI_SEARCH = 5; // 搜索URI
private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议URI
static {
// 初始化UriMatcher
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);
}
/**
* 搜索功能投影定义(用于搜索建议和结果)
* 字段说明:
* - ID: 便签ID
* - SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA: 意图附加数据便签ID
* - SearchManager.SUGGEST_COLUMN_TEXT_1: 搜索建议文本1便签内容去除换行符
* - SearchManager.SUGGEST_COLUMN_TEXT_2: 搜索建议文本2便签内容去除换行符
* - SearchManager.SUGGEST_COLUMN_ICON_1: 搜索建议图标使用R.drawable.search_result
* - SearchManager.SUGGEST_COLUMN_INTENT_ACTION: 意图动作ACTION_VIEW
* - SearchManager.SUGGEST_COLUMN_INTENT_DATA: 意图数据文本便签MIME类型
*/
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
*/
@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;
// 根据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
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
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;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
// 从URI路径获取搜索字符串搜索建议场景
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);
// 执行搜索SQL查询
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;
}
/**
* 处理数据插入请求
* 根据URI匹配结果插入不同类型的数据
* @param uri 插入请求URI
* @param values 要插入的ContentValues
* @return 插入成功的URI包含ID
*/
@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:
// 插入便签数据记录需关联便签ID
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);
}
return ContentUris.withAppendedId(uri, insertedId);
}
/**
* 处理数据删除请求
* 根据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;
// 根据URI匹配结果执行不同的删除逻辑
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过滤系统文件夹
id = uri.getPathSegments().get(1);
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
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;
}
/**
* 处理数据更新请求
* 根据URI匹配结果更新不同类型的数据
* @param uri 更新请求URI
* @param values 要更新的ContentValues
* @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;
// 根据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增加版本号
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
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;
}
/**
* 解析查询条件添加AND连接符
* @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);
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);
}
// 执行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;
}
}