From 6942c6d0b83b3bbb1b6f4a298cfe82cc7a11f092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81?= <2251219137@qq.com> Date: Thu, 26 Dec 2024 23:16:32 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e69de29 -- 2.34.1 From 2af5d22b5f9a8504f277921030f065f8de11f7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81?= <2251219137@qq.com> Date: Thu, 26 Dec 2024 23:25:02 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ActionFailureException.java | 33 +++ Contact.java | 73 +++++ MetaData.java | 83 ++++++ NetworkFailureException.java | 33 +++ Node.java | 101 +++++++ Notes.java | 309 +++++++++++++++++++++ NotesDatabaseHelper.java | 380 +++++++++++++++++++++++++ NotesProvider.java | 311 +++++++++++++++++++++ SqlData.java | 190 +++++++++++++ SqlNote.java | 518 +++++++++++++++++++++++++++++++++++ Task.java | 393 ++++++++++++++++++++++++++ TaskList.java | 344 +++++++++++++++++++++++ 12 files changed, 2768 insertions(+) create mode 100644 ActionFailureException.java create mode 100644 Contact.java create mode 100644 MetaData.java create mode 100644 NetworkFailureException.java create mode 100644 Node.java create mode 100644 Notes.java create mode 100644 NotesDatabaseHelper.java create mode 100644 NotesProvider.java create mode 100644 SqlData.java create mode 100644 SqlNote.java create mode 100644 Task.java create mode 100644 TaskList.java diff --git a/ActionFailureException.java b/ActionFailureException.java new file mode 100644 index 0000000..d8e4019 --- /dev/null +++ b/ActionFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception;//包 net.micode.notes.gtask.exception。 + +public class ActionFailureException extends RuntimeException {//公共类 ActionFailureException 继承自 RuntimeException。 + private static final long serialVersionUID = 4425249765923293627L;//私有静态最终 long 型变量 serialVersionUID,赋值为 4425249765923293627L。 + + public ActionFailureException() {//公共的无参数构造函数。 + super();//调用父类的无参数构造函数。 + } + + public ActionFailureException(String paramString) {//公共的带有一个字符串参数的构造函数。 + super(paramString);//调用父类的带有一个字符串参数的构造函数,并将参数传递给父类。 + } + + public ActionFailureException(String paramString, Throwable paramThrowable) {//公共的带有一个字符串参数和一个 Throwable 参数的构造函数。 + super(paramString, paramThrowable);//调用父类的带有一个字符串参数和一个 Throwable 参数的构造函数,并将参数传递给父类。 + } +} diff --git a/Contact.java b/Contact.java new file mode 100644 index 0000000..f969a43 --- /dev/null +++ b/Contact.java @@ -0,0 +1,73 @@ +/* + * 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.data; + +import android.content.Context; +import android.database.Cursor; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.HashMap; + +public class Contact { + private static HashMap sContactCache;//声明一个静态的哈希表,用于缓存联系人信息。 + private static final String TAG = "Contact";//定义一个静态常量,用于日志输出的标记。 + + private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT raw_contact_id " + + " FROM phone_lookup" + + " WHERE min_match = '+')";//定义了一个查询条件的字符串,用于查找与给定电话号码匹配的联系人。 + + public static String getContact(Context context, String phoneNumber)//定义了一个静态方法,用于获取与给定电话号码对应的联系人姓名。 { + if(sContactCache == null) { + sContactCache = new HashMap();//如果缓存为空,则创建一个新的缓存。 + } + + if(sContactCache.containsKey(phoneNumber)) { + return sContactCache.get(phoneNumber);//检查缓存中是否已经存在该电话号码对应的联系人姓名,如果存在则直接返回。 + } + + String selection = CALLER_ID_SELECTION.replace("+", + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));//根据电话号码生成实际的查询条件。 + Cursor cursor = context.getContentResolver().query( + Data.CONTENT_URI, + new String [] { Phone.DISPLAY_NAME }, + selection, + new String[] { phoneNumber }, + null);//使用查询条件进行数据库查询,并获取游标 + + if (cursor != null && cursor.moveToFirst()) { + try { + String name = cursor.getString(0); + sContactCache.put(phoneNumber, name); + return name;//检查游标是否有效且是否有数据,如果有则尝试获取联系人姓名并放入缓存,同时返回该姓名。 + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, " Cursor get string error " + e.toString());//在获取联系人姓名时发生异常时,记录错误日志。 + return null; + } finally { + cursor.close();//关闭游标。 + } + } else { + Log.d(TAG, "No contact matched with number:" + phoneNumber);//:如果没有找到匹配的联系人,记录日志。 + return null; + } + } +} diff --git a/MetaData.java b/MetaData.java new file mode 100644 index 0000000..e192ba2 --- /dev/null +++ b/MetaData.java @@ -0,0 +1,83 @@ +/* + * 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;//“包名:net.micode.notes.gtask.data;”。 + +import android.database.Cursor;//“导入安卓数据库游标类;”。 +import android.util.Log;//导入安卓工具类 Log;”。 + +import net.micode.notes.tool.GTaskStringUtils;//翻译为“导入来自 net.micode.notes.tool 包下的 GTaskStringUtils 类;”。 + +import org.json.JSONException;//导入来自 org.json 包下的 JSON 异常类;”。 +import org.json.JSONObject;//导入来自 org.json 包下的 JSON 对象类;”。. + + +public class MetaData extends Task {//公共类 MetaData 继承自 Task 类。” + private final static String TAG = MetaData.class.getSimpleName();//“私有最终静态字符串变量 TAG 被赋值为 MetaData 类的简单名称。” + + private String mRelatedGid = null;//私有字符串变量 mRelatedGid 初始化为 null。” + + public void setMeta(String gid, JSONObject metaInfo) {//“公共方法 setMeta,接收一个字符串 gid 和一个 JSON 对象 metaInfo。” + try {//“尝试执行以下代码块。” + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);//“将 gid 放入 metaInfo 的键为 GTaskStringUtils.META_HEAD_GTASK_ID 的位置。 + } catch (JSONException e) {//“将 gid 放入 metaInfo 的键为 GTaskStringUtils.META_HEAD_GTASK_ID 的位置。 + Log.e(TAG, "failed to put related gid");//“使用错误级别记录日志,TAG 为标识,信息为‘failed to put related gid’(未能放入相关的 gid)。” + } + setNotes(metaInfo.toString());//“设置注释为 metaInfo 的字符串形式。” + setName(GTaskStringUtils.META_NOTE_NAME);//“设置注释为 metaInfo 的字符串形式。”v + } + + public String getRelatedGid() {//“公共方法 getRelatedGid,返回一个字符串。” + return mRelatedGid;//“返回 mRelatedGid 的值。” + } + + @Override + public boolean isWorthSaving() {//翻译为“重写父类方法。公共方法 isWorthSaving,返回一个布尔值。” + return getNotes() != null;//“返回获取到的注释不为 null 的结果。” + } + + @Override + public void setContentByRemoteJSON(JSONObject js) {//“重写父类方法。公共方法 setContentByRemoteJSON,接收一个 JSON 对象 js。” + super.setContentByRemoteJSON(js);//“重写父类方法。公共方法 setContentByRemoteJSON,接收一个 JSON 对象 js。” + if (getNotes() != null) {//“重写父类方法。公共方法 setContentByRemoteJSON,接收一个 JSON 对象 js。” + try {//“重写父类方法。公共方法 setContentByRemoteJSON,接收一个 JSON 对象 js。” + JSONObject metaInfo = new JSONObject(getNotes().trim());//“创建一个新的 JSON 对象 metaInfo,参数为去除首尾空白的获取到的注释。” + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);//将 metaInfo 中键为 GTaskStringUtils.META_HEAD_GTASK_ID 的值赋给 mRelatedGid。” + } catch (JSONException e) {//“捕获 JSON 异常,如果发生异常执行以下代码块。” + Log.w(TAG, "failed to get related gid");//“使用警告级别记录日志,TAG 为标识,信息为‘failed to get related gid’(未能获取到相关的 gid)。” + mRelatedGid = null;//“将 mRelatedGid 赋值为 null。” + } + } + } + + @Override + public void setContentByLocalJSON(JSONObject js) {//“重写父类方法。公共方法 setContentByLocalJSON,接收一个 JSON 对象 js。” + // this function should not be called + //此函数不应被调用。” + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");//“抛出非法访问错误,信息为‘MetaData:setContentByLocalJSON should not be called’(MetaData 的 setContentByLocalJSON 不应被调用)。” + } + + @Override + public JSONObject getLocalJSONFromContent() {//“重写父类方法。公共方法 getLocalJSONFromContent,返回一个 JSON 对象。” + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");//“抛出非法访问错误,信息为‘MetaData:getLocalJSONFromContent should not be called’(MetaData 的 getLocalJSONFromContent 不应被调用)。” + } + + @Override + public int getSyncAction(Cursor c) {//“重写父类方法。公共方法 getSyncAction,接收一个游标 c,返回一个整数。” + throw new IllegalAccessError("MetaData:getSyncAction should not be called");//抛出非法访问错误,信息为‘MetaData:getSyncAction should not be called’(MetaData 的 getSyncAction 不应被调用)。” + } + +} diff --git a/NetworkFailureException.java b/NetworkFailureException.java new file mode 100644 index 0000000..0c37bf0 --- /dev/null +++ b/NetworkFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception;//这是一个 Java 包声明,指定了该代码所在的包名为net.micode.notes.gtask.exception。 + +public class NetworkFailureException extends Exception {//定义了一个名为NetworkFailureException的公共类,它继承自Exception类。 + private static final long serialVersionUID = 2107610287180234136L;//这是一个私有静态最终的长整型变量serialVersionUID,用于标识该类的序列化版本号。 + + public NetworkFailureException() {//这是NetworkFailureException类的无参构造函数,它调用了父类Exception的无参构造函数。 + super(); + } + + public NetworkFailureException(String paramString) {//这是一个带有一个字符串参数的构造函数,它调用了父类Exception的带有一个字符串参数的构造函数,并将参数传递给父类。 + super(paramString); + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) {//这是一个带有一个字符串参数和一个Throwable参数的构造函数,它调用了父类Exception的带有一个字符串参数和一个Throwable参数的构造函数,并将参数传递给父类。 + super(paramString, paramThrowable); + } +} diff --git a/Node.java b/Node.java new file mode 100644 index 0000000..a1a4102 --- /dev/null +++ b/Node.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data;//“包名:net.micode.notes.gtask.data;”。 + +import android.database.Cursor;//“导入安卓数据库游标类;”。 + +import org.json.JSONObject;//“导入 JSON 对象类;”。 + +public abstract class Node {//“公共抽象类 Node;”。 + public static final int SYNC_ACTION_NONE = 0;//“公共静态最终整型常量 SYNC_ACTION_NONE(同步动作无)赋值为 0;”。 + + public static final int SYNC_ACTION_ADD_REMOTE = 1;//“公共静态最终整型常量 SYNC_ACTION_ADD_REMOTE(远程添加动作)赋值为 1;”。 + + public static final int SYNC_ACTION_ADD_LOCAL = 2;//公共静态最终整型常量 SYNC_ACTION_ADD_LOCAL(本地添加动作)赋值为 2;”。 + + public static final int SYNC_ACTION_DEL_REMOTE = 3;//“公共静态最终整型常量 SYNC_ACTION_DEL_REMOTE(远程删除动作)赋值为 3;”。 + + public static final int SYNC_ACTION_DEL_LOCAL = 4;//“公共静态最终整型常量 SYNC_ACTION_DEL_REMOTE(远程删除动作)赋值为 3;”。 + + public static final int SYNC_ACTION_UPDATE_REMOTE = 5;//“公共静态最终整型常量 SYNC_ACTION_UPDATE_REMOTE(远程更新动作)赋值为 5;”。 + + public static final int SYNC_ACTION_UPDATE_LOCAL = 6;//“公共静态最终整型常量 SYNC_ACTION_UPDATE_LOCAL(本地更新动作)赋值为 6;”。 + + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;//公共静态最终整型常量 SYNC_ACTION_UPDATE_CONFLICT(更新冲突动作)赋值为 7;”。 + + public static final int SYNC_ACTION_ERROR = 8;//“公共静态最终整型常量 SYNC_ACTION_ERROR(错误动作)赋值为 8;”。 + + private String mGid;//“私有字符串变量 mGid;”。 + + private String mName;//“私有字符串变量 mName;”。 + + private long mLastModified;//“私有长整型变量 mLastModified;”。 + private boolean mDeleted;//“私有长整型变量 mLastModified;”。 + + public Node() {//公共的 Node 类的构造方法。” + mGid = null;//“将成员变量 mGid 赋值为 null。” + mName = "";//将成员变量 mName 赋值为空字符串。” + mLastModified = 0;//将成员变量 mLastModified 赋值为 0。” + mDeleted = false;//将成员变量 mDeleted 赋值为 false。” + } + + public abstract JSONObject getCreateAction(int actionId);//“公共的抽象方法 getCreateAction,接收一个整数参数 actionId,返回一个 JSON 对象。” + + public abstract JSONObject getUpdateAction(int actionId);//“公共的抽象方法 getUpdateAction,接收一个整数参数 actionId,返回一个 JSON 对象。” + + public abstract void setContentByRemoteJSON(JSONObject js);//“公共的抽象方法 setContentByRemoteJSON,接收一个 JSON 对象参数 js。” + + public abstract void setContentByLocalJSON(JSONObject js);//“公共的抽象方法 setContentByRemoteJSON,接收一个 JSON 对象参数 js。” + + public abstract JSONObject getLocalJSONFromContent();//“公共的抽象方法 getLocalJSONFromContent,返回一个 JSON 对象。” + + public abstract int getSyncAction(Cursor c);//“公共的抽象方法 getSyncAction,接收一个 Cursor 参数 c,返回一个整数。” + + public void setGid(String gid) {//“公共方法 setGid,接收一个字符串参数 gid。” + this.mGid = gid;//“将成员变量 mGid 赋值为传入的 gid。” + } + + public void setName(String name) {//公共方法 setName,接收一个字符串参数 name。” +this.mName = name;//“将成员变量 mName 赋值为传入的 name。” + + } + + public void setLastModified(long lastModified) {//公共方法 setLastModified,接收一个长整型参数 lastModified。” + this.mLastModified = lastModified;//将成员变量 mLastModified 赋值为传入的 lastModified。” + } + + public void setDeleted(boolean deleted) {//“公共方法 setDeleted,接收一个布尔型参数 deleted。” + this.mDeleted = deleted;//“将成员变量 mDeleted 赋值为传入的 deleted。” + } + + public String getGid() {//“公共方法 getGid,返回一个字符串。” + return this.mGid;//返回成员变量 mGid。” + } + + public String getName() {//“公共方法 getName,返回一个字符串。” + return this.mName;//“返回成员变量 mName。” + } + + public long getLastModified() {//“公共方法 getLastModified,返回一个长整型。” + return this.mLastModified;//“返回成员变量 mLastModified。” + } + + public boolean getDeleted() {//公共方法 getDeleted,返回一个布尔型。” + return this.mDeleted;//“返回成员变量 mDeleted。” + } + +} diff --git a/Notes.java b/Notes.java new file mode 100644 index 0000000..cadb931 --- /dev/null +++ b/Notes.java @@ -0,0 +1,309 @@ +/* + * 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.data; +// 包名:net.micode.notes.data +import android.net.Uri; +// 导入 Android 中的 Uri 类 +public class Notes {// 定义名为 Notes 的公共类 + public static final String AUTHORITY = "micode_notes";// 静态常量,权限字符串为“micode_notes” + public static final String TAG = "Notes";// 静态常量,标签字符串为“ + public static final int TYPE_NOTE = 0;// 静态常量,笔记类型为 0 + public static final int TYPE_FOLDER = 1;// 静态常量,文件夹类型为 1 + public static final int TYPE_SYSTEM = 2;// 静态常量,系统类型为 2 + + /** + * Following IDs are system folders' identifiers + * {@link Notes#ID_ROOT_FOLDER } is default folder + * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder + * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + */ + public static final int ID_ROOT_FOLDER = 0;// 静态常量,根文件夹 ID 为 0 + public static final int ID_TEMPARAY_FOLDER = -1;// 静态常量,临时文件夹 ID 为 -1 + public static final int ID_CALL_RECORD_FOLDER = -2;// 静态常量,通话记录文件夹 ID 为 -2 + public static final int ID_TRASH_FOLER = -3;// 静态常量,回收站文件夹 ID 为 -3 + + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";// 静态常量,意图额外参数(提醒日期)的字符串为“net.micode.notes.alert_date” + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";// 静态常量,意图额外参数(背景颜色 ID)的字符串为“net.micode.notes.background_color_id” + public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";// 静态常量,意图额外参数(小组件 ID)的字符串为“net.micode.notes.widget_id” + public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";// 静态常量,意图额外参数(小组件类型)的字符串为“net.micode.notes.widget_type” + public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";// 静态常量,意图额外参数(文件夹 ID)的字符串为“net.micode.notes.folder_id” + public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";// 静态常量,意图额外参数(通话日期)的字符串为“net.micode.notes.call_date” + + public static final int TYPE_WIDGET_INVALIDE = -1;// 静态常量,无效小组件类型为 -1 + public static final int TYPE_WIDGET_2X = 0;// 静态常量,2X 小组件类型为 0 + public static final int TYPE_WIDGET_4X = 1;// 静态常量,2X 小组件类型为 0 + + public static class DataConstants {// 定义静态内部类 + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;// 静态常量,笔记字符串为 TextNote 类的 CONTENT_ITEM_TYPE 常量 + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;// 静态常量,通话笔记字符串为 CallNote 类的 CONTENT_ITEM_TYPE 常量 + } + + /** + * Uri to query all notes and folders + */ + public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");// 静态常量,笔记内容的 Uri 为通过解析“content://权限字符串/note”得到的 Uri。 + + /** + * Uri to query data + */ // 注释:用于查询数据的 Uri。 + public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");// 静态常量,数据内容的 Uri 为通过解析“content://权限字符串/data”得到的 Uri。 + + public interface NoteColumns {// 定义名为 NoteColumns 的公共接口 + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The parent's id for note or folder + *

Type: INTEGER (long)

+ */ + // 注释:一行的唯一 ID。类型:整数(长整型)。 + public static final String PARENT_ID = "parent_id";// 静态常量,ID 字段为“_id”。 + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + // 注释:笔记或文件夹的父级 ID。类型:整数(长整型)。 + public static final String CREATED_DATE = "created_date";// 静态常量,PARENT_ID 字段为“parent_id”。 + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + // 注释:笔记或文件夹的创建日期。类型:整数(长整型)。 + public static final String MODIFIED_DATE = "modified_date";// 静态常量,CREATED_DATE 字段为“created_date”。 + + + /** + * Alert date + *

Type: INTEGER (long)

+ */ + // 注释:最新修改日期。类型:整数(长整型)。 + public static final String ALERTED_DATE = "alert_date";// 静态常量,MODIFIED_DATE 字段为“modified_date”。 + + /** + * Folder's name or text content of note + *

Type: TEXT

+ */ + // 注释:提醒日期。类型:整数(长整型)。 + public static final String SNIPPET = "snippet";// 静态常量,ALERTED_DATE 字段为“alert_date”。 + + /** + * Note's widget id + *

Type: INTEGER (long)

+ */ + // 注释:文件夹的名称或笔记的文本内容。类型:文本。 + public static final String WIDGET_ID = "widget_id";// 静态常量,小组件 ID 为“ + + /** + * Note's widget type + *

Type: INTEGER (long)

+ */ + // 注释:笔记的小组件类型。类型:整数(长整型)。 + public static final String WIDGET_TYPE = "widget_type";// 静态常量,小组件类型字段为“widget_type”。 + + /** + * Note's background color's id + *

Type: INTEGER (long)

+ */ + // 注释:笔记的背景颜色 ID。类型:整数(长整型)。 + public static final String BG_COLOR_ID = "bg_color_id";// 静态常量,背景颜色 ID 字段为“bg_color_id”。 + + /** + * For text note, it doesn't has attachment, for multi-media + * note, it has at least one attachment + *

