Compare commits

..

45 Commits

Author SHA1 Message Date
Conquer_CN 971d178aae 6/17 文档提交
3 years ago
Conquer_CN 4e823eeb9e 6/7 提交
3 years ago
Conquer_CN 2d604791f0 6/5 提交
3 years ago
Conquer_CN 7bfc07781d Merge branch 'master' of https://bdgit.educoder.net/prhm3ew4x/gieProject
3 years ago
Conquer_CN 4196a324f3 5/18 修改
3 years ago
唐志发 42e7784040 Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note into dev
3 years ago
唐志发 2bd190632e 5.18
3 years ago
Conquer_CN 78b4e993b8 Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/gieProject
3 years ago
唐志发 fa5f5a93bb Merge branch 'master' of https://bdgit.educoder.net/prhm3ew4x/MI_Note into dev
3 years ago
Conquer_CN 4eac841c18 Merge branch 'master' of https://bdgit.educoder.net/prhm3ew4x/gieProject
3 years ago
Conquer_CN c922d0d4a1 5/18 详细设计
3 years ago
Conquer_CN 8ca3099d26 5/18 详细设计
3 years ago
Conquer_CN fb644cd8cf 5/18 修改
3 years ago
唐志发 5c169c3ad3 5.12
3 years ago
唐志发 f26d7df4d8 5.12
3 years ago
唐志发 dd2992225f Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
唐志发 c0add4df1c 5.12
3 years ago
pj36foInf 8899bbe288 4/28
3 years ago
pj36foInf eb91a71f15 4/28
3 years ago
pj36foInf c7dc0a704e Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note into Git-wql
3 years ago
Conquer_CN 0f4d70ea60 4/28 修改
3 years ago
pj36foInf 6a449db11e Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note into Git-wql
3 years ago
王仕豪 b514a2f861 Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
王仕豪 dfc228193c Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
王仕豪 272c79f8df Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
王仕豪 2fe11d2da7 4.28
3 years ago
王仕豪 787d1771b3 Merge branch 'master' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
Conquer_CN 56f1a6f2a6 4/27 用户界面设计提交
3 years ago
唐志发 8409e91f40 4.27类图提交
3 years ago
唐志发 5e491545f6 4.21
3 years ago
唐志发 de771e886d 4.21
3 years ago
唐志发 ec52b832d5 4.21
3 years ago
唐志发 cd1ac2063c 4.21
3 years ago
唐志发 976b79428c 4.21
3 years ago
唐志发 1e7ba48625 4.20
3 years ago
唐志发 e0829a7f1e 4.20
3 years ago
pj36foInf 69ef45c5ea 4/20
3 years ago
唐志发 b213ece6e7 Merge branch 'dev' of https://bdgit.educoder.net/prhm3ew4x/MI_Note
3 years ago
m3yv74fzq a631cec69c ADD file via upload
3 years ago
m3yv74fzq a75ef7d92a ADD file via upload
3 years ago
唐志发 6cf6deedaa 4.20
3 years ago
pj36foInf 252d72e6ec 414
3 years ago
pj36foInf 8e050b9c45 414
3 years ago
唐志发 bc43a956e9 4/13 精读报告提交
3 years ago
唐志发 2f60bdcb61 四月十三,唐志发精读报告提交
3 years ago

9
.gitignore vendored

@ -1,9 +0,0 @@
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
project.properties
.settings/
.classpath
.project

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Notes-master.iml" filepath="$PROJECT_DIR$/Notes-master.iml" />
</modules>
</component>
</project>

190
NOTICE

@ -1,190 +0,0 @@
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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Android API 33, extension level 5 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -1,23 +0,0 @@
[中文]
1. MiCode便签是小米便签的社区开源版由MIUI团队(www.miui.com) 发起并贡献第一批代码遵循NOTICE文件所描述的开源协议
今后为MiCode社区(www.micode.net) 拥有,并由社区发布和维护。
2. Bug反馈和跟踪请访问Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. 功能建议和综合讨论请访问MiCode,
http://micode.net/forum.php?mod=forumdisplay&fid=38
[English]
1. MiCode Notes is open source edition of XM notepad, it's first initiated and sponsored by MIUI team (www.miui.com).
It's opened under license described by NOTICE file. It's owned by the MiCode community (www.micode.net). In future,
the MiCode community will release and maintain this project.
2. Regarding issue tracking, please visit Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. Regarding feature request and general discussion, please visit Micode forum,
http://micode.net/forum.php?mod=forumdisplay&fid=38

@ -1,2 +0,0 @@
# MI_Note Project

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

@ -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;//小米便签行为异常处理
public class ActionFailureException extends RuntimeException {//从RuntimeException类派生出ActionFailureException类用来处理行为异常
private static final long serialVersionUID = 4425249765923293627L;//定义serialVersionUID相当于java类的身份证主要用于版本控制。
public ActionFailureException() {// 函数:交给父类的构造函数(包括下面的两个构造函数)
super();//super是指向父类的一个指针与其相对的还有this指向当前类。
}
public ActionFailureException(String paramString) {
super(paramString);
}
public ActionFailureException(String paramString, Throwable paramThrowable) {//调用父类具有相同形参paramString和paramThrowable的构造方法
super(paramString, paramThrowable);
}
}

@ -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;//小米便签网络异常处理包,该源文件里定义的所有类都属于这个包
public class NetworkFailureException extends Exception {//应该是处理网络异常的类直接继承于Exception
private static final long serialVersionUID = 2107610287180234136L;//定义serialVersionUID相当于java类的身份证主要用于版本控制。作用验证版本一致性如果不一致会导致反序列化的时候版本不一致的异常。
public NetworkFailureException() {
super();
}//构造函数(以下三个都是)
public NetworkFailureException(String paramString) {
super(paramString);//调用父类具有相同形参paramString的构造方法相当于Exception(paramString)
}
public NetworkFailureException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable);//调用父类具有相同形参paramString和paramThrowable的构造方法
}
}

@ -0,0 +1,253 @@
/*//单个便签项
* 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;//用于添加和获取Uri后面的ID
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;//以下5行都是对小米便签数据处理的一些数据或操作相关
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;//以上全是导入
public class Note {//这个类是用来刻画单个Note的
private ContentValues mNoteDiffValues;//ContentValues是用于给其他应用调用小米便签的内容的共享数据
private NoteData mNoteData;//申明一个NoteData变量用来记录note的一些基本信息
private static final String TAG = "Note";//软件的名称
/**
* Create a new note id for adding a new note to databases//创建一个新的便签id以便把新便签加入数据库
*/
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);//外部应用对ContentProvider中的数据进行添加、删除、修改和查询操作
long noteId = 0;//实现外部应用对数据的插入删除等操作
try {//异常处理
noteId = Long.valueOf(uri.getPathSegments().get(1));//获取便签的id
} catch (NumberFormatException e) {//异常处理和提示
Log.e(TAG, "Get note id error :" + e.toString());//获取id错误
noteId = 0;//获取了错误的id
}
if (noteId == -1) {//块错误ID异常处理
throw new IllegalStateException("Wrong note id:" + noteId);//非法状态时返回出错便签编号
}
return noteId;//若没有异常那么返回这个id
}
public Note() {//定义两个变量用来存储便签的数据,一个是存储便签属性、一个是存储便签内容
mNoteDiffValues = new ContentValues();//便签属性
mNoteData = new NoteData();//便签内容
}
public void setNoteValue(String key, String value) {//设置数据库表格的标签属性数据
mNoteDiffValues.put(key, value);//设置key值和对应的value值
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);//修改之后标志为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();//相较上次存在修改size()>0
}
public boolean syncNote(Context context, long noteId) {//判断便签是否同步
if (noteId <= 0) {//便签ID不合法时抛出异常
throw new IllegalArgumentException("Wrong note id:" + noteId);//抛出异常弹出对话框错误ID并显示错误ID号
}
if (!isLocalModified()) {//如果本地没有发现修改直接返回1指示已经同步到数据库中
return true;//返回true
}
/**
* 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;//成功则返回true
}
private class NoteData {//定义一个基本的便签内容的数据类,主要包含文本数据和电话号码数据
private long mTextDataId;//文本数据id
private ContentValues mTextDataValues;//文本数据内容
private long mCallDataId;//电话号码数据ID
private ContentValues mCallDataValues;//电话号码数据内容
private static final String TAG = "NoteData";//初始化
public NoteData() {// NoteData的构造函数初始化四个变量
mTextDataValues = new ContentValues();//初始化一个文本数据,为了储存文本
mCallDataValues = new ContentValues();//初始化一个电话号码数据,为了储存电话号码
mTextDataId = 0;//初始化文本数据ID置为0
mCallDataId = 0;//初始化电话号码ID置为0
}
boolean isLocalModified() {//下面是上述几个函数的具体实现
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
void setTextDataId(long id) {//设定文本数据id
if(id <= 0) {// id不能小于0
throw new IllegalArgumentException("Text data id should larger than 0");//id保证大于0
}
mTextDataId = id;//设定电话号码id
}
void setCallDataId(long id) {//设置电话号码对应的id
if (id <= 0) {//判断ID是否合法
throw new IllegalArgumentException("Call data id should larger than 0");//电话号码数据ID应大于0
}
mCallDataId = id;//设定电话数据
}
void setCallData(String key, String value) {//设置电话号码数据内容,并且保存修改时间
mCallDataValues.put(key, value);//修改之后将属性值置为1
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);//系统修改时间
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());//设置修改时间为当前系统时间
}
void setTextData(String key, String value) {//设置文本数据
mTextDataValues.put(key, value);//修改后标志置为1
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);//设置修改时间为当前系统时间
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());//设置修改时间为当前系统时间
}
Uri pushIntoContentResolver(Context context, long noteId) {//下面函数的作用是将新的数据通过Uri的操作存储到数据库
/**
* Check for safety
*/
if (noteId <= 0) {//保证便器的编号为正数
throw new IllegalArgumentException("Wrong note id:" + noteId);//判断数据是否合法
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();//数据库操作链表
ContentProviderOperation.Builder builder = null;//数据库的操作列表
if(mTextDataValues.size() > 0) {//把文本数据存入DataColumns
mTextDataValues.put(DataColumns.NOTE_ID, noteId);//设定文本数据的属性
if (mTextDataId == 0) {//uri插入文本数据
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);//把文本便签的类型填入文本数据库中的对应一行
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,//uri内容接收器插入文本数据库放在Notes.CONTENT_DATA_URI的路径下
mTextDataValues);
try {//尝试重新给它设置id
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
Log.e(TAG, "Insert new text data fail with noteId" + noteId);//插入数据失败
mTextDataValues.clear();//把电话号码数据存入DataColumns
return null;
}
} else {
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(//内容提供者的更新操作因为这个uri对应的数据是已经存在的所以不需要向上面一样新建而是更新即可
Notes.CONTENT_DATA_URI, mTextDataId));//语句: 将uri和id合并后更新
builder.withValues(mTextDataValues);
operationList.add(builder.build());//操作队列添加上内容提供者
}
mTextDataValues.clear();//设定电话号码的属性数据
}
if(mCallDataValues.size() > 0) {//对于电话号码的数据也是和文本数据一样的同步处理
mCallDataValues.put(DataColumns.NOTE_ID, noteId);//写入noteID
if (mCallDataId == 0) {//将电话号码的id设定为uri提供的id
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;
}
} else {//当电话号码不为新建时更新电话号码ID
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(//内容提供者的更新操作这个uri对应的数据是已经存在的因此进行更新即可
Notes.CONTENT_DATA_URI, mCallDataId));
builder.withValues(mCallDataValues);
operationList.add(builder.build());//存储过程中的异常处理
}
mCallDataValues.clear();
}
if (operationList.size() > 0) {//存储过程中如果遇到异常,通过如下进行处理
try {//抛出远程异常,并写回日志
ContentProviderResult[] results = context.getContentResolver().applyBatch(//Android源码中对通讯录的操作应用端使用ContentProvider提供的applyBatch进行批量处理通讯录的联系人入库
Notes.AUTHORITY, operationList);
return (results == null || results.length == 0 || results[0] == null) ? null//完成后返回一个URI
: 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;
}
}
}

