From cfdea719e1c60219093c0d7411221240fb8ba051 Mon Sep 17 00:00:00 2001 From: p3o5nagmu <1329414441@qq.com> Date: Tue, 31 Dec 2024 03:26:52 +0800 Subject: [PATCH] ADD file via upload --- SqlNote.java | 670 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 670 insertions(+) create mode 100644 SqlNote.java diff --git a/SqlNote.java b/SqlNote.java new file mode 100644 index 0000000..f0ebc5a --- /dev/null +++ b/SqlNote.java @@ -0,0 +1,670 @@ +/* + * 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 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(); + } + + // 构造函数,用于从已有的游标(Cursor)数据中加载笔记信息,通常是在查询数据库获取笔记记录后进行初始化操作,设置相关成员变量,并根据笔记类型决定是否加载具体数据内容 + public SqlNote(Context context, Cursor c) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDataList = new ArrayList(); + 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(); + 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)中存在,则获取对应的值,否则将其设为无效ID(INVALID_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)中存在,则获取对应的值, + // 否则将其设为无效的小部件ID(AppWidgetManager.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; +}