Merge pull request 'model tool 代码标注' (#41) from zhangshiyi1 into main

pull/50/head
p67p8couq 2 years ago
commit d92a744b78

Binary file not shown.

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--版本号和解码方式-->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
@ -16,7 +16,16 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <selector> 是 Android 中的一个 XML 标签,用于定义在不同状态下(例如按下、选中等)如何显示视图。
一般用于按钮、标签等控件的背景、前景以及文本颜色等属性的设置。
android= 是 Android XML 文件中的命名空间声明,这里使用的andriod的官方配置-->
<item android:state_pressed="true" android:color="#88555555" />
<item android:state_selected="true" android:color="#ff999999" />
<item android:color="#ff000000" />
</selector>
</selector>
<!-- 这段代码是一个XML文件用于定义一个颜色选择器selector。它包含了三个颜色项
第一个 <item> 元素定义了当状态为 pressed按下时的颜色为 #88555555这是一个半透明的深灰色。
第二个 <item> 元素定义了当状态为 selected选中时的颜色为 #ff999999这是一个较浅的灰色。
第三个 <item> 元素定义了默认情况下(即没有特定状态匹配)的颜色为 #ff000000即纯黑色。-->

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
@ -17,4 +17,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#50000000" />
</selector>
</selector>
<!-- 引入半透明黑色-->

@ -13,6 +13,17 @@
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.
这段代码是一个 Android XML 文件它定义了一个状态选择器selector用于根据控件的状态来显示不同的背景图片。
在这个 XML 文件中,<selector> 元素的命名空间被声明为 xmlns:android="http://schemas.android.com/apk/res/android"。
<selector> 元素内部,包含了两个 <item> 元素。每个 <item> 元素定义了一个状态和对应的背景图片。
第一个 <item> 元素使用了 android:state_pressed="true" 属性,表示当控件被按下时的状态,并指定了对应的背景图片为 @drawable/new_note_pressed。
第二个 <item> 元素没有指定任何状态,表示默认状态,并指定了对应的背景图片为 @drawable/new_note_normal。
根据 Android 系统的状态选择器机制,当将这个选择器应用到一个控件上时,如果控件处于对应的状态,那么它将使用相应的背景图片来呈现。
此外,该文件还包含了版权声明和 Apache 许可证信息,表明该代码是受 Apache License, Version 2.0 许可的开源代码,使用时需要遵循许可证中的规定。
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)