@ -0,0 +1,368 @@
/*//当前活动便签项
* 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;//在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;
public class WorkingNote {//声明一个workingnote类
// Note for the working note
private Note mNote;//声明一个Note类型的变量
// Note Id
private long mNoteId;//声明便签content
// 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;//当前便签的上下文
private static final String TAG = "WorkingNote";//声明 DATA_PROJECTION字符串数组
private boolean mIsDeleted;//是否应该被删除
private NoteSettingChangedListener mNoteSettingStatusListener;//一个用来监听设置是否有变化的接口
public static final String[] DATA_PROJECTION = new String[] {//声明 NOTE_PROJECTION字符串数组
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
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;//WorkingNote的构造函数
mAlertDate = 0;//默认提醒日期为0
mModifiedDate = System.currentTimeMillis();//系统现在的时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数
mFolderId = folderId;//默认最后一次修改日期为当前时间
mNote = new Note();//加载一个已存在的便签
mNoteId = 0;//没有提供id所以默认为0
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;//默认是不可见
}
// Existing note construct
private WorkingNote(Context context, long noteId, long folderId) {//加载Note
mContext = context;//调用query函数找到第一个条目
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();//加载便签
}
private void loadNote() {//加载已有的便签
Cursor cursor = mContext.getContentResolver().query(//存在第一个条目
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {//通过数据库调用query函数找到第一个条目
if (cursor.moveToFirst()) {//如果游标移动到第一行
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);//存储各项信息
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);//关闭cursor游标
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);
}
cursor.close();//若不存在,报错
} else {//否则说明不存在这个便签,加载错误
Log.e(TAG, "No note with id:" + mNoteId);//未能找到此note,返回异常
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);//抛出异常找不到NoteID
}
loadNoteData();//调用方法loadNoteData导入便签的内容。
}
private void loadNoteData() {//加载NoteData
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);//判断信息是否为空
if (cursor != null) {//光标存在时,将光标移动到便签的起始位置
if (cursor.moveToFirst()) {//do while循环将便签数据全部读取出来
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));//否则在日志中标志错误类型为note type错误
} else {//如果类型错误,则提示异常
Log.d(TAG, "Wrong note type with type:" + type);//再否则就是有错误
}
} while (cursor.moveToNext());//查阅所有项,直到为空
}
cursor.close();//查找失败时在日志中记录错误信息为无法找到id号为mNoteId的便签
} else {//否则记录异常日志没有ID为mNoteId的数据
Log.e(TAG, "No data with id:" + mNoteId);//否则记录异常日志没有ID为mNoteId的数据
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);//抛出异常
}
}
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,//创建一个新便签的构造函数
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);//根据环境和id创建一个空的便签
note.setBgColorId(defaultBgColorId);//设定相关属性
note.setWidgetId(widgetId);//设定widget id
note.setWidgetType(widgetType);//设置窗口类型
return note;
}
public static WorkingNote load(Context context, long id) {//导入一个新的正在写入的便签WorkingNote
return new WorkingNote(context, id, 0);//返回便签
}
public synchronized boolean saveNote() {//保存Note
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);//同步便签内容及便签id
/**
* Update widget content if there exist any widget of this note
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID//判断是否存在widget可以上传
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
public boolean existInDatabase() {
return mNoteId > 0;
}//是否在数据库中存在判断是否在数据库中存储过直接判定id大小即可
private boolean isWorthSaving() {//判断是否需要保存
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))//被删除,或(不在数据库中 内容为空),或 本地已保存过
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;//其余情况要保存返回true
}
}
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {//设置常量
mNoteSettingStatusListener = l;
}
public void setAlertDate(long date, boolean set) {//设置AlertDate;若 mAlertDate与data不同则更改mAlertDate并设定NoteValue
if (date != mAlertDate) {//发现date与mAlertDate不一致时设置mAlertDate为date
mAlertDate = date;//为设置的日期打标签
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {//判断是否为空
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
public void markDeleted(boolean mark) {//设定删除标记
mIsDeleted = mark;//设定标记
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {//调用mNoteSettingStatusListener的 onWidgetChanged方法
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) {//设定一个判断条件
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);//设定之后更改mMode
}
mMode = mode;//把当前便签蛇者为清单模式的便签
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));//语句重设文本数据改变MODE项
}
}
public void setWidgetType(int type) {//设定WidgetType
if (type != mWidgetType) {//设定条件
mWidgetType = type;//调用Note的setNoteValue方法更改WidgetType
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}//调用Note的setNoteValue方法更改WidgetType
}
public void setWidgetId(int id) {//设定WidgetId
if (id != mWidgetId) {//设定条件
mWidgetId = id;//调用Note的setNoteValue方法更改WidgetId
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}//调用Note的setNoteValue方法更改WidgetId
}
public void setWorkingText(String text) {//设定WorkingText
if (!TextUtils.equals(mContent, text)) {//判断条件
mContent = text;//调用Note的setTextData方法更改WorkingText
mNote.setTextData(DataColumns.CONTENT, mContent);//调用Note的setTextData方法更改WorkingText
}
}
public void convertToCallNote(String phoneNumber, long callDate) {//转变mNote的CallData及CallNote信息
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);
}//判断是否有时钟提醒
public String getContent() {
return mContent;
}//获取内容
public long getAlertDate() {
return mAlertDate;
}//获取闹钟警戒数据
public long getModifiedDate() {
return mModifiedDate;
}//获取修改的数据
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}//获取背景颜色id
public int getBgColorId() {
return mBgColorId;
}//获取背景颜色id
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}//获取标题背景颜色id
public int getCheckListMode() {
return mMode;
}//获取CheckListMode
public long getNoteId() {
return mNoteId;
}//获取便签id
public long getFolderId() {
return mFolderId;
}//获取文件夹id
public int getWidgetId() {
return mWidgetId;
}//获取WidgetType
public int getWidgetType() {
return mWidgetType;
}//创建接口 NoteSettingChangedListener,便签更新监视为NoteEditActivity提供接口
public interface NoteSettingChangedListener {//该类用于侦听关于Note的设置的改变
/**
* 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);//mode的改变按键
}
}

@ -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;//实现异步操作避免单个操作耗时过久而堵塞任务的关键路径,从而整体卡顿
import android.app.Notification;//以下几行都是引用基于数据库服务的类
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<Void, String, Integer> {//异步任务类:任务同步和取消,显示同步任务的进程、通知和结果
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;//定义一个int变量作用是存储GTASK同步通知ID
public interface OnCompleteListener {//方法声明了一个名为OnCompleteListener的接口其内部的OnComplete方法在GTaskSyncService里面实现用来初始化异步的功能
void onComplete();//初始化
}
private Context mContext;//定义成员:文本内容
private NotificationManager mNotifiManager;//对象: 通知管理器类的实例化
private GTaskManager mTaskManager;//实例化任务管理器
private OnCompleteListener mOnCompleteListener;//实例化是否完成的监听器
public GTaskASyncTask(Context context, OnCompleteListener listener) {//传入两个变量构造形成GtaskASyncTask类
mContext = context;// 引入两个变量构造形成GtaskASyncTask类
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext//getSystemService是Activity的一个方法可根据传入的参数获得应服务的对象。这里以Context的NOTIFICATION_SERVICE为对象。
.getSystemService(Context.NOTIFICATION_SERVICE);//getSystemService是一种可根据传入的参数获得应服务的对象的端口函数。
mTaskManager = GTaskManager.getInstance();//getInstance ()函数用于使用单例模式创建类的实例。
}
public void cancelSync() {//取消同步
mTaskManager.cancelSync();//调用类中的同名方法来取消同步
}
public void publishProgess(String message) {//显示消息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;//描述了想要启动一个Activity、Broadcast或是Service的意图
if (tickerId != R.string.ticker_success) {// 点击清除按钮或点击通知后会自动消失
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,//若同步不成功就从系统取得一个来启动NotesPreferenceActivity的对象
NotesPreferenceActivity.class), 0);//获取首选项设置页
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,//若同步成功就从系统取得一个来启动一个NotesListActivity的对象
NotesListActivity.class), 0);
}
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);//设置最新事件信息
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);//通过NotificationManager对象的notify方法来执行一个notification的消息
}
@Override//这是Java5的元数据自动加上去的一个标志目的是告诉你下面这个方法是从父类/接口继承过来的,需要重写一次
protected Integer doInBackground(Void... unused) {//执行后台操作
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity//利用getString,将把 字符串内容传进sync_progress_login中
.getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this);//在后台进行同步
}
@Override//这是Java5的元数据自动加上去的一个标志目的是告诉你下面这个方法是从父类/接口继承过来的,需要重写一次
protected void onProgressUpdate(String... progress) {//显示进度的更新
showNotification(R.string.ticker_syncing, progress[0]);//显示进度的更新
if (mContext instanceof GTaskSyncService) {//判断mContext是否是GTaskSyncService的实例
((GTaskSyncService) mContext).sendBroadcast(progress[0]);// 如果mContext是GTaskSyncService实例化的对象则发送一个广播
}
}
@Override//这是Java5的元数据自动加上去的一个标志目的是告诉你下面这个方同步失败取消法是从父类/接口继承过来的,需要重写一次
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();
}
}
}

@ -0,0 +1,584 @@
/*
* 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;//这个函数用于自动登录账号并更新消息,创建或者获取任务列表
import android.accounts.Account;//引入包
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 {//GTaskClient类实现GTask的登录以及创建GTask任务和任务列表从网络上获取任务内容
private static final String TAG = GTaskClient.class.getSimpleName();//Google邮箱指定URL
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";//发布的uri
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() {//getInstance获取实例化对象并返回
mHttpClient = null;//初始化客户端,使用 getInstance()返回mInstance这个实例化对象从而获取实例化对象
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() {//获取实例如果当前没有示例则新建一个登陆的gtask如果有直接返回
if (mInstance == null) {// 若无实例,则新建一个
mInstance = new GTaskClient();
}
return mInstance;
}
public boolean login(Activity activity) {//login用于实现登录的方法以activity作为参数
// we suppose that the cookie would expire after 5 minutes//设置登录时间一旦超过5分钟即需要重新登录
// then we need to re-login//实现登录使用下面定义的loginGoogleAccount( )方法登录Google账户 使用下面定义的loginGtask( )方法登录gtask登录成功返回true登录失败返回false
final long interval = 1000 * 60 * 5;//interval时间间隔为1000ns * 60 * 5 = 5min 即间隔5分钟时间
if (mLastLoginTime + interval < System.currentTimeMillis()) {//判断距离上次登录的时间间隔若超过5分钟则重置登录状态
mLoggedin = false;//取消登录
}
// need to re-login after account switch//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"))) {// 判断是不是一个谷歌账户
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");//变为小写
int index = mAccount.name.indexOf('@') + 1;//语句:返回@第一次出现的位置并把位置+1后记录在index里
String suffix = mAccount.name.substring(index);//语句substring() 方法用于提取字符串中介于两个指定下标之间的字符此语句中index为start没有设置stop所以提取了index开始到后面的字符也就是账户名的后缀例如qq.com 163.com之类的后缀
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//尝试使用谷歌的官方URL登录
if (!mLoggedin) {//代码块: 若前面的尝试失败,则尝试使用官方的域名登陆
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
if (!tryToLoginGtask(activity, authToken)) {//第二次登录失败返回false
return 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");//获取谷歌账户
if (accounts.length == 0) {//如果没有这样的账号输出日志信息“无有效的google账户”
Log.e(TAG, "there is no available google account");//显示没有该谷歌账户
return null;
}
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);//代码块匹配活动的账户里是否存在账户存在的话把这个账户记在mAccount中没有的话显示不能在设置中获取相同名字的账户
Account account = null;//遍历account数组寻找已登录过的信息
for (Account a : accounts) {//找到后,为属性赋值
if (a.name.equals(accountName)) {//没找到,输出例外信息
account = a;
break;
}
}
if (account != null) {//若存在把这个账户记在mAccount中否则显示“不能在设置中获取相同名字的账户”字样并返回值为null的令牌
mAccount = account;//若invalidateToken则需调用invalidateAuthToken废除这个无效的token
} else {
Log.e(TAG, "unable to get an account with the same name in the settings");//语句: 若遍历完之后仍没有用户名匹配的账号,则直接返回
return null;
}
// get the token now//获取token
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,//getAuthToken方法 获取令牌
"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缓存应用必须调用这个方法将缓存的authToken置为过期否则getAuthToken获取到的一直是缓存的token
loginGoogleAccount(activity, false);
}
} catch (Exception e) {//捕捉令牌获取失败的异常
Log.e(TAG, "get auth token failed");//错误,获取授权标记失败
authToken = null;//tryToLoginGtask尝试登录Gtask方法这是一个试探方法作为登录的预判
}
return authToken;//再次验证令牌。令牌可能是过期的,因此我们需要废除过期令牌
}
private boolean tryToLoginGtask(Activity activity, String authToken) {//方法用于判断令牌对于登陆gtask账号是否有效
if (!loginGtask(authToken)) {//代码块: 如果令牌不可用于登陆gtask账号则需要重新登陆google账号获取令牌
// maybe the auth token is out of date, now let's invalidate the//判断令牌对于登陆gtask账号是否有效
// token and try again
authToken = loginGoogleAccount(activity, true);//删除过一个无效的authToken申请一个新的后再次尝试登陆
if (authToken == null) {//代码块对再次登陆google账号失败的处理
Log.e(TAG, "login google account failed");//错误,登录 google 帐户失败
return false;//对再次登陆google账号失败的处理
}
if (!loginGtask(authToken)) {//代码块: 重新获取的令牌再次失效
Log.e(TAG, "login gtask failed");//错误,登录 gtask 失败
return false;
}
}
return true;
}
private boolean loginGtask(String authToken) {//loginGtask实现登录Gtask的方法
int timeoutConnection = 10000;//连接超时为10000毫秒即10秒
int timeoutSocket = 15000;//socketandroid与服务器的通信工具与http的主要区别是能够主动推送信息而不需要每次发送请求
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 {//登录Gtask
String loginUrl = mGetUrl + "?auth=" + authToken;//设置登录的url以及令牌信息
HttpGet httpGet = new HttpGet(loginUrl);//通过HttpGet向服务器申请
HttpResponse response = null;//通过HttpGet向服务器申请
response = mHttpClient.execute(httpGet);//从cookiestore中获取cookie
// get the cookie now
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();//语句获取cookie值
boolean hasAuthCookie = false;//获取cookie值
for (Cookie cookie : cookies) {//功能遍历cookies集合中的每个Cookie对象如果有一个Cookie对象的名中含有GTLhasAuthCookie被赋给True
if (cookie.getName().contains("GTL")) {//验证cookie信息这里通过GTL标志来验证
hasAuthCookie = true;//验证cookie信息
}
}
if (!hasAuthCookie) {//代码块显示这是没有授权的cookie
Log.w(TAG, "it seems that there is no auth cookie");//显示没有授权的cookie
}
// get the client version//获取client的版本号
String resString = getResponseContent(response.getEntity());//获取客户端版本
String jsBegin = "_setup(";//在取得的内容中截取从_setup(到)}</script>中间的部分这是html语言格式获取的是gtask_url
String jsEnd = ")}</script>";
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) {//catch例外GET失败
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");//获取异常类型和异常详细消息
return false;
}
return true;
}
private int getActionId() {
return mActionId++;
}//获取动作的id号码
private HttpPost createHttpPost() {//创建一个用来保存URL的httppost对象
HttpPost httpPost = new HttpPost(mPostUrl);//创建一个httppost对象用来保存URL
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");//创建一个httppost对象保存URL
httpPost.setHeader("AT", "1");
return httpPost;
}
private String getResponseContent(HttpEntity entity) throws IOException {//getResponseContent获取服务器响应的数据主要通过方法getContentEncoding来获取网上资源返回这些资源
String contentEncoding = null;//通过URL得到HttpEntity对象如果不为空则使用getContent方法创建一个流将数据从网络都过来
if (entity.getContentEncoding() != null) {//代码块如果获取的内容编码不为空给内容编码赋值显示encoding内容编码
contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding);//gzip是使用deflate进行压缩数据的一个压缩库
}
InputStream input = entity.getContent();
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {//deflate是一种压缩算法,是huffman编码的一种加强
input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {//语句deflate是一种压缩算法
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);//InflaterInputStream类实现了一个流过滤器用于以“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 {//postRequest利用JSON发送请求返回获取的内容
if (!mLoggedin) {//未登录,输出提示信息
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
HttpPost httpPost = createHttpPost();//实例化一个httpPost的对象用来向服务器传输数据发送在js里请求的内容
try {//实例化一个对象,用于与服务器交互和发送请求
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();//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) {//下面是几个异常
Log.e(TAG, e.toString());//下面是对四种不同的异常出现时的处理
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");//post请求失败
} catch (IOException e) {//post执行时发生异常
Log.e(TAG, e.toString());//post执行时发生异常
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");//createTask创建一个任务对象
} 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 {//方法创建Task,设置好action的链表、client_version、post
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);//用户版本
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);//client的版本号
// post//通过postRequest获取任务的返回信息
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));
} 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 {//createTaskList创建任务列表与createTask类似
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//post操作
JSONObject jsResponse = postRequest(jsPost);
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 {//提交更新数据还是利用JSON
if (mUpdateArray != null) {
try {//新建JSONObject对象使用jsPost.put( )添加对应关系使用postRequest( )方法发送jspost最后给mUpdateArray赋值NULL表示现在没有内容要更新
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);//post操作
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 {//添加更新节点主要利用commitUpdate
if (node != null) {//更新太多内容可能导致出现异常
// too many update items may result in an error
// set max to 10 items
if (mUpdateArray != null && mUpdateArray.length() > 10) {//提交后更新
commitUpdate();//更新的阵列为空的话就使用JSONArray
}
if (mUpdateArray == null)
mUpdateArray = new JSONArray();
mUpdateArray.put(node.getUpdateAction(getActionId()));//语句:将更新节点加入列表
}
}
public void moveTask(Task task, TaskList preParent, TaskList curParent)//移动一个任务通过getGid获取task所属的Id还是通过JSONObject和postRequest实现
throws NetworkFailureException {//把任务移动到指定的列表中
commitUpdate();//方法:提交更新数据
try {
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());
}//移动前所属列表
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());//设置当前列表
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid());//在不同列表间移动时,放入目标列表中去
if (preParent != curParent) {//语句当移动发生在不同的任务列表之间设置为dest_list
// put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());//将动作列表放入jsPost中
}
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 {//代码块新建jsPost把除了node的其他节点都放入jsPost并提交
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// action_list
node.setDeleted(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 {//获取任务列表首先通过getURI在网上获取数据在截取所需部分内容返回
if (!mLoggedin) {//检查是否在登录状态
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
try {//使用url实例化一个获取对象
HttpGet httpGet = new HttpGet(mGetUrl);//通过发送HttpPost请求访问HTTP资源
HttpResponse response = null;//语句初始化Httpresponse (回复)为空
response = mHttpClient.execute(httpGet);//语句使用httpget发送一个请求返回一个response对象
// get the task list//代码块获取任务链表判断resString的开头结尾格式对不对正确的话则重建一个字符串赋值给jsString然后用jsString新建一个JSONObject对象js,并通过js调用一系列方法把任务列表返回
String resString = getResponseContent(response.getEntity());//获取任务列表
String jsBegin = "_setup(";//截取字符串并放入到jsString里
String jsEnd = ")}</script>";
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 {//方法对于已经获取的任务列表可以通过其id来获取到
commitUpdate();
try {//设置为传入的listGid
JSONObject jsPost = new JSONObject();//设置为传入的listGid
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,//通过action.pu()t对JSONObject对象action添加元素通过jsPost.put()对jsPost添加相关元素然后通过postRequest()提交更新后的请求并返回一个JSONObject的对象最后使用jsResponse.getJSONArray( )获取jsResponse中的JSONArray值并作为函数返回值
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);//这里设置为传入的listGid
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost);
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);
} 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;
}//重置更新内容
}

@ -0,0 +1,800 @@
/*
* 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;//实现同步功能的主函数
import android.app.Activity;//在包里引用各种类
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import net.micode.notes.R;//引用小米便签中的类
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.data.MetaData;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.SqlNote;
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.DataUtils;
import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
public class GTaskManager {//声明一个类,类名称的命名规范:所有单词的首字母大写
private static final String TAG = GTaskManager.class.getSimpleName();//定义了一系列静态变量来显示GTask当前的状态1设置GTask的TAG
public static final int STATE_SUCCESS = 0;//成功
public static final int STATE_NETWORK_ERROR = 1;//网络错误
public static final int STATE_INTERNAL_ERROR = 2;//内部错误
public static final int STATE_SYNC_IN_PROGRESS = 3;//进程同步中
public static final int STATE_SYNC_CANCELLED = 4;//取消同步
private static GTaskManager mInstance = null;//private 定义一系列不可被外部的类访问的量
private Activity mActivity;//构造函数
private Context mContext;
private ContentResolver mContentResolver;
private boolean mSyncing;
private boolean mCancelled;
private HashMap<String, TaskList> mGTaskListHashMap;
private HashMap<String, Node> mGTaskHashMap;
private HashMap<String, MetaData> mMetaHashMap;
private TaskList mMetaList;
private HashSet<Long> mLocalDeleteIdMap;
private HashMap<String, Long> mGidToNid;
private HashMap<Long, String> mNidToGid;
private GTaskManager() {//方法:类的构造函数,对其内部变量进行初始化
mSyncing = false;//正在同步标识false代表未同步
mCancelled = false;
mGTaskListHashMap = new HashMap<String, TaskList>();//全局标识flase代表可以执行
mGTaskHashMap = new HashMap<String, Node>();//<>代表Java的泛型,就是创建一个用类型作为参数的类。
mMetaHashMap = new HashMap<String, MetaData>();//创建一个删除本地ID的map
mMetaList = null;//创建一个删除本地ID的map
mLocalDeleteIdMap = new HashSet<Long>();
mGidToNid = new HashMap<String, Long>();//建立一个google id到节点id的映射
mNidToGid = new HashMap<Long, String>();//创建一个节点id到google id的映射
}
public static synchronized GTaskManager getInstance() {//synchronized指明该函数可以运行在多线程下
if (mInstance == null) {//初始化mInstance
mInstance = new GTaskManager();//初始化
}
return mInstance;
}
public synchronized void setActivityContext(Activity activity) {//对类的当前实例进行加锁防止其他线程同时访问该类的该实例的所有synchronized块
// used for getting authtoken//获得操作指令
mActivity = activity;
}
public int sync(Context context, GTaskASyncTask asyncTask) {//实现本地和远程同步的操作
if (mSyncing) {//正在同步时,日志中写入正在同步
Log.d(TAG, "Sync is in progress");//debug进程已在同步中
return STATE_SYNC_IN_PROGRESS;//返回同步状态
}
mContext = context;//代码块对同步时GTaskManager的属性进行更新
mContentResolver = mContext.getContentResolver();//对GTaskManager的属性进行更新
mSyncing = true;//初始化各种标志变量
mCancelled = false;
mGTaskListHashMap.clear();//各种环境清空
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
try {//异常处理程序
GTaskClient client = GTaskClient.getInstance();//创建一个用户的实例
client.resetUpdateArray();//getInstance即为创建一个实例
// login google task //更新数组操作
if (!mCancelled) {//登陆用户端
if (!client.login(mActivity)) {//执行登录程序登录到google task
throw new NetworkFailureException("login google task failed");//抛出异常,登录 google 任务失败
}
}
// get the task list from google
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList();//调用下面自定义的方法初始化GTaskList
// do content sync work
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
syncContent();//同步便签内容
} catch (NetworkFailureException e) {//网络连接中断
Log.e(TAG, e.toString());//获取异常类型和异常详细消息
return STATE_NETWORK_ERROR;
} catch (ActionFailureException e) {//操作未完成
Log.e(TAG, e.toString());
return STATE_INTERNAL_ERROR;
} catch (Exception e) {//处理各种异常
Log.e(TAG, e.toString());//获取异常类型和异常详细消息
e.printStackTrace();
return STATE_INTERNAL_ERROR;
} finally {//代码块在同步操作结束之后更新GTaskManager的属性
mGTaskListHashMap.clear();//同步结束后清空环境
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
mSyncing = false;
}
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;//语句:若在同步时操作未取消,则说明同步成功,否则返回同步操作取消
}
private void initGTaskList() throws NetworkFailureException {//初始化GTask列表将google上的JSONTaskList转为本地任务列表
if (mCancelled)//是否中途取消
return;
GTaskClient client = GTaskClient.getInstance();//创建一个用户的实例
try {//客户端获取任务列表
JSONArray jsTaskLists = client.getTaskLists();//客户端获取任务列表jsTaskLists
// init meta list first
mMetaList = null;//初始化元数据列表
for (int i = 0; i < jsTaskLists.length(); i++) {//代码块:对获取到任务列表中的每一个元素进行操作
JSONObject object = jsTaskLists.getJSONObject(i);//取出单个JSON对象
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);//获取它的id
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);//获取它的名字
if (name
.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {//如果 name 等于 字符串 "[MIUI_Notes]" + "METADATA"执行if语句块
mMetaList = new TaskList();//新建数组,并为新建的数组设定内容
mMetaList.setContentByRemoteJSON(object);//新建一个元数据列表
// load meta data
JSONArray jsMetas = client.getTaskList(gid);//将JSON中部分数据复制到自己定义的对象中相对应的数据name->mname...
for (int j = 0; j < jsMetas.length(); j++) {//代码块把jsMetas里的每一个有识别码的metaData都放到哈希表中
object = (JSONObject) jsMetas.getJSONObject(j);//获取一个JSON类型的对象
MetaData metaData = new MetaData();//新建一个Metadata
metaData.setContentByRemoteJSON(object);//新建MetaData
if (metaData.isWorthSaving()) {//需要保存时,将子任务加入到主任务中
mMetaList.addChildTask(metaData);//新建一个元数据列表。
if (metaData.getGid() != null) {//操作getGid取得组识别码函数
mMetaHashMap.put(metaData.getRelatedGid(), metaData);//语句:把元数据放到哈希表中
}
}
}
}
}
// create meta list if not existed
if (mMetaList == null) {//代码块:若元数据列表不存在则创建一个
mMetaList = new TaskList();//元数据列表不存在,则在客户端创建一个
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META);
GTaskClient.getInstance().createTaskList(mMetaList);
}
// init task list
for (int i = 0; i < jsTaskLists.length(); i++) {//以下循环用于初始化任务列表
JSONObject object = jsTaskLists.getJSONObject(i);//代码块:先获取列表中每一个节点的属性
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);//通过getString函数传入本地某个标志数据的名称获取其在远端的名称
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);//获取JSON名字
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList();//语句: 创建一个新的任务列表
tasklist.setContentByRemoteJSON(object);//语句:对任务列表的内容进行设置
mGTaskListHashMap.put(gid, tasklist);//对任务列表的内容进行设置
mGTaskHashMap.put(gid, tasklist);
// load tasks
JSONArray jsTasks = client.getTaskList(gid);//获取任务id号
for (int j = 0; j < jsTasks.length(); j++) {//任务id号
object = (JSONObject) jsTasks.getJSONObject(j);
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);//语句获取当前任务的gid
Task task = new Task();
task.setContentByRemoteJSON(object);//语句:设置任务内容
if (task.isWorthSaving()) {//语句:判断该任务有无价值保存
task.setMetaInfo(mMetaHashMap.get(gid));//判断该任务是否有价值
tasklist.addChildTask(task);
mGTaskHashMap.put(gid, task);
}
}
}
}
} catch (JSONException e) {//初始化时捕捉异常
Log.e(TAG, e.toString());//获取异常类型和异常详细消息
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");//抛出异常,提交 JSONObject 失败
}
}
private void syncContent() throws NetworkFailureException {//实现内容同步
int syncType;//本地内容同步操作
Cursor c = null;//同步操作类
String gid;
Node node;//Node包含Sync_Action的不同类型
mLocalDeleteIdMap.clear();//初始化本地删除列表
if (mCancelled) {//对于本地已删除的便签采取的动作
return;
}
// for local deleted note
try {//代码块:对于删除本地便签的操作的同步
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,//语句:指针指向待删除的便签位置
"(type<>? AND parent_id=?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, null);
if (c != null) {//代码块:若获取到的待删除便签不为空,则进行同步操作
while (c.moveToNext()) {//通过while用指针遍历所有结点
gid = c.getString(SqlNote.GTASK_ID_COLUMN);//语句获取待删除便签的gid
node = mGTaskHashMap.get(gid);//语句:获取待删除便签的节点
if (node != null) {//节点非空则从哈希表里删除,并进行同步操作
mGTaskHashMap.remove(gid);//语句将待删除的节点对应的google id从映射表中移除
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);//语句:在远程删除对应节点
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));//查找不到时,在日志中记录信息
}
} else {//若c结点为空即寻找错误的时候报错
Log.w(TAG, "failed to query trash folder");//警告,询问垃圾文件夹失败
}
} finally {//代码块最后把c关闭并重置代码块
if (c != null) {//结束操作之后,将指针指向内容关闭并将指针置空
c.close();//记得关闭 cursor 所指文件
c = null;//cursor 置空
}
}
// sync folder first
syncFolder();//语句:对文件夹进行同步
// for note existing in database
try {//对于数据库中已经存在的便签,采取以下操作
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,//语句使c指针指向待操作的便签位置
"(type=? AND parent_id<>?)", new String[] {//c指针指向待操作的位置
String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {//query语句的用法
while (c.moveToNext()) {//语句:指针向后移动
gid = c.getString(SqlNote.GTASK_ID_COLUMN);//语句获取待操作的便签的gid
node = mGTaskHashMap.get(gid);//语句:获取待操作的便签的节点
if (node != null) {//若结点不为空将其对应的google id从映射表中移除然后建立google id到节点id的映射通过hashmap、gid和nid之间的映射表
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));//建立google id到节点id的映射
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);//通过hashmap建立联系
syncType = node.getSyncAction(c);//语句:更新此时的同步类型
} else {//代码块如果note是空的则判断c的trim的长度是否为0如果是则设置同步类型为ADD REMOTE,否则设置为DEL LOCAL
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {//语句:若本地增加了内容,则远程也要增加内容
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;//远程增加内容
} else {//语句若本地删除了内容则远程也要删除内容应的gid
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;//本地删除
}
}
doContentSync(syncType, node, c);//进行同步操作
}
} else {
Log.w(TAG, "failed to query existing note in database");//查询失败时在日志中写回错误信息
}
} finally {//代码块在最后关闭c并重置
if (c != null) {//代码块:结束操作之后,将指针指向内容关闭并将指针置空
c.close();//关闭 cursor 所指文件
c = null;//cursor 置空
}
}
// go through remaining items
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();//扫描剩下的项目,逐个进行同步
while (iter.hasNext()) {//迭代
Map.Entry<String, Node> entry = iter.next();
node = entry.getValue();//getValue()可以获取指定对象的属性值
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);//语句:在本地增加这些节点
}
// mCancelled can be set by another thread, so we neet to check one by
// one
// clear local delete table
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {//语句:终止标识有可能被其他进程改变,因此需要一个个进行检查
throw new ActionFailureException("failed to batch-delete local deleted notes");//终止标识有可能被其他线程改变
}
}
// refresh local sync id
if (!mCancelled) {//更新同步表
GTaskClient.getInstance().commitUpdate();//更新同步表
refreshLocalSyncId();//更新同步的id
}
}
private void syncFolder() throws NetworkFailureException {//同步文件夹
Cursor c = null;//同步文件夹
String gid;
Node node;
int syncType;
if (mCancelled) {//语句:判断是否取消该操作
return;
}
// for root folder
try {//对于根文件夹
c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,//语句:使指针指向根文件夹的位置
Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);//使指针指向根文件夹的位置
if (c != null) {
c.moveToNext();
gid = c.getString(SqlNote.GTASK_ID_COLUMN);//语句获取指针指向内容对应的gid
node = mGTaskHashMap.get(gid);//语句获取该gid所代表的节点
if (node != null) {//获取gid所代表的节点
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);//语句将该节点的gid到nid的映射加入映射表
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// for system folder, only update remote name if necessary
if (!node.getName().equals(//添加MIUI文件前缀
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);//语句:若非系统文件夹则在远程进行增加节点操作
}
} else {
Log.w(TAG, "failed to query root folder");//出现异常时,在日志中写回出错信息
}
} finally {//结束操作之后,最后将指针关闭,并将指针置空。方法与之前相同
if (c != null) {//代码块:结束操作之后,将指针指向内容关闭并将指针置空
c.close();//关闭 cursor 所指文件
c = null;//cursor 置空
}
}
// for call-note folder
try {//对于电话号码数据文件
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",//语句:使指针指向文件夹的位置
new String[] {//添加MIUI文件前缀
String.valueOf(Notes.ID_CALL_RECORD_FOLDER)//添加MIUI文件前缀
}, null);
if (c != null) {
if (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);//语句获取指针指向内容对应的gid
node = mGTaskHashMap.get(gid);//语句获取指针指向内容对应的gid
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);//语句将该节点的gid到nid的映射加入映射表
mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
// for system folder, only update remote name if
// necessary
if (!node.getName().equals(//语句:若当前访问的文件夹是系统文件夹则只需要更新
GTaskStringUtils.MIUI_FOLDER_PREFFIX//若当前访问的文件夹是系统文件夹则只需更新
+ GTaskStringUtils.FOLDER_CALL_NOTE))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);//语句:若非系统文件夹则在远程进行增加节点操作
}
}
} else {
Log.w(TAG, "failed to query call note folder");//警告,询问通讯便签文件夹失败
}
} finally {
if (c != null) {//代码块:结束操作之后,将指针指向内容关闭并将指针置空
c.close();//关闭 cursor 所指文件
c = null;//cursor 置空
}
}
// for local existing folders
try {//对于本地已存在的文件的操作
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,// 语句:使指针指向第一个文件夹的位置
"(type=? AND parent_id<>?)", new String[] {//使指针指向第一个文件夹的位置
String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {//语句:使指针遍历所有的文件夹
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));//语句获取指针指向内容对应的gid
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);//语句获取指针指向内容对应的gid
syncType = node.getSyncAction(c);//语句:更新同步类型
} else {//更新同步类型
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {//语句:若本地增加了内容,则远程也要增加内容
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;//远程添加
} else {//语句:若远程删除了内容,则本地也要删除内容
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;//本地删除
}
}
doContentSync(syncType, node, c);//语句:进行同步操作
}
} else {//进行同步操作
Log.w(TAG, "failed to query existing folder");//警告,询问已创建的文件夹失败
}
} finally {
if (c != null) {//代码块:结束操作之后,将指针指向内容关闭并将指针置空
c.close();//关闭 cursor 所指文件
c = null;//cursor 置空
}
}
// for remote add folders
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();//远程添加文件需要执行的操作
while (iter.hasNext()) {//语句:使用迭代器对远程增添的内容进行遍
Map.Entry<String, TaskList> entry = iter.next();
gid = entry.getKey();//语句获取对应的gid
node = entry.getValue();//语句获取gid对应的节点
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);//语句:进行本地增添操作
}
}
if (!mCancelled)
GTaskClient.getInstance().commitUpdate();//语句如果没有取消在GTsk的客户端进行实例的提交更新
}
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {//功能syncType分类
if (mCancelled) {//进行本地增添操作
return;
}
MetaData meta;
switch (syncType) {//根据同步类型选择操作
case Node.SYNC_ACTION_ADD_LOCAL://远程添加
addLocalNode(node);//本地添加
break;
case Node.SYNC_ACTION_ADD_REMOTE://本地删除
addRemoteNode(node, c);//本地删除
break;
case Node.SYNC_ACTION_DEL_LOCAL://远程删除
meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
break;
case Node.SYNC_ACTION_DEL_REMOTE://更新本地数据
meta = mMetaHashMap.get(node.getGid());//更新本地数据
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
}
GTaskClient.getInstance().deleteNode(node);
break;
case Node.SYNC_ACTION_UPDATE_LOCAL://更新远程数据
updateLocalNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_REMOTE:
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_CONFLICT://同步出错
// merging both modifications maybe a good idea
// right now just use local update simply
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_NONE://代码块:空操作
break;//空操作
case Node.SYNC_ACTION_ERROR://代码块:操作错误
default://代码块default:同步类型不在规定的类型范围内
throw new ActionFailureException("unkown sync action type");//抛出异常,未知的同步行为类型
}
}
private void addLocalNode(Node node) throws NetworkFailureException {//功能本地增加Node
if (mCancelled) {//这个函数是添加本地结点参数node即为要添加的本地结点
return;
}
SqlNote sqlNote;
if (node instanceof TaskList) {//节点是任务列表中的一个节点
if (node.getName().equals(//代码块:在根目录中增加节点
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
} else if (node.getName().equals(//代码块:在存放电话号码便签的文件夹中增加节点
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) {
sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER);
} else {//代码块如果在其他文件夹里则用一般操作设置sqlNote的参数
sqlNote = new SqlNote(mContext);//代码块:若没有存放的文件夹,则将其放在根文件夹中
sqlNote.setContent(node.getLocalJSONFromContent());//从本地任务列表中获取内容
sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
}
} else {//代码块:若待增添节点不是任务列表中的节点,进一步操作
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();//语句从待增添节点中获取jsonobject对象
try {//异常判断
if (js.has(GTaskStringUtils.META_HEAD_NOTE)) {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//语句获取对应便签的jsonobject对象
if (note.has(NoteColumns.ID)) {//语句:判断便签中是否有条目
long id = note.getLong(NoteColumns.ID);
if (DataUtils.existInNoteDatabase(mContentResolver, id)) {//id存在时删除id
// the id is not available, have to create a new one
note.remove(NoteColumns.ID);
}
}
}
if (js.has(GTaskStringUtils.META_HEAD_DATA)) {//以下为判断便签中的数据条目
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
for (int i = 0; i < dataArray.length(); i++) {//依次删除存在的data的ID
JSONObject data = dataArray.getJSONObject(i);//语句获取对应数据的jsonobject对象
if (data.has(DataColumns.ID)) {//语句:判断数据中是否有条目
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {//data id存在时删除已建立的对应ID
// the data id is not available, have to create
// a new one
data.remove(DataColumns.ID);
}
}
}
}
} catch (JSONException e) {//出现异常时,打印异常信息
Log.w(TAG, e.toString());//获取异常类型和异常详细消息
e.printStackTrace();
}
sqlNote.setContent(js);//语句将之前的操作获取到的js更新至该节点中
Long parentId = mGidToNid.get(((Task) node).getParent().getGid());//代码块找到父任务的ID号并作为sqlNote的父任务的ID没有父任务则报错
if (parentId == null) {//语句当不能找到该任务上一级的id时报错
Log.e(TAG, "cannot find task's parent id locally");//本地无法找到父进程id
throw new ActionFailureException("cannot add local node");//报告无法添加本地节点
}
sqlNote.setParentId(parentId.longValue());
}
// create the local node
sqlNote.setGtaskId(node.getGid());//设置google task id
sqlNote.commit(false);//更新本地便签
// update gid-nid mapping
mGidToNid.put(node.getGid(), sqlNote.getId());//语句更新gid与nid的映射表
mNidToGid.put(sqlNote.getId(), node.getGid());
// update meta
updateRemoteMeta(node.getGid(), sqlNote);//语句:更新远程的数据
}
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {//函数:更新本地节点,两个传入参数,一个是待更新的节点,一个是指向待增加位置的指针
if (mCancelled) {//如果正在取消同步,直接返回
return;
}
SqlNote sqlNote;//新建一个sql节点并将内容存储进Node中
// update the note locally
sqlNote = new SqlNote(mContext, c);//语句:在指针指向处创建一个新的节点
sqlNote.setContent(node.getLocalJSONFromContent());//语句:利用待更新节点中的内容对数据库节点进行设置
Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())//语句: 设置父任务的ID通过判断node是不是Task的实例
: new Long(Notes.ID_ROOT_FOLDER);
if (parentId == null) {//语句当不能找到该任务上一级的id时报错
Log.e(TAG, "cannot find task's parent id locally");//错误,不能在本地找到任务的父 id
throw new ActionFailureException("cannot update local node");//抛出异常,不能更新本地节点
}
sqlNote.setParentId(parentId.longValue());//设置该任务节点上一级的id
sqlNote.commit(true);//抛出异常,不能更新本地节点
// update meta info
updateRemoteMeta(node.getGid(), sqlNote);//升级meta
}
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {//添加远程节点
if (mCancelled) {//如果正在取消同步,直接返回
return;
}
SqlNote sqlNote = new SqlNote(mContext, c);//新建一个sql节点并将内容存储进Node中
Node n;
// update remotely
if (sqlNote.isNoteType()) {//语句:若待增添的节点为任务节点,进一步操作
Task task = new Task();
task.setContentByLocalJSON(sqlNote.getContent());
String parentGid = mNidToGid.get(sqlNote.getParentId());//找不到parentID时报错
if (parentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist");//错误,无法找到任务的父列表
throw new ActionFailureException("cannot add remote task");//抛出异常,无法添加云端任务
}
mGTaskListHashMap.get(parentGid).addChildTask(task);//在本地生成的GTaskList中增加子结点
GTaskClient.getInstance().createTask(task);//在本地生成的GTaskList中增加子结点
n = (Node) task;
// add meta//添加元数据
updateRemoteMeta(task.getGid(), sqlNote);
} else {
TaskList tasklist = null;
// we need to skip folder if it has already existed//当文件夹存在则跳过,若不存在则创建新的文件夹
String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;//代码块:当文件夹已经存在则跳过处理,若不存在则创建新的文件夹
if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)//按照文件夹的形式进行命名
folderName += GTaskStringUtils.FOLDER_DEFAULT;//语句:若为根文件夹则按根文件夹命名
else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER)
folderName += GTaskStringUtils.FOLDER_CALL_NOTE;//语句:若为电话号码便签文件夹则按电话号码便签命名
else
folderName += sqlNote.getSnippet();
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();//使用iterator作为map接口对map进行遍历
while (iter.hasNext()) {//iterator迭代器通过统一的接口迭代所有的map元素
Map.Entry<String, TaskList> entry = iter.next();
String gid = entry.getKey();
TaskList list = entry.getValue();
if (list.getName().equals(folderName)) {//代码块:若寻找到的任务列表已存在,则直接在里面更新
tasklist = list;//找不到可以匹配的任务链
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
}
break;
}
}
// no match we can add now
if (tasklist == null) {//若没有匹配的任务列表,则创建一个新的任务列表
tasklist = new TaskList();//直接新建一个任务链
tasklist.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().createTaskList(tasklist);
mGTaskListHashMap.put(tasklist.getGid(), tasklist);
}
n = (Node) tasklist;
}
// update local note//进行本地节点的更新
sqlNote.setGtaskId(n.getGid());//创建id间的映射
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// gid-id mapping//进行gid与nid映射关系的更新
mGidToNid.put(n.getGid(), sqlNote.getId());//代码块更新gid-nid映射表
mNidToGid.put(sqlNote.getId(), n.getGid());
}
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {//更新远程节点
if (mCancelled) {//如果正在取消同步,直接返回
return;
}
SqlNote sqlNote = new SqlNote(mContext, c);//语句:在指针指向处创建一个新的节点
// update remotely
node.setContentByLocalJSON(sqlNote.getContent());//代码块:远程更新
GTaskClient.getInstance().addUpdateNode(node);//GTaskClient用途为从本地登陆远端服务器
// update meta
updateRemoteMeta(node.getGid(), sqlNote);//语句:更新数据
// move task if necessary
if (sqlNote.isNoteType()) {//判断节点类型是否符合要求
Task task = (Task) node;//新建一个task节点
TaskList preParentList = task.getParent();//找到当前任务列表的父节点google id和之前父任务链
String curParentGid = mNidToGid.get(sqlNote.getParentId());//curParentGid为通过光标在数据库中找到sqlNote的mParentId再通过mNidToGid由long类型转为String类型的Gid
if (curParentGid == null) {//代码块:找不到当前任务的上一级任务列表,报错
Log.e(TAG, "cannot find task's parent tasklist");//错误,无法找到任务的父列表
throw new ActionFailureException("cannot update remote task");//抛出异常,无法添加云端任务
}
TaskList curParentList = mGTaskListHashMap.get(curParentGid);//通过HashMap找到对应Gid的TaskList
if (preParentList != curParentList) {//语句:若两个上一级任务列表不一致,进行任务的移动,从之前的任务列表中移动到该列表中
preParentList.removeChildTask(task);
curParentList.addChildTask(task);
GTaskClient.getInstance().moveTask(task, preParentList, curParentList);
}
}
// clear local modified flag
sqlNote.resetLocalModified();//清除本地的modified flag
sqlNote.commit(true);
}
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {//更新远程元数组的google id和数据库节点
if (sqlNote != null && sqlNote.isNoteType()) {//判断节点类型是否符合,类型符合时才进行更新操作
MetaData metaData = mMetaHashMap.get(gid);//从google id获取任务列表
if (metaData != null) {//语句:若元数据组为空,则创建一个新的元数据组
metaData.setMeta(gid, sqlNote.getContent());//任务列表不存在时 新建一个空的任务列表并将其放入google id中
GTaskClient.getInstance().addUpdateNode(metaData);
} else {//语句:若元数据组不为空,则进行更新
metaData = new MetaData();
metaData.setMeta(gid, sqlNote.getContent());
mMetaList.addChildTask(metaData);
mMetaHashMap.put(gid, metaData);
GTaskClient.getInstance().createTask(metaData);
}
}
}
private void refreshLocalSyncId() throws NetworkFailureException {//刷新本地便签id从远程同步
if (mCancelled) {
return;
}//获取最近的gtask list
// get the latest gtask list//获取最新的gtask列表
mGTaskHashMap.clear();//否则初始化hash表
mGTaskListHashMap.clear();
mMetaHashMap.clear();
initGTaskList();
Cursor c = null;
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");//query语句五个参数NoteColumns.TYPE + " DESC"-----为按类型递减顺序返回查询结果。newString[{String.valueOf(Notes.TYPE_SYSTEM),String.valueOf(Notes.ID_TRASH_FOLER)}------为选择参数。"(type<>? AND parent_id<>?)"-------指明返回行过滤器。SqlNote.PROJECTION_NOTE--------应返回的数据列的名字。Notes.CONTENT_NOTE_URI--------contentProvider包含所有数据集所对应的uri
if (c != null) {
while (c.moveToNext()) {//获取最新的GTask列表
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues();
values.put(NoteColumns.SYNC_ID, node.getLastModified());//在ContentValues中创建键值对。准备通过contentResolver写入数据
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else {
Log.e(TAG, "something is missed");//错误,部分内容丢失
throw new ActionFailureException(
"some local items don't have gid after sync");
}
}
} else {//进行批量更改选择参数为NULL应该可以用insert替换参数分别为表名和需要更新的value对象。
Log.w(TAG, "failed to query local note to refresh sync id");//警告,询问本地待同步更新的便签 id 失败
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
}
public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name;//返回待同步帐户的名称
}//获取同步账号
public void cancelSync() {
mCancelled = true;
}//取消同步置mCancelled为true
}

@ -0,0 +1,128 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)//GTask同步服务用于提供同步服务开始、取消同步发送广播
*
* 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;//Service是Android四大组件之一通常用作在后台处理耗时的逻辑不用与用户进行交互即使应用被销毁依然可以继续工作。
import android.app.Activity;//引用包
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public class GTaskSyncService extends Service {//Service是在一段不定的时间运行在后台不和用户交互的应用组件
public final static String ACTION_STRING_NAME = "sync_action_type";//定义一系列静态变量
public final static int ACTION_START_SYNC = 0;//开始同步
public final static int ACTION_CANCEL_SYNC = 1;//取消同步
public final static int ACTION_INVALID = 2;//活动非法为2
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";//服务广播的名称
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";//正在同步中
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";//进程消息
private static GTaskASyncTask mSyncTask = null;
private static String mSyncProgress = "";
private void startSync() {//开始同步
if (mSyncTask == null) {//若当前没有同步工作申请一个task并把指针指向新任务广播后执行
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {//实现了在GTaskASyncTask类中定义的接口onComplete( )
mSyncTask = null;
sendBroadcast("");//广播同步消息
stopSelf();//当完成所有功能之后将service停掉
}
});
sendBroadcast("");
mSyncTask.execute();//语句:调用同步执行的函数
}
}
private void cancelSync() {//取消同步
if (mSyncTask != null) {//同步的任务非空
mSyncTask.cancelSync();//如果正在同步,结束当前同步
}
}
@Override
public void onCreate() {
mSyncTask = null;
}//初始化一个service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//充当重启便签指令
Bundle bundle = intent.getExtras();//Bundle类用作携带数据用于存放key-value明值对应形式的值。
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {//判断当前的同步状态,根据开始或取消,执行对应操作
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {//判断当前同步状态,启动或取消
case ACTION_START_SYNC://启动同步
startSync();
break;
case ACTION_CANCEL_SYNC://取消同步
cancelSync();
break;
default:
break;
}
return START_STICKY;//等待新的intent来是这个service继续运行
}
return super.onStartCommand(intent, flags, startId);//调用父类的函数
}
@Override//处理内存不足的情况,即取消同步
public void onLowMemory() {//广播信息msg
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
public IBinder onBind(Intent intent) {
return null;
}//函数:用于绑定操作的函数
public void sendBroadcast(String msg) {//发送广播内容
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);//创建一个新的Intent
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);//附加INTENT中的相应参数的值
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent);//发送这个通知
}
public static void startSync(Activity activity) {//启动同步
GTaskManager.getInstance().setActivityContext(activity);//任务管理器获取活动的内容
Intent intent = new Intent(activity, GTaskSyncService.class);//创建新intent
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);//将之前初始化的各种字符串加入intent
activity.startService(intent);//开始同步服务
}
public static void cancelSync(Context context) {//取消同步
Intent intent = new Intent(context, GTaskSyncService.class);//显式构建intent以函数参数context作为第一个参数以自己GTaskSyncService.class作为目标活动
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);//把想要传递的数据暂存到intent中传递数据以“键值对”形式传入。键为"sync_action_type"值为1
context.startService(intent);//使用startService执行intent在本活动基础上打开GTaskSyncService活动
}
public static boolean isSyncing() {
return mSyncTask != null;
}//判断当前是否处于同步状态
public static String getProgressString() {
return mSyncProgress;
}//返回当前同步状态
}

@ -0,0 +1,118 @@
/*
* 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.tool.GTaskStringUtils;
import org.json.JSONException;
import org.json.JSONObject;
public class MetaData extends Task {
/*
* TAG
* getSimpleName ()
*/
private final static String TAG = MetaData.class.getSimpleName();
private String mRelatedGid = null;
/*
*
* JSONObjectput ()TasksetNotes ()setName ()
*/
public void setMeta(String gid, JSONObject metaInfo) {
//对函数块进行注释
try {
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
/*
* metaInfojsonobject
*/
} catch (JSONException e) {
Log.e(TAG, "failed to put related gid");
/*
*
*/
}
setNotes(metaInfo.toString());
setName(GTaskStringUtils.META_NOTE_NAME);
}
/*
* Gid
*/
public String getRelatedGid() {
return mRelatedGid;
}
/*
*
*/
@Override
public boolean isWorthSaving() {
return getNotes() != null;
}
/*
* 使json
* TasksetContentByRemoteJSON ()
*/
@Override
public void setContentByRemoteJSON(JSONObject js) {
super.setContentByRemoteJSON(js);
if (getNotes() != null) {
try {
JSONObject metaInfo = new JSONObject(getNotes().trim());
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
} catch (JSONException e) {
Log.w(TAG, "failed to get related gid");
/*
*
*/
mRelatedGid = null;
}
}
}
/*
* 使json
*/
@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");
/*
*
*/
}
@Override
public int getSyncAction(Cursor c) {
throw new IllegalAccessError("MetaData:getSyncAction should not be called");
/*
*
*/
}
}

