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.
MiNote/model/Note.java

357 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)
*
* 版权声明本文件由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表中的ID0表示未创建
*/
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表中的ID0表示未创建
private ContentValues mTextDataValues; // 文本数据的差异值(待更新的字段)
private long mCallDataId; // 通话数据在data表中的ID0表示未创建
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;
}
}
}