Type: INTEGER

+ */ + // 注释:对于文本笔记,它没有附件;对于多媒体笔记,它至少有一个附件。类型:整数。 + public static final String HAS_ATTACHMENT = "has_attachment";// 静态常量,是否有附件字段为“has_attachment”。 + + /** + * Folder's count of notes + *

Type: INTEGER (long)

+ */ + // 注释:文件夹中的笔记数量。类型:整数(长整型)。 + public static final String NOTES_COUNT = "notes_count";// 静态常量,笔记数量字段为“ + + /** + * The file type: folder or note + *

Type: INTEGER

+ */ + // 注释:文件类型:文件夹或笔记。类型:整数。 + public static final String TYPE = "type";// 静态常量,文件类型字段为“ + + /** + * The last sync id + *

Type: INTEGER (long)

+ */ + // 注释:最后一次同步的 ID。类型:整数(长整型)。 + public static final String SYNC_ID = "sync_id";// 静态常量,同步 ID 字段为“sync_id”。 + + /** + * Sign to indicate local modified or not + *

Type: INTEGER

+ */ + // 注释:用于指示是否本地修改的标志。类型:整数。 + public static final String LOCAL_MODIFIED = "local_modified";// 静态常量,本地修改标志字段为 + + /** + * Original parent id before moving into temporary folder + *

Type : INTEGER

+ */ + // 注释:移动到临时文件夹之前的原始父级 ID。类型:整数。 + public static final String ORIGIN_PARENT_ID = "origin_parent_id";// 静态常量,原始父级 ID 字段为“origin_parent_id”。 + + /** + * The gtask id + *

Type : TEXT

+ */ + // 注释:gtask 的 ID。类型:文本。 + public static final String GTASK_ID = "gtask_id";// 静态常量,gtask ID 字段为“gtask_id”。 + + /** + * The version code + *

Type : INTEGER (long)

+ */ + // 注释:版本代码。类型:整数(长整型)。 + public static final String VERSION = "version";// 静态常量,版本字段为“version”。 + } + + public interface DataColumns {// 定义名为 DataColumns 的公共接口。 + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + // 注释:一行的唯一 ID。类型:整数(长整型)。 + public static final String ID = "_id";// 静态常量,ID 字段为“_id”。 + + /** + * The MIME type of the item represented by this row. + *

Type: Text

+ */ + public static final String MIME_TYPE = "mime_type";// 静态常量,MIME 类型为“mime_type”。 + + /** + * The reference id to note that this data belongs to + *

Type: INTEGER (long)

+ */ + // 注释:此数据所属的引用 ID。类型:整数(长整型)。 + public static final String NOTE_ID = "note_id";// 静态常量,NOTE_ID 字段为“note_id”。 + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + // 注释:笔记或文件夹的创建日期。类型:整数(长整型)。 + public static final String CREATED_DATE = "created_date";// 静态常量,CREATED_DATE 字段为“created_date”。 + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + // 注释:最新修改日期。类型:整数(长整型)。 + public static final String MODIFIED_DATE = "modified_date";// 静态常量,MODIFIED_DATE 字段为“modified_date”。 + + /** + * Data's content + *

Type: TEXT

+ */ + // 注释:数据的内容。类型:文本。 + public static final String CONTENT = "content";// 静态常量,CONTENT 字段为“content”。 + + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + // 注释:通用数据列,其含义特定于 MIME 类型,用于整数数据类型。类型:整数。 + public static final String DATA1 = "data1";// 静态常量,DATA1 字段为“data1”。 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + // 注释:通用数据列,其含义特定于 MIME 类型,用于整数数据类型。类型:整数。 + public static final String DATA2 = "data2";// 静态常量,DATA2 字段为“data2”。 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + // 注释:通用数据列,其含义特定于 MIME 类型,用于文本数据类型。类型:文本。 + public static final String DATA3 = "data3";// 静态常量,DATA3 字段为“data3”。 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + // 注释:通用数据列,其含义特定于 MIME 类型,用于文本数据类型。类型:文本。 + public static final String DATA4 = "data4";// 静态常量,DATA4 字段为“data4”。 + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + // 注释:通用数据列,其含义特定于 MIME 类型,用于文本数据类型。类型:文本。 + public static final String DATA5 = "data5";// 静态常量,DATA5 字段为“data5”。 + } + + public static final class TextNote implements DataColumns {// 定义名为 TextNote 的静态内部类并实现 DataColumns 接口。 + /** + * Mode to indicate the text in check list mode or not + *

Type: Integer 1:check list mode 0: normal mode

+ */ + // 定义名为 TextNote 的静态内部类并实现 DataColumns 接口。 + public static final String MODE = DATA1;// 静态常量,模式字段为 DATA1。 + + public static final int MODE_CHECK_LIST = 1;// 静态常量,清单模式的值为 1。 + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";// 静态常量,清单模式的值为 1。 + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";// 静态常量,内容项类型为“vnd.android.cursor.item/text_note”。 + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");// 静态常量,内容 Uri 为通过解析“content://权限字符串/text_note”得到的 Uri。 + } + + public static final class CallNote implements DataColumns {// 定义名为 CallNote 的静态内部类并实现 DataColumns 接口。 + /** + * Call date for this record + *

Type: INTEGER (long)

+ */ + // 注释:此记录的通话日期。类型:整数(长整型)。 + public static final String CALL_DATE = DATA1;// 静态常量,通话日期字段为 DATA1。 + + /** + * Phone number for this record + *

Type: TEXT