@ -13,19 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//单个便签项提供了一个笔记模块的功能。包括管理和操作笔记的数据、插入、更新和查询笔记数据并将数据存储到ContentResolver中
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import android.content.ContentProviderOperation;//用于描述对内容提供者的单个操作,例如插入、更新或删除数据。
import android.content.ContentProviderResult;// 用于存储一组内容提供者操作的结果。
import android.content.ContentUris;//包含用于处理内容URI的实用方法例如从URI中提取ID等。
import android.content.ContentValues;// 用于存储键值对形式的数据,通常用于向内容提供者插入新数据或更新现有数据。
import android.content.Context;//用于访问应用程序全局的上下文信息,例如应用程序资源和类加载器等。
import android.content.OperationApplicationException;//当应用多个操作到内容提供者时,可能会抛出此异常,表示其中一个操作失败。
import android.net.Uri;//代表一个统一资源标识符,用于定位数据的位置,常用于访问内容提供者中的数据。
import android.os.RemoteException;//可能会在跨进程通信时抛出,表示远程过程调用发生错误。
import android.util.Log;//用于在Android系统中记录日志信息帮助开发者调试应用程序。
//上面为标准库下面为在notes.data包里的用法
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.NoteColumns;
@ -35,73 +35,79 @@ import java.util.ArrayList;
public class Note {
private ContentValues mNoteDiffValues;
private NoteData mNoteData;
private ContentValues mNoteDiffValues;//
private NoteData mNoteData;//note数据
private static final String TAG = "Note";
/**
* Create a new note id for adding a new note to databases
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// Create a new note in the database
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
values.put(NoteColumns.MODIFIED_DATE, createdTime);
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.PARENT_ID, folderId);
ContentValues values = new ContentValues();//新建values来保存信息
long createdTime = System.currentTimeMillis();//以当前时间为创建时间
values.put(NoteColumns.CREATED_DATE, createdTime);//创建时间
values.put(NoteColumns.MODIFIED_DATE, createdTime);//修改时间
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);//类型
values.put(NoteColumns.LOCAL_MODIFIED, 1);//本地修改标志
values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
//ContentResolver()主要是实现外部应用对ContentProvider中的数据
//进行添加、删除、修改和查询操作
long noteId = 0;
try {
noteId = Long.valueOf(uri.getPathSegments().get(1));
// uri.getPathSegments().get(1) 的作用是从 Uri 对象中获取路径的分段,并返回索引为 1 的分段内容。
// 在这段代码中,通过调用 uri.getPathSegments() 方法获取到一个 List<String> 对象,其中包含了 Uri 路径的各个分段内容。而后使用 get(1) 方法获取到索引为 1 的分段内容。
// 根据代码的上下文,可以推测索引为 1 的分段内容代表新生成的笔记的 ID 号。因此uri.getPathSegments().get(1) 的作用就是从 Uri 中解析出新生成的笔记的 ID 号,并将其作为一个长整型值返回给变量 noteId。
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
}//try-catch异常处理
if (noteId == -1) {
throw new IllegalStateException("Wrong note id:" + noteId);
}
return noteId;
return noteId;//生成的新笔记的 ID 号
}
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
}//定义两个变量用来存储便签的数据,一个是存储便签属性、一个是存储便签内容
public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value);
public void setNoteValue(String key, String value) {//接受两个参数key 和 value分别表示属性的键和值。
mNoteDiffValues.put(key, value);//将 key 和 value 存储到 mNoteDiffValues 对象中
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());//修改时间为当前时间
}//设置数据库表格的标签属性数据
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
//设置数据库表格的标签文本内容的数据
public void setTextDataId(long id) {
mNoteData.setTextDataId(id);
}
//设置文本数据的ID
public long getTextDataId() {
return mNoteData.mTextDataId;
}
//得到文本数据的ID
public void setCallDataId(long id) {
mNoteData.setCallDataId(id);
}
//设置电话号码数据的ID
public void setCallData(String key, String value) {
mNoteData.setCallData(key, value);
}
//得到电话号码数据的ID
public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
}//判断是否是本地修改
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
if (noteId <= 0) {//id<=0报错
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
@ -114,12 +120,12 @@ public class Note {
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
if (context.getContentResolver().update(
if (context.getContentResolver().update(//更新数据库中的笔记数据。
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
null) == 0) {//笔记的 Uri通过 ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId) 获取到特定 ID 的笔记的 Uri。
Log.e(TAG, "Update note error, should not happen");
// Do not return, fall through
}
}//如果更新操作返回的结果为 0表示没有进行更新操作则记录错误日志 "Update note error, should not happen",并且在注释中说明不要返回,而是继续执行后面的代码。
mNoteDiffValues.clear();
if (mNoteData.isLocalModified()
@ -128,32 +134,32 @@ public class Note {
}
return true;
}
}//判断是否同步
private class NoteData {
private class NoteData {//定义一个基本的便签内容的数据类,主要包含文本数据和电话号码数据
private long mTextDataId;
private ContentValues mTextDataValues;
private ContentValues mTextDataValues;//文本数据
private long mCallDataId;
private ContentValues mCallDataValues;
private ContentValues mCallDataValues;//电话号码数据
private static final String TAG = "NoteData";
public NoteData() {
public NoteData() {//初始化NoteData
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
// 函数实现部分
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
}//判断是否本地修改,即是否有新电话号码数据或文本数据
void setTextDataId(long id) {
if(id <= 0) {
if(id <= 0) {//id<=0报错
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
@ -178,23 +184,26 @@ public class Note {
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
//下面函数的作用是将新的数据通过Uri的操作存储到数据库
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
}//判断是否合法
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
ContentProviderOperation.Builder builder = null;//数据库的操作列表
if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
if(mTextDataValues.size() > 0) {//有文本数据
mTextDataValues.put(DataColumns.NOTE_ID, noteId);//如果有文本数据,首先将笔记的 IDnoteId存储到 mTextDataValues 中
if (mTextDataId == 0) {//如果是 0表示当前没有对应的文本数据记录需要进行插入操作。
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
//在插入操作中,设置 mTextDataValues 的 MIME 类型为 TextNote.CONTENT_ITEM_TYPE表示文本类型的数据。
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
mTextDataValues);//通过 context.getContentResolver().insert() 方法将 mTextDataValues 插入到 DataColumns 表中,并返回插入数据的 Uri。
//尝试从插入返回的 Uri 中获取新插入数据的 ID并将其转换为 Long 类型,并存储到 mTextDataId 中。若转换失败,记录错误日志并清空 mTextDataValues然后返回 null。
try {
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
@ -202,21 +211,25 @@ public class Note {
mTextDataValues.clear();
return null;
}
} else {
} else {//如果 mTextDataId 不为 0则表示已存在对应的文本数据记录需要进行更新操作。
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
Notes.CONTENT_DATA_URI, mTextDataId));//通过 ContentUris.withAppendedId() 获取特定 ID 的文本数据记录的 Uri并设置更新值为 mTextDataValues。
builder.withValues(mTextDataValues);
operationList.add(builder.build());
operationList.add(builder.build());//将更新操作添加到 operationList 中。
}
mTextDataValues.clear();
}
mTextDataValues.clear();//清空 mTextDataValues。
}//把文本数据存入DataColumns
if(mCallDataValues.size() > 0) {
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
if(mCallDataValues.size() > 0) {//有电话号码。用于将电话号码数据存储到数据库的 DataColumns 表中
mCallDataValues.put(DataColumns.NOTE_ID, noteId);//如果有电话号码数据,首先将笔记的 IDnoteId存储到 mCallDataValues 中。
if (mCallDataId == 0) {//判断 mCallDataId 是否为 0。如果是 0表示当前没有对应的电话号码数据记录需要进行插入操作。
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
//在插入操作中,设置 mCallDataValues 的 MIME 类型为 CallNote.CONTENT_ITEM_TYPE表示电话号码类型的数据。
// 然后通过 context.getContentResolver().insert() 方法将 mCallDataValues 插入到 DataColumns 表中,并返回插入数据的 Uri。
//尝试从插入返回的 Uri 中获取新插入数据的 ID并将其转换为 Long 类型,并存储到 mCallDataId 中。若转换失败,记录错误日志并清空 mCallDataValues然后返回 null。
try {
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
@ -224,16 +237,19 @@ public class Note {
mCallDataValues.clear();
return null;
}
} else {
} else {//如果 mCallDataId 不为 0则表示已存在对应的电话号码数据记录需要进行更新操作。
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mCallDataId));
Notes.CONTENT_DATA_URI, mCallDataId));//创建一个 ContentProviderOperation.Builder 对象,通过 ContentUris.withAppendedId() 获取特定 ID 的电话号码数据记录的 Uri并设置更新值为 mCallDataValues。
builder.withValues(mCallDataValues);
operationList.add(builder.build());
operationList.add(builder.build());////将更新操作添加到 operationList 中。
}
mCallDataValues.clear();
}
mCallDataValues.clear();//清空 mCallDataValues。
}//把电话号码数据存入DataColumns
//将电话号码数据与笔记关联起来,方便后续读取和展示。
if (operationList.size() > 0) {
if (operationList.size() > 0) {//总长大于0
// 在 try-catch 块中,捕获可能发生的 RemoteException 和 OperationApplicationException 异常。
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
@ -248,6 +264,6 @@ public class Note {
}
}
return null;
}
}//存储过程中的异常处理
}
}

@ -16,12 +16,12 @@
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 android.appwidget.AppWidgetManager;//用于管理Android应用中的小部件。
import android.content.ContentUris;//提供了用于处理内容URI的实用方法用于获取数据的URI。
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;
@ -31,7 +31,7 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
//定义WorkingNote
public class WorkingNote {
// Note for the working note
private Note mNote;
@ -41,27 +41,30 @@ public class WorkingNote {
private String mContent;
// Note mode
private int mMode;
//模式值初始化为0
private long mAlertDate;
//警戒值
private long mModifiedDate;
//修改值
private int mBgColorId;
//颜色
private int mWidgetId;
//宽 ID
private int mWidgetType;
//宽大小
private long mFolderId;
//文件夹ID
private Context mContext;
//内容
private static final String TAG = "WorkingNote";
//标签
private boolean mIsDeleted;
//删除时改变
private NoteSettingChangedListener mNoteSettingStatusListener;
//判断设置是否改变默认为0
// 声明 DATA_PROJECTION字符串数组
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
@ -72,6 +75,9 @@ public class WorkingNote {
DataColumns.DATA4,
};
// 声明 NOTE_PROJECTION字符串数组
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
@ -101,17 +107,19 @@ public class WorkingNote {
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
// WorkingNote的构造函数
// New note construct
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mModifiedDate = System.currentTimeMillis();//将修改日期设置为当前时间的毫秒数。
mFolderId = folderId;//将传入的文件夹 ID 赋值给 mFolderId。
mNote = new Note();
mNoteId = 0;
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;//将小部件类型设置为无效类型Notes.TYPE_WIDGET_INVALIDE
}
// Existing note construct
@ -124,11 +132,15 @@ public class WorkingNote {
loadNote();
}
// 加载Note
// 通过数据库调用query函数找到第一个条目
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);
@ -139,6 +151,7 @@ public class WorkingNote {
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);
@ -146,14 +159,18 @@ public class WorkingNote {
loadNoteData();
}
// 加载NoteData
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
//根据ID加载note
if (cursor != null) {
// 查到信息不为空
if (cursor.moveToFirst()) {
// 查看第一项是否存在
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
@ -166,37 +183,45 @@ public class WorkingNote {
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);
}
}
// 创建空的Note
// 传参context文件夹idwidget背景颜色
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
// 设定颜色widget
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
//根据id加载note
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
//保存Note
public synchronized boolean saveNote() {
if (isWorthSaving()) {
//是否值得保存
if (!existInDatabase()) {
//是否存在数据库中
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
//ID为0报错
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId);
mNote.syncNote(mContext, mNoteId);//保存
/**
* Update widget content if there exist any widget of this note
@ -204,7 +229,7 @@ public class WorkingNote {
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
mNoteSettingStatusListener.onWidgetChanged();// 状态变为已保存
}
return true;
} else {
@ -212,11 +237,14 @@ public class WorkingNote {
}
}
//判断数据库是否存在该便签
public boolean existInDatabase() {
return mNoteId > 0;
}
//是否值得保存
private boolean isWorthSaving() {
// 被删除,或(不在数据库中 内容为空),或 本地已保存过
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
@ -225,9 +253,12 @@ public class WorkingNote {
}
}
// 设置mNoteSettingStatusListener
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
// 设置AlertDate
// 若 mAlertDate与data不同则更改mAlertDate并设定NoteValue
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
@ -239,16 +270,20 @@ public class WorkingNote {
}
}
// 设定删除标记
public void markDeleted(boolean mark) {
//设定标记
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
// 调用mNoteSettingStatusListener的 onWidgetChanged方法
}
}
// 设定背景颜色
public void setBgColorId(int id) {
if (id != mBgColorId) {
if (id != mBgColorId) {//设定条件 id != mBgColorId
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
@ -257,7 +292,10 @@ public class WorkingNote {
}
}
// 设定检查列表模式
// 参数mode
public void setCheckListMode(int mode) {
//设定条件 mMode != mode才改变
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
@ -266,82 +304,107 @@ public class WorkingNote {
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
// 设定WidgetType
// 参数type
public void setWidgetType(int type) {
if (type != mWidgetType) {
if (type != mWidgetType) {//设定条件 type != mWidgetType
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
// 调用Note的setNoteValue方法更改WidgetType
}
}
// 设定WidgetId
// 参数id
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
// 调用Note的setNoteValue方法更改WidgetId
}
}
// 设定WorkingText
// 参数更改的text
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
if (!TextUtils.equals(mContent, text)) {//设定条件 mContent, text内容不同
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
// 调用Note的setTextData方法更改WorkingText
}
}
// 转变mNote的CallData及CallNote信息
// 参数String phoneNumber, long callDate
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));
}
// 判断是否有时钟题型
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
// 获取Content
public String getContent() {
return mContent;
}
// 获取AlertDate
public long getAlertDate() {
return mAlertDate;
}
// 获取ModifiedDate
public long getModifiedDate() {
return mModifiedDate;
}
// 获取背景颜色来源id
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
// 获取背景颜色id
public int getBgColorId() {
return mBgColorId;
}
// 获取标题背景颜色id
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
// 获取CheckListMode
public int getCheckListMode() {
return mMode;
}
// 获取便签id
public long getNoteId() {
return mNoteId;
}
// 获取文件夹id
public long getFolderId() {
return mFolderId;
}
// 获取WidgetId
public int getWidgetId() {
return mWidgetId;
}
// 获取WidgetType
public int getWidgetType() {
return mWidgetType;
}
// 创建接口 NoteSettingChangedListener,便签更新监视
// 为NoteEditActivity提供接口
// 提供函数有
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed

@ -16,12 +16,12 @@
package net.micode.notes.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.content.Context;//导入 Android 应用程序上下文(Context)类。
import android.database.Cursor;//导入 Android 数据库查询结果集(Cursor)类。
import android.os.Environment;//导入 Android 系统环境(Environment)类。
import android.text.TextUtils;//导入 Android 文本工具(TextUtils)类。
import android.text.format.DateFormat;//导入 Android 日期时间格式化(DateFormat)类。
import android.util.Log;//导入 Android 日志(Log)类。
import net.micode.notes.R;
import net.micode.notes.data.Notes;
@ -29,68 +29,72 @@ import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.File;//导入 Java 的文件(File)类,用于表示文件和目录的抽象路径名。
import java.io.FileNotFoundException;//导入 Java 的文件找不到异常(FileNotFoundException)类,用于处理文件未找到的异常情况。
import java.io.FileOutputStream;//导入 Java 的文件输出流(FileOutputStream)类,用于将数据写入文件。
import java.io.IOException;//导入 Java 的输入输出异常(IOException)类,用于处理输入输出操作中可能发生的异常情况。
import java.io.PrintStream;//导入 Java 的打印流(PrintStream)类,用于向标准输出流打印输出。
//备份的处理
public class BackupUtils {
private static final String TAG = "BackupUtils";
private static final String TAG = "BackupUtils";//定义了一个私有的静态字符串常量 TAG用于日志输出。
// Singleton stuff
private static BackupUtils sInstance;
private static BackupUtils sInstance;//定义了一个私有的静态成员变量 sInstance表示单例对象。
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
if (sInstance == null) {
//如果当前备份不存在,则新声明一个
sInstance = new BackupUtils(context);//并将传入的上下文参数赋值给它
}
return sInstance;
return sInstance;//。最后返回 sInstance。
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
// Currently, the sdcard is not mounted SD卡没有被装入手机
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist
// The backup file not exist 备份文件夹不存在
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs
// The data is not well formated, may be changed by other programs数据已被破坏,可能被修改
public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails
// Some run-time exception which causes restore or backup fails超时异常
public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success
// Backup or restore success成功存储
public static final int STATE_SUCCESS = 4;
//备份或恢复成功。
private TextExport mTextExport;//声明了一个私有成员变量 mTextExport表示文本导出对象。
private TextExport mTextExport;
//初始化函数
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
}//用于初始化 BackupUtils 对象
private static boolean externalStorageAvailable() {
//外部存储功能是否可用
private static boolean externalStorageAvailable() {//外部存储功能是否可用
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
}//用于检查外部存储是否可用。它通过比较外部存储的状态是否为 Environment.MEDIA_MOUNTED 来判断外部存储是否可用,并返回布尔值。
//
public int exportToText() {
return mTextExport.exportToText();
}
//用于导出数据到文本文件。该方法调用了 mTextExport 对象的 exportToText() 方法,并返回结果。
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
// 用于获取导出的文本文件名。该方法返回 mTextExport 对象的 mFileName 属性。
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
private static class TextExport {
// 用于获取导出的文本文件目录。该方法返回 mTextExport 对象的 mFileDirectory 属性。 private static class TextExport {
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
NoteColumns.ID,//表示笔记的唯一标识符。
NoteColumns.MODIFIED_DATE,//表示笔记的修改日期。
NoteColumns.SNIPPET,//表示笔记的摘要或片段。
NoteColumns.TYPE//表示笔记的类型。
};
private static final int NOTE_COLUMN_ID = 0;
@ -125,6 +129,7 @@ public class BackupUtils {
private String mFileName;
private String mFileDirectory;
//导出文本功能,将其作为字符串形式导出
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
@ -140,22 +145,22 @@ public class BackupUtils {
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder
// Query notes belong to this folder通过查询parent id是文件夹id的note来选出制定ID文件夹下的Note
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor != null) {//有内容
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
// Print note's last modified date ps里面保存有这份note的日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
exportNoteToText(noteId, ps); //将文件导出到text
} while (notesCursor.moveToNext());
}
notesCursor.close();
@ -166,22 +171,26 @@ public class BackupUtils {
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
// 通过 mContext.getContentResolver().query() 方法查询数据库,获取与指定笔记 ID 相关的数据。
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
// if (dataCursor != null) { //利用光标来扫描内容区别为callnote和note两种靠ps.printline输出
// 如果类型为 DataConstants.CALL_NOTE则获取电话号码、通话时间、附件位置等信息并使用 ps.println() 方法输出到文本文件中。
// 如果类型为 DataConstants.NOTE则获取笔记内容信息并使用 ps.println() 方法输出到文本文件中。
// 在每个笔记结束后,输出一行分隔符(由 Character.LINE_SEPARATOR, Character.LETTER_NUMBER 组成),以便区分不同笔记的内容
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
if (DataConstants.CALL_NOTE.equals(mimeType)) {//是否类型相同
// Print phone number
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
if (!TextUtils.isEmpty(phoneNumber)) {//判断是否为空字符
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
@ -206,7 +215,7 @@ public class BackupUtils {
dataCursor.close();
}
// print a line separator between note
try {
try {//如果在输出时发生异常,则会记录日志。
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
@ -218,7 +227,7 @@ public class BackupUtils {
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {
public int exportToText() {//总函数调用上面的exportFolder和exportNote
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
@ -229,7 +238,7 @@ public class BackupUtils {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// First export folder and its notes
// First export folder and its notes 导出文件夹,就是导出里面包含的便签
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
@ -257,7 +266,7 @@ public class BackupUtils {
folderCursor.close();
}
// Export notes in root's folder
// Export notes in root's folder 将根目录里的便签导出(由于不属于任何文件夹,因此无法通过文件夹导出来实现这一部分便签的导出)
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
@ -287,7 +296,7 @@ public class BackupUtils {
*/
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
R.string.file_name_txt_format);//导出后的文件路径
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
@ -313,17 +322,17 @@ public class BackupUtils {
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
StringBuilder sb = new StringBuilder();//创建一个 StringBuilder 对象 sb用于构建文件的路径。
sb.append(Environment.getExternalStorageDirectory()); //外部SD卡的存储路径
sb.append(context.getString(filePathResId));//文件的存储路径
File filedir = new File(sb.toString());//filedir应该就是用来存储路径信息
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
System.currentTimeMillis())));//将资源 ID 为 fileNameFormatResId 的字符串添加到 sb 中,该字符串是文件名的格式化模板,其中使用了当前时间进行格式化。
File file = new File(sb.toString());
try {
try { //如果这些文件不存在,则新建
if (!filedir.exists()) {
filedir.mkdir();
}
@ -337,6 +346,7 @@ public class BackupUtils {
e.printStackTrace();
}
// try catch 异常处理
return null;
}
}

