Compare commits

..

19 Commits

9
.gitignore vendored

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

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

@ -0,0 +1,8 @@
<?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>

@ -41,6 +41,7 @@
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan" >
<intent-filter>

190
NOTICE

@ -0,0 +1,190 @@
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

@ -0,0 +1,16 @@
<?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>

@ -0,0 +1,23 @@
[中文]
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

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

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before

Width:  |  Height:  |  Size: 412 KiB

After

Width:  |  Height:  |  Size: 412 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

@ -0,0 +1,345 @@
/*
* 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.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
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.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class BackupUtils {
private static final String TAG = "BackupUtils";//定义一个常量,用于日志输出的标签
// Singleton stuff
private static BackupUtils sInstance; // 定义一个静态变量,用于保存单例对象的引用
public static synchronized BackupUtils getInstance(Context context) {// 定义一个静态同步方法,用于获取单例对象的实例
if (sInstance == null) {// 如果单例对象还没有创建
sInstance = new BackupUtils(context);// 就用传入的上下文参数创建一个新的单例对象
}
return sInstance;// 返回单例对象的引用
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0;// 定义一个常量,表示当前的状态是 SD 卡没有挂载
// The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;// 定义一个常量,表示当前的状态是备份文件不存在
// The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2;// 定义一个常量,表示当前的状态是数据格式不正确,可能被其他程序修改
// Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3;// 定义一个常量,表示当前的状态是系统错误,导致恢复或备份失败
// Backup or restore success
public static final int STATE_SUCCESS = 4;// 定义一个常量,表示当前的状态是恢复或备份成功
private TextExport mTextExport;// 定义一个私有变量,用于保存一个 TextExport 对象的引用
private BackupUtils(Context context) {
// 定义一个私有构造器,用于创建 BackupUtils 对象mTextExport = new TextExport(context);// 用传入的上下文参数创建一个 TextExport 对象,并赋值给 mTextExport 变量
}
private static boolean externalStorageAvailable() {// 定义一个私有静态方法,用于判断外部存储是否可用
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
public int exportToText() {
// 定义一个公有方法,用于导出文本文件
return mTextExport.exportToText();//调用 mTextExport 对象的 exportToText 方法,并返回其结果
}
public String getExportedTextFileName() {// 定义一个公有方法,用于获取导出的文本文件名
return mTextExport.mFileName;// 返回 mTextExport 对象的 mFileName 变量的值
}
public String getExportedTextFileDir() {// 定义一个公有方法,用于获取导出的文本文件目录
return mTextExport.mFileDirectory;// 返回 mTextExport 对象的 mFileDirectory 变量的值
}
private static class TextExport {// 定义一个私有静态内部类,用于实现文本文件的导出功能
private static final String[] NOTE_PROJECTION = { // 定义一个私有静态常量数组,用于指定查询笔记表时需要返回的列名
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
private static final int NOTE_COLUMN_ID = 0;// 定义一个私有静态常量,表示笔记 ID 列在 NOTE_PROJECTION 数组中的索引
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;// 定义一个私有静态常量,表示笔记修改日期列在 NOTE_PROJECTION 数组中的索引
private static final int NOTE_COLUMN_SNIPPET = 2; // 定义一个私有静态常量,表示笔记摘要列在 NOTE_PROJECTION 数组中的索引
private static final String[] DATA_PROJECTION = {// 定义一个私有静态常量数组,用于指定查询数据表时需要返回的列名
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
private static final int DATA_COLUMN_CONTENT = 0;// 定义一个私有静态常量,表示数据内容列在 DATA_PROJECTION 数组中的索引
private static final int DATA_COLUMN_MIME_TYPE = 1;// 定义一个私有静态常量,表示数据 MIME 类型列在 DATA_PROJECTION 数组中的索引
private static final int DATA_COLUMN_CALL_DATE = 2;// 定义一个私有静态常量,表示数据 MIME 类型列在 DATA_PROJECTION 数组中的索引
private static final int DATA_COLUMN_PHONE_NUMBER = 4;// 定义一个私有静态常量,表示数据 3 列在 DATA_PROJECTION 数组中的索引,用于存储电话号码
private final String [] TEXT_FORMAT;// 定义一个私有不可变数组,用于存储文本文件的格式字符串
private static final int FORMAT_FOLDER_NAME = 0;// 定义一个私有静态常量,表示文件夹名称格式在 TEXT_FORMAT 数组中的索引
private static final int FORMAT_NOTE_DATE = 1;// 定义一个私有静态常量,表示笔记日期格式在 TEXT_FORMAT 数组中的索引
private static final int FORMAT_NOTE_CONTENT = 2;// 定义一个私有静态常量,表示笔记内容格式在 TEXT_FORMAT 数组中的索引
private Context mContext;// 定义一个私有变量,用于保存上下文对象的引用
private String mFileName;// 定义一个私有变量,用于保存导出的文本文件名
private String mFileDirectory;// 定义一个私有变量,用于保存导出的文本文件目录
public TextExport(Context context) {// 定义一个公有构造器,用于创建 TextExport 对象
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);// 用传入的上下文参数获取资源数组,并赋值给 TEXT_FORMAT 数组
mContext = context; // 用传入的上下文参数赋值给 mContext 变量
mFileName = "";// 初始化 mFileName 变量为空字符串
mFileDirectory = "";// 初始化 mFileDirectory 变量为空字符串
}
private String getFormat(int id) {
return TEXT_FORMAT[id];
}// 定义一个私有方法,用于根据索引获取格式字符串
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {// 定义一个私有方法,用于导出指定文件夹 ID 的文本文件,需要传入文件夹 ID 和打印流对象作为参数
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,// 用 mContext 变量获取内容解析器,并查询笔记表的 URI
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {// 指定需要返回的列名数组为 NOTE_PROJECTION
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(// 用打印流对象打印一行字符串,格式化为笔记日期格式,内容为游标当前行的修改日期列的值,转换为指定的日期时间格式
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));// 用游标获取当前行的修改日期列的值,转换为长整型
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);// 用游标获取当前行的笔记 ID 列的值,转换为字符串,并赋值给 noteId 变量
exportNoteToText(noteId, ps);// 调用 exportNoteToText 方法,传入笔记 ID 和打印流对象作为参数,导出该笔记的文本文件
} while (notesCursor.moveToNext());// 循环条件为游标移动到下一行,直到没有更多行为止
}
notesCursor.close();//关闭游标对象,释放资源
}
}
/**
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) { // 定义一个私有方法,用于导出指定笔记 ID 的文本文件,需要传入笔记 ID 和打印流对象作为参数
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,// 用 mContext 变量获取内容解析器,并查询数据表的 URI
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {// 指定查询条件为笔记 ID 等于传入的笔记 ID noteId }, null); // 指定排序方式为 null
noteId
}, null);
if (dataCursor != null) { // 如果数据游标不为空
if (dataCursor.moveToFirst()) { // 如果数据游标移动到第一行
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); // 获取数据的类型
if (DataConstants.CALL_NOTE.equals(mimeType)) { // 如果数据是通话记录
// 打印电话号码
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); // 获取电话号码
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); // 获取通话日期
String location = dataCursor.getString(DATA_COLUMN_CONTENT); // 获取通话附件位置
if (!TextUtils.isEmpty(phoneNumber)) { // 如果电话号码不为空
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber)); // 格式化并打印电话号码
}
// 打印通话日期
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate))); // 格式化并打印通话日期
// 打印通话附件位置
if (!TextUtils.isEmpty(location)) { // 如果通话附件位置不为空
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location)); // 格式化并打印通话附件位置
}
} else if (DataConstants.NOTE.equals(mimeType)) { // 如果数据是便签
String content = dataCursor.getString(DATA_COLUMN_CONTENT); // 获取便签内容
if (!TextUtils.isEmpty(content)) { // 如果便签内容不为空
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content)); // 格式化并打印便签内容
}
}
} while (dataCursor.moveToNext()); // 循环直到数据游标移动到最后一行
}
dataCursor.close(); // 关闭数据游标
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
}); // 尝试写入一个字节数组,包含换行符和字母数字
} catch (IOException e) { // 如果发生输入输出异常
Log.e(TAG, e.toString()); // 打印错误日志
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() { // 定义一个导出文本的方法
if (!externalStorageAvailable()) { // 如果外部存储不可用
Log.d(TAG, "Media was not mounted"); // 打印调试日志
return STATE_SD_CARD_UNMOUONTED; // 返回SD卡未挂载的状态
}
PrintStream ps = getExportToTextPrintStream(); // 获取导出文本的打印流
if (ps == null) { // 如果打印流为空
Log.e(TAG, "get print stream error"); // 打印错误日志
return STATE_SYSTEM_ERROR; // 返回系统错误的状态
}
// 首先导出文件夹和它们的便签
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); // 查询文件夹类型的便签,排除回收站和通话记录文件夹
if (folderCursor != null) { // 如果文件夹游标不为空
if (folderCursor.moveToFirst()) { // 如果文件夹游标移动到第一行
do {
// 打印文件夹的名字
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { // 如果文件夹是通话记录文件夹
folderName = mContext.getString(R.string.call_record_folder_name); // 获取通话记录文件夹的名字
} else { // 否则
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); // 获取文件夹的摘要作为名字
}
if (!TextUtils.isEmpty(folderName)) { // 如果文件夹名字不为空
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); // 格式化并打印文件夹名字
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID); // 获取文件夹的ID
exportFolderToText(folderId, ps); // 调用导出文件夹到文本的方法传入文件夹ID和打印流
} while (folderCursor.moveToNext()); // 循环直到文件夹游标移动到最后一行
}
folderCursor.close(); // 关闭文件夹游标
}
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null); // 查询便签类型的便签,且没有父文件夹
if (noteCursor != null) { // 如果便签游标不为空
if (noteCursor.moveToFirst()) { // 如果便签游标移动到第一行
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); // 格式化并打印便签的修改日期
// 查询属于这个便签的数据
String noteId = noteCursor.getString(NOTE_COLUMN_ID); // 获取便签的ID
exportNoteToText(noteId, ps); // 调用导出便签到文本的方法传入便签ID和打印流
} while (noteCursor.moveToNext()); // 循环直到便签游标移动到最后一行
}
noteCursor.close(); // 关闭便签游标
}
ps.close(); // 关闭打印流
return STATE_SUCCESS; // 返回成功的状态
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
private PrintStream getExportToTextPrintStream() { // 定义一个获取导出文本的打印流的方法
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format); // 调用生成挂载在SD卡上的文件的方法传入上下文文件路径和文件名格式
if (file == null) { // 如果文件为空
Log.e(TAG, "create file to exported failed"); // 打印错误日志
return null; // 返回空值
}
mFileName = file.getName(); // 获取文件的名字
mFileDirectory = mContext.getString(R.string.file_path); // 获取文件的目录
PrintStream ps = null; // 声明一个打印流变量
try {
FileOutputStream fos = new FileOutputStream(file); // 创建一个文件输出流,传入文件
ps = new PrintStream(fos); // 创建一个打印流,传入文件输出流
} catch (FileNotFoundException e) { // 如果发生文件未找到异常
e.printStackTrace(); // 打印异常堆栈
return null; // 返回空值
} catch (NullPointerException e) { // 如果发生空指针异常
e.printStackTrace(); // 打印异常堆栈
return null; // 返回空值
}
return ps; // 返回打印流
}
}
/**
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { // 定义一个生成挂载在SD卡上的文件的方法传入上下文文件路径资源ID和文件名格式资源ID
StringBuilder sb = new StringBuilder(); // 创建一个字符串构建器
sb.append(Environment.getExternalStorageDirectory()); // 追加外部存储目录
sb.append(context.getString(filePathResId)); // 追加文件路径字符串
File filedir = new File(sb.toString()); // 创建一个文件目录对象,传入字符串构建器的内容
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis()))); // 追加文件名字符串,根据日期格式化
File file = new File(sb.toString()); // 创建一个文件对象,传入字符串构建器的内容
try {
if (!filedir.exists()) { // 如果文件目录不存在
filedir.mkdir(); // 创建文件目录
}
if (!file.exists()) { // 如果文件不存在
file.createNewFile(); // 创建新文件
}
return file; // 返回文件对象
} catch (SecurityException e) { // 如果发生安全异常
e.printStackTrace(); // 打印异常堆栈
} catch (IOException e) { // 如果发生输入输出异常
e.printStackTrace(); // 打印异常堆栈
}
return null; // 返回空值
}
}

@ -0,0 +1,306 @@
/*
* 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.tool;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
public class DataUtils { // 定义一个数据工具类
public static final String TAG = "DataUtils"; // 定义一个静态常量字符串,表示日志标签
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) { // 定义一个静态方法批量删除便签传入内容解析器和便签ID的集合
if (ids == null) { // 如果ID集合为空
Log.d(TAG, "the ids is null"); // 打印调试日志
return true; // 返回真值
}
if (ids.size() == 0) { // 如果ID集合的大小为零
Log.d(TAG, "no id is in the hashset"); // 打印调试日志
return true; // 返回真值
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); // 创建一个内容提供者操作的列表
for (long id : ids) { // 遍历ID集合中的每个ID
if(id == Notes.ID_ROOT_FOLDER) { // 如果ID是根文件夹的ID
Log.e(TAG, "Don't delete system folder root"); // 打印错误日志
continue; // 跳过本次循环
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 创建一个内容提供者操作的构建器指定删除便签的URI和ID
operationList.add(builder.build()); // 把构建器构建出来的操作添加到操作列表中
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 调用内容解析器的批量应用方法,传入便签的授权和操作列表,获取结果数组
if (results == null || results.length == 0 || results[0] == null) { // 如果结果数组为空或长度为零或第一个元素为空
Log.d(TAG, "delete notes failed, ids:" + ids.toString()); // 打印调试日志显示失败的ID集合
return false; // 返回假值
}
return true; // 返回真值
} catch (RemoteException e) { // 如果发生远程异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 打印错误日志,显示异常信息
} catch (OperationApplicationException e) { // 如果发生操作应用异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 打印错误日志,显示异常信息
}
return false; // 返回假值
}
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { // 定义一个静态方法移动便签到文件夹传入内容解析器便签ID源文件夹ID和目标文件夹ID
ContentValues values = new ContentValues(); // 创建一个内容值对象
values.put(NoteColumns.PARENT_ID, desFolderId); // 把目标文件夹ID作为父ID放入内容值中
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); // 把源文件夹ID作为原始父ID放入内容值中
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 把本地修改标志设为1放入内容值中
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); // 调用内容解析器的更新方法传入便签的URI和ID内容值空的选择和选择参数
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) { // 定义一个静态方法批量移动到文件夹传入内容解析器便签ID的集合和文件夹ID
if (ids == null) { // 如果ID集合为空
Log.d(TAG, "the ids is null"); // 打印调试日志
return true; // 返回真值
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); // 创建一个内容提供者操作的列表
for (long id : ids) { // 遍历ID集合中的每个ID
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 创建一个内容提供者操作的构建器指定更新便签的URI和ID
builder.withValue(NoteColumns.PARENT_ID, folderId); // 把文件夹ID作为父ID放入构建器中
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); // 把本地修改标志设为1放入构建器中
operationList.add(builder.build()); // 把构建器构建出来的操作添加到操作列表中
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 调用内容解析器的批量应用方法,传入便签的授权和操作列表,获取结果数组
if (results == null || results.length == 0 || results[0] == null) { // 如果结果数组为空或长度为零或第一个元素为空
Log.d(TAG, "delete notes failed, ids:" + ids.toString()); // 打印调试日志显示失败的ID集合
return false; // 返回假值
}
return true; // 返回真值
} catch (RemoteException e) { // 如果发生远程异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 打印错误日志,显示异常信息
} catch (OperationApplicationException e) { // 如果发生操作应用异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 打印错误日志,显示异常信息
}
return false; // 返回假值
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
public static int getUserFolderCount(ContentResolver resolver) { // 定义一个静态方法,获取用户文件夹的数量,传入内容解析器
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null); // 查询便签的URI返回文件夹类型且不在回收站的便签的数量
int count = 0; // 声明一个整型变量,表示数量
if(cursor != null) { // 如果游标不为空
if(cursor.moveToFirst()) { // 如果游标移动到第一行
try {
count = cursor.getInt(0); // 获取游标的第一列的值,即数量
} catch (IndexOutOfBoundsException e) { // 如果发生索引越界异常
Log.e(TAG, "get folder count failed:" + e.toString()); // 打印错误日志,显示异常信息
} finally {
cursor.close(); // 最终关闭游标
}
}
}
return count; // 返回数量
}
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { // 定义一个静态方法判断便签是否在数据库中可见传入内容解析器便签ID和类型
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null); // 查询便签的URI和ID返回指定类型且不在回收站的便签
boolean exist = false; // 声明一个布尔型变量,表示是否存在
if (cursor != null) { // 如果游标不为空
if (cursor.getCount() > 0) { // 如果游标的数量大于零
exist = true; // 把存在设为真值
}
cursor.close(); // 关闭游标
}
return exist; // 返回是否存在
}
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 查询便签的URI和ID返回便签的所有列
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false; // 声明一个布尔型变量,表示是否存在
if (cursor != null) { // 如果游标不为空
if (cursor.getCount() > 0) { // 如果游标的数量大于零
exist = true; // 把存在设为真值
}
cursor.close(); // 关闭游标
}
return exist; // 返回是否存在
}
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 查询数据的URI和ID返回数据的所有列
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false; // 声明一个布尔型变量,表示是否存在
if (cursor != null) { // 如果游标不为空
if (cursor.getCount() > 0) { // 如果游标的数量大于零
exist = true; // 把存在设为真值
}
cursor.close(); // 关闭游标
}
return exist; // 返回是否存在
}
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 查询便签的URI返回文件夹类型且不在回收站且名称等于指定值的便签
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false; // 声明一个布尔型变量,表示是否存在
if(cursor != null) { // 如果游标不为空
if(cursor.getCount() > 0) { // 如果游标的数量大于零
exist = true; // 把存在设为真值
}
cursor.close(); // 关闭游标
}
return exist; // 返回是否存在
}
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 查询便签的URI返回指定父ID的便签的小部件ID和类型
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet<AppWidgetAttribute> set = null; // 声明一个哈希集合,用于存储便签小部件的属性
if (c != null) { // 如果游标不为空
if (c.moveToFirst()) { // 如果游标移动到第一行
set = new HashSet<AppWidgetAttribute>(); // 创建一个新的哈希集合
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute(); // 创建一个新的便签小部件属性对象
widget.widgetId = c.getInt(0); // 获取游标的第一列的值即小部件ID
widget.widgetType = c.getInt(1); // 获取游标的第二列的值,即小部件类型
set.add(widget); // 把便签小部件属性对象添加到哈希集合中
} catch (IndexOutOfBoundsException e) { // 如果发生索引越界异常
Log.e(TAG, e.toString()); // 打印错误日志,显示异常信息
}
} while (c.moveToNext()); // 当游标移动到下一行时,重复上述操作
}
c.close(); // 关闭游标
}
return set; // 返回哈希集合
}
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 查询数据的URI返回指定便签ID和MIME类型的数据的电话号码
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
if (cursor != null && cursor.moveToFirst()) { // 如果游标不为空且移动到第一行
try {
return cursor.getString(0); // 返回游标的第一列的值,即电话号码
} catch (IndexOutOfBoundsException e) { // 如果发生索引越界异常
Log.e(TAG, "Get call number fails " + e.toString()); // 打印错误日志,显示异常信息
} finally {
cursor.close(); // 最终关闭游标
}
}
return ""; // 返回空字符串
}
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 查询数据的URI返回指定通话日期、MIME类型和电话号码的数据的便签ID
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
if (cursor != null) { // 如果游标不为空
if (cursor.moveToFirst()) { // 如果游标移动到第一行
try {
return cursor.getLong(0); // 返回游标的第一列的值即便签ID
} catch (IndexOutOfBoundsException e) { // 如果发生索引越界异常
Log.e(TAG, "Get call note id fails " + e.toString()); // 打印错误日志,显示异常信息
}
}
cursor.close(); // 关闭游标
}
return 0; // 返回0
}
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 查询便签的URI返回指定ID的便签的摘要
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
if (cursor != null) { // 如果游标不为空
String snippet = ""; // 声明一个字符串变量,表示摘要
if (cursor.moveToFirst()) { // 如果游标移动到第一行
snippet = cursor.getString(0); // 获取游标的第一列的值,即摘要
}
cursor.close(); // 关闭游标
return snippet; // 返回摘要
}
throw new IllegalArgumentException("Note is not found with id: " + noteId); // 抛出非法参数异常,显示错误信息
}
public static String getFormattedSnippet(String snippet) {
if (snippet != null) { // 如果摘要不为空
snippet = snippet.trim(); // 去掉摘要两端的空格
int index = snippet.indexOf('\n'); // 查找摘要中第一个换行符的位置
if (index != -1) { // 如果找到了换行符
snippet = snippet.substring(0, index); // 截取换行符之前的部分作为新的摘要
}
}
return snippet; // 返回格式化后的摘要字符串
}
}

@ -18,96 +18,98 @@ package net.micode.notes.tool;
public class GTaskStringUtils {
public final static String GTASK_JSON_ACTION_ID = "action_id";
// 定义一些常量字符串表示Google任务的JSON格式的属性名
public final static String GTASK_JSON_ACTION_LIST = "action_list";
public final static String GTASK_JSON_ACTION_ID = "action_id"; // 动作ID
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
public final static String GTASK_JSON_ACTION_LIST = "action_list"; // 动作列表
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
public final static String GTASK_JSON_ACTION_TYPE = "action_type"; // 动作类型
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; // 创建动作
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; // 获取所有动作
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; // 移动动作
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; // 更新动作
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
public final static String GTASK_JSON_CREATOR_ID = "creator_id"; // 创建者ID
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; // 子实体
public final static String GTASK_JSON_COMPLETED = "completed";
public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; // 客户端版本
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
public final static String GTASK_JSON_COMPLETED = "completed"; // 完成状态
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; // 当前列表ID
public final static String GTASK_JSON_DELETED = "deleted";
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; // 默认列表ID
public final static String GTASK_JSON_DEST_LIST = "dest_list";
public final static String GTASK_JSON_DELETED = "deleted"; // 删除状态
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
public final static String GTASK_JSON_DEST_LIST = "dest_list"; // 目标列表
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; // 目标父实体
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; // 目标父实体类型
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; // 实体变化
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; // 实体类型
public final static String GTASK_JSON_ID = "id";
public final static String GTASK_JSON_GET_DELETED = "get_deleted"; // 获取删除状态
public final static String GTASK_JSON_INDEX = "index";
public final static String GTASK_JSON_ID = "id"; // ID
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
public final static String GTASK_JSON_INDEX = "index"; // 索引
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; // 最后修改时间
public final static String GTASK_JSON_LIST_ID = "list_id";
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; // 最新同步点
public final static String GTASK_JSON_LISTS = "lists";
public final static String GTASK_JSON_LIST_ID = "list_id"; // 列表ID
public final static String GTASK_JSON_NAME = "name";
public final static String GTASK_JSON_LISTS = "lists"; // 列表
public final static String GTASK_JSON_NEW_ID = "new_id";
public final static String GTASK_JSON_NAME = "name"; // 名称
public final static String GTASK_JSON_NOTES = "notes";
public final static String GTASK_JSON_NEW_ID = "new_id"; // 新ID
public final static String GTASK_JSON_PARENT_ID = "parent_id";
public final static String GTASK_JSON_NOTES = "notes"; // 便签
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
public final static String GTASK_JSON_PARENT_ID = "parent_id"; // 父ID
public final static String GTASK_JSON_RESULTS = "results";
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; // 前一个兄弟ID
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
public final static String GTASK_JSON_RESULTS = "results"; // 结果
public final static String GTASK_JSON_TASKS = "tasks";
public final static String GTASK_JSON_SOURCE_LIST = "source_list"; // 源列表
public final static String GTASK_JSON_TYPE = "type";
public final static String GTASK_JSON_TASKS = "tasks"; // 任务
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
public final static String GTASK_JSON_TYPE = "type"; // 类型
public final static String GTASK_JSON_TYPE_TASK = "TASK";
public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; // 组类型
public final static String GTASK_JSON_USER = "user";
public final static String GTASK_JSON_TYPE_TASK = "TASK"; // 任务类型
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
public final static String GTASK_JSON_USER = "user"; // 用户
public final static String FOLDER_DEFAULT = "Default";
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; // MIUI文件夹的前缀
public final static String FOLDER_CALL_NOTE = "Call_Note";
public final static String FOLDER_DEFAULT = "Default"; // 默认文件夹的名称
public final static String FOLDER_META = "METADATA";
public final static String FOLDER_CALL_NOTE = "Call_Note"; // 通话便签文件夹的名称
public final static String META_HEAD_GTASK_ID = "meta_gid";
public final static String FOLDER_META = "METADATA"; // 元数据文件夹的名称
public final static String META_HEAD_NOTE = "meta_note";
public final static String META_HEAD_GTASK_ID = "meta_gid"; // 元数据中的Google任务ID的头部
public final static String META_HEAD_DATA = "meta_data";
public final static String META_HEAD_NOTE = "meta_note"; // 元数据中的便签的头部
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
public final static String META_HEAD_DATA = "meta_data"; // 元数据中的数据的头部
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; // 元数据便签的名称,提示不要更新和删除
}

@ -0,0 +1,214 @@
/*
* 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.tool;
import android.content.Context;
import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
public class ResourceParser {
// 定义一些常量整型,表示便签的背景颜色
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
// 定义一个常量整型,表示默认的背景颜色
public static final int BG_DEFAULT_COLOR = YELLOW;
// 定义一些常量整型,表示便签的文字大小
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
// 定义一个常量整型,表示默认的文字大小
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
// 定义一个静态内部类,用于存储便签的背景资源
public static class NoteBgResources {
// 定义一个静态整型数组,表示编辑模式下的便签背景资源
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
// 定义一个静态整型数组,表示编辑模式下的便签标题背景资源
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
// 定义一个静态方法根据ID获取便签背景资源
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
// 定义一个静态方法根据ID获取便签标题背景资源
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
// 定义一个静态方法获取默认的背景ID
public static int getDefaultBgId(Context context) {
// 如果用户设置了随机背景颜色
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
// 返回一个随机的背景ID
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
// 否则返回默认的背景ID
return BG_DEFAULT_COLOR;
}
}
// 这是一个公共静态类,用于存储笔记项的背景资源
public static class NoteItemBgResources {
// 这是一个整型数组,用于存储第一个笔记项的背景资源
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
// 这是一个整型数组,用于存储中间笔记项的背景资源
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
// 这是一个整型数组,用于存储最后一个笔记项的背景资源
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
// 这是一个整型数组,用于存储单个笔记项的背景资源
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
// 这是一个公共静态方法用于根据id返回第一个笔记项的背景资源
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
// 这是一个公共静态方法用于根据id返回最后一个笔记项的背景资源
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
// 这是一个公共静态方法用于根据id返回单个笔记项的背景资源
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
// 这是一个公共静态方法用于根据id返回中间笔记项的背景资源
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
// 这是一个公共静态方法,用于返回文件夹的背景资源
// 这是一个公共静态方法,用于返回文件夹的背景资源
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
// 这是一个公共静态类,用于存储小部件的背景资源
public static class WidgetBgResources {
// 这是一个整型数组用于存储2x2小部件的背景资源
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
// 这是一个公共静态方法用于根据id返回2x2小部件的背景资源
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
// 这是一个整型数组用于存储4x4小部件的背景资源
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
// 这是一个公共静态方法用于根据id返回4x4小部件的背景资源
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
// 这是一个公共静态类,用于存储文本外观的资源
public static class TextAppearanceResources {
// 这是一个整型数组,用于存储不同大小的文本外观资源
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
// 这是一个公共静态方法用于根据id返回文本外观资源
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
// 这是一个公共静态方法,用于返回文本外观资源的大小
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}

@ -41,34 +41,42 @@ import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
//. 继承了Activity类改类实现了OnClickListener和 OnDismissListener两个接口OnClickListener接口用来监听点击事件OnDismissListener接口用来监听关闭对话框事件
private long mNoteId;//文本在数据库中的ID号
private String mSnippet;//闹钟提示时显示的文本
private static final int SNIPPET_PREW_MAX_LEN = 60;//文本最大长度
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);//Bundle类型的数据与Map类型的数据相似都是以key-value的形式存储数据的
//onsaveInstanceState方法是用来保存Activity的状态的
//能从onCreate的参数savedInsanceState中获得状态数据
requestWindowFeature(Window.FEATURE_NO_TITLE);//界面显示无标题
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn()) {
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
//保持屏幕亮屏
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
//唤醒屏幕
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
//允许亮屏时锁屏
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
}//手机息屏后若闹钟铃响则亮屏
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
//根据ID从数据库中获取标签的内容
//getContentResolver是实现数据共享实例存储
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
: mSnippet;//判断标签片段是否达到符合长度
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
@ -76,21 +84,23 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
showActionDialog();//弹出对话框
playAlarmSound();//闹钟发出提示音
} else {
finish();
finish();//完成闹钟动作
}
}
private boolean isScreenOn() {
//判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
private void playAlarmSound() {
//闹钟发出提示音
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
//调用系统的铃声管理URI得到闹钟提示音
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
@ -101,12 +111,14 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
try {
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
//根据Uri设置多媒体数据来源
mPlayer.prepare();//播放器准备
mPlayer.setLooping(true);//设置循环播放
mPlayer.start();//开始播放
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
e.printStackTrace();//e.printStackTrace()函数功能是抛出异常, 还将显示出更深的调用信息
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -121,37 +133,43 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.app_name);
dialog.setMessage(mSnippet);
dialog.setPositiveButton(R.string.notealert_ok, this);
//AlertDialog的构造方法全部是Protected的
//所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。
//要创建一个AlertDialog就要用到AlertDialog.Builder中的create()方法
//如这里的dialog就是新建了一个AlertDialog
dialog.setTitle(R.string.app_name);//为对话框设置标题
dialog.setMessage(mSnippet);//为对话框设置内容
dialog.setPositiveButton(R.string.notealert_ok, this);//给对话框添加"Yes"按钮
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
}//对话框添加"No"按钮
dialog.show().setOnDismissListener(this);
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
//用which来选择click后下一步的操作
case DialogInterface.BUTTON_NEGATIVE://这是取消操作
Intent intent = new Intent(this, NoteEditActivity.class);//实现两个类间的数据传输
intent.setAction(Intent.ACTION_VIEW);//设置动作属性
intent.putExtra(Intent.EXTRA_UID, mNoteId);//实现key-value对
//EXTRA_UID为keymNoteId为键
startActivity(intent);//开始动作
break;
default:
default://这是确定操作
break;
}
}
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
public void onDismiss(DialogInterface dialog) {//忽略
stopAlarmSound();//停止闹钟声音
finish();//完成该动作
}
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer.stop();//停止播放
mPlayer.release();//释放MediaPlayer对象
mPlayer = null;
}
}

@ -34,32 +34,34 @@ public class AlarmInitReceiver extends BroadcastReceiver {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
//对数据库的操作调用标签ID和闹钟时间
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override
public void onReceive(Context context, Intent intent) {
long currentDate = System.currentTimeMillis();
long currentDate = System.currentTimeMillis();//System.currentTimeMillis()产生一个当前的毫秒
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
new String[] { String.valueOf(currentDate) },//将long变量currentDate转化为字符串
null);
if (c != null) {
if (c.moveToFirst()) {
//Cursor在这里的作用是通过查找数据库中的标签内容找到和当前系统时间相等的标签
if (c != null) {//当c != null的时候将cursor移动到开始处然后执行相关对信息的读取工作直至读取结束然后关闭该cursor
if (c.moveToFirst()) {//游标移动到开始位置
do {
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
long alertDate = c.getLong(COLUMN_ALERTED_DATE);//获取便签的提醒时间
Intent sender = new Intent(context, AlarmReceiver.class);//新建一个intent类 来指向alarmreceiver 来传输数据
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));//设置数据为便签的uri和id
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
AlarmManager alermManager = (AlarmManager) context
//使用了PendingIntent方法得到可以延时向之前定义的Intent sender发送广播的PendingIntent实例Pendingintent
AlarmManager alermManager = (AlarmManager) context//新建系统的闹钟服务
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
//调用android系统方法AlarmManager,设置一个系统提醒事项设置提醒时间为当前便签中所存alertdate并设置激活提醒时,广播类Pendingintent
} while (c.moveToNext());//游标移动到下一位置
}
c.close();
c.close();//关闭游标
}
}
}

@ -23,8 +23,9 @@ import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, AlarmAlertActivity.class);
intent.setClass(context, AlarmAlertActivity.class);//启动AlarmAlertActivity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
//属性设置启动Activity模式在任务栈中会判断是否存在相同的activity如果存在那么会清除该activity之上的其他activity对象显示如果不存在则会创建一个新的activity放入栈顶
context.startActivity(intent);//使用intent启动activity
}
}

@ -29,28 +29,32 @@ import android.widget.FrameLayout;
import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout {
//FrameLayout是布局模板之一
//所有的子元素全部在屏幕的右上方
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
private static final int DATE_SPINNER_MIN_VAL = 0;
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
private static final int MINUT_SPINNER_MIN_VAL = 0;
private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
private static final int HOURS_IN_HALF_DAY = 12;//定义半天为12小时
private static final int HOURS_IN_ALL_DAY = 24;//定义一天为24小时
private static final int DAYS_IN_ALL_WEEK = 7;//定义一周为七天
private static final int DATE_SPINNER_MIN_VAL = 0;//定义日期循环最小值为0
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;//一周循环最大天数为一周天数减1天
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;//24小时制小时最小值为0
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;//定义24小时循环的最大值为23
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;// 12小时制下小时转轮最小值
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;// 12小时制下小时转轮最大值
private static final int MINUT_SPINNER_MIN_VAL = 0;//分钟轮转最小值为0
private static final int MINUT_SPINNER_MAX_VAL = 59;//分钟轮转最大值为59
private static final int AMPM_SPINNER_MIN_VAL = 0;//标志上下午的轮转值,没有具体意义
private static final int AMPM_SPINNER_MAX_VAL = 1;//标志上下午的轮转值,没有具体意义
//初始化控件
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
//NumberPicker是数字选择器
//这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午)
private Calendar mDate;
//定义了Calendar类型的变量mDate用于操作时间
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
@ -71,41 +75,43 @@ public class DateTimePicker extends FrameLayout {
updateDateControl();
onDateTimeChanged();
}
};
};//OnValueChangeListener这是时间改变监听器这里主要是对日期的监听
//将现在日期的值传递给mDateupdateDateControl是同步操作
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {//这里是对 小时Hour 的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
Calendar cal = Calendar.getInstance();//声明一个Calendar的变量cal便于后续的操作
if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
isDateChanged = true;//这里是对于12小时制时晚上11点和12点交替时对日期的更改
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
isDateChanged = true;//这里是对于12小时制时凌晨11点和12点交替时对日期的更改
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
updateAmPmControl();//这里是对于12小时制时中午11点和12点交替时对AM和PM的更改
}
} else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
isDateChanged = true;//这里是对于24小时制时晚上11点和12点交替时对日期的更改
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
isDateChanged = true;//这里是对于12小时制时凌晨11点和12点交替时对日期的更改
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);//通过数字选择器对newHour的赋值
mDate.set(Calendar.HOUR_OF_DAY, newHour);//通过set函数将新的Hour值传给mDate
onDateTimeChanged();
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
@ -116,16 +122,17 @@ public class DateTimePicker extends FrameLayout {
};
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
@Override//这里是对 分钟Minute改变的监听
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
int offset = 0;//设置offset作为小时改变的一个记录数据
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
}//如果原值为59新值为0则offset加1
//如果原值为0新值为59则offset减1
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
@ -144,7 +151,7 @@ public class DateTimePicker extends FrameLayout {
}
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { //对AM和PM的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
@ -165,61 +172,64 @@ public class DateTimePicker extends FrameLayout {
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
}//通过对数据库的访问,获取当前的系统时间
public DateTimePicker(Context context, long date) {
public DateTimePicker(Context context, long date) {//实例化时间日期选择器
this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
super(context);//获取系统时间
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
//如果当前Activity里用到别的layout比如对话框layout
//还要设置这个layout上的其他组件的内容就必须用inflate()方法先将对话框的layout找出来
//然后再用findViewById()找到它上面的其它组件
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);//设置组件最小值属性日期最小值
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);//设置组件最大值属性为日期最大值
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);//显示设置小时的视图
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);//显示设置分钟的视图
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);//设置组件最小值属性为分钟最小值
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);//设置组件最大值属性为分钟最大值
mMinuteSpinner.setOnLongPressUpdateInterval(100);//置监听长按时间间隔为100ms
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();//对24小时下的 am 与pm各属性值进行初始化
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);//显示设置上下午的视图
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);//设置组件最小值属性为上下午编码最小值
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);//设置组件最大值属性为上下午编码最大值
mAmPmSpinner.setDisplayedValues(stringsForAmPm);//设置显示上下午字符串的值
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
// update controls to initial state将日期的各参数值更新为初始化状态
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
// set to current time设置当前时间
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
mInitialising = false;//表示初始化已经结束
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
public void setEnabled(boolean enabled) {//将当前监听器的状态设置为开启状态
if (mIsEnabled == enabled) {//如果已经在开启状态,则返回
return;
}
super.setEnabled(enabled);
super.setEnabled(enabled);//将各部分设置为开启状态
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
@ -239,7 +249,7 @@ public class DateTimePicker extends FrameLayout {
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
}//实现函数——得到当前的秒数
/**
* Set the current date
@ -250,7 +260,7 @@ public class DateTimePicker extends FrameLayout {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));//实现函数功能——设置当前的时间参数是date
}
/**
@ -269,13 +279,14 @@ public class DateTimePicker extends FrameLayout {
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
}//实现函数功能——设置当前的时间,参数是各详细的变量
/**
* Get current year
*
* @return The current year
*/
//下面是得到year、month、day等值
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
@ -445,7 +456,7 @@ public class DateTimePicker extends FrameLayout {
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
mDateSpinner.invalidate();// 对于星期几的算法
}
private void updateAmPmControl() {
@ -455,7 +466,7 @@ public class DateTimePicker extends FrameLayout {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}// 对于上下午操作的算法
}
private void updateHourControl() {
@ -465,7 +476,7 @@ public class DateTimePicker extends FrameLayout {
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
} //对于小时的算法
}
/**

@ -31,19 +31,20 @@ import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance();
private Calendar mDate = Calendar.getInstance();//创建一个Calendar类型的变量 mDate方便时间的操作
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker;
private OnDateTimeSetListener mOnDateTimeSetListener;//声明一个时间日期滚动选择控件 mOnDateTimeSetListener
private DateTimePicker mDateTimePicker;//DateTimePicker控件控件一般用于让用户可以从日期列表中选择单个值。
//运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
public DateTimePickerDialog(Context context, long date) {
super(context);
public DateTimePickerDialog(Context context, long date) {//对该界面对话框的实例化
super(context);//对数据库的操作
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
setView(mDateTimePicker);//添加一个子视图
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
@ -51,16 +52,16 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
mDate.set(Calendar.MINUTE, minute);//将视图中的各选项设置为系统当前时间
updateTitle(mDate.getTimeInMillis());
}
});
mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0);
mDate.setTimeInMillis(date);//得到系统时间
mDate.set(Calendar.SECOND, 0);//将秒数设置为0
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
set24HourView(DateFormat.is24HourFormat(this.getContext()));
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);//设置按钮
set24HourView(DateFormat.is24HourFormat(this.getContext()));//时间标准化打印
updateTitle(mDate.getTimeInMillis());
}
@ -70,7 +71,7 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
}//将时间日期滚动选择控件实例化
private void updateTitle(long date) {
int flag =
@ -79,12 +80,13 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
}//android开发中常见日期管理工具类API——DateUtils按照上下午显示时间
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}//第一个参数arg0是接收到点击事件的对话框
//第二个参数arg1是该对话框上的按钮
}

@ -29,15 +29,17 @@ import net.micode.notes.R;
public class DropdownMenu {
private Button mButton;
private PopupMenu mPopupMenu;
private PopupMenu mPopupMenu;//声明一个下拉菜单
private Menu mMenu;
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
mButton.setBackgroundResource(R.drawable.dropdown_icon);//设置这个view的背景
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
//MenuInflater是用来实例化Menu目录下的Menu布局文件
//根据ID来确认menu的内容选项
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
@ -48,14 +50,14 @@ public class DropdownMenu {
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
}
}//设置菜单的监听
}
public MenuItem findItem(int id) {
return mMenu.findItem(id);
}
}//对于菜单选项的初始化,根据索引搜索菜单需要的选项
public void setTitle(CharSequence title) {
mButton.setText(title);
}
}//布局文件,设置标题
}

@ -16,7 +16,7 @@
package net.micode.notes.ui;
import android.content.Context;
import android.content.Context;//引用一些类,大多为与数据库的交互,对文件夹的列表名称等做调整
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
@ -30,23 +30,27 @@ import net.micode.notes.data.Notes.NoteColumns;
public class FoldersListAdapter extends CursorAdapter {
//CursorAdapter是Cursor和ListView的接口
//FoldersListAdapter继承了CursorAdapter的类
//主要作用是便签数据库和用户的交互
//这里就是用folder文件夹的形式展现给用户
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};
};//调用数据库中便签的ID和片段
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
public static final int NAME_COLUMN = 1;//初始化Column的id和name,id唯一标识了Colum
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
}//数据库操作
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context);
}
}//创建一个文件夹,对于各文件夹中子标签的初始化
@Override
public void bindView(View view, Context context, Cursor cursor) {
@ -55,20 +59,20 @@ public class FoldersListAdapter extends CursorAdapter {
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName);
}
}
}//将各个布局文件绑定起来
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
}//根据数据库中标签的ID得到标签的各项内容
private class FolderListItem extends LinearLayout {
private TextView mName;
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this);
super(context);//操作数据库
inflate(context, R.layout.folder_list_item, this);//根据布局文件的名字等信息将其找出来
mName = (TextView) findViewById(R.id.tv_folder_name);
}

@ -36,7 +36,7 @@ import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
//继承edittext设置便签设置文本框
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex;
@ -45,8 +45,9 @@ public class NoteEditText extends EditText {
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
//建立一个字符和整数的hash表用于链接电话网站还有邮箱
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
//一个字符串和文本的静态映射的哈希表,将字符串转化成文本内容,然后放在弹出文本框内
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
@ -56,28 +57,28 @@ public class NoteEditText extends EditText {
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
public interface OnTextViewChangeListener {
public interface OnTextViewChangeListener {//该接口用于实现对TextView组件中的文字信息进行修改
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
void onEditTextDelete(int index, String text);
void onEditTextDelete(int index, String text);//当delete键按下时删除当前编辑的文字块
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
void onEditTextEnter(int index, String text);
void onEditTextEnter(int index, String text);//当enter键按下时添加一个文字编辑块
/**
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
void onTextChange(int index, boolean hasText);//当文字发生变化时隐藏或者显示设置
}
private OnTextViewChangeListener mOnTextViewChangeListener;
private OnTextViewChangeListener mOnTextViewChangeListener;//声明文本视图变化监听器
public NoteEditText(Context context) {
public NoteEditText(Context context) {//下面有三个构造函数,继承父类的方法,各个参数不同
super(context, null);
mIndex = 0;
}
@ -90,7 +91,7 @@ public class NoteEditText extends EditText {
mOnTextViewChangeListener = listener;
}
public NoteEditText(Context context, AttributeSet attrs) {
public NoteEditText(Context context, AttributeSet attrs) {//下面两个函数都是NoteEditText的构造函数,它们同样继承了父类的构造函数,不同的是它们调用的参数不一样
super(context, attrs, android.R.attr.editTextStyle);
}
@ -101,24 +102,25 @@ public class NoteEditText extends EditText {
@Override
public boolean onTouchEvent(MotionEvent event) {
//触摸屏幕编辑便签时触发参数event为手机屏幕触摸事件封装类的对象其中封装了该事件的所有信息例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_DOWN://重写屏幕触发事件
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
x -= getTotalPaddingLeft();//减去左边控件的距离
y -= getTotalPaddingTop();
x += getScrollX();
x += getScrollX();//加上滚轮滚过的距离
y += getScrollY();
Layout layout = getLayout();
Layout layout = getLayout();//用布局控件layout根据x,y的新值设置新的位置
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event);
return super.onTouchEvent(event);//这是调用父类的方法当屏幕有Touch事件时此方法就会被调用
}
@Override
@ -126,92 +128,94 @@ public class NoteEditText extends EditText {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
//mOnTextViewChangeListener是上面接口OnTextViewChangeListener的引用指向初始化时的listener对象该对象是OnTextViewChangeListener的实现类的对象接口无法直接实例化。mOnTextViewChangeListener标志着文本是否被改变。
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
mSelectionStartBeforeDelete = getSelectionStart();
mSelectionStartBeforeDelete = getSelectionStart();//获取删除文本开始位置
break;
default:
break;
}
return super.onKeyDown(keyCode, event);
return super.onKeyDown(keyCode, event);//调用父类的方法,响应其他按键,如数字键和字母键等
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
public boolean onKeyUp(int keyCode, KeyEvent event) {//处理用户松开一个键盘按键时会触发的事件
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
if (mOnTextViewChangeListener != null) {//如果文本视图发生变化
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {//若之前有被修改并且文档不为空
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());//监听文本的删除
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
Log.d(TAG, "OnTextViewChangeListener was not seted");////其他情况报错,文档的改动监听器并没有建立
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
int selectionStart = getSelectionStart();////获取当前位置
String text = getText().subSequence(selectionStart, length()).toString();//获取选择区域后面的文本信息
setText(getText().subSequence(0, selectionStart));//根据获取的文本设置当前文本
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);//将选择区域内的文字移到下一行
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
Log.d(TAG, "OnTextViewChangeListener was not seted");//其他情况报错监听器OnTextViewChangeListener并没有建立
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event);
return super.onKeyUp(keyCode, event);//继续执行父类的其他按键弹起的事件
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {//当焦点发生变化时,会自动调用该方法来处理焦点改变的事件
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
mOnTextViewChangeListener.onTextChange(mIndex, false);
mOnTextViewChangeListener.onTextChange(mIndex, false);//mOnTextViewChangeListener子函数置false隐藏事件选项
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
mOnTextViewChangeListener.onTextChange(mIndex, true);//mOnTextViewChangeListener子函数置true显示事件选项
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
super.onFocusChanged(focused, direction, previouslyFocusedRect);//继续执行父类的其他焦点变化的事件
}
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
protected void onCreateContextMenu(ContextMenu menu) {//生成上下文菜单
if (getText() instanceof Spanned) {//如果有文本存在
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int selEnd = getSelectionEnd();//获取文本开始和结尾位置
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
int max = Math.max(selStart, selEnd);//获取开始到结尾的最大、最小值
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);//获取一段text内容并且把这段内容强制转换为Spanned类型并存入URLSpan数组urls中
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
if(urls[0].getURL().indexOf(schema) >= 0) {//若url可以添加则在添加后将defaultResId置为key所映射的值
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
if (defaultResId == 0) {//defaultResId == 0则说明url并没有添加任何东西所以置为连接其他SchemaActionResMap的值
defaultResId = R.string.note_link_other;
}
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
public boolean onMenuItemClick(MenuItem item) {//如果点击菜单执行操作
// goto a new intent
urls[0].onClick(NoteEditText.this);
urls[0].onClick(NoteEditText.this);//根据相应的文本设置菜单的按键
return true;
}
});
}
}
super.onCreateContextMenu(menu);
super.onCreateContextMenu(menu);//继续执行父类的其他菜单创建的事件
}
}

@ -33,23 +33,36 @@ import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
public abstract class NoteWidgetProvider extends AppWidgetProvider {
//查询便签数据库时使用的列名
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
//PROJECTION中对应列的下标
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
//Log输出的标签
private static final String TAG = "NoteWidgetProvider";
/*
* 重写onDeleted()方法,实现删除 Widget 时清除相应 Widget 对应的便签数据库中的记录
* value用于更新的ContentValues将 Widget ID 设为无效值
* appWidgetIds被删除的所有 Widget 的 ID 数组
*/
@Override
// 定义一个方法用于在widget被删除时更新数据库中的widget_id字段
public void onDeleted(Context context, int[] appWidgetIds) {
// 创建一个ContentValues对象用于存放要更新的字段和值
ContentValues values = new ContentValues();
// 把widget_id设置为无效的值
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
//遍历所有被删除的widget的id
for (int i = 0; i < appWidgetIds.length; i++) {
// 根据widget_id更新数据库中对应的笔记记录
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
@ -57,7 +70,9 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 定义一个方法用于根据widget_id查询数据库中的笔记信息
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
// 使用ContentResolver查询笔记表返回一个Cursor对象
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
@ -65,68 +80,90 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
null);
}
// 定义一个方法用于更新widget的视图
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用另一个重载的update方法传入false表示不是访客模式
update(context, appWidgetManager, appWidgetIds, false);
}
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取 Widget 的默认背景 ID
int bgId = ResourceParser.getDefaultBgId(context);
// 默认便签摘要为空字符串
String snippet = "";
// 创建用于启动编辑页面的 Intent并添加必要的参数
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 从数据库中查询指定 Widget ID 对应的便签信息
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
// 检查查询结果是否出现异常,如出现异常则打印错误日志和异常信息
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
// 设置 Widget 中需要显示的便签摘要、背景 ID、绑定的便签 ID 和点击跳转功能
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
intent.setAction(Intent.ACTION_VIEW);
} else {
// 若查询结果为空,则打开编辑页面并设置其模式为插入模式
snippet = context.getResources().getString(R.string.widget_havenot_content);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 关闭查询结果的 Cursor 对象
if (c != null) {
c.close();
}
// 创建 RemoteViews 对象,并为其设置相应布局及需要显示的参数
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* Generate the pending intent to start host for the widget
*/
// 定义一个变量pendingIntent初始化为null
PendingIntent pendingIntent = null;
// 如果privacyMode为真表示用户处于访客模式
if (privacyMode) {
// 设置widget_text的文本为“访客模式”
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
// 创建一个PendingIntent用于启动NotesListActivity
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 否则设置widget_text的文本为snippet即笔记的摘要
rv.setTextViewText(R.id.widget_text, snippet);
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
// 创建一个PendingIntent用于启动intent指定的Activity
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 设置widget_text的点击事件为pendingIntent
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 更新widget的视图
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
// 定义一个抽象方法用于根据bgId返回背景资源的id
protected abstract int getBgResourceId(int bgId);
// 定义一个抽象方法用于返回布局资源的id
protected abstract int getLayoutId();
// 定义一个抽象方法用于返回widget的类型
protected abstract int getWidgetType();
}

@ -0,0 +1,48 @@
/*
* 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.widget; //声明package
import android.appwidget.AppWidgetManager; //引用类
import android.content.Context; //引用类
import net.micode.notes.R; //引用类
import net.micode.notes.data.Notes; //引用类
import net.micode.notes.tool.ResourceParser; //引用类
public class NoteWidgetProvider_2x extends NoteWidgetProvider {//继承NoteWidgetProvider类
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds); //调用NoteWidgetProvider的update方法
}
@Override
protected int getLayoutId() { //重写NoteWidgetProvider中的getLayoutId方法
return R.layout.widget_2x; //返回 widget_2x 布局文件的ID
}
@Override
protected int getBgResourceId(int bgId) { //重写NoteWidgetProvider中的getBgResourceId方法接受传入的bgId参数
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); //调用ResourceParser类中的getWidget2xBgResource(bgId)方法返回该bgId对应的Widget2xBg资源ID
}
@Override
protected int getWidgetType() { //重写NoteWidgetProvider中的getWidgetType方法
return Notes.TYPE_WIDGET_2X; //返回Note中的TYPE_WIDGET_2X常量值
}
}

@ -25,20 +25,32 @@ import net.micode.notes.tool.ResourceParser;
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类中的 update 方法,进行具体的 Widget 内容更新操作
super.update(context, appWidgetManager, appWidgetIds);
}
protected int getLayoutId() {
return R.layout.widget_4x;
}
/**
* 根据传入的背景颜色 ID 获取对应的背景资源 ID
* @param bgId 背景颜色 ID
* @return 对应的背景资源 ID
*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
/**
* 获取该 Widget 对应的类型常量,用于在程序中进行判断和处理
* @return 4x4 Widget 对应的类型常量
*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

@ -1,118 +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.
* 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");
/*
*
*/
}
}

@ -1,104 +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.
* 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;
}
}

@ -1,218 +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.
* 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;
}
}

@ -1,566 +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.
* 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;
}
}

@ -1,351 +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.
* 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;
}
}

@ -1,401 +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.
* 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;
}
}

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 245 B

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 443 B

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Loading…
Cancel
Save