@ -0,0 +1,104 @@
/*
* 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;// 本地和云端都无可更新内容(即本地和云端内容一致)
public static final int SYNC_ACTION_ADD_REMOTE = 1;// 需要在远程云端增加内容
public static final int SYNC_ACTION_ADD_LOCAL = 2;// 需要在本地增加内容
public static final int SYNC_ACTION_DEL_REMOTE = 3;// 需要在远程云端删除内容
public static final int SYNC_ACTION_DEL_LOCAL = 4;// 需要在本地删除内容
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;// 需要将本地内容更新到远程云端
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;// 需要将远程云端内容更新到本地
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;// 同步出现冲突
public static final int SYNC_ACTION_ERROR = 8;// 同步出现错误
private String mGid;
private String mName;
private long mLastModified;
//记录最后一次修改时间
private boolean mDeleted;
//表征是否被删除
public Node() {
mGid = null;
mName = "";
mLastModified = 0;
mDeleted = false;
}
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;
}
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;
}
public String getName() {
return this.mName;
}
public long getLastModified() {
return this.mLastModified;
}
public boolean getDeleted() {
return this.mDeleted;
}
}

@ -0,0 +1,218 @@
/*
* 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.
*/
/*
* Description便sqlnotedatanote
* SqlData
*/
package net.micode.notes.gtask.data;
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 {
/*
* TAG
* getSimpleName ()
*/
private static final String TAG = SqlData.class.getSimpleName();
private static final int INVALID_ID = -99999;
/*
* NotesDataColumn
*/
// 集合了interface DataColumns中所有SF常量
public static final String[] PROJECTION_DATA = new String[] {
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
DataColumns.DATA3
};
/*
* sql5
*/
public static final int DATA_ID_COLUMN = 0;
public static final int DATA_MIME_TYPE_COLUMN = 1;
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;
//判断是否直接用Content生成是为true否则为false
private boolean mIsCreate;
private long mDataId;
private String mDataMimeType;
private String mDataContent;
private long mDataContentData1;
private String mDataContentData3;
private ContentValues mDiffDataValues;
/*
*
* mContentResolverContentProvider
* mIsCreate
*/
public SqlData(Context context) {
mContentResolver = context.getContentResolver();
mIsCreate = true;
mDataId = INVALID_ID;
mDataMimeType = DataConstants.NOTE;
mDataContent = "";
mDataContentData1 = 0;
mDataContentData3 = "";
mDiffDataValues = new ContentValues();
}
public SqlData(Context context, Cursor c) {
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(c);
mDiffDataValues = new ContentValues();
}
/*
*
*
*/
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);
}
/*
*
*/
public void setContent(JSONObject js) throws JSONException {
//如果传入的JSONObject对象中有DataColumns.ID这一项则设置否则设为INVALID_ID
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.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;
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);
}
mDataContent = dataContent;
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.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;
}
/*
*
*/
public JSONObject getContent() throws JSONException {
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
//创建JSONObject对象。并将相关数据放入其中并返回。
}
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;
}
/*
* commit
*/
public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) {
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
try {
mDataId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
} else {
if (mDiffDataValues.size() > 0) {
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
} 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)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
}
mDiffDataValues.clear();
mIsCreate = false;
}
/*
* id
*/
public long getId() {
return mDataId;
}
}