@ -16,15 +16,15 @@
package net.micode.notes.tool;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import android.util.Log;
import android.content.ContentProviderOperation;//导入 Android 内容提供者操作(ContentProviderOperation)类,用于操作内容提供者中的数据。
import android.content.ContentProviderResult;//导入 Android 内容提供者结果(ContentProviderResult)类,用于存储内容提供者操作的结果。
import android.content.ContentResolver;//导入 Android 内容解析器(ContentResolver)类,用于访问应用程序中的各种内容提供者。
import android.content.ContentUris;//导入 Android 内容 URI(ContentUris)类,用于处理内容提供者的 URI。
import android.content.ContentValues;//导入 Android 内容值(ContentValues)类,用于封装要插入或更新到内容提供者中的数据。
import android.content.OperationApplicationException;//导入 Android 操作应用程序异常(OperationApplicationException)类,用于处理内容提供者操作时可能出现的异常情况。
import android.database.Cursor;//导入 Android 数据库查询结果集(Cursor)类,用于表示数据库查询结果集。
import android.os.RemoteException;//导入 Android 远程操作异常(RemoteException)类,用于处理远程操作时可能出现的异常情况。
import android.util.Log;//导入 Android 日志(Log)类,用于记录调试信息。
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
@ -37,6 +37,8 @@ import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";
//删除多个note
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
@ -47,19 +49,20 @@ public class DataUtils {
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();//提供一个任务列表
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
} //如果发现是根文件夹,则不删除
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); //用newDelete实现删除功能
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);//主机名或叫Authority用于唯一标识这个ContentProvider外部调用者可以根据这个标识来找到它。
//数据库事务,数据库事务是由一组数据库操作序列组成,事务作为一个整体被执行
if (results == null || results.length == 0 || results[0] == null) {//若为空则没必要删
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
@ -72,14 +75,16 @@ public class DataUtils {
return false;
}
//将便签移动至文件夹内
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);//对需要移动的便签进行数据更新然后用update实现
}
//批量移动
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
if (ids == null) {
@ -90,14 +95,14 @@ public class DataUtils {
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));//通过withAppendedId方法为该Uri加上ID
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build());
}
}//将ids里包含的每一列的数据逐次加入到operationList中等待最后的批量处理
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); //applyBatch一次性处理一个操作列表
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
@ -114,12 +119,14 @@ public class DataUtils {
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
//获取文件夹的编号
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
null);//筛选条件源文件不为trash folder
int count = 0;
if(cursor != null) {
@ -136,23 +143,24 @@ public class DataUtils {
return count;
}
//查看是否有指定ID的note可见
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
null);//查询条件type符合且不属于垃圾文件夹
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
if (cursor.getCount() > 0) {//用getcount函数判断cursor是否为空
exist = true;
}
cursor.close();
}
return exist;
}
///判定是否有指定note即不需条件条件type符合且不属于垃圾文件夹
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
@ -166,7 +174,7 @@ public class DataUtils {
}
return exist;
}
//判定是否有指定的data
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
@ -180,13 +188,14 @@ public class DataUtils {
}
return exist;
}
//判定是否有指定的可见folder
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
//通过名字查询文件是否存在
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
@ -197,12 +206,13 @@ public class DataUtils {
return exist;
}
//文件夹工具
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
null);//查询条件父ID是传入的folderId;
HashSet<AppWidgetAttribute> set = null;
if (c != null) {
@ -211,8 +221,8 @@ public class DataUtils {
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
widget.widgetId = c.getInt(0);//0对应的NoteColumns.WIDGET_ID
widget.widgetType = c.getInt(1);//1对应的NoteColumns.WIDGET_TYPE
set.add(widget);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString());
@ -224,20 +234,21 @@ public class DataUtils {
return set;
}
//根据给定的便签ID(noteId)获取该便签中的电话号码(CallNote.PHONE_NUMBER)
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
null);//在查询条件中限定了便签ID(NoteId)和便签类型(CallNote.MIME_TYPE),使用了占位符(?)和参数数组避免了SQL注入攻击
if (cursor != null && cursor.moveToFirst()) {
if (cursor != null && cursor.moveToFirst()) {//如果查询结果非空且有记录,则尝试获取第一条记录的电话号码,并将其返回。
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
Log.e(TAG, "Get call number fails " + e.toString());//如果获取电话号码失败,则在日志中打印错误信息,并返回空字符串
} finally {
cursor.close();
cursor.close();//关闭游标(Cursor)对象
}
}
return "";
@ -249,19 +260,19 @@ public class DataUtils {
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
null);//在查询条件中限定了通话日期(CallNote.CALL_DATE)、便签类型(CallNote.MIME_TYPE)以及电话号码(CallNote.PHONE_NUMBER)。//使用了占位符(?)和参数数组来避免SQL注入攻击并使用PHONE_NUMBERS_EQUAL函数来比较电话号码。
//通过数据库操作查询条件是callDate和phoneNumber匹配传入参数的值
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0);
return cursor.getLong(0);//0对应的CallNote.NOTE_ID
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
cursor.close();
}
return 0;
return 0;//如果查询结果为空则返回0作为默认值。
}
public static String getSnippetById(ContentResolver resolver, long noteId) {
@ -269,8 +280,8 @@ public class DataUtils {
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
null);//查询条件noteId
//如果查询结果非空则尝试获取第一条记录的便签片段并将其赋值给变量snippet。
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
@ -279,17 +290,17 @@ public class DataUtils {
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
throw new IllegalArgumentException("Note is not found with id: " + noteId);//如果查询结果为空则抛出IllegalArgumentException异常提示便签ID不存在。
}
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');
public static String getFormattedSnippet(String snippet) { //对字符串进行格式处理,将字符串两头的空格去掉,同时将换行符去掉
if (snippet != null) {//首先判断传入的便签片段(snippet)是否为空。如果不为空,则执行下一步;否则直接返回空字符串
snippet = snippet.trim();//对便签片段进行trim操作去除字符串前后的空格。
int index = snippet.indexOf('\n');//查找便签片段中第一个换行符(\n)的索引位置,如果找到了,则截取从开头到该索引位置的子字符串;否则保留原始字符串。
if (index != -1) {
snippet = snippet.substring(0, index);
}
}
return snippet;
return snippet;//返回格式化后的便签片段。
}
}

