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.
qw1-notes/java/net/micode/notes/gtask/data/SqlNote.java

599 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;
/**
* 笔记数据模型类,用于表示数据库中的笔记记录
*
* 功能概述:
* 1. 封装笔记的数据库操作
* 2. 处理笔记与JSON数据的相互转换
* 3. 管理笔记内容和元数据
* 4. 支持与Google Tasks的同步
*
* 主要功能:
* - 从数据库加载笔记数据
* - 将笔记数据保存到数据库
* - 处理笔记内容的JSON序列化和反序列化
* - 管理笔记的版本控制和同步状态
*/
public class SqlNote {
private static final String TAG = SqlNote.class.getSimpleName();
// 无效ID常量
private static final int INVALID_ID = -99999;
// 笔记表查询列定义
public static final String[] PROJECTION_NOTE = new String[]{
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE,
NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID,
NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,
NoteColumns.VERSION
};
// 列索引常量
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; // 笔记ID
private long mAlertDate; // 提醒日期
private int mBgColorId; // 背景颜色ID
private long mCreatedDate; // 创建日期
private int mHasAttachment; // 是否有附件
private long mModifiedDate; // 修改日期
private long mParentId; // 父文件夹ID
private String mSnippet; // 内容摘要
private int mType; // 笔记类型
private int mWidgetId; // 小部件ID
private int mWidgetType; // 小部件类型
private long mOriginParent; // 原始父文件夹ID
private long mVersion; // 版本号
private ContentValues mDiffNoteValues; // 变更的值
private ArrayList<SqlData> mDataList; // 笔记内容数据列表
/**
* 构造函数 - 创建新笔记
*
* @param context 应用上下文
*/
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = true;
mId = INVALID_ID;
mAlertDate = 0;
mBgColorId = ResourceParser.getDefaultBgId(context);
mCreatedDate = System.currentTimeMillis();
mHasAttachment = 0;
mModifiedDate = System.currentTimeMillis();
mParentId = 0;
mSnippet = "";
mType = Notes.TYPE_NOTE;
mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
mOriginParent = 0;
mVersion = 0;
mDiffNoteValues = new ContentValues();
mDataList = new ArrayList<SqlData>();
}
/**
* 构造函数 - 从Cursor加载笔记
*
* @param context 应用上下文
* @param c 数据库Cursor
*/
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(c);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/**
* 构造函数 - 从ID加载笔记
*
* @param context 应用上下文
* @param id 笔记ID
*/
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(id);
mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/**
* 从Cursor加载笔记数据
*
* @param id 笔记ID
*/
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);
if (c != null) {
c.moveToNext();
loadFromCursor(c);
} else {
Log.w(TAG, "loadFromCursor: cursor = null");
}
} finally {
if (c != null)
c.close();
}
}
/**
* 从Cursor加载笔记字段
*
* @param c 数据库Cursor
*/
private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);
mParentId = c.getLong(PARENT_ID_COLUMN);
mSnippet = c.getString(SNIPPET_COLUMN);
mType = c.getInt(TYPE_COLUMN);
mWidgetId = c.getInt(WIDGET_ID_COLUMN);
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
mVersion = c.getLong(VERSION_COLUMN);
}
/**
* 加载笔记内容数据
*/
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
try {
// 查询笔记关联的内容数据
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[]{String.valueOf(mId)}, null);
if (c != null) {
if (c.getCount() == 0) {
Log.w(TAG, "it seems that the note has not data");
return;
}
while (c.moveToNext()) {
SqlData data = new SqlData(mContext, c);
mDataList.add(data);
}
} else {
Log.w(TAG, "loadDataContent: cursor = null");
}
} finally {
if (c != null)
c.close();
}
}
/**
* 从JSON对象设置笔记内容
*
* @param js JSON对象
* @return 是否设置成功
*/
public boolean setContent(JSONObject js) {
try {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
// 处理不同类型的笔记
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder");
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 处理文件夹类型
String snippet = note.has(NoteColumns.SNIPPET) ? note.getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
mSnippet = snippet;
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
// 处理普通笔记类型
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
// 设置笔记基本属性
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
if (mIsCreate || mId != id) {
mDiffNoteValues.put(NoteColumns.ID, id);
}
mId = id;
// 设置提醒日期
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note.getLong(NoteColumns.ALERTED_DATE) : 0;
if (mIsCreate || mAlertDate != alertDate) {
mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
}
mAlertDate = alertDate;
// 设置背景颜色
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note.getInt(NoteColumns.BG_COLOR_ID)
: ResourceParser.getDefaultBgId(mContext);
if (mIsCreate || mBgColorId != bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
mBgColorId = bgColorId;
// 设置创建日期
long createDate = note.has(NoteColumns.CREATED_DATE) ? note.getLong(NoteColumns.CREATED_DATE)
: System.currentTimeMillis();
if (mIsCreate || mCreatedDate != createDate) {
mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
}
mCreatedDate = createDate;
// 设置附件标记
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
if (mIsCreate || mHasAttachment != hasAttachment) {
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
}
mHasAttachment = hasAttachment;
// 设置修改日期
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note.getLong(NoteColumns.MODIFIED_DATE)
: System.currentTimeMillis();
if (mIsCreate || mModifiedDate != modifiedDate) {
mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
}
mModifiedDate = modifiedDate;
// 设置父文件夹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;
}
/**
* 获取笔记内容的JSON表示
*
* @return JSON对象
*/
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
note.put(NoteColumns.CREATED_DATE, mCreatedDate);
note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
note.put(NoteColumns.PARENT_ID, mParentId);
note.put(NoteColumns.SNIPPET, mSnippet);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.WIDGET_ID, mWidgetId);
note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
if (data != null) {
dataArray.put(data);
}
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
}
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return null;
}
/**
* 设置笔记的父ID
*
* @param id 父笔记的ID
*/
public void setParentId(long id) {
mParentId = id;
// 将父ID的更改添加到差异值映射中用于后续提交到数据库
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
/**
* 设置Google任务ID
*
* @param gid Google任务的ID字符串
*/
public void setGtaskId(String gid) {
// 将Google任务ID的更改添加到差异值映射中
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
/**
* 设置同步ID
*
* @param syncId 同步ID
*/
public void setSyncId(long syncId) {
// 将同步ID的更改添加到差异值映射中
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
/**
* 重置本地修改标记
* 将LOCAL_MODIFIED字段设置为0表示没有本地修改
*/
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
/**
* 获取笔记ID
*
* @return 笔记的ID
*/
public long getId() {
return mId;
}
/**
* 获取父笔记ID
*
* @return 父笔记的ID
*/
public long getParentId() {
return mParentId;
}
/**
* 获取笔记摘要
*
* @return 笔记的摘要内容
*/
public String getSnippet() {
return mSnippet;
}
/**
* 判断是否为笔记类型
*
* @return 如果是笔记类型返回true否则返回false
*/
public boolean isNoteType() {
return mType == Notes.TYPE_NOTE;
}
/**
* 提交笔记更改到数据库
*
* @param validateVersion 是否验证版本true表示只在版本匹配时更新
*/
public void commit(boolean validateVersion) {
if (mIsCreate) {
// 创建新笔记的处理逻辑
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
// 如果是创建新笔记移除可能已存在的ID字段
mDiffNoteValues.remove(NoteColumns.ID);
}
// 插入新笔记到数据库
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
// 从返回的URI中获取新生成的笔记ID
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");
}
// 如果是笔记类型提交相关的SqlData
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
}
}
} else {
// 更新现有笔记的处理逻辑
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
}
if (mDiffNoteValues.size() > 0) {
// 有更改需要提交,增加版本号
mVersion++;
int result = 0;
if (!validateVersion) {
// 不验证版本,直接更新
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[]{
String.valueOf(mId)
});
} else {
// 验证版本,只在当前版本小于等于数据库版本时更新
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[]{
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
// 如果是笔记类型提交相关的SqlData
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;
}
}