@ -0,0 +1,566 @@
/*
* 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 {
/*
* TAG
* getSimpleName ()
*/
private static final String TAG = SqlNote.class.getSimpleName();
private static final int INVALID_ID = -99999;
// 集合了interface NoteColumns中所有SF常量17个
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
};
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;
public static final int SNIPPET_COLUMN = 8;
public static final int TYPE_COLUMN = 9;
public static final int WIDGET_ID_COLUMN = 10;
public static final int WIDGET_TYPE_COLUMN = 11;
public static final int SYNC_ID_COLUMN = 12;
public static final int LOCAL_MODIFIED_COLUMN = 13;
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
public static final int GTASK_ID_COLUMN = 15;
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;
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<SqlData> mDataList;
/*
*
* mIsCreate
*/
//构造函数只有context对所有的变量进行初始化
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = true;
mId = INVALID_ID;
mAlertDate = 0;
mBgColorId = ResourceParser.getDefaultBgId(context);
mCreatedDate = System.currentTimeMillis();//调用系统函数获得创建时间
mHasAttachment = 0;
mModifiedDate = System.currentTimeMillis();//最后一次修改时间初始化为创建时间
mParentId = 0;
mSnippet = "";
mType = Notes.TYPE_NOTE;
mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
mOriginParent = 0;
mVersion = 0;
mDiffNoteValues = new ContentValues();
mDataList = new ArrayList<SqlData>();
}
/*
*
* mIsCreate
*/
//构造函数有context和一个数据库的cursor多数变量通过cursor指向的一条记录直接进行初始化
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(c);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/*
*
* mIsCreate
*/
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(id);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/*
* id
*/
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);//通过id获得对应的ContentResolver中的cursor
if (c != null) {
c.moveToNext();
loadFromCursor(c);//然后加载数据进行初始化,这样函数
//SqlNote(Context context, long id)与SqlNote(Context context, long id)的实现方式基本相同
} else {
Log.w(TAG, "loadFromCursor: cursor = null");
}
} finally {
if (c != null)
c.close();
}
}
/*
*
*/
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);
}
/*
* content
*/
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
try {
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[] {
String.valueOf(mId)
}, null);
if (c != null) {
if (c.getCount() == 0) {
Log.w(TAG, "it seems that the note has not data");
return;
}
while (c.moveToNext()) {
SqlData data = new SqlData(mContext, c);
mDataList.add(data);
}
} else {
Log.w(TAG, "loadDataContent: cursor = null");
}
} finally {
if (c != null)
c.close();
}
}
/*
* content
*/
public boolean setContent(JSONObject js) {
try {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_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) : "";
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;
} 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;
if (mIsCreate || mId != id) {
mDiffNoteValues.put(NoteColumns.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);
}
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);
}
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);
}
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);
}
mOriginParent = originParent;
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
if (data.has(DataColumns.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) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
}
return true;
}
/*
* contentnote
*/
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();
if (mType == Notes.TYPE_NOTE) {
//类型为note时
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);
}
}
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);
}
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return null;
}
/*
* idid
*/
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
/*
* idGtaskid
*/
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
/*
* idid
*/
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
/*
*
*/
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
/*
* id
*/
public long getId() {
return mId;
}
/*
* idid
*/
public long getParentId() {
return mParentId;
}
/*
* 便
*/
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);
}
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
mId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
if (mId == 0) {
throw new IllegalStateException("Create thread id failed");
}
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
//直接使用sqldata中的实现
sqlData.commit(mId, false, -1);
}
}
} 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");
}
if (mDiffNoteValues.size() > 0) {
mVersion ++;
int result = 0;
if (!validateVersion) {
//构造字符串
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
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);
}
}
}
// refresh local info
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues.clear();
mIsCreate = false;
}
}