@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//定义了很多的静态字符串目的就是为了提供jsonObject中相应字符串的"key"。把这些静态的定义单独写到了一个类里面,这是非常好的编程规范
package net.micode.notes.tool;
//这个类就是定义了一堆static string实际就是为jsonObject提供Key把这些定义全部写到一个类里方便查看管理
public class GTaskStringUtils {
// 前面一部分常量名称都以"GTASK_JSON_"开头表示它们是Google Tasks API中JSON格式的字段名称。
public final static String GTASK_JSON_ACTION_ID = "action_id";//操作的ID
public final static String GTASK_JSON_ACTION_ID = "action_id";
public final static String GTASK_JSON_ACTION_LIST = "action_list";
public final static String GTASK_JSON_ACTION_LIST = "action_list";//操作列表
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
public final static String GTASK_JSON_ACTION_TYPE = "action_type";//操作类型
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";

@ -16,14 +16,30 @@
package net.micode.notes.tool;
import android.content.Context;
import android.preference.PreferenceManager;
import android.content.Context;//Context是一个Android系统级别的类它提供了访问应用程序资源和执行应用程序操作的方法。通过使用Context开发者可以获取应用程序的上下文环境例如访问应用程序的资源文件、启动活动、发送广播等。
import android.preference.PreferenceManager;//PreferenceManager是Android提供的一个管理应用程序偏好设置的类。偏好设置是一种存储和检索简单数据的机制例如用户首选项、应用程序设置等。PreferenceManager提供了一些方便的方法来读取和写入偏好设置并且可以自动处理数据类型转换和持久化存储。
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
public class ResourceParser {
////*简介:字面意义是资源分析器,实际上就是获取资源并且在程序中使用,比如颜色图片等
// * 实现方法主要利用R.java这个类其中包括
// * R.id 组件资源引用
// * R.drawable 图片资源 (被使用)
// * R.layout 布局资源
// * R.menu 菜单资源
// * R.String 文字资源
// * R.style 主题资源 (被使用)
// * 在按顺序设置好相应的id后就可以编写简单的getXXX函数获取需要的资源
// *
// * 特殊的变量
// * @BG_DEFAULT_COLOR 默认背景颜色(黄)
// * BG_DEFAULT_FONT_SIZE 默认文本大小(中)
public class ResourceParser {
//定义颜色编号
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
@ -31,7 +47,7 @@ public class ResourceParser {
public static final int RED = 4;
public static final int BG_DEFAULT_COLOR = YELLOW;
//定义大小编号
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
@ -40,20 +56,21 @@ public class ResourceParser {
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
public static class NoteBgResources {
// 表示笔记编辑界面的不同背景颜色
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
//标题栏背景颜色
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
public static int getNoteBgResource(int id) {
@ -64,7 +81,9 @@ public class ResourceParser {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
//直接获取默认的背景颜色。这个PREFERENCE_SET_BG_COLOR_KEY是个final string,也就是说getBoolean肯定执行else为什么要这么写
// getDefaultBgId方法是一个公共静态方法它接收一个Context对象作为参数并根据应用程序的偏好设置确定默认的背景颜色ID。如果偏好设置中设置了背景颜色则随机选择一个背景颜色ID否则返回一个默认的背景颜色ID。
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
@ -74,37 +93,38 @@ public class ResourceParser {
}
}
// NoteItemBgResources类类似于NoteBgResources类它也包含一个私有的静态整型数组BG_FIRST_RESOURCES用于表示笔记列表项的不同背景颜色。这些资源文件ID代表了笔记列表项在不同状态下的背景样式。
public static class NoteItemBgResources {
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
public static int getNoteBgFirstRes(int id) {
@ -128,13 +148,14 @@ public class ResourceParser {
}
}
// 这些数组存储了一组资源文件的ID用于表示小部件widget的不同背景颜色。其中BG_2X_RESOURCES数组存储了用于2x尺寸的小部件背景颜色的资源文件ID而BG_4X_RESOURCES数组存储了用于4x尺寸的小部件背景颜色的资源文件ID。
public static class WidgetBgResources {
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
public static int getWidget2xBgResource(int id) {
@ -142,11 +163,11 @@ public class ResourceParser {
}
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
public static int getWidget4xBgResource(int id) {
@ -154,14 +175,18 @@ public class ResourceParser {
}
}
// 用于存储一组文本外观TextAppearance的资源文件ID。这些资源文件ID代表了不同文本样式的设置如正常字体、中等字体、大号字体、超大号字体等
public static class TextAppearanceResources {
// 可以根据给定的索引获取对应的文本样式的资源文件ID。注意这里有一个容错的函数当传入的索引大于等于资源数量时会返回默认的字体大小设置。
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
//这里有一个容错的函数防止输入的id大于资源总量若如此则自动返回默认的设置结果
// getResourcesSize方法返回文本外观资源文件ID数组的长度也就是资源文件的总数。
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
@ -179,3 +204,5 @@ public class ResourceParser {
}
}
}

@ -40,103 +40,115 @@ import net.micode.notes.tool.DataUtils;
import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener //AlarmAlertActivity类实现了OnClickListener, OnDismissListener两个接口OnClickListener接口用于处理点击事件当用户点击了界面上的某个视图时会调用相应的点击事件处理方法。在AlarmAlertActivity中通过实现OnClickListener接口可以监听用户对界面上的按钮或其他可点击视图的点击事件并根据需要进行相应的处理。OnDismissListener接口用于处理对话框消失事件。当一个对话框被关闭或隐藏时会调用相应的消失事件处理方法。在AlarmAlertActivity中通过实现OnDismissListener接口可以监听闹钟提醒对话框的消失事件并在对话框消失后执行相应的操作。通过实现这两个接口AlarmAlertActivity可以监听界面上的点击事件和闹钟提醒对话框的消失事件并在相应事件发生时执行相应的逻辑操作。
{
private long mNoteId; //文本在数据库存储中的ID号
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
private String mSnippet;//闹钟提示时出现的文本片段。
private static final int SNIPPET_PREW_MAX_LEN = 60;// 文本片段预览的最大长度
MediaPlayer mPlayer;// 媒体播放器
@Override
protected void onCreate(Bundle savedInstanceState) {
protected void on2Create(Bundle savedInstanceState) //当一个Activity被创建时系统会调用该的onCreate()方法,在onCreate()方法的参数savedInstanceState是一个Bundle对象用于保存Activity的状态。当Activity因为某种原因例如屏幕旋转、内存不足等被销毁时系统会将Activity的状态保存在savedInstanceState中。当Activity重新创建时系统会将savedInstanceState传递给onCreate()方法,以便恢复之前保存的状态。
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题栏
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);// 在锁屏状态下显示窗口
if (!isScreenOn()) {
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
if (!isScreenOn()) // 如果屏处于关闭状态,设置一些窗口标志来保持屏幕亮起
{
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON//保持屏幕亮起的标志,表示在该窗口下,保持屏幕处于亮屏状态。
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON//点亮屏幕的标志,表示在该窗口下,当窗口显示时,点亮屏幕。
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON//在屏幕亮起时允许锁定的标志,表示在该窗口下,允许用户在屏幕亮起时锁定屏幕。
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);//布局插入装饰的标志,表示在该窗口下,窗口的布局可以延伸到系统装饰的边缘。
}//用于在创建Activity时设置窗口标志以确保在显示闹钟提醒界面时屏幕保持亮起并且在锁屏状态下仍然显示窗口。这样可以确保用户能够及时看到闹钟提醒的内容。
Intent intent = getIntent();
try {
// 从传递的Intent中获取文本的ID号并获取对应的文本片段
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
// 如果文本片段长度超过预设的最大长度,截取一部分,并添加提示信息
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
e.printStackTrace();// 打印异常信息
return;// 结束方法的执行
}
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
mPlayer = new MediaPlayer(); // 创建一个媒体播放器对象
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) // 如果文本在数据库中可见
{
showActionDialog();// 显示操作对话框
playAlarmSound(); // 播放闹钟声音
} else {
finish();
finish();// 结束当前Activity
}
}
/**
*
* @return truefalse
*/
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
return pm.isScreenOn(); // 返回屏幕是否处于亮屏状态
}
/**
*
*/
private void playAlarmSound() {
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);// 获取闹钟铃声的URI
//这里的流都是音频流
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);// 获取当前静音模式下影响铃声的流
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) // 如果静音模式下影响铃声的流中包含闹钟铃声流,设置媒体播放器的音频流为静音模式下影响铃声的流
{
mPlayer.setAudioStreamType(silentModeStreams);
} else {
} else// 否则,设置媒体播放器的音频流为闹钟铃声流
{
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
mPlayer.setDataSource(this, url); // 设置媒体放器的数据源为闹钟铃声的URI
mPlayer.prepare(); // 准备异步播放
mPlayer.setLooping(true); // 设置循环播放
mPlayer.start(); // 开始播放
} catch (IllegalArgumentException e) { // 处理参数异常
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
} catch (SecurityException e) { // 处理安全异常
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
} catch (IllegalStateException e) { // 处理状态异常
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
} catch (IOException e) { // 处理IO异常
e.printStackTrace();
}
}
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.app_name);
dialog.setMessage(mSnippet);
dialog.setPositiveButton(R.string.notealert_ok, this);
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
dialog.setTitle(R.string.app_name); // 设置对话框标题
dialog.setMessage(mSnippet); // 设置对话框消息内容
dialog.setPositiveButton(R.string.notealert_ok, this); // 设置对话框的确定按钮和点击事件
if (isScreenOn()) { // 判断屏幕是否亮屏
dialog.setNegativeButton(R.string.notealert_enter, this); // 设置对话框的取消按钮和点击事件
}
dialog.show().setOnDismissListener(this);
dialog.show().setOnDismissListener(this); // 显示对话框,并设置对话框消失时的监听器
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
Intent intent = new Intent(this, NoteEditActivity.class); // 创建一个Intent对象跳转到NoteEditActivity
intent.setAction(Intent.ACTION_VIEW); // 设置Intent的操作为查看操作
intent.putExtra(Intent.EXTRA_UID, mNoteId); // 设置Intent的额外数据
startActivity(intent); // 启动Activity
break;
default:
break;
@ -144,15 +156,14 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
stopAlarmSound(); // 停止播放铃声
finish(); // 结束当前Activity
}
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
mPlayer.stop(); // 停止播放
mPlayer.release(); // 释放资源
mPlayer = null; // 置空。
}
}
}

