|
|
|
|
|
/*
|
|
|
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
|
|
*
|
|
|
* 版权声明:本文件由MiCode开源社区开发,遵循Apache License, Version 2.0协议;
|
|
|
* 您仅在遵守协议的前提下使用本文件,完整协议可通过以下链接获取:
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
* 注:未书面明确要求时,本软件按"原样"提供,不附带任何明示或暗示的保证。
|
|
|
*/
|
|
|
|
|
|
package net.micode.notes.model;
|
|
|
|
|
|
import android.content.ContentProviderOperation;
|
|
|
import android.content.ContentProviderResult;
|
|
|
import android.content.ContentUris;
|
|
|
import android.content.ContentValues;
|
|
|
import android.content.Context;
|
|
|
import android.content.OperationApplicationException;
|
|
|
import android.net.Uri;
|
|
|
import android.os.RemoteException;
|
|
|
import android.util.Log;
|
|
|
|
|
|
import net.micode.notes.data.Notes;
|
|
|
import net.micode.notes.data.Notes.CallNote;
|
|
|
import net.micode.notes.data.Notes.DataColumns;
|
|
|
import net.micode.notes.data.Notes.NoteColumns;
|
|
|
import net.micode.notes.data.Notes.TextNote;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
/**
|
|
|
* 笔记模型类
|
|
|
* 负责管理笔记的创建、数据修改(文本/通话记录)以及与数据库的同步操作
|
|
|
*/
|
|
|
public class Note {
|
|
|
// 存储笔记主表(note表)的差异值(待更新的字段)
|
|
|
private ContentValues mNoteDiffValues;
|
|
|
// 存储笔记关联数据(文本/通话记录等子表数据)的管理类实例
|
|
|
private NoteData mNoteData;
|
|
|
private static final String TAG = "Note";
|
|
|
|
|
|
/**
|
|
|
* 静态方法:生成新笔记ID并插入数据库
|
|
|
* @param context 上下文环境
|
|
|
* @param folderId 笔记所属文件夹ID
|
|
|
* @return 新笔记的数据库ID(唯一标识)
|
|
|
*/
|
|
|
public static synchronized long getNewNoteId(Context context, long folderId) {
|
|
|
// 创建新笔记的初始值:创建时间、修改时间、类型、本地修改标志、父文件夹ID
|
|
|
ContentValues values = new ContentValues();
|
|
|
long createdTime = System.currentTimeMillis();
|
|
|
values.put(NoteColumns.CREATED_DATE, createdTime); // 设置创建时间
|
|
|
values.put(NoteColumns.MODIFIED_DATE, createdTime); // 初始修改时间与创建时间相同
|
|
|
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); // 笔记类型为普通笔记
|
|
|
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地已修改(待同步)
|
|
|
values.put(NoteColumns.PARENT_ID, folderId); // 所属文件夹ID
|
|
|
|
|
|
// 插入到note表,获取返回的Uri(包含新笔记ID)
|
|
|
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
|
|
|
|
|
|
long noteId = 0;
|
|
|
try {
|
|
|
// 从Uri路径中解析笔记ID(格式:content://.../notes/[noteId])
|
|
|
noteId = Long.valueOf(uri.getPathSegments().get(1));
|
|
|
} catch (NumberFormatException e) {
|
|
|
Log.e(TAG, "获取笔记ID失败 :" + e.toString());
|
|
|
noteId = 0;
|
|
|
}
|
|
|
if (noteId == -1) {
|
|
|
throw new IllegalStateException("错误的笔记ID:" + noteId);
|
|
|
}
|
|
|
return noteId;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 构造方法:初始化笔记差异值和数据管理类
|
|
|
*/
|
|
|
public Note() {
|
|
|
mNoteDiffValues = new ContentValues(); // 初始化主表差异值容器
|
|
|
mNoteData = new NoteData(); // 初始化子表数据管理实例
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置笔记主表的属性值(如标题、提醒时间等)
|
|
|
* 每次调用会自动更新本地修改标志和修改时间
|
|
|
* @param key 笔记主表字段名(如NoteColumns.TITLE)
|
|
|
* @param value 字段对应的值
|
|
|
*/
|
|
|
public void setNoteValue(String key, String value) {
|
|
|
mNoteDiffValues.put(key, value);
|
|
|
// 标记本地修改(用于同步判断)
|
|
|
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
// 更新修改时间为当前系统时间
|
|
|
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置文本类型子表数据(如笔记内容)
|
|
|
* @param key 文本数据字段名(如TextNote.CONTENT)
|
|
|
* @param value 字段对应的值
|
|
|
*/
|
|
|
public void setTextData(String key, String value) {
|
|
|
mNoteData.setTextData(key, value);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置文本子表的ID(用于更新已有文本数据)
|
|
|
* @param id 文本数据在data表中的ID(需大于0)
|
|
|
*/
|
|
|
public void setTextDataId(long id) {
|
|
|
mNoteData.setTextDataId(id);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取当前文本子表的ID
|
|
|
* @return 文本数据在data表中的ID(0表示未创建)
|
|
|
*/
|
|
|
public long getTextDataId() {
|
|
|
return mNoteData.mTextDataId;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置通话记录子表的ID(用于更新已有通话数据)
|
|
|
* @param id 通话数据在data表中的ID(需大于0)
|
|
|
*/
|
|
|
public void setCallDataId(long id) {
|
|
|
mNoteData.setCallDataId(id);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置通话记录类型子表数据(如通话号码、时长等)
|
|
|
* @param key 通话数据字段名(如CallNote.NUMBER)
|
|
|
* @param value 字段对应的值
|
|
|
*/
|
|
|
public void setCallData(String key, String value) {
|
|
|
mNoteData.setCallData(key, value);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 判断笔记是否有本地修改(主表或子表数据被修改)
|
|
|
* @return true-有修改;false-无修改
|
|
|
*/
|
|
|
public boolean isLocalModified() {
|
|
|
// 主表差异值非空 或 子表数据有修改时,标记为有本地修改
|
|
|
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将笔记的修改同步到数据库(主表+子表)
|
|
|
* @param context 上下文环境
|
|
|
* @param noteId 待同步的笔记ID
|
|
|
* @return 同步成功-true;失败-false
|
|
|
*/
|
|
|
public boolean syncNote(Context context, long noteId) {
|
|
|
if (noteId <= 0) {
|
|
|
throw new IllegalArgumentException("错误的笔记ID:" + noteId);
|
|
|
}
|
|
|
|
|
|
// 无修改时直接返回成功
|
|
|
if (!isLocalModified()) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 理论上,数据修改时应更新note表的LOCAL_MODIFIED和MODIFIED_DATE字段。
|
|
|
* 为数据安全,即使note表更新失败,仍尝试同步子表数据
|
|
|
*/
|
|
|
// 更新note表的差异值(如标题、修改时间等)
|
|
|
int updateCount = context.getContentResolver().update(
|
|
|
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), // 目标笔记Uri
|
|
|
mNoteDiffValues, // 待更新的字段
|
|
|
null,
|
|
|
null
|
|
|
);
|
|
|
if (updateCount == 0) {
|
|
|
Log.e(TAG, "更新笔记主表失败(不应发生)");
|
|
|
// 不返回,继续处理子表同步
|
|
|
}
|
|
|
mNoteDiffValues.clear(); // 清空已处理的差异值
|
|
|
|
|
|
// 子表数据有修改时,同步到data表
|
|
|
if (mNoteData.isLocalModified()
|
|
|
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
|
|
|
return false; // 子表同步失败时返回false
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 内部类:管理笔记的子表数据(文本/通话记录)
|
|
|
*/
|
|
|
private class NoteData {
|
|
|
private long mTextDataId; // 文本数据在data表中的ID(0表示未创建)
|
|
|
private ContentValues mTextDataValues; // 文本数据的差异值(待更新的字段)
|
|
|
private long mCallDataId; // 通话数据在data表中的ID(0表示未创建)
|
|
|
private ContentValues mCallDataValues; // 通话数据的差异值(待更新的字段)
|
|
|
private static final String TAG = "NoteData";
|
|
|
|
|
|
/**
|
|
|
* 构造方法:初始化子表数据容器
|
|
|
*/
|
|
|
public NoteData() {
|
|
|
mTextDataValues = new ContentValues();
|
|
|
mCallDataValues = new ContentValues();
|
|
|
mTextDataId = 0;
|
|
|
mCallDataId = 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 判断子表数据是否有本地修改
|
|
|
* @return true-有修改;false-无修改
|
|
|
*/
|
|
|
boolean isLocalModified() {
|
|
|
// 文本或通话数据差异值非空时,标记为有修改
|
|
|
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置文本数据ID(校验ID有效性)
|
|
|
* @param id 文本数据在data表中的ID(需>0)
|
|
|
*/
|
|
|
void setTextDataId(long id) {
|
|
|
if (id <= 0) {
|
|
|
throw new IllegalArgumentException("文本数据ID必须大于0");
|
|
|
}
|
|
|
mTextDataId = id;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置通话数据ID(校验ID有效性)
|
|
|
* @param id 通话数据在data表中的ID(需>0)
|
|
|
*/
|
|
|
void setCallDataId(long id) {
|
|
|
if (id <= 0) {
|
|
|
throw new IllegalArgumentException("通话数据ID必须大于0");
|
|
|
}
|
|
|
mCallDataId = id;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置通话数据字段值(自动触发笔记修改标志)
|
|
|
* @param key 通话数据字段名(如CallNote.DURATION)
|
|
|
* @param value 字段值
|
|
|
*/
|
|
|
void setCallData(String key, String value) {
|
|
|
mCallDataValues.put(key, value);
|
|
|
// 同步更新主表的本地修改标志和修改时间
|
|
|
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置文本数据字段值(自动触发笔记修改标志)
|
|
|
* @param key 文本数据字段名(如TextNote.CONTENT)
|
|
|
* @param value 字段值
|
|
|
*/
|
|
|
void setTextData(String key, String value) {
|
|
|
mTextDataValues.put(key, value);
|
|
|
// 同步更新主表的本地修改标志和修改时间
|
|
|
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将子表数据(文本/通话)插入或更新到数据库
|
|
|
* @param context 上下文环境
|
|
|
* @param noteId 关联的笔记ID
|
|
|
* @return 操作成功返回笔记Uri;失败返回null
|
|
|
*/
|
|
|
Uri pushIntoContentResolver(Context context, long noteId) {
|
|
|
// 校验笔记ID有效性
|
|
|
if (noteId <= 0) {
|
|
|
throw new IllegalArgumentException("错误的笔记ID:" + noteId);
|
|
|
}
|
|
|
|
|
|
// 批量操作容器(用于同时执行多个ContentProvider操作)
|
|
|
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
|
|
|
ContentProviderOperation.Builder builder = null;
|
|
|
|
|
|
// 处理文本数据
|
|
|
if (mTextDataValues.size() > 0) {
|
|
|
mTextDataValues.put(DataColumns.NOTE_ID, noteId); // 关联笔记ID
|
|
|
|
|
|
if (mTextDataId == 0) { // ID为0表示新增文本数据
|
|
|
// 设置MIME类型为文本笔记
|
|
|
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
|
|
|
// 插入到data表,获取新数据的Uri
|
|
|
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues);
|
|
|
try {
|
|
|
// 解析新数据的ID并保存
|
|
|
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
|
|
|
} catch (NumberFormatException e) {
|
|
|
Log.e(TAG, "插入新文本数据失败(笔记ID:" + noteId + ")");
|
|
|
mTextDataValues.clear(); // 清空无效数据
|
|
|
return null;
|
|
|
}
|
|
|
} else { // ID非0表示更新已有文本数据
|
|
|
// 构建更新操作(指定data表Uri和待更新值)
|
|
|
builder = ContentProviderOperation.newUpdate(
|
|
|
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mTextDataId));
|
|
|
builder.withValues(mTextDataValues);
|
|
|
operationList.add(builder.build()); // 添加到批量操作列表
|
|
|
}
|
|
|
mTextDataValues.clear(); // 清空已处理的差异值
|
|
|
}
|
|
|
|
|
|
// 处理通话数据(逻辑与文本数据类似)
|
|
|
if (mCallDataValues.size() > 0) {
|
|
|
mCallDataValues.put(DataColumns.NOTE_ID, noteId); // 关联笔记ID
|
|
|
|
|
|
if (mCallDataId == 0) { // ID为0表示新增通话数据
|
|
|
// 设置MIME类型为通话记录
|
|
|
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
|
|
|
// 插入到data表,获取新数据的Uri
|
|
|
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues);
|
|
|
try {
|
|
|
// 解析新数据的ID并保存
|
|
|
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
|
|
|
} catch (NumberFormatException e) {
|
|
|
Log.e(TAG, "插入新通话数据失败(笔记ID:" + noteId + ")");
|
|
|
mCallDataValues.clear(); // 清空无效数据
|
|
|
return null;
|
|
|
}
|
|
|
} else { // ID非0表示更新已有通话数据
|
|
|
// 构建更新操作(指定data表Uri和待更新值)
|
|
|
builder = ContentProviderOperation.newUpdate(
|
|
|
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, mCallDataId));
|
|
|
builder.withValues(mCallDataValues);
|
|
|
operationList.add(builder.build()); // 添加到批量操作列表
|
|
|
}
|
|
|
mCallDataValues.clear(); // 清空已处理的差异值
|
|
|
}
|
|
|
|
|
|
// 执行批量操作(仅当有操作需要执行时)
|
|
|
if (operationList.size() > 0) {
|
|
|
try {
|
|
|
ContentProviderResult[] results = context.getContentResolver().applyBatch(
|
|
|
Notes.AUTHORITY, // 内容提供者的AUTHORITY
|
|
|
operationList // 批量操作列表
|
|
|
);
|
|
|
// 操作成功时返回笔记Uri;否则返回null
|
|
|
return (results == null || results.length == 0 || results[0] == null)
|
|
|
? null
|
|
|
: ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
|
|
|
} catch (RemoteException e) {
|
|
|
Log.e(TAG, "远程调用异常: " + e.toString() + " - " + e.getMessage());
|
|
|
return null;
|
|
|
} catch (OperationApplicationException e) {
|
|
|
Log.e(TAG, "操作应用异常: " + e.toString() + " - " + e.getMessage());
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
} |