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.
test/src/Note.java

344 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.

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;
// Note类用于处理笔记相关操作包括创建、修改及与内容提供器交互等功能
public class Note {
// 用于存储笔记发生变化的相关数据,例如修改笔记时变化的数据存于此
private ContentValues mNoteDiffValues;
// NoteData实例用于更细致地管理笔记的数据内容文本、通话等数据
private NoteData mNoteData;
// 用于日志记录时标识当前类,方便调试定位问题
private static final String TAG = "Note";
/**
* 创建一个新的笔记 ID用于向数据库中添加新笔记
*
* @param context 上下文对象用于获取ContentResolver等操作相关资源
* @param folderId 新笔记所属的文件夹ID用于确定笔记在文件夹结构中的位置
* @return 返回新创建笔记的ID如果出现异常可能返回0或者抛出异常
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个准备插入数据库的ContentValues对象用于存放笔记的各项初始数据
ContentValues values = new ContentValues();
// 获取当前系统时间(毫秒数),作为笔记的创建时间
long createdTime = System.currentTimeMillis();
// 设置笔记的创建日期字段
values.put(NoteColumns.CREATED_DATE, createdTime);
// 设置笔记的修改日期字段,初始创建时与创建时间相同
values.put(NoteColumns.MODIFIED_DATE, createdTime);
// 设置笔记的类型为普通笔记类型可能在Notes类中有对应定义
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
// 标记笔记为本地已修改,可能后续同步等操作会用到此标识
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 设置笔记的父文件夹ID
values.put(NoteColumns.PARENT_ID, folderId);
// 通过内容提供器将包含笔记初始数据的ContentValues插入到指定的笔记内容URI对应的数据库表中
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
// 尝试从插入操作返回的Uri中获取路径片段中的第二个元素假设其为笔记ID并转换为长整型
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 如果获取笔记ID出现格式转换异常记录错误日志并将笔记ID设为0
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
if (noteId == -1) {
// 如果获取到的笔记ID为-1抛出非法状态异常表示笔记ID错误
throw new IllegalStateException("Wrong note id:" + noteId);
}
return noteId;
}
// Note类的默认构造函数初始化相关成员变量
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
/**
* 设置笔记的某个通用值(比如标题、描述等普通属性)
*
* @param key 要设置的属性对应的键,例如 "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 文本数据中要设置的属性对应的键,比如文本内容的格式等相关属性键
* @param value 对应属性要设置的值
*/
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
/**
* 设置笔记的文本数据的ID可能用于关联特定的文本数据记录等
*
* @param id 文本数据的ID应为大于0的长整型值
*/
public void setTextDataId(long id) {
mNoteData.setTextDataId(id);
}
/**
* 获取笔记的文本数据的ID
*
* @return 返回笔记文本数据的ID
*/
public long getTextDataId() {
return mNoteData.mTextDataId;
}
/**
* 设置笔记的通话数据的ID用于关联特定通话相关的数据记录等
*
* @param id 通话数据的ID应为大于0的长整型值
*/
public void setCallDataId(long id) {
mNoteData.setCallDataId(id);
}
/**
* 设置笔记的通话数据的某个具体属性值(针对通话相关的特定数据部分)
*
* @param key 通话数据中要设置的属性对应的键,比如通话时长对应的键等
* @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 上下文对象用于获取ContentResolver等操作相关资源
* @param noteId 要同步的笔记的ID必须大于0
* @return 如果同步成功返回true否则返回false
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
// 如果传入的笔记ID不合法小于等于0抛出非法参数异常
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
// 如果笔记没有本地修改直接返回true表示无需同步操作
return true;
}
/**
* 在理论上一旦数据发生变化笔记应该在NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段上更新。
* 为了数据安全,即便更新笔记失败,我们也要更新笔记数据信息。
*/
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
Log.e(TAG, "Update note error, should not happen");
// 即使更新笔记操作失败,也不直接返回,继续往下执行其他可能的数据更新操作
}
mNoteDiffValues.clear();
if (mNoteData.isLocalModified()
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
return false;
}
return true;
}
// Note类的私有内部类用于更细致地管理笔记的数据内容文本、通话等数据
private class NoteData {
// 笔记的文本数据的ID
private long mTextDataId;
// 用于存储笔记文本数据相关的ContentValues对象存放文本数据的具体属性值
private ContentValues mTextDataValues;
// 笔记的通话数据的ID
private long mCallDataId;
// 用于存储笔记通话数据相关的ContentValues对象存放通话数据的具体属性值
private ContentValues mCallDataValues;
// 用于日志记录时标识当前内部类,方便调试定位问题
private static final String TAG = "NoteData";
// NoteData类的默认构造函数初始化相关成员变量
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
/**
* 判断文本数据或通话数据是否有本地修改即对应的ContentValues对象中是否有数据
*
* @return 如果文本数据或通话数据有变化则返回true否则返回false
*/
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
/**
* 设置笔记的文本数据的ID应大于0
*
* @param id 文本数据的ID应为大于0的长整型值
*/
void setTextDataId(long id) {
if (id <= 0) {
// 如果传入的文本数据ID不合法小于等于0抛出非法参数异常
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
/**
* 设置笔记的通话数据的ID应大于0
*
* @param id 通话数据的ID应为大于0的长整型值
*/
void setCallDataId(long id) {
if (id <= 0) {
// 如果传入的通话数据ID不合法小于等于0抛出非法参数异常
throw new IllegalArgumentException("Call data id should larger than 0");
}
mCallDataId = id;
}
/**
* 设置笔记的通话数据的某个具体属性值,并标记笔记整体为本地已修改,更新修改日期
*
* @param key 通话数据中要设置的属性对应的键,比如通话时长对应的键等
* @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 文本数据中要设置的属性对应的键,比如文本内容的格式等相关属性键
* @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 上下文对象用于获取ContentResolver等操作相关资源
* @param noteId 要推送数据的笔记的ID必须大于0
* @return 如果操作成功返回对应的笔记的Uri可能用于后续关联等操作否则返回null
*/
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* 检查传入的笔记ID是否合法若不合法则抛出非法参数异常
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
if (mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
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);
if (mCallDataId == 0) {
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
try {
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
mCallDataValues.clear();
return null;
}
} else {
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, operationList);
return (results == null || results.length == 0 || results[0] == null)? null
: ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
}
}
return null;
}
}
}