/* * 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 + "分钟"; } }