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.
XiaoMiNotes/src/ui/NoteItemData.java

477 lines
16 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.ui;
import android.content.Context;
import android.database.Cursor;
import android.text.Html;
import android.text.TextUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
/**
* 笔记项数据封装类
* 用于封装从数据库查询得到的笔记数据,提供便捷的数据访问方法
* 在笔记列表等界面中作为数据项使用
*/
public class NoteItemData {
// 数据库查询列投影
static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 0: 笔记ID
NoteColumns.ALERTED_DATE, // 1: 提醒时间
NoteColumns.BG_COLOR_ID, // 2: 背景颜色ID
NoteColumns.CREATED_DATE, // 3: 创建时间
NoteColumns.HAS_ATTACHMENT, // 4: 是否有附件
NoteColumns.MODIFIED_DATE, // 5: 修改时间
NoteColumns.NOTES_COUNT, // 6: 笔记数量(针对文件夹)
NoteColumns.PARENT_ID, // 7: 父文件夹ID
NoteColumns.SNIPPET, // 8: 内容摘要
NoteColumns.TITLE, // 9: 笔记标题
NoteColumns.TYPE, // 10: 类型(笔记/文件夹/系统文件夹)
NoteColumns.WIDGET_ID, // 11: 小部件ID
NoteColumns.WIDGET_TYPE, // 12: 小部件类型
NoteColumns.PINNED, // 13: 是否置顶
NoteColumns.PRIVATE, // 14: 是否为隐私文件夹
NoteColumns.IS_STUDY, // 15: 是否为学习文件夹
NoteColumns.FOCUS_DURATION, // 16: 专注时长(毫秒)
};
// 列索引常量
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TITLE_COLUMN = 9;
private static final int TYPE_COLUMN = 10;
private static final int WIDGET_ID_COLUMN = 11;
private static final int WIDGET_TYPE_COLUMN = 12;
private static final int PINNED_COLUMN = 13;
private static final int PRIVATE_COLUMN = 14;
private static final int IS_STUDY_COLUMN = 15;
private static final int FOCUS_DURATION_COLUMN = 16;
// 笔记数据字段
private long mId; // 笔记ID
private long mAlertDate; // 提醒时间
private int mBgColorId; // 背景颜色ID
private long mCreatedDate; // 创建时间
private boolean mHasAttachment; // 是否有附件
private long mModifiedDate; // 修改时间
private int mNotesCount; // 笔记数量(针对文件夹)
private long mParentId; // 父文件夹ID
private String mSnippet; // 内容摘要
private String mTitle; // 笔记标题
private int mType; // 类型
private int mWidgetId; // 小部件ID
private int mWidgetType; // 小部件类型
private boolean mIsPinned; // 是否置顶
private boolean mIsPrivate; // 是否为隐私文件夹
private boolean mIsStudy; // 是否为学习文件夹
private long mFocusDuration; // 专注时长(毫秒)
private String mName; // 联系人姓名(针对通话记录)
private String mPhoneNumber; // 电话号码(针对通话记录)
// 位置信息字段
private boolean mIsLastItem; // 是否是最后一项
private boolean mIsFirstItem; // 是否是第一项
private boolean mIsOnlyOneItem; // 是否是唯一一项
private boolean mIsOneNoteFollowingFolder; // 是否是紧跟在文件夹后的单个笔记
private boolean mIsMultiNotesFollowingFolder;// 是否是紧跟在文件夹后的多个笔记
/**
* 构造函数
* @param context 上下文
* @param cursor 数据库游标
*/
public NoteItemData(Context context, Cursor cursor) {
// 从游标读取数据
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
// 获取并处理snippet移除HTML标签和清单标记
mSnippet = cursor.getString(SNIPPET_COLUMN);
if (mSnippet != null) {
// 直接保存原始内容不进行HTML转换
// 因为我们需要保留内容的第一行作为标题而HTML转换可能会丢失结构信息
// 只移除清单标记
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
// 移除多余的空白字符
mSnippet = mSnippet.trim();
}
mTitle = cursor.getString(TITLE_COLUMN); // 读取标题字段,修复索引偏移问题
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mIsPinned = (cursor.getInt(PINNED_COLUMN) > 0) ? true : false;
mIsPrivate = (cursor.getInt(PRIVATE_COLUMN) > 0) ? true : false;
mIsStudy = (cursor.getInt(IS_STUDY_COLUMN) > 0) ? true : false;
mFocusDuration = cursor.getLong(FOCUS_DURATION_COLUMN);
// 动态计算文件夹的便签数量
if (mType == Notes.TYPE_FOLDER && mParentId == Notes.ID_TRASH_FOLER) {
// 只对回收站中的文件夹进行动态计算
// 查询ORIGIN_PARENT_ID等于当前文件夹ID的便签数量
// 这样可以在回收站中显示文件夹内的便签数量
Cursor countCursor = context.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
new String[]{"COUNT(*)"},
Notes.NoteColumns.ORIGIN_PARENT_ID + "=?",
new String[]{String.valueOf(mId)},
null);
if (countCursor != null) {
if (countCursor.moveToFirst()) {
mNotesCount = countCursor.getInt(0);
}
countCursor.close();
} else {
mNotesCount = 0;
}
} else {
// 对于其他文件夹使用原来的NOTES_COUNT字段
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
}
// 初始化通话记录相关字段
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
// 获取通话记录的电话号码
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
// 根据电话号码获取联系人姓名
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = "";
}
// 检查位置信息
checkPostion(cursor);
}
/**
* 检查当前项在列表中的位置
* @param cursor 数据库游标
*/
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
// 如果是笔记类型且不是第一项,检查前一项是否为文件夹
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
// 判断当前笔记后面是否还有更多笔记
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true;
}
}
// 移动回原来的位置
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
/**
* 检查是否是紧跟在文件夹后的单个笔记
*/
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
/**
* 检查是否是紧跟在文件夹后的多个笔记
*/
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
/**
* 检查是否是列表中的最后一项
*/
public boolean isLast() {
return mIsLastItem;
}
/**
* 获取通话记录的联系人姓名
*/
public String getCallName() {
return mName;
}
/**
* 检查是否是列表中的第一项
*/
public boolean isFirst() {
return mIsFirstItem;
}
/**
* 检查是否是列表中的唯一项
*/
public boolean isSingle() {
return mIsOnlyOneItem;
}
/**
* 获取笔记ID
*/
public long getId() {
return mId;
}
/**
* 获取提醒时间
*/
public long getAlertDate() {
return mAlertDate;
}
/**
* 获取创建时间
*/
public long getCreatedDate() {
return mCreatedDate;
}
/**
* 检查是否有附件
*/
public boolean hasAttachment() {
return mHasAttachment;
}
/**
* 获取修改时间
*/
public long getModifiedDate() {
return mModifiedDate;
}
/**
* 获取背景颜色ID
*/
public int getBgColorId() {
return mBgColorId;
}
/**
* 获取父文件夹ID
*/
public long getParentId() {
return mParentId;
}
/**
* 获取笔记数量(针对文件夹)
*/
public int getNotesCount() {
return mNotesCount;
}
/**
* 获取文件夹ID与getParentId相同
*/
public long getFolderId () {
return mParentId;
}
/**
* 获取类型
*/
public int getType() {
return mType;
}
/**
* 获取小部件类型
*/
public int getWidgetType() {
return mWidgetType;
}
/**
* 获取小部件ID
*/
public int getWidgetId() {
return mWidgetId;
}
/**
* 获取内容摘要
*/
public String getSnippet() {
return mSnippet;
}
/**
* 获取标题
*/
public String getTitle() {
return mTitle;
}
/**
* 获取显示标题:优先使用标题字段,无标题时使用内容摘要的第一行
*/
public String getDisplayTitle() {
if (!TextUtils.isEmpty(mTitle)) {
return mTitle;
}
// 没有标题时,返回内容摘要的第一行
if (!TextUtils.isEmpty(mSnippet)) {
// 临时存储原始snippet
String originalSnippet = mSnippet;
// 第一步移除HTML标签提取纯文本
String plainText = originalSnippet.replaceAll("<[^>]*>", "");
// 第二步移除HTML实体
plainText = plainText.replaceAll("&[^;]+;", "");
// 第三步:移除多余的空白字符
plainText = plainText.trim();
// 第四步如果处理后仍然为空尝试从原始HTML中提取有意义的内容
if (TextUtils.isEmpty(plainText)) {
// 尝试直接提取文本,不使用正则表达式
try {
// 使用Android内置的Html.fromHtml方法转换为纯文本
CharSequence fromHtmlText = Html.fromHtml(originalSnippet);
plainText = fromHtmlText.toString().trim();
} catch (Exception e) {
// 如果转换失败,使用原始文本
plainText = originalSnippet.replaceAll("<[^>]*>", "").trim();
}
}
// 第五步:如果仍然为空,返回默认标题
if (TextUtils.isEmpty(plainText)) {
return "新便签";
}
// 第六步:获取第一行作为标题
int newlineIndex = plainText.indexOf('\n');
if (newlineIndex > 0) {
return plainText.substring(0, newlineIndex);
} else {
// 如果没有换行返回前20个字符作为标题
if (plainText.length() > 20) {
return plainText.substring(0, 20) + "...";
} else {
return plainText;
}
}
}
return "新便签";
}
/**
* 检查是否有提醒
*/
public boolean hasAlert() {
return (mAlertDate > 0);
}
/**
* 检查是否是通话记录
*/
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
/**
* 从游标获取笔记类型
* @param cursor 数据库游标
* @return 笔记类型
*/
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
/**
* 检查笔记是否置顶
* @return 是否置顶
*/
public boolean isPinned() {
return mIsPinned;
}
/**
* 检查是否为隐私文件夹
* @return 是否为隐私文件夹
*/
public boolean isPrivate() {
return mIsPrivate;
}
/**
* 检查是否为学习文件夹
* @return 是否为学习文件夹
*/
public boolean isStudy() {
return mIsStudy;
}
/**
* 获取专注时长(毫秒)
* @return 专注时长
*/
public long getFocusDuration() {
return mFocusDuration;
}
/**
* 获取格式化的专注时长
* @return 格式化的专注时长,格式为"xx小时xx分钟"
*/
public String getFormattedFocusDuration() {
long totalMinutes = mFocusDuration / (1000 * 60);
long hours = totalMinutes / 60;
long minutes = totalMinutes % 60;
return hours > 0 ? hours + "小时" + minutes + "分钟" : minutes + "分钟";
}
}