@ -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;
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 {
private static final String TAG = Task.class.getSimpleName();
private boolean mCompleted;//是否完成
private String mNotes;
private JSONObject mMetaInfo;//将在实例中存储数据的类型
private Task mPriorSibling;//对应的优先兄弟Task的指针待完善
private TaskList mParent;//所在的任务列表的指针
public Task() {
super();
mCompleted = false;
mNotes = null;
mPriorSibling = null;//TaskList中当前Task前面的Task的指针
mParent = null;//当前Task所在的TaskList
mMetaInfo = null;
}
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);
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
// entity_delta
JSONObject entity = new JSONObject();
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());
}
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
// parent_id
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());
// 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());
// prior_sibling_id
if (mPriorSibling != null) {
js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate task-create jsonobject");
}
return js;
}
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));
}
// name
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
}
// notes
if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) {
setNotes(js.getString(GTaskStringUtils.GTASK_JSON_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");
}
}
}
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;
}
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;
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
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 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);
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;
}
}
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;
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;
}
if (!noteInfo.has(NoteColumns.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)) {
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;
}
} 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;
}
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 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;
}
public void setNotes(String notes) {
this.mNotes = notes;
}
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;
}
public Task getPriorSibling() {
return this.mPriorSibling;
}
public TaskList getParent() {
return this.mParent;
}
}

