diff --git a/TaskList.java b/TaskList.java new file mode 100644 index 0000000..4b76fc3 --- /dev/null +++ b/TaskList.java @@ -0,0 +1,504 @@ +/* + * 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.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +// TaskList类继承自Node类,可理解为用于表示任务列表相关的对象,包含了任务列表自身的一些属性以及操作方法。 +public class TaskList extends Node { + // 定义一个用于日志记录的标签,其值为类的简单名称(不含包名部分),便于在日志中清晰识别与该类相关的输出信息。 + private static final String TAG = TaskList.class.getSimpleName(); + + // 用于记录任务列表的索引位置(可能在某个更大的分组或者排序体系中的序号等),初始值为1,后续可根据实际情况改变。 + private int mIndex; + + // 用于存储该任务列表包含的多个任务对象,以ArrayList形式进行管理,方便进行任务的添加、删除、遍历等操作,初始化为一个空的ArrayList。 + private ArrayList mChildren; + + // 无参构造函数,用于创建TaskList对象时进行默认的初始化操作,先调用父类(Node类)的无参构造函数,然后初始化mChildren为一个新的空ArrayList,并将mIndex设为1。 + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + // getCreateAction方法用于生成一个表示创建任务列表操作的JSONObject对象,该JSON对象包含了创建任务列表所需的关键信息, + // 例如操作类型、任务列表的基本属性等内容,若在构建JSON过程中出现异常,则抛出ActionFailureException异常。 + public JSONObject getCreateAction(int actionId) { + // 创建一个新的JSONObject对象,用于组装并最终返回包含创建任务列表操作相关信息的JSON数据结构。 + JSONObject js = new JSONObject(); + + try { + // 设置JSON对象中的"action_type"字段,表示操作类型,将其值设为GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE, + // 明确该JSON数据代表的是创建任务列表的操作,方便后续解析此JSON的模块知晓操作意图。 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // 设置JSON对象中的"action_id"字段,把传入的actionId参数值赋给该字段,actionId可能用于唯一标识这个创建任务列表操作的序号等,便于区分不同操作实例。 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // 设置JSON对象中的"index"字段,将当前任务列表对象的索引位置(mIndex)赋给该字段,用于表示任务列表在所属体系中的顺序等相关情况。 + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // 创建一个新的JSONObject对象,用于组装任务列表实体相关的详细信息,后续会将其整体放入外层的js对象中,代表要创建的任务列表主体内容。 + JSONObject entity = new JSONObject(); + + // 设置entity JSON对象中的"name"字段,通过调用getName方法(应是在父类Node或者本类中定义的获取名称的方法)获取任务列表名称,并赋给该字段,使创建操作的JSON数据包含名称信息,便于识别。 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + + // 设置entity JSON对象中的"creator_id"字段,暂时将其值设为"null",可能后续需根据实际创建者标识准确赋值,这里先占位表示创建者信息暂未明确。 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + + // 设置entity JSON对象中的"entity_type"字段,将其值设为GTaskStringUtils.GTASK_JSON_TYPE_GROUP, + // 表明这个实体是任务列表类型(分组类型),方便系统区分不同类型的实体。 + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // 将包含任务列表实体详细信息的entity对象,以GTaskStringUtils.GTASK_JSON_ENTITY_DELTA为键,添加到外层的js对象中, + // 这样整个JSON数据结构就完整包含了创建任务列表操作以及要创建的任务列表具体内容等信息。 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + // 如果在构建JSON对象(如往JSONObject添加元素等操作)过程中出现JSONException异常,记录错误日志,打印异常堆栈信息,方便排查问题, + // 同时抛出ActionFailureException异常,表示生成创建任务列表的JSON对象失败,以便上层调用者捕获并处理该异常情况。 + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + // 如果成功构建了包含创建任务列表操作相关信息的JSON对象,无异常情况,则返回该js对象,供后续操作(如发送给服务器等场景)使用。 + return js; + } +} + // getUpdateAction方法用于生成一个表示更新任务列表操作的JSONObject对象,该JSON对象包含了更新任务列表所需的关键信息, +// 例如操作类型、任务列表的唯一标识符以及要更新的任务列表具体属性等内容,若在构建JSON过程中出现异常,则抛出ActionFailureException异常。 +public JSONObject getUpdateAction(int actionId) { + // 创建一个新的JSONObject对象,用于组装并最终返回包含更新任务列表操作相关信息的JSON数据结构。 + JSONObject js = new JSONObject(); + + try { + // 设置JSON对象中的"action_type"字段,表示操作类型,将其值设为GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE, + // 意味着这个JSON数据代表的是更新任务列表的操作,方便后续解析该JSON数据的模块能识别操作意图。 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // 设置JSON对象中的"action_id"字段,将传入的actionId参数值赋给该字段,actionId可能是用于唯一标识这个更新任务列表操作的序号等, + // 在整个任务管理系统中便于区分不同的操作实例。 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // 设置JSON对象中的"id"字段,通过调用getGid方法(该方法应该是在当前类或者父类中定义的用于获取任务列表唯一标识符的方法)获取任务列表的唯一标识符(Gid), + // 并将其赋给该字段,用于明确要更新的是哪个具体任务列表,建立与具体任务列表实体的关联。 + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // 创建一个新的JSONObject对象,用于组装任务列表实体相关的详细信息,这些信息就是此次更新操作中涉及要改变的任务列表属性内容, + // 后续会将其作为整体放入外层的js对象中,代表更新任务列表时具体的变更内容。 + JSONObject entity = new JSONObject(); + + // 设置entity JSON对象中的"name"字段,通过调用getName方法(用于获取任务列表名称的方法)获取任务列表名称, + // 并将其赋给该字段,这样在更新任务列表的JSON数据中包含了可能要更新的任务列表名称信息,方便根据实际需求修改任务列表的显示名称等情况。 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + + // 设置entity JSON对象中的"deleted"字段,通过调用getDeleted方法(用于获取任务列表是否已删除状态的方法)获取任务列表的删除状态, + // 并将其赋给该字段,表明更新任务列表操作也可以涉及任务列表删除状态的变更,比如将未删除的任务列表标记为已删除等情况。 + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + + // 将包含任务列表实体详细信息(要更新的具体属性内容)的entity对象,以GTaskStringUtils.GTASK_JSON_ENTITY_DELTA为键,添加到外层的js对象中, + // 这样整个JSON数据结构就完整地包含了更新任务列表操作以及具体要更新的任务列表内容等信息。 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + // 如果在构建JSON对象(如往JSONObject中添加元素等操作)过程中出现了JSONException异常,记录错误日志,打印异常堆栈信息, + // 方便排查问题,同时抛出ActionFailureException异常,表示生成更新任务列表的JSON对象失败,以便上层调用者能捕获并处理该异常情况。 + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-update jsonobject"); + } + + // 如果成功构建了包含更新任务列表操作相关信息的JSON对象,没有出现异常情况,则返回该js对象,供后续操作(比如发送给服务器等场景)使用。 + return js; +} + +// setContentByRemoteJSON方法用于根据传入的JSONObject对象(通常是从远程获取的包含任务列表相关信息的JSON数据)来设置当前TaskList对象的各项属性值, +// 若传入的JSON对象为null或者在解析JSON过程中出现异常,则抛出ActionFailureException异常。 +public void setContentByRemoteJSON(JSONObject js) { + if (js!= null) { + try { + // 判断传入的JSON对象(js)中是否包含"id"字段,如果包含,则调用setGid方法(用于设置任务列表唯一标识符的方法), + // 并将该字段对应的字符串值(通过getString方法获取)传递进去,实现根据远程JSON数据更新当前任务列表对象的唯一标识符属性。 + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // 判断传入的JSON对象(js)中是否包含"last_modified"字段,如果包含,则调用setLastModified方法(用于设置任务列表最后修改时间的方法), + // 并将该字段对应的长整型值(通过getLong方法获取)传递进去,实现根据远程JSON数据更新当前任务列表对象的最后修改时间属性,用于记录任务列表最新的修改情况。 + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // 判断传入的JSON对象(js)中是否包含"name"字段,如果包含,则调用setName方法(用于设置任务列表名称的方法), + // 并将该字段对应的字符串值(通过getString方法获取)传递进去,实现根据远程JSON数据更新当前任务列表对象的任务列表名称属性,以便任务列表显示正确的名称。 + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + } catch (JSONException e) { + // 如果在解析JSON对象(如获取对应字段的值等操作)过程中出现了JSONException异常,记录错误日志,打印异常堆栈信息, + // 方便排查问题,同时抛出ActionFailureException异常,表示从JSON对象中获取任务列表内容并设置到当前对象失败,以便上层调用者能捕获并处理该异常情况。 + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + } + } +} + + // setContentByLocalJSON方法用于根据本地的JSONObject对象来设置当前TaskList对象的相关内容, +// 如果传入的JSON对象不符合要求(为null或者缺少关键的节点信息),则记录警告日志;若解析JSON出现异常也会记录错误日志。 +public void setContentByLocalJSON(JSONObject js) { + // 判断传入的JSON对象是否为null,或者是否不包含名为GTaskStringUtils.META_HEAD_NOTE的节点, + // 如果满足这些情况之一,则表示该JSON对象不符合处理要求,记录警告日志提示没有可用信息。 + if (js == null ||!js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + // 从传入的JSON对象中获取名为GTaskStringUtils.META_HEAD_NOTE的子JSON对象,该对象预期包含了与文件夹或系统相关的属性信息,比如类型、名称等关键内容。 + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + // 判断获取到的folder对象中表示类型的字段(NoteColumns.TYPE)的值是否等于文件夹类型(Notes.TYPE_FOLDER), + // 如果是文件夹类型,则从folder对象中获取表示摘要(在这里可理解为文件夹名称)的字段(NoteColumns.SNIPPET)对应的字符串值, + // 然后调用setName方法(用于设置任务列表名称的方法),将其设置为特定格式(加上前缀GTaskStringUtils.MIUI_FOLDER_PREFFIX)的名称,完成根据本地JSON数据对任务列表名称的更新。 + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + String name = folder.getString(NoteColumns.SNIPPET); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + // 如果类型是系统类型(Notes.TYPE_SYSTEM),则进一步根据系统文件夹的ID来设置不同的默认名称。 + // 判断folder对象中表示ID的字段(NoteColumns.ID)的值是否等于根文件夹的ID(Notes.ID_ROOT_FOLDER), + // 如果是,则调用setName方法,将任务列表名称设置为特定格式(加上前缀GTaskStringUtils.MIUI_FOLDER_PREFFIX和默认文件夹名称GTaskStringUtils.FOLDER_DEFAULT)的名称。 + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + // 判断folder对象中表示ID的字段(NoteColumns.ID)的值是否等于通话记录文件夹的ID(Notes.ID_CALL_RECORD_FOLDER), + // 如果是,则调用setName方法,将任务列表名称设置为特定格式(加上前缀GTaskStringUtils.MIUI_FOLDER_PREFFIX和通话记录文件夹对应的名称GTaskStringUtils.FOLDER_CALL_NOTE)的名称。 + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE); + else + // 如果不是上述已知的系统文件夹ID,则记录错误日志,表示是无效的系统文件夹,因为无法确定如何正确设置名称了。 + Log.e(TAG, "invalid system folder"); + } else { + // 如果类型既不是文件夹类型也不是系统类型,则记录错误日志,表示出现了错误的类型,不符合预期的处理逻辑。 + Log.e(TAG, "error type"); + } + } catch (JSONException e) { + // 如果在解析JSON对象(如获取子对象、获取字段值等操作)过程中出现了JSONException异常,记录错误日志,打印异常堆栈信息,方便排查问题。 + Log.e(TAG, e.toString()); + e.printStackTrace(); + } +} + +// getLocalJSONFromContent方法用于根据当前TaskList对象的内容生成对应的本地JSONObject表示形式, +// 如果在构建JSON过程中出现异常,则记录错误日志并返回null,主要是按照特定格式组装包含任务列表相关信息的JSON数据。 +public JSONObject getLocalJSONFromContent() { + try { + // 创建一个新的JSONObject对象,用于组装并最终返回包含任务列表相关信息的本地JSON数据结构。 + JSONObject js = new JSONObject(); + // 创建一个新的JSONObject对象,用于存放与任务列表相关的属性信息(这里主要是类型和名称相关内容),后续会将其作为整体放入外层的js对象中。 + JSONObject folder = new JSONObject(); + + // 获取当前任务列表对象的名称,后续会根据名称来判断和设置相应的属性信息到folder对象中。 + String folderName = getName(); + // 判断任务列表名称是否以特定前缀(GTaskStringUtils.MIUI_FOLDER_PREFFIX)开头,如果是,则截取去掉前缀后的部分作为实际的文件夹名称, + // 这样处理可能是为了在生成JSON数据时只保留关键的名称内容,去除特定格式相关的前缀部分。 + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + + // 将处理后的文件夹名称(folderName)添加到folder对象的NoteColumns.SNIPPET字段中,相当于在JSON数据中记录了任务列表对应的名称信息。 + folder.put(NoteColumns.SNIPPET, folderName); + + // 判断处理后的文件夹名称是否等于默认文件夹名称(GTaskStringUtils.FOLDER_DEFAULT)或者通话记录文件夹对应的名称(GTaskStringUtils.FOLDER_CALL_NOTE), + // 如果是,则将folder对象中表示类型的字段(NoteColumns.TYPE)的值设为系统类型(Notes.TYPE_SYSTEM),表明这是系统相关的文件夹; + // 否则将类型字段设为文件夹类型(Notes.TYPE_FOLDER),以此来明确任务列表对应的类型属性并添加到folder对象中。 + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + + // 将包含任务列表相关属性信息的folder对象,以GTaskStringUtils.META_HEAD_NOTE为键,添加到外层的js对象中,形成完整的符合要求的本地JSON数据结构。 + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + // 返回组装好的js对象,即包含了当前任务列表相关信息的本地JSON表示形式,可供在本地存储、传输或者与其他本地模块交互时使用。 + return js; + } catch (JSONException e) { + // 如果在构建JSON对象(如往JSONObject中添加元素等操作)过程中出现了JSONException异常,记录错误日志,打印异常堆栈信息,方便排查问题。 + Log.e(TAG, e.toString()); + e.printStackTrace(); + // 如果出现异常情况,最终返回null,表示无法成功获取任务列表对应的本地JSON内容表示形式。 + return null; + } +} + +// getSyncAction方法用于根据传入的Cursor对象(通常用于从数据库查询结果中获取数据)以及当前TaskList对象的相关状态, +// 来确定在数据同步操作中应该执行的具体动作,返回对应的同步动作标识(SYNC_ACTION_* 系列常量之一),若出现异常则返回SYNC_ACTION_ERROR。 +public int getSyncAction(Cursor c) { + try { + // 判断本地是否没有更新,通过检查Cursor对象中代表本地修改状态的列(SqlNote.LOCAL_MODIFIED_COLUMN指定的列)的值是否为0来确定, + // 如果为0,表示本地没有进行过更新操作。 + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 进一步判断远程和本地是否都没有更新,通过比较Cursor对象中代表同步ID的列(SqlNote.SYNC_ID_COLUMN指定的列)的值, + // 和当前TaskList对象的最后修改时间(通过getLastModified方法获取)是否相等来确定,如果相等,说明两边数据都没有更新, + // 则返回SYNC_ACTION_NONE,表示不需要进行任何同步操作,两边数据是一致的。 + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + return SYNC_ACTION_NONE; + } else { + // 如果同步ID不相等,说明远程有更新,需要将远程数据应用到本地,返回SYNC_ACTION_UPDATE_LOCAL,表示以远程数据更新本地数据。 + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // 如果本地有更新(即本地修改状态列的值不为0),则需要进一步验证任务列表的Gtask ID是否匹配, + // 通过比较Cursor对象中代表Gtask ID的列(SqlNote.GTASK_ID_COLUMN指定的列)获取的字符串值, + // 和当前TaskList对象通过getGid方法获取的Gtask ID是否相等来确定,如果不相等,记录错误日志(因为这可能是比较严重的不一致情况), + // 并返回SYNC_ACTION_ERROR,表示出现了同步错误,两边的关键标识不一致了。 + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + // 再次判断远程和本地的更新情况,通过比较Cursor对象中代表同步ID的列(SqlNote.SYNC_ID_COLUMN指定的列)的值, + // 和当前TaskList对象的最后修改时间(通过getLastModified方法获取)是否相等来确定,如果相等,说明只有本地进行了修改, + // 则返回SYNC_ACTION_UPDATE_REMOTE,表示需要将本地的修改同步到远程,让远程数据也更新为本地修改后的状态。 + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // 对于文件夹类型的冲突情况(根据注释这里针对文件夹做了特殊处理),直接采用本地修改进行同步, + // 也就是返回SYNC_ACTION_UPDATE_REMOTE,表示将本地的修改应用到远程数据上,以本地为准进行同步,可能是基于特定业务逻辑考虑文件夹的同步策略。 + return SYNC_ACTION_UPDATE_REMOTE; + } + } + } catch (Exception e) { + // 如果在整个方法执行过程中出现了任何异常(捕获了Exception类型,涵盖了各种可能的运行时异常等情况),记录错误日志,打印异常堆栈信息,方便排查问题, + // 并返回SYNC_ACTION_ERROR,表示出现了同步相关的错误情况,无法准确判断同步动作。 + Log.e(TAG, "gtask id doesn't match"); + e.printStackTrace(); + } + + // 如果前面执行过程中出现了未处理的情况或者最终正常流程执行完毕但没有返回合适的同步动作标识,默认返回SYNC_ACTION_ERROR,表示出现错误。 + return SYNC_ACTION_ERROR; +} + +// getChildTaskCount方法用于获取当前TaskList对象中包含的子任务(Task类型)的数量, +// 它直接返回存储子任务的ArrayList(mChildren)的大小,即其中包含的元素个数,也就是子任务的数量。 +public int getChildTaskCount() { + return mChildren.size(); +} + + // addChildTask方法(单个参数版本)用于向当前TaskList对象的子任务列表(mChildren)中添加一个Task类型的子任务, +// 如果添加成功,还会设置该子任务的前序兄弟任务(priorSibling)和父任务(parent)属性,返回添加操作是否成功的布尔值。 +public boolean addChildTask(Task task) { + // 初始化一个布尔变量ret,用于记录最终添加子任务操作是否成功,初始值设为false。 + boolean ret = false; + // 首先判断传入的任务对象(task)不为null,并且当前子任务列表(mChildren)中不包含该任务对象(避免重复添加),才进行后续添加操作。 + if (task!= null &&!mChildren.contains(task)) { + // 调用ArrayList的add方法尝试将任务对象添加到子任务列表(mChildren)中,add方法会返回添加操作是否成功的布尔值,将其赋值给ret变量。 + ret = mChildren.add(task); + // 如果添加操作成功(ret为true),则需要设置该子任务的前序兄弟任务(priorSibling)和父任务(parent)属性。 + if (ret) { + // 判断子任务列表是否为空,如果为空,则将该子任务的前序兄弟任务设为null,因为没有其他任务在它之前; + // 如果不为空,则将该子任务的前序兄弟任务设为子任务列表中的最后一个任务(即当前刚添加任务之前的那个任务),通过获取子任务列表最后一个元素的方式来设置。 + task.setPriorSibling(mChildren.isEmpty()? null : mChildren + .get(mChildren.size() - 1)); + // 设置该子任务的父任务为当前TaskList对象,表明该任务属于这个任务列表,建立正确的层级关系。 + task.setParent(this); + } + } + // 返回添加操作是否成功的结果(ret的值),供外部调用者知晓添加子任务的情况。 + return ret; +} + +// addChildTask方法(两个参数版本)用于向当前TaskList对象的子任务列表(mChildren)中指定索引位置添加一个Task类型的子任务, +// 如果索引位置不合法会记录错误日志并返回false,添加成功会更新相关任务的前序兄弟任务关系,返回添加操作是否成功的布尔值(按逻辑这里始终返回true,但可以根据实际情况调整返回值来更准确反馈操作结果)。 +public boolean addChildTask(Task task, int index) { + // 判断传入的索引值(index)是否小于0或者大于子任务列表的当前大小(mChildren.size()),如果是,则表示索引位置不合法,记录错误日志并返回false,表示添加操作失败。 + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "add child task: invalid index"); + return false; + } + + // 查找传入的任务对象(task)在子任务列表(mChildren)中的位置索引,如果不存在则返回 -1,这里获取其位置索引用于后续判断是否重复添加等情况。 + int pos = mChildren.indexOf(task); + // 再次判断任务对象(task)不为null,并且在子任务列表中的位置索引为 -1(即不存在于列表中,避免重复添加),才进行后续添加操作。 + if (task!= null && pos == -1) { + // 使用ArrayList的add方法,在指定的索引位置(index)添加任务对象,将任务插入到子任务列表中相应位置。 + mChildren.add(index, task); + + // 更新任务列表中相关任务的前序兄弟任务关系,以下操作是为了维护任务之间正确的顺序关系。 + + // 初始化前序任务(preTask)和后续任务(afterTask)变量,分别用于存储插入位置之前和之后的任务对象,初始值设为null,后续根据实际情况赋值。 + Task preTask = null; + Task afterTask = null; + // 如果插入的索引位置不是0(即不是在列表开头插入),则获取插入位置前一个位置的任务对象,赋值给preTask,作为新插入任务的前序兄弟任务。 + if (index!= 0) + preTask = mChildren.get(index - 1); + // 如果插入的索引位置不是子任务列表的最后一个位置(即不是在列表末尾插入),则获取插入位置后一个位置的任务对象,赋值给afterTask,后续用于更新其前序兄弟任务关系。 + if (index!= mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + // 设置新插入任务(task)的前序兄弟任务为preTask,建立正确的顺序关系。 + task.setPriorSibling(preTask); + // 如果afterTask不为null(即存在后续任务),则设置该后续任务(afterTask)的前序兄弟任务为新插入的任务(task),更新顺序关系,确保任务之间的先后顺序正确。 + if (afterTask!= null) + afterTask.setPriorSibling(task); + } + + // 按当前逻辑,只要执行到这里就认为添加操作是进行了的(即使可能有一些逻辑上的异常情况未完全考虑周全),所以返回true,表示添加子任务操作成功, + // 也可以根据更严格的业务逻辑需求,进一步完善这里的返回值判断,使其更准确地反馈添加操作的实际结果。 + return true; +} + +// removeChildTask方法用于从当前TaskList对象的子任务列表(mChildren)中移除指定的Task类型子任务, +// 如果移除成功,会重置该子任务的前序兄弟任务和父任务属性,同时更新子任务列表中相关任务的前序兄弟任务关系,返回移除操作是否成功的布尔值。 +public boolean removeChildTask(Task task) { + // 初始化一个布尔变量ret,用于记录最终移除子任务操作是否成功,初始值设为false。 + boolean ret = false; + // 获取要移除的任务对象(task)在子任务列表(mChildren)中的索引位置,如果不存在则返回 -1,后续根据该索引进行相关操作判断。 + int index = mChildren.indexOf(task); + // 如果任务对象在子任务列表中的索引位置不为 -1(即存在于列表中),则进行移除操作。 + if (index!= -1) { + // 调用ArrayList的remove方法尝试移除任务对象,remove方法会返回移除操作是否成功的布尔值,将其赋值给ret变量。 + ret = mChildren.remove(task); + + // 如果移除操作成功(ret为true),则需要重置该子任务的前序兄弟任务和父任务属性,以及更新子任务列表中相关任务的前序兄弟任务关系。 + if (ret) { + // 将该子任务的前序兄弟任务设为null,因为它已经从任务列表中移除了,不再有前序兄弟任务的关联。 + task.setPriorSibling(null); + // 将该子任务的父任务设为null,表明它不再属于当前的任务列表,解除层级关系。 + task.setParent(null); + + // 更新子任务列表中相关任务的前序兄弟任务关系,以下操作是为了维护剩余任务之间正确的顺序关系。 + + // 判断移除的任务所在索引位置(index)是否不等于子任务列表的当前大小(即不是移除的最后一个任务),如果是,则需要更新后续任务的前序兄弟任务关系。 + if (index!= mChildren.size()) { + // 获取移除任务所在索引位置对应的任务对象(即后续任务),并设置其前序兄弟任务为移除任务之前的那个任务(如果移除的是第一个任务则设为null), + // 通过获取相应索引位置的任务对象来更新其前序兄弟任务属性,确保任务之间的先后顺序正确。 + mChildren.get(index).setPriorSibling( + index == 0? null : mChildren.get(index - 1)); + } + } + } + // 返回移除操作是否成功的结果(ret的值),供外部调用者知晓移除子任务的情况。 + return ret; +} + +// moveChildTask方法用于在当前TaskList对象的子任务列表(mChildren)中移动指定的Task类型子任务到新的索引位置, +// 如果索引位置不合法或者任务不在列表中会记录错误日志并返回false,移动成功则返回true,内部通过先移除再添加的方式实现移动操作。 +public boolean moveChildTask(Task task, int index) { + // 判断传入的新索引位置(index)是否小于0或者大于等于子任务列表的当前大小(mChildren.size()),如果是,则表示索引位置不合法,记录错误日志并返回false,表示移动操作失败。 + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "move child task: invalid index"); + return false; + } + + // 获取要移动的任务对象(task)在子任务列表(mChildren)中的当前索引位置,如果不存在则返回 -1,后续根据该索引进行相关操作判断。 + int pos = mChildren.indexOf(task); + // 如果任务对象在子任务列表中的索引位置为 -1(即任务不在列表中),记录错误日志并返回false,表示移动操作失败,因为无法移动一个不存在于列表中的任务。 + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list"); + return false; + } + + // 判断任务对象当前的索引位置(pos)是否与传入的新索引位置(index)相等,如果相等,说明任务已经在目标位置了,不需要进行移动操作,直接返回true,表示移动操作成功(其实就是无需操作的情况)。 + if (pos == index) + return true; + + // 通过先调用removeChildTask方法移除任务对象,再调用addChildTask方法(两个参数版本)将任务对象添加到新的索引位置的方式,来实现移动任务的操作, + // 如果移除和添加操作都成功,则整体移动操作成功,返回true;如果有任何一个操作失败,则返回false,表示移动操作失败。 + return (removeChildTask(task) && addChildTask(task, index)); +} + +// findChildTaskByGid方法用于在当前TaskList对象的子任务列表(mChildren)中根据任务的唯一标识符(Gid)查找对应的Task类型子任务, +// 如果找到则返回该任务对象,没找到则返回null,通过遍历子任务列表进行查找匹配。 +public Task findChildTaskByGid(String gid) { + // 遍历子任务列表(mChildren),从索引0开始,到列表大小结束(不包含列表大小对应的索引,即遍历所有元素)。 + for (int i = 0; i < mChildren.size(); i++) { + // 获取当前遍历到的任务对象。 + Task t = mChildren.get(i); + // 判断当前任务对象的唯一标识符(通过getGid方法获取)是否与传入的Gid参数相等,如果相等,则找到了对应的任务对象,返回该任务对象。 + if (t.getGid().equals(gid)) { + return t; + } + } + // 如果遍历完整个子任务列表都没有找到匹配的任务对象,则返回null,表示未找到。 + return null; +} + +// getChildTaskIndex方法用于获取指定Task类型子任务在当前TaskList对象的子任务列表(mChildren)中的索引位置, +// 通过调用ArrayList的indexOf方法来查找并返回对应的索引值,如果任务不在列表中则返回 -1。 +public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); +} + +// getChildTaskByIndex方法用于根据索引位置从当前TaskList对象的子任务列表(mChildren)中获取对应的Task类型子任务, +// 如果索引位置不合法会记录错误日志并返回null,合法则返回对应索引位置的任务对象,通过直接从列表中获取元素的方式来获取任务对象。 +public Task getChildTaskByIndex(int index) { + // 判断传入的索引值(index)是否小于0或者大于等于子任务列表的当前大小(mChildren.size()),如果是,则表示索引位置不合法,记录错误日志并返回null,表示无法获取任务对象。 + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + // 如果索引位置合法,则直接从子任务列表中获取对应索引位置的任务对象,并返回该任务对象。 + return mChildren.get(index); +} + +// getChilTaskByGid方法同样用于在当前TaskList对象的子任务列表(mChildren)中根据任务的唯一标识符(Gid)查找对应的Task类型子任务, +// 不过这里是通过增强for循环来遍历列表进行查找匹配,找到则返回任务对象,没找到则返回null,功能和findChildTaskByGid方法类似,但遍历方式不同。 +public Task getChilTaskByGid(String gid) { + // 使用增强for循环遍历子任务列表(mChildren),依次获取每个任务对象进行判断。 + for (Task task : mChildren) { + // 判断当前遍历到的任务对象的唯一标识符(通过getGid方法获取)是否与传入的Gid参数相等,如果相等,则找到了对应的任务对象,返回该任务对象。 + if (task.getGid().equals(gid)) + return task; + } + // 如果遍历完整个子任务列表都没有找到匹配的任务对象,则返回null,表示未找到。 + return null; +} + +// getChildTaskList方法用于获取当前TaskList对象的子任务列表(mChildren),直接返回存储子任务的ArrayList对象, +// 外部调用者可以通过获取到的列表进行进一步的操作,比如遍历、修改等(但需遵循相应的规则和逻辑)。 +public ArrayList getChildTaskList() { + return this.mChildren; +} + +// setIndex方法用于设置当前TaskList对象的索引属性(mIndex),将传入的索引值赋给该属性,方便更新任务列表在所属体系中的顺序等相关情况。 +public void setIndex(int index) { + this.mIndex = index; +} + +// getIndex方法用于获取当前TaskList对象的索引属性(mIndex)的值,直接返回该属性的值,外部调用者可以获取该索引值来了解任务列表的相关顺序信息等。 +public int getIndex() { + return this.mIndex; +}