You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Notes-master/SqlNote.java

582 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.gtask.data;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import net.micode.notes.tool.ResourceParser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
/**
* SqlNote 类用于管理与 Notes 数据库相关的操作,
* 提供对笔记数据的增、删、查、改操作。
* 该类将与 GTask 等数据交互,使用 SQLite 数据库与 ContentProvider 进行数据存取。
*/
public class SqlNote {
// 日志标签,用于输出调试信息
private static final String TAG = SqlNote.class.getSimpleName();
// 定义一个无效的 ID 值,用于初始化或标识错误的 ID
private static final int INVALID_ID = -99999;
// 定义一个 Notes 表的列名数组,用于查询操作中获取不同的列数据
public static final String[] PROJECTION_NOTE = new String[] {
NoteColumns.ID, // 笔记的 ID
NoteColumns.ALERTED_DATE, // 提醒日期
NoteColumns.BG_COLOR_ID, // 背景颜色 ID
NoteColumns.CREATED_DATE, // 创建日期
NoteColumns.HAS_ATTACHMENT, // 是否有附件
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.NOTES_COUNT, // 笔记内容数量
NoteColumns.PARENT_ID, // 父笔记 ID
NoteColumns.SNIPPET, // 笔记内容的摘录
NoteColumns.TYPE, // 笔记类型
NoteColumns.WIDGET_ID, // 小部件 ID
NoteColumns.WIDGET_TYPE, // 小部件类型
NoteColumns.SYNC_ID, // 同步 ID
NoteColumns.LOCAL_MODIFIED, // 本地修改标记
NoteColumns.ORIGIN_PARENT_ID, // 原始父笔记 ID
NoteColumns.GTASK_ID, // Google 任务 ID
NoteColumns.VERSION // 笔记版本
};
// 每一列在查询返回的 Cursor 中的索引
public static final int ID_COLUMN = 0;
public static final int ALERTED_DATE_COLUMN = 1;
public static final int BG_COLOR_ID_COLUMN = 2;
public static final int CREATED_DATE_COLUMN = 3;
public static final int HAS_ATTACHMENT_COLUMN = 4;
public static final int MODIFIED_DATE_COLUMN = 5;
public static final int NOTES_COUNT_COLUMN = 6;
public static final int PARENT_ID_COLUMN = 7;
public static final int SNIPPET_COLUMN = 8;
public static final int TYPE_COLUMN = 9;
public static final int WIDGET_ID_COLUMN = 10;
public static final int WIDGET_TYPE_COLUMN = 11;
public static final int SYNC_ID_COLUMN = 12;
public static final int LOCAL_MODIFIED_COLUMN = 13;
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
public static final int GTASK_ID_COLUMN = 15;
public static final int VERSION_COLUMN = 16;
// 上下文和内容解析器,提供与系统服务的交互接口
private Context mContext;
private ContentResolver mContentResolver;
// 是否是新创建的笔记
private boolean mIsCreate;
// 笔记的各类数据成员
private long mId;
private long mAlertDate;
private int mBgColorId;
private long mCreatedDate;
private int mHasAttachment;
private long mModifiedDate;
private long mParentId;
private String mSnippet;
private int mType;
private int mWidgetId;
private int mWidgetType;
private long mOriginParent;
private long mVersion;
// 保存笔记的差异数据
private ContentValues mDiffNoteValues;
// 存储与笔记相关的数据列表
private ArrayList<SqlData> mDataList;
}
// 构造函数:用于创建一个新笔记对象,初始化其成员变量
public SqlNote(Context context) {
// 初始化上下文和内容解析器,用于访问应用的内容提供者
mContext = context;
mContentResolver = context.getContentResolver();
// 标记这是一个新创建的笔记
mIsCreate = true;
// 初始化各个成员变量,设置默认值
mId = INVALID_ID; // 默认的无效ID
mAlertDate = 0; // 默认没有提醒
mBgColorId = ResourceParser.getDefaultBgId(context); // 获取默认背景颜色
mCreatedDate = System.currentTimeMillis(); // 设置为当前时间戳
mHasAttachment = 0; // 默认没有附件
mModifiedDate = System.currentTimeMillis(); // 设置为当前时间戳
mParentId = 0; // 默认父ID为0
mSnippet = ""; // 默认笔记摘录为空字符串
mType = Notes.TYPE_NOTE; // 默认笔记类型为普通笔记
mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; // 默认小部件ID无效
mWidgetType = Notes.TYPE_WIDGET_INVALIDE; // 默认小部件类型无效
mOriginParent = 0; // 默认原始父ID为0
mVersion = 0; // 默认版本为0
mDiffNoteValues = new ContentValues(); // 用于存储笔记差异的ContentValues
mDataList = new ArrayList<SqlData>(); // 初始化数据列表,用于存储与笔记相关的数据
}
// 构造函数用于从数据库中的Cursor加载现有笔记对象
public SqlNote(Context context, Cursor c) {
// 初始化上下文和内容解析器
mContext = context;
mContentResolver = context.getContentResolver();
// 标记这是一个已存在的笔记
mIsCreate = false;
// 从Cursor中加载数据
loadFromCursor(c);
// 初始化数据列表
mDataList = new ArrayList<SqlData>();
// 如果是普通笔记,加载相关的数据内容
if (mType == Notes.TYPE_NOTE) {
loadDataContent();
}
// 初始化差异数据的ContentValues
mDiffNoteValues = new ContentValues();
}
// 构造函数用于根据笔记ID从数据库加载笔记数据
public SqlNote(Context context, long id) {
// 初始化上下文和内容解析器
mContext = context;
mContentResolver = context.getContentResolver();
// 标记这是一个已存在的笔记
mIsCreate = false;
// 根据ID加载数据
loadFromCursor(id);
// 初始化数据列表
mDataList = new ArrayList<SqlData>();
// 如果是普通笔记,加载相关的数据内容
if (mType == Notes.TYPE_NOTE) {
loadDataContent();
}
// 初始化差异数据的ContentValues
mDiffNoteValues = new ContentValues();
}
// 私有方法根据笔记ID加载数据执行数据库查询返回指定笔记的Cursor
private void loadFromCursor(long id) {
Cursor c = null;
try {
// 通过ID查询笔记数据
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
new String[] {
String.valueOf(id)
}, null);
// 如果查询结果不为空移动Cursor到下一行并加载数据
if (c != null) {
c.moveToNext();
loadFromCursor(c); // 从Cursor加载数据到当前对象
} else {
Log.w(TAG, "loadFromCursor: cursor = null"); // 如果Cursor为空打印警告
}
} finally {
// 查询完成后关闭Cursor
if (c != null)
c.close();
}
}
// 私有方法从Cursor中加载数据将数据映射到当前对象的成员变量
private void loadFromCursor(Cursor c) {
// 根据列的索引从Cursor中获取数据并赋值给相应的成员变量
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);
mParentId = c.getLong(PARENT_ID_COLUMN);
mSnippet = c.getString(SNIPPET_COLUMN);
mType = c.getInt(TYPE_COLUMN);
mWidgetId = c.getInt(WIDGET_ID_COLUMN);
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
mVersion = c.getLong(VERSION_COLUMN);
}
// 私有方法:加载与笔记相关的数据内容,查询笔记的附加数据并存储在 mDataList 中
private void loadDataContent() {
Cursor c = null;
mDataList.clear(); // 清空当前数据列表
try {
// 根据笔记ID查询相关的数据内容
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[] {
String.valueOf(mId)
}, null);
// 如果查询结果不为空,处理数据
if (c != null) {
if (c.getCount() == 0) {
Log.w(TAG, "it seems that the note has not data"); // 如果没有数据,打印警告
return;
}
// 遍历查询结果,将每一条数据加入到数据列表
while (c.moveToNext()) {
SqlData data = new SqlData(mContext, c);
mDataList.add(data);
}
} else {
Log.w(TAG, "loadDataContent: cursor = null"); // 如果Cursor为空打印警告
}
} finally {
// 查询完成后关闭Cursor
if (c != null)
c.close();
}
}
// 设置笔记内容的方法
public boolean setContent(JSONObject js) {
try {
// 从传入的JSONObject中获取笔记对象
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
// 检查笔记类型是否为系统笔记,如果是,则打印警告日志
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder");
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 如果是文件夹类型,只能更新摘要和类型
// 更新摘要
String snippet = note.has(NoteColumns.SNIPPET) ? note
.getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
mSnippet = snippet;
// 更新类型
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
: Notes.TYPE_NOTE;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
// 如果是普通笔记类型,更新笔记的各种属性
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
// 更新ID
if (mIsCreate || mId != id) {
mDiffNoteValues.put(NoteColumns.ID, id);
}
mId = id;
// 更新提醒日期
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
.getLong(NoteColumns.ALERTED_DATE) : 0;
if (mIsCreate || mAlertDate != alertDate) {
mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
}
mAlertDate = alertDate;
// 更新背景颜色ID
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
if (mIsCreate || mBgColorId != bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
mBgColorId = bgColorId;
// 更新创建日期
long createDate = note.has(NoteColumns.CREATED_DATE) ? note
.getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
if (mIsCreate || mCreatedDate != createDate) {
mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
}
mCreatedDate = createDate;
// 更新是否有附件
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note
.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
if (mIsCreate || mHasAttachment != hasAttachment) {
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
}
mHasAttachment = hasAttachment;
// 更新修改日期
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note
.getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
if (mIsCreate || mModifiedDate != modifiedDate) {
mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
}
mModifiedDate = modifiedDate;
// 更新父ID
long parentId = note.has(NoteColumns.PARENT_ID) ? note
.getLong(NoteColumns.PARENT_ID) : 0;
if (mIsCreate || mParentId != parentId) {
mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
}
mParentId = parentId;
// 更新摘要
String snippet = note.has(NoteColumns.SNIPPET) ? note
.getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
mSnippet = snippet;
// 更新类型
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
: Notes.TYPE_NOTE;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
// 更新小部件ID
int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID)
: AppWidgetManager.INVALID_APPWIDGET_ID;
if (mIsCreate || mWidgetId != widgetId) {
mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);
}
mWidgetId = widgetId;
// 更新小部件类型
int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note
.getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;
if (mIsCreate || mWidgetType != widgetType) {
mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);
}
mWidgetType = widgetType;
// 更新原始父ID
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
if (mIsCreate || mOriginParent != originParent) {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
mOriginParent = originParent;
// 更新数据内容
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
for (SqlData temp : mDataList) {
if (dataId == temp.getId()) {
sqlData = temp;
}
}
}
if (sqlData == null) {
sqlData = new SqlData(mContext);
mDataList.add(sqlData);
}
sqlData.setContent(data);
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
}
return true;
}
// 获取笔记内容的方法
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
// 如果是普通笔记类型构建包含所有属性的JSONObject
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
note.put(NoteColumns.CREATED_DATE, mCreatedDate);
note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
note.put(NoteColumns.PARENT_ID, mParentId);
note.put(NoteColumns.SNIPPET, mSnippet);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.WIDGET_ID, mWidgetId);
note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
if (data != null) {
dataArray.put(data);
}
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
// 如果是文件夹或系统笔记类型构建包含ID、类型和摘要的JSONObject
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
}
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return null;
}
// 设置笔记的父ID
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
// 设置笔记的GTask ID
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
// 设置笔记的同步ID
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
// 重置本地修改标记
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
// 获取笔记的ID
public long getId() {
return mId;
}
// 获取笔记的父ID
public long getParentId() {
return mParentId;
}
// 获取笔记的摘要
public String getSnippet() {
return mSnippet;
}
// 检查笔记是否为普通笔记类型
public boolean isNoteType() {
return mType == Notes.TYPE_NOTE;
}
// 提交笔记数据,包括创建和更新
public void commit(boolean validateVersion) {
if (mIsCreate) {
// 如果是创建笔记插入数据到数据库并获取新创建的笔记ID
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
mDiffNoteValues.remove(NoteColumns.ID);
}
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
mId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
if (mId == 0) {
throw new IllegalStateException("Create thread id failed");
}
if (mType == Notes.TYPE_NOTE) {
// 如果是普通笔记类型,提交关联的数据内容
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
}
}
} else {
// 如果是更新笔记检查ID是否有效
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
}
if (mDiffNoteValues.size() > 0) {
mVersion ++;
// 更新数据库中的笔记数据
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
if (mType == Notes.TYPE_NOTE) {
// 如果是普通笔记类型,提交关联的数据内容
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
}
}
}
// 刷新本地信息
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues.clear();
mIsCreate = false;
}