diff --git a/src/gtask/data/MetaData.java b/src/gtask/data/MetaData.java new file mode 100644 index 0000000..cbb6900 --- /dev/null +++ b/src/gtask/data/MetaData.java @@ -0,0 +1,84 @@ +/* + * 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;//声名一个包,包名包含多个层次,每个层次用'.'分割,这是data包,用倒置的域名开头 + +import android.database.Cursor;//引用android.database.Cursor类,基于数据库服务的类 +import android.util.Log;//日志工具类,常用的方法有:Log.v() Log.d() Log.i() Log.w() Log.e() + +import net.micode.notes.tool.GTaskStringUtils;//引用tool包中的GTaskStringUtils工具类。 + +import org.json.JSONException;//引用JSON使用失败异常处理类 +import org.json.JSONObject;//引用JSON对象库类,JsonObject是一种数据格式。其格式为{"key1":value1,"key2",value2....};key必须是字符串。 + + +public class MetaData extends Task {//创建一个继承于Task的类MetaData,主要用于记录数据的变化 + private final static String TAG = MetaData.class.getSimpleName();//调用getSimpleName()函数,得到类的简写名称存入字符串TAG中 + + private String mRelatedGid = null;//创建私有变量mRelatedGid,并初始化为null。 + + public void setMeta(String gid, JSONObject metaInfo) {//调用JSONObject库函数put (),Task类中的setNotes ()和setName ()函数,实现设置数据,即生成元数据库 + try {//执行 + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);//将这对键值放入metaInfo这个jsonobject对象中 + } catch (JSONException e) {//捕捉异常并进行异常处理放入TAG + Log.e(TAG, "failed to put related gid");//输出错误信息,TAG为类名,即该类无法放入相关联gid信息 + } + setNotes(metaInfo.toString());//设置便签,将json类的metaInfo转为string + setName(GTaskStringUtils.META_NOTE_NAME);//设置GTask的名字 + } + + public String getRelatedGid() { + return mRelatedGid;//获取相关联的Gid + } + + @Override// @Override 注解是用来指定方法重写的 + public boolean isWorthSaving() { + return getNotes() != null; + }//如果getNotes不等于null就返回真,如果便签内容非空,getNotes就不为null + + @Override//表示重写 + public void setContentByRemoteJSON(JSONObject js) {//调用父类Task中的setContentByRemoteJSON ()函数,使用远程json数据对象设置元数据内容 + super.setContentByRemoteJSON(js);//调用函数 + if (getNotes() != null) {//如果数据非空 + try {//执行 + JSONObject metaInfo = new JSONObject(getNotes().trim());//创建新json对象,调用trim方法去掉首尾空格 + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);//获取关联gid,且为字符串类型 + } catch (JSONException e) {//进行异常处理 + Log.w(TAG, "failed to get related gid");//输出警告信息 + mRelatedGid = null; + } + } + } + + @Override//表示重写 + public void setContentByLocalJSON(JSONObject js) {//使用本地json数据对象设置元数据内容,一般不会用到,若用到,则抛出异常 + // this function should not be called + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");//抛出非法参数异常 + } + + @Override//表示重写 + public JSONObject getLocalJSONFromContent() {//从元数据内容中获取本地json对象 + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");//抛出非法参数异常 + } + + @Override//表示重写 + public int getSyncAction(Cursor c) {//获取同步动作状态 + throw new IllegalAccessError("MetaData:getSyncAction should not be called");//抛出非法参数异常 + } + +} + + diff --git a/src/gtask/data/Node.java b/src/gtask/data/Node.java new file mode 100644 index 0000000..1d36629 --- /dev/null +++ b/src/gtask/data/Node.java @@ -0,0 +1,101 @@ +/* + * 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;//声名gtask下的data包 + +import android.database.Cursor;//引用android.database.Cursor类,基于数据库服务的类 + +import org.json.JSONObject;//引用 JSONObject + +public abstract class Node {//同步操作的基础数据类型,定义相关指示同步操作的常量 + public static final int SYNC_ACTION_NONE = 0;//不进行操作时=0 + + public static final int SYNC_ACTION_ADD_REMOTE = 1;//远程云端增加内容=1 + + public static final int SYNC_ACTION_ADD_LOCAL = 2;//本地增加内容=2 + + public static final int SYNC_ACTION_DEL_REMOTE = 3;//远程云端删除内容=3 + + public static final int SYNC_ACTION_DEL_LOCAL = 4;//本地删除内容=4 + + public static final int SYNC_ACTION_UPDATE_REMOTE = 5;//本地内容更新到远程云端=5 + + public static final int SYNC_ACTION_UPDATE_LOCAL = 6;//远程云端内容更新到本地=6 + + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;//同步出现冲突=7 + + public static final int SYNC_ACTION_ERROR = 8;//同步出现错误=8 + + private String mGid;//string类型,记录gid + + private String mName;//string类型,记录name + + private long mLastModified;//long类型 表明最后一次的修改的时间 + + private boolean mDeleted;//bool类型,判断是否被删除 + + public Node() {//构造函数,进行初始化 + mGid = null;//mgid为空 + mName = "";//名字为空 + mLastModified = 0;//最后一次修改时间为0 + mDeleted = false;//mDeleted为false表示未删除 + } + + public abstract JSONObject getCreateAction(int actionId);//声明JSONObject对象抽象类,并创建操作 + + public abstract JSONObject getUpdateAction(int actionId);//声明JSONObject对象抽象类,获取更新行为 + + public abstract void setContentByRemoteJSON(JSONObject js);//声明JSONObject对象抽象类,从远端JSON中同步设置目录。 + + public abstract void setContentByLocalJSON(JSONObject js);//声明JSONObject对象抽象类,从本地JSON中同步设置目录 + + public abstract JSONObject getLocalJSONFromContent();//声明JSONObject对象抽象类,从目录中获取本地JSON + + public abstract int getSyncAction(Cursor c);//声明int抽象类,获取同步行为代号 + + public void setGid(String gid) {//设置 gid + this.mGid = gid;//赋值给mDid + } + + public void setName(String name) {//设置名称 + this.mName = name;//赋值给mName + } + + public void setLastModified(long lastModified) {//设置最新修改时间 + this.mLastModified = lastModified;//赋值给mLastModified + } + + public void setDeleted(boolean deleted) {//设置删除标识 + this.mDeleted = deleted;//赋值给mDeleted + } + + public String getGid() {//得到gid + return this.mGid;//返回mGid + } + + public String getName() {//获取名称 + return this.mName;//返回mName + } + + public long getLastModified() {//获取最新修改 + return this.mLastModified; + }//返回最新修改 + + public boolean getDeleted() {//获取删除标识 + return this.mDeleted;//返回mDeleted + } + +} diff --git a/src/gtask/data/SqlData.java b/src/gtask/data/SqlData.java new file mode 100644 index 0000000..7faf059 --- /dev/null +++ b/src/gtask/data/SqlData.java @@ -0,0 +1,189 @@ +/* + * 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;//声名gtask下的data包 + +import android.content.ContentResolver;//引用android中content的几个类 +import android.content.ContentUris; +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.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; +import net.micode.notes.gtask.exception.ActionFailureException; + +import org.json.JSONException;//引用JSON使用失败异常处理类 +import org.json.JSONObject;//引用JSON对象库类 + + +public class SqlData {//定义数据库中基本数据类:读取数据、获取数据库中数据、提交数据到数据库 + private static final String TAG = SqlData.class.getSimpleName();//调用getSimpleName()函数,得到类的简写名称存入字符串TAG中 + + private static final int INVALID_ID = -99999;//将INVALID_ID初始化为-99999 + + public static final String[] PROJECTION_DATA = new String[] {//新建一个字符串数组,集合了interface DataColumns中所有SF常量 + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3//获得数据列id,mime类型,内容,1类型数据,3类型数据 + }; + + public static final int DATA_ID_COLUMN = 0;//0号列的名称为DATA_ID_COLUMN + + public static final int DATA_MIME_TYPE_COLUMN = 1;//1号列的名称为DATA_MIME_TYPE_COLUMN + + public static final int DATA_CONTENT_COLUMN = 2;//2号列的名称为DATA_CONTENT_COLUMN + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3;//3号列的名称为DATA_CONTENT_DATA_1_COLUMN + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4;//4号列的名称为DATA_CONTENT_DATA_3_COLUMN + + private ContentResolver mContentResolver;//定义私有全局变量 + + private boolean mIsCreate;//mIsCreate用于下文中选择SqlData的生成方式 + + private long mDataId;//数据ID号 + + private String mDataMimeType;//数据mime类型 + + private String mDataContent;//数据内容 + + private long mDataContentData1;//数据内容中的1类型数据 + + private String mDataContentData3;//数据内容中的3类型数据 + + private ContentValues mDiffDataValues;//mDiffDataValues用于构造SqiData,操作数据库 + + public SqlData(Context context) {//构造函数,初始化数据,参数类型为Context + mContentResolver = context.getContentResolver();//获取ContentResovler对象,如果需要查询数据,可以直接在mContetResolver上操作 + mIsCreate = true;//mIsCreate为TRUE是这种构造方式的标志 + mDataId = INVALID_ID;//mDataId 置初始值-99999。 + mDataMimeType = DataConstants.NOTE;//设置数据mime类型为note + mDataContent = "";//数据内容 + mDataContentData1 = 0;//1数据类型 + mDataContentData3 = "";//3数据类型 + mDiffDataValues = new ContentValues();//创建内容 + } + + public SqlData(Context context, Cursor c) {//构造函数,初始化数据,参数类型分别为Context和Cursor + mContentResolver = context.getContentResolver();//获取ContentProvider提供的数据 + mIsCreate = false;//记录当前数据创建方式 + loadFromCursor(c);//从光标处载入数据 + mDiffDataValues = new ContentValues(); + } + + private void loadFromCursor(Cursor c) {//从光标c处加载数据,帮助实现SqlData的第二种构造,将5列的数据赋给该类的对象 + mDataId = c.getLong(DATA_ID_COLUMN);//调用cursor类的方法,获取数据id,参数为id长度 + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);//调用cursor类的方法,获取数据mime类型,参数为其长度 + mDataContent = c.getString(DATA_CONTENT_COLUMN);//调用cursor类的方法,获取数据内容,参数为其长度 + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);//1类型数据 + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);//3类型数据 + } + + public void setContent(JSONObject js) throws JSONException {//设置共享数据,并且抛出JSON类型的异常处理机制 + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;//如果传入的JSONObject对象有DataColumns.ID这一项,则设置dataID为这个ID,否则设为INVALID_ID + if (mIsCreate || mDataId != dataId) {//如果采用的是第一种SqlData构造方式,或者这个对象的ID不是共享数据ID + mDiffDataValues.put(DataColumns.ID, dataId);//将共享数据ID加入数据库中 + } + mDataId = dataId;//共享数据同步后,共享数据ID等于该数据ID + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)//问号语句,若json中有MIME_TYPE这一项,则将其获取,否则,将其定义为notes类中定义的文本类型 + : DataConstants.NOTE; + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {//如果采用的是第一种SqlData构造方式,或者这个对象的MimeType不和共享数据一样 + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);//则将共享数据.MIME_TYPE加入数据库中 + } + mDataMimeType = dataMimeType;//共享数据同步后,共享数据mime类型等于该数据mime类型 + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";//如果传入的JSONObject对象有DataColumn.CONTENT一项,那么将其获取,否则。将其设置为空 + if (mIsCreate || !mDataContent.equals(dataContent)) {//如果是第一种sqldata函数,或者该数据内容和共享数据内容不同 + mDiffDataValues.put(DataColumns.CONTENT, dataContent);//那么就把这个数据内容添加到数据库这一数据内容列中 + } + mDataContent = dataContent;//共享数据同步后,共享数据内容等于该数据内容 + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;//如果js中有这一列1类型数据,则将datacontentdata1设为这个数据 ,否则设为0 + if (mIsCreate || mDataContentData1 != dataContentData1) {//如果是第一种sqldata函数,或者该1类型数据和共享1类型数据不同 + mDiffDataValues.put(DataColumns.DATA1, dataContentData1);//那么就把这个1类型数据添加到数据库这一1类型数据列中 + } + mDataContentData1 = dataContentData1;//共享数据同步后,共享1类型数据等于该1类型数据 + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";//如果js中有这一列3类型数据,则将datacontentdata1设为这个数据 ,否则设为空 + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {//如果是第一种sqldata函数,或者该3类型数据和共享3类型数据不同 + mDiffDataValues.put(DataColumns.DATA3, dataContentData3);//那么就把这个3类型数据添加到数据库这一3类型数据列中 + } + mDataContentData3 = dataContentData3;//共享数据同步后,共享3类型数据等于该3类型数据 + } + + public JSONObject getContent() throws JSONException {//获取共享的数据内容,并抛出json异常与处理机制 + if (mIsCreate) {//当采用的是第一种SqlData构造方式时 + Log.e(TAG, "it seems that we haven't created this in database yet");//在日志中显示错误“没有在数据库中创建这个数据” + return null;//返回null + } + JSONObject js = new JSONObject();//将相关数据放入新创建的JSONObject对象并返回 + js.put(DataColumns.ID, mDataId); + js.put(DataColumns.MIME_TYPE, mDataMimeType); + js.put(DataColumns.CONTENT, mDataContent); + js.put(DataColumns.DATA1, mDataContentData1); + js.put(DataColumns.DATA3, mDataContentData3);//将JSON中放入数据,这个JSON一共有五个数据,对应着SqlData中的五行数据 + return js; + } + + public void commit(long noteId, boolean validateVersion, long version) {//commit函数把当前所做的修改保存到数据库 + + if (mIsCreate) {//当采用的是第一种SqlData构造方式时 + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {如果该id是无效id且在共享数据中不存在该数据id对应的键 + mDiffDataValues.remove(DataColumns.ID);//则从共享数据中移除ID + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId);//更新共享数据,键为NOTE_ID,值为noteId + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);//在note的资源标识下加入data数据,并往URI中插入共享数据。 + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1));//获取有效便签id并创建 + } catch (NumberFormatException e) {//处理异常 + Log.e(TAG, "Get note id error :" + e.toString());//获取note id错误,e.toString()获取异常类型和异常详细消息 + throw new ActionFailureException("create note failed");//抛出异常“创建note失败” + } + } else {//当采用的是第二种SqlData构造方式时 + if (mDiffDataValues.size() > 0) { + int result = 0;//若共享数据存在,则通过内容解析器更新关于新URI的共享数据 + if (!validateVersion) {//如果版本还没确认 + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);//则结果记录下的只是data的ID,还有data内容 + } else {//如果版本确认了 + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { + String.valueOf(noteId), String.valueOf(version) + });//则从数据库中选取对应版本的id进行更新 + } + if (result == 0)//{如果更新不存在(或许用户在同步时已经完成更新 + Log.w(TAG, "there is no update. maybe user updates note when syncing");//报错 + } + } + } + + mDiffDataValues.clear();//回到初始化,清空,表示已经更新 + mIsCreate = false; + } + + public long getId() { + return mDataId;//获取当前id + } +} diff --git a/src/gtask/data/SqlNote.java b/src/gtask/data/SqlNote.java new file mode 100644 index 0000000..941a70a --- /dev/null +++ b/src/gtask/data/SqlNote.java @@ -0,0 +1,506 @@ +/* + * 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;//声名gtask下的data包 + +import android.appwidget.AppWidgetManager;//引用android中content的几个类 +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;//导入json数组类 +import org.json.JSONException;//异常处理类 +import org.json.JSONObject;//对象类 +用于支持小米便签最底层数据库的相关操作 +import java.util.ArrayList;//java中向量处理类 + + +public class SqlNote {//用于支持小米便签最底层数据库的相关操作的类 + private static final String TAG = SqlNote.class.getSimpleName();//调用getSimpleName ()函数得到类的简写名称存入字符串TAG中 + + private static final int INVALID_ID = -99999;//将INVALID_ID 初始化为-99999 + + public static final String[] PROJECTION_NOTE = new String[] {//集合了interface NoteColumns中所有17个SF常量 + 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 + }; + + public static final int ID_COLUMN = 0;//ID为0 + + public static final int ALERTED_DATE_COLUMN = 1;//提醒时间为1 + + public static final int BG_COLOR_ID_COLUMN = 2;//背景颜色为2 + + public static final int CREATED_DATE_COLUMN = 3;//创建时间为3 + + public static final int HAS_ATTACHMENT_COLUMN = 4;//有无附件为4 + + public static final int MODIFIED_DATE_COLUMN = 5;//更改时间为5 + + public static final int NOTES_COUNT_COLUMN = 6;//便签数为6 + + public static final int PARENT_ID_COLUMN = 7;//父节点ID为7 + + public static final int SNIPPET_COLUMN = 8;//文本片段为8 + + public static final int TYPE_COLUMN = 9;//文件类型为9 + + public static final int WIDGET_ID_COLUMN = 10;//小部件ID为10 + + public static final int WIDGET_TYPE_COLUMN = 11;//小部件类型为11 + + public static final int SYNC_ID_COLUMN = 12;//同步ID为12 + + public static final int LOCAL_MODIFIED_COLUMN = 13;//本地修改为13 + + public static final int ORIGIN_PARENT_ID_COLUMN = 14;//原始父文件夹ID为14 + + public static final int GTASK_ID_COLUMN = 15;//用户ID为15 + + public static final int VERSION_COLUMN = 16;//版本号为16 + + /*以下定义了17个内部的变量,其中12个可以由content中获得,5个需要初始化为0或者new*/ + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mId; + + private long mAlertDate; + + private int mBgColorId; + + private long mCreatedDate; + + private int mHasAttachment; + + private long mModifiedDate; + + private long mParentId; + + private String mSnippet; + + private int mType; + + private int mWidgetId; + + private int mWidgetType; + + private long mOriginParent; + + private long mVersion; + + private ContentValues mDiffNoteValues; + + private ArrayList mDataList; + + public SqlNote(Context context) {//第一种构造函数,参数只有context,初始化新建的对象中的所有变量 + mContext = context;//获取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;//默认父类ID + mSnippet = "";//文本片段默认为空 + mType = Notes.TYPE_NOTE;//默认文件类型为Note的文件类型 + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;//控件ID初始化 + mWidgetType = Notes.TYPE_WIDGET_INVALIDE;//控件类型初始化 + mOriginParent = 0;//默认原始父文件夹ID + mVersion = 0;//版本号 + mDiffNoteValues = new ContentValues();//新建一个NoteValues值,用来记录改变的values + mDataList = new ArrayList();//新建一个data的列表 + } + + public SqlNote(Context context, Cursor c) {//构造函数,参数有context和cursor,对cursor指向的对象进行初始化 + mContext = context;//数据表初始化 + mContentResolver = context.getContentResolver();//获取ContentProvider提供的数据 + mIsCreate = false;//提示第二种构造方式为false + loadFromCursor(c);//从光标ID处加载数据 + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE)//如果是数据是便签类型 + loadDataContent();//则加载数据内容 + mDiffNoteValues = new ContentValues();//新建一个NoteValues值 + } + + public SqlNote(Context context, long id) {//第三种构造函数,参数有Context和id + mContext = context;//数据表初始化 + mContentResolver = context.getContentResolver();//获取ContentProvider提供的数据 + mIsCreate = false;//第三种构造方式的标识 + loadFromCursor(id);//从光标ID处加载数据 + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE)//如果是数据是便签类型 + loadDataContent();//则加载数据内容 + mDiffNoteValues = new ContentValues();//新建一个NoteValues值 + + } + + private void loadFromCursor(long id) {//从光标ID处加载数据 + Cursor c = null;//没有内容 + try {//通过try避免异常 + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(id) + }, null);//通过id获取ContentResolver中的相应内容,并赋给cursor + if (c != null) {//如果有内容 + c.moveToNext();//移入文档 + loadFromCursor(c);//再次等待光标的内容 + } else {//否则 + Log.w(TAG, "loadFromCursor: cursor = null");//日志显示警告 + } + } finally { + if (c != null) + c.close();//关闭释放cursor + } + } + + private void loadFromCursor(Cursor c) {//通过游标从光标处加载数据 + mId = c.getLong(ID_COLUMN);//这是loadFromCursor(long id)方法实现时调用的方法,以下从cursor指向的那条记录中加载相应的数据从而实现SQLnote和sqldata间的联系,实现sqlnote的构造 + 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); + } + + private void loadDataContent() {//通过content机制获取共享数据并加载到数据库当前游标处 + Cursor c = null; + mDataList.clear();//清空数据表 + try { + c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, + "(note_id=?)", new String[] { + String.valueOf(mId) + }, null);//获得该ID对应的数据内容 + if (c != null) { + if (c.getCount() == 0) {//如果光标处无内容 + Log.w(TAG, "it seems that the note has not data");//提示note无数据warning + 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();//关闭游标 + } + } + + public boolean setContent(JSONObject js) {//设置通过content机制用于共享的数据信息 + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//创建一个JSONObject对象note + if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {//判断是不是系统文件夹 + Log.w(TAG, "cannot set system folder");//警告不能设置系统文件 + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {//文件夹只能更新摘要和类型 + // for folder we can only update the snnipet and type + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : "";//如果共享数据存在摘要,则将其赋给声明的 snippet变量,否则变量为空 + if (mIsCreate || !mSnippet.equals(snippet)) {//如果没有创建或者该摘要没有匹配原摘要 + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);//则将其加入解析器 + } + mSnippet = snippet;//将该摘要覆盖原摘要 + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE;//获取数据的类型 + if (mIsCreate || mType != type) {//如果只是通过上下文对note进行数据库操作,或者该类型与原类型不相同 + mDiffNoteValues.put(NoteColumns.TYPE, type);//将该类型保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mType = type;//将该类型覆盖原类型 + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {//如果不是文件夹而是note + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);//获取便签提示日期 + long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;//获取数据的ID + if (mIsCreate || mId != id) {//如果只是通过上下文对note进行数据库操作,或者该ID与原ID不相同 + mDiffNoteValues.put(NoteColumns.ID, id);//将该ID保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mId = id;//将该ID覆盖原ID + + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note + .getLong(NoteColumns.ALERTED_DATE) : 0;//获取数据的提醒日期 + if (mIsCreate || mAlertDate != alertDate) {//如果只是通过上下文对note进行数据库操作,或者该提醒日期与原提醒日期不相同 + mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);//将该提醒日期保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mAlertDate = alertDate;//将该提醒日期覆盖原提醒日期 + + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note + .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);//获取数据的背景颜色 + if (mIsCreate || mBgColorId != bgColorId) {//如果只是通过上下文对note进行数据库操作,或者该背景颜色与原背景颜色不相同 + mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);//将该背景颜色保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mBgColorId = bgColorId;//将该背景颜色覆盖原背景颜色 + + long createDate = note.has(NoteColumns.CREATED_DATE) ? note + .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();//将该背景颜色覆盖原背景颜色 + if (mIsCreate || mCreatedDate != createDate) {//如果只是通过上下文对note进行数据库操作,或者该创建日期与原创建日期不相同 + mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);//将该创建日期保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mCreatedDate = createDate;//将该创建日期覆盖原创建日期 + + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note + .getInt(NoteColumns.HAS_ATTACHMENT) : 0;//获取数据的有无附件的布尔值 + if (mIsCreate || mHasAttachment != hasAttachment) {//如果只是通过上下文对note进行数据库操作,或者该有无附件的布尔值与原有无附件的布尔值不相同 + mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);//将该有无附件的布尔值保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mHasAttachment = hasAttachment;//将该有无附件的布尔值覆盖原有无附件的布尔值 + + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note + .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();//获取数据的修改日期 + if (mIsCreate || mModifiedDate != modifiedDate) {//如果只是通过上下文对note进行数据库操作,或者该修改日期与原修改日期不相同 + mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);//将该修改日期保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mModifiedDate = modifiedDate;//将该修改日期覆盖原修改日期 + + long parentId = note.has(NoteColumns.PARENT_ID) ? note + .getLong(NoteColumns.PARENT_ID) : 0;//获取数据的父节点ID + if (mIsCreate || mParentId != parentId) {//如果只是通过上下文对note进行数据库操作,或者该父节点ID与原父节点ID不相同 + mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);//将该父节点ID保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mParentId = parentId;//将该父节点ID覆盖原父节点ID + + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : "";//获取数据的文本片段 + if (mIsCreate || !mSnippet.equals(snippet)) {//如果只是通过上下文对note进行数据库操作,或者该文本片段与原文本片段不相同 + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);//将该文本片段保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mSnippet = snippet;//将该文本片段覆盖原文本片段 + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE;//获取数据的文件类型 + if (mIsCreate || mType != type) {//如果只是通过上下文对note进行数据库操作,或者该文件类型与原文件类型不相同 + mDiffNoteValues.put(NoteColumns.TYPE, type);//将该文件类型保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mType = type;//将该文件类型覆盖原文件类型 + + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) + : AppWidgetManager.INVALID_APPWIDGET_ID;//获取数据的小部件ID + if (mIsCreate || mWidgetId != widgetId) {//如果只是通过上下文对note进行数据库操作,或者该小部件ID与原小部件ID不相同 + mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);//将该小部件ID保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mWidgetId = widgetId;//将该小部件ID覆盖原小部件ID + + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note + .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;//获取数据的小部件种类 + if (mIsCreate || mWidgetType != widgetType) {//如果只是通过上下文对note进行数据库操作,或者该小部件种类与原小部件种类不相同 + mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);//将该小部件种类保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mWidgetType = widgetType;//将该小部件种类覆盖原小部件种类 + + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note + .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;//获取数据的原始父文件夹ID + if (mIsCreate || mOriginParent != originParent) {//如果只是通过上下文对note进行数据库操作,或者该原始父文件夹ID与原原始父文件夹ID不相同 + mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);//将该原始父文件夹ID保存在mDiffNoteValue这个变量中,说明这两个值不相同 + } + mOriginParent = originParent;//将该原始父文件夹ID覆盖原原始父文件夹ID + + for (int i = 0; i < dataArray.length(); i++) {//遍历dataArray,查找id为dataId的数据 + JSONObject data = dataArray.getJSONObject(i);//依次获取数据表中的数据ID + SqlData sqlData = null;//如果没有找到,就新建一个并加入到列表中 + if (data.has(DataColumns.ID)) {//该数据ID对应的数据如果存在 + long dataId = data.getLong(DataColumns.ID);//将对应的数据存在数据库中 + for (SqlData temp : mDataList) { + if (dataId == temp.getId()) { + sqlData = temp; + } + } + } + + if (sqlData == null) {//如果数据库数据没有进行更新 + sqlData = new SqlData(mContext);//就根据上下文创建一个数据库数据 + mDataList.add(sqlData);//添加到数据列表中 + } + + sqlData.setContent(data);//最后为数据库数据进行设置 + } + } + } catch (JSONException e) {//如果JSONE出现异常 + Log.e(TAG, e.toString());//日志显示错误 + e.printStackTrace();//打印堆栈轨迹 + return false; + } + return true; + } + + public JSONObject getContent() {//获取content机制提供的数据并加载到note中 + try { + JSONObject js = new JSONObject();//创建一个新的jsonobject js + + if (mIsCreate) {//如果采取第一种数据库便签处理 + Log.e(TAG, "it seems that we haven't created this in database yet");//报错, 没有创建数据库 + return null; + } + + JSONObject note = new JSONObject();//创建变量存储共享数据 + if (mType == Notes.TYPE_NOTE) {//如果对象的类型是note类型 + note.put(NoteColumns.ID, mId);//设置ID + note.put(NoteColumns.ALERTED_DATE, mAlertDate);//设置提醒时间 + note.put(NoteColumns.BG_COLOR_ID, mBgColorId);//设置背景颜色ID + note.put(NoteColumns.CREATED_DATE, mCreatedDate);//创建日期 + note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);//是否有附件 + note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);//修改日期 + note.put(NoteColumns.PARENT_ID, mParentId);//父节点ID + note.put(NoteColumns.SNIPPET, mSnippet);//文件片段 + note.put(NoteColumns.TYPE, mType);//类型 + note.put(NoteColumns.WIDGET_ID, mWidgetId);//小部件ID + note.put(NoteColumns.WIDGET_TYPE, mWidgetType);//小部件类型 + note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);//原始父节点ID + js.put(GTaskStringUtils.META_HEAD_NOTE, note);//将这个便签存入元数据中 + + JSONArray dataArray = new JSONArray();//获取数据库数据,并存入数组中 + for (SqlData sqlData : mDataList) {//利用循环将数据链表的数据获取 + JSONObject data = sqlData.getContent();//获取内容 + if (data != null) {//如果data非空 + dataArray.put(data);//数据存入数组中 + } + } + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);//将元数据存入数组中 + } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {//类型为系统文件或目录文件时 + note.put(NoteColumns.ID, mId);//将id存入jsonobject + note.put(NoteColumns.TYPE, mType);//将类型存入jsonobject + note.put(NoteColumns.SNIPPET, mSnippet);//将文件片段存入jsonobject + js.put(GTaskStringUtils.META_HEAD_NOTE, note);//存入元便签中 + } + + return js; + } catch (JSONException e) {//json异常处理 + Log.e(TAG, e.toString());//提示错误 + e.printStackTrace();//打印栈轨迹 + } + return null; + } + + public void setParentId(long id) {//设置当前ID的父ID + mParentId = id; + mDiffNoteValues.put(NoteColumns.PARENT_ID, id); + } + + public void setGtaskId(String gid) {//给当前id设置Gtaskid + mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); + } + + public void setSyncId(long syncId) {//给当前id设置同步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的父id + return mParentId; + } + + public String getSnippet() {//获取用于显示的便签内容片段 + return mSnippet; + } + + public boolean isNoteType() {//判断是否为note类型 + 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);//将它移除 + } + + Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);//内容解析器中插入该便签的uri + try { + mId = Long.valueOf(uri.getPathSegments().get(1));//强制转换path为id,Long型 + } catch (NumberFormatException e) {//捕获异常,转换出错 + Log.e(TAG, "Get note id error :" + e.toString());//显示错误“获取note的id出现错误” + throw new ActionFailureException("create note failed");//抛出异常,创建 note 失败 + } + if (mId == 0) {//id为0 + throw new IllegalStateException("Create thread id failed");//创建线程失败 + } + + if (mType == Notes.TYPE_NOTE) {//如果当前类型是便签类型 + for (SqlData sqlData : mDataList) {//直接使用sqldata中的方式遍历 + sqlData.commit(mId, false, -1);//引用sqldata的方式实现commit + } + } + } else {如果是其他构造方式 + if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {//如果该便签ID是无效ID + Log.e(TAG, "No such note");//报错:没有这个便签 + throw new IllegalStateException("Try to update note with invalid id");//提示:尝试去更新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) + });//更新内容解析器:存入便签内容uri,便签ID,mID + } else {//如果是有效版本 + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",//没有更新 + new String[] { + String.valueOf(mId), String.valueOf(mVersion) + });//更新不存在,可能用户在同步时更新了 note + } + if (result == 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);////引用sqldata的commit提交数据 + } + } + } + + // refresh local info + loadFromCursor(mId);//通过cursor从当前id处加载数据 + if (mType == Notes.TYPE_NOTE)//如果是便签类型 + loadDataContent();//加载数据内容 + + mDiffNoteValues.clear();//清空,回到初始化状态 + mIsCreate = false;//改变数据库构造模式 + } +} diff --git a/src/gtask/data/Task.java b/src/gtask/data/Task.java new file mode 100644 index 0000000..ce5af76 --- /dev/null +++ b/src/gtask/data/Task.java @@ -0,0 +1,351 @@ +/* + * 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;//声名gtask下的data包 + +import android.database.Cursor;//引用andriod和JSON中的一些操作,以及引用自己的包里的一些类 +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes;//引用小米便签中的类 +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray;//导入json数组类 +import org.json.JSONException;//异常处理类 +import org.json.JSONObject;//对象类 + + +public class Task extends Node {//创建Task类,继承自父类Node + private static final String TAG = Task.class.getSimpleName();//调用 getSimpleName ()函数来得到类的简写名称并存入字符串TAG中 + + private boolean mCompleted;//声明布尔类型,判断是否完成 + + private String mNotes;//页面标签信息 + + private JSONObject mMetaInfo;//元数据信息 + + private Task mPriorSibling;//对应的优先兄弟类Task的指针 + + private TaskList mParent;//任务列表的指针 + + public Task() {//Task构造方法 + super();//调用父类构造方法 + mCompleted = false;//对类的变量进行初始化,下同 + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + public JSONObject getCreateAction(int actionId) {//获取创建action + JSONObject js = new JSONObject();//新建一个 JSONObject 的对象用来存放同步过程中所用到的 task 信息 + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);//共享数据存入动作类型action_ type + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);//设置活动的id + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));//设置索引 + + // entity_delta + JSONObject entity = new JSONObject();//创建实体数据 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//将name存入数据 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");//将创建者ID存入数据 + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK);//将实体类型存入数据 + if (getNotes() != null) {//如果有数据 + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());//则将数据存放入实体数据中 + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);//将entity变量作为一个数据存放进js变量中 + + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());//存入父ID + + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,//存入父类型 + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());//存入列表ID + + // prior_sibling_id + if (mPriorSibling != null) {//如果存在优先兄弟task + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());//则将其ID放入js中 + } + + } catch (JSONException e) {//抛出异常处理机制 + Log.e(TAG, e.toString());//e.toString()获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to generate task-create jsonobject");//产生 JSONObject 任务创建操作失败 + } + + return js;//将这个存储字符串的变量返回 + } + + public JSONObject getUpdateAction(int actionId) {//获取更新的action + JSONObject js = new JSONObject();//新建一个存储数据的类 + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + if (getNotes() != null) {//如果存在 notes + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());//则将其也放入 entity 中 + } + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);//导入参数entity和DELTA到js + + } catch (JSONException e) {//抛出异常处理机制 + Log.e(TAG, e.toString());//e.toString()获取异常类型和异常详细消息 + e.printStackTrace();//命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to generate task-update jsonobject");//产生jsonobject任务创建操作失败 + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) {//通过远端的jsonobject获取任务内容 + if (js != null) {//如果js指向的目标存在 + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));//设置ID + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));////设置最近修改 + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));//设置name + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));//设置notes + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED));//设置已删除 + } + + // completed + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED));//设置已完成 + } + } catch (JSONException e) {//异常处理 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to get task content from jsonobject");//产生jsonobject任务创建操作失败 + } + } + } + + public void setContentByLocalJSON(JSONObject js) {//通过本地的jsonobject获取内容 + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) {//如果js不存在或者js没有元数据的开头或者js指针没有元数据 + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");//那么反馈给用户出错信息 + } + + try {//否则进行try和catch的异常处理操作 + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {//如果note类型匹配失败 + Log.e(TAG, "invalid type");//无效类型 + return; + } + + for (int i = 0; i < dataArray.length(); i++) {//遍历数据数组 + JSONObject data = dataArray.getJSONObject(i);//遍历dataArray查找与数据库中DataConstants.NOTE记录信息一致的data + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + } + + } catch (JSONException e) {//异常处理操作 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//命令行打印异常信息在程序中出错的位置及原因 + } + } + + public JSONObject getLocalJSONFromContent() {//从Content中获取本地的JSON数据 + String name = getName();//获取名称 + try { + if (mMetaInfo == null) {//如果元数据不存在,创建一个新的对象 + // new task created from web + if (name == null) {//且此名字也不存在 + Log.w(TAG, "the note seems to be an empty one");//反馈给用户异常信息 + return null; + } + + JSONObject js = new JSONObject();//初始化 + JSONObject note = new JSONObject();//初始化 + JSONArray dataArray = new JSONArray();//初始化 + JSONObject data = new JSONObject();//初始化 + data.put(DataColumns.CONTENT, name); + dataArray.put(data); + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + js.put(GTaskStringUtils.META_HEAD_NOTE, note);//获取metainfo中的head_note + return js; + } else {//否则将元数据同步至数据中 + // synced task + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//同步任务 + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);//定义一个数组并进行初始化 + + for (int i = 0; i < dataArray.length(); i++) {//遍历dataArray查找与数据库中DataConstants.NOTE记录信息一致的data + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + return mMetaInfo; + } + } catch (JSONException e) {//异常处理操作 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//命令行打印异常信息在程序中出错的位置及原因 + return null; + } + } + + public void setMetaInfo(MetaData metaData) {//设置元数据信息 + if (metaData != null && metaData.getNotes() != null) {//如果元数据存在且能够获取到文本信息 + try { + mMetaInfo = new JSONObject(metaData.getNotes());//进行异常处理,更新数据 + } catch (JSONException e) {//异常处理操作 + Log.w(TAG, e.toString());//获取异常类型和异常详细消息 + mMetaInfo = null;//将元数据的信息置空 + } + } + } + + public int getSyncAction(Cursor c) {//实现同步操作 + try { + JSONObject noteInfo = null;//新建一个JSONObject的对象实体 + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) {//元数据信息不为空并且元数据信息还含有“META_HEAD_NOTE”,说明便签存在 + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + } + + if (noteInfo == null) {//便签元数据已被删除,不存在 + Log.w(TAG, "it seems that note meta has been deleted");//提醒已被删除 + return SYNC_ACTION_UPDATE_REMOTE;//返回更新云端数据的同步行为 + } + + if (!noteInfo.has(NoteColumns.ID)) {//如果远端的数据库中没有它的ID + Log.w(TAG, "remote note id seems to be deleted");//提醒远端数据库中该便签已经被删除 + return SYNC_ACTION_UPDATE_LOCAL//返回更新云端数据的同步行为 + } + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {//如果ID不同 + Log.w(TAG, "note id doesn't match");//提醒信息不匹配 + return SYNC_ACTION_UPDATE_LOCAL;//返回更新云端数据的同步行为 + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {//如果本地的便签数据未修改过,即未更新 + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果修改后的ID匹配成功 + // no update both side + return SYNC_ACTION_NONE;//成功则返回无同步操作 + } else {//不成功 + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL;//返回本地同步更新操作 + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {//如果gtask的id与获取的id不匹配 + Log.e(TAG, "gtask id doesn't match");//提醒不匹配 + return SYNC_ACTION_ERROR;//返回同步操作错误 + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果本地id与云端id一致 + // local modification only + return SYNC_ACTION_UPDATE_REMOTE;//则返回云端更新的同步动作 + } else { + return SYNC_ACTION_UPDATE_CONFLICT;//冲突 + } + } + } catch (Exception e) {//异常处理操作 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//命令行打印异常信息在程序中出错的位置及原因 + } + + return SYNC_ACTION_ERROR;//返回同步操作错误 + } + + public boolean isWorthSaving() {//判断是否值得存放,即当前数据是否有效 + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0);//若数据非空 或 名字合法存在且去除空格后的名字长度大于零则返回真值 + } + + public void setCompleted(boolean completed) { + this.mCompleted = completed;//将Task置为修改完毕 + } + + public void setNotes(String notes) { + this.mNotes = notes;//设定mNotes + } + + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling;//设置这个任务的优先兄弟 + } + + public void setParent(TaskList parent) { + this.mParent = parent;//设置这个任务的父节点 + } + + public boolean getCompleted() { + return this.mCompleted;//返回是否完成的标志 + } + + public String getNotes() { + return this.mNotes;//获取成员变量mNotes的信息。 + } + + public Task getPriorSibling() { + return this.mPriorSibling;//获取优先兄弟列表 + } + + public TaskList getParent() { + return this.mParent;//获取父节点列表 + } + +} diff --git a/src/gtask/data/TaskList.java b/src/gtask/data/TaskList.java new file mode 100644 index 0000000..7ae2b80 --- /dev/null +++ b/src/gtask/data/TaskList.java @@ -0,0 +1,343 @@ +/* + * 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;//声名gtask下的data包 + +import android.database.Cursor;//引用andriod和JSON中的一些操作,以及引用自己的包里的一些类 +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;//导入json异常处理类 +import org.json.JSONObject;//对象类 + +import java.util.ArrayList; + + +public class TaskList extends Node {//TaskList类继承于Node类 + private static final String TAG = TaskList.class.getSimpleName();//调用 getSimpleName ()函数来得到类的简写名称并存入字符串TAG中 + + private int mIndex;//当前TaskList的指针 + + private ArrayList mChildren;//声明了一个ArrayList的动态数组,这类数组可以加入不同类型的数据 + + public TaskList() {//TaskList的构造函数 + super();//调用父类构造方法,也就是Node类 + mChildren = new ArrayList();//声明arraylist数组 + mIndex = 1;//初始化 + } + + public JSONObject getCreateAction(int actionId) {//定义在Node中定义的一个抽象函数 + JSONObject js = new JSONObject();//创建一个新的JSONObject对象 + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);//指明了操作类型是“create” + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);//调用put放入编号 + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // entity_delta + JSONObject entity = new JSONObject();//新建一个新的JSONObject对象 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//getName是父类的一个函数 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");//将创建者的ID设置为空 + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP);//将实体类型设置为“GROUP” + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to generate tasklist-create jsonobject");//抛出异常,生成更新任务列表的 JSONObject 失败 + } + + return js;//将已经存储数据的js对象返回 + } + + public JSONObject getUpdateAction(int actionId) {//获取更新的行为 + JSONObject js = new JSONObject();//创建一个JSONObject的实例化对象js + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);//初始化js中的类型 + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);//初始化actionID + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());//初始化ID + + // entity_delta + JSONObject entity = new JSONObject();//创建一个 JSONObject 的实例化对象entity + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to generate tasklist-update jsonobject");//抛出异常 + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) {//通过远端设置内容 + if (js != null) {//js对象不为空 + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {//如果传入的对象中含有GTASK_JSON_ID + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));//根据内容进行设置 + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {//若 js 中存在 GATSK_JSON_LAST_MODIFIED 信息 + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));//获取 GTASK_JSON_NAME 字符串并设置为该 js 对象的名称 + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));//对任务的name进行设置 + } + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("fail to get tasklist content from jsonobject");//抛出异常 + } + } + } + + public void setContentByLocalJSON(JSONObject js) {//通过本地 JSON 数据设置对象 js 内容 + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {//若 js 创建失败或 js 中不存在 META_HEAD_NOTE信息 + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");//警告,没有可用资源 + } + + try { + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {//若为一般类型的文件夹 + String name = folder.getString(NoteColumns.SNIPPET);//获取文件夹片段字符串作为文件夹名称 + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name);//设置名称为MIUI系统文件夹前缀+文件夹名称 + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {//若为系统类型文件夹 + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER)//若为根目录文件夹 + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT);//设置名称为MIUI系统文件夹前缀+默认文件夹名称 + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER)//若为通话记录文件夹 + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE);//设置名称为MIUI系统文件夹前缀+默认文件夹名称 + else + Log.e(TAG, "invalid system folder");//错误,无效的系统文件夹 + } else { + Log.e(TAG, "error type");//错误的类型 + } + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + } + } + + public JSONObject getLocalJSONFromContent() {//通过 Content 机制获取本地 JSON 数据 + try { + JSONObject js = new JSONObject();//创建一个 JSONObject 的实例化对象 js + JSONObject folder = new JSONObject();//创建一个 JSONObject 的实例化对象 folder + + String folderName = getName();//获取完整的文件名字 + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX))//如果这个文件名字是以"[MIUI_Notes]"开头 + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length());//文件名字应该去掉这个前缀 + folder.put(NoteColumns.SNIPPET, folderName); + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE))//当获取的文件夹名称是以"Default"或"Call_Note开头 + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);//则为系统文件夹 + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);//否则为一般文件夹 + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + return null; + } + } + + public int getSyncAction(Cursor c) {//通过游标类获取同步操作 + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {//若本地记录未修改 + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//最近一次修改的 id 匹配成功 + // no update both side + return SYNC_ACTION_NONE;//返回无的同步行为 + } else {//匹配失败 + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL;//返回更新本地数据的同步行为 + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {//如果获取的ID不匹配 + Log.e(TAG, "gtask id doesn't match");//错误,id不匹配 + return SYNC_ACTION_ERROR;//返回同步动作失败 + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果是最近一次修改的 id + // local modification only + return SYNC_ACTION_UPDATE_REMOTE;//返回云端更新的同步动作 + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE;//返回云端更新的同步动作 + } + } + } catch (Exception e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + } + + return SYNC_ACTION_ERROR;//返回同步动作失败 + } + + public int getChildTaskCount() {//获取列表中子任务数量 + return mChildren.size(); + } + + public boolean addChildTask(Task task) {//在当前任务表中添加新的子任务 + boolean ret = false;//定义一个布尔型变量,初始化为false。 + if (task != null && !mChildren.contains(task)) {//任务非空且任务表中不存在该任务 + ret = mChildren.add(task);//在mChildren中 添加子任务,成功与否返回至ret中保存 + if (ret) {//添加子任务成功 + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1));//设置兄弟任务优先级,若子任务为空,返回 NULL,否则返回子任务数量-1 + task.setParent(this);//设置该任务的父任务 + } + } + return ret;//返回值为是否成功添加任务 + } + + public boolean addChildTask(Task task, int index) {//在当前任务表中的索引位置添加新的子任务 + if (index < 0 || index > mChildren.size()) {//无效的索引 + Log.e(TAG, "add child task: invalid index");//错误,无效的索引导致添加子任务失败 + return false; + } + + int pos = mChildren.indexOf(task);//获取要添加的任务在列表中的位置 + if (task != null && pos == -1) {//任务非空且任务表中不存在该任务 + mChildren.add(index, task);//在索引位置添加任务 + + // update the task list + Task preTask = null; + Task afterTask = null;//更新任务表 + if (index != 0) + preTask = mChildren.get(index - 1); + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + task.setPriorSibling(preTask);//使得三个任务前后连在一块 + if (afterTask != null)//下一个任务设置兄弟任务优先级 + afterTask.setPriorSibling(task); + } + + return true; + } + + public boolean removeChildTask(Task task) {//删除任务表中的子任务 + boolean ret = false;//声明布尔类型ret为false + int index = mChildren.indexOf(task);//获取该任务在任务表中的索引 + if (index != -1) {//index不等于-1,说明任务列表中存在该任务 + ret = mChildren.remove(task);//删除mChildren中的任务 + + if (ret) {//如果删除成功 + // reset prior sibling and parent + task.setPriorSibling(null); + task.setParent(null);//就要将该任务的优先兄弟任务和父任务设置为空 + + // update the task list + if (index != mChildren.size()) {//删除成功后 + mChildren.get(index).setPriorSibling( + index == 0 ? null : mChildren.get(index - 1));//对任务列表进行更新 + } + } + } + return ret;//返回值是是否删除成功 + } + + public boolean moveChildTask(Task task, int index) {//将当前TaskList中含有的某个Task移到index位置 + + if (index < 0 || index >= mChildren.size()) {//目标位置越界,无效 + Log.e(TAG, "move child task: invalid index");//错误,无效的索引导致移动子任务失败 + return false; + } + + int pos = mChildren.indexOf(task);//若所要查找的子任务不存在 + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list");//错误,任务不在列表中导致移动子任务失败 + return false; + } + + if (pos == index)//当前位置与索引匹配成功 + return true;//返回真值 + return (removeChildTask(task) && addChildTask(task, index));//不相等则进行删除和添加即移动操作 + } + + public Task findChildTaskByGid(String gid) {//按gid寻找Task + for (int i = 0; i < mChildren.size(); i++) {//遍历整个任务列表判断任务的gid与传入的gid是否相等 + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + public int getChildTaskIndex(Task task) {//获取子任务索引 + return mChildren.indexOf(task); + } + + public Task getChildTaskByIndex(int index) {//通过索引获取子任务 + if (index < 0 || index >= mChildren.size()) {//指针不在范围内 + Log.e(TAG, "getTaskByIndex: invalid index");//错误,无效的索引导致获取任务失败 + return null; + } + return mChildren.get(index); + } + + public Task getChilTaskByGid(String gid) {//返回指定gid的任务task + for (Task task : mChildren) {//ArrayList的遍历 + if (task.getGid().equals(gid)) + return task; + } + return null; + } + + public ArrayList getChildTaskList() {//获取子任务列表 + return this.mChildren; + } + + public void setIndex(int index) {//设置任务索引 + this.mIndex = index; + } + + public int getIndex() { + return this.mIndex;//获取任务索引 + } +} diff --git a/src/gtask/exception/ActionFailureException.java b/src/gtask/exception/ActionFailureException.java new file mode 100644 index 0000000..56a97e6 --- /dev/null +++ b/src/gtask/exception/ActionFailureException.java @@ -0,0 +1,32 @@ +/* + * 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.exception;//声名gtask下的data包,小米便签行为异常处理 +public class ActionFailureException extends RuntimeException {//从RuntimeException类派生出ActionFailureException类,用来处理行为异常 + private static final long serialVersionUID = 4425249765923293627L;//用来验证版本一致性,如果不一致会导致反序列化的时候版本不一致的异常 + + public ActionFailureException() {//交给父类的构造函数 + super();//指向父类的一个指针 + } + + public ActionFailureException(String paramString) {//调用父类具有相同形参paramString的构造方法 + super(paramString); + } + + public ActionFailureException(String paramString, Throwable paramThrowable) {//调用父类具有相同形参paramString和paramThrowable的构造方法 + super(paramString, paramThrowable); + } +} diff --git a/src/gtask/exception/NetworkFailureException.java b/src/gtask/exception/NetworkFailureException.java new file mode 100644 index 0000000..f29ac34 --- /dev/null +++ b/src/gtask/exception/NetworkFailureException.java @@ -0,0 +1,33 @@ +/* + * 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.exception;//声名gtask下的data包,小米便签网络异常处理 + +public class NetworkFailureException extends Exception {//从Exception类派生出NetworkFailureException类,用来处理网络异常 + private static final long serialVersionUID = 2107610287180234136L;//用来验证版本一致性,如果不一致会导致反序列化的时候版本不一致的异常 + + public NetworkFailureException() {//不带参数的构造函数 + super();//指向父类的一个指针 + } + + public NetworkFailureException(String paramString) {//调用父类具有相同形参paramString的构造方法 + super(paramString); + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) {//调用父类具有相同形参paramString和paramThrowable的构造方法 + super(paramString, paramThrowable); + } +} diff --git a/src/gtask/remote/GTaskASyncTask.java b/src/gtask/remote/GTaskASyncTask.java new file mode 100644 index 0000000..b2cc756 --- /dev/null +++ b/src/gtask/remote/GTaskASyncTask.java @@ -0,0 +1,123 @@ + +/* + * 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.remote;//声名gtask下的remote包,用来实现异步操作 + +import android.app.Notification;//引用andriod和JSON中的一些操作,以及引用自己的包里的一些类 +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; + +import net.micode.notes.R;//引用小米便签中的类 +import net.micode.notes.ui.NotesListActivity; +import net.micode.notes.ui.NotesPreferenceActivity; + + +public class GTaskASyncTask extends AsyncTask {//扩展于AysyncTask类,是用来处理GTask的异步任务的类 + + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;//static int变量存储GTask同步通知的ID。 + + public interface OnCompleteListener {//通过interface实现多个接口,接口为OnCompleteListener,用来初始化异步的功能 + void onComplete();//初始化 + } + + private Context mContext;//文本内容 + + private NotificationManager mNotifiManager;//通知管理器类的实例化 + + private GTaskManager mTaskManager;//实例化任务管理器 + + private OnCompleteListener mOnCompleteListener;//实例化是否完成的监听器 + + public GTaskASyncTask(Context context, OnCompleteListener listener) {//GTaskASyncTask类的构造函数 + mContext = context;//初始化 + mOnCompleteListener = listener;//初始化 + mNotifiManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE);//getSystemService是Activity的一个方法,可根据传入的参数获得应服务的对象。这里以Context的NOTIFICATION_SERVICE为对象 + mTaskManager = GTaskManager.getInstance();//getInstance ()函数用于使用单例模式创建类的实例 + } + + public void cancelSync() {//中断同步操作 + mTaskManager.cancelSync();//调用类中的同名方法来取消同步 + } + + public void publishProgess(String message) {//显示消息 + publishProgress(new String[] {//String[]创建java数组 + message + }); + } + + private void showNotification(int tickerId, String content) {//显示通知消息 + Notification notification = new Notification(R.drawable.notification, mContext + .getString(tickerId), System.currentTimeMillis());//新建通知 + notification.defaults = Notification.DEFAULT_LIGHTS;//默认调用系统自带灯光 + notification.flags = Notification.FLAG_AUTO_CANCEL;//点击清除按钮或点击通知后,提示会自动消失 + PendingIntent pendingIntent;// PendingIntent 是 Android 提供的一种用于外部程序调起自身程序的能力,生命周期不与主程序相关 + if (tickerId != R.string.ticker_success) {//若同步不成功 + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), 0);//从系统获得一个对象来启动NotesPreferenceActivity + + } else {//若同步成功 + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), 0);//从系统取得一个来启动一个NotesListActivity的对象 + } + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, + pendingIntent);//设置在通知列表里该通知的显示情况 + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);//通过NotificationManager对象的notify()方法来执行一个notification的消息 + } + + @Override//表示重写 + protected Integer doInBackground(Void... unused) {//异步执行后台线程将要完成的任务 + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity + .getSyncAccountName(mContext)));//利用getString,将把 getSyncAccountName(mContext)的字符串内容传进sync_progress_login中 + return mTaskManager.sync(mContext, this);//进行后台同步具体操作 + } + + @Override//表示重写 + protected void onProgressUpdate(String... progress) {//显示进度的更新 + showNotification(R.string.ticker_syncing, progress[0]);//调用显示通知的方法,将第一个进度的通知进行显示 + if (mContext instanceof GTaskSyncService) {//如果mContext是GTaskSyncService实例化的对象 + ((GTaskSyncService) mContext).sendBroadcast(progress[0]);//则发送一个广播 + } + } + + @Override//表示重写 + protected void onPostExecute(Integer result) {//设置任务,比如在用户界面显示一个进度条 + if (result == GTaskManager.STATE_SUCCESS) {//若匹配成功 + showNotification(R.string.ticker_success, mContext.getString( + R.string.success_sync_account, mTaskManager.getSyncAccount()));//则显示成功及展示出同步的账户 + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());//设置最新同步的时间,防止时间出现更改 + } else if (result == GTaskManager.STATE_NETWORK_ERROR) {//因网络错误而同步失败 + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));//显示网络失败 + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) {//若因为系统内部原因失败 + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) {//若因为主动取消同步失败 + showNotification(R.string.ticker_cancel, mContext + .getString(R.string.error_sync_cancelled));//显示取消同步通知 + } + if (mOnCompleteListener != null) {//若监听器为空 + new Thread(new Runnable() {//则创建新进程 + + public void run() {//执行完后调用然后返回主线程中 + mOnCompleteListener.onComplete(); + } + }).start(); + } + } +} diff --git a/src/gtask/remote/GTaskClient.java b/src/gtask/remote/GTaskClient.java new file mode 100644 index 0000000..47cec86 --- /dev/null +++ b/src/gtask/remote/GTaskClient.java @@ -0,0 +1,585 @@ +/* + * 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.remote;//声名gtask下的remote包,用于自动登录账号并更新消息,创建或者获取任务列表 + +import android.accounts.Account;//引用andriod和JSON中的一些操作,以及引用自己的包里的一些类 +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.gtask.data.Node;//引用小米便签中的类 +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.ui.NotesPreferenceActivity; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + + +public class GTaskClient {//实现GTask的登录以及创建GTask任务和任务列表,从网络上获取任务内容 + private static final String TAG = GTaskClient.class.getSimpleName();//设置本类的TAG,作用是日志的打印输出和标识此类 + + private static final String GTASK_URL = "https://mail.google.com/tasks/";//谷歌邮箱的URL + + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";//传递URL + + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";//发布的URL + + private static GTaskClient mInstance = null;//后续使用的参数以及变量 + + private DefaultHttpClient mHttpClient;//网络地址客户端 + + private String mGetUrl;//构造函数:初始化各属性 + + private String mPostUrl; + + private long mClientVersion; + + private boolean mLoggedin; + + private long mLastLoginTime; + + private int mActionId; + + private Account mAccount; + + private JSONArray mUpdateArray; + + private GTaskClient() {//初始化客户端 + mHttpClient = null;//初始化 + mGetUrl = GTASK_GET_URL;//初始化 + mPostUrl = GTASK_POST_URL;//初始化 + mClientVersion = -1;//初始化 + mLoggedin = false;//初始化 + mLastLoginTime = 0;//初始化 + mActionId = 1;//初始化 + mAccount = null;//初始化 + mUpdateArray = null;//初始化 + } + + public static synchronized GTaskClient getInstance() {//getInstance函数,在java中,可以使用这种方式使用单例模式创建类的实例 + if (mInstance == null) {//若无实例 + mInstance = new GTaskClient();//则新建一个 + } + return mInstance; + } + + public boolean login(Activity activity) {//用于实现登录的方法,以activity作为参数 + // we suppose that the cookie would expire after 5 minutes + // then we need to re-login + final long interval = 1000 * 60 * 5;//间隔5分钟时间 + if (mLastLoginTime + interval < System.currentTimeMillis()) {//距离上次登录的时间间隔,若超过5分钟,则重置登录状态 + mLoggedin = false;//超过规定时间,则需要重新登录 + } + + // need to re-login after account switch + if (mLoggedin + && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity + .getSyncAccountName(activity))) {//在登陆成功的情况下检测到用户名和密码不匹配时登录失败 + mLoggedin = false;//如果没超过时间,则不需要重新登录 + } + + if (mLoggedin) {//如果登录的时候符合上面的要求则让其显示已登录登陆 + Log.d(TAG, "already logged in");//显示已登录 + return true;//不需要再次登录 + } + + mLastLoginTime = System.currentTimeMillis();//更新登录时间为系统当前时间 + String authToken = loginGoogleAccount(activity, false);//用用户提供的账户密码去登陆Google账户 + if (authToken == null) {//登录失败的情况 + Log.e(TAG, "login google account failed");//显示登录谷歌失败 + return false;//谷歌登录失败 + } + + // login with custom domain if necessary + if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() + .endsWith("googlemail.com"))) {//在google账号登陆成功后,尝试能否使用用户的域名登陆gtask + StringBuilder url = new StringBuilder(GTASK_URL).append("a/");//append()函数是在文本末添加,这里用于新建一个url + int index = mAccount.name.indexOf('@') + 1;//返回@第一次出现的位置并把位置+1后记录在index里 + String suffix = mAccount.name.substring(index);//substring() 方法用于提取字符串中介于两个指定下标之间的字符 + url.append(suffix + "/");//均为字符串操作来构建url链接 + mGetUrl = url.toString() + "ig";//设置用户的getUrl + mPostUrl = url.toString() + "r/ig";//设置用户postURL + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true;//若不能使用用户域名登录,使用google官方url登录 + } + } + + // try to login with google official url + if (!mLoggedin) {//若前面的尝试失败,则尝试使用官方的域名登陆 + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) {//第二次登录失败 + return false;//返回false + } + } + + mLoggedin = true;//如果没有报错说明登录成功 + return true; + } + + private String loginGoogleAccount(Activity activity, boolean invalidateToken) {//用以具体实现登录Google账号的方法,方法返回账号令牌 + String authToken;//使用登录令牌核实用户信息和判断登录状态 + AccountManager accountManager = AccountManager.get(activity);//账户管理器集中管理已注册账号和密码 + Account[] accounts = accountManager.getAccountsByType("com.google");//将所有以com.google结尾的账号存入accounts数组中 + + if (accounts.length == 0) {//如果没有这样的账号 + Log.e(TAG, "there is no available google account");//信息无有效的google账户 + return null;//显示没有谷歌账户 + } + + String accountName = NotesPreferenceActivity.getSyncAccountName(activity);//匹配活动的账户里是否存在账户 + Account account = null;//遍历account数组,寻找已登录过的信息 + for (Account a : accounts) {//如果找到匹配的就记下来,下次用于自动登录 + if (a.name.equals(accountName)) {//没找到,输出例外信息 + account = a; + break; + } + } + if (account != null) {//若存在 + mAccount = account;//把这个账户记在mAccount中 + } else {//不存在 + Log.e(TAG, "unable to get an account with the same name in the settings");//显示“不能在设置中获取相同名字的账户” + return null;//若遍历完之后仍没有用户名匹配的账号,则直接返回 + } + + // get the token now + AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, + "goanna_mobile", null, activity, null, null);//从账户中获取目标账户的令牌 + try { + Bundle authTokenBundle = accountManagerFuture.getResult();//bundle是一个key-value对,这里获取目标账户的最终结果集 + authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);//这里获取bundle类的对象的String串并赋值给令牌对象 + if (invalidateToken) {//如果是非法的令牌 + accountManager.invalidateAuthToken("com.google", authToken);//删除存储AccountManager中此账号类型对应的authToken缓存 + loginGoogleAccount(activity, false);//登录账号失败 + } + } catch (Exception e) {//捕捉令牌获取失败的异常 + Log.e(TAG, "get auth token failed");//获取令牌失败 + authToken = null; + } + + return authToken; + } + + private boolean tryToLoginGtask(Activity activity, String authToken) {//用于判断令牌对于登陆gtask账号是否有效 + if (!loginGtask(authToken)) { + // maybe the auth token is out of date, now let's invalidate the + // token and try again + authToken = loginGoogleAccount(activity, true); + if (authToken == null) {//再次获取令牌失败 + Log.e(TAG, "login google account failed");//错误,登录 google 帐户失败 + return false; + } + + if (!loginGtask(authToken)) {//重新获取的令牌再次失效 + Log.e(TAG, "login gtask failed");//错误,登录 gtask 失败 + return false; + } + } + return true; + } + + private boolean loginGtask(String authToken) {//登录gtask的实现函数,根据令牌判断是否登陆成功 + int timeoutConnection = 10000;//连接超时为10000毫秒,即10秒 + int timeoutSocket = 15000;//端口超时为15000毫秒,即15秒 + HttpParams httpParameters = new BasicHttpParams();//申请一个httpParameters参数类的对象 + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);//设置连接超时的时间 + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);//设置端口超时的时间 + mHttpClient = new DefaultHttpClient(httpParameters);//新建一个默认客户端 + BasicCookieStore localBasicCookieStore = new BasicCookieStore();//用来存储本地的cookie值 + mHttpClient.setCookieStore(localBasicCookieStore);//为新建客户端设置cookie存储 + HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);//setUseExpectContinu方法:设置http协议1.1中的一个header属性Expect 100 Continue + + // login gtask + try { + String loginUrl = mGetUrl + "?auth=" + authToken; + HttpGet httpGet = new HttpGet(loginUrl);//通过令牌与原始的url设置登录的URL + HttpResponse response = null;//通过HttpGet向服务器申请 + response = mHttpClient.execute(httpGet);//执行execute()方法之后会返回一个HttpResponse对象,服务器所返回的所有信息就保护在HttpResponse里面 + + // get the cookie now + List cookies = mHttpClient.getCookieStore().getCookies();//获取cookie + boolean hasAuthCookie = false;//hasauthcookie默认为false + for (Cookie cookie : cookies) {//遍历cookies集合中的每个Cookie对象 + if (cookie.getName().contains("GTL")) {//验证cookie信息,这里通过GTL标志来验证 + hasAuthCookie = true; + } + } + if (!hasAuthCookie) {//若这是未授权的Cookie值 + Log.w(TAG, "it seems that there is no auth cookie");//显示没有授权的cookie + } + + // get the client version + String resString = getResponseContent(response.getEntity());//获取客户端的内容 + String jsBegin = "_setup(";//取得的内容是html语言,截取内容为"_setup(" 与 ")}" 中间的部分 + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin);//获取jsbegin中begin的序号 + int end = resString.lastIndexOf(jsEnd);//获取jsend中end的序号 + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + mClientVersion = js.getLong("v");//设置客户端版本 + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + return false; + } catch (Exception e) {//捕获所有的异常 + // simply catch all exceptions + Log.e(TAG, "httpget gtask_url failed");//截取失败打印日志 + return false; + } + + return true; + } + + private int getActionId() {//获取动作的id号码 + return mActionId++; + } + + private HttpPost createHttpPost() { + HttpPost httpPost = new HttpPost(mPostUrl);//创建一个httppost对象,用来保存URL + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");//设置httppost对象的头部 + httpPost.setHeader("AT", "1"); + return httpPost; + } + + private String getResponseContent(HttpEntity entity) throws IOException {//使用getContentEncoding()获取网络上的资源和数据 + String contentEncoding = null; + if (entity.getContentEncoding() != null) {//如果获取的内容编码不为空 + contentEncoding = entity.getContentEncoding().getValue();//给contentEncoding赋值 + Log.d(TAG, "encoding: " + contentEncoding);//打印日志“encoding:内容编码” + } + + InputStream input = entity.getContent(); + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {//如果contentEncoding不为null且contentEncoding与"gzip"相等 + input = new GZIPInputStream(entity.getContent());//使用GZIPInputStream对entity内容进行GZIP解压,把值赋给InputStream类对象input + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {//DEFLATE是一个无专利的压缩算法,它可以实现无损数据压缩 + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater);//实现了一个流过滤器,用于以“deflate”压缩格式解压缩数据 + } + + try { + InputStreamReader isr = new InputStreamReader(input);//InputStreamReader类:是从字节流到字符流的桥接器,它使用指定的字符集读取字节并将它们解码为字符 + BufferedReader br = new BufferedReader(isr);//缓存读取类,用于快速的读缓存操作 + StringBuilder sb = new StringBuilder(); + + while (true) {//将BufferedReader类br的内容逐行读取并存储StringBuilder类的sb中。然后返回sb的string格式。 + String buff = br.readLine(); + if (buff == null) { + return sb.toString(); + } + sb = sb.append(buff); + } + } finally { + input.close(); + } + } + + private JSONObject postRequest(JSONObject js) throws NetworkFailureException {//利用JSON发送请求,返回获取的内容 + if (!mLoggedin) {//用户未登录 + Log.e(TAG, "please login first");//显示请先登录 + throw new ActionFailureException("not logged in");//抛出异常没有登录 + } + + HttpPost httpPost = createHttpPost();//实例化一个httpPost的对象用来向服务器传输数据,发送在js里请求的内容 + try { + LinkedList list = new LinkedList();//LinkedList 类:是一个继承于AbstractSequentialList的双向链表 + list.add(new BasicNameValuePair("r", js.toString())); + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");//普通的键值对"UTF-8" + httpPost.setEntity(entity);//向httpPost对象中添加参数 + + // execute the post + HttpResponse response = mHttpClient.execute(httpPost);//执行请求 + String jsString = getResponseContent(response.getEntity()); + return new JSONObject(jsString); + + } catch (ClientProtocolException e) {//下面的四个catch是对4种失败情况的处理和抛出异常 + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed");//post请求失败 + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("unable to convert response content to jsonobject"); + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("error occurs when posting request"); + } + } + + public void createTask(Task task) throws NetworkFailureException {//创建单个任务 + commitUpdate();//提交更新 + try { + JSONObject jsPost = new JSONObject();//用JSON获取Task中的内容,并创建相应的jspost + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(task.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);//将要提交的内容放入jsPost对象中 + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);//client的版本号 + + // post + JSONObject jsResponse = postRequest(jsPost);//通过postRequest获取任务的返回信息 + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));//设置任务的id + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("create task: handing jsonobject failed"); + } + } + + public void createTaskList(TaskList tasklist) throws NetworkFailureException {//创建一个任务列表 + commitUpdate();//提交后更新 + try { + JSONObject jsPost = new JSONObject();//操作列表 + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(tasklist.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);////客户端版本 + + // post + JSONObject jsResponse = postRequest(jsPost);//post操作 + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("create tasklist: handing jsonobject failed"); + } + } + + public void commitUpdate() throws NetworkFailureException {//同步更新操作 + if (mUpdateArray != null) {//更新列表不为空 + try { + JSONObject jsPost = new JSONObject();//更新数据 + + // action_list + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null;//更新处理完毕,列表置空 + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("commit update: handing jsonobject failed"); + } + } + } + + public void addUpdateNode(Node node) throws NetworkFailureException {//添加更新节点 + if (node != null) {//如果node节点不是空的 + // too many update items may result in an error + // set max to 10 items + if (mUpdateArray != null && mUpdateArray.length() > 10) {//判断现在mUpdateArray是否是空的或者满的 + commitUpdate();//提交更新 + } + + if (mUpdateArray == null)//空的则新建一个数组 + mUpdateArray = new JSONArray(); + mUpdateArray.put(node.getUpdateAction(getActionId()));//将更新节点加入列表 + } + } + + public void moveTask(Task task, TaskList preParent, TaskList curParent) + throws NetworkFailureException {//把任务移动到指定的列表中 + commitUpdate();//提交更新数据 + try {//创建新的jsonobject + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); + if (preParent == curParent && task.getPriorSibling() != null) {//当移动发生在同一个任务列表中且该移动不是第一个时 + // put prioring_sibing_id only if moving within the tasklist and + // it is not the first one + action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());//设置优先级ID + } + action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());//设置当前列表 + action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); + if (preParent != curParent) {//在不同列表间移动时,放入目标列表中去 + // put the dest_list only if moving between tasklists + action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); + } + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);//用户版本 + + postRequest(jsPost);//postRequst()进行更新后的发送 + + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("move task: handing jsonobject failed"); + } + } + + public void deleteNode(Node node) throws NetworkFailureException {//删除节点操作 + commitUpdate(); + try { + JSONObject jsPost = new JSONObject();//新建jsPost,把除了node的其他节点都放入jsPost,并提交 + JSONArray actionList = new JSONArray(); + + // action_list + node.setDeleted(true);//把要删除的node的成员变量的删除属性设置为true + actionList.put(node.getUpdateAction(getActionId()));//将该节点要更新的操作的id加入操作列表 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) {//异常处理机制 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("delete node: handing jsonobject failed"); + } + } + + public JSONArray getTaskLists() throws NetworkFailureException {//获取所有任务列表 + if (!mLoggedin) {//如果没有登录 + Log.e(TAG, "please login first");//显示请先登录 + throw new ActionFailureException("not logged in");//抛出异常 + } + + try { + HttpGet httpGet = new HttpGet(mGetUrl);//通过GetURI使用getResponseContent从网上获取初步的数据 + HttpResponse response = null;//初始化Httpresponse (回复)为空 + response = mHttpClient.execute(httpGet);//用httpGet作消息发给客户端执行并回复消息 + + // get the task list + String resString = getResponseContent(response.getEntity());//获取任务列表 + String jsBegin = "_setup(";//把字符串截取并放入jsStrIng + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS);//获取GTASK_JSON_LISTS + } catch (ClientProtocolException e) {//捕捉httpget失败异常 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (IOException e) {//异常处理 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (JSONException e) {//异常处理 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("get task lists: handing jasonobject failed"); + } + } + + public JSONArray getTaskList(String listGid) throws NetworkFailureException {//获取任务列表 + commitUpdate(); + try { + JSONObject jsPost = new JSONObject();//设置为传入的listGid + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,//通过action.put()对JSONObject对象action添加元素 + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); + action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);//通过jsPost.put()对jsPost添加相关元素 + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + JSONObject jsResponse = postRequest(jsPost);//通过postRequest()提交更新后的请求并返回一个JSONObject的对象 + return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);//使用jsResponse.getJSONArray( )获取jsResponse中的JSONArray值并作为函数返回值 + } catch (JSONException e) {//异常处理 + Log.e(TAG, e.toString());//获取异常类型和异常详细消息 + e.printStackTrace();//在命令行打印异常信息在程序中出错的位置及原因 + throw new ActionFailureException("get task list: handing jsonobject failed"); + } + } + + public Account getSyncAccount() {//获取同步账户 + return mAccount;//同步账户 + } + + public void resetUpdateArray() {//重置更新内容 + mUpdateArray = null;//更新内容 + } +} diff --git a/src/ui包.docx b/src/ui包.docx new file mode 100644 index 0000000..2820ada Binary files /dev/null and b/src/ui包.docx differ diff --git a/src/widget包.docx b/src/widget包.docx new file mode 100644 index 0000000..e9f6a6e Binary files /dev/null and b/src/widget包.docx differ diff --git a/小米便签泛读(1)(1).docx b/小米便签泛读(1)(1).docx new file mode 100644 index 0000000..3a8c0fb Binary files /dev/null and b/小米便签泛读(1)(1).docx differ