@ -30,36 +30,43 @@ import net.micode.notes.data.Notes.NoteColumns;
public class AlarmInitReceiver extends BroadcastReceiver {
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
// 查询备忘录的投影
private static final String[] PROJECTION = new String[] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
// 列索引
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前时间
long currentDate = System.currentTimeMillis();
// 查询所有需要提醒的备忘录
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
new String[]{String.valueOf(currentDate)},
null);
if (c != null) {
if (c.moveToFirst()) {
do {
// 获取提醒时间和备忘录的ID
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
long noteId = c.getLong(COLUMN_ID);
// 创建一个发送广播的Intent并设置数据为备忘录的URI
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId));
// 创建一个PendingIntent用于发送广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
// 获取AlarmManager实例并设置提醒时间和发送广播的PendingIntent
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
c.close();
}
}
}
}

@ -23,8 +23,11 @@ import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 创建一个新的Intent对象将目标Activity设置为AlarmAlertActivity
intent.setClass(context, AlarmAlertActivity.class);
// 添加标志FLAG_ACTIVITY_NEW_TASK以确保启动Activity时具有新的任务栈
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动目标Activity
context.startActivity(intent);
}
}
}

@ -30,45 +30,60 @@ import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout {
// 默认启用状态
private static final boolean DEFAULT_ENABLE_STATE = true;
// 一半天的小时数
private static final int HOURS_IN_HALF_DAY = 12;
// 一天的小时数
private static final int HOURS_IN_ALL_DAY = 24;
// 一周的天数
private static final int DAYS_IN_ALL_WEEK = 7;
// 日期选择器的最小值
private static final int DATE_SPINNER_MIN_VAL = 0;
// 日期选择器的最大值
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
// 24小时制时钟的小时选择器的最小值
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
// 24小时制时钟的小时选择器的最大值
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
// 12小时制时钟的小时选择器的最小值
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
// 12小时制时钟的小时选择器的最大值
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
// 分钟选择器的最小值
private static final int MINUT_SPINNER_MIN_VAL = 0;
// 分钟选择器的最大值
private static final int MINUT_SPINNER_MAX_VAL = 59;
// 上午/下午选择器的最小值
private static final int AMPM_SPINNER_MIN_VAL = 0;
// 上午/下午选择器的最大值
private static final int AMPM_SPINNER_MAX_VAL = 1;
private final NumberPicker mDateSpinner; // 日期选择器控件
private final NumberPicker mHourSpinner; // 小时选择器控件
private final NumberPicker mMinuteSpinner; // 分钟选择器控件
private final NumberPicker mAmPmSpinner; // 上午/下午选择器控件
private Calendar mDate; // 当前日期时间
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate;
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; // 一周的日期显示值
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm; // 当前是否为上午
private boolean mIsAm;
private boolean mIs24HourView; // 是否为24小时制显示
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; // 是否启用选择器的状态,默认为启用
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener;
private boolean mInitialising; // 是否正在初始化
private OnDateTimeChangedListener mOnDateTimeChangedListener; // 日期时间变化监听器
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 计算日期差值并更新日期
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
// 更新日期选择器控件
updateDateControl();
// 触发日期时间变化监听器
onDateTimeChanged();
}
};
@ -79,6 +94,7 @@ public class DateTimePicker extends FrameLayout {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
// 处理上午/下午切换时的日期变化
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
@ -88,12 +104,14 @@ public class DateTimePicker extends FrameLayout {
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
// 处理上午/下午切换时的上午/下午选择器更新
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
} else {
// 处理24小时制下的日期变化
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
@ -104,9 +122,11 @@ public class DateTimePicker extends FrameLayout {
isDateChanged = true;
}
}
// 更新小时,并触发日期时间变化监听器
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
onDateTimeChanged();
// 如果日期发生了变化,更新年、月、日
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
@ -114,143 +134,169 @@ public class DateTimePicker extends FrameLayout {
}
}
};
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
int minValue = mMinuteSpinner.getMinValue(); // 获取分钟选择器的最小值
int maxValue = mMinuteSpinner.getMaxValue(); // 获取分钟选择器的最大值
int offset = 0; // 偏移量,用于处理分钟的循环
if (oldVal == maxValue && newVal == minValue) { // 判断是否从最大值切换到最小值
offset += 1; // 如果是则偏移量加1
} else if (oldVal == minValue && newVal == maxValue) { // 判断是否从最小值切换到最大值
offset -= 1; // 如果是则偏移量减1
}
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
if (offset != 0) { // 如果偏移量不为0表示分钟发生了循环
mDate.add(Calendar.HOUR_OF_DAY, offset); // 更新小时的值
mHourSpinner.setValue(getCurrentHour()); // 更新小时选择器的值
updateDateControl(); // 更新日期选择器控件
int newHour = getCurrentHourOfDay(); // 获取当前小时
if (newHour >= HOURS_IN_HALF_DAY) { // 如果当前小时大于等于一半天的小时数
mIsAm = false; // 设置为下午
updateAmPmControl(); // 更新上午/下午选择器控件
} else {
mIsAm = true;
updateAmPmControl();
mIsAm = true; // 设置为上午
updateAmPmControl(); // 更新上午/下午选择器控件
}
}
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
mDate.set(Calendar.MINUTE, newVal); // 更新分钟的值
onDateTimeChanged(); // 触发日期时间变化监听器
}
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
mIsAm = !mIsAm; // 切换上午/下午的状态
if (mIsAm) { // 如果切换为上午
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); // 将当前时间减去半天的小时数
} else { // 如果切换为下午
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); // 将当前时间加上半天的小时数
}
updateAmPmControl();
onDateTimeChanged();
updateAmPmControl(); // 更新上午/下午选择器控件
onDateTimeChanged(); // 触发日期时间变化监听器
}
};
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
/**
*
*
* @param view DateTimePicker
* @param year
* @param month 0-110
* @param dayOfMonth
* @param hourOfDay 24 0-23 1-12
* @param minute
*/
void onDateTimeChanged(DateTimePicker view, int year, int month, int dayOfMonth, int hourOfDay, int minute);
}
/**
* DateTimePicker
*
* @param context
*/
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
/**
* DateTimePicker
*
* @param context
* @param date
* @param is24HourView 24
*/
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
super(context); // 调用父类的构造方法
mDate = Calendar.getInstance(); // 获取当前时间的日历对象
mInitialising = true; // 表示正在初始化
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; // 根据当前小时判断是否为上午
inflate(context, R.layout.datetime_picker, this); // 将布局文件实例化为视图并添加到当前视图
mDateSpinner = (NumberPicker) findViewById(R.id.date); // 获取日期选择器视图
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); // 设置日期选择器的最小值
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); // 设置日期选择器的最大值
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); // 设置日期选择器值变化的监听器
mHourSpinner = (NumberPicker) findViewById(R.id.hour); // 获取小时选择器视图
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); // 设置小时选择器值变化的监听器
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); // 获取分钟选择器视图
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); // 设置分钟选择器的最小值
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); // 设置分钟选择器的最大值
mMinuteSpinner.setOnLongPressUpdateInterval(100); // 设置长按更新间隔
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); // 设置分钟选择器值变化的监听器
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); // 获取上午/下午的字符串数组
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); // 获取上午/下午选择器视图
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); // 设置上午/下午选择器的最小值
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); // 设置上午/下午选择器的最大值
mAmPmSpinner.setDisplayedValues(stringsForAmPm); // 设置上午/下午选择器的显示值
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); // 设置上午/下午选择器值变化的监听器
updateDateControl(); // 更新日期选择器控件
updateHourControl(); // 更新小时选择器控件
updateAmPmControl(); // 更新上午/下午选择器控件
set24HourView(is24HourView); // 设置小时制显示
setCurrentDate(date); // 设置当前日期时间
setEnabled(isEnabled()); // 设置是否可用
mInitialising = false; // 初始化完成
}
/**
*
*
* @param enabled
*/
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
super.setEnabled(enabled);
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
super.setEnabled(enabled); // 调用父类方法设置控件可用状态
mDateSpinner.setEnabled(enabled); // 设置日期选择器是否可用
mMinuteSpinner.setEnabled(enabled); // 设置分钟选择器是否可用
mHourSpinner.setEnabled(enabled); // 设置小时选择器是否可用
mAmPmSpinner.setEnabled(enabled); // 设置上午/下午选择器是否可用
mIsEnabled = enabled; // 更新控件是否可用状态
}
/**
*
*
* @return
*/
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* Get the current date in millis
*
*
* @return the current date in millis
* @return
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* Set the current date
*
*
* @param date The current date in millis
* @param date The current date in millis
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
mDate.setTimeInMillis(date); // 将指定的日期毫秒值设置到日期对象中
updateDateControl(); // 更新日期选择器控件
updateHourControl(); // 更新小时选择器控件
updateAmPmControl(); // 更新上午/下午选择器控件
}
/**
@ -262,13 +308,12 @@ public class DateTimePicker extends FrameLayout {
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
public void setCurrentDate(int year, int month, int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year); // 设置当前年份
setCurrentMonth(month); // 设置当前月份
setCurrentDay(dayOfMonth); // 设置当前日期
setCurrentHour(hourOfDay); // 设置当前小时
setCurrentMinute(minute); // 设置当前分钟
}
/**
@ -289,9 +334,9 @@ public class DateTimePicker extends FrameLayout {
if (!mInitialising && year == getCurrentYear()) {
return;
}
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
mDate.set(Calendar.YEAR, year); // 设置日期对象的年份
updateDateControl(); // 更新日期选择器控件
onDateTimeChanged(); // 通知日期时间改变
}
/**
@ -300,180 +345,192 @@ public class DateTimePicker extends FrameLayout {
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
return mDate.get(Calendar.MONTH); // 获取日期对象的月份字段
}
/**
* Set current month in the year
*
*
* @param month The month in the year
* @param month 00
*/
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
mDate.set(Calendar.MONTH, month); // 设置日期对象的月份
updateDateControl(); // 更新日期选择器控件
onDateTimeChanged(); // 通知日期时间改变
}
/**
* Get current day of the month
*
*
* @return The day of the month
* @return
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
return mDate.get(Calendar.DAY_OF_MONTH); // 获取日期对象的天数字段
}
/**
* Set current day of the month
*
*
* @param dayOfMonth The day of the month
* @param dayOfMonth
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); // 设置日期对象的天数字段
updateDateControl(); // 更新日期选择器控件
onDateTimeChanged(); // 通知日期时间改变
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
* 240~23
*
* @return 24
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
return mDate.get(Calendar.HOUR_OF_DAY); // 获取日期对象的小时字段24小时制
}
/**
*
*
* @return
*/
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
if (mIs24HourView) { // 如果是24小时制
return getCurrentHourOfDay(); // 直接获取当前的小时
} else { // 如果是12小时制
int hour = getCurrentHourOfDay(); // 获取当前的小时
if (hour > HOURS_IN_HALF_DAY) { // 如果大于12小时
return hour - HOURS_IN_HALF_DAY; // 返回减去12小时的小时数
} else { // 如果小于等于12小时
return hour == 0 ? HOURS_IN_HALF_DAY : hour; // 如果小时为0则返回12小时否则返回当前的小时
}
}
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
* 240~23
*
* @param hourOfDay
* @param hourOfDay 24
*/
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); // 设置日期对象的小时字段24小时制
if (!mIs24HourView) { // 如果是12小时制
if (hourOfDay >= HOURS_IN_HALF_DAY) { // 如果小时大于等于12
mIsAm = false; // 设置为下午
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
hourOfDay -= HOURS_IN_HALF_DAY; // 减去12小时
}
} else {
mIsAm = true;
} else { // 如果小时小于12
mIsAm = true; // 设置为上午
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
hourOfDay = HOURS_IN_HALF_DAY; // 如果小时为0则设置为12小时
}
}
updateAmPmControl();
updateAmPmControl(); // 更新上午/下午选择控件
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
mHourSpinner.setValue(hourOfDay); // 设置小时选择控件的值
onDateTimeChanged(); // 通知日期时间改变
}
/**
* Get currentMinute
*
*
* @return The Current Minute
* @return
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
return mDate.get(Calendar.MINUTE); // 获取日期对象的分钟字段
}
/**
* Set current minute
*
*
* @param minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
mMinuteSpinner.setValue(minute); // 设置分钟选择控件的值
mDate.set(Calendar.MINUTE, minute); // 设置日期对象的分钟字段
onDateTimeChanged(); // 通知日期时间改变
}
/**
* @return true if this is in 24 hour view else false.
* 24
*
* @return 24truefalse
*/
public boolean is24HourView () {
return mIs24HourView;
public boolean is24HourView() {
return mIs24HourView; // 返回是否为24小时制的标志位
}
/**
* Set whether in 24 hour or AM/PM mode.
* 24/
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
* @param is24HourView true24false/
*/
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
mIs24HourView = is24HourView; // 设置是否为24小时制的标志位
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); // 根据是否为24小时制设置上午/下午选择控件的可见性
int hour = getCurrentHourOfDay(); // 获取当前的小时24小时制
updateHourControl(); // 更新小时选择控件
setCurrentHour(hour); // 设置当前的小时
updateAmPmControl(); // 更新上午/下午选择控件
}
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
mDateSpinner.setDisplayedValues(null);
Calendar cal = Calendar.getInstance(); // 创建一个新的Calendar对象
cal.setTimeInMillis(mDate.getTimeInMillis()); // 将mDate的时间设置给新的Calendar对象
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); // 将日期向前移动DAYS_IN_ALL_WEEK / 2 + 1天
mDateSpinner.setDisplayedValues(null); // 清空日期选择控件的显示值
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
cal.add(Calendar.DAY_OF_YEAR, 1);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
cal.add(Calendar.DAY_OF_YEAR, 1); // 将日期加上1天
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); // 将日期格式化为"MM.dd EEEE"的字符串格式并存储到mDateDisplayValues数组中
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
}
mDateSpinner.setDisplayedValues(mDateDisplayValues); // 设置日期选择控件的显示值为mDateDisplayValues数组
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); // 设置日期选择控件的当前值为DAYS_IN_ALL_WEEK / 2
mDateSpinner.invalidate(); // 使日期选择控件无效,以便重绘
}
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
mAmPmSpinner.setVisibility(View.GONE); // 如果是24小时制隐藏上午/下午选择控件
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
int index = mIsAm ? Calendar.AM : Calendar.PM; // 根据当前上午/下午的标志位确定要显示的上午/下午选项
mAmPmSpinner.setValue(index); // 设置上午/下午选择控件的值为对应的索引
mAmPmSpinner.setVisibility(View.VISIBLE); // 显示上午/下午选择控件
}
}
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); // 设置小时选择控件的最小值为24小时制的最小值
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); // 设置小时选择控件的最大值为24小时制的最大值
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); // 设置小时选择控件的最小值为12小时制的最小值
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); // 设置小时选择控件的最大值为12小时制的最大值
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*
*
* @param callback null
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
mOnDateTimeChangedListener = callback; // 设置回调接口
}
private void onDateTimeChanged() {
@ -482,4 +539,3 @@ public class DateTimePicker extends FrameLayout {
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -31,37 +31,38 @@ import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance();
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker;
private Calendar mDate = Calendar.getInstance(); // 创建一个Calendar对象并初始化为当前日期时间
private boolean mIs24HourView; // 是否为24小时制
private OnDateTimeSetListener mOnDateTimeSetListener; // 日期时间设置回调接口
private DateTimePicker mDateTimePicker; // 日期时间选择器控件
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
public DateTimePickerDialog(Context context, long date) {
super(context);
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
super(context); // 调用父类的构造方法
mDateTimePicker = new DateTimePicker(context); // 创建一个DateTimePicker对象
setView(mDateTimePicker); // 设置对话框的视图为DateTimePicker对象
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
updateTitle(mDate.getTimeInMillis());
int dayOfMonth, int hourOfDay, int minute) {
mDate.set(Calendar.YEAR, year); // 设置日期对象的年份
mDate.set(Calendar.MONTH, month); // 设置日期对象的月份
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); // 设置日期对象的日
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); // 设置日期对象的小时
mDate.set(Calendar.MINUTE, minute); // 设置日期对象的分钟
updateTitle(mDate.getTimeInMillis()); // 更新对话框的标题
}
});
mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0);
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
set24HourView(DateFormat.is24HourFormat(this.getContext()));
updateTitle(mDate.getTimeInMillis());
mDate.setTimeInMillis(date); // 将传入的日期设置给日期对象mDate
mDate.set(Calendar.SECOND, 0); // 将日期对象的秒设置为0
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); // 将日期对象的时间设置给DateTimePicker控件
setButton(context.getString(R.string.datetime_dialog_ok), this); // 设置对话框的“确定”按钮,并设置点击监听器为当前对象
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); // 设置对话框的“取消”按钮,不设置点击监听器
set24HourView(DateFormat.is24HourFormat(this.getContext())); // 根据系统设置判断是否为24小时制并设置给日期时间选择器控件
updateTitle(mDate.getTimeInMillis()); // 更新对话框的标题,将日期对象的时间设置为标题显示
}
public void set24HourView(boolean is24HourView) {
@ -74,16 +75,15 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
DateUtils.FORMAT_SHOW_YEAR | // 显示年份
DateUtils.FORMAT_SHOW_DATE | // 显示日期
DateUtils.FORMAT_SHOW_TIME; // 显示时间
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; // 根据是否为24小时制设置时间格式化标志
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); // 使用DateUtils类将日期时间格式化为字符串并设置为对话框的标题
}
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
if (mOnDateTimeSetListener != null) { // 判断是否设置了日期时间设置回调接口
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); // 调用回调接口的OnDateTimeSet方法并传入当前对话框和日期时间的毫秒值
}
}

