diff --git a/doc/泛读分析和维护报告.docx b/doc/泛读分析和维护报告.docx index ef788e5..933e86f 100644 Binary files a/doc/泛读分析和维护报告.docx and b/doc/泛读分析和维护报告.docx differ diff --git a/src/main/java/net/micode/notes/ui/NoteItemData.java b/src/main/java/net/micode/notes/ui/NoteItemData.java index 0f5a878..ffea372 100644 --- a/src/main/java/net/micode/notes/ui/NoteItemData.java +++ b/src/main/java/net/micode/notes/ui/NoteItemData.java @@ -25,8 +25,15 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; - +/** + * @classname: NoteItemData + * @description:是用于存储数据的一个类 + * 主要通过光标完成数据的获取,并设置相应的标记和属性值 + * @date: 2024/1/4 11:31 + * @author: Xia Yanbo +*/ public class NoteItemData { + //设置属性值 static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.ALERTED_DATE, @@ -41,7 +48,7 @@ public class NoteItemData { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, }; - + //定义常量和对应的数据值 private static final int ID_COLUMN = 0; private static final int ALERTED_DATE_COLUMN = 1; private static final int BG_COLOR_ID_COLUMN = 2; @@ -54,7 +61,7 @@ public class NoteItemData { private static final int TYPE_COLUMN = 9; private static final int WIDGET_ID_COLUMN = 10; private static final int WIDGET_TYPE_COLUMN = 11; - + //声明变量 private long mId; private long mAlertDate; private int mBgColorId; @@ -70,12 +77,20 @@ public class NoteItemData { private String mName; private String mPhoneNumber; - private boolean mIsLastItem; - private boolean mIsFirstItem; - private boolean mIsOnlyOneItem; - private boolean mIsOneNoteFollowingFolder; - private boolean mIsMultiNotesFollowingFolder; - + private boolean mIsLastItem;//判断是否为最后一个对象 + private boolean mIsFirstItem;//判断是否为第一个对象 + private boolean mIsOnlyOneItem;//判断是否只有一个对象 + private boolean mIsOneNoteFollowingFolder;//包中有单个便签 + private boolean mIsMultiNotesFollowingFolder;//包中有复数个便签 + /** + * @classname: NoteItemData + * @methodname NoteItemData + * @description:构造函数,构造便签数据存储对象,主要通过光标获取数据 + * @date: 2024/1/4 11:10 + * @author: Xia Yanbo + * @param:context上下文 + * @param:cursor光标 + */ public NoteItemData(Context context, Cursor cursor) { mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); @@ -91,49 +106,61 @@ public class NoteItemData { mType = cursor.getInt(TYPE_COLUMN); mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); - + //初始化电话号码的信息 mPhoneNumber = ""; + //获取电话号码的id if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) { + //mphonenumber里有符合字符串,则用contact功能连接 mName = Contact.getContact(context, mPhoneNumber); if (mName == null) { mName = mPhoneNumber; } } } - + //本身有名字用名字,没有用号码当名字 if (mName == null) { mName = ""; } + //检查光标的状态 checkPostion(cursor); } - + /** + * @classname: NoteItemData + * @methodname checkPostion + * @description:检查并设置光标的位置,记录标记值 + * @date: 2024/1/4 11:26 + * @author: Xia Yanbo + * @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) { + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {//若是note格式并且不是第一个元素 int position = cursor.getPosition(); - if (cursor.moveToPrevious()) { + if (cursor.moveToPrevious()) {//获取光标位置后看上一行 if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER - || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { + || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {//若光标满足系统或note格式 if (cursor.getCount() > (position + 1)) { - mIsMultiNotesFollowingFolder = true; + mIsMultiNotesFollowingFolder = true;//若是数据行数大于但前位置+1则设置成正确 } else { - mIsOneNoteFollowingFolder = true; + mIsOneNoteFollowingFolder = true;//否则单一文件夹标记为true } } - if (!cursor.moveToNext()) { + if (!cursor.moveToNext()) {//若不能再往下走则报错 throw new IllegalStateException("cursor move to previous but can't move back"); } } } } - + //直接从自身的属性值获取判断结果 public boolean isOneFollowingFolder() { return mIsOneNoteFollowingFolder; } @@ -145,7 +172,7 @@ public class NoteItemData { public boolean isLast() { return mIsLastItem; } - + //获取联系人的姓名 public String getCallName() { return mName; } @@ -205,15 +232,16 @@ public class NoteItemData { public int getWidgetId() { return mWidgetId; } - + //获取标题 public String getSnippet() { return mSnippet; } - + //判断是否设置了提醒 public boolean hasAlert() { return (mAlertDate > 0); } + //若数据父id为保存至文件夹模式的id且满足电话号码单元不为空,则isCallRecord为tru public boolean isCallRecord() { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } diff --git a/src/main/java/net/micode/notes/ui/NotesListAdapter.java b/src/main/java/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..38149b7 100644 --- a/src/main/java/net/micode/notes/ui/NotesListAdapter.java +++ b/src/main/java/net/micode/notes/ui/NotesListAdapter.java @@ -31,153 +31,212 @@ import java.util.HashSet; import java.util.Iterator; +// 定义一个名为NotesListAdapter的类,该类继承自CursorAdapter。CursorAdapter是Android中用于绑定数据库查询结果的适配器。 public class NotesListAdapter extends CursorAdapter { + // 定义一个静态常量TAG,用于日志记录 private static final String TAG = "NotesListAdapter"; + + // 定义一个私有变量mContext,用于存储上下文环境。 private Context mContext; + + // 定义一个私有变量mSelectedIndex,类型为HashMap,用于存储选中的项的索引和对应的选中状态。 private HashMap mSelectedIndex; + + // 定义一个私有变量mNotesCount,用于记录笔记的数量。 private int mNotesCount; + + // 定义一个私有变量mChoiceMode,用于表示是否处于选择模式。 private boolean mChoiceMode; + // 定义一个内部类AppWidgetAttribute,包含widgetId和widgetType两个属性。这个类看起来像是用于描述小部件的一些属性,但在这段代码中并未使用。 public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; + // 定义一个构造函数,接收一个Context对象作为参数,并初始化一些成员变量。 public NotesListAdapter(Context context) { - super(context, null); - mSelectedIndex = new HashMap(); - mContext = context; - mNotesCount = 0; + super(context, null); // 调用父类的构造函数,传入上下文和Cursor对象。这里Cursor对象为null,意味着这个适配器不直接与数据库交互。 + mSelectedIndex = new HashMap(); // 初始化mSelectedIndex为新的HashMap对象。 + mContext = context; // 将传入的上下文赋值给mContext。 + mNotesCount = 0; // 将笔记数量初始化为0。 } + // 重写父类的newView方法,该方法用于创建新的视图。这里返回一个新的NotesListItem对象。 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return new NotesListItem(context); + return new NotesListItem(context); // 返回一个新的NotesListItem对象。 } + // 重写父类的bindView方法,该方法用于绑定数据到视图上。如果视图是NotesListItem的实例,则从Cursor中获取数据并绑定到视图中。 @Override public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof NotesListItem) { - NoteItemData itemData = new NoteItemData(context, cursor); - ((NotesListItem) view).bind(context, itemData, mChoiceMode, - isSelectedItem(cursor.getPosition())); + if (view instanceof NotesListItem) { // 检查视图是否是NotesListItem的实例。 + NoteItemData itemData = new NoteItemData(context, cursor); // 从Cursor中获取数据并创建一个新的NoteItemData对象。 + ((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); // 将数据绑定到视图中。这里还传入了mChoiceMode和是否选中该项的状态。 } } + // 定义一个公共方法setCheckedItem,接收两个参数:position表示项的索引,checked表示是否选中该项。该方法将选中的状态存储到mSelectedIndex中,并通知数据集发生变化。 public void setCheckedItem(final int position, final boolean checked) { - mSelectedIndex.put(position, checked); - notifyDataSetChanged(); + mSelectedIndex.put(position, checked); // 将选中的状态存储到mSelectedIndex中。 + notifyDataSetChanged(); // 通知数据集发生变化,这会导致适配器绑定的列表重新绘制。 } + // 定义一个公共方法isInChoiceMode,返回mChoiceMode的值,表示是否处于选择模式。 public boolean isInChoiceMode() { return mChoiceMode; } + // 定义一个公共方法setChoiceMode,接收一个boolean类型的参数mode,清空已选中的项的集合,并将mChoiceMode设置为传入的mode值。 public void setChoiceMode(boolean mode) { - mSelectedIndex.clear(); - mChoiceMode = mode; + mSelectedIndex.clear(); // 清空已选中的项的集合。 + mChoiceMode = mode; // 将mChoiceMode设置为传入的mode值。 } + // 定义一个公共方法selectAll,接收一个boolean类型的参数checked。遍历列表中的所有项,如果某项的类型是笔记类型(TYPE_NOTE),则调用setCheckedItem方法设置其选中状态为checked。 public void selectAll(boolean checked) { - Cursor cursor = getCursor(); - for (int i = 0; i < getCount(); i++) { - if (cursor.moveToPosition(i)) { - if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { - setCheckedItem(i, checked); + Cursor cursor = getCursor(); // 获取当前列表的Cursor对象。 + for (int i = 0; i < getCount(); i++) { // 遍历列表中的所有项。 + if (cursor.moveToPosition(i)) { // 如果Cursor对象可以移动到当前位置。 + if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { // 如果当前项的类型是笔记类型。 + setCheckedItem(i, checked); // 则调用setCheckedItem方法设置其选中状态为checked。 } } } } + // 定义一个公共方法getSelectedItemIds,返回一个HashSet类型的对象,包含所有已选中项的ID。遍历已选中的项的集合,如果某项的选中状态为true,则获取其ID并添加到itemSet中。如果ID等于根文件夹的ID(ID_ROOT_FOLDER),则记录一条错误日志,否则将ID添加到itemSet中。 public HashSet getSelectedItemIds() { - HashSet itemSet = new HashSet(); - for (Integer position : mSelectedIndex.keySet()) { - if (mSelectedIndex.get(position) == true) { - Long id = getItemId(position); - if (id == Notes.ID_ROOT_FOLDER) { - Log.d(TAG, "Wrong item id, should not happen"); - } else { - itemSet.add(id); + HashSet itemSet = new HashSet(); // 创建一个HashSet对象用于存储已选中项的ID。 + for (Integer position : mSelectedIndex.keySet()) { // 遍历已选中的项的集合。 + if (mSelectedIndex.get(position) == true) { // 如果某项的选中状态为true。 + Long id = getItemId(position); // 获取该项的ID。 + if (id == Notes.ID_ROOT_FOLDER) { // 如果ID等于根文件夹的ID。 + Log.d(TAG, "Wrong item id, should not happen"); // 则记录一条错误日志,提示发生了不应该发生的情况。 + } else { // 否则。 + itemSet.add(id); // 将ID添加到itemSet中。 } } } - - return itemSet; + return itemSet; // 返回已选中项的ID集合。 } + /** + * 获取已选中的AppWidgetAttribute集合 + * + * @return 返回一个HashSet,包含了所有选中的AppWidgetAttribute对象 + */ public HashSet getSelectedWidget() { + // 创建一个新的HashSet来存放选中的AppWidgetAttribute对象 HashSet itemSet = new HashSet(); + + // 遍历已选中的项的键值(即位置) for (Integer position : mSelectedIndex.keySet()) { + // 如果该位置的项被选中(值为true) if (mSelectedIndex.get(position) == true) { + // 获取该位置的Cursor对象 Cursor c = (Cursor) getItem(position); + + // 如果Cursor不为空 if (c != null) { + // 创建一个新的AppWidgetAttribute对象 AppWidgetAttribute widget = new AppWidgetAttribute(); + + // 使用给定的上下文和Cursor创建一个NoteItemData对象 NoteItemData item = new NoteItemData(mContext, c); + + // 从NoteItemData对象中获取widget的ID和类型,并设置到AppWidgetAttribute对象中 widget.widgetId = item.getWidgetId(); widget.widgetType = item.getWidgetType(); + + // 将创建的AppWidgetAttribute对象添加到itemSet集合中 itemSet.add(widget); + /** * Don't close cursor here, only the adapter could close it + * 这段注释表示这里不应该关闭Cursor,只有适配器才能关闭它。这是为了避免在非适当的时间或地方关闭Cursor,可能导致资源泄漏或其他问题。 */ } else { + // 如果Cursor为空,记录错误日志并返回null。这表示获取Cursor失败。 Log.e(TAG, "Invalid cursor"); return null; } } } + // 返回包含所有选中的AppWidgetAttribute对象的HashSet。 return itemSet; } + // 获取被选中的项的数量 public int getSelectedCount() { + // 从mSelectedIndex集合中获取所有的值 Collection values = mSelectedIndex.values(); + // 如果values为null,则返回0,表示没有选中的项 if (null == values) { return 0; } + // 创建一个迭代器来遍历values集合 Iterator iter = values.iterator(); + // 初始化一个计数器为0,用于计算被选中的项的数量 int count = 0; + // 使用迭代器遍历values集合 while (iter.hasNext()) { + // 检查当前项是否为true。如果是,则表示该项被选中 if (true == iter.next()) { + // 如果当前项被选中,则计数器增加1 count++; } } + // 返回被选中的项的数量 return count; } + // 检查是否所有项都被选中 public boolean isAllSelected() { + // 获取被选中的项的数量 int checkedCount = getSelectedCount(); + // 如果被选中的项的数量不为0且等于总项数,则返回true,否则返回false return (checkedCount != 0 && checkedCount == mNotesCount); } + // 检查指定位置的项是否被选中 public boolean isSelectedItem(final int position) { + // 检查指定位置的项是否为null。如果是,则返回false,表示该项没有被选中 if (null == mSelectedIndex.get(position)) { return false; + } else { + // 如果上一步为false,则继续检查该项的值。如果该值为true,则表示该项被选中;如果为false,则表示未被选中。 + return mSelectedIndex.get(position); } - return mSelectedIndex.get(position); } + // 当内容发生变化时调用此方法。它调用了calcNotesCount()方法来重新计算笔记数量。 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } + // 当游标发生变化时调用此方法。它调用了calcNotesCount()方法来重新计算笔记数量。 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } + // 计算笔记数量。遍历集合,对每个项目检查其类型,如果类型为Notes.TYPE_NOTE,则笔记数量加1。如果出现无效的游标,会记录错误日志并立即返回。 private void calcNotesCount() { - mNotesCount = 0; - for (int i = 0; i < getCount(); i++) { - Cursor c = (Cursor) getItem(i); - if (c != null) { - if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { - mNotesCount++; + mNotesCount = 0; // 初始化笔记数量为0 + for (int i = 0; i < getCount(); i++) { // 遍历集合中的每个项目 + Cursor c = (Cursor) getItem(i); // 获取当前项目的游标 + if (c != null) { // 如果游标不为null + if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { // 检查当前项目的类型是否为Notes.TYPE_NOTE + mNotesCount++; // 如果类型匹配,笔记数量加1 } - } else { - Log.e(TAG, "Invalid cursor"); - return; + } else { // 如果游标为null,记录错误日志并立即返回。 + Log.e(TAG, "Invalid cursor"); // 记录错误日志:无效的游标 + return; // 立即返回,不再继续执行后续代码。 } } } diff --git a/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/main/java/net/micode/notes/ui/NotesListItem.java index 1221e80..d8255f9 100644 --- a/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -30,7 +30,9 @@ import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; +// 定义一个继承自LinearLayout的NotesListItem类 public class NotesListItem extends LinearLayout { + // 声明私有成员变量 private ImageView mAlert; private TextView mTitle; private TextView mTime; @@ -38,9 +40,12 @@ public class NotesListItem extends LinearLayout { private NoteItemData mItemData; private CheckBox mCheckBox; + // 构造函数,接收一个Context参数,并调用父类的构造函数进行初始化 public NotesListItem(Context context) { super(context); + // 使用inflate方法从note_item布局文件中创建视图,并添加到当前LinearLayout中 inflate(context, R.layout.note_item, this); + // 通过ID查找对应的视图控件,并赋值给对应的私有变量 mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTitle = (TextView) findViewById(R.id.tv_title); mTime = (TextView) findViewById(R.id.tv_time); @@ -48,30 +53,44 @@ public class NotesListItem extends LinearLayout { mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } + // 定义一个方法用于绑定数据和设置某些UI控件的状态 public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { + // 如果choiceMode为真且data的类型为NOTE,则显示复选框,并设置其选中状态 if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(checked); } else { + // 否则,隐藏复选框 mCheckBox.setVisibility(View.GONE); } + // 将传入的数据赋值给私有变量mItemData mItemData = data; + // 如果data的ID为CALL_RECORD_FOLDER,则执行以下操作: if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 隐藏mCallName文本视图 mCallName.setVisibility(View.GONE); + // 显示警告图标视图 mAlert.setVisibility(View.VISIBLE); + // 设置标题文本的样式为TextAppearancePrimaryItem样式 mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - mTitle.setText(context.getString(R.string.call_record_folder_name) - + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + // 设置标题文本的内容,包括固定的字符串call_record_folder_name和动态格式化的文件数量信息 + mTitle.setText(context.getString(R.string.call_record_folder_name) + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + // 设置警告图标的资源为call_record图标 mAlert.setImageResource(R.drawable.call_record); - } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { // 如果data的父ID为CALL_RECORD_FOLDER,则执行以下操作: + // 显示mCallName文本视图,并设置其文本内容为data的callName属性值 mCallName.setVisibility(View.VISIBLE); mCallName.setText(data.getCallName()); + // 设置标题文本的样式为TextAppearanceSecondaryItem样式 mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); + // 设置标题文本的内容为经过格式化处理的片段信息,使用DataUtils工具类的getFormattedSnippet方法进行格式化处理 mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + // 如果data具有警告信息,则执行以下操作: if (data.hasAlert()) { + // 设置警告图标的资源为clock图标,并显示该视图 mAlert.setImageResource(R.drawable.clock); - mAlert.setVisibility(View.VISIBLE); + mAlert.setVisibility(View.VISIBLE); // 显示警告图标视图 } else { mAlert.setVisibility(View.GONE); } @@ -82,7 +101,7 @@ public class NotesListItem extends LinearLayout { if (data.getType() == Notes.TYPE_FOLDER) { mTitle.setText(data.getSnippet() + context.getString(R.string.format_folder_files_count, - data.getNotesCount())); + data.getNotesCount())); mAlert.setVisibility(View.GONE); } else { mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); @@ -99,24 +118,40 @@ public class NotesListItem extends LinearLayout { setBackground(data); } + // 这是一个私有方法,名为setBackground,它接受一个NoteItemData类型的参数data。这个方法主要用于设置背景资源。 private void setBackground(NoteItemData data) { + // 从传入的data对象中获取背景颜色ID int id = data.getBgColorId(); + + // 检查data的类型是否为笔记类型 if (data.getType() == Notes.TYPE_NOTE) { + // 如果data是单条笔记或者是某个文件夹下的第一条笔记 if (data.isSingle() || data.isOneFollowingFolder()) { + // 调用NoteItemBgResources类的getNoteBgSingleRes方法,传入id作为参数,获取对应的单条笔记背景资源,并设置为背景 setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); } else if (data.isLast()) { + // 如果data是最后一条笔记 + // 调用NoteItemBgResources类的getNoteBgLastRes方法,传入id作为参数,获取对应的最后一条笔记背景资源,并设置为背景 setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); } else if (data.isFirst() || data.isMultiFollowingFolder()) { + // 如果data是第一条笔记或者是某个文件夹下的多条笔记中的第一条 + // 调用NoteItemBgResources类的getNoteBgFirstRes方法,传入id作为参数,获取对应的首条笔记背景资源,并设置为背景 setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); } else { + // 如果以上条件都不满足,说明data是一条普通的笔记 + // 调用NoteItemBgResources类的getNoteBgNormalRes方法,传入id作为参数,获取对应的普通笔记背景资源,并设置为背景 setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } } else { + // 如果data的类型不是笔记类型,那么它应该是一个文件夹 + // 调用NoteItemBgResources类的getFolderBgRes方法,获取文件夹的背景资源,并设置为背景 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } + // 这是一个公共方法,名为getItemData,它没有参数,返回值类型为NoteItemData。这个方法用于获取mItemData对象。 public NoteItemData getItemData() { + // 返回mItemData对象 return mItemData; } } diff --git a/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..6f8a89f 100644 --- a/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java @@ -1,19 +1,3 @@ -/* - * 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.accounts.Account; @@ -47,66 +31,92 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; - +/* + *该类功能:NotesPreferenceActivity,在小米便签中主要实现的是对背景颜色和字体大小的数据储存。 + * 继承了PreferenceActivity主要功能为对系统信息和配置进行自动保存的Activity + */ public class NotesPreferenceActivity extends PreferenceActivity { public static final String PREFERENCE_NAME = "notes_preferences"; - + //优先名 public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - + //同步账号 public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - + //同步时间 public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - + //同步密码 private static final String AUTHORITIES_FILTER_KEY = "authorities"; - + //本地密码 private PreferenceCategory mAccountCategory; - + //账户分组 private GTaskReceiver mReceiver; - + //同步任务接收器 private Account[] mOriAccounts; - + //账户 private boolean mHasAddedAccount; + //账户的hash标记 @Override + /* + *函数功能:创建一个activity,在函数里要完成所有的正常静态设置 + *参数:Bundle icicle:存放了 activity 当前的状态 + *函数实现:如下注释 + */ protected void onCreate(Bundle icicle) { + //先执行父类的创建函数 super.onCreate(icicle); /* using the app icon for navigation */ getActionBar().setDisplayHomeAsUpEnabled(true); + //给左上角图标的左边加上一个返回的图标 addPreferencesFromResource(R.xml.preferences); + //添加xml来源并显示 xml mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); + //根据同步账户关键码来初始化分组 mReceiver = new GTaskReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); registerReceiver(mReceiver, filter); + //初始化同步组件 mOriAccounts = null; View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); + //获取listvivew,ListView的作用:用于列出所有选择 getListView().addHeaderView(header, null, true); + //在listview组件上方添加其他组件 } @Override + /* + * 函数功能:activity交互功能的实现,用于接受用户的输入 + * 函数实现:如下注释 + */ protected void onResume() { + //先执行父类 的交互实现 super.onResume(); // need to set sync account automatically if user has added a new // account if (mHasAddedAccount) { + //若用户新加了账户则自动设置同步账户 Account[] accounts = getGoogleAccounts(); + //获取google同步账户 if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + //若原账户不为空且当前账户有增加 for (Account accountNew : accounts) { boolean found = false; for (Account accountOld : mOriAccounts) { if (TextUtils.equals(accountOld.name, accountNew.name)) { + //更新账户 found = true; break; } } if (!found) { setSyncAccount(accountNew.name); + //若是没有找到旧的账户,那么同步账号中就只添加新账户 break; } } @@ -114,58 +124,83 @@ public class NotesPreferenceActivity extends PreferenceActivity { } refreshUI(); + //刷新标签界面 } @Override + /* + * 函数功能:销毁一个activity + * 函数实现:如下注释 + */ protected void onDestroy() { if (mReceiver != null) { unregisterReceiver(mReceiver); + //注销接收器 } super.onDestroy(); + //执行父类的销毁动作 } + /* + * 函数功能:重新设置账户信息 + * 函数实现:如下注释 + */ private void loadAccountPreference() { mAccountCategory.removeAll(); - + //销毁所有的分组 Preference accountPref = new Preference(this); + //建立首选项 final String defaultAccount = getSyncAccountName(this); accountPref.setTitle(getString(R.string.preferences_account_title)); accountPref.setSummary(getString(R.string.preferences_account_summary)); + //设置首选项的大标题和小标题 accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { + //建立监听器 if (!GTaskSyncService.isSyncing()) { if (TextUtils.isEmpty(defaultAccount)) { // the first time to set account + //若是第一次建立账户显示选择账户提示对话框 showSelectAccountAlertDialog(); } else { // if the account has already been set, we need to promp // user about the risk + //若是已经建立则显示修改对话框并进行修改操作 showChangeAccountConfirmAlertDialog(); } } else { + //若在没有同步的情况下,则在toast中显示不能修改 Toast.makeText(NotesPreferenceActivity.this, - R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) .show(); } return true; } }); + //根据新建首选项编辑新的账户分组 mAccountCategory.addPreference(accountPref); } + /* + *函数功能:设置按键的状态和最后同步的时间 + *函数实现:如下注释 + */ private void loadSyncButton() { Button syncButton = (Button) findViewById(R.id.preference_sync_button); TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - + //获取同步按钮控件和最终同步时间的的窗口 // set button state + //设置按钮的状态 if (GTaskSyncService.isSyncing()) { + //若是在同步状态下 syncButton.setText(getString(R.string.preferences_button_sync_cancel)); syncButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { GTaskSyncService.cancelSync(NotesPreferenceActivity.this); } }); + //设置按钮显示的文本为“取消同步”以及监听器 } else { syncButton.setText(getString(R.string.preferences_button_sync_immediately)); syncButton.setOnClickListener(new View.OnClickListener() { @@ -173,50 +208,67 @@ public class NotesPreferenceActivity extends PreferenceActivity { GTaskSyncService.startSync(NotesPreferenceActivity.this); } }); + //若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器 } syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); + //设置按键可用还是不可用 // set last sync time + // 设置最终同步时间 if (GTaskSyncService.isSyncing()) { + //若是在同步的情况下 lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE); + // 根据当前同步服务器设置时间显示框的文本以及可见性 } else { + //若是非同步情况 long lastSyncTime = getLastSyncTime(this); if (lastSyncTime != 0) { lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, DateFormat.format(getString(R.string.preferences_last_sync_time_format), lastSyncTime))); lastSyncTimeView.setVisibility(View.VISIBLE); + //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性 } else { + //若时间为空直接设置为不可见状态 lastSyncTimeView.setVisibility(View.GONE); } } } - + /* + *函数功能:刷新标签界面 + *函数实现:调用上文设置账号和设置按键两个函数来实现 + */ private void refreshUI() { loadAccountPreference(); loadSyncButton(); } + /* + * 函数功能:显示账户选择的对话框并进行账户的设置 + * 函数实现:如下注释 + */ private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + //创建一个新的对话框 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - + //设置标题以及子标题的内容 dialogBuilder.setCustomTitle(titleView); dialogBuilder.setPositiveButton(null, null); - + //设置对话框的自定义标题,建立一个YES的按钮 Account[] accounts = getGoogleAccounts(); String defAccount = getSyncAccountName(this); - + //获取同步账户信息 mOriAccounts = accounts; mHasAddedAccount = false; if (accounts.length > 0) { + //若账户不为空 CharSequence[] items = new CharSequence[accounts.length]; final CharSequence[] itemMapping = items; int checkedItem = -1; @@ -224,83 +276,119 @@ public class NotesPreferenceActivity extends PreferenceActivity { for (Account account : accounts) { if (TextUtils.equals(account.name, defAccount)) { checkedItem = index; + //在账户列表中查询到所需账户 } items[index++] = account.name; } dialogBuilder.setSingleChoiceItems(items, checkedItem, + //在对话框建立一个单选的复选框 new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { setSyncAccount(itemMapping[which].toString()); dialog.dismiss(); + //取消对话框 refreshUI(); } + //设置点击后执行的事件,包括检录新同步账户和刷新标签界面 }); + //建立对话框网络版的监听器 } View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); dialogBuilder.setView(addAccountView); + //给新加账户对话框设置自定义样式 final AlertDialog dialog = dialogBuilder.show(); + //显示对话框 addAccountView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mHasAddedAccount = true; + //将新加账户的hash置true Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + //建立网络建立组件 intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { - "gmail-ls" + "gmail-ls" }); startActivityForResult(intent, -1); + //跳回上一个选项 dialog.dismiss(); } }); + //建立新加账户对话框的监听器 } + /* + * 函数功能:显示账户选择对话框和相关账户操作 + * 函数实现:如下注释 + */ private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - + //创建一个新的对话框 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, getSyncAccountName(this))); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); + //根据同步修改的账户信息设置标题以及子标题的内容 dialogBuilder.setCustomTitle(titleView); - + //设置对话框的自定义标题 CharSequence[] menuItemArray = new CharSequence[] { getString(R.string.preferences_menu_change_account), getString(R.string.preferences_menu_remove_account), getString(R.string.preferences_menu_cancel) }; + //定义一些标记字符串 dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel public void onClick(DialogInterface dialog, int which) { + //按键功能,由which来决定 if (which == 0) { + //进入账户选择对话框 showSelectAccountAlertDialog(); } else if (which == 1) { + //删除账户并且跟新便签界面 removeSyncAccount(); refreshUI(); } } }); dialogBuilder.show(); + //显示对话框 } + /* + *函数功能:获取谷歌账户 + *函数实现:通过账户管理器直接获取 + */ private Account[] getGoogleAccounts() { AccountManager accountManager = AccountManager.get(this); return accountManager.getAccountsByType("com.google"); } + /* + * 函数功能:设置同步账户 + * 函数实现:如下注释: + */ private void setSyncAccount(String account) { if (!getSyncAccountName(this).equals(account)) { + //假如该账号不在同步账号列表中 SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); + //编辑共享的首选项 if (account != null) { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); } else { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + //将该账号加入到首选项中 + editor.commit(); + //提交修改的数据 + - // clean up last sync time setLastSyncTime(this, 0); + //将最后同步时间清零 // clean up local gtask related info new Thread(new Runnable() { @@ -311,23 +399,33 @@ public class NotesPreferenceActivity extends PreferenceActivity { getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).start(); + //重置当地同步任务的信息 Toast.makeText(NotesPreferenceActivity.this, getString(R.string.preferences_toast_success_set_accout, account), Toast.LENGTH_SHORT).show(); + //将toast的文本信息置为“设置账户成功”并显示出来 } } - + /* + * 函数功能:删除同步账户 + * 函数实现:如下注释: + */ private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); + //设置共享首选项 + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); + //假如当前首选项中有账户就删除 } if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { editor.remove(PREFERENCE_LAST_SYNC_TIME); + //删除当前首选项中有账户时间 } editor.commit(); + //提交更新后的数据 // clean up local gtask related info new Thread(new Runnable() { @@ -338,51 +436,79 @@ public class NotesPreferenceActivity extends PreferenceActivity { getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).start(); + //重置当地同步任务的信息 } + /* + * 函数功能:获取同步账户名称 + * 函数实现:通过共享的首选项里的信息直接获取 + */ public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + /* + * 函数功能:设置最终同步的时间 + * 函数实现:如下注释 + */ public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); + // 从共享首选项中找到相关账户并获取其编辑器 editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); editor.commit(); + //编辑最终同步时间并提交更新 } - + /* + * 函数功能:获取最终同步时间 + * 函数实现:通过共享的首选项里的信息直接获取 + */ public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } + /* + * 函数功能:接受同步信息 + * 函数实现:继承BroadcastReceiver + */ private class GTaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { refreshUI(); if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + //获取随广播而来的Intent中的同步服务的数据 TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); + //通过获取的数据在设置系统的状态 } } } + /* + * 函数功能:处理菜单的选项 + * 函数实现:如下注释 + * 参数:MenuItem菜单选项 + */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + //根据选项的id选择,这里只有一个主页 case android.R.id.home: Intent intent = new Intent(this, NotesListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; + //在主页情况下在创建连接组件intent,发出清空的信号并开始一个相应的activity default: return false; } } } + \ No newline at end of file