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.

432 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.
*/
/**
* 文件: NotesProvider.java
* 描述: 便签应用的内容提供者,负责管理便签数据的访问
* 作用: 为应用程序和其他应用提供便签数据的统一访问接口,实现数据的增删改查操作
*/
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类型 */
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 {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加各种URI匹配规则
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;
/**
* 便签片段搜索查询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 初始化是否成功
*/
@Override
public boolean onCreate() {
// 获取数据库帮助类实例
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* 查询数据
*
* @param uri 请求的URI
* @param projection 要返回的列
* @param selection 查询条件
* @param selectionArgs 查询条件参数
* @param sortOrder 排序方式
* @return 查询结果游标
*/
@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;
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);
}
// 设置通知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);
}
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;
// 根据URI类型执行不同的删除操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 删除便签ID必须大于0系统文件夹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) {
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;
// 根据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) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 解析选择条件
* 将额外的选择条件添加到现有条件中
*
* @param selection 选择条件
* @return 解析后的选择条件
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 增加便签版本号
* 当便签内容更新时,增加版本号以便于同步
*
* @param id 便签ID如果为-1则使用selection和selectionArgs
* @param selection 选择条件
* @param selectionArgs 选择条件参数
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
// 构建更新版本号的SQL语句
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());
}
/**
* 获取内容类型
*
* @param uri 请求的URI
* @return 内容类型
*/
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}