@ -28,34 +28,34 @@ import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
public class DropdownMenu {
private Button mButton;
private PopupMenu mPopupMenu;
private Menu mMenu;
private Button mButton; // 下拉菜单的按钮
private PopupMenu mPopupMenu; // 弹出菜单
private Menu mMenu; // 菜单
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
mButton = button; // 设置按钮
mButton.setBackgroundResource(R.drawable.dropdown_icon); // 设置按钮的背景图为下拉图标
mPopupMenu = new PopupMenu(context, mButton); // 创建弹出菜单,关联到按钮
mMenu = mPopupMenu.getMenu(); // 获取菜单对象
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); // 通过菜单资源ID填充菜单条目
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
mPopupMenu.show(); // 点击按钮时显示弹出菜单
}
});
}
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
mPopupMenu.setOnMenuItemClickListener(listener); // 设置菜单条目点击监听器
}
}
public MenuItem findItem(int id) {
return mMenu.findItem(id);
return mMenu.findItem(id); // 根据菜单条目ID查找菜单条目对象
}
public void setTitle(CharSequence title) {
mButton.setText(title);
mButton.setText(title); // 设置按钮的文本
}
}
}

@ -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.content.Context;
@ -28,11 +12,10 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class FoldersListAdapter extends CursorAdapter {
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
NoteColumns.ID,
NoteColumns.SNIPPET
};
public static final int ID_COLUMN = 0;
@ -45,12 +28,14 @@ public class FoldersListAdapter extends CursorAdapter {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// 创建新的列表项的视图
return new FolderListItem(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
// 判断视图类型是否为FolderListItem如果是则获取文件夹的名称并绑定到视图上
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName);
@ -58,6 +43,7 @@ public class FoldersListAdapter extends CursorAdapter {
}
public String getFolderName(Context context, int position) {
// 获取指定位置的文件夹名称
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
@ -68,13 +54,14 @@ public class FoldersListAdapter extends CursorAdapter {
public FolderListItem(Context context) {
super(context);
// 填充布局文件到当前视图中
inflate(context, R.layout.folder_list_item, this);
mName = (TextView) findViewById(R.id.tv_folder_name);
}
public void bind(String name) {
// 将文件夹名称绑定到视图上
mName.setText(name);
}
}
}
}