+ */ + // 注释:此记录的电话号码。类型:文本。 + public static final String PHONE_NUMBER = DATA3;// 静态常量,电话号码字段为 DATA3。 + + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";// 静态常量,内容类型为“vnd.android.cursor.dir/call_note”。 + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";// 静态常量,内容项类型为“vnd.android.cursor.item/call_note”。 + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");// 静态常量,内容 Uri 为通过解析“content://权限字符串/call_note”得到的 Uri。 + } +} diff --git a/NotesDatabaseHelper.java b/NotesDatabaseHelper.java new file mode 100644 index 0000000..ed5dbe9 --- /dev/null +++ b/NotesDatabaseHelper.java @@ -0,0 +1,380 @@ +/* + * 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.data; + +import android.content.ContentValues; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + + +public class NotesDatabaseHelper extends SQLiteOpenHelper // 定义一个名为 NotesDatabaseHelper 的公共类,它继承自 SQLiteOpenHelper。{ + private static final String DB_NAME = "note.db";// 定义一个私有静态常量字符串 DB_NAME,其值为“note.db”,表示数据库名称。 + + private static final int DB_VERSION = 4;// 定义一个私有静态常量整数 DB_VERSION,其值为 4,表示数据库版本号。 + + public interface TABLE {// 定义一个公共接口 TABLE。 + public static final String NOTE = "note";// 定义一个公共静态常量字符串 NOTE,其值为“note”。 + + public static final String DATA = "data";// 定义一个公共静态常量字符串 DATA,其值为“data”。 + } + + private static final String TAG = "NotesDatabaseHelper";// 定义一个私有静态常量字符串 TAG,其值为“NotesDatabaseHelper”。 + + private static NotesDatabaseHelper mInstance;// 定义一个私有静态变量 mInstance,类型为 NotesDatabaseHelper。 + + private static final String CREATE_NOTE_TABLE_SQL =// 定义一个私有静态常量字符串 + "CREATE TABLE " + TABLE.NOTE + "(" +// “创建表”+接口 TABLE 中的常量 NOTE+“(”。 + NoteColumns.ID + " INTEGER PRIMARY KEY," +// NoteColumns 中的常量 ID+“整数类型,为主键”+逗号分隔符。 + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 PARENT_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 ALERTED_DATE+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 BG_COLOR_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +// NoteColumns 中的常量 CREATED_DATE+“整数类型,不能为空,默认值为(通过 strftime 函数获取当前时间的秒数并乘以 1000)”+逗号分隔符。 + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 HAS_ATTACHMENT+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +// NoteColumns 中的常量 MODIFIED_DATE+“整数类型,不能为空,默认值为(通过 strftime 函数获取当前时间的秒数并乘以 1000)”+逗号分隔符。 + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 NOTES_COUNT+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +// NoteColumns 中的常量 SNIPPET+“文本类型,不能为空,默认值为空字符串”+逗号分隔符。 + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 TYPE+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 WIDGET_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +// NoteColumns 中的常量 WIDGET_TYPE+“整数类型,不能为空,默认值为 -1”+逗号分隔符。 + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 SYNC_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 LOCAL_MODIFIED+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +// NoteColumns 中的常量 ORIGIN_PARENT_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +// NoteColumns 中的常量 GTASK_ID+“文本类型,不能为空,默认值为空字符串”+逗号分隔符。 + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +// NoteColumns 中的常量 VERSION+“整数类型,不能为空,默认值为 0”。 + ")"; + + private static final String CREATE_DATA_TABLE_SQL =// 定义一个私有静态常量字符串 CREATE_DATA_TABLE_SQL。 + "CREATE TABLE " + TABLE.DATA + "(" +// “创建表”+接口 TABLE 中的常量 DATA+“(”。 + DataColumns.ID + " INTEGER PRIMARY KEY," +// DataColumns 中的常量 ID+“整数类型,为主键”+逗号分隔符。 + DataColumns.MIME_TYPE + " TEXT NOT NULL," +// DataColumns 中的常量 MIME_TYPE+“文本类型,不能为空”+逗号分隔符。 + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +// DataColumns 中的常量 NOTE_ID+“整数类型,不能为空,默认值为 0”+逗号分隔符。 + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +// NoteColumns 中的常量 CREATED_DATE+“整数类型,不能为空,默认值为(通过 strftime 函数获取当前时间的秒数并乘以 1000)”+逗号分隔符。 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +// NoteColumns 中的常量 MODIFIED_DATE+“整数类型,不能为空,默认值为(通过 strftime 函数获取当前时间的秒数并乘以 1000)”+逗号分隔符。 + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," +// DataColumns 中的常量 CONTENT+“文本类型,不能为空,默认值为空字符串”+逗号分隔符。 + DataColumns.DATA1 + " INTEGER," +// DataColumns 中的常量 DATA1+“整数类型”+逗号分隔符。 + DataColumns.DATA2 + " INTEGER," +// DataColumns 中的常量 DATA2+“整数类型”+逗号分隔符。 + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +// DataColumns 中的常量 DATA3+“文本类型,不能为空,默认值为空字符串”+逗号分隔符。 + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +// DataColumns 中的常量 DATA4+“文本类型,不能为空,默认值为空字符串”+逗号分隔符。 + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +// DataColumns 中的常量 DATA5+“文本类型,不能为空,默认值为空字符串”。 + ")"; + + private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =// 定义一个私有静态常量字符串 CREATE_DATA_NOTE_ID_INDEX_SQL。 + "CREATE INDEX IF NOT EXISTS note_id_index ON " +// “创建索引,如果索引不存在,索引名为 note_id_index,在”+。 + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";// 接口 TABLE 中的常量 DATA+“(”+DataColumns 中的常量 NOTE_ID+“)”,即基于数据表中的 NOTE_ID 字段创建索引。 + + /** + * Increase folder's note count when move note to the folder + */ + // 这是一段注释,意思是“当将笔记移动到文件夹时,增加文件夹的笔记计数”。 + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =// 定义一个私有静态常量字符串 NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER。 + "CREATE TRIGGER increase_folder_count_on_update "+// “创建触发器,触发器名为 increase_folder_count_on_update”+空格分隔符。 + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +// “在更新 Note 表中的 PARENT_ID 字段之后”+空格分隔符+NoteColumns 中的常量 PARENT_ID+“在”+接口 TABLE 中的常量 NOTE。 + " BEGIN " +// “开始”。 + " UPDATE " + TABLE.NOTE + +// “更新”+接口 TABLE 中的常量 NOTE。 + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +// “设置”+NoteColumns 中的常量 NOTES_COUNT+“等于”+NoteColumns 中的常量 NOTES_COUNT+“加 1”。 + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +// “条件是”+NoteColumns 中的常量 ID+“等于 new.”+NoteColumn.PARENT_ID,其中 new 表示新值。 + " END";// “结束”。 + + /** + * Decrease folder's note count when move note from folder + */ + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =// 定义一个私有静态常量字符串 NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER。 + "CREATE TRIGGER decrease_folder_count_on_update " +// “创建触发器,触发器名为 decrease_folder_count_on_update”+空格分隔符。 + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +// “在更新 Note 表中的 PARENT_ID 字段之后”+空格分隔符+NoteColumns 中的常量 PARENT_ID+“在”+接口 TABLE 中的常量 NOTE。 + " BEGIN " +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +// “设置”+NoteColumns 中的常量 NOTES_COUNT+“等于”+NoteColumns 中的常量 NOTES_COUNT+“减 1”。 + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +// “条件是”+NoteColumns 中的常量 ID+“等于 old.”+NoteColumns 中的常量 PARENT_ID,其中 old 表示旧值。 + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +// “并且”+NoteColumns 中的常量 NOTES_COUNT+“大于 0”+分号分隔符。 + " END";// “结束”。 + + /** + * Increase folder's note count when insert new note to the folder + */ + // 这是一段注释,意思是“当向文件夹中插入新笔记时,增加文件夹的笔记计数”。 + private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =// 定义一个私有静态常量字符串 NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER。 + "CREATE TRIGGER increase_folder_count_on_insert " +// “创建触发器,触发器名为 increase_folder_count_on_insert”+空格分隔符。 + " AFTER INSERT ON " + TABLE.NOTE +// “在向 Note 表插入数据之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " BEGIN " +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +// “设置”+NoteColumns 中的常量 NOTES_COUNT+“等于”+NoteColumns 中的常量 NOTES_COUNT+“加 1”。 + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +// “条件是”+NoteColumns 中的常量 ID+“等于 new.”+NoteColumns 中的常量 PARENT_ID,其中 new 表示新值。 + " END";// “结束”。 + + /** + * Decrease folder's note count when delete note from the folder + */ + // 这是一段注释,意思是“当从文件夹中删除笔记时,减少文件夹的笔记计数”。 + private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =// 定义一个私有静态常量字符串 NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER。 + "CREATE TRIGGER decrease_folder_count_on_delete " +// “创建触发器,触发器名为 decrease_folder_count_on_delete”+空格分隔符。 + " AFTER DELETE ON " + TABLE.NOTE +// “在从 Note 表删除数据之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " BEGIN " +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +// “设置”+NoteColumns 中的常量 NOTES_COUNT+“等于”+NoteColumns 中的常量 NOTES_COUNT+“减 1”。 + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +// “条件是”+NoteColumns 中的常量 ID+“等于 old.”+NoteColumns 中的常量 PARENT_ID,其中 old 表示旧值。 + " AND " + NoteColumns.NOTES_COUNT + ">0;" +// “并且”+NoteColumns 中的常量 NOTES_COUNT+“大于 0”+分号分隔符。 + " END";// “结束”。 + + /** + * Update note's content when insert data with type {@link DataConstants#NOTE} + */ + // 这是一段注释,意思是“当插入类型为{@link DataConstants#NOTE}的数据时,更新笔记的内容”。 + private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =// 定义一个私有静态常量字符串 DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER。 + "CREATE TRIGGER update_note_content_on_insert " +// “创建触发器,触发器名为 update_note_content_on_insert”+空格分隔符。 + " AFTER INSERT ON " + TABLE.DATA +// “在向 Data 表插入数据之后”+空格分隔符+接口 TABLE 中的常量 DATA。 + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +// “当新插入的数据中,DataColumns 中的常量 MIME_TYPE 等于‘DataConstants.NOTE’时”+空格分隔符。 + " BEGIN" +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +// “设置”+NoteColumns 中的常量 SNIPPET+“等于新插入数据中的”+DataColumns 中的常量 CONTENT。 + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +// “条件是”+NoteColumns 中的常量 ID+“等于新插入数据中的”+DataColumns 中的常量 NOTE_ID+分号分隔符。 + " END";// “结束”。 + + /** + * Update note's content when data with {@link DataConstants#NOTE} type has changed + */ + // 这是一段注释,意思是“当类型为{@link DataConstants#NOTE}的数据发生变化时,更新笔记的内容”。 + private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =// 定义一个私有静态常量字符串 DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER。 + "CREATE TRIGGER update_note_content_on_update " +// “创建触发器,触发器名为 update_note_content_on_update”+空格分隔符。 + " AFTER UPDATE ON " + TABLE.DATA +// “在更新 Data 表之后”+空格分隔符+接口 TABLE 中的常量 DATA。 + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +// “当旧数据中,DataColumns 中的常量 MIME_TYPE 等于‘DataConstants.NOTE’时”+空格分隔符。 + " BEGIN" +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +// “设置”+NoteColumns 中的常量 SNIPPET+“等于新数据中的”+DataColumns 中的常量 CONTENT。 + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +// “条件是”+NoteColumns 中的常量 ID+“等于新数据中的”+DataColumns 中的常量 NOTE_ID+分号分隔符。 + " END";// “结束”。 + + /** + * Update note's content when data with {@link DataConstants#NOTE} type has deleted + */ + // 这是一段注释,意思是“当类型为{@link DataConstants#NOTE}的数据被删除时,更新笔记的内容”。 + private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =// 定义一个私有静态常量字符串 DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER。 + "CREATE TRIGGER update_note_content_on_delete " +// “创建触发器,触发器名为 update_note_content_on_delete”+空格分隔符。 + " AFTER delete ON " + TABLE.DATA +// “在删除 Data 表中的数据之后”+空格分隔符+接口 TABLE 中的常量 DATA。 + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +// “当旧数据中,DataColumns 中的常量 MIME_TYPE 等于‘DataConstants.NOTE’时”+空格分隔符。 + " BEGIN" +// “开始”。 + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.SNIPPET + "=''" +// “设置”+NoteColumns 中的常量 SNIPPET 为空字符串。 + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +// “条件是”+NoteColumns 中的常量 ID+“等于旧数据中的”+DataColumns 中的常量 NOTE_ID+分号分隔符。 + " END";// “结束”。 + + /** + * Delete datas belong to note which has been deleted + */ + // 这是一段注释,意思是“删除已被删除的笔记所对应的所有数据”。 + private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =// 定义一个私有静态常量字符串 NOTE_DELETE_DATA_ON_DELETE_TRIGGER。 + "CREATE TRIGGER delete_data_on_delete " +// “创建触发器,触发器名为 delete_data_on_delete”+空格分隔符。 + " AFTER DELETE ON " + TABLE.NOTE +// “在删除 Note 表中的数据之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " BEGIN" +// “开始”。 + " DELETE FROM " + TABLE.DATA +// “从”+接口 TABLE 中的常量 DATA 表中删除数据。 + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +// “条件是”+DataColumns 中的常量 NOTE_ID+“等于旧数据中的”+NoteColumns 中的常量 ID+分号分隔符。 + " END";// “结束”。 + + /** + * Delete notes belong to folder which has been deleted + */ + // 这是一段注释,意思是“删除已被删除的文件夹所对应的所有笔记”。 + private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =// 定义一个私有静态常量字符串 FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER。 + "CREATE TRIGGER folder_delete_notes_on_delete " +// “创建触发器,触发器名为 folder_delete_notes_on_delete”+空格分隔符。 + " AFTER DELETE ON " + TABLE.NOTE +// “在删除 Note 表中的数据之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " BEGIN" +// “开始”。 + " DELETE FROM " + TABLE.NOTE +// “从”+接口 TABLE 中的常量 NOTE 表中删除数据。 + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +// “条件是”+NoteColumns 中的常量 PARENT_ID+“等于旧数据中的”+NoteColumns 中的常量 ID+分号分隔符。 + " END";// “结束”。 + + /** + * Move notes belong to folder which has been moved to trash folder + */ + // 这是一段注释,意思是“将已被移动到回收站文件夹的文件夹所对应的笔记移动到回收站”。 + private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =// 定义一个私有静态常量字符串 FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER。 + "CREATE TRIGGER folder_move_notes_on_trash " +// “创建触发器,触发器名为 folder_move_notes_on_trash”+空格分隔符。 + " AFTER UPDATE ON " + TABLE.NOTE +// “在更新 Note 表之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +// “在更新 Note 表之后”+空格分隔符+接口 TABLE 中的常量 NOTE。 + " BEGIN" + + " UPDATE " + TABLE.NOTE +// “更新”+接口 TABLE 中的常量 NOTE。 + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +// “设置”+NoteColumns 中的常量 PARENT_ID 等于 Notes.ID_TRASH_FOLER。 + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +// “条件是”+NoteColumns 中的常量 PARENT_ID+“等于旧数据中的”+NoteColumns 中的常量 ID+分号分隔符。 + " END";// “结束”。 + + public NotesDatabaseHelper(Context context) {// 定义一个公共的构造函数 NotesDatabaseHelper,接收一个 Context 参数。 + super(context, DB_NAME, null, DB_VERSION);// 调用父类 SQLiteOpenHelper 的构造函数,传入 Context、数据库名、游标工厂(这里为 null)和数据库版本号。 + } + + public void createNoteTable(SQLiteDatabase db) {// 定义一个公共方法 createNoteTable,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL(CREATE_NOTE_TABLE_SQL);// 执行 SQL 语句 CREATE_NOTE_TABLE_SQL,用于创建 note 表。 + reCreateNoteTableTriggers(db);// 调用私有方法 reCreateNoteTableTriggers,传入参数 db,用于重新创建 note 表的触发器。 + createSystemFolder(db);// 调用 createSystemFolder 方法,传入参数 db,可能用于创建系统文件夹。 + Log.d(TAG, "note table has been created");// 使用日志记录工具打印信息,表明 note 表已被创建,TAG 是一个常量,用于标识日志来源。 + } + + private void reCreateNoteTableTriggers(SQLiteDatabase db) {// 定义一个私有方法 reCreateNoteTableTriggers,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");// 执行 SQL 语句,如果存在名为 increase_folder_count_on_update 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");// 执行 SQL 语句,如果存在名为 decrease_folder_count_on_update 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");// 执行 SQL 语句,如果存在名为 decrease_folder_count_on_delete 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");// 执行 SQL 语句,如果存在名为 delete_data_on_delete 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");// 执行 SQL 语句,如果存在名为 increase_folder_count_on_insert 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");// 执行 SQL 语句,如果存在名为 folder_delete_notes_on_delete 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");// 执行 SQL 语句,如果存在名为 folder_move_notes_on_trash 的触发器,则删除它。 + + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);// 执行 SQL 语句,创建名为 NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER 的触发器。 + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);// 执行 SQL 语句,创建名为 NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER 的触发器。 + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);// 执行 SQL 语句,创建名为 NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER 的触发器。 + db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);// 执行 SQL 语句,创建名为 NOTE_DELETE_DATA_ON_DELETE_TRIGGER 的触发器。 + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);// 执行 SQL 语句,创建名为 NOTE_DELETE_DATA_ON_DELETE_TRIGGER 的触发器。 + db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);// 执行 SQL 语句,创建名为 FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER 的触发器。 + db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);// 执行 SQL 语句,创建名为 FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER 的触发器。 + } + + private void createSystemFolder(SQLiteDatabase db) {// 定义一个私有方法 createSystemFolder,接收一个 SQLiteDatabase 类型的参数 db。 + ContentValues values = new ContentValues();// 创建一个 ContentValues 对象 values。 + + /** + * call record foler for call notes + */ + // 这是一段注释,意思是“用于通话记录笔记的通话记录文件夹”。 + values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);// 将 Notes 类中的常量 ID_CALL_RECORD_FOLDER 放入 values 的键为 NoteColumns.ID 的位置。 + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);// 将 Notes 类中的常量 TYPE_SYSTEM 放入 values 的键为 NoteColumns.TYPE 的位置。 + db.insert(TABLE.NOTE, null, values);// 向表 TABLE.NOTE 插入数据,第三个参数为 values。 + + /** + * root folder which is default folder + */ + // 这是一段注释,意思是“根文件夹,它是默认文件夹”。 + values.clear(); + values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * temporary folder which is used for moving note + */ + values.clear();// 清空 values。 + values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);// 将 Notes 类中的常量 ID_ROOT_FOLDER 放入 values 的键为 NoteColumns.ID 的位置。 + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);// 将 Notes 类中的常量 TYPE_SYSTEM 放入 values 的键为 NoteColumns.TYPE 的位置。 + db.insert(TABLE.NOTE, null, values);// 向表 TABLE.NOTE 插入数据,第三个参数为 values。 + + /** + * create trash folder + */ + // 这是一段注释,意思是“临时文件夹,用于移动笔记”。 + values.clear();// 清空 values。 + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);// 将 Notes 类中的常量 ID_TEMPARAY_FOLDER 放入 values 的键为 NoteColumns.ID 的位置。 + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);// 将 Notes 类中的常量 TYPE_SYSTEM 放入 values 的键为 NoteColumns.TYPE 的位置。 + db.insert(TABLE.NOTE, null, values);// 向表 TABLE.NOTE 插入数据,第三个参数为 values。 + } + + public void createDataTable(SQLiteDatabase db) {// 定义一个公共方法 createDataTable,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL(CREATE_DATA_TABLE_SQL);// 执行 SQL 语句 CREATE_DATA_TABLE_SQL,用于创建 data 表。 + reCreateDataTableTriggers(db);// 调用私有方法 reCreateDataTableTriggers,传入参数 db,用于重新创建 data 表的触发器。 + db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);// 执行 SQL 语句 CREATE_DATA_NOTE_ID_INDEX_SQL,用于创建 data 表的索引。 + Log.d(TAG, "data table has been created");// 使用日志记录工具打印信息,表明 data 表已被创建,TAG 是一个常量,用于标识日志来源。 + } + + private void reCreateDataTableTriggers(SQLiteDatabase db) {// 定义一个私有方法 reCreateDataTableTriggers,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");// 执行 SQL 语句,如果存在名为 update_note_content_on_insert 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");// 执行 SQL 语句,如果存在名为 update_note_content_on_update 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");// 执行 SQL 语句,如果存在名为 update_note_content_on_delete 的触发器,则删除它。 + + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);// 执行 SQL 语句,创建名为 DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER 的触发器。 + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);// 执行 SQL 语句,创建名为 DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER 的触发器。 + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);// 执行 SQL 语句,创建名为 DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER 的触发器。 + } + + static synchronized NotesDatabaseHelper getInstance(Context context) {// 定义一个静态同步方法 getInstance,接收一个 Context 类型的参数 context。 + if (mInstance == null) {// 如果 mInstance 为 null。 + mInstance = new NotesDatabaseHelper(context);// 创建一个新的 NotesDatabaseHelper 实例,传入 context 参数,并赋值给 mInstance。 + } + return mInstance;// 返回 mInstance。 + } + + @Override + public void onCreate(SQLiteDatabase db) {// 重写 onCreate 方法,接收一个 SQLiteDatabase 类型的参数 db。 + createNoteTable(db);// 调用 createNoteTable 方法,传入参数 db,创建 note 表。 + createDataTable(db);// 调用 createDataTable 方法,传入参数 db,创建 data 表。 + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 重写 onUpgrade 方法,接收一个 SQLiteDatabase 类型的参数 db,以及旧版本号 oldVersion 和新版本号 newVersion。 + boolean reCreateTriggers = false;false; +// 定义一个布尔变量 reCreateTriggers,初始值为 false。 + boolean skipV2 = false;// 定义一个布尔变量 skipV2,初始值为 false。 + + if (oldVersion == 1) {// 如果旧版本号为 1。 + upgradeToV2(db);// 调用 upgradeToV2 方法,传入参数 db,进行从版本 1 到版本 2 的升级。 + skipV2 = true; // this upgrade including the upgrade from v2 to v3 + // 将 skipV2 设置为 true,表示此次升级包括从版本 2 到版本 3 的升级。 + oldVersion++;// 旧版本号加 1。 + } + + if (oldVersion == 2 && !skipV2) {// 如果旧版本号为 2 且 skipV2 为 false。 + upgradeToV3(db);// 调用 upgradeToV3 方法,传入参数 db,进行从版本 2 到版本 3 的升级。 + reCreateTriggers = true;// 将 reCreateTriggers 设置为 true,表示需要重新创建触发器。 + oldVersion++;// 旧版本号加 1。 + } + + if (oldVersion == 3) {// 如果旧版本号为 3。 + upgradeToV4(db);// 调用 upgradeToV4 方法,传入参数 db,进行从版本 3 到版本 4 的升级。 + oldVersion++;// 旧版本号加 1。 + } + + if (reCreateTriggers) {// 如果 reCreateTriggers 为 true。 + reCreateNoteTableTriggers(db);// 调用 reCreateNoteTableTriggers 方法,传入参数 db,重新创建 note 表的触发器。 + reCreateDataTableTriggers(db);// 调用 reCreateDataTableTriggers 方法,传入参数 db,重新创建 data 表的触发器。 + } + + if (oldVersion != newVersion) {// 如果旧版本号不等于新版本号。 + throw new IllegalStateException("Upgrade notes database to version " + newVersion + + "fails");// 抛出一个 IllegalStateException 异常,表明升级笔记数据库到新版本失败。 + } + } + + private void upgradeToV2(SQLiteDatabase db) {// 定义一个私有方法 upgradeToV2,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);// 执行 SQL 语句,如果存在表 TABLE.NOTE,则删除它。 + db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);// 执行 SQL 语句,如果存在表 TABLE.DATA,则删除它。 + createNoteTable(db);// 调用 createNoteTable 方法,传入参数 db,创建 note 表。 + createDataTable(db);// 调用 createDataTable 方法,传入参数 db,创建 data 表。 + } + + private void upgradeToV3(SQLiteDatabase db) {// 定义一个私有方法 upgradeToV3,接收一个 SQLiteDatabase 类型的参数 db。 + // drop unused triggers + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");// 执行 SQL 语句,如果存在名为 update_note_modified_date_on_insert 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");// 执行 SQL 语句,如果存在名为 update_note_modified_date_on_insert 的触发器,则删除它。 + db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");// 执行 SQL 语句,如果存在名为 update_note_modified_date_on_update 的触发器,则删除它。 + // add a column for gtask id + // 注释:添加一个用于 gtask id 的列。 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + + " TEXT NOT NULL DEFAULT ''");// 执行 SQL 语句,在表 TABLE.NOTE 中添加一个名为 NoteColumns.GTASK_ID 的列,类型为文本,默认值为空字符串。 + // add a trash system folder + // 注释:添加一个回收站系统文件夹。 + ContentValues values = new ContentValues();// 创建一个 ContentValues 对象 values。 + values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);// 将 Notes 类中的常量 ID_TRASH_FOLER 放入 values 的键为 NoteColumns.ID 的位置。 + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);// 将 Notes 类中的常量 TYPE_SYSTEM 放入 values 的键为 NoteColumns.TYPE 的位置。 + db.insert(TABLE.NOTE, null, values);// 向表 TABLE.NOTE 插入数据,第三个参数为 values。 + } + + private void upgradeToV4(SQLiteDatabase db) {// 定义一个私有方法 upgradeToV4,接收一个 SQLiteDatabase 类型的参数 db。 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + + " INTEGER NOT NULL DEFAULT 0");// 执行 SQL 语句,在表 TABLE.NOTE 中添加一个名为 NoteColumns.VERSION 的列,类型为整数,默认值为 0。 + } +} diff --git a/NotesProvider.java b/NotesProvider.java new file mode 100644 index 0000000..94ca0bd --- /dev/null +++ b/NotesProvider.java @@ -0,0 +1,311 @@ +/* + * 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.data;// 包名:net.micode.notes.data + + +import android.app.SearchManager;// 导入 Android 的搜索管理器类 +import android.content.ContentProvider;// 导入 Android 的内容提供器类 +import android.content.ContentUris;// 导入 Android 的内容 URI 工具类 +import android.content.ContentValues;// 导入 Android 的内容值类 +import android.content.Intent; +import android.content.UriMatcher;// 导入 Android 的 URI 匹配器类 +import android.database.Cursor;// 导入 Android 的游标类 +import android.database.sqlite.SQLiteDatabase;// 导入 Android 的 SQLite 数据库类 +import android.net.Uri;// 导入 Android 的统一资源标识符类 +import android.text.TextUtils;// 导入 Android 的文本工具类 +import android.util.Log;// 导入 Android 的日志工具类 + +import net.micode.notes.R; +import net.micode.notes.data.Notes.DataColumns;// 导入 net.micode.notes.data.Notes 类中的 DataColumns 内部类 +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE;// 导入 net.micode.notes.data.NotesDatabaseHelper 类中的 TABLE 静态成员 + + +public class NotesProvider extends ContentProvider {// 定义一个名为 NotesProvider 的公共类,它继承自 ContentProvider。 + private static final UriMatcher mMatcher;// 定义一个私有的静态常量 mMatcher,类型为 UriMatcher。 + + private NotesDatabaseHelper mHelper;// 定义一个私有的静态常量 mMatcher,类型为 UriMatcher。 + + private static final String TAG = "NotesProvider";// 定义一个私有的静态常量 TAG,值为"NotesProvider"。 + + private static final int URI_NOTE = 1;// 定义一个私有的静态常量 URI_NOTE,值为 1。 + private static final int URI_NOTE_ITEM = 2;// 定义一个私有的静态常量 URI_NOTE_ITEM,值为 2。 + private static final int URI_DATA = 3;// 定义一个私有的静态常量 URI_DATA,值为 3。 + private static final int URI_DATA_ITEM = 4;// 定义一个私有的静态常量 URI_DATA_ITEM,值为 4。 + + private static final int URI_SEARCH = 5;// 定义一个私有的静态常量 URI_SEARCH,值为 5。 + private static final int URI_SEARCH_SUGGEST = 6;// 定义一个私有的静态常量 URI_SEARCH_SUGGEST,值为 6。 + + static {// 静态代码块。 + mMatcher = new UriMatcher(UriMatcher.NO_MATCH);// 创建一个新的 UriMatcher 对象并赋值给 mMatcher,初始匹配结果为 UriMatcher.NO_MATCH。 + mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为"note"时,匹配结果为 URI_NOTE。 + mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为"note/任意数字"时,匹配结果为 URI_NOTE_ITEM。 + mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为"note/任意数字"时,匹配结果为 URI_NOTE_ITEM。 + mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为"data/任意数字"时,匹配结果为 URI_DATA_ITEM。 + mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为"search"时,匹配结果为 URI_SEARCH。 + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为 SearchManager.SUGGEST_URI_PATH_QUERY 时,匹配结果为 URI_SEARCH_SUGGEST。 + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);// 向 mMatcher 添加一个 URI 匹配规则,当 authority 为 Notes.AUTHORITY,路径为 SearchManager.SUGGEST_URI_PATH_QUERY 加上任意字符时,匹配结果为 URI_SEARCH_SUGGEST。 + } + + /** + * x'0A' represents the '\n' character in sqlite. For title and content in the search result, + * we will trim '\n' and white space in order to show more information. + */ + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + // 定义一个私有的静态常量 NOTES_SEARCH_PROJECTION,它是一个字符串,由多个部分组成,分别是 NoteColumns.ID(可能是笔记的 ID)、NoteColumns.ID 作为 SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA、对 NoteColumns.SNIPPET(可能是笔记片段)进行处理后的结果作为 SearchManager.SUGGEST_COLUMN_TEXT_1 和 SearchManager.SUGGEST_COLUMN_TEXT_2、R.drawable.search_result(可能是搜索结果的图标资源)作为 SearchManager.SUGGEST_COLUMN_ICON_1、Intent.ACTION_VIEW(可能是查看的动作)作为 SearchManager.SUGGEST_COLUMN_INTENT_ACTION、Notes.TextNote.CONTENT_TYPE(可能是文本笔记的内容类型)作为 SearchManager.SUGGEST_COLUMN_INTENT_DATA。 + + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + SNIPPET_SEARCH_QUERY,它是一个 SQL 查询语句,包含选择 NOTES_SEARCH_PROJECTION 指定的列,从 TABLE.NOTE(可能是一个表名)中选择,有条件判断 NoteColumns.SNIPPET(笔记片段)类似某个值,NoteColumns.PARENT_ID(可能是父 ID)不等于 Notes.ID_TRASH_FOLER(可能是垃圾文件夹的 ID),以及 NoteColumns.TYPE(可能是类型)等于 Notes.TYPE_NOTE(可能是笔记类型)。 + + @Override + public boolean onCreate() { + mHelper = NotesDatabaseHelper.getInstance(getContext()); + return true; + }// 重写 onCreate 方法。在这个方法中,获取 NotesDatabaseHelper 的实例并赋值给 mHelper,然后返回 true 表示创建成功。 + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) {// 重写 query 方法,该方法接收一个 Uri(统一资源标识符)、一个字符串数组(projection,指定要返回的列)、一个字符串(selection,用于筛选数据的条件)、一个字符串数组(selectionArgs,用于填充 selection 中的占位符)和一个字符串(sortOrder,用于指定排序方式),并返回一个 Cursor(游标)对象。 + Cursor c = null;// 初始化一个 Cursor 对象为 null。 + SQLiteDatabase db = mHelper.getReadableDatabase();// 获取一个可读的 SQLiteDatabase 对象。 + String id = null;// 初始化一个字符串 id 为 null。 + switch (mMatcher.match(uri)) {// 根据 Uri 匹配器 mMatcher 对传入的 Uri 进行匹配,并根据匹配结果进行不同的处理。 + case URI_NOTE:// 如果匹配到 URI_NOTE。 + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, + sortOrder);// 使用数据库对象 db 执行查询操作,从名为 TABLE.NOTE 的表中查询指定的列(projection),根据条件(selection 和 selectionArgs)进行筛选,并按照指定的排序方式(sortOrder)进行排序,结果存储在 Cursor 对象 c 中。 + break;// 跳出 switch 语句。 + case URI_NOTE_ITEM:// 如果匹配到 URI_NOTE_ITEM。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder);// 使用数据库对象 db 执行查询操作,从名为 TABLE.NOTE 的表中查询指定的列(projection),根据条件(NoteColumns.ID 等于 id 加上经过 parseSelection 方法处理后的 selection)进行筛选,并按照指定的排序方式(sortOrder)进行排序,结果存储在 Cursor 对象 c 中。 + break;// 跳出 switch 语句。 + case URI_DATA:// 如果匹配到 URI_DATA。 + c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, + sortOrder);// 使用数据库对象 db 执行查询操作,从名为 TABLE.DATA 的表中查询指定的列(projection),根据条件(selection 和 selectionArgs)进行筛选,并按照指定的排序方式(sortOrder)进行排序,结果存储在 Cursor 对象 c 中。 + break;// 跳出 switch 语句。 + case URI_DATA_ITEM:// 如果匹配到 URI_DATA_ITEM。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs, null, null, sortOrder);// 使用数据库对象 db 执行查询操作,从名为 TABLE.DATA 的表中查询指定的列(projection),根据条件(DataColumns.ID 等于 id 加上经过 parseSelection 方法处理后的 selection)进行筛选,并按照指定的排序方式(sortOrder)进行排序,结果存储在 Cursor 对象 c 中。 + break;// 跳出 switch 语句。 + case URI_SEARCH:// 如果匹配到 URI_SEARCH。 + case URI_SEARCH_SUGGEST:// 如果匹配到 URI_SEARCH_SUGGEST。 + if (sortOrder != null || projection != null) {// 如果 sortOrder 不为 null 或者 projection 不为 null。 + throw new IllegalArgumentException( + "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");// 抛出一个非法参数异常,提示在这个查询中不要指定 sortOrder、selection、selectionArgs 或 projection。 + } + + String searchString = null;// 定义一个字符串 searchString 并初始化为 null。 + if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {// 如果传入的 Uri 与 URI_SEARCH_SUGGEST 匹配。 + if (uri.getPathSegments().size() > 1) {// 如果 Uri 的路径片段数量大于 1。 + searchString = uri.getPathSegments().get(1);// 获取 Uri 的第二个路径片段并赋值给 searchString。 + } + } else { + // 如果传入的 Uri 不与 URI_SEARCH_SUGGEST 匹配。 + searchString = uri.getQueryParameter("pattern");// 从 Uri 的查询参数中获取名为 "pattern" 的参数值并赋值给 searchString。 + } + + if (TextUtils.isEmpty(searchString)) {// 如果 searchString 为空。 + return null;// 返回 null。 + } + + try {// 尝试执行以下代码块。 + searchString = String.format("%%%s%%", searchString);// 使用 String.format 方法格式化 searchString,在其前后加上 "%%"。 + c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, + new String[] { searchString });// 使用数据库对象 db 执行原始 SQL 查询 NOTES_SNIPPET_SEARCH_QUERY,并将 searchString 作为参数传入,结果存储在 Cursor 对象 c 中。 + } catch (IllegalStateException ex) {// 如果发生非法状态异常。 + Log.e(TAG, "got exception: " + ex.toString());// 使用日志记录错误信息,包括标签 TAG 和异常的字符串表示。 + } + break;// 跳出 switch 语句。 + default:// 如果没有匹配到任何已知的 Uri。 + throw new IllegalArgumentException("Unknown URI " + uri);// 抛出非法参数异常,提示未知的 Uri。 + } + if (c != null) {// 抛出非法参数异常,提示未知的 Uri。 + c.setNotificationUri(getContext().getContentResolver(), uri);// 设置 Cursor 对象的通知 Uri,以便在数据发生变化时通知内容解析器。 + } + return c;// 返回 Cursor 对象 c。 + } + + @Override + public Uri insert(Uri uri, ContentValues values) {// 重写 insert 方法,该方法接收一个 Uri 和一个 ContentValues 对象,用于插入数据,并返回插入数据的 Uri。 + SQLiteDatabase db = mHelper.getWritableDatabase();// 获取一个可写的 SQLiteDatabase 对象。 + long dataId = 0, noteId = 0, insertedId = 0;// 获取一个可写的 SQLiteDatabase 对象。 + switch (mMatcher.match(uri)) {// 根据 Uri 匹配器 mMatcher 对传入的 Uri 进行匹配,并根据匹配结果进行不同的处理。 + case URI_NOTE:// 如果匹配到 URI_NOTE。 + insertedId = noteId = db.insert(TABLE.NOTE, null, values);// 使用数据库对象 db 将 values 插入到名为 TABLE.NOTE 的表中,返回插入的行 ID,并将其同时赋值给 insertedId 和 noteId。 + break;// 跳出 switch 语句。 + case URI_DATA:// 如果匹配到 URI_DATA。 + if (values.containsKey(DataColumns.NOTE_ID)) {// 如果 values 中包含 DataColumns.NOTE_ID 键。 + noteId = values.getAsLong(DataColumns.NOTE_ID);// 获取 values 中对应 DataColumns.NOTE_ID 的长整型值,并赋值给 noteId。 + } else {// 如果 values 中不包含 DataColumns.NOTE_ID 键。 + Log.d(TAG, "Wrong data format without note id:" + values.toString());// 使用日志记录错误信息,提示数据格式错误,没有 note id,并打印 values 的字符串表示。 + } + insertedId = dataId = db.insert(TABLE.DATA, null, values);// 使用数据库对象 db 将 values 插入到名为 TABLE.DATA 的表中,返回插入的行 ID,并将其同时赋值给 insertedId 和 dataId。 + break;// 跳出 switch 语句。 + default:// 如果没有匹配到任何已知的 Uri。 + throw new IllegalArgumentException("Unknown URI " + uri);// 抛出非法参数异常,提示未知的 Uri。 + } + // Notify the note uri + // 注释:通知 note 的 Uri。 + if (noteId > 0) {// 如果 noteId 大于 0。 + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);// 使用上下文的内容解析器通知 Note 的 Uri 发生了变化,传入带有 noteId 的 Uri 和 null。 + } + + // Notify the data uri + if (dataId > 0) {// 如果 dataId 大于 0。 + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);// 使用上下文的内容解析器通知 Data 的 Uri 发生了变化,传入带有 dataId 的 Uri 和 null。 + } + + return ContentUris.withAppendedId(uri, insertedId);// 返回带有 insertedId 的 Uri。 + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) {// 重写 delete 方法,该方法接收一个 Uri、一个筛选条件字符串和一个筛选参数数组,用于删除数据,并返回删除的行数。 + int count = 0;// 定义一个整数变量 count 并初始化为 0,表示删除的行数。 + String id = null;// 定义一个字符串变量 id 并初始化为 null。 + SQLiteDatabase db = mHelper.getWritableDatabase();// 获取一个可写的 SQLiteDatabase 对象。 + boolean deleteData = false;// 定义一个布尔变量 deleteData 并初始化为 false,表示是否删除了数据。 + switch (mMatcher.match(uri)) {// 根据 Uri 匹配器 mMatcher 对传入的 Uri 进行匹配,并根据匹配结果进行不同的处理。 + case URI_NOTE:// 如果匹配到 URI_NOTE。 + selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";// 重新构建筛选条件,在原有的筛选条件上加上 NoteColumns.ID 大于 0 的条件。 + count = db.delete(TABLE.NOTE, selection, selectionArgs);// 使用数据库对象 db 删除名为 TABLE.NOTE 的表中符合筛选条件的行,并将删除的行数赋值给 count。 + break;// 跳出 switch 语句。 + case URI_NOTE_ITEM:// 如果匹配到 URI_NOTE_ITEM。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + /** + * ID that smaller than 0 is system folder which is not allowed to + * trash + */ + long noteId = Long.valueOf(id);// 将 id 转换为长整型并赋值给 noteId。 + if (noteId <= 0) {// 如果 noteId 小于等于 0。 + break;// 跳出 switch 语句。 + } + count = db.delete(TABLE.NOTE, + NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);// 使用数据库对象 db 删除名为 TABLE.NOTE 的表中符合筛选条件的行,并将删除的行数赋值给 count。 + break; + case URI_DATA:// 如果匹配到 URI_DATA。 + count = db.delete(TABLE.DATA, selection, selectionArgs);// 使用数据库对象 db 删除名为 TABLE.DATA 的表中符合筛选条件的行,并将删除的行数赋值给 count。 + deleteData = true;// 将 deleteData 设置为 true,表示删除了数据。 + break; + case URI_DATA_ITEM:// 将 deleteData 设置为 true,表示删除了数据。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + count = db.delete(TABLE.DATA, + DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);// 使用数据库对象 db 删除名为 TABLE.DATA 的表中符合筛选条件的行,并将删除的行数赋值给 count。 + deleteData = true;// 将 deleteData 设置为 true,表示删除了数据。 + break; + default:// 如果没有匹配到任何已知的 Uri。 + throw new IllegalArgumentException("Unknown URI " + uri);// 抛出非法参数异常,提示未知的 Uri。 + } + if (count > 0) {// 如果删除的行数大于 0。 + if (deleteData) {// 如果 deleteData 为 true,表示删除了数据。 + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);// 使用上下文的内容解析器通知 Notes.CONTENT_NOTE_URI 发生了变化,传入 null。 + } + getContext().getContentResolver().notifyChange(uri, null);// 使用上下文的内容解析器通知传入的 Uri 发生了变化,传入 null。 + } + return count;// 返回删除的行数。 + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {// 重写 update 方法,该方法接收一个 Uri、一个 ContentValues 对象、一个筛选条件字符串和一个筛选参数数组,用于更新数据,并返回更新的行数。 + int count = 0;// 定义一个整数变量 count 并初始化为 0,表示更新的行数。 + String id = null;// 定义一个字符串变量 id 并初始化为 null。 + SQLiteDatabase db = mHelper.getWritableDatabase();// 获取一个可写的 SQLiteDatabase 对象 + boolean updateData = false;// 定义一个布尔变量 updateData 并初始化为 false,表示是否更新了数据。 + switch (mMatcher.match(uri)) {// 根据 Uri 匹配器 mMatcher 对传入的 Uri 进行匹配,并根据匹配结果进行不同的处理。 + case URI_NOTE:// 如果匹配到 URI_NOTE。 + increaseNoteVersion(-1, selection, selectionArgs);// 调用 increaseNoteVersion 方法,传入 -1、筛选条件和筛选参数。 + count = db.update(TABLE.NOTE, values, selection, selectionArgs);// 使用数据库对象 db 更新名为 TABLE.NOTE 的表中符合筛选条件的行,并将更新的行数赋值给 count。 + break; + case URI_NOTE_ITEM:// 使用数据库对象 db 更新名为 TABLE.NOTE 的表中符合筛选条件的行,并将更新的行数赋值给 count。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id// 使用数据库对象 db 更新名为 TABLE.NOTE 的表中符合筛选条件的行,并将更新的行数赋值给 count。 + + parseSelection(selection), selectionArgs); + break; + case URI_DATA:// 如果匹配到 URI_DATA。 + count = db.update(TABLE.DATA, values, selection, selectionArgs);// 使用数据库对象 db 更新名为 TABLE.DATA 的表中符合筛选条件的行,并将更新的行数赋值给 count。 + updateData = true;// 将 updateData 设置为 true,表示更新了数据。 + break; + case URI_DATA_ITEM:// 如果匹配到 URI_DATA_ITEM。 + id = uri.getPathSegments().get(1);// 从 Uri 的路径片段中获取第二个元素(索引为 1),并赋值给 id。 + count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + + parseSelection(selection), selectionArgs);// 使用数据库对象 db 更新名为 TABLE.DATA 的表中符合筛选条件的行,并将更新的行数赋值给 count。 + updateData = true;// 将 updateData 设置为 true,表示更新了数据。 + break; + default:// 如果没有匹配到任何已知的 Uri。 + throw new IllegalArgumentException("Unknown URI " + uri);// 抛出非法参数异常,提示未知的 Uri。 + } + + if (count > 0) {// 如果更新的行数大于 0。 + if (updateData) {// 如果 updateData 为 true,表示更新了数据。 + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);// 使用上下文的内容解析器通知 Notes.CONTENT_NOTE_URI 发生了变化,传入 null。 + } + getContext().getContentResolver().notifyChange(uri, null);// 使用上下文的内容解析器通知传入的 Uri 发生了变化,传入 null。 + } + return count;// 返回更新的行数。 + } + + private String parseSelection(String selection) {// 定义一个私有方法 parseSelection,该方法接收一个筛选条件字符串,用于处理筛选条件。 + return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");// 如果筛选条件不为空,返回 " AND (" + selection + ")",否则返回空字符串。 + } + + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {// 定义一个私有方法 increaseNoteVersion,该方法接收一个长整型 id、一个筛选条件字符串和一个筛选参数数组,用于增加笔记的版本号。 + StringBuilder sql = new StringBuilder(120);// 创建一个 StringBuilder 对象,初始容量为 120。 + sql.append("UPDATE ");// 将 "UPDATE " 添加到 StringBuilder 对象中。 + sql.append(TABLE.NOTE);// 将 TABLE.NOTE(可能是表名)添加到 StringBuilder 对象中。 + sql.append(" SET ");// 将 " SET " 添加到 StringBuilder 对象中。 + sql.append(NoteColumns.VERSION);// 将 NoteColumns.VERSION(可能是版本号列名)添加到 StringBuilder 对象中。 + sql.append("=" + NoteColumns.VERSION + "+1 ");// 将 "=" + NoteColumns.VERSION + "+1 " 添加到 StringBuilder 对象中,表示将版本号设置为当前版本号加 1。 + + if (id > 0 || !TextUtils.isEmpty(selection)) {// 如果 id 大于 0 或者筛选条件不为空。 + sql.append(" WHERE "); +// 将 " WHERE " 添加到 StringBuilder 对象中。 + sql.append(" WHERE "); + } + if (id > 0) { + sql.append(NoteColumns.ID + "=" + String.valueOf(id));// 将 NoteColumns.ID + "=" + id 的字符串表示添加到 StringBuilder 对象中,表示根据 id 进行筛选。 + } + if (!TextUtils.isEmpty(selection)) {// 如果筛选条件不为空。 + String selectString = id > 0 ? parseSelection(selection) : selection;// 如果 id 大于 0,调用 parseSelection 方法处理筛选条件,否则直接使用筛选条件。 + for (String args : selectionArgs) {// 遍历筛选参数数组。 + selectString = selectString.replaceFirst("\\?", args);// 将筛选条件中的第一个问号替换为当前的筛选参数。 + } + sql.append(selectString);// 将处理后的筛选条件添加到 + } + + mHelper.getWritableDatabase().execSQL(sql.toString());// 使用数据库助手对象 mHelper 获取可写数据库,并执行 StringBuilder 对象中的 SQL 语句。 + } + + @Override + public String getType(Uri uri) {// 重写 getType 方法,该方法接收一个 Uri,用于获取数据的 MIME 类型。 + // TODO Auto-generated method stub + return null; + } + +} diff --git a/SqlData.java b/SqlData.java new file mode 100644 index 0000000..38b2d18 --- /dev/null +++ b/SqlData.java @@ -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. + * 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;//包名:net.micode.notes.gtask.data;”。 + +import android.content.ContentResolver;//“导入安卓内容解析器类;” +import android.content.ContentUris;//“导入安卓内容 URI 工具类;” +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;//“导入 net.micode.notes.data 包下的 Notes 类;”。 +import net.micode.notes.data.Notes.DataColumns;//“导入 net.micode.notes.data.Notes 类中的 DataColumns 内部类;”。 +import net.micode.notes.data.Notes.DataConstants;//导入 net.micode.notes.data.Notes 类中的 DataConstants 内部类;”。 +import net.micode.notes.data.Notes.NoteColumns;//导入 net.micode.notes.data.Notes 类中的 NoteColumns 内部类;”。 +import net.micode.notes.data.NotesDatabaseHelper.TABLE;//导入 net.micode.notes.data.NotesDatabaseHelper 类中的 TABLE 内部类;”。 +import net.micode.notes.gtask.exception.ActionFailureException;//导入 net.micode.notes.gtask.exception 包下的 ActionFailureException 类;” + +import org.json.JSONException;//导入 JSON 异常类;”。 +import org.json.JSONObject;//导入 JSON 异常类;”。 + + +public class SqlData {//“公共类 SqlData。” + private static final String TAG = SqlData.class.getSimpleName();//“私有静态最终字符串变量 TAG 被赋值为 SqlData 类的简单名称。” + + private static final int INVALID_ID = -99999;//“私有静态最终整数变量 INVALID_ID 被赋值为 -99999。” + + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 + };//“公共静态最终字符串数组变量 PROJECTION_DATA 被赋值为包含 DataColumns 类中的 ID、MIME_TYPE、CONTENT、DATA1 和 DATA3 的字符串数组。” + + public static final int DATA_ID_COLUMN = 0;//“公共静态最终字符串数组变量 PROJECTION_DATA 被赋值为包含 DataColumns 类中的 ID、MIME_TYPE、CONTENT、DATA1 和 DATA3 的字符串数组。” + + public static final int DATA_MIME_TYPE_COLUMN = 1;//“公共静态最终整数变量 DATA_MIME_TYPE_COLUMN 被赋值为 1。” + + public static final int DATA_CONTENT_COLUMN = 2;//“公共静态最终整数变量 DATA_CONTENT_COLUMN 被赋值为 2。” + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3;//“公共静态最终整数变量 DATA_CONTENT_DATA_1_COLUMN 被赋值为 3。” + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4;//公共静态最终整数变量 DATA_CONTENT_DATA_3_COLUMN 被赋值为 4。” + + private ContentResolver mContentResolver;//“私有 ContentResolver 类型变量 mContentResolver。” + + private boolean mIsCreate;//私有布尔类型变量 mIsCreate。” + + private long mDataId;//“私有长整型变量 mDataId。” + + private String mDataMimeType;//私有字符串类型变量 mDataMimeType。” + + private String mDataContent;//“私有字符串类型变量 mDataContent。” + + private long mDataContentData1;//“私有长整型变量 mDataContentData1。” + + private String mDataContentData3;//“私有字符串类型变量 mDataContentData3。” + + private ContentValues mDiffDataValues;//“私有 ContentValues 类型变量 mDiffDataValues。” + + public SqlData(Context context) {//“公共的 SqlData 构造方法,接收一个 Context 参数。” + mContentResolver = context.getContentResolver();//“将成员变量 mContentResolver 赋值为传入的 context 的内容解析器。” + mIsCreate = true;//“将成员变量 mIsCreate 赋值为 true。” + mDataId = INVALID_ID;//“将成员变量 mIsCreate 赋值为 true。” + mDataMimeType = DataConstants.NOTE;//“将成员变量 mIsCreate 赋值为 true。” + mDataContent = "";//“将成员变量 mDataContent 赋值为空字符串。 + mDataContentData1 = 0;//“将成员变量 mDataContentData1 赋值为 0。” + mDataContentData3 = "";//将成员变量 mDataContentData3 赋值为空字符串。” + mDiffDataValues = new ContentValues();//将成员变量 mDataContentData3 赋值为空字符串。” + } + + public SqlData(Context context, Cursor c) {//将成员变量 mDataContentData3 赋值为空字符串。” + mContentResolver = context.getContentResolver();//将成员变量 mDataContentData3 赋值为空字符串。” + mIsCreate = false;//将成员变量 mIsCreate 赋值为 false。” + loadFromCursor(c);//调用 loadFromCursor 方法,传入 Cursor 参数 c。” + mDiffDataValues = new ContentValues();//“将成员变量 mDiffDataValues 赋值为一个新的 ContentValues 对象。” + } + + private void loadFromCursor(Cursor c) {//“私有方法 loadFromCursor,接收一个 Cursor 参数。” + mDataId = c.getLong(DATA_ID_COLUMN);//“将成员变量 mDataId 赋值为 Cursor 中对应 DATA_ID_COLUMN 列的长整型值。” + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);//将成员变量 mDataMimeType 赋值为 Cursor 中对应 DATA_MIME_TYPE_COLUMN 列的字符串值。” + mDataContent = c.getString(DATA_CONTENT_COLUMN);//“将成员变量 mDataContent 赋值为 Cursor 中对应 DATA_CONTENT_COLUMN 列的字符串值。” + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);//将成员变量 mDataContentData1 赋值为 Cursor 中对应 DATA_CONTENT_DATA_1_COLUMN 列的长整型值。” + + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);//“将成员变量 mDataContentData3 赋值为 Cursor 中对应 DATA_CONTENT_DATA_3_COLUMN 列的字符串值。” + } + + public void setContent(JSONObject js) throws JSONException {//“公共方法 setContent,接收一个 JSON 对象参数 js,可能抛出 JSON 异常。” + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;//“如果 JSON 对象 js 包含键为 DataColumns.ID 的值,则将 dataId 赋值为该值的长整型,否则赋值为无效 ID(INVALID_ID)。” + if (mIsCreate || mDataId != dataId) {//“如果 JSON 对象 js 包含键为 DataColumns.ID 的值,则将 dataId 赋值为该值的长整型,否则赋值为无效 ID(INVALID_ID)。” + mDiffDataValues.put(DataColumns.ID, dataId);//将 dataId 放入成员变量 mDiffDataValues 的键为 DataColumns.ID 的位置。” + } + mDataId = dataId;//“将成员变量 mDataId 赋值为 dataId。” + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE;//“如果 JSON 对象 js 包含键为 DataColumns.MIME_TYPE 的值,则将 dataMimeType 赋值为该值的字符串,否则赋值为常量 NOTE。” + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {//如果是创建状态或者成员变量 mDataMimeType 不等于 dataMimeType。” + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);//将 dataMimeType 放入成员变量 mDiffDataValues 的键为 DataColumns.MIME_TYPE 的位置。” + } + mDataMimeType = dataMimeType;//“将成员变量 mDataMimeType 赋值为 dataMimeType。” + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";//如果 JSON 对象 js 包含键为 DataColumns.CONTENT 的值,则将 dataContent 赋值为该值的字符串,否则赋值为空字符串。” + if (mIsCreate || !mDataContent.equals(dataContent)) {//“如果是创建状态或者成员变量 mDataContent 不等于 dataContent。” + mDiffDataValues.put(DataColumns.CONTENT, dataContent);//“将 dataContent 放入成员变量 mDiffDataValues 的键为 DataColumns.CONTENT 的位置。” + } + mDataContent = dataContent;//“将成员变量 mDataContent 赋值为 dataContent。” + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;//“如果 JSON 对象 js 包含键为 DataColumns.DATA1 的值,则将 dataContentData1 赋值为该值的长整型,否则赋值为 0。” + if (mIsCreate || mDataContentData1 != dataContentData1) {//“如果是创建状态或者成员变量 mDataContentData1 不等于 dataContentData1。” + mDiffDataValues.put(DataColumns.DATA1, dataContentData1);//将 dataContentData1 放入成员变量 mDiffDataValues 的键为 DataColumns.DATA1 的位置。” + } + mDataContentData1 = dataContentData1;//将成员变量 mDataContentData1 赋值为 dataContentData1。” + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";//“如果 JSON 对象 js 包含键为 DataColumns.DATA3 的值,则将 dataContentData3 赋值为该值的字符串,否则赋值为空字符串。” + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {//如果是创建状态或者成员变量 mDataContentData3 不等于 dataContentData3。” + mDiffDataValues.put(DataColumns.DATA3, dataContentData3);//将 dataContentData3 放入成员变量 mDiffDataValues 的键为 DataColumns.DATA3 的位置。” + } + mDataContentData3 = dataContentData3;//“将成员变量 mDataContentData3 赋值为 dataContentData3。” + } + + public JSONObject getContent() throws JSONException {//“公共方法 getContent,返回一个 JSON 对象,可能抛出 JSON 异常。” + if (mIsCreate) {//“如果是创建状态。” + Log.e(TAG, "it seems that we haven't created this in database yet");//使用错误级别记录日志,TAG 为标识,信息为‘it seems that we haven't created this in database yet’(看起来我们还没有在数据库中创建这个)。” + return null;//“返回 null。 + } + JSONObject js = new JSONObject();//“返回 null。 + js.put(DataColumns.ID, mDataId);//“将成员变量 mDataId 放入 JSON 对象 js 的键为 DataColumns.ID 的位置。” + js.put(DataColumns.MIME_TYPE, mDataMimeType);//将成员变量 mDataMimeType 放入 JSON 对象 js 的键为 DataColumns.MIME_TYPE 的位置。” + js.put(DataColumns.CONTENT, mDataContent);//“将成员变量 mDataContent 放入 JSON 对象 js 的键为 DataColumns.CONTENT 的位置。” + js.put(DataColumns.DATA1, mDataContentData1);//“将成员变量 mDataContentData1 放入 JSON 对象 js 的键为 DataColumns.DATA1 的位置。” + js.put(DataColumns.DATA3, mDataContentData3);//“将成员变量 mDataContentData3 放入 JSON 对象 js 的键为 DataColumns.DATA3 的位置。” + return js;//“返回 JSON 对象 js。” + } + + public void commit(long noteId, boolean validateVersion, long version) {//“公共方法 commit,接收一个长整型 noteId、一个布尔型 validateVersion 和一个长整型 version。” + + if (mIsCreate) {//“如果是创建状态。” + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {//“如果数据 ID 为无效值且差异数据值(mDiffDataValues)中包含键为 DataColumns.ID。” + mDiffDataValues.remove(DataColumns.ID);//从差异数据值中移除键为 DataColumns.ID 的数据。 + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId);//“将 noteId 放入差异数据值的键为 DataColumns.NOTE_ID 的位置。” + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);//“使用内容解析器插入数据到 Notes 的 CONTENT_DATA_URI,并将返回的 Uri 赋值给 uri。” + try {//“尝试执行以下代码块。” + mDataId = Long.valueOf(uri.getPathSegments().get(1));//“将 uri 的路径片段中的第二个值转换为长整型并赋值给数据 ID(mDataId)。” + } catch (NumberFormatException e) {//“捕获数字格式异常,如果发生异常执行以下代码块。” + Log.e(TAG, "Get note id error :" + e.toString());//“使用错误级别记录日志,TAG 为标识,信息为‘Get note id error :’加上异常信息。” + throw new ActionFailureException("create note failed");//抛出创建笔记失败的异常。” + } + } else {//“如果不是创建状态。 + if (mDiffDataValues.size() > 0) {//如果差异数据值的大小大于 0。” + int result = 0;//初始化结果变量为 0。” + if (!validateVersion) {//“如果不验证版本。” + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);//使用内容解析器更新数据,将结果赋值给 result。 + } 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) + });//“使用内容解析器更新数据,带有版本验证,将结果赋值给 result。” + } + if (result == 0) {//“使用内容解析器更新数据,带有版本验证,将结果赋值给 result。” + Log.w(TAG, "there is no update. maybe user updates note when syncing");//“使用警告级别记录日志,TAG 为标识,信息为‘there is no update. maybe user updates note when syncing’(没有更新。可能用户在同步时更新了笔记)。” + } + } + } + + mDiffDataValues.clear();//“清空差异数据值。” + mIsCreate = false;//将创建状态设置为 false。” + } + + public long getId() {//公共方法 getId,返回一个长整型。” + return mDataId;//返回数据 ID(mDataId)。” + } +} diff --git a/SqlNote.java b/SqlNote.java new file mode 100644 index 0000000..ddefde6 --- /dev/null +++ b/SqlNote.java @@ -0,0 +1,518 @@ +/* + * 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;//包名为“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;//导入“Notes”类; +import net.micode.notes.data.Notes.DataColumns;//导入“Notes”类的“DataColumns”内部类; + +import net.micode.notes.data.Notes.NoteColumns;//导入“Notes”类的“NoteColumns”内部类; +import net.micode.notes.gtask.exception.ActionFailureException;//导入“gtask 操作失败异常”; +import net.micode.notes.tool.GTaskStringUtils;//导入“Notes 工具类的字符串操作工具”; +import net.micode.notes.tool.ResourceParser;//导入资源解析器; + +import org.json.JSONArray;//导入 JSON 数组; +import org.json.JSONException;//导入 JSON 异常; +import org.json.JSONObject;//导入 JSON 对象; + +import java.util.ArrayList;//导入数组列表。 + + +public class SqlNote {//:定义一个名为“SqlNote”的公共类; + private static final String TAG = SqlNote.class.getSimpleName();//定义一个静态字符串变量“tag”,其值为当前类的简单名称; + + private static final int INVALID_ID = -99999;//定义一个静态整型常量“INVALID_ID”,其值为-99999; + + public static final String[] PROJECTION_NOTE = new String[] {//定义一个公共静态字符串数组“PROJECTION_NOTE”,其中包含了一些列名; + + NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,//“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.ID”、“NoteColumns.ALERTED_DATE”和“NoteColumns.BG_COLOR_ID”; + NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,//定义“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.CREATED_DATE”、“NoteColumns.HAS_ATTACHMENT”和“NoteColumns.MODIFIED_DATE”; + NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE,//定义“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.NOTES_COUNT”、“NoteColumns.PARENT_ID”、“NoteColumns.SNIPPET”和“NoteColumns.TYPE”; + NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID,//定义“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.WIDGET_ID”、“NoteColumns.WIDGET_TYPE”和“NoteColumns.SYNC_ID”; + NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,//定义“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.LOCAL_MODIFIED”、“NoteColumns.ORIGIN_PARENT_ID”和“NoteColumns.GTASK_ID”; + NoteColumns.VERSION//定义“PROJECTION_NOTE”数组中的元素,包括“NoteColumns.VERSION”; + }; + + public static final int ID_COLUMN = 0;//定义一个公共静态整型常量“ID_COLUMN”,其值为 0; + + public static final int ALERTED_DATE_COLUMN = 1;//定义一个公共静态整型常量“ALERTED_DATE_COLUMN”,其值为 1; + + public static final int BG_COLOR_ID_COLUMN = 2;//定义一个公共静态整型常量“BG_COLOR_ID_COLUMN”,其值为 2; + + public static final int CREATED_DATE_COLUMN = 3;//定义一个公共静态整型常量“CREATED_DATE_COLUMN”,其值为 3; + + public static final int HAS_ATTACHMENT_COLUMN = 4;//定义一个公共静态整型常量“HAS_ATTACHMENT_COLUMN”,其值为 4; + + public static final int MODIFIED_DATE_COLUMN = 5;//定义一个公共静态整型常量“MODIFIED_DATE_COLUMN”,其值为 5; + + public static final int NOTES_COUNT_COLUMN = 6;//定义一个公共静态整型常量“NOTES_COUNT_COLUMN”,其值为 6; + + public static final int PARENT_ID_COLUMN = 7;//定义一个公共静态整型常量“PARENT_ID_COLUMN”,其值为 7; + + + public static final int SNIPPET_COLUMN = 8;//定义一个公共静态整型常量“SNIPPET_COLUMN”,其值为 8; + + public static final int TYPE_COLUMN = 9;//定义一个公共静态整型常量“TYPE_COLUMN”,其值为 9; + + public static final int WIDGET_ID_COLUMN = 10;//定义一个公共静态整型常量“WIDGET_ID_COLUMN”,其值为 10; + + public static final int WIDGET_TYPE_COLUMN = 11;//定义一个公共静态整型常量“WIDGET_TYPE_COLUMN”,其值为 11; + + public static final int SYNC_ID_COLUMN = 12;//定义一个公共静态整型常量“SYNC_ID_COLUMN”,其值为 12; + + public static final int LOCAL_MODIFIED_COLUMN = 13;//定义一个公共静态整型常量“LOCAL_MODIFIED_COLUMN”,其值为 13; + + public static final int ORIGIN_PARENT_ID_COLUMN = 14;//定义一个公共静态整型常量“ORIGIN_PARENT_ID_COLUMN”,其值为 14; + + public static final int GTASK_ID_COLUMN = 15;//定义一个公共静态整型常量“GTASK_ID_COLUMN”,其值为 15; + + public static final int VERSION_COLUMN = 16;//定义一个公共静态整型常量“VERSION_COLUMN”,其值为 16; + + private Context mContext;//定义一个私有成员变量“mContext”,其类型为“Context”; + + private ContentResolver mContentResolver;//定义一个私有成员变量“mContentResolver”,其类型为“ContentResolver”; + + private boolean mIsCreate;//定义一个私有成员变量“mIsCreate”,其类型为“boolean”; + + private long mId;//定义一个私有成员变量“mId”,其类型为“long”; + + private long mAlertDate;//定义一个私有成员变量“mAlertDate”,其类型为“long”; + + private int mBgColorId;//定义一个私有成员变量“mBgColorId”,其类型为“int”; + + private long mCreatedDate;//定义一个私有成员变量“mCreatedDate”,其类型为“long”; + + private int mHasAttachment;//定义一个私有成员变量“mHasAttachment”,其类型为“int”; + + private long mModifiedDate;//定义一个私有成员变量“mModifiedDate”,其类型为“long”; + + private long mParentId;//定义一个私有成员变量“mParentId”,其类型为“long”; + + private String mSnippet;//定义一个私有成员变量“mSnippet”,其类型为“String”; + + private int mType;//定义一个私有成员变量“mType”,其类型为“int”; + + private int mWidgetId;//定义一个私有成员变量“mWidgetId”,其类型为“int”; + + private int mWidgetType;//定义一个私有成员变量“mWidgetType”,其类型为“int”; + + private long mOriginParent;//定义一个私有成员变量“mOriginParent”,其类型为“long”; + + private long mVersion;//定义一个私有成员变量“mVersion”,其类型为“long”; + + private ContentValues mDiffNoteValues;//定义一个私有成员变量“mDiffNoteValues”,其类型为“ContentValues”; + + private ArrayList mDataList;//定义一个私有成员变量“mDataList”,其类型为“ArrayList”; + + public SqlNote(Context context) {//定义类的构造函数,接收一个“Context”类型的参数; + mContext = context;//在构造函数中,将传入的“context”赋值给成员变量“mContext”; + mContentResolver = context.getContentResolver();//获取“context”的内容解析器,并赋值给成员变量“mContentResolver”; + mIsCreate = true;//将成员变量“mIsCreate”设置为“true”,表示当前是创建新的笔记; + mId = INVALID_ID;//将成员变量“mId”设置为“INVALID_ID”,表示笔记的 ID 无效; + mAlertDate = 0;//将成员变量“mAlertDate”设置为 0,表示笔记的提醒日期为 0; + mBgColorId = ResourceParser.getDefaultBgId(context);//获取默认的背景颜色 ID,并赋值给成员变量“mBgColorId”; + mCreatedDate = System.currentTimeMillis();//获取当前系统时间,并赋值给成员变量“mCreatedDate”,表示笔记的创建日期; + mHasAttachment = 0;//将成员变量“mHasAttachment”设置为 0,表示笔记没有附件; + mModifiedDate = System.currentTimeMillis();//获取当前系统时间,并赋值给成员变量“mModifiedDate”,表示笔记的修改日期; + mParentId = 0;//“mParentId”设置为 0,表示笔记的父 ID 为 0; + mSnippet = "";//将成员变量“mSnippet”设置为空字符串,表示笔记的片段为空; + mType = Notes.TYPE_NOTE;//Notes.TYPE_NOTE”,表示笔记的类型为普通笔记; + + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;//将成员变量“mWidgetId”设置为“AppWidgetManager.INVALID_APPWIDGET_ID”,表示笔记的小部件 ID 无效; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE;//将成员变量“mWidgetType”设置为“Notes.TYPE_WIDGET_INVALIDE”,表示笔记的小部件类型无效; + mOriginParent = 0;//将成员变量“mOriginParent”设置为 0,表示笔记的原始父 ID 为 0; + mVersion = 0;//将成员变量“mVersion”设置为 0,表示笔记的版本号为 0; + mDiffNoteValues = new ContentValues();//创建一个新的“ContentValues”对象,并赋值给成员变量“mDiffNoteValues”; + mDataList = new ArrayList();//创建一个新的“ArrayList”对象,并赋值给成员变量“mDataList”; + } + + public SqlNote(Context context, Cursor c) {//定义类的另一个构造函数,接收一个“Context”类型的参数和一个“Cursor”类型的参数; + mContext = context;//在构造函数中,将传入的“context”赋值给成员变量“mContext”; + mContentResolver = context.getContentResolver();//获取“context”的内容解析器,并赋值给成员变量“mContentResolver”; + + mIsCreate = false;//将成员变量“mIsCreate”设置为“false”,表示当前不是创建新的笔记; + loadFromCursor(c);//调用“loadFromCursor”方法,从“Cursor”中加载笔记的数据; + mDataList = new ArrayList();//创建一个新的“ArrayList”对象,并赋值给成员变量“mDataList”; + if (mType == Notes.TYPE_NOTE)//如果笔记的类型为普通笔记,则调用“loadDataContent”方法加载笔记的数据内容; + loadDataContent(); + mDiffNoteValues = new ContentValues();//创建一个新的“ContentValues”对象,并赋值给成员变量“mDiffNoteValues” + } + + public SqlNote(Context context, long id) {//定义类的另一个构造函数,接收一个“Context”类型的参数和一个“long”类型的参数; + mContext = context;//在构造函数中,将传入的“context”赋值给成员变量“mContext”; + mContentResolver = context.getContentResolver();//获取“context”的内容解析器,并赋值给成员变量“mContentResolver”; + mIsCreate = false;//:将成员变量“mIsCreate”设置为“false”,表示当前不是创建新的笔记; + loadFromCursor(id);//调用“loadFromCursor”方法,根据“id”从数据库中加载笔记的数据; + mDataList = new ArrayList();//创建一个新的“ArrayList”对象,并赋值给成员变量“mDataList”; + if (mType == Notes.TYPE_NOTE)//如果笔记的类型为普通笔记,则调用“loadDataContent”方法加载笔记的数据内容; + loadDataContent(); + mDiffNoteValues = new ContentValues();//创建一个新的“ContentValues”对象,并赋值给成员变量“mDiffNoteValues” + + } + + private void loadFromCursor(long id) {//定义一个私有方法“loadFromCursor”,接收一个“long”类型的参数; + Cursor c = null;//声明一个“Cursor”类型的变量“c”,并初始化为“null”; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",//使用“mContentResolver”查询“Notes.CONTENT_NOTE_URI”表,获取指定“id”的笔记数据,并将结果存储在“c”中; + new String[] {//设置查询条件的参数值; + String.valueOf(id)//将“id”转换为字符串,并作为查询条件的参数值; + }, null);//设置查询的排序方式为“null”; + + if (c != null) {//如果“c”不为“null”,则执行以下操作; + c.moveToNext();//将游标移动到下一条记录; + loadFromCursor(c);//调用“loadFromCursor”方法,从游标中加载笔记的数据; + } else {//如果“c”为“null”,则执行以下操作; + Log.w(TAG, "loadFromCursor: cursor = null");//输出日志信息,提示游标为空; + } + } finally {//V在“finally”代码块中,执行以下操作; + if (c != null)//如果“c”不为“null”,则执行以下操作; + c.close();//关闭游标; + } + } + + private void loadFromCursor(Cursor c) {//定义一个私有方法“loadFromCursor”,接收一个“Cursor”类型的参数; + mId = c.getLong(ID_COLUMN);//从游标中获取“ID_COLUMN”列的值,并赋值给成员变量“mId”; + mAlertDate = c.getLong(ALERTED_DATE_COLUMN);//从游标中获取“ALERTED_DATE_COLUMN”列的值,并赋值给成员变量“mAlertDate”; + mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);//从游标中获取“BG_COLOR_ID_COLUMN”列的值,并赋值给成员变量“mBgColorId”; + mCreatedDate = c.getLong(CREATED_DATE_COLUMN);//从游标中获取“CREATED_DATE_COLUMN”列的值,并赋值给成员变量“mCreatedDate”; + mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);//从游标中获取“HAS_ATTACHMENT_COLUMN”列的值,并赋值给成员变量“mHasAttachment”; + mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);//从游标中获取“MODIFIED_DATE_COLUMN”列的值,并赋值给成员变量“mModifiedDate”; + mParentId = c.getLong(PARENT_ID_COLUMN);//从游标中获取“PARENT_ID_COLUMN”列的值,并赋值给成员变量“mParentId”; + mSnippet = c.getString(SNIPPET_COLUMN);//从游标中获取“SNIPPET_COLUMN”列的值,并赋值给成员变量“mSnippet”; + + mType = c.getInt(TYPE_COLUMN);//:从游标中获取“TYPE_COLUMN”列的值,并赋值给成员变量“mType”; + mWidgetId = c.getInt(WIDGET_ID_COLUMN);//从游标中获取“WIDGET_ID_COLUMN”列的值,并赋值给成员变量“mWidgetId”; + + mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);//从游标中获取“WIDGET_TYPE_COLUMN”列的值,并赋值给成员变量“mWidgetType” + mVersion = c.getLong(VERSION_COLUMN);//从游标中获取“VERSION_COLUMN”列的值,并赋值给成员变量“mVersion”; + } + + private void loadDataContent() {//定义一个私有方法“loadDataContent”; + Cursor c = null;//明一个“Cursor”类型的变量“c”,并初始化为“null”; + mDataList.clear();//清空“mDataList”列表; + try {在“try”代码块中,执行以下操作; + c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,//使用“mContentResolver”查询“Notes.CONTENT_DATA_URI”表,获取指定笔记的“SqlData”数据,并将结果存储在“c”中; + "(note_id=?)", new String[] { + String.valueOf(mId)// //是一个字符串数组,用于在数据库查询中指定条件。其中, note_id=?  是查询条件,表示要查找的笔记的 ID 为指定的值; new String[] { String.valueOf(mId) }  是条件的值,将当前笔记的 ID 转换为字符串并作为查询条件的值; null  表示不指定排序方式。 + }, null); + + if (c != null) {//如果游标不为空,则执行以下操作。 + if (c.getCount() == 0) {//如果游标中的行数为 0,则表示该笔记似乎没有数据。 + Log.w(TAG, "it seems that the note has not data");//:输出一条警告日志,提示该笔记似乎没有数据。 + return; + } + while (c.moveToNext()) {//当游标可以向下移动时,执行以下循环。 + SqlData data = new SqlData(mContext, c);//创建一个新的  SqlData  对象,用于表示当前游标指向的数据。 + mDataList.add(data);//将新创建的  SqlData  对象添加到  mDataList  列表中。 + } + } else {//如果游标为空,则执行以下操作。 + Log.w(TAG, "loadDataContent: cursor = null");//输出一条警告日志,提示游标为空。 + } + } finally {//无论游标是否为空,都执行以下操作。 + if (c != null) + c.close();//如果游标不为空,则关闭游标。 + } + } + + public boolean setContent(JSONObject js) { + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//从传入的  JSONObject  中获取名为  GTaskStringUtils.META_HEAD_NOTE  的子对象,并将其赋值给  note  变量。 + if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {//如果  note  对象的  type  字段的值为  Notes.TYPE_SYSTEM ,则表示这是一个系统文件夹,不能进行设置操作。 + Log.w(TAG, "cannot set system folder");//输出一条警告日志,提示不能设置系统文件夹。 + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {//如果  note  对象的  type  字段的值为  Notes.TYPE_FOLDER ,则表示这是一个文件夹,可以进行设置操作,但只能更新  snippet  和  type  字段。 + // for folder we can only update the snnipet and type + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : "";//获取  note  对象的  snippet  字段的值,如果该字段不存在,则使用空字符串作为默认值。 + if (mIsCreate || !mSnippet.equals(snippet)) {//如果当前是创建新笔记,或者当前的  snippet  值与从  note  对象中获取的  snippet  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);//将  snippet  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mSnippet = snippet;//将从  note  对象中获取的  snippet  值赋值给当前笔记的  snippet  字段。 + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE;//Notes.TYPE_NOTE; :获取  note  对象的  type  字段的值,如果该字段不存在,则使用  Notes.TYPE_NOTE  作为默认值。 + if (mIsCreate || mType != type) {//如果当前是创建新笔记,或者当前的  type  值与从  note  对象中获取的  type  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.TYPE, type);//将  type  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mType = type;//将从  note  对象中获取的  type  值赋值给当前笔记的  type  字段。 + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {//如果  note  对象的  type  字段的值为  Notes.TYPE_NOTE ,则表示这是一个普通笔记,可以进行设置操作。 + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);//从传入的  JSONObject  中获取名为  GTaskStringUtils.META_HEAD_DATA  的子对象,并将其转换为  JSONArray  类型。 + long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;//获取  note  对象的  id  字段的值,如果该字段不存在,则使用  INVALID_ID  作为默认值。 + if (mIsCreate || mId != id) {//如果当前是创建新笔记,或者当前的  id  值与从  note  对象中获取的  id  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.ID, id);//将  id  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mId = id;//将从  note  对象中获取的  id  值赋值给当前笔记的  id  字段。 + + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note + .getLong(NoteColumns.ALERTED_DATE) : 0;//获取  note  对象的  alertDate  字段的值,如果该字段不存在,则使用 0 作为默认值。 + if (mIsCreate || mAlertDate != alertDate) {//如果当前是创建新笔记,或者当前的  alertDate  值与从  note  对象中获取的  alertDate  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);//将  alertDate  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 +3 + } + mAlertDate = alertDate;//将从  note  对象中获取的  alertDate  值赋值给当前笔记的  alertDate  字段。 + + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note + .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);//获取  note  对象的  bgColorId  字段的值,如果该字段不存在,则使用默认的背景颜色 ID。 + if (mIsCreate || mBgColorId != bgColorId) {//如果当前是创建新笔记,或者当前的  bgColorId  值与从  note  对象中获取的  bgColorId  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);//将  bgColorId  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 +。 + } + mBgColorId = bgColorId;//将从  note  对象中获取的  bgColorId  值赋值给当前笔记的  bgColorId  字段。 + + long createDate = note.has(NoteColumns.CREATED_DATE) ? note + .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();//获取  note  对象的  createDate  字段的值,如果该字段不存在,则使用当前时间作为默认值。 + if (mIsCreate || mCreatedDate != createDate) {//如果当前是创建新笔记,或者当前的  createdDate  值与从  note  对象中获取的  createdDate  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);//将  createdDate  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mCreatedDate = createDate;//将从  note  对象中获取的  createdDate  值赋值给当前笔记的  createdDate  字段。 + + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note + .getInt(NoteColumns.HAS_ATTACHMENT) : 0;//获取  note  对象的  hasAttachment  字段的值,如果该字段不存在,则使用 0 作为默认值。 + if (mIsCreate || mHasAttachment != hasAttachment) {//如果当前是创建新笔记,或者当前的  hasAttachment  值与从  note  对象中获取的  hasAttachment  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);//将  hasAttachment  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mHasAttachment = hasAttachment;//将从  note  对象中获取的  hasAttachment  值赋值给当前笔记的  hasAttachment  字段。 + + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note + .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();//获取  note  对象的  modifiedDate  字段的值,如果该字段不存在,则使用当前时间作为默认值。 + if (mIsCreate || mModifiedDate != modifiedDate) {//如果当前是创建新笔记,或者当前的  modifiedDate  值与从  note  对象中获取的  modifiedDate  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);//将  modifiedDate  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mModifiedDate = modifiedDate;//将从  note  对象中获取的  modifiedDate  值赋值给当前笔记的  modifiedDate  字段。 + + long parentId = note.has(NoteColumns.PARENT_ID) ? note + .getLong(NoteColumns.PARENT_ID) : 0;//获取  note  对象的  parentId  字段的值,如果该字段不存在,则使用 0 作为默认值。 + if (mIsCreate || mParentId != parentId) {//如果当前是创建新笔记,或者当前的  parentId  值与从  note  对象中获取的  parentId  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);//将  parentId  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + + } + mParentId = parentId;//将从  note  对象中获取的  parentId  值赋值给当前笔记的  parentId  字段。 + + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : "";//获取  note  对象的  snippet  字段的值,如果该字段不存在,则使用空字符串作为默认值。 + if (mIsCreate || !mSnippet.equals(snippet)) {//如果当前是创建新笔记,或者当前的  snippet  值与从  note  对象中获取的  snippet  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);//将  snippet  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mSnippet = snippet;//将从  note  对象中获取的  snippet  值赋值给当前笔记的  snippet  字段。 + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE;//获取  note  对象的  type  字段的值,如果该字段不存在,则使用  Notes.TYPE_NOTE  作为默认值。 + if (mIsCreate || mType != type) {//如果当前是创建新笔记,或者当前的  type  值与从  note  对象中获取的  type  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.TYPE, type);//将  type  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mType = type;//从  note  对象中获取的  type  值赋值给当前笔记的  type  字段。 + + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) + : AppWidgetManager.INVALID_APPWIDGET_ID;.//获取  note  对象的  widgetId  字段的值,如果该字段不存在,则使用无效的小部件 ID 作为默认值。 + if (mIsCreate || mWidgetId != widgetId) {//如果当前是创建新笔记,或者当前的  widgetId  值与从  note  对象中获取的  widgetId  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);//将  widgetId  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mWidgetId = widgetId;//将从  note  对象中获取的  widgetId  值赋值给当前笔记的  widgetId  字段。 + + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note + .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;//获取  note  对象的  widgetType  字段的值,如果该字段不存在,则使用无效的小部件类型作为默认值。 + if (mIsCreate || mWidgetType != widgetType) {//如果当前是创建新笔记,或者当前的  widgetType  值与从  note  对象中获取的  widgetType  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);//将  widgetType  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mWidgetType = widgetType;//将从  note  对象中获取的  widgetType  值赋值给当前笔记的  widgetType  字段。 + + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note + .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;//获取  note  对象的  originParent  字段的值,如果该字段不存在,则使用 0 作为默认值。 + if (mIsCreate || mOriginParent != originParent) {//如果当前是创建新笔记,或者当前的  originParent  值与从  note  对象中获取的  originParent  值不相等,则执行以下操作。 + mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);//将  originParent  值添加到  mDiffNoteValues  对象中,以便在提交笔记时进行更新。 + } + mOriginParent = originParent;//将从  note  对象中获取的  originParent  值赋值给当前笔记的  originParent  字段。 + + for (int i = 0; i < dataArray.length(); i++) {//遍历  dataArray  数组,对每个元素执行以下操作。 + JSONObject data = dataArray.getJSONObject(i);//获取当前元素,并将其转换为  JSONObject  类型。 + SqlData sqlData = null;.//创建一个  SqlData  对象,并将其初始化为  null 。 + if (data.has(DataColumns.ID)) {//如果  data  对象包含名为  DataColumns.ID  的字段,则执行以下操作 + long dataId = data.getLong(DataColumns.ID);//获取该字段的值,并将其转换为  long  类型。 + for (SqlData temp : mDataList) {//遍历  mDataList  列表,对每个元素执行以下操作。 + if (dataId == temp.getId()) {//如果当前元素的  id  值与  dataId  相等,则执行以下操作。 + sqlData = temp;//将当前元素赋值给  sqlData  变量。 + } + } + } + + if (sqlData == null) {//如果  sqlData  变量仍然为  null ,则执行以下操作。 + sqlData = new SqlData(mContext);//创建一个新的  SqlData  对象,并将其赋值给  sqlData  变量。 + mDataList.add(sqlData);//新创建的  SqlData  对象添加到  mDataList  列表中。 + } + + sqlData.setContent(data);//将 data 的内容设置到 sqlData 中。 + } + } + } catch (JSONException e) {//如果在解析 JSONObject 时发生 JSONException 异常,则执行以下操作。 + Log.e(TAG, e.toString());//使用 Log.e 方法输出错误日志,其中包含异常的详细信息。 + e.printStackTrace();//打印异常的堆栈跟踪信息。 + return false;//返回 false ,表示设置内容失败。 + } + return true;//返回 true ,表示设置内容成功。 + } + + public JSONObject getContent() {//定义一个公共方法 getContent ,用于获取笔记的内容。 + try {//在 try 代码块中执行以下操作。 + JSONObject js = new JSONObject();//创建一个新的 JSONObject 对象 js 。 + + if (mIsCreate) {//如果笔记是新创建的,则执行以下操作。 + Log.e(TAG, "it seems that we haven't created this in database yet");//输出一条错误日志,提示笔记似乎还没有在数据库中创建。 + return null;//返回 null ,表示获取内容失败。 + } + + JSONObject note = new JSONObject();//创建一个新的 JSONObject 对象 note 。 + if (mType == Notes.TYPE_NOTE) {//如果笔记的类型是普通笔记,则执行以下操作。 + note.put(NoteColumns.ID, mId);//将笔记的 ID 添加到 note 对象中。 + note.put(NoteColumns.ALERTED_DATE, mAlertDate);//将笔记的提醒日期添加到 note 对象中。 + note.put(NoteColumns.BG_COLOR_ID, mBgColorId);//将笔记的背景颜色 ID 添加到 note 对象中。 + note.put(NoteColumns.CREATED_DATE, mCreatedDate);//将笔记的创建日期添加到 note 对象中。 + note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);//将笔记是否有附件添加到 note 对象中。 + note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);//将笔记的修改日期添加到 note 对象中。 + note.put(NoteColumns.PARENT_ID, mParentId);//将笔记的父 ID 添加到 note 对象中。 + note.put(NoteColumns.SNIPPET, mSnippet);//将笔记的片段添加到 note 对象中。 + note.put(NoteColumns.TYPE, mType);//将笔记的类型添加到 note 对象中。 + note.put(NoteColumns.WIDGET_ID, mWidgetId);//将笔记的小部件 ID 添加到 note 对象中。 + note.put(NoteColumns.WIDGET_TYPE, mWidgetType);//将笔记的小部件类型添加到 note 对象中。 + note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);//将笔记的原始父 ID 添加到 note 对象中。 + js.put(GTaskStringUtils.META_HEAD_NOTE, note);//将 note 对象添加到 js 对象中,键为 GTaskStringUtils.META_HEAD_NOTE 。 + + JSONArray dataArray = new JSONArray();//创建一个新的 JSONArray 对象 dataArray 。 + for (SqlData sqlData : mDataList) {//遍历 mDataList 列表中的每个 SqlData 对象。 + JSONObject data = sqlData.getContent();//获取当前 SqlData 对象的内容,并将其转换为 JSONObject 对象。 + if (data != null) {//如果 data 对象不为 null ,则执行以下操作。 + dataArray.put(data);//将 data 对象添加到 dataArray 对象中。 + } + } + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);//将 dataArray 对象添加到 js 对象中,键为 GTaskStringUtils.META_HEAD_DATA 。 + } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {//如果笔记的类型是文件夹或系统文件夹,则执行以下操作。 + note.put(NoteColumns.ID, mId);//将笔记的 ID 添加到 note 对象中。 + note.put(NoteColumns.TYPE, mType);//将笔记的类型添加到 note 对象中。 + note.put(NoteColumns.SNIPPET, mSnippet);//将笔记的片段添加到 note 对象中。 + js.put(GTaskStringUtils.META_HEAD_NOTE, note);//:将 note 对象添加到 js 对象中,键为 GTaskStringUtils.META_HEAD_NOTE 。 + } + + return js;//返回 js 对象,其中包含笔记的内容。 + } catch (JSONException e) {//如果在创建 JSONObject 或 JSONArray 时发生 JSONException 异常,则执行以下操作。 + Log.e(TAG, e.toString());//使用 Log.e 方法输出错误日志,其中包含异常的详细信息。 + e.printStackTrace();//打印异常的堆栈跟踪信息。 + } + return null;//返回 null ,表示获取内容失败。 + } + + public void setParentId(long id) {//定义一个公共方法 setParentId ,用于设置笔记的父 ID 。 + mParentId = id;//将 id 赋值给成员变量 mParentId 。 + mDiffNoteValues.put(NoteColumns.PARENT_ID, id);//将 id 添加到 mDiffNoteValues 对象中,以便在提交更改时更新数据库中的父 ID  + } + + public void setGtaskId(String gid) {//定义一个公共方法 setGtaskId ,用于设置笔记的 gtask_id 。 + mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);//将 gid 添加到 mDiffNoteValues 对象中,以便在提交更改时更新数据库中的 gtask_id 。 + } + + public void setSyncId(long syncId) {//定义一个公共方法 setSyncId ,用于设置笔记的同步 ID 。 + mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);//将 syncId 添加到 mDiffNoteValues 对象中,以便在提交更改时更新数据库中的同步 ID 。 + } + + public void resetLocalModified() {//定义一个公共方法 resetLocalModified ,用于重置笔记的本地修改状态。 + mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);//将 0 添加到 mDiffNoteValues 对象中,以便在提交更改时将本地修改状态重置为 false 。 + } + + public long getId() {//定义一个公共方法 getId ,用于获取笔记的 ID 。 + + return mId;//返回成员变量 mId 的值,即笔记的 ID 。 + } + + public long getParentId() {//定义一个公共方法 getParentId ,用于获取笔记的父 ID 。 + return mParentId;//返回成员变量 mParentId 的值,即笔记的父 ID 。 + } + + public String getSnippet() {//定义一个公共方法 getSnippet ,用于获取笔记的片段。 + return mSnippet;//返回成员变量 mSnippet 的值,即笔记的片段。 + } + + public boolean isNoteType() {//定义一个公共方法 isNoteType ,用于判断笔记是否为普通笔记。 + return mType == Notes.TYPE_NOTE;//返回一个布尔值,表示笔记的类型是否为普通笔记。 + } + + public void commit(boolean validateVersion) {//定义一个公共方法 commit ,用于提交笔记的更改。 + if (mIsCreate) {//如果笔记是新创建的,则执行以下操作。 + if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {//如果笔记的 ID 为无效值( INVALID_ID ),并且 mDiffNoteValues 对象中包含 NoteColumns.ID 键,则执行以下操作。 + mDiffNoteValues.remove(NoteColumns.ID);//从 mDiffNoteValues 对象中删除 NoteColumns.ID 键及其对应的值。 + } + + Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);//使用 mContentResolver 对象的 insert 方法将 mDiffNoteValues 对象中的数据插入到数据库中,并获取插入后生成的 Uri 对象。 + try {//在 try 代码块中执行以下操作。 + mId = Long.valueOf(uri.getPathSegments().get(1));//从 uri 对象中获取路径段的第二个元素,并将其转换为 long 类型,然后赋值给成员变量 mId 。 + } catch (NumberFormatException e) {//如果在将路径段元素转换为 long 类型时发生 NumberFormatException 异常,则执行以下操作 + Log.e(TAG, "Get note id error :" + e.toString());//使用 Log.e 方法输出错误日志,其中包含异常的详细信息。 + throw new ActionFailureException("create note failed");//抛出一个 ActionFailureException 异常,提示创建笔记失败。 + } + if (mId == 0) {//如果笔记的 ID 为 0,则执行以下操作。 + throw new IllegalStateException("Create thread id failed");//出一个 IllegalStateException 异常,提示创建线程 ID 失败。 + } + + if (mType == Notes.TYPE_NOTE) {//果笔记的类型是普通笔记,则执行以下操作。 + for (SqlData sqlData : mDataList) {//遍历 mDataList 列表中的每个 SqlData 对象。 + sqlData.commit(mId, false, -1);//调用 sqlData 对象的 commit 方法,将笔记的 ID 、是否验证版本和版本号作为参数传递给该方法。 + } + } + } else {//如果笔记不是新创建的,则执行以下操作。 + if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {//如果笔记的 ID 小于等于 0,并且不等于根文件夹 ID 和通话记录文件夹 ID ,则执行以下操作。 + Log.e(TAG, "No such note");//使用 Log.e 方法输出错误日志,提示没有找到这样的笔记。 + throw new IllegalStateException("Try to update note with invalid id");//抛出一个 IllegalStateException 异常,提示尝试使用无效的 ID 更新笔记。 + } + if (mDiffNoteValues.size() > 0) {//如果 mDiffNoteValues 对象中包含的键值对数量大于 0,则执行以下操作。 + mVersion ++;//将成员变量 mVersion 的值加 1。 + int result = 0;//定义一个整型变量 result ,并将其初始化为 0。 + if (!validateVersion) {//如果不验证版本,则执行以下操作。 + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?)", new String[] { + String.valueOf(mId) + });//使用 mContentResolver 对象的 update 方法更新数据库中的笔记数据,将 mDiffNoteValues 对象中的键值对作为更新的数据, NoteColumns.ID 作为条件, mId 作为条件的值。 + } else { + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", + new String[] { + String.valueOf(mId), String.valueOf(mVersion) + });//使用 mContentResolver 对象的 update 方法更新数据库中的笔记数据,将 mDiffNoteValues 对象中的键值对作为更新的数据, NoteColumns.ID 和 NoteColumns.VERSION 作为条件, mId 和 mVersion 作为条件的值。 + } + if (result == 0) {//如果更新操作没有成功执行(即更新的行数为 0),则执行以下操作。 + Log.w(TAG, "there is no update. maybe user updates note when syncing");//使用 Log.w 方法输出警告日志,提示没有更新笔记,可能是用户在同步时更新了笔记。 + } + } + + if (mType == Notes.TYPE_NOTE) {//如果笔记的类型是普通笔记,则执行以下操作。 + for (SqlData sqlData : mDataList) {//遍历 mDataList 列表中的每个 SqlData 对象。 + sqlData.commit(mId, validateVersion, mVersion);//调用 sqlData 对象的 commit 方法,将笔记的 ID 、是否验证版本和版本号作为参数传递给该方法。 + } + } + } + + // refresh local info + loadFromCursor(mId);//从数据库中加载笔记的最新信息。 + if (mType == Notes.TYPE_NOTE)//如果笔记的类型是普通笔记,则执行以下操作。 + loadDataContent();//加载笔记的数据内容。 + + mDiffNoteValues.clear();//清空 mDiffNoteValues 对象中的键值对。 + mIsCreate = false;//将成员变量 mIsCreate 的值设置为 false ,表示笔记不再是新创建的。 + } +} diff --git a/Task.java b/Task.java new file mode 100644 index 0000000..c4f8c31 --- /dev/null +++ b/Task.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data;//定义了该Java类所属的包名为net.micode.notes.gtask.data + +import android.database.Cursor;//引入Android数据库游标(Cursor)类,用于在数据库查询结果中遍历数据 +import android.text.TextUtils;//引入Android的文本工具类TextUtils,它提供了一些处理文本的实用方法,比如判断文本是否为空等操作 +import android.util.Log;//引入Android的日志工具类Log,用于在应用程序中输出日志信息,方便调试等用途 + +import net.micode.notes.data.Notes;//入名为Notes的类,它应该是来自net.micode.notes.data包下的,可能用于处理笔记相关的数据或操作 +import net.micode.notes.data.Notes.DataColumns;//引入Notes类中的DataColumns内部类或静态成员类,可能定义了与数据列相关的常量或属性,用于在数据库操作等场景下明确数据列的标识 +import net.micode.notes.data.Notes.DataConstants;//引入Notes类中的DataConstants内部类或静态成员类,可能包含了一些与数据相关的常量定义,用于在整个应用程序中统一使用这些常量值来表示特定的数据相关概念 +import net.micode.notes.data.Notes.NoteColumns;//引入Notes类中的NoteColumns内部类或静态成员类,可能用于定义笔记相关的数据列信息,比如笔记的标题、内容等各列在数据库中的标识等 +import net.micode.notes.gtask.exception.ActionFailureException;//引入名为ActionFailureException的异常类,它应该是来自net.micode.notes.gtask.exception包下的,用于在特定操作失败时抛出相应的异常以便进行错误处理 +import net.micode.notes.tool.GTaskStringUtils;//引入名为GTaskStringUtils的工具类,它应该是来自net.micode.notes.tool包下的,可能提供了一些与处理字符串相关的实用方法,用于在与GTask相关的字符串操作场景中使用 + +import org.json.JSONArray;//引入JSON数组处理类JSONArray,用于在Java程序中处理JSON格式数据中的数组类型数据 +import org.json.JSONException;//引入JSON异常处理类JSONException,用于在处理JSON数据时捕获可能出现的异常,比如JSON格式不正确等情况导致的异常 +import org.json.JSONObject;//引入JSON对象处理类JSONObject,用于在Java程序中处理JSON格式数据中的对象类型数据 + + +public class Task extends Node {// // 定义一个公共的类Task,它继承自Node类 + private static final String TAG = Task.class.getSimpleName(); // 定义一个私有静态常量TAG,其值为Task类的简单名称(即类名本身,不包含包名等),通常用于在日志输出等场景中标识当前类 + + private boolean mCompleted;// 定义一个私有布尔型变量mCompleted,用于表示任务是否已完成 + + private String mNotes; // 定义一个私有字符串变量mNotes,可能用于存储与任务相关的备注信息 + + private JSONObject mMetaInfo; // 定义一个私有JSONObject类型的变量mMetaInfo,可能用于存储任务的一些元信息(以JSON对象的形式) + + private Task mPriorSibling;// 定义一个私有Task类型的变量mPriorSibling,用于指向当前任务的前一个兄弟任务(在任务列表等场景下,同一层级的前一个任务) + + private TaskList mParent; // 定义一个私有TaskList类型的变量mParent,用于指向当前任务所属的任务列表(即当前任务的父级任务列表对象) + + public Task() {// 定义Task类的构造函数 + super();// 调用父类(Node类)的构造函数 + + mCompleted = false;// 将任务完成状态mCompleted初始化为false,表示任务初始未完成 + mNotes = null; // 将任务备注信息mNotes初始化为null + mPriorSibling = null; // 将前一个兄弟任务mPriorSibling初始化为null + mParent = null;// 将所属任务列表mParent初始化为null + mMetaInfo = null; // 将任务元信息mMetaInfo初始化为null + } + + public JSONObject getCreateAction(int actionId) {// 定义一个公共方法getCreateAction,它接受一个整数类型的参数actionId,用于生成创建任务的相关操作信息(以JSONObject的形式返回) + JSONObject js = new JSONObject();// 创建一个新的JSONObject对象js,用于构建要返回的操作信息 + + try { // 开始一个try块,用于捕获可能出现的JSONException异常 + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + // 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ACTION_TYPE(通过工具类GTaskStringUtils获取的表示操作类型的常量字符串), + // 值为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)); + // 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ACTION_ID(通过工具类获取的表示操作ID的常量字符串), + // 值为传入的参数actionId,用于标识此次创建任务操作的唯一ID + // entity_delta + JSONObject entity = new JSONObject(); + // 创建一个新的JSONObject对象entity,用于构建任务实体相关的差异信息(在创建或更新任务等操作中,可能用于表示任务实体的新增、修改等情况的信息) + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());// 在entity这个JSONObject对象中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_CREATOR_ID(通过工具类获取的表示创建者ID的常量字符串), + + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");// 值为字符串"null",可能用于表示当前任务创建者的ID信息,这里暂时设置为"null"(具体含义可能根据业务逻辑而定) + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK); // 在entity这个JSONObject对象中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ENTITY_TYPE(通过工具类获取的表示实体类型的常量字符串), + // 值为GTaskStringUtils.GTASK_JSON_TYPE_TASK(通过工具类获取的表示任务类型的常量字符串),用于明确当前实体是任务类型 + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + // 如果通过调用getNotes方法(获取任务备注信息的方法)得到的结果不为null,就在entity这个JSONObject对象中添加一个键值对, + // 键为GTaskStringUtils.GTASK_JSON_NOTES(通过工具类获取的表示任务备注的常量字符串),值为通过getNotes方法得到的任务备注信息 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); +// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ENTITY_DELTA(通过工具类获取的表示实体差异的常量字符串), + // 值为前面构建好的entity这个JSONObject对象,用于将任务实体相关的差异信息添加到整体的操作信息中 + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); +// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_PARENT_ID(通过工具类获取的表示父级ID的常量字符串), + // 值为通过mParent(当前任务所属的任务列表对象)调用getGid方法得到的结果,用于设置当前任务所属任务列表的唯一标识(GID),可能用于关联任务和其所属任务列表 + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); +// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE(通过工具类获取的表示目标父级类型的常量字符串), + // 值为GTaskStringUtils.GTASK_JSON_TYPE_GROUP(通过工具类获取的表示组类型的常量字符串),可能用于在某些操作场景下(比如移动任务等)标识目标父级的类型为组类型 +java +复制 + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); +// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_LIST_ID(通过工具类获取的表示列表ID的常量字符串), + // 值为通过mParent(当前任务所属的任务列表对象)调用getGid方法得到的结果,用于设置当前任务所属任务列表的唯一标识(GID),可能用于在某些操作场景下(比如查询任务所在列表等)标识任务所属的列表 + // prior_sibling_id + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + } + // 如果mPriorSibling(当前任务的前一个兄弟任务对象)不为null,就在JSONObject对象js中添加一个键值对, + // 键为GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID(通过工具类获取的表示前一个兄弟任务ID的常量字符串), + // 值为通过mPriorSibling调用getGid方法得到的结果,用于设置当前任务前一个兄弟任务的唯一标识(GID),可能用于在某些操作场景下(比如排序任务等)标识前一个兄弟任务 + } catch (JSONException e) { + // 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.e(TAG, e.toString());// 使用Log类的e方法(用于输出错误级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出错误日志,以便在调试时查看出现异常的情况 +java +复制 + e.printStackTrace(); // 打印异常的堆栈跟踪信息,用于更详细地了解异常出现的位置和调用栈情况,方便调试 + throw new ActionFailureException("fail to generate task-create jsonobject");// 抛出一个ActionFailureException异常,并传入一个字符串参数"fail to generate task-create jsonobject",表示生成创建任务的JSONObject对象失败 + } + } + + return js;// 如果上述操作过程中没有出现异常,就返回构建好的JSONObject对象js,它包含了创建任务的相关操作信息 + } + + public JSONObject getUpdateAction(int actionId) {// 定义一个公共方法getUpdateAction,它接受一个整数类型的参数actionId,用于生成更新任务的相关操作信息(以JSONObject的形式返回) + JSONObject js = new JSONObject(); // 创建一个新的JSONObject对象js,用于构建要返回的操作信息 + + try { // 开始一个try块,用于捕获可能出现的JSONException异常 + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + // 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ACTION_TYPE(通过工具类获取的表示操作类型的常量字符串), + // 值为GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE(同样通过工具类获取的表示更新操作类型的常量字符串),用于标识此次操作是更新任务的操作类型 + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); +// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ACTION_ID(通过工具类获取的表示操作ID的常量字符串), + // 值为传入的参数actionId,用于标识此次更新任务操作的唯一ID + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + // 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ID(通过工具类获取的表示任务ID的常量字符串), + // 值为通过调用getGid方法(应该是从父类继承或在当前类中定义的获取任务唯一标识的方法)得到的结果,用于设置要更新的任务的唯一标识(GID) + // entity_delta + JSONObject entity = new JSONObject(); + // 创建一个新的JSONObject对象entity,用于构建任务实体相关的差异信息(在创建或更新任务等操作中,可能用于表示任务实体的新增、修改等情况的信息) + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + // 在entity这个JSONObject对象中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_NAME(通过工具类获取的表示任务名称的常量字符串), + // 值为通过调用getName方法(应该是从父类继承或在当前类中定义的获取任务名称的方法)得到的结果,用于设置任务的名称信息 + + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } // 如果通过调用getNotes()方法(获取任务备注信息的方法)得到的结果不为null,就在entity这个JSONObject对象中添加一个键值对, + // 键为GTaskStringUtils.GTASK_JSON_NOTES(通过工具类获取的表示任务备注的常量字符串),值为通过getNotes()方法得到的任务备注信息 + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());// 在entity这个JSONObject对象中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_DELETED(通过工具类获取的表示是否删除的常量字符串), + // 值为通过调用getDeleted()方法(应该是从父类继承或在当前类中定义的获取任务是否删除状态的方法)得到的结果,用于设置任务的删除状态信息 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);// 在JSONObject对象js中添加一个键值对,键为GTaskStringUtils.GTASK_JSON_ENTITY_DELTA(通过工具类获取的表示实体差异的常量字符串), + // 值为前面构建好的entity这个JSONObject对象,用于将任务实体相关的差异信息添加到整体的操作信息中 + + } catch (JSONException e) { // 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.e(TAG, e.toString());// 使用Log类的e方法(用于输出错误级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出错误日志,以便在调试时查看出现异常的情况 + e.printStackTrace(); // 打印异常的堆栈跟踪信息,用于更详细地了解异常出现的位置和调用栈情况,方便调试 + throw new ActionFailureException("fail to generate task-update jsonobject"); // 抛出一个ActionFailureException异常,并传入一个字符串参数"fail to generate task-update jsonobject",表示生成更新任务的JSONObject对象失败 + } + } + + return js;// 如果上述操作过程中没有出现异常,就返回构建好的JSONObject对象js,它包含了更新任务的相关操作信息 + } + + public void setContentByRemoteJSON(JSONObject js) {// 定义一个公共方法setContentByRemoteJSON,它接受一个JSONObject类型的参数js,用于根据远程传来的JSON数据设置任务的相关内容 + if (js!= null) { // 如果传入的JSONObject对象js不为空 + + try { // 开始一个try块,用于捕获可能出现的JSONException异常 + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { // 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_ID(通过工具类获取的表示任务ID的常量字符串)的键值对 + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); // 调用setGid方法(应该是在当前类或其父类中定义的设置任务唯一标识的方法),并传入从JSON对象js中获取到的对应键的值(以字符串形式),用于设置任务的唯一标识(GID) + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { // 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_LAST_MODIFIED(通过工具类获取的表示最后修改时间的常量字符串)的键值对 + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); // 调用setLastModified方法(应该是在当前类或其父类中定义的设置最后修改时间的方法),并传入从JSON对象js中获取到的对应键的值(以长整型形式),用于设置任务的最后修改时间 + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {// 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_NAME(通过工具类获取的表示任务名称的常量字符串)的键值对 + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); // 调用setName方法(应该是在当前类或其父类中定义的设置任务名称的方法),并传入从JSON对象js中获取到的对应键的值(以字符串形式),用于设置任务的名称 + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { // 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_NOTES(通过工具类获取的表示任务备注的常量字符串)的键值对 + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));// 调用setNotes方法(应该是在当前类或其父类中定义的设置任务备注的方法),并传入从JSON对象js中获取到的对应键的值(以字符串形式),用于设置任务的备注信息 + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { // 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_DELETED(通过工具类获取的表示是否删除的常量字符串)的键值对 + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); // 调用setDeleted方法(应该是在当前类或其父类中定义的设置任务是否删除状态的方法),并传入从JSON对象js中获取到的对应键的值(以布尔型形式),用于设置任务的删除状态 + } + + // completed + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) {// 如果传入的JSON对象js包含键为GTaskStringUtils.GTASK_JSON_COMPLETED(通过工具类获取的表示是否完成的常量字符串)的键值对 + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); // 调用setCompleted方法(应该是在当前类或其父类中定义的设置任务是否完成状态的 method),并传入从JSON对象js中获取到的对应键的值(以布尔型形式),用于设置任务的完成状态 + } + } catch (JSONException e) { // 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.e(TAG, e.toString()); // 使用Log类的e方法(用于输出错误级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出错误日志,以便在调试时查看出现异常的情况 + e.printStackTrace(); // 打印异常的堆栈跟踪信息,用于更详细地了解异常出现的位置和调用栈情况,方便调试 + throw new ActionFailureException("fail to get task content from jsonobject");// 抛出一个ActionFailureException异常,并传入一个字符串参数"fail to get task content from jsonobject",表示从JSON对象中获取任务内容失败 + } + } + } + + public void setContentByLocalJSON(JSONObject js) {// 定义一个公共方法setContentByLocalJSON,它接受一个JSONObject类型的参数js,用于根据本地的JSON数据设置任务的相关内容 + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) {// 如果传入的JSONObject对象js为空,或者不包含键为GTaskStringUtils.META_HEAD_NOTE(通过工具类获取的特定头部信息相关的常量字符串)的键值对, + // 或者不包含键为GTaskStringUtils.META_HEAD_DATA(通过工具类获取的特定数据相关的常量字符串)的键值对 + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + }// 使用Log类的w方法(用于输出警告级别日志),输出一条警告日志,内容为"setContentByLocalJSON: nothing is avaiable",表示设置本地JSON内容时没有可用信息 + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);// 从传入的JSONObject对象js中获取键为GTaskStringUtils.META_HEAD_NOTE的键值对,并将其值(也是一个JSONObject类型)赋给note变量,用于获取特定的笔记相关信息 + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);// 从传入的JSONObject对象js中获取键为GTaskStringUtils.META_HEAD_DATA的键值对,并将其值(也是一个JSONArray类型)赋给dataArray变量,用于获取特定的数据数组相关信息 + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {// 如果从note这个JSONObject对象中获取到的键为NoteColumns.TYPE(通过工具类获取的表示类型的常量字符串)的值不等于Notes.TYPE_NOTE(应该是在Notes类中定义的表示笔记类型的常量) + Log.e(TAG, "invalid type");// 使用Log类的e方法(用于输出错误级别日志),输出一条错误日志,内容为"invalid type",表示类型无效 + return;// 直接返回,不再执行后续代码 + } + + for (int i = 0; i < dataArray.length(); i++) { // 遍历dataArray这个JSONArray对象中的每个元素 + JSONObject data = dataArray.getJSONObject(i);// 获取当前遍历到的元素(也是一个JSONObject类型),并赋给data变量 + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { // 如果通过TextUtils工具类的equals方法比较data这个JSONObject对象中键为DataColumns.MIME_TYPE(通过工具类获取的表示MIME类型的常量字符串)的值 + // 与DataConstants.NOTE(应该是在DataConstants类中定义的表示笔记的常量)相等 + setName(data.getString(DataColumns.CONTENT));// 调用setName方法(应该是在当前类或其父类中定义的设置任务名称的方法),并传入从data这个JSONObject对象中获取到的键为DataColumns.CONTENT(通过工具类获取的表示内容的常量字符串)的值(以字符串形式),用于设置任务的名称 + break; + } + } + + } catch (JSONException e) {// 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.e(TAG, e.toString());// 使用Log类的e方法(用于输出错误级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出错误日志,以便在调试时查看出现异常的情况 + e.printStackTrace(); // 打印异常的堆栈跟踪信息,用于更详细地了解异常出现的位置和调用栈情况,方便调试 + } + } + } + + public JSONObject getLocalJSONFromContent() { // 定义一个公共方法getLocalJSONFromContent,用于根据任务的内容生成本地的JSON对象 + String name = getName(); // 获取任务的名称,并赋给name变量 + try { // 开始一个try块,用于捕获可能出现的JSONException异常 + if (mMetaInfo == null) {// 如果任务的元信息mMetaInfo为空 + // new task created from web + if (name == null) { // 如果任务的名称name也为空 + Log.w(TAG, "the note seems to be an empty one"); // 使用Log类的w方法(用于输出警告级别日志),输出一条警告日志,内容为"the note seems to be an empty one",表示这个笔记似乎是空的 + return null;// 直接返回null,表示无法生成有效的本地JSON对象 + } + } + + JSONObject js = new JSONObject(); // 创建一个新的JSONObject对象js,用于构建要返回的本地JSON对象 + JSONObject note = new JSONObject(); // 创建一个新的JSONObject对象note,用于构建笔记相关的信息部分 + JSONArray dataArray = new JSONArray(); // 创建一个新的JSONArray对象dataArray,用于构建数据数组相关的信息部分 + JSONObject data = new JSONObject(); // 创建一个新的JSONObject对象data,用于构建具体的数据信息部分 + data.put(DataColumns.CONTENT, name);// 在data这个JSONObject对象中添加一个键值对,键为DataColumns.CONTENT(通过工具类获取的表示内容的常量字符串),值为前面获取到的任务名称name,用于设置数据的内容信息 + dataArray.put(data);// 将data这个JSONObject对象添加到dataArray这个JSONArray对象中,作为其中的一个元素 + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); // 在js这个JSONObject对象中添加一个键值对,键为GTreeStringUtils.META_HEAD_DATA(通过工具类获取的表示特定数据头部信息的常量字符串),值为前面构建好的dataArray这个JSONArray对象,用于设置本地JSON对象中的数据数组部分 + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); // 在note这个JSONObject对象中添加一个键值对,键为NoteColumns.TYPE(通过工具类获取的表示类型的常量字符串),值为Notes.TYPE_NOTE(应该是在Notes类中定义的表示笔记类型的常量),用于设置笔记的类型信息 + js.put(GTaskStringUtils.META_HEAD_NOTE, note); // 在js这个JSONObject对象中添加一个键值对,键为GTreeStringUtils.META_HEAD_NOTE(通过工具类获取的表示特定笔记头部信息的常量字符串),值为前面构建好的note这个JSONObject对象,用于设置本地JSON对象中的笔记部分 + return js; // 返回构建好的js这个JSONObject对象,它就是根据任务内容生成的本地JSON对象 + } else { + } else { + // synced task + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); // 从任务的元信息mMetaInfo中获取键为GTreeStringUtils.META_HEAD_NOTE的键值对,并将其值(也是一个JSONObject类型)赋给note变量,用于获取特定的笔记相关信息 + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); // 从任务的元信息mMetaInfo中获取键为GTreeStringUtils.META_HEAD_DATA的键值对,并将其值(也是一个JSONArray类型)赋给dataArray变量,用于获取特定的数据数组相关信息 + + for (int i = 0; i < dataArray.length(); i++) { // 遍历dataArray这个JSONArray对象中的每个元素 + JSONObject data = dataArray.getJSONObject(i); // 获取当前遍历到的元素(也是一个JSONObject类型),并赋给data变量 + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { // 如果通过TextUtils工具类的equals方法比较data这个JSONObject对象中键为DataColumns.MIME_TYPE(通过工具类获取的表示MIME类型的常量字符串)的值 + data.put(DataColumns.CONTENT, getName()); // 在data这个JSONObject对象中添加一个键值对,键为DataColumns.CONTENT(通过工具类获取的表示内容的常量字符串),值为通过getName方法获取到的任务名称,用于更新数据的内容信息 + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); // 在note这个JSONObject对象中添加一个键值对,键为NoteColumns.TYPE(通过工具类获取的表示类型的常量字符串),值为Notes.TYPE_NOTE(应该是在Notes类中定义的表示笔记类型的常量),用于更新笔记的类型信息 + return mMetaInfo; + } + } catch (JSONException e) { // 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.e(TAG, e.toString());// 使用Log类的e方法(用于输出错误级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出错误日志,以便在调试时查看出现异常的情况 + + e.printStackTrace(); // 打印异常的堆栈跟踪信息,用于更详细地了解异常出现的位置和调用栈情况,方便调试 + return null; // 返回null,表示生成本地JSON对象失败 + + } + } + + public void setMetaInfo(MetaData metaData) {// 定义一个公共方法setMetaInfo,它接受一个MetaData类型的参数metaData,用于设置任务的元信息 + if (metaData != null && metaData.getNotes() != null) { // 如果传入的MetaData对象metaData不为空,并且通过调用其getNotes方法获取到的笔记信息也不为空 + try {// 开始一个try块,用于捕获可能出现的JSONException异常 + mMetaInfo = new JSONObject(metaData.getNotes()); // 创建一个新的JSONObject对象mMetaInfo,并将通过metaData.getNotes()获取到的笔记信息(应该是字符串形式)转换为JSONObject类型后赋给mMetaInfo变量,用于设置任务的元信息 + } catch (JSONException e) { // 如果在上述操作过程中出现了JSONException异常,就执行以下代码块 + Log.w(TAG, e.toString()); // 使用Log类的w方法(用于输出警告级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出警告日志,以便在调试时查看出现异常的情况 + mMetaInfo = null; // 使用Log类的w方法(用于输出警告级别日志),将TAG(当前类的标识)和异常e的字符串表示形式(e.toString())作为参数,输出警告日志,以便在调试时查看出现异常的情况 + } + } + } + + public int getSyncAction(Cursor c) {// 定义一个公共方法getSyncAction,它接受一个Cursor类型的参数c,用于根据数据库游标(Cursor)中的信息获取同步操作的类型 + try { // 开始一个try块,用于捕获可能出现的Exception异常 + JSONObject noteInfo = null; // 定义一个JSONObject类型的变量noteInfo,并初始化为null,用于存储笔记相关的信息 + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { // 如果任务的元信息mMetaInfo不为空,并且包含键为GTaskStringUtils.META_HEAD_NOTE(通过工具类获取的表示特定笔记头部信息的常量字符串)的键值对 + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);// 从mMetaInfo这个JSONObject对象中获取键为GTreeStringUtils.META_HEAD_NOTE的键值对,并将其值(也是一个JSONObject类型)赋给noteInfo变量,用于获取特定的笔记相关信息 + } + + if (noteInfo == null) { // 如果noteInfo变量为null,即没有获取到有效的笔记相关信息 + Log.w(TAG, "it seems that note meta has been deleted"); // 使用Log类的 + return SYNC_ACTION_UPDATE_REMOTE;//返回同步操作更新远程。 + } + + if (!noteInfo.has(NoteColumns.ID)) {//如果笔记信息不包含(笔记列的 ID)。 + Log.w(TAG, "remote note id seems to be deleted");//记录警告日志,“远程笔记 ID 似乎已被删除”。 + return SYNC_ACTION_UPDATE_LOCAL;//返回同步操作更新本地。 + } + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {//如果 c 中获取的长整型(SQL 笔记的 ID 列)与笔记信息获取的长整型(笔记列的 ID)不相等。 + Log.w(TAG, "note id doesn't match");//记录警告日志,“笔记 ID 不匹配”。 + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {//如果 c 中获取的整型(SQL 笔记的本地修改列)为 0。 + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果 c 中获取的长整型(SQL 笔记的同步 ID 列)等于最后修改的时间。 + // no update both side + return SYNC_ACTION_NONE;//返回同步操作无。 + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL;//返回同步操作更新本地。 + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {//如果 c 中获取的字符串(SQL 笔记的谷歌任务 ID 列)不等于获取的谷歌任务 ID。 + Log.e(TAG, "gtask id doesn't match");//记录错误日志,“谷歌任务 ID 不匹配”。 + return SYNC_ACTION_ERROR;//返回同步操作更新远程。 + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果 c 中获取的长整型(SQL 笔记的同步 ID 列)等于最后修改的时间。 + // local modification only + return SYNC_ACTION_UPDATE_REMOTE;//返回同步操作更新远程。 + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + }//返回同步操作更新冲突。 + } + } catch (Exception e) {//捕获异常。 + Log.e(TAG, e.toString());//记录错误日志,标签和异常的字符串表示。 + e.printStackTrace();//打印异常堆栈跟踪。 + } + + return SYNC_ACTION_ERROR;//返回同步操作错误。 + } + + public boolean isWorthSaving() {//公共方法,判断是否值得保存。 + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); + }//返回元信息不为空,或者名称不为空且经过修剪后的长度大于 0,或者笔记不为空且经过修剪后的长度大于 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;//返回父任务列表。 + + } + + diff --git a/TaskList.java b/TaskList.java new file mode 100644 index 0000000..7649bce --- /dev/null +++ b/TaskList.java @@ -0,0 +1,344 @@ +/* + * 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;//包 net.micode.notes.gtask.data + +import android.database.Cursor;//导入安卓数据库游标 +import android.util.Log;//导入安卓日志记录 + +import net.micode.notes.data.Notes;//导入 net.micode.notes 数据的 Notes +import net.micode.notes.data.Notes.NoteColumns;//导入 net.micode.notes 数据的 Notes 的 NoteColumns +import net.micode.notes.gtask.exception.ActionFailureException;//导入 net.micode.notes.gtask 异常的操作失败异常 +import net.micode.notes.tool.GTaskStringUtils;//导入 net.micode.notes 工具的 GTaskStringUtils + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class TaskList extends Node {//公共类 TaskList 继承自 Node 类。 + private static final String TAG = TaskList.class.getSimpleName();//私有静态最终字符串变量 TAG,赋值为 TaskList 类的简单名称。 + + private int mIndex;//私有整型变量 mIndex。 + + private ArrayList mChildren;//私有 ArrayList 类型的变量 mChildren,泛型参数为 Task。 + + public TaskList() {//公共的 TaskList 构造函数。 + super();//调用父类的构造函数。 + mChildren = new ArrayList();//将 mChildren 初始化为一个新的 ArrayList,用于存储 Task 对象 + mIndex = 1;//将 mIndex 初始化为 1。 + } + + public JSONObject getCreateAction(int actionId) {//公共方法 getCreateAction,接收一个整型参数 actionId,返回一个 JSONObject 对象。 + JSONObject js = new JSONObject();//创建一个新的 JSONObject 对象 js。 + + try {//尝试执行以下代码块。 + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);//向 JSONObject 对象 js 中添加键值对,键为‘action_type’,值为 GTaskStringUtils 类中常量 GTASK_JSON_ACTION_TYPE_CREATE。 + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);//向 JSONObject 对象 js 中添加键值对,键为‘action_id’,值为传入的参数 actionId。 + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);//向 JSONObject 对象 js 中添加键值对,键为‘index’,值为 mIndex 变量的值。 + + // entity_delta + JSONObject entity = new JSONObject();//创建一个新的 JSONObject 对象 entity。 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//向 entity 对象中添加键值对,键为‘name’,值为调用 getName 方法返回的值。 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");//向 entity 对象中添加键值对,键为‘creator_id’,值为字符串‘null’。 + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP);//向 entity 对象中添加键值对,键为‘entity_type’,值为 GTaskStringUtils 类中常量 GTASK_JSON_TYPE_GROUP。 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);//向 js 对象中添加键值对,键为‘entity_delta’,值为 entity 对象。 + + } catch (JSONException e) {//捕获 JSONException 异常。 + Log.e(TAG, e.toString());//使用日志记录错误信息,TAG 为类名的简单名称,输出异常的字符串表示。 + e.printStackTrace();//打印异常堆栈跟踪。 + throw new ActionFailureException("fail to generate tasklist-create jsonobject");//抛出一个 ActionFailureException 异常,异常信息为‘fail to generate tasklist-create jsonobject’。 + } + + return js;//返回 JSONObject 对象 js。 + } + + public JSONObject getUpdateAction(int actionId) {//公共的 JSONObject 方法 getUpdateAction,接收一个动作 id。 + JSONObject js = new JSONObject();//创建一个新的 JSONObject 对象 js + + try {//尝试执行以下代码块 + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);//在 js 对象中添加键值对,action_type 为指定的值。 + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);//在 js 对象中添加键值对,action_id 为传入的动作 id。 + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());//在 js 对象中添加键值对,id 为获取的 gid。 + + // entity_delta + JSONObject entity = new JSONObject();//创建一个新的 JSONObject 对象 entity。 + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//在 entity 对象中添加键值对,name 为获取的名称。 + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());//在 entity 对象中添加键值对,deleted 为获取的已删除状态。 + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);//在 js 对象中添加键值对,entity_delta 为 entity 对象。 + + } catch (JSONException e) {//捕获 JSONException 异常。 + Log.e(TAG, e.toString());//记录错误日志,输出异常信息的字符串表示。 + e.printStackTrace();//打印异常的堆栈跟踪信息。 + throw new ActionFailureException("fail to generate tasklist-update jsonobject");//抛出一个 ActionFailureException 异常,提示生成任务列表更新 JSONObject 对象失败。 + } + + return js;//返回 js 对象。 + } + + public void setContentByRemoteJSON(JSONObject js) {//公共的方法 setContentByRemoteJSON,接收一个 JSONObject 对象 js。 + if (js != null) {//如果 js 对象不为空,则执行以下代码块。 + try {//尝试执行以下代码块。 + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {//如果 js 对象包含键 GTaskStringUtils.GTASK_JSON_ID,则执行以下代码块。 + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));//设置 gid 为 js 对象中键 GTaskStringUtils.GTASK_JSON_ID 的对应的值。 + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {//如果 js 对象包含键 GTaskStringUtils.GTASK_JSON_LAST_MODIFIED,则执行以下代码块。 + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));//设置最后修改时间为 js 对象中键 GTaskStringUtils.GTASK_JSON_LAST_MODIFIED 的对应的值。 + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {//如果 js 对象包含键 GTaskStringUtils.GTASK_JSON_NAME,则执行以下代码块。 + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));//设置名称为 js 对象中键 GTaskStringUtils.GTASK_JSON_NAME 的对应的值。 + } + + } catch (JSONException e) {//捕获 JSONException 异常。 + Log.e(TAG, e.toString());//记录错误日志,输出异常信息的字符串表示。 + e.printStackTrace();//打印异常的堆栈跟踪信息。 + throw new ActionFailureException("fail to get tasklist content from jsonobject");//抛出一个 ActionFailureException 异常,提示从 JSONObject 对象获取任务列表内容失败。 + } + } + } + + public void setContentByLocalJSON(JSONObject js) {//公共的方法 setContentByLocalJSON,接收一个 JSONObject 对象 js。 + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {//如果 js 对象为空或者不包含键 GTaskStringUtils.META_HEAD_NOTE,则执行以下代码块。 + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");//记录警告日志,提示在 setContentByLocalJSON 方法中没有可用内容。 + } + + try {//尝试执行以下代码块。 + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//从 js 对象中获取键 GTaskStringUtils.META_HEAD_NOTE 对应的 JSONObject 对象 folder。 + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {//如果 folder 对象中键 NoteColumns.TYPE 的对应的值为 Notes.TYPE_FOLDER,则执行以下代码块。 + String name = folder.getString(NoteColumns.SNIPPET);//获取名称为 folder 对象中键 NoteColumns.SNIPPET 对应的值。 + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name);//设置名称为指定的前缀加上获取的名称。 + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {//否则如果 folder 对象中键 NoteColumns.TYPE 的对应的值为 Notes.TYPE_SYSTEM,则执行以下代码块。 + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER)//如果 folder 对象中键 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)//否则如果 folder 对象中键 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) {//捕获 JSONException 异常。 + Log.e(TAG, e.toString());//记录错误日志,输出异常信息的字符串表示。 + e.printStackTrace();//打印异常的堆栈跟踪信息。 + } + } + + public JSONObject getLocalJSONFromContent() {//公共的 JSONObject 方法,用于从内容中获取本地 JSON 对象。 + try { + JSONObject js = new JSONObject();//创建一个新的 JSONObject 对象 js。 + JSONObject folder = new JSONObject();//创建一个新的 JSONObject 对象 folder。 + + + String folderName = getName();//获取名称并赋值给变量 folderName。 + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX))//如果名称以指定前缀开头。 + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length());//截取名称,去除前缀部分。 + folder.put(NoteColumns.SNIPPET, folderName);//在 folder 对象中添加键值对。 + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE))//如果名称等于指定的默认文件夹名或电话笔记文件夹名。 + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);//在 folder 对象中设置类型为系统类型。 + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);//在 folder 对象中设置类型为文件夹类型。 + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder);//在 js 对象中添加键值对。 + + return js; + } catch (JSONException e) {//捕获 JSONException 异常。 + Log.e(TAG, e.toString());//记录错误日志。 + e.printStackTrace();//打印异常堆栈信息。 + return null; + } + } + + public int getSyncAction(Cursor c) {//公共的获取同步操作方法。 + try {//尝试执行以下代码块。 + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {//如果游标中本地修改列的值为 0。 + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果同步 ID 列的值等于最后修改的值。 + // no update both side + return SYNC_ACTION_NONE;//返回无操作。 + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL;//返回更新本地。 + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {//如果游标中任务 ID 列的值不等于当前任务的 ID。 + Log.e(TAG, "gtask id doesn't match");//记录错误日志(任务 ID 不匹配)。 + return SYNC_ACTION_ERROR;//返回错误操作。 + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {//如果同步 ID 列的值等于最后修改的值。 + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE;//返回更新远程。 + } + } + } catch (Exception e) {//捕获异常。 + Log.e(TAG, e.toString());//记录错误日志 + e.printStackTrace();//打印异常堆栈信息 + } + + return SYNC_ACTION_ERROR;//返回错误操作。 + } + + public int getChildTaskCount() {//公共的获取子任务数量方法。 + return mChildren.size();//返回子任务列表的大小。 + } + + public boolean addChildTask(Task task) {//公共的添加子任务方法。 + boolean ret = false;//初始化返回值为假。 + if (task != null && !mChildren.contains(task)) {//如果任务不为空且子任务列表中不包含该任务。 + ret = mChildren.add(task);//将任务添加到子任务列表中并更新返回值。 + if (ret) {//如果添加成功。 + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1));//设置前一个兄弟任务。 + task.setParent(this);//设置父任务为当前任务。 + } + } + return ret;//返回添加子任务的结果。 + } + + public boolean addChildTask(Task task, int index) {//公共的在指定索引处添加子任务方法。 + if (index < 0 || index > mChildren.size()) {//如果索引无效。 + Log.e(TAG, "add child task: invalid index");//记录错误日志(添加子任务索引无效)。 + return false;//返回假。 + } + + int pos = mChildren.indexOf(task);//获取任务在子任务列表中的索引。 + if (task != null && pos == -1) {//如果任务不为空且索引为 - 1(即子任务列表中不包含该任务)。 + mChildren.add(index, task);//在指定索引处添加任务。 + + // update the task list + Task preTask = null;//初始化前一个任务为 null。 + Task afterTask = null;//初始化后一个任务为 null + if (index != 0) + preTask = mChildren.get(index - 1);//如果索引不为 0,获取前一个任务。 + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1);//如果索引不为子任务列表最后一个索引,获取后一个任务。 + + task.setPriorSibling(preTask);//设置任务的前一个兄弟任务。 + if (afterTask != null) + afterTask.setPriorSibling(task);//如果后一个任务不为空,设置后一个任务的前一个兄弟任务为当前任务。 + } + + return true;//返回添加子任务成功。 + } + + public boolean removeChildTask(Task task) {//公共的移除子任务方法。 + boolean ret = false;//初始化返回值为假。 + int index = mChildren.indexOf(task);//获取任务在子任务列表中的索引。 + if (index != -1) {//如果索引不为 - 1(即子任务列表中包含该任务)。 + ret = mChildren.remove(task);//从子任务列表中移除任务并更新返回值。 + + if (ret) {//如果移除成功。 + // reset prior sibling and parent + task.setPriorSibling(null);//设置任务的前一个兄弟任务为 null。 + task.setParent(null);//设置任务的父任务为 null。 + + // update the task list + if (index != mChildren.size()) {//如果索引不是子任务列表最后一个索引。 + mChildren.get(index).setPriorSibling(//设置当前索引处任务的前一个兄弟任务。 + index == 0 ? null : mChildren.get(index - 1));//如果索引为 0,则前一个兄弟任务为 null,否则为前一个索引处的任务。 + } + } + } + return ret;//返回移除子任务的结果。 + } + + public boolean moveChildTask(Task task, int index) {//公共的移动子任务方法。 + + if (index < 0 || index >= mChildren.size()) {//如果索引无效。 + Log.e(TAG, "move child task: invalid index");//记录错误日志(移动子任务索引无效)。 + return false; + } + + int pos = mChildren.indexOf(task);//获取任务在子任务列表中的索引。 + if (pos == -1) {//如果索引为 - 1(即子任务列表中不包含该任务)。 + Log.e(TAG, "move child task: the task should in the list");//记录错误日志(要移动的任务应该在列表中)。 + return false; + } + + if (pos == index) + return true;//如果当前索引等于要移动到的索引,则直接返回真。 + return (removeChildTask(task) && addChildTask(task, index));//移除任务,再在指定索引处添加任务,并返回结果。 + } + + public Task findChildTaskByGid(String gid) {//公共的通过任务 ID 查找子任务方法 + for (int i = 0; i < mChildren.size(); i++) {//遍历子任务列表。 + Task t = mChildren.get(i);//获取当前索引处的子任务 + if (t.getGid().equals(gid)) {//如果子任务的任务 ID 等于要查找的任务 ID。 + return t; + } + } + return null;//如果遍历完子任务列表都没有找到,则返回 null。 + } + + public int getChildTaskIndex(Task task) {//公共的获取子任务索引方法。 + return mChildren.indexOf(task);//返回子任务在子任务列表中的索引。 + } + + public Task getChildTaskByIndex(int index) {//公共的通过索引获取子任务方法。 + if (index < 0 || index >= mChildren.size()) {//如果索引无效。 + Log.e(TAG, "getTaskByIndex: invalid index");//记录错误日志(通过索引获取子任务索引无效)。 + return null; + } + return mChildren.get(index);//返回指定索引处的子任务。 + } + + public Task getChilTaskByGid(String gid) {//通过任务 ID 获取子任务方法。 + for (Task task : mChildren) {//遍历子任务列表。 + if (task.getGid().equals(gid))//如果子任务的任务 ID 等于要查找的任务 ID。 + return task;//返回该子任务。 + } + return null;//如果遍历完子任务列表都没有找到,则返回 null。 + } + + public ArrayList getChildTaskList() {//公共方法,用于获取子任务列表。 + return this.mChildren;//返回成员变量mChildren,即子任务列表。 + } + + public void setIndex(int index) {//公共方法,用于设置索引 + this.mIndex = index;//将传入的索引值赋给成员变量mIndex。 + } + + public int getIndex() {//公共方法,用于获取索引。 + return this.mIndex;//返回成员变量mIndex的值,即当前的索引。 + } +} -- 2.34.1 From 9d0c89b012996c0e5dbe652a302c2c906461cecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81?= <2251219137@qq.com> Date: Thu, 26 Dec 2024 23:29:40 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 小米便签开源代码的泛读报告.docx | Bin 0 -> 145395 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 小米便签开源代码的泛读报告.docx diff --git a/小米便签开源代码的泛读报告.docx b/小米便签开源代码的泛读报告.docx new file mode 100644 index 0000000000000000000000000000000000000000..8e6a1b5c1823ab7ccb531ad97f65580fe7223e6a GIT binary patch literal 145395 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 literal 0 HcmV?d00001 -- 2.34.1