@ -0,0 +1,401 @@
/*
* 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;
public class TaskList extends Node {
private static final String TAG = TaskList.class.getSimpleName();//tag标记
private int mIndex;//当前TaskList的指针
private ArrayList<Task> mChildren;//类中主要的保存数据的单元用来实现一个以Task为元素的ArrayList
public TaskList() {
super();
mChildren = new ArrayList<Task>();
mIndex = 1;
}
/* (non-Javadoc)
* @see net.micode.notes.gtask.data.Node#getCreateAction(int)
* JSONObject
*/
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);
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);
// entity_delta
JSONObject entity = new JSONObject();
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);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate tasklist-create jsonobject");
}
return js;
}
/* (non-Javadoc)
* @see net.micode.notes.gtask.data.Node#getUpdateAction(int)
* JSONObject
*/
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());
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) {
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));
}
} 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) {
if (js == null || !js.has(GTaskStringUtils.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);
} 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);
else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER)
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_CALL_NOTE);
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() {
try {
JSONObject js = new JSONObject();
JSONObject folder = new JSONObject();
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);
if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT)
|| folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE))
folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
else
folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
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()) {
// 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())) {
Log.e(TAG, "gtask id doesn't match");
return SYNC_ACTION_ERROR;
}
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;
}
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return SYNC_ACTION_ERROR;
}
/*
* TaskListmChildren
*/
public int getChildTaskCount() {
return mChildren.size();
}
/**
* @param task
* @return
*
*/
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);
//注意每一次ArrayList的变化都要紧跟相关Task中PriorSibling的更改
//,接下来几个函数都有相关操作
}
}
return ret;
}
/**
* @param task
* @param index
* @return
*
*/
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;
}
/**
* @param task
* @return
* TaskListTask
*/
/**
* @param task
* @param index
* @return
* TaskListTaskindex
*/
public boolean removeChildTask(Task task) {
boolean ret = false;
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));
}
}
}
return ret;
}
/**
* @param gid
* @return
* gidTask
*/
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));
//利用已实现好的功能完成当下功能;
}
/**
* @param task
* @return
* Taskindex
*/
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;
}
/**
* @param index
* @return
* indexTask
*/
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);
}
/**
* @param gid
* @return
* gidTask
*/
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {//一种常见的ArrayList的遍历方法四种见精读笔记
if (task.getGid().equals(gid))
return task;
}
return null;
}
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
public void setIndex(int index) {
this.mIndex = index;
}
public int getIndex() {
return this.mIndex;
}
}

