diff --git a/jingdu.docx b/jingdu.docx new file mode 100644 index 0000000..a9f49e9 Binary files /dev/null and b/jingdu.docx differ diff --git a/src/notes/gtask/data/MetaData.java b/src/notes/gtask/data/MetaData.java new file mode 100644 index 0000000..c23bc0f --- /dev/null +++ b/src/notes/gtask/data/MetaData.java @@ -0,0 +1,82 @@ +/* + * 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;//package是包名,继承于gtask,用于记录数据变化// + +import android.database.Cursor;//引用基于数据库服务游标的类// +import android.util.Log;//引用日志输出工具类// + +import net.micode.notes.tool.GTaskStringUtils;// 导入tool包的GTaskStringUtils工具类// + +import org.json.JSONException;//Json使用失败异常处理// +import org.json.JSONObject;//包:jsonObject就是常说的json,是一种重要的数据传输对象。其格式为{“key1":value1."key2":value2,......};key必须是字符串,内部封装了一个函数用来储存json对象// + + +public class MetaData extends Task { + 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); + } catch (JSONException e) { + Log.e(TAG, "failed to put related gid"); + }// 捕捉异常并进行异常处理放入TAG// + setNotes(metaInfo.toString()); + setName(GTaskStringUtils.META_NOTE_NAME); + } + + public String getRelatedGid() { + return mRelatedGid; + }//获取相关Gid// + + @Override + public boolean isWorthSaving() { + return getNotes() != null; + }//判断是否值得存放,即当前数据是否有效,若数据非空则返回真值。// + + @Override + public void setContentByRemoteJSON(JSONObject js) { + super.setContentByRemoteJSON(js);//如果是继承的方法,是没有必要使用 super 来调用,直接即可调用。但如果子类覆盖或重写了父类的方法,则只有使用 super 才能在子类中调用父类中的被重写的方法// + if (getNotes() != null) { + try {//捕捉异常,获取关联 Gid 失败// + JSONObject metaInfo = new JSONObject(getNotes().trim());//创建新json对象,getnotes返回值mNotes调用trim方法去掉首尾空格// + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);//获取关联gid,且为字符串类型// + } catch (JSONException e) { + Log.w(TAG, "failed to get related gid"); + mRelatedGid = null; + }//用catch进行异常处理,并输出警告信息// + } + }// 功能描述:使用远程json数据对象设置元数据内容,实现过程:调用父类Task中的setContentByRemoteJSON ()函数// + + @Override + public void setContentByLocalJSON(JSONObject js) { + // this function should not be called + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + }//使用本地json数据对象设置元数据内容,一般不会用到,若用到,则抛出异常// + + @Override + public JSONObject getLocalJSONFromContent() { + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + }//从元数据内容中获取本地json对象,一般不会用到,若用到,则抛出异常// + + @Override + public int getSyncAction(Cursor c) { + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + }//获取同步动作状态,一般不会用到,若用到,则抛出异常 + +}//新建一个继承Task类的MetaData类,该类主要用于记录数据的变化,作为元数据类描述数据属性的信息。// diff --git a/src/notes/gtask/data/Node.java b/src/notes/gtask/data/Node.java new file mode 100644 index 0000000..205cdae --- /dev/null +++ b/src/notes/gtask/data/Node.java @@ -0,0 +1,100 @@ +/* + * 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 org.json.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;//同步出现冲突// + + public static final int SYNC_ACTION_ERROR = 8;//同步出现错误// + + private String mGid;//记录最后一次修改时间// + + private String mName;//bool类型,表明表征是否被删除// + + private long mLastModified;//声明long类型,表示记录最后行为时间// + + private boolean mDeleted;//判断 表征是否被删除// + + public Node() { + mGid = null; + mName = ""; + mLastModified = 0; + mDeleted = false; + }/*构造函数,进行初始化,界面没有,名字为空,最后一次修改时间为0(没有修改),表征是否删除。*/ + + public abstract JSONObject getCreateAction(int actionId);//引用一个抽象的类,下同不再注释// + + public abstract JSONObject getUpdateAction(int actionId); + + public abstract void setContentByRemoteJSON(JSONObject js); + + public abstract void setContentByLocalJSON(JSONObject js); + + public abstract JSONObject getLocalJSONFromContent(); + + public abstract int getSyncAction(Cursor c); + + public void setGid(String gid) { + this.mGid = gid; + }//以下几个函数都是对于上面的公共类Node的里的变量进行赋值和修改。// + + public void setName(String name) { + this.mName = name; + }//设置名称// + + public void setLastModified(long lastModified) { + this.mLastModified = lastModified; + }//设置最近修改时间标识// + + public void setDeleted(boolean deleted) { + this.mDeleted = deleted; + }//设置删除标识// + + public String getGid() { + return this.mGid; + }//获取Gid// + + public String getName() { + return this.mName; + }//获取名称// + + public long getLastModified() { + return this.mLastModified; + }//获取最近创建时间标识// + + public boolean getDeleted() { + return this.mDeleted; + }//获取删除标识// + +}//这里是一个类,用于建立node类来提供模板,设置各种参数及定义各种函数,会在别的地方用到,定义了各种同步活动的标识码。// diff --git a/src/notes/gtask/data/SqlData.java b/src/notes/gtask/data/SqlData.java new file mode 100644 index 0000000..0666ad1 --- /dev/null +++ b/src/notes/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;//Java中除注释外的第一行代码,将SplData类置于net.micode.notes.gtask.data类库单元中,以后使用者想要使用SQLData类时,需要import加net.micode.notes.gtask.data.SqlData// + +import android.content.ContentResolver; +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; +import org.json.JSONObject; + + +public class SqlData {//类:数据库中的基本数据// + private static final String TAG = SqlData.class.getSimpleName();//调用getSimpleName ()函数得到类简称存入字符串TAG中// + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3//获得数据列id,mime类型,内容,1类型数据,3类型数据// + };//新建一个字符串数组,集合了 interface DataColumns 中所有SF常量// + + 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;//同上// + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + + private ContentResolver mContentResolver;//代码块:定义的一些私有全局变量,可以与sqlNote中的变量相对应分析,定义以下8个内部变量// + + private boolean mIsCreate; + + private long mDataId; + + private String mDataMimeType; + + private String mDataContent; + + private long mDataContentData1; + + private String mDataContentData3; + + private ContentValues mDiffDataValues; + + public SqlData(Context context) { + mContentResolver = context.getContentResolver();//getContentResolver()获取ContentResovler对象,如果需要查询数据,就直接可以在mContentResolver上操作// + mIsCreate = true; + mDataId = INVALID_ID; + mDataMimeType = DataConstants.NOTE; + mDataContent = ""; + mDataContentData1 = 0; + mDataContentData3 = ""; + mDiffDataValues = new ContentValues(); + }//第一种SQLData的构造方式,只从上下文获取,初始化其中的变量// + + public SqlData(Context context, Cursor c) { + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDiffDataValues = new ContentValues(); + }//第二种SqlData的构造方式,通过cursor来获取数据// + + private void loadFromCursor(Cursor c) { + mDataId = c.getLong(DATA_ID_COLUMN); + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + mDataContent = c.getString(DATA_CONTENT_COLUMN); + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + }//从光标c处加载数据,帮助实现SqlData的第二种构造,将5列的数据赋给该类的对象// + + public void setContent(JSONObject js) throws JSONException { + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;/*如果传入的JSONObject对象有DataColumns.ID这一项,则设置dataID为这个ID,否则设为INVALID_ID*/ + if (mIsCreate || mDataId != dataId) { + mDiffDataValues.put(DataColumns.ID, dataId); + } + mDataId = dataId; + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE;/*如果传入的JSONObject对象有DataColumns.MIME_TYPE一项,则设置dataMimeType为这个,否则设为SqlData.java*/ + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); + } + mDataMimeType = dataMimeType; + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + if (mIsCreate || !mDataContent.equals(dataContent)) { + mDiffDataValues.put(DataColumns.CONTENT, dataContent); + }//代码块:对比DataContent,并更新contentValue中的DataContent// + mDataContent = dataContent; + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;/*如果传入的JSONObject对象有DataColumn.DATA1一项,那么将其获取,否则。将其设置为0。*/ + if (mIsCreate || mDataContentData1 != dataContentData1) { + mDiffDataValues.put(DataColumns.DATA1, dataContentData1); + } + mDataContentData1 = dataContentData1; + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { + mDiffDataValues.put(DataColumns.DATA3, dataContentData3); + } + mDataContentData3 = dataContentData3; + }//设置用于共享的数据,并提供异常抛出与处理机制,其中很多if 条件语句的判断,某些条件下某些特定的操作// + + public JSONObject getContent() throws JSONException { + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + JSONObject js = new 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); + return js; + }//获取共享的数据内容,并提供异常抛出与处理机制// + public void commit(long noteId, boolean validateVersion, long version) { + + if (mIsCreate) { + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { + mDiffDataValues.remove(DataColumns.ID); + }//判断是否是第一种SqlData构造方式// + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);//在note的资源标识下加入data数据// + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1));//上一句实现的是URI到Uri的转换将路径转换为Long型,附识给当前id// + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + }//如果转换出错则日志中显示错误“获取note的ID出错”// + } else { + if (mDiffDataValues.size() > 0) {//若共享数据存在,则通过内容解析器更新关于新URI的共享数据// + int result = 0; + 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; + }//commit 函数用于把当前所做的修改保存到数据库// + + public long getId() { + return mDataId; + }//获取当前id// +} diff --git a/src/notes/gtask/data/SqlNote.java b/src/notes/gtask/data/SqlNote.java new file mode 100644 index 0000000..80768b2 --- /dev/null +++ b/src/notes/gtask/data/SqlNote.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.tool.ResourceParser; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class SqlNote { + private static final String TAG = SqlNote.class.getSimpleName();////调用getSimpleName ()函数得到类的简写名称存入字符串TAG中//// + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_NOTE = new String[] { + NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE, + NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, + NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, + NoteColumns.VERSION + };//集合了interface NoteColumns中所有17个SF常量// + + //以下设置17个列的编号,对应不同的属性// + public static final int ID_COLUMN = 0;//通知日期列// + + public static final int ALERTED_DATE_COLUMN = 1;//提醒时间// + + public static final int BG_COLOR_ID_COLUMN = 2;//背景颜色// + + public static final int CREATED_DATE_COLUMN = 3;//创建时间// + + public static final int HAS_ATTACHMENT_COLUMN = 4;//有无附件// + + public static final int MODIFIED_DATE_COLUMN = 5;//修改时间// + + public static final int NOTES_COUNT_COLUMN = 6;//便签数// + + public static final int PARENT_ID_COLUMN = 7;//父节点ID// + + public static final int SNIPPET_COLUMN = 8;//文本片段// + + public static final int TYPE_COLUMN = 9;//文件类型// + + public static final int WIDGET_ID_COLUMN = 10;//窗口小部件ID// + + public static final int WIDGET_TYPE_COLUMN = 11;//小部件种类// + + public static final int SYNC_ID_COLUMN = 12;//同步ID// + + public static final int LOCAL_MODIFIED_COLUMN = 13;//本地修改的符号// + + public static final int ORIGIN_PARENT_ID_COLUMN = 14;//原始文件夹ID// + + public static final int GTASK_ID_COLUMN = 15;//用户ID// + + public static final int VERSION_COLUMN = 16;//版本号// + + //以下定义了17个内部变量,其中12个可以由content获得,5个需要初始化为0或者new// + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mId;//通过ArrayList记录note中的data// + 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; + + //以下定义了三种构造SqlNote的方法,分别需要不同的参数// + public SqlNote(Context context) { + mContext = context;//获取context程序间共享数据// + mContentResolver = context.getContentResolver(); + mIsCreate = true;//第一种构造方式的标识,对象是新建的// + mId = INVALID_ID;//无效ID// + mAlertDate = 0; + mBgColorId = ResourceParser.getDefaultBgId(context);//系统默认背景// + mCreatedDate = System.currentTimeMillis();//调用系统函数获取创建时间// + mHasAttachment = 0; + mModifiedDate = System.currentTimeMillis();//调用系统函数最近修改时间// + mParentId = 0; + mSnippet = ""; + mType = Notes.TYPE_NOTE; + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + mOriginParent = 0; + mVersion = 0; + mDiffNoteValues = new ContentValues();//新建一个NoteValues值,用来记录改变的values// + mDataList = new ArrayList();//新建一个data的列表// + }//构造函数参数只有context,对所有的变量进行初始化// + + public SqlNote(Context context, Cursor c) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false;//第二种方式标识为false,对象已存在,不是新建的// + loadFromCursor(c); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent();//如果是note类型,则调用下面的 loadDataContent()函数,加载数据内容// + mDiffNoteValues = new ContentValues(); + }//构造函数有context和一个数据库的cursor两个参数,多数变量通过cursor指向的一条记录直接进行初始化// + + public SqlNote(Context context, long id) { + mContext = context;//将mIsCreate定义为False作为标识,以id为参数,运行loadFromCursor函数// + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(id); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + + }//第三种构造方式,采用context和id// + + private void loadFromCursor(long id) { + Cursor c = null; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(id) + }, null);//通过try避免异常// + if (c != null) { + c.moveToNext(); + loadFromCursor(c); + } else { + Log.w(TAG, "loadFromCursor: cursor = null"); + }//代码块:如果有内容就将移入文档,并再次等待光标的内容,否则报错// + } finally { + if (c != null) + c.close(); + }//执行完后关闭释放// + }//通过id从cursor加载数据// + + private void loadFromCursor(Cursor c) { + mId = c.getLong(ID_COLUMN); + mAlertDate = c.getLong(ALERTED_DATE_COLUMN); + mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = c.getLong(CREATED_DATE_COLUMN); + mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); + mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); + mParentId = c.getLong(PARENT_ID_COLUMN); + mSnippet = c.getString(SNIPPET_COLUMN); + mType = c.getInt(TYPE_COLUMN); + mWidgetId = c.getInt(WIDGET_ID_COLUMN); + mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); + mVersion = c.getLong(VERSION_COLUMN); + }//通过游标从光标处加载数据// + + private void loadDataContent() { + Cursor c = null;//通过content获取共享数据并加载到数据库当前游标处// + mDataList.clear(); + try { + c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, + "(note_id=?)", new String[] { + String.valueOf(mId) + }, null);//获取ID对应content内容// + if (c != null) { + if (c.getCount() == 0) { + Log.w(TAG, "it seems that the note has not data"); + return; + }//查询到该note的id确实有对应项,即cursor有对应// + while (c.moveToNext()) { + SqlData data = new SqlData(mContext, c);//将获取数据存入数据表// + mDataList.add(data); + }//记录数量不为0,则循环直到记录不存在,不断地取出记录放到DataList中// + } else { + Log.w(TAG, "loadDataContent: cursor = null"); + } + } finally { + if (c != null) + c.close(); + }//最后若游标不为空,关闭游标并释放// + }/*获取ID对应content内容,如果查询到该note的id确实有对应项,即cursor有对应,获取ID对应content内容*/ + + /*设置通过content机制共享的数据信息*/ + public boolean setContent(JSONObject js) { + 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); + }//如果SQLNote采用的是第一种构造方式,或者snippet为空,则将snippet这一项键值存入contentvalue中,尽管是“”// + mSnippet = snippet; + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE;// 获取数据类型,以下操作都和上面对snippet的操作一样,一起根据共享的数据设置SqlNote内容的上述17项// + if (mIsCreate || mType != type) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + }//如果是新建的或 type 不匹配// + mType = type; + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_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) { + mDiffNoteValues.put(NoteColumns.ID, id); + }//如果只是通过上下文对note进行数据库操作,或者该ID与原ID不相同// + mId = id; + + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note + .getLong(NoteColumns.ALERTED_DATE) : 0;//获取提醒时间// + if (mIsCreate || mAlertDate != alertDate) { + mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); + }//如果只是通过上下文对note进行数据库操作,或者该提醒日期与原提醒日期不相同,// + mAlertDate = alertDate; + + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note + .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);//获取背景颜色// + if (mIsCreate || mBgColorId != bgColorId) { + mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); + }//同上,此处为背景颜色// + mBgColorId = bgColorId; + + long createDate = note.has(NoteColumns.CREATED_DATE) ? note + .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();//获取数据的创建日期,// + if (mIsCreate || mCreatedDate != createDate) { + mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); + }//同上,此处为创建日期// + mCreatedDate = createDate; + + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note + .getInt(NoteColumns.HAS_ATTACHMENT) : 0; + if (mIsCreate || mHasAttachment != hasAttachment) { + mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); + }//同上,此处为有无附件// + mHasAttachment = hasAttachment; + + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note + .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); + if (mIsCreate || mModifiedDate != modifiedDate) { + mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); + }//同上,此处为修改时间// + mModifiedDate = modifiedDate; + + long parentId = note.has(NoteColumns.PARENT_ID) ? note + .getLong(NoteColumns.PARENT_ID) : 0; + if (mIsCreate || mParentId != parentId) { + mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); + }//同上,此处为父类ID// + mParentId = parentId; + + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.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) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + }//同上,此处为文件类型// + mType = type; + + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) + : AppWidgetManager.INVALID_APPWIDGET_ID; + if (mIsCreate || mWidgetId != widgetId) { + mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); + }//同上,此处为小部件ID// + mWidgetId = widgetId; + + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note + .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; + if (mIsCreate || mWidgetType != widgetType) { + mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); + }//同上,此处为部件类型// + mWidgetType = widgetType; + + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note + .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; + if (mIsCreate || mOriginParent != originParent) { + mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); + }//同上,此处为将该原始父文件夹ID覆盖原原始父文件夹ID// + mOriginParent = originParent; + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i);//依次获取数据表中的数据ID// + SqlData sqlData = null; + if (data.has(DataColumns.ID)) { + long dataId = data.getLong(DataColumns.ID); + for (SqlData temp : mDataList) { + if (dataId == temp.getId()) { + sqlData = temp; + } + }// 该数据ID对应的数据如果存在,将对应的数据存在数据库中// + }//遍历 dataArray,查找 id 为 dataId 的数据// + + if (sqlData == null) { + sqlData = new SqlData(mContext); + mDataList.add(sqlData); + }/*如果数据库没有更新, 就根据上下文创建一个数据库数据,并添加到数据列表中*/ + + sqlData.setContent(data);// 最后为数据库数据进行设置// + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + }//出现JSONException时,日志显示错误,同时打印堆栈轨迹// + return true; + } +//获取content机制提供的数据并加载到note中// + public JSONObject getContent() { + try { + JSONObject js = new JSONObject(); + + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + }//采用的是第一种构造方式,自然实施初始化而已,显示错误:没创建数据库// + + JSONObject note = new JSONObject();//新建变量note用于传输共享数据// + if (mType == Notes.TYPE_NOTE) {//如果对象的类型是note类型,设置以上12个内部变量// + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.ALERTED_DATE, mAlertDate); + note.put(NoteColumns.BG_COLOR_ID, mBgColorId); + note.put(NoteColumns.CREATED_DATE, mCreatedDate); + note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment); + note.put(NoteColumns.MODIFIED_DATE, mModifiedDate); + note.put(NoteColumns.PARENT_ID, mParentId); + note.put(NoteColumns.SNIPPET, mSnippet); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.WIDGET_ID, mWidgetId); + note.put(NoteColumns.WIDGET_TYPE, mWidgetType); + note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + + JSONArray dataArray = new JSONArray(); + for (SqlData sqlData : mDataList) { + JSONObject data = sqlData.getContent(); + if (data != null) { + dataArray.put(data); + }//将note中的所有数据存进dataarray中, + }//利用循环将数据链表的数据获取// + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.SNIPPET, mSnippet); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + }//如果是文件夹或者系统文件,那么只存放id,种类,摘要和note// + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + }//捕获json类型异常,显示错误,打印堆栈痕迹// + return null; + } + + public void setParentId(long id) { + mParentId = id; + mDiffNoteValues.put(NoteColumns.PARENT_ID, id); + }//给当前id设置父id// + + public void setGtaskId(String gid) { + mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); + }//给当前id设置Gtaskid// + + public void setSyncId(long syncId) { + mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); + }//给当前id设置同步id// + + public void resetLocalModified() { + mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); + }//重新设置本地的修改// + + public long getId() { + return mId; + }//获取当前ID// + + public long getParentId() { + return mParentId; + }//获取当前ID的父类ID// + + public String getSnippet() { + return mSnippet; + }//获取小片段即用于显示的部分便签内容// + + public boolean isNoteType() { + return mType == Notes.TYPE_NOTE; + }//判断是否为便签类型// + + //commit函数用于把当前造作所做的修改保存到数据库// + public void commit(boolean validateVersion) { + if (mIsCreate) {//如果是第一种构造方式// + if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { + mDiffNoteValues.remove(NoteColumns.ID); + }//如果是一个无效的id并且还含有这个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()); + throw new ActionFailureException("create note failed"); + } //捕获异常,转换出错,显示错误“获取note的id出现错误”// + if (mId == 0) { + throw new IllegalStateException("Create thread id failed"); + }//创建线程 id 失败// + + if (mType == Notes.TYPE_NOTE) { + for (SqlData sqlData : mDataList) { + sqlData.commit(mId, false, -1); + } + }//对于note类型,引用sqlData.commit方法操作// + } else { + if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { + Log.e(TAG, "No such note"); + throw new IllegalStateException("Try to update note with invalid id"); + }//判断是否含有这个便签,不存在的话,尝试以无效 id 更新 note// + 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) + }); + }//如果是有效版本, 更新内容解析器:存入便签内容uri,便签ID,便签版本,mID,mVersion// + 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); + } + }//对note类型,还是对其中的data引用commit,从而实现目的// + } + + // refresh local info + loadFromCursor(mId);//通过 cursor 从当前 id 处加载数据// + if (mType == Notes.TYPE_NOTE) + loadDataContent();// 如果是便签类型:获取共享数据并加载到数据库// + + mDiffNoteValues.clear();//改变数据库构造模式// + mIsCreate = false;//改变数据库构造模式// + } +} diff --git a/src/notes/gtask/data/Task.java b/src/notes/gtask/data/Task.java new file mode 100644 index 0000000..5259d84 --- /dev/null +++ b/src/notes/gtask/data/Task.java @@ -0,0 +1,356 @@ +/* + * 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.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; +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;//以下四个变量用于Task构造,mCompleted判断是否完成// + + private String mNotes;//页面标签信息// + + private JSONObject mMetaInfo;//元数据信息// + + private Task mPriorSibling;//对应的优先兄弟类Task的指针// + + private TaskList mParent;//所在任务列表的指针// + + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + }//Task类的构造函数,对对象进行初始化// + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + //共享数据存入动作类型// + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);//存入当前task的指针// + + // 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();//新建一个 JSONObject 对象打包存放 name,creator id,type task// + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + }//如果存在 notes ,则将其也放入 entity 中// + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);//这里将entity变量(属于JSONObject类)作为一个数据存放进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);//更新列表,id存入父id// + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());//存入列表id// + + // prior_sibling_id + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + }//如果存在优先兄弟 task,则将其 id 放入 js 中// + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-create jsonobject"); + }//抛出异常处理机制// + + return js; + }//对操作号即actionId 进行一些操作的公用函数// + + //此函数和上一个getCreatAction()的功能差不多,一个是creat,一个updata,都是对action进行操作// + public JSONObject getUpdateAction(int actionId) { + 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) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + 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 task-update jsonobject"); + }//异常处理// + + return js; + } + + //通过云端传输的数据设置内容// + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + }//如果传入的任务变量不是空的,那就说明有任务,设置修改;否则不用执行// + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + }//设置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) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { + Log.e(TAG, "invalid type"); + return; + }/*如果js不存在或者js没有元数据的开头或者js指针没有元数据,那么反馈给用户出错信息*/ + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + }//遍历 dataArray 查找与数据库中DataConstants.NOTE 记录信息一致的 data// + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + }//异常处理操作,打印堆栈痕迹// + } + + //通过内容更新本地的jsonobject// + public JSONObject getLocalJSONFromContent() { + 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 对象并将其返回// + + 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++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + }//遍历 dataArray 查找与数据库中DataConstants.NOTE 记录信息一致的 data// + + 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; + }//抛出异常信息并将元数据信息置空// + }//如果元数据非空且其 notes 非空,则修改元数据类型信息// + }//设置元数据信息// + + //设置同步action// + public int getSyncAction(Cursor c) { + try { + JSONObject noteInfo = null; + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.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; + }//云端便签 id 已被删除,不存在,返回更新本地数据的同步行为// + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "remote note id seems to be deleted"); + return SYNC_ACTION_UPDATE_LOCAL; + }//便签 id 不匹配,返回更新本地数据的同步行为// + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.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()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + }/*判断修改后的ID匹配是否成功,成功则返回无同步操作,未成功则应用云端到本地,返回本地同步更新操作*/ + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + }//判断gtask的id与获取的id是否匹配// + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + }//本地id与云端id一致,即更新云端// + } + } 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; + }//设置是否完成的标志// + + public void setNotes(String notes) { + this.mNotes = notes; + }//设定是note成员变量// + + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling; + }//设置这个任务的优先兄弟// + + public void setParent(TaskList parent) { + this.mParent = parent; + }//设置父节点列表// + + public boolean getCompleted() { + return this.mCompleted; + }//获取 task 是否修改完毕的记录// + + public String getNotes() { + return this.mNotes; + }//获取成员变量 mNotes 的信息// + + public Task getPriorSibling() { + return this.mPriorSibling; + }//获取优先兄弟列表// + + public TaskList getParent() { + return this.mParent; + }//获取父节点列表// + +} diff --git a/src/notes/gtask/data/TaskList.java b/src/notes/gtask/data/TaskList.java new file mode 100644 index 0000000..3f8c8ed --- /dev/null +++ b/src/notes/gtask/data/TaskList.java @@ -0,0 +1,347 @@ +/* + * 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; + +//创建继承 Node的任务表类// +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName();//调用getSimpleName ()函数得到类的简称存入字符串TAG中// + + private int mIndex;//当前tasklist的指针// + + private ArrayList mChildren; + + public TaskList() { + super(); + mChildren = new ArrayList();//类中主要的保存数据的单元,用来实现一个以Task为元素的ArrayList// + mIndex = 1; + }//构造方法,调用父类构造方法,同时初始化自身特有元素// + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new 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对象,用于存放一些不同的数据。最后这个结构会被放入之前创建的js对象中,一起返回*/ + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } //指令类型,向js对象放入数据// + catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + return js; + }//接受新建action,返回jsonobject// + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + //初始化 js 中的数据// + 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()); + 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; + }//接受更新action,返回jsonobject// + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_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)); + } + + } //判断js对象是否为空,如果为空即没有内容就不需要进行设置了,若不是,进行设置// + catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + }//异常处理// + } + }//通过云端 JSON 数据设置实例化对象 js 的内容// + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + }//若 js 创建失败或 js 中不存在 META_HEAD_NOTE信息,警告// + + 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); + } 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(); + } + }//通过本地 JSON 数据设置对象 js 内容// + + public JSONObject getLocalJSONFromContent() { + try { + JSONObject js = new JSONObject();//创建一个 JSONObject 的实例化对象 js// + JSONObject folder = new JSONObject();//创建一个 JSONObject 的实例化对象 folder// + + String folderName = getName(); + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + folder.put(NoteColumns.SNIPPET, folderName);//如果这个文件名字是以"[MIUI_Notes]"开头,说明文件名字应该去掉这个前缀// + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);/*当获取的文件夹名称是以"Default"或"Call_Note开头,则为系统文件夹。否则为一般文件夹*/ + 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; + } + }//通过 Content 机制获取本地 JSON 数据// + + 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()) { + // no update both side + return SYNC_ACTION_NONE;//若本地记录未修改,.最近一次修改的 id 匹配成功,返回无的同步行为// + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL;//否则返回更新本地数据的同步行为// + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + }//如果ID未匹配,抛出错误,返回gtask ID未匹配// + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE; + }//如果是最近一次修改的 id,则返回云端更新的同步动作,更新成功// + } + } //通过游标获取同步信息// + catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + }//同步数据// + + public int getChildTaskCount() { + return mChildren.size(); + }//获得TaskList的大小,即mChildren的大小,mChildren 是TaskList 的一个实例// + + public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 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; + }//在当前任务表的指定位置添加新的任务,index是指针// + + public boolean removeChildTask(Task task) { + boolean ret = false;/*首先声明布尔类型ret为false,判断task'是否为空且是否在mChilldren中,并对父兄任务做出设置*/ + int index = mChildren.indexOf(task); + if (index != -1) { + ret = mChildren.remove(task); + + 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)); + }//代码块:删除成功后,要对任务列表进行更新// + }//index不等于-1,说明任务列表中存在该任务,就要进行删除,删除成功,task的上一个任务指针和父指针置空// + } + return ret; + }//.删除任务表中的子任务// + + public boolean moveChildTask(Task task, int 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));//不相等则进行删除和添加即移动操作// + }//将当前TaskList中含有的某个Task移到index位置// + + public Task findChildTaskByGid(String gid) { + for (int i = 0; i < mChildren.size(); i++) { + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null;//更具判断条件返回寻找结果// + }//按gid寻找Task,从头至尾遍历整个任务列表判断任务的gid与传入的gid是否相等// + + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); + }//返回指定Task的index// + + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + return mChildren.get(index); + }//返回指定gid的Task// + + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + 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/notes/model/Note.java b/src/notes/model/Note.java new file mode 100644 index 0000000..0475364 --- /dev/null +++ b/src/notes/model/Note.java @@ -0,0 +1,257 @@ +/* + * 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.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + +//定义note类,处理单个便签// +public class Note { + private ContentValues mNoteDiffValues;//声明一个ContentValues变量,用来存储note与上次修改后的改动// + private NoteData mNoteData;//申明一个NoteData变量,用来记录note的一些基本信息// + private static final String TAG = "Note";//设置软件标签// + /** + * Create a new note id for adding a new note to databases + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + ContentValues values = new ContentValues();//在数据库中新建一个便签文件// + long createdTime = System.currentTimeMillis();//设置系统当前时间为新便签创建时间// + values.put(NoteColumns.CREATED_DATE, createdTime);//将创建时间和修改时间都更改为当前系统时间// + values.put(NoteColumns.MODIFIED_DATE, createdTime);// 将便签的创建时间和修改时间都设定为创建时间// + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);//设定类型为便签类型// + values.put(NoteColumns.LOCAL_MODIFIED, 1);//修改标志置为1// + values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格// + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);//将数据写入到数据库中// + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + }//异常处理,捕获异常// + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + }//块:错误ID,异常处理// + return noteId;//没有异常,返回ID// + }//获取新建便签的编号// + + public Note() { + mNoteDiffValues = new ContentValues();//设置存储便签属性// + mNoteData = new NoteData();//设置存储便签内容// + }//构造Note,实例化note数据// + + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + }//设置数据库表格的标签属性数据// + + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + }/*设置数据库表格的标签文本内容的数据*/ + + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + }//设置文本数据的ID// + + public long getTextDataId() { + return mNoteData.mTextDataId; + }//获取文本数据的id// + + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + }//设置电话号码数据的ID// + + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + }//得到电话号码数据的ID// + + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + }//根据属性特征值判定便签是否被本地修改// + + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + }//便签ID不合法时抛出异常// + + if (!isLocalModified()) { + return true; + }//如果本地没有发现修改,直接返回1,指示已经同步到数据库中// + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + }//数据被修改后被同步到数据库,为了安全操作,现在更新后重新同步// + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + }//判断数据是否同步 + + return true; + }//判断是否是本地修改// + //定义一个基本的便签内容的数据类,主要包含文本数据和电话号码数据// + private class NoteData { + private long mTextDataId;//文本数据id// + + private ContentValues mTextDataValues;//文本数据属性// + + private long mCallDataId;//电话号码数据ID// + + private ContentValues mCallDataValues;//电话号码数据内容// + + private static final String TAG = "NoteData";//NoteData成员主要负责给几个变量赋初值// + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + }//变量初始化// + + //以下是几个函数的具体实现// + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + }//判断是否本地修改// + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + }//设置文本数据ID号// + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + }//设置电话号码对应的id// + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + }//设置呼叫数据ID,若传入参数小于0,则抛出参数错误的异常// + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + }//设置文本数据内容,并且保存修改时间// + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + }//为了安全,检查异常// + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues);//文本数据ID为零,意味着这个id是新建默认的id// + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + }//捕获异常// + } //.把文本数据存入DataColumns// + else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + }//内容提供者的更新操作,因为这个uri对应的数据是已经存在的,所以不需要向上面一样新建,而是更新即可// + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + }//如果这个便签之前有历史操作的话,那么在此基础上返回上一个操作对应位置的uri// + }//存储过程中如果遇到异常,通过以上操作进行处理// + else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + }//当电话号码不为新建时,更新电话号码ID// + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } //抛出远程异常,并写回日志// + catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null;//捕捉操作异常并写回日志// + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null;//异常日志// + } + }//操作列表不为空,即需要进行操作 + return null; + } + } +} diff --git a/src/notes/model/WorkingNote.java b/src/notes/model/WorkingNote.java new file mode 100644 index 0000000..9a444f7 --- /dev/null +++ b/src/notes/model/WorkingNote.java @@ -0,0 +1,370 @@ +/* + * 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.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + +/*申明WorkingNote类,创建小米便签的主要类,包括创建空便签,保存便签,加载小米便签内容,和设置小米便签的一些小部件之类的操作*/ +public class WorkingNote { + // Note for the working note + private Note mNote;//声明一个Note类型的变量// + // Note Id + private long mNoteId;//定义NoteID// + // Note content + private String mContent;//声明便签mode// + // Note mode + private int mMode;//是否为清单模式判断条件// + + private long mAlertDate;//设置闹钟时间// + + private long mModifiedDate;//最后修改时间// + + private int mBgColorId;//背景颜色ID// + + private int mWidgetId;//小部件ID// + + private int mWidgetType;//小部件类型// + + private long mFolderId;//文件夹所对应的ID// + + private Context mContext;//声明一个Context类型的变量,用以用户与系统进行交互,当前便签的上下文// + + private static final String TAG = "WorkingNote";//声明 DATA_PROJECTION字符串数组// + + private boolean mIsDeleted;//判断是否应该被删除// + + private NoteSettingChangedListener mNoteSettingStatusListener;//一个用来监听设置是否有变化的接口// + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + };//新建一个NOTE_PROJECTION数组// + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + };//保存便签自身属性的字符串数组// + + private static final int DATA_ID_COLUMN = 0;//以下定义的整形是上述两个列表中各元素的索引,规定每一个数据类型在哪一行// + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0;// 以下6个常量表示便签投影的0-5列// + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note();//加载一个已存在的便签// + mNoteId = 0; + mIsDeleted = false;//没有提供ID,默认为0// + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + }//该方法初始化类里的各项变量// + + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + }//从Cursor中导入Note的属性值,包括文件夹id、背景颜色id、窗口图标的id和类型、提醒时间以及改动时间// + + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null);//查找当前便签属性对应的便签的位置// + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + }/*通过数据库调用query函数找到第一个条目,通过判断cursor.moveToFirst()的值为true或false来确定查询结果是否为空,不为空的话存储各项信息*/ + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + }//.否则说明不存在这个便签,加载错误// + loadNoteData(); + } + + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null);//调用query函数查找到便签ID为mNoteId的位置,并将光标移动到该位置// + + if (cursor != null) {//光标存在时,将光标移动到便签的起始位置// + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN);//如果光标执行到第一行,获取存储内容的类型// + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } //数据类型为文本类型时,存为文本// + else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } //为电话号码类信息时 存为电话号码// + else { + Log.d(TAG, "Wrong note type with type:" + type); + }//如果类型错误,则提示异常// + } while (cursor.moveToNext());//查阅所有项,直到为空// + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + }//否则记录异常日志,没有ID为mNoteId的数据// + }//载入便签数据// + + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + }//创建空的Note;传参:context,文件夹id,widget,背景颜色// + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + }//导入一个新的正在写入的便签(WorkingNote)// + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) {//判断是否存在数据库中// + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + }//没有成功创建一个便签时,返回出错日志// + }//判断是否有价值去保存// + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + }//判断窗口大小是否变化,如果变化也要保存这个变化// + }//保存便签,成功保存返回true,否则返回false// + + public boolean existInDatabase() { + return mNoteId > 0; + }//判断便签是否已经存在于数据库中,mNoteID >0时存在,返回true,否则返回false// + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + }/*判断是否需要保存,已被删除或者不在数据库中但是是空便签或者已经在数据库中但本地没有修改过,都不保存*/ + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + }//设置监听“设置状态改变”// + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + }//判断是否为空// + }//设置AlertDate;若 mAlertDate与data不同,则更改mAlertDate并设定NoteValue// + + public void markDeleted(boolean mark) { + mIsDeleted = mark;//设定标志// + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + }//设置删除标志// + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id;//判断背景颜色// + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + }//更新背景颜色// + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + }//设定背景颜色// + + public void setCheckListMode(int mode) { + if (mMode != mode) {//当mMode和mode不相同时进行设定// + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode;//如果传入的模式与原模式不同,则更改原模式为当前模式// + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + }//判断参数,然后更改mMode// + }//设置检查列表模式,// + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + }//判断传入的类型是否与当前类型一样,否则更改为传入的类型并储存便签的窗口数据// + }//设置窗口类型// + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + }//判断传入是否与当前id一样,否则更改为传入id// + }//设置窗口编号// + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + }//判断文本内容是否相同,否则更新文本// + }//设置文本内容// + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + }//转换到电话号码信息// + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + }//检测是否有时钟提醒,mAlertDate > 0返回真,否则返回假// + + public String getContent() { + return mContent; + }//获取便签内容// + + public long getAlertDate() { + return mAlertDate; + }//获取提醒时间// + + public long getModifiedDate() { + return mModifiedDate; + }//获取最近修改时间// + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + }//返回来源信息// + + public int getBgColorId() { + return mBgColorId; + }//获取背景颜色ID// + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + }//获取标题背景颜色id// + + public int getCheckListMode() { + return mMode; + }//获取检查列表模式// + + public long getNoteId() { + return mNoteId; + }//获取便签id// + + public long getFolderId() { + return mFolderId; + }//获取文件ID// + + public int getWidgetId() { + return mWidgetId; + }//获取小部件ID// + + public int getWidgetType() { + return mWidgetType; + }//获取小部件类型// + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged();//背景颜色改变按钮// + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set);//提醒时间按钮,可进行时间的更改和提醒的开关// + + /** + * Call when user create note from widget + */ + void onWidgetChanged();//小部件的修改按钮// + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode);//便签检查列表模式改变// + }// 该接口用来监视是否有设置改变// +}