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.
git.text/src/model/WorkingNote.java

764 lines
25 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.model;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
/**
* <p>工作笔记管理类,负责处理笔记的编辑、保存、加载等操作。</p>
* <p>设计意图作为Note类的包装类提供更便捷的笔记操作接口处理用户交互和数据同步。</p>
* <ul>
* <li>管理笔记的元数据和内容数据</li>
* <li>提供笔记的加载、保存、删除等功能</li>
* <li>处理笔记的设置变更(如背景颜色、提醒时间、清单模式)</li>
* <li>支持小组件交互和通知</li>
* </ul>
* <p>关键关联:</p>
* <ul>
* <li>与{@link Note}类紧密协作,管理笔记的核心数据</li>
* <li>通过{@link Context}与系统服务交互</li>
* <li>使用{@link NotesProvider}进行数据持久化</li>
* <li>通过{@link NoteSettingChangedListener}接口回调设置变更事件</li>
* </ul>
*/
public class WorkingNote {
/**
* <p>笔记的核心数据管理对象。</p>
* <p>业务含义:封装笔记的元数据和内容数据的变更管理。</p>
*/
private Note mNote;
/**
* <p>笔记的唯一标识符。</p>
* <p>业务含义:在数据库中唯一标识一条笔记记录。</p>
* <p>约束条件0表示笔记尚未保存到数据库。</p>
*/
private long mNoteId;
/**
* <p>笔记的内容文本。</p>
* <p>业务含义:笔记的主要内容数据。</p>
*/
private String mContent;
/**
* <p>笔记的模式。</p>
* <p>业务含义:表示笔记的显示模式,如普通文本模式或清单模式。</p>
*/
private int mMode;
/**
* <p>笔记的提醒时间。</p>
* <p>业务含义:笔记的闹钟提醒时间戳。</p>
* <p>约束条件0表示无提醒。</p>
*/
private long mAlertDate;
/**
* <p>笔记的最后修改时间。</p>
* <p>业务含义:记录笔记最后一次修改的时间戳。</p>
*/
private long mModifiedDate;
/**
* <p>笔记的背景颜色ID。</p>
* <p>业务含义:标识笔记使用的背景颜色资源。</p>
*/
private int mBgColorId;
/**
* <p>笔记关联的小组件ID。</p>
* <p>业务含义:与笔记关联的桌面小组件的唯一标识符。</p>
* <p>约束条件:{@link AppWidgetManager#INVALID_APPWIDGET_ID}表示无关联小组件。</p>
*/
private int mWidgetId;
/**
* <p>笔记关联的小组件类型。</p>
* <p>业务含义:标识与笔记关联的桌面小组件的类型。</p>
* <p>约束条件:{@link Notes#TYPE_WIDGET_INVALIDE}表示无关联小组件。</p>
*/
private int mWidgetType;
/**
* <p>笔记所在的文件夹ID。</p>
* <p>业务含义:标识笔记所属的文件夹。</p>
*/
private long mFolderId;
/**
* <p>上下文对象。</p>
* <p>业务含义:用于访问系统服务和资源。</p>
*/
private Context mContext;
/**
* <p>日志标签。</p>
* <p>业务含义用于在日志中标识WorkingNote类的相关操作。</p>
*/
private static final String TAG = "WorkingNote";
/**
* <p>笔记是否已删除的标记。</p>
* <p>业务含义:标识笔记是否已被标记为删除。</p>
*/
private boolean mIsDeleted;
/**
* <p>笔记设置变更监听器。</p>
* <p>业务含义:用于监听笔记设置的变更事件。</p>
*/
private NoteSettingChangedListener mNoteSettingStatusListener;
/**
* <p>笔记内容数据的查询投影。</p>
* <p>业务含义定义从Notes.CONTENT_DATA_URI查询时需要获取的列。</p>
*/
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
/**
* <p>笔记元数据的查询投影。</p>
* <p>业务含义定义从Notes.CONTENT_NOTE_URI查询时需要获取的列。</p>
*/
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
/**
* <p>DATA_PROJECTION中ID列的索引。</p>
*/
private static final int DATA_ID_COLUMN = 0;
/**
* <p>DATA_PROJECTION中CONTENT列的索引。</p>
*/
private static final int DATA_CONTENT_COLUMN = 1;
/**
* <p>DATA_PROJECTION中MIME_TYPE列的索引。</p>
*/
private static final int DATA_MIME_TYPE_COLUMN = 2;
/**
* <p>DATA_PROJECTION中DATA1列的索引用于存储模式信息。</p>
*/
private static final int DATA_MODE_COLUMN = 3;
/**
* <p>NOTE_PROJECTION中PARENT_ID列的索引。</p>
*/
private static final int NOTE_PARENT_ID_COLUMN = 0;
/**
* <p>NOTE_PROJECTION中ALERTED_DATE列的索引。</p>
*/
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
/**
* <p>NOTE_PROJECTION中BG_COLOR_ID列的索引。</p>
*/
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
/**
* <p>NOTE_PROJECTION中WIDGET_ID列的索引。</p>
*/
private static final int NOTE_WIDGET_ID_COLUMN = 3;
/**
* <p>NOTE_PROJECTION中WIDGET_TYPE列的索引。</p>
*/
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
/**
* <p>NOTE_PROJECTION中MODIFIED_DATE列的索引。</p>
*/
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
/**
* <p>创建新笔记的构造函数。</p>
* <p>功能:初始化一个新的工作笔记对象,设置默认值。</p>
*
* @param context 上下文对象,用于访问系统服务
* @param folderId 文件夹ID新笔记将被添加到该文件夹
*
* <p>默认初始化:</p>
* <ul>
* <li>提醒时间0无提醒</li>
* <li>修改时间:当前时间戳</li>
* <li>笔记ID0未保存</li>
* <li>删除标记false</li>
* <li>模式0普通文本模式</li>
* <li>小组件类型:无效</li>
* </ul>
*/
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mNote = new Note();
mNoteId = 0;
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
/**
* <p>加载现有笔记的构造函数。</p>
* <p>功能:初始化一个工作笔记对象,并从数据库加载现有笔记数据。</p>
*
* @param context 上下文对象,用于访问系统服务
* @param noteId 笔记ID要加载的现有笔记
* @param folderId 文件夹ID笔记所属的文件夹
*
* <p>加载流程:</p>
* <ul>
* <li>初始化基本字段</li>
* <li>调用{@link #loadNote()}加载笔记元数据</li>
* <li>调用{@link #loadNoteData()}加载笔记内容数据</li>
* </ul>
*/
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();
}
/**
* <p>加载笔记的元数据。</p>
* <p>功能:从数据库查询并加载笔记的元数据信息。</p>
*
* <p>加载流程:</p>
* <ol>
* <li>通过ContentResolver查询{@link Notes#CONTENT_NOTE_URI}获取笔记元数据</li>
* <li>如果查询成功,提取并保存元数据字段</li>
* <li>关闭Cursor</li>
* <li>如果查询失败,记录日志并抛出异常</li>
* <li>调用{@link #loadNoteData()}加载笔记内容数据</li>
* </ol>
*
* <p>加载的元数据字段:</p>
* <ul>
* <li>文件夹ID</li>
* <li>背景颜色ID</li>
* <li>小组件ID</li>
* <li>小组件类型</li>
* <li>提醒时间</li>
* <li>修改时间</li>
* </ul>
*
* <p>错误处理:</p>
* <ul>
* <li>如果查询失败,记录错误日志</li>
* <li>抛出IllegalArgumentException异常</li>
* </ul>
*/
private void loadNote() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData();
}
/**
* <p>加载笔记的内容数据。</p>
* <p>功能:从数据库查询并加载笔记的内容数据信息。</p>
*
* <p>加载流程:</p>
* <ol>
* <li>通过ContentResolver查询{@link Notes#CONTENT_DATA_URI}获取笔记内容数据</li>
* <li>如果查询成功,遍历所有结果</li>
* <li>根据数据类型处理不同类型的内容数据:</li>
* <ul>
* <li>普通笔记保存内容、模式、文本数据ID</li>
* <li>通话笔记保存通话数据ID</li>
* <li>其他类型:记录日志</li>
* </ul>
* <li>关闭Cursor</li>
* <li>如果查询失败,记录日志并抛出异常</li>
* </ol>
*
* <p>错误处理:</p>
* <ul>
* <li>如果查询失败,记录错误日志</li>
* <li>抛出IllegalArgumentException异常</li>
* </ul>
*/
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
/**
* <p>创建一个空的工作笔记。</p>
* <p>功能:创建一个新的工作笔记对象,并设置默认属性。</p>
*
* @param context 上下文对象,用于访问系统服务
* @param folderId 文件夹ID新笔记将被添加到该文件夹
* @param widgetId 小组件ID如果笔记关联到小组件
* @param widgetType 小组件类型,如果笔记关联到小组件
* @param defaultBgColorId 默认背景颜色ID
*
* @return 创建的工作笔记对象
*
* <p>初始化步骤:</p>
* <ol>
* <li>创建新的WorkingNote实例</li>
* <li>设置默认背景颜色</li>
* <li>设置小组件ID如果有</li>
* <li>设置小组件类型(如果有)</li>
* <li>返回创建的笔记对象</li>
* </ol>
*/
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
/**
* <p>从数据库加载现有笔记。</p>
* <p>功能根据笔记ID从数据库加载笔记数据并创建工作笔记对象。</p>
*
* @param context 上下文对象,用于访问系统服务
* @param id 笔记ID要加载的现有笔记
*
* @return 加载的工作笔记对象
*
* <p>加载流程:</p>
* <ul>
* <li>调用构造函数创建WorkingNote实例</li>
* <li>在构造函数中调用{@link #loadNote()}加载元数据</li>
* <li>在loadNote()中调用{@link #loadNoteData()}加载内容数据</li>
* </ul>
*/
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
/**
* <p>保存笔记到数据库。</p>
* <p>功能:将工作笔记的内容和设置保存到数据库中。</p>
*
* @return 保存是否成功
*
* <p>保存流程:</p>
* <ol>
* <li>检查笔记是否值得保存({@link #isWorthSaving()}</li>
* <li>如果笔记不存在于数据库:</li>
* <ul>
* <li>调用{@link Note#getNewNoteId(Context, long)}获取新笔记ID</li>
* <li>如果获取失败记录日志并返回false</li>
* </ul>
* <li>调用{@link Note#syncNote(Context, long)}同步笔记数据到数据库</li>
* <li>如果笔记关联到小组件,通知小组件更新</li>
* <li>返回保存结果</li>
* </ol>
*
* <p>线程安全:</p>
* <ul>
* <li>使用synchronized关键字保证多线程环境下的安全</li>
* </ul>
*/
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId);
/**
* Update widget content if there exist any widget of this note
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
/**
* <p>检查笔记是否存在于数据库中。</p>
* <p>功能根据笔记ID判断笔记是否已保存到数据库。</p>
*
* @return 如果笔记存在于数据库返回true否则返回false
*
* <p>判断逻辑:</p>
* <ul>
* <li>笔记ID大于0表示已保存到数据库</li>
* <li>笔记ID等于0表示未保存到数据库</li>
* </ul>
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
/**
* <p>检查笔记是否值得保存。</p>
* <p>功能:判断笔记是否需要保存到数据库。</p>
*
* @return 如果笔记值得保存返回true否则返回false
*
* <p>判断条件:</p>
* <ul>
* <li>如果笔记已标记为删除 → 不值得保存</li>
* <li>如果笔记不存在于数据库且内容为空 → 不值得保存</li>
* <li>如果笔记存在于数据库但没有本地修改 → 不值得保存</li>
* <li>其他情况 → 值得保存</li>
* </ul>
*
* <p>优化作用:</p>
* <ul>
* <li>避免保存空笔记或未修改的笔记</li>
* <li>减少不必要的数据库操作</li>
* </ul>
*/
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
/**
* <p>设置笔记设置变更监听器。</p>
* <p>功能:注册一个监听器,用于接收笔记设置变更的通知。</p>
*
* @param l 笔记设置变更监听器
*/
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
/**
* <p>设置笔记的提醒时间。</p>
* <p>功能:更新笔记的提醒时间,并通知监听器。</p>
*
* @param date 提醒时间戳
* @param set 是否设置提醒true表示设置false表示取消
*
* <p>处理流程:</p>
* <ol>
* <li>如果提醒时间发生变化:</li>
* <ul>
* <li>更新内部提醒时间字段</li>
* <li>调用{@link Note#setNoteValue(String, String)}更新笔记元数据</li>
* </ul>
* <li>通知监听器提醒时间已变更</li>
* </ol>
*/
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
/**
* <p>标记笔记是否已删除。</p>
* <p>功能:设置笔记的删除标记,并通知关联的小组件。</p>
*
* @param mark 删除标记true表示已删除false表示未删除
*
* <p>处理流程:</p>
* <ol>
* <li>更新内部删除标记字段</li>
* <li>如果笔记关联到小组件,通知小组件更新</li>
* </ol>
*/
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
/**
* <p>将笔记转换为通话笔记。</p>
* <p>功能:设置笔记为通话笔记类型,并添加通话相关信息。</p>
*
* @param phoneNumber 电话号码
* @param callDate 通话时间戳
*
* <p>转换流程:</p>
* <ol>
* <li>设置通话日期</li>
* <li>设置电话号码</li>
* <li>将笔记移动到通话记录文件夹</li>
* </ol>
*/
public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
/**
* <p>检查笔记是否有提醒。</p>
* <p>功能:判断笔记是否设置了提醒时间。</p>
*
* @return 如果有提醒返回true否则返回false
*
* <p>判断逻辑:</p>
* <ul>
* <li>提醒时间大于0表示有提醒</li>
* <li>提醒时间等于0表示无提醒</li>
* </ul>
*/
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
/**
* <p>获取笔记内容。</p>
* @return 笔记的内容文本
*/
public String getContent() {
return mContent;
}
/**
* <p>获取笔记的提醒时间。</p>
* @return 笔记的提醒时间戳
*/
public long getAlertDate() {
return mAlertDate;
}
/**
* <p>获取笔记的最后修改时间。</p>
* @return 笔记的最后修改时间戳
*/
public long getModifiedDate() {
return mModifiedDate;
}
/**
* <p>获取笔记背景颜色的资源ID。</p>
* @return 背景颜色的资源ID
*/
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
/**
* <p>获取笔记背景颜色的ID。</p>
* @return 背景颜色的ID
*/
public int getBgColorId() {
return mBgColorId;
}
/**
* <p>获取笔记标题背景的资源ID。</p>
* @return 标题背景的资源ID
*/
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
/**
* <p>获取笔记的清单模式。</p>
* @return 清单模式0表示普通模式1表示清单模式
*/
public int getCheckListMode() {
return mMode;
}
/**
* <p>获取笔记的ID。</p>
* @return 笔记的唯一标识符
*/
public long getNoteId() {
return mNoteId;
}
/**
* <p>获取笔记所属的文件夹ID。</p>
* @return 文件夹ID
*/
public long getFolderId() {
return mFolderId;
}
/**
* <p>获取笔记关联的小组件ID。</p>
* @return 小组件ID无效时返回{@link AppWidgetManager#INVALID_APPWIDGET_ID}
*/
public int getWidgetId() {
return mWidgetId;
}
/**
* <p>获取笔记关联的小组件类型。</p>
* @return 小组件类型(无效时返回{@link Notes#TYPE_WIDGET_INVALIDE}
*/
public int getWidgetType() {
return mWidgetType;
}
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
* Called when user set clock
*/
void onClockAlertChanged(long date, boolean set);
/**
* Call when user create note from widget
*/
void onWidgetChanged();
/**
* Call when switch between check list mode and normal mode
* @param oldMode is previous mode before change
* @param newMode is new mode
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
}