@ -39,18 +39,18 @@ import java.util.Map;
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex;
private int mSelectionStartBeforeDelete;
private int mIndex; // 当前索引
private int mSelectionStartBeforeDelete; // 删除前的光标位置
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
private static final String SCHEME_TEL = "tel:"; // 电话号码链接的Scheme
private static final String SCHEME_HTTP = "http:"; // 网页链接的Scheme
private static final String SCHEME_EMAIL = "mailto:"; // 邮件链接的Scheme
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>(); // 存储链接Scheme和对应操作资源的映射
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); // 将电话号码链接的Scheme和对应的操作资源ID放入映射中
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); // 将网页链接的Scheme和对应的操作资源ID放入映射中
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); // 将邮件链接的Scheme和对应的操作资源ID放入映射中
}
/**
@ -58,52 +58,69 @@ public class NoteEditText extends EditText {
*/
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
* {@link KeyEvent#KEYCODE_DEL}
*
* @param index
* @param text
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
* {@link KeyEvent#KEYCODE_ENTER}
*
* @param index
* @param text
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*
*
* @param index
* @param hasText
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener;
//根据context设置文本
public NoteEditText(Context context) {
super(context, null);
super(context, null);//用super引用父类变量
mIndex = 0;
}
//设置当前光标
public void setIndex(int index) {
mIndex = index;
}
//初始化文本修改标记
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
public NoteEditText(Context context, AttributeSet attrs) {
//AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性
//初始化便签
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
// 根据defstyle自动初始化
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
// TODO Auto-generated construct or stub
}
@Override
//view里的函数处理手机屏幕的所有事件
/*event
*/
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//重写了需要处理屏幕被按下的事件
case MotionEvent.ACTION_DOWN:
//跟新当前坐标值
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
@ -111,9 +128,12 @@ public class NoteEditText extends EditText {
x += getScrollX();
y += getScrollY();
//用布局控件layout根据x,y的新值设置新的位置
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
//更新光标新的位置
Selection.setSelection(getText(), off);
break;
}
@ -122,96 +142,147 @@ public class NoteEditText extends EditText {
}
@Override
/*
*
*
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
//根据按键的 Unicode 编码值来处理
case KeyEvent.KEYCODE_ENTER:
//“进入”按键
if (mOnTextViewChangeListener != null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
//“删除”按键
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
//继续执行父类的其他点击事件
return super.onKeyDown(keyCode, event);
}
@Override
/*
*
*
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
//根据按键的 Unicode 编码值来处理有删除和进入2种操作
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
//若是被修改过
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
//若之前有被修改并且文档不为空
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
//利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
//其他情况报错,文档的改动监听器并没有建立
}
break;
case KeyEvent.KEYCODE_ENTER:
//同上也是分为监听器是否建立2种情况
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
//获取当前位置
String text = getText().subSequence(selectionStart, length()).toString();
//获取当前文本
setText(getText().subSequence(0, selectionStart));
//根据获取的文本设置当前文本
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
//当{@link KeyEvent#KEYCODE_ENTER}添加新文本
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
//其他情况报错,文档的改动监听器并没有建立
}
break;
default:
break;
}
//继续执行父类的其他按键弹起的事件
return super.onKeyUp(keyCode, event);
}
@Override
/*
*
*
* focusedViewFocusedtruefalse
direction
RectViewnull
*/
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
//若监听器已经建立
if (!focused && TextUtils.isEmpty(getText())) {
//获取到焦点并且文本不为空
mOnTextViewChangeListener.onTextChange(mIndex, false);
//mOnTextViewChangeListener子函数置false隐藏事件选项
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
//mOnTextViewChangeListener子函数置true显示事件选项
}
}
//继续执行父类的其他焦点变化的事件
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
/*
*
*
*/
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
//有文本存在
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
//获取文本开始和结尾位置
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
//获取开始到结尾的最大值和最小值
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
//设置url的信息的范围值
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
//获取计划表中所有的key值
if(urls[0].getURL().indexOf(schema) >= 0) {
//若url可以添加则在添加后将defaultResId置为key所映射的值
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
//defaultResId == 0则说明url并没有添加任何东西所以置为连接其他SchemaActionResMap的值
defaultResId = R.string.note_link_other;
}
//建立菜单
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
//新建按键监听器
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
//根据相应的文本设置菜单的按键
return true;
}
});
}
}
//继续执行父类的其他菜单创建的事件
super.onCreateContextMenu(menu);
}
}