@ -0,0 +1,2 @@
#Fri Apr 07 15:33:17 CST 2023
gradle.version=7.5

Binary file not shown.

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$PROJECT_DIR$/../gradle-8.0.2" />
<option name="gradleJvm" value="jbr-11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="jbr-11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$PROJECT_DIR$/../../gradle-8.0.2" />
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="NONE" />
</component>
<component name="ChangeListManager">
<list default="true" id="63d402ba-e296-4bf0-a498-7b96b335bce7" name="变更" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ProjectId" id="2P35ITg8ZBF4JZu3OwS7u3jWkAI" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"cidr.known.project.marker": "true",
"last_opened_file_path": "D:/MI_Note/beifen1"
}
}]]></component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="默认任务">
<changelist id="63d402ba-e296-4bf0-a498-7b96b335bce7" name="变更" comment="" />
<created>1682672845153</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1682672845153</updated>
</task>
<servers />
</component>
</project>

@ -0,0 +1,20 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 33
buildToolsVersion "33.0.2"
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "net.micode.notes"
minSdkVersion 14
targetSdkVersion 14
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}

@ -0,0 +1,12 @@
/**
* Automatically generated file. DO NOT MODIFY
*/
package net.micode.notes;
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "net.micode.notes";
public static final String BUILD_TYPE = "debug";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "0.1";
}

@ -0,0 +1,12 @@
/**
* Automatically generated file. DO NOT MODIFY
*/
package net.micode.notes;
public final class BuildConfig {
public static final boolean DEBUG = false;
public static final String APPLICATION_ID = "net.micode.notes";
public static final String BUILD_TYPE = "release";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "0.1";
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save