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.
note/SqlNote.java

671 lines
44 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.gtask.data;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import net.micode.notes.tool.ResourceParser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class SqlNote {
// 定义一个用于日志记录的标签,其值为类的简单名称(不含包名部分),方便在查看日志时能快速识别与该类相关的输出信息
private static final String TAG = SqlNote.class.getSimpleName();
// 定义一个表示无效的ID值通常用于初始化成员变量或者在某些逻辑判断中表示不符合要求、尚未正确赋值的ID情况这里设定为 -99999
private static final int INVALID_ID = -99999;
// 定义一个字符串数组用于指定查询笔记信息时从数据库中要获取的列名列表涵盖了笔记的各种属性字段如ID、提醒日期、背景颜色ID等
public static final String[] PROJECTION_NOTE = new String[] {
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE,
NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID,
NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,
NoteColumns.VERSION
};
// 定义一个常量表示在查询结果游标Cursor中笔记ID列对应的索引位置方便后续从游标中准确获取该列数据值为0对应上述PROJECTION_NOTE数组中的顺序
public static final int ID_COLUMN = 0;
// 定义一个常量表示在查询结果游标中提醒日期列对应的索引位置用于从游标获取对应数据值为1
public static final int ALERTED_DATE_COLUMN = 1;
// 定义一个常量表示在查询结果游标中背景颜色ID列对应的索引位置便于提取相应数据值为2
public static final int BG_COLOR_ID_COLUMN = 2;
// 定义一个常量表示在查询结果游标中创建日期列对应的索引位置用于获取创建时间相关数据值为3
public static final int CREATED_DATE_COLUMN = 3;
// 定义一个常量表示在查询结果游标中是否有附件列对应的索引位置用于判断笔记是否包含附件值为4
public static final int HAS_ATTACHMENT_COLUMN = 4;
// 定义一个常量表示在查询结果游标中修改日期列对应的索引位置用于获取笔记最后修改时间信息值为5
public static final int MODIFIED_DATE_COLUMN = 5;
// 定义一个常量表示在查询结果游标中笔记数量列对应的索引位置可能用于表示文件夹下包含笔记的数量等情况值为6
public static final int NOTES_COUNT_COLUMN = 6;
// 定义一个常量表示在查询结果游标中父级ID列对应的索引位置用于确定笔记在层级结构中的位置即父级的标识值为7
public static final int PARENT_ID_COLUMN = 7;
// 定义一个常量表示在查询结果游标中摘要或文本内容片段列对应的索引位置用于获取笔记相关的文本摘要信息值为8
public static final int SNIPPET_COLUMN = 8;
// 定义一个常量表示在查询结果游标中笔记类型列对应的索引位置用于区分不同类型的笔记比如普通笔记、文件夹类型等值为9
public static final int TYPE_COLUMN = 9;
// 定义一个常量表示在查询结果游标中小部件ID列对应的索引位置可能用于关联笔记与桌面小部件相关的操作值为10
public static final int WIDGET_ID_COLUMN = 10;
// 定义一个常量表示在查询结果游标中小部件类型列对应的索引位置用于区分不同样式、功能的小部件值为11
public static final int WIDGET_TYPE_COLUMN = 11;
// 定义一个常量表示在查询结果游标中同步ID列对应的索引位置通常在数据同步相关操作中用于标识值为12
public static final int SYNC_ID_COLUMN = 12;
// 定义一个常量表示在查询结果游标中本地是否修改列对应的索引位置用于判断本地对笔记数据是否有修改值为13
public static final int LOCAL_MODIFIED_COLUMN = 13;
// 定义一个常量表示在查询结果游标中原始父级ID列对应的索引位置可能用于记录笔记在某些操作之前的原始归属情况值为14
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
// 定义一个常量表示在查询结果游标中Google Tasks相关ID列对应的索引位置可能用于和Google Tasks服务进行关联操作值为15
public static final int GTASK_ID_COLUMN = 15;
// 定义一个常量表示在查询结果游标中版本列对应的索引位置用于跟踪笔记数据的版本变化情况值为16
public static final int VERSION_COLUMN = 16;
// 保存传入的上下文对象Context通过它可以访问系统资源、服务等例如获取内容提供器ContentResolver来操作数据库
private Context mContext;
// 用于与安卓的内容提供器进行交互,实现对数据库的查询、插入、更新等操作,比如获取笔记数据或者将笔记数据保存到数据库中
private ContentResolver mContentResolver;
// 一个布尔类型的标志位用于标记当前操作是创建新笔记true还是对已有笔记进行操作false初始化为true表示创建情况
private boolean mIsCreate;
// 用于存储笔记的唯一标识符ID初始化为无效ID值INVALID_ID后续会根据实际情况进行赋值或者更新
private long mId;
// 用于存储笔记的提醒日期以时间戳的形式表示通常是从某个特定时间点开始到提醒时间所经过的毫秒数初始化为0
private long mAlertDate;
// 用于存储笔记的背景颜色ID初始值通过ResourceParser获取默认背景色ID来赋值可能用于设置笔记的显示外观相关功能
private int mBgColorId;
// 用于存储笔记的创建日期以时间戳形式记录创建时刻初始化为当前系统时间调用System.currentTimeMillis()获取)
private long mCreatedDate;
// 用于存储表示笔记是否有附件的标志0表示没有附件初始化为0根据实际情况后续可能会被修改
private int mHasAttachment;
// 用于存储笔记的最后修改日期,同样以时间戳形式表示,初始化为当前系统时间,方便跟踪笔记内容的更新情况
private long mModifiedDate;
// 用于存储笔记的父级ID用于构建笔记的层级结构比如笔记属于哪个文件夹等情况初始化为0
private long mParentId;
// 用于存储笔记的摘要(或文本内容片段),初始化为空字符串,可能是笔记内容的简短描述等,后续可根据实际情况更新
private String mSnippet;
// 用于存储笔记的类型通过Notes.TYPE_NOTE等常量来区分不同类型如普通笔记、文件夹等初始化为普通笔记类型Notes.TYPE_NOTE
private int mType;
// 用于存储笔记关联的小部件ID初始化为AppWidgetManager.INVALID_APPWIDGET_ID表示无效的小部件ID在有实际关联小部件时会被更新
private int mWidgetId;
// 用于存储笔记关联的小部件类型初始化为Notes.TYPE_WIDGET_INVALIDE表示无效的小部件类型根据实际使用情况会改变
private int mWidgetType;
// 用于存储笔记的原始父级ID可能用于记录笔记在某些操作之前的归属情况初始化为0在特定业务场景下会被更新
private long mOriginParent;
// 用于存储笔记的版本号初始化为0用于跟踪笔记数据的版本变化例如每次更新后版本号可能会递增
private long mVersion;
// 用于存放要更新到数据库的笔记相关数据值是一种键值对形式的集合类似Map可以批量设置要更新的字段和对应的值方便数据库更新操作
private ContentValues mDiffNoteValues;
// 定义一个ArrayList用于存储与该笔记相关的具体数据列表每个元素是SqlData类型的对象可能一条笔记对应多条相关的数据记录
private ArrayList<SqlData> mDataList;
// 构造函数,用于创建新笔记时的初始化操作,设置各种成员变量的初始值,例如默认的创建日期、初始的标志位等,为后续创建笔记流程做准备
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = true;
mId = INVALID_ID;
mAlertDate = 0;
mBgColorId = ResourceParser.getDefaultBgId(context);
mCreatedDate = System.currentTimeMillis();
mHasAttachment = 0;
mModifiedDate = System.currentTimeMillis();
mParentId = 0;
mSnippet = "";
mType = Notes.TYPE_NOTE;
mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
mOriginParent = 0;
mVersion = 0;
mDiffNoteValues = new ContentValues();
mDataList = new ArrayList<SqlData>();
}
// 构造函数用于从已有的游标Cursor数据中加载笔记信息通常是在查询数据库获取笔记记录后进行初始化操作设置相关成员变量并根据笔记类型决定是否加载具体数据内容
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(c);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
// 构造函数根据给定的笔记ID从数据库加载笔记信息先通过查询获取游标再调用loadFromCursor方法来加载具体数据到成员变量中同样根据笔记类型决定是否加载相关数据内容
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(id);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
// 根据给定的笔记ID从数据库查询并加载笔记信息到成员变量的私有方法先发起数据库查询获取游标对象若游标不为空则移动到第一条记录并继续调用loadFromCursor(Cursor c)方法进行具体数据加载,同时处理游标为空的情况并记录日志
private void loadFromCursor(long id) {
Cursor c = null;
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
new String[] {
String.valueOf(id)
}, null);
if (c!= null) {
c.moveToNext();
loadFromCursor(c);
} else {
Log.w(TAG, "loadFromCursor: cursor = null");
}
} finally {
if (c!= null)
c.close();
}
}
// 从给定的游标Cursor中加载笔记各字段数据到对应的成员变量的私有方法按照之前定义好的列索引位置如ID_COLUMN等常量从游标中获取相应数据并赋值给成员变量实现数据的提取和初始化
private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);
mParentId = c.getLong(PARENT_ID_COLUMN);
mSnippet = c.getString(SNIPPET_COLUMN);
mType = c.getInt(TYPE_COLUMN);
mWidgetId = c.getInt(WIDGET_ID_COLUMN);
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
mVersion = c.getLong(VERSION_COLUMN);
}
// 加载与该笔记相关的数据内容的私有方法通过向数据库发起查询获取与该笔记ID关联的数据记录通过note_id字段关联若查询到记录则循环创建SqlData对象并添加到mDataList列表中同时处理查询结果为空以及游标关闭等情况并记录日志
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
try {
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[] {
String.valueOf(mId)
}, null);
if (c!= null) {
if (c.getCount() == 0) {
Log.w(TAG, "it seems that the note has not data");
return;
}
while (c.moveToNext()) {
SqlData data = new SqlData(mContext, c);
mDataList.add(data);
}
} else {
Log.w(TAG, "loadDataContent: cursor = null");
}
} finally {
if (c!= null)
c.close();
}
}
// setContent方法用于根据传入的JSONObject对象来设置当前SqlNote对象的各项属性值。
// 它会根据笔记的不同类型系统、文件夹、普通笔记进行相应的属性更新操作若解析JSON出现异常则返回false。
public boolean setContent(JSONObject js) {
try {
// 从传入的JSON对象js中获取名为GTaskStringUtils.META_HEAD_NOTE的子JSON对象
// 这个子对象预期包含了笔记主体相关的属性信息比如类型、ID、摘要等关键内容。
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
// 判断笔记类型是否为系统类型Notes.TYPE_SYSTEM如果是则记录一条警告日志表示不能设置系统文件夹类型的笔记内容然后直接返回不进行后续操作。
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder");
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 如果笔记类型是文件夹类型Notes.TYPE_FOLDER在此分支下只允许更新笔记的摘要snippet和类型type这两个属性。
// 尝试获取笔记摘要字段NoteColumns.SNIPPET的值如果该字段在JSON对象中存在则获取对应的值否则将其设为空字符串。
String snippet = note.has(NoteColumns.SNIPPET)? note.getString(NoteColumns.SNIPPET) : "";
// 判断当前操作是否为创建操作mIsCreate为true或者当前笔记的摘要与传入的摘要不一致。
// 如果满足条件则将新的摘要值放入mDiffNoteValues中这个mDiffNoteValues是用于后续更新数据库操作的键值对集合
// 键为NoteColumns.SNIPPET值为新的摘要内容以此来标记该字段有更新变动需要同步到数据库中。
if (mIsCreate ||!mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
// 更新当前SqlNote对象的摘要字段值使其与传入的摘要内容保持一致便于后续使用该对象时获取到最新的摘要信息。
mSnippet = snippet;
// 尝试获取笔记类型字段NoteColumns.TYPE的值如果该字段在JSON对象中存在则获取对应的值否则将其设为默认的普通笔记类型Notes.TYPE_NOTE
int type = note.has(NoteColumns.TYPE)? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE;
// 判断当前操作是否为创建操作或者当前笔记的类型与传入的类型不一致。
// 如果满足条件则将新的类型值放入mDiffNoteValues中键为NoteColumns.TYPE用于后续数据库更新操作标记类型字段有变化。
if (mIsCreate || mType!= type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
// 更新当前SqlNote对象的类型字段值使其与传入的类型保持一致确保对象状态反映最新的类型设定。
mType = type;
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
// 如果笔记类型是普通笔记类型Notes.TYPE_NOTE则进行一系列详细的属性更新操作。
// 从传入的JSON对象js中获取名为GTaskStringUtils.META_HEAD_DATA的JSON数组
// 该数组预期包含了与该普通笔记相关的多条具体数据内容,比如可能是笔记正文的不同部分等具体信息。
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
// 尝试获取笔记ID字段NoteColumns.ID的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为无效IDINVALID_ID
long id = note.has(NoteColumns.ID)? note.getLong(NoteColumns.ID) : INVALID_ID;
// 判断当前操作是否为创建操作或者当前笔记的ID与传入的ID不一致。
// 如果满足条件则将新的ID值放入mDiffNoteValues中键为NoteColumns.ID以便后续在更新数据库时能将新ID同步过去标记ID字段有变化。
if (mIsCreate || mId!= id) {
mDiffNoteValues.put(NoteColumns.ID, id);
}
// 更新当前SqlNote对象的ID字段值使其与传入的ID保持一致保证对象内记录的笔记ID是最新的。
mId = id;
// 尝试获取笔记提醒日期字段NoteColumns.ALERTED_DATE的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为0。
long alertDate = note.has(NoteColumns.ALERTED_DATE)? note.getLong(NoteColumns.ALERTED_DATE) : 0;
// 判断当前操作是否为创建操作或者当前笔记的提醒日期与传入的提醒日期不一致。
// 如果满足条件则将新的提醒日期值放入mDiffNoteValues中键为NoteColumns.ALERTED_DATE用于后续数据库更新标记提醒日期字段有变化。
if (mIsCreate || mAlertDate!= alertDate) {
mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
}
// 更新当前SqlNote对象的提醒日期字段值使其与传入的提醒日期保持一致确保对象内记录的提醒日期是最新的。
mAlertDate = alertDate;
// 尝试获取笔记背景颜色ID字段NoteColumns.BG_COLOR_ID的值如果该字段在JSON对象note中存在则获取对应的值
// 否则通过ResourceParser获取默认背景色ID作为该字段的值这意味着如果JSON中未指定背景色会使用默认设定。
int bgColorId = note.has(NoteColumns.BG_COLOR_ID)? note.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
// 判断当前操作是否为创建操作或者当前笔记的背景颜色ID与传入的背景颜色ID不一致。
// 如果满足条件则将新的背景颜色ID值放入mDiffNoteValues中键为NoteColumns.BG_COLOR_ID用于后续数据库更新标记背景色字段有变化。
if (mIsCreate || mBgColorId!= bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
// 更新当前SqlNote对象的背景颜色ID字段值使其与传入的背景颜色ID保持一致保证对象内记录的背景色信息是最新的。
mBgColorId = bgColorId;
// 尝试获取笔记创建日期字段NoteColumns.CREATED_DATE的值如果该字段在JSON对象note中存在则获取对应的值
// 否则将其设为当前系统时间通过System.currentTimeMillis()获取这表示如果JSON中未指定创建日期就使用当前时间作为默认值。
long createDate = note.has(NoteColumns.CREATED_DATE)? note.getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
// 判断当前操作是否为创建操作或者当前笔记的创建日期与传入的创建日期不一致。
// 如果满足条件则将新的创建日期值放入mDiffNoteValues中键为NoteColumns.CREATED_DATE用于后续数据库更新标记创建日期字段有变化。
if (mIsCreate || mCreatedDate!= createDate) {
mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
}
// 更新当前SqlNote对象的创建日期字段值使其与传入的创建日期保持一致确保对象内记录的创建时间是最新的。
mCreatedDate = createDate;
// 尝试获取笔记是否有附件字段NoteColumns.HAS_ATTACHMENT的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为0表示无附件
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT)? note.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
// 判断当前操作是否为创建操作或者当前笔记的是否有附件标识与传入的标识不一致。
// 如果满足条件则将新的是否有附件值放入mDiffNoteValues中键为NoteColumns.HAS_ATTACHMENT用于后续数据库更新标记附件标识字段有变化。
if (mIsCreate || mHasAttachment!= hasAttachment) {
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
}
// 更新当前SqlNote对象的是否有附件字段值使其与传入的附件标识保持一致保证对象内记录的附件情况是最新的。
mHasAttachment = hasAttachment;
// 尝试获取笔记修改日期字段NoteColumns.MODIFIED_DATE的值如果该字段在JSON对象note中存在则获取对应的值
// 否则将其设为当前系统时间意味着如果JSON中未指定修改日期就使用当前时间作为默认值方便记录最后修改时间。
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE)? note.getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
// 判断当前操作是否为创建操作或者当前笔记的修改日期与传入的修改日期不一致。
// 如果满足条件则将新的修改日期值放入mDiffNoteValues中键为NoteColumns.MODIFIED_DATE用于后续数据库更新标记修改日期字段有变化。
if (mIsCreate || mModifiedDate!= modifiedDate) {
mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
}
// 更新当前SqlNote对象的修改日期字段值使其与传入的修改日期保持一致确保对象内记录的最后修改时间是最新的。
mModifiedDate = modifiedDate;
// 尝试获取笔记父级ID字段NoteColumns.PARENT_ID的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为0。
long parentId = note.has(NoteColumns.PARENT_ID)? note.getLong(NoteColumns.PARENT_ID) : 0;
// 判断当前操作是否为创建操作或者当前笔记的父级ID与传入的父级ID不一致。
// 如果满足条件则将新的父级ID值放入mDiffNoteValues中键为NoteColumns.PARENT_ID用于后续数据库更新标记父级ID字段有变化。
if (mIsCreate || mParentId!= parentId) {
mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
}
// 更新当前SqlNote对象的父级ID字段值使其与传入的父级ID保持一致保证对象内记录的笔记层级关系是准确的。
mParentId = parentId;
// 再次尝试获取笔记摘要字段NoteColumns.SNIPPET的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为空字符串。
String snippet = note.has(NoteColumns.SNIPPET)? note.getString(NoteColumns.SNIPPET) : "";
// 判断当前操作是否为创建操作或者当前笔记的摘要与传入的摘要不一致。
// 如果满足条件则将新的摘要值放入mDiffNoteValues中键为NoteColumns.SNIPPET用于后续数据库更新标记摘要字段有变化。
if (mIsCreate ||!mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
// 更新当前SqlNote对象的摘要字段值使其与传入的摘要内容保持一致便于后续使用该对象时获取到最新的摘要信息。
mSnippet = snippet;
// 再次尝试获取笔记类型字段NoteColumns.TYPE的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为默认的普通笔记类型Notes.TYPE_NOTE
int type = note.has(NoteColumns.TYPE)? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE;
// 判断当前操作是否为创建操作或者当前笔记的类型与传入的类型不一致。
// 如果满足条件则将新的类型值放入mDiffNoteValues中键为NoteColumns.TYPE用于后续数据库更新标记类型字段有变化。
if (mIsCreate || mType!= type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
// 更新当前SqlNote对象的类型字段值使其与传入的类型保持一致确保对象状态反映最新的类型设定。
mType = type;
// 尝试获取笔记小部件ID字段NoteColumns.WIDGET_ID的值如果该字段在JSON对象note中存在则获取对应的值
// 否则将其设为无效的小部件IDAppWidgetManager.INVALID_APPWIDGET_ID这表示如果JSON中未指定小部件ID就使用默认的无效标识。
int widgetId = note.has(NoteColumns.WIDGET_ID)? note.getInt(NoteColumns.WIDGET_ID) : AppWidgetManager.INVALID_APPWIDGET_ID;
// 判断当前操作是否为创建操作或者当前笔记的小部件ID与传入的小部件ID不一致。
// 如果满足条件则将新的小部件ID值放入mDiffNoteValues中键为NoteColumns.WIDGET_ID用于后续数据库更新标记小部件ID字段有变化。
if (mIsCreate || mWidgetId!= widgetId) {
mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);
}
// 更新当前SqlNote对象的小部件ID字段值使其与传入的小部件ID保持一致保证对象内记录的小部件关联信息是最新的。
mWidgetId = widgetId;
// 尝试获取笔记小部件类型字段NoteColumns.WIDGET_TYPE的值如果该字段在JSON对象note中存在则获取对应的值
// 否则将其设为无效的小部件类型Notes.TYPE_WIDGET_INVALIDE即如果JSON中未指定小部件类型就使用默认的无效类型标识。
int widgetType = note.has(NoteColumns.WIDGET_TYPE)? note.getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;
// 判断当前操作是否为创建操作或者当前笔记的小部件类型与传入的小部件类型不一致。
// 如果满足条件则将新的小部件类型值放入mDiffNoteValues中键为NoteColumns.WIDGET_TYPE用于后续数据库更新标记小部件类型字段有变化。
if (mIsCreate || mWidgetType!= widgetType) {
mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);
}
// 更新当前SqlNote对象的小部件类型字段值使其与传入的小部件类型保持一致确保对象内记录的小部件类型信息是最新的。
mWidgetType = widgetType;
// 尝试获取笔记原始父级ID字段NoteColumns.ORIGIN_PARENT_ID的值如果该字段在JSON对象note中存在则获取对应的值否则将其设为0。
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID)? note.getLong(NoteColumns.ORIGIN_PACTER_ID) : 0;
// 判断当前操作是否为创建操作或者当前笔记的原始父级ID与传入的原始父级ID不一致。
// 如果满足条件则将新的原始父级ID值放入mDiffNoteValues中键为NoteColumns.ORIGIN_PARENT_ID用于后续数据库更新标记原始父级ID字段有变化。
if (mIsCreate || mOriginParent!= originParent) {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
// 更新当前SqlNote对象的原始父级ID字段值使其与传入的原始父级ID保持一致保证对象内记录的原始层级关系信息是准确的。
mOriginParent = originParent;
// 遍历GTaskStringUtils.META_HEAD_DATA数组中的每一个JSON对象即遍历与该普通笔记相关的每一条具体数据内容
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
// 如果当前具体数据对象data中包含DataColumns.ID字段说明该数据有对应的唯一标识。
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
// 在当前SqlNote对象关联的mDataList列表中查找是否已存在具有相同ID的SqlData对象
// mDataList可能存储了与该笔记相关的多条具体数据记录对应的SqlData对象实例。
for (SqlData temp : mDataList) {
if (dataId == temp.getId()) {
sqlData = temp;
}
}
}
// 如果在mDataList中未找到对应的SqlData对象即当前遍历的数据是新的之前不存在关联的SqlData实例
// 则创建一个新的SqlData对象传入当前上下文mContext用于后续操作该数据相关内容。
if (sqlData == null) {
sqlData = new SqlData(mContext);
mDataList.add(sqlData);
}
// 调用找到或新创建的SqlData对象的setContent方法将当前遍历到的具体数据内容data传递进去
sqlData.setContent(data);
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
}
return true;
}
// getContent方法用于将当前SqlNote对象的相关属性及数据内容转换为JSONObject格式返回
// 如果当前对象处于创建状态尚未在数据库中创建完成则记录错误日志并返回null若在构建JSON过程中出现异常也返回null。
public JSONObject getContent() {
try {
// 创建一个新的JSONObject对象用于组装并最终返回包含笔记相关信息的JSON数据结构。
JSONObject js = new JSONObject();
// 判断当前笔记是否处于创建状态mIsCreate为true表示正在创建还未真正在数据库中创建好
// 如果是创建状态则记录一条错误日志表示还未在数据库中创建该笔记然后直接返回null因为此时还没有完整有效的数据可供返回。
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
// 创建一个新的JSONObject对象用于存放笔记主体相关的属性信息后续会根据笔记类型往里面添加不同的字段数据。
JSONObject note = new JSONObject();
// 判断当前笔记类型是否为普通笔记类型Notes.TYPE_NOTE如果是则将笔记的多个属性字段及其对应的值添加到note对象中。
if (mType == Notes.TYPE_NOTE) {
// 将笔记的ID字段NoteColumns.ID及其对应的值mId添加到note对象中这样在生成的JSON数据中就包含了笔记的唯一标识符信息。
note.put(NoteColumns.ID, mId);
// 将笔记的提醒日期字段NoteColumns.ALERTED_DATE及其对应的值mAlertDate添加到note对象中用于表示笔记是否有提醒以及提醒的时间信息以时间戳形式等
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
// 将笔记的背景颜色ID字段NoteColumns.BG_COLOR_ID及其对应的值mBgColorId添加到note对象中方便后续在展示等场景中设置笔记的背景颜色。
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
// 将笔记的创建日期字段NoteColumns.CREATED_DATE及其对应的值mCreatedDate添加到note对象中记录笔记最初创建的时间信息以时间戳形式
note.put(NoteColumns.CREATED_DATE, mCreatedDate);
// 将笔记是否有附件字段NoteColumns.HAS_ATTACHMENT及其对应的值mHasAttachment添加到note对象中用于标识该笔记是否关联了附件比如文件等
note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
// 将笔记的修改日期字段NoteColumns.MODIFIED_DATE及其对应的值mModifiedDate添加到note对象中方便追踪笔记最后一次被修改的时间情况以时间戳形式
note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
// 将笔记的父级ID字段NoteColumns.PARENT_ID及其对应的值mParentId添加到note对象中用于构建笔记的层级结构关系比如属于哪个文件夹等情况。
note.put(NoteColumns.PARENT_ID, mParentId);
// 将笔记的摘要字段NoteColumns.SNIPPET及其对应的值mSnippet添加到note对象中摘要可能是笔记内容的简短描述等方便快速预览笔记大致内容。
note.put(NoteColumns.SNIPPET, mSnippet);
// 将笔记的类型字段NoteColumns.TYPE及其对应的值mType添加到note对象中再次明确笔记的类型虽然前面已经通过条件判断知道是普通笔记类型但这里是完整的数据组装。
note.put(NoteColumns.TYPE, mType);
// 将笔记的小部件ID字段NoteColumns.WIDGET_ID及其对应的值mWidgetId添加到note对象中可能用于关联笔记与桌面小部件等相关的操作和展示情况。
note.put(NoteColumns.WIDGET_ID, mWidgetId);
// 将笔记的小部件类型字段NoteColumns.WIDGET_TYPE及其对应的值mWidgetType添加到note对象中用于区分不同样式、功能的小部件与笔记的关联情况。
note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
// 将笔记的原始父级ID字段NoteColumns.ORIGIN_PARENT_ID及其对应的值mOriginParent添加到note对象中可能用于记录笔记在某些操作之前的原始归属情况等。
note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
// 将包含笔记主体属性信息的note对象以GTaskStringUtils.META_HEAD_NOTE为键添加到外层的js对象中这样最终生成的JSON数据结构就符合特定的格式要求方便后续解析使用。
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
// 创建一个新的JSONArray对象用于存放与该笔记相关的具体数据内容对应的JSON对象这些具体数据可能是笔记正文的不同部分等详细信息。
JSONArray dataArray = new JSONArray();
// 遍历当前SqlNote对象关联的mDataList列表mDataList中存放的是与该笔记相关的SqlData类型对象每个对象代表一部分具体数据内容。
for (SqlData sqlData : mDataList) {
// 调用每个SqlData对象的getContent方法获取其对应的JSON表示形式返回一个JSONObject对象该方法内部会根据SqlData对象自身的属性组装成相应的JSON数据结构。
JSONObject data = sqlData.getContent();
// 判断获取到的JSON对象是否为null如果不为null则表示该SqlData对象成功转换为有效的JSON数据将其添加到dataArray数组中。
if (data!= null) {
dataArray.put(data);
}
}
// 将包含所有具体数据内容JSON对象的dataArray数组以GTaskStringUtils.META_HEAD_DATA为键添加到外层的js对象中完善整个笔记的JSON数据结构使其包含了主体属性和具体数据两部分内容。
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
// 如果笔记类型是文件夹类型Notes.TYPE_FOLDER或者系统类型Notes.TYPE_SYSTEM则只将笔记的部分关键属性添加到note对象中。
// 将笔记的ID字段NoteColumns.ID及其对应的值mId添加到note对象中确保有唯一标识符信息用于区分不同的文件夹或系统对象如果是系统类型可能有特定用途等
note.put(NoteColumns.ID, mId);
// 将笔记的类型字段NoteColumns.TYPE及其对应的值mType添加到note对象中明确其是文件夹还是系统类型方便后续处理逻辑识别。
note.put(NoteColumns.TYPE, mType);
// 将笔记的摘要字段NoteColumns.SNIPPET及其对应的值mSnippet添加到note对象中对于文件夹类型可能摘要就是文件夹名称等方便展示和识别。
note.put(NoteColumns.SNIPPET, mSnippet);
// 将包含关键属性信息的note对象以GTaskStringUtils.META_HEAD_NOTE为键添加到外层的js对象中形成符合特定格式要求的JSON数据结构虽然相较于普通笔记类型数据内容会少一些但满足对应类型的需求。
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
}
// 如果整个JSON数据结构组装成功没有出现异常情况则返回最终组装好的js对象该对象包含了符合要求的笔记相关信息的JSON表示形式。
return js;
} catch (JSONException e) {
// 如果在构建JSON对象如往JSONObject或JSONArray中添加元素等操作过程中出现了JSONException异常记录错误日志打印异常堆栈信息方便排查问题。
Log.e(TAG, e.toString());
e.printStackTrace();
}
// 如果出现异常情况最终返回null表示无法成功获取笔记的JSON内容表示形式。
return null;
}
public void setParentId(long id) {
// 设置笔记的父ID并将其添加到待更新的值中
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
public void setGtaskId(String gid) {
// 设置与Google任务同步的ID并将其添加到待更新的值中
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
public void setSyncId(long syncId) {
// 设置同步ID并将其添加到待更新的值中
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
public void resetLocalModified() {
// 重置本地修改标志
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
public long getId() {
// 获取笔记的ID
return mId;
}
public long getParentId() {
// 获取笔记的父ID
return mParentId;
}
public String getSnippet() {
// 获取笔记的摘要
return mSnippet;
}
public boolean isNoteType() {
// 检查笔记是否为普通笔记类型
return mType == Notes.TYPE_NOTE;
}
public void commit(boolean validateVersion) {
// 提交笔记的更改
if (mIsCreate) {
// 如果是创建笔记
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
// 如果ID无效且待更新值中包含ID则移除它
mDiffNoteValues.remove(NoteColumns.ID);
}
// 插入笔记并获取新插入的笔记ID
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
mId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 处理获取ID时的异常
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
if (mId == 0) {
// 如果ID为0则创建失败
throw new IllegalStateException("Create thread id failed");
}
// 如果是普通笔记类型,则提交相关联的数据
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
}
}
} else {
// 如果是更新笔记
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
// 如果ID无效则抛出异常
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
}
if (mDiffNoteValues.size() > 0) {
// 如果有待更新的值,则增加版本号并执行更新
mVersion ++;
int result = 0;
if (!validateVersion) {
// 不验证版本号的更新
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
// 验证版本号的更新
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
// 如果更新结果为0可能是在同步时用户更新了笔记
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
// 如果是普通笔记类型,则提交相关联的数据
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
}
}
}
// 刷新本地信息
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();
// 清除待更新的值,并标记为非创建状态
mDiffNoteValues.clear();
mIsCreate = false;
}