@ -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.content.Context;
@ -28,20 +12,20 @@ import net.micode.notes.tool.DataUtils;
public class NoteItemData {
static final String [] PROJECTION = 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.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,
};
//常量标记和数据就不一一标记了,意义翻译基本就知道
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
@ -75,8 +59,9 @@ public class NoteItemData {
private boolean mIsOnlyOneItem;
private boolean mIsOneNoteFollowingFolder;
private boolean mIsMultiNotesFollowingFolder;
public NoteItemData(Context context, Cursor cursor) {
//初始化NoteItemData主要利用光标cursor获取的东西
public NoteItemData(Context context, Cursor cursor) {
//getxxx为转换格式
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
@ -92,10 +77,11 @@ public class NoteItemData {
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
//初始化电话号码的信息
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
if (!TextUtils.isEmpty(mPhoneNumber)) {//mphonenumber里有符合字符串则用contart功能连接
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
@ -108,32 +94,35 @@ public class NoteItemData {
}
checkPostion(cursor);
}
///根据鼠标的位置设置标记,和位置
private void checkPostion(Cursor cursor) {
//初始化几个标记cursor具体功能笔记中已提到不一一叙述
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
//初始化“多重子文件”“单一子文件”2个标记
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
//主要是设置上诉2标记
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;
}
@ -214,6 +203,7 @@ public class NoteItemData {
return (mAlertDate > 0);
}
//若数据父id为保存至文件夹模式的id且满足电话号码单元不为空则isCallRecord为true
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
@ -221,4 +211,4 @@ public class NoteItemData {
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
}
}

@ -82,7 +82,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()));

@ -144,7 +144,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
} else {
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;
@ -246,7 +246,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mHasAddedAccount = 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();

Loading…
Cancel
Save