Compare commits

..

41 Commits
mjc ... main

Author SHA1 Message Date
pn4rlwvet f39d0ec629 ADD file via upload
5 months ago
pn4rlwvet 118937013f ADD file via upload
5 months ago
zjz e59e590186 报告
8 months ago
zjz f23e1a1fc3 注释代码
8 months ago
zjz abe758f08e 注释代码
8 months ago
zjz 85491d31c4 项目上传
8 months ago
pn4rlwvet cd4c5322fe Delete '1'
8 months ago
pn4rlwvet 1cc93ac39c ADD file via upload
8 months ago
pn4rlwvet b301a68d5a Delete 'README.md'
8 months ago
pn4rlwvet 6435167547 Delete '源程序代码-小米便签-Notes-master.zip'
8 months ago
pn4rlwvet d2d298b448 Delete '新建文本文档.txt'
8 months ago
pn4rlwvet d67d474fe9 Delete '新建文本文档 (2).txt'
8 months ago
zjz 8c451902a1 注释
8 months ago
zjz d3a27ea11c 注释
8 months ago
zjz 2f825b58ba 123
10 months ago
zjz ad37348fe6 2134
10 months ago
zjz 4f2bdf3f3a Merge branch 'zjz' of https://bdgit.educoder.net/pcs8ovtw5/test
10 months ago
zjz 3cff856503 2134
10 months ago
pn4rlwvet a3daf1ce0f Delete '新建 XLS 工作表.xls'
10 months ago
zjz 8cb5829eea 1234
10 months ago
pn4rlwvet a9d8c483ec Delete '新建 文本文档.txt'
11 months ago
pn4rlwvet 3bccc41abe Delete 'abc.txt'
11 months ago
pn4rlwvet 87573baac3 Delete 'README.md'
11 months ago
pn4rlwvet 579ced321d Delete '5345234.txt'
11 months ago
pn4rlwvet cc7d1e5b66 Delete '11.docx'
11 months ago
zjz 29b839130f 234123
11 months ago
zjz 1141748845 1234
11 months ago
zjz 9d9e53c4b0 242341234
11 months ago
zjz 5dd3238bff 234234
11 months ago
zjz 36f9c9bc3b 111
11 months ago
ÕŽ¨ÖÒ edcdaa57dd 顶替
11 months ago
zjz c67c0395b1 dsfasdf
11 months ago
zjz 8515b6c069 dsfsadf
11 months ago
zjz aa05198973 顶替
11 months ago
zjz f67f82e2a9
11 months ago
zjz 417112c521 sdfddfs
11 months ago
zjz 607daae236 实打实
11 months ago
zjz c0089f03eb 压顶
11 months ago
zjz c95b524fee sdfadsf
11 months ago
zjz 1566dd7c91 sdfas
11 months ago
zjz 02b855e12b 123
11 months ago

@ -1,2 +0,0 @@
# test

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Notes-master.iml" filepath="$PROJECT_DIR$/Notes-master.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -50,14 +50,38 @@ public class NotesProvider extends ContentProvider {
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
<<<<<<< HEAD
// 静态初始化块,用于在类加载时初始化静态资源
static {
// 创建一个UriMatcher对象用于匹配URI初始状态设置为NO_MATCH表示没有匹配
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加URI匹配规则匹配"content://com.example.notes/note"路径匹配码为URI_NOTE
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 添加URI匹配规则匹配"content://com.example.notes/note/#"路径匹配码为URI_NOTE_ITEM
// 其中"#"表示任意数字用于匹配特定ID的note
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 添加URI匹配规则匹配"content://com.example.notes/data"路径匹配码为URI_DATA
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 添加URI匹配规则匹配"content://com.example.notes/data/#"路径匹配码为URI_DATA_ITEM
// 其中"#"表示任意数字用于匹配特定ID的data
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 添加URI匹配规则匹配"content://com.example.notes/search"路径匹配码为URI_SEARCH
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 添加URI匹配规则匹配"content://com.example.notes/search_suggest_query"路径匹配码为URI_SEARCH_SUGGEST
// SearchManager.SUGGEST_URI_PATH_QUERY是Android系统定义的用于搜索建议的URI路径
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
// 添加URI匹配规则匹配"content://com.example.notes/search_suggest_query/*"路径匹配码为URI_SEARCH_SUGGEST
// 其中"*"表示任意字符串用于匹配带有查询参数的搜索建议URI
=======
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}

@ -14,185 +14,88 @@
* limitations under the License.
*/
java
// 导入所需的包
package net.micode.notes.gtask.data;
package net.micode.notes.gtask.data;
import android.database.Cursor;
import org.json.JSONObject;
import android.database.Cursor; // 用于数据库查询结果的游标
public abstract class Node {
public static final int SYNC_ACTION_NONE = 0;
import org.json.JSONObject; // JSON对象类
public static final int SYNC_ACTION_ADD_REMOTE = 1;
public static final int SYNC_ACTION_ADD_LOCAL = 2;
// 定义一个抽象类Node它代表了一个节点如任务、笔记等的基本属性和行为
public static final int SYNC_ACTION_DEL_REMOTE = 3;
public abstract class Node {
public static final int SYNC_ACTION_DEL_LOCAL = 4;
// 定义同步操作的常量,用于指示节点的同步状态或要执行的同步动作
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
public static final int SYNC_ACTION_NONE = 0; // 无操作
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
public static final int SYNC_ACTION_ADD_REMOTE = 1; // 在远程添加
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
public static final int SYNC_ACTION_ADD_LOCAL = 2; // 在本地添加
public static final int SYNC_ACTION_ERROR = 8;
public static final int SYNC_ACTION_DEL_REMOTE = 3; // 在远程删除
private String mGid;
public static final int SYNC_ACTION_DEL_LOCAL = 4; // 在本地删除
private String mName;
public static final int SYNC_ACTION_UPDATE_REMOTE = 5; // 更新远程节点
private long mLastModified;
public static final int SYNC_ACTION_UPDATE_LOCAL = 6; // 更新本地节点
private boolean mDeleted;
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; // 更新冲突
public Node() {
mGid = null;
mName = "";
mLastModified = 0;
mDeleted = false;
}
public static final int SYNC_ACTION_ERROR = 8; // 同步错误
public abstract JSONObject getCreateAction(int actionId);
public abstract JSONObject getUpdateAction(int actionId);
// 定义节点的私有属性
public abstract void setContentByRemoteJSON(JSONObject js);
private String mGid; // 节点的全局唯一标识符
public abstract void setContentByLocalJSON(JSONObject js);
private String mName; // 节点的名称
public abstract JSONObject getLocalJSONFromContent();
private long mLastModified; // 节点最后修改的时间戳
public abstract int getSyncAction(Cursor c);
private boolean mDeleted; // 节点是否被删除的标志
public void setGid(String gid) {
this.mGid = gid;
}
public void setName(String name) {
this.mName = name;
}
// 构造方法,初始化节点的属性
public void setLastModified(long lastModified) {
this.mLastModified = lastModified;
}
public Node() {
public void setDeleted(boolean deleted) {
this.mDeleted = deleted;
}
mGid = null; // GID初始化为null表示新创建的节点还没有GID
public String getGid() {
return this.mGid;
}
mName = ""; // 名称初始化为空字符串
public String getName() {
return this.mName;
}
mLastModified = 0; // 最后修改时间初始化为0表示节点还没有被修改过
public long getLastModified() {
return this.mLastModified;
}
mDeleted = false; // 删除标志初始化为false表示节点没有被删除
public boolean getDeleted() {
return this.mDeleted;
}
}
// 抽象方法用于获取创建节点的动作JSON对象具体实现由子类提供
public abstract JSONObject getCreateAction(int actionId);
// 抽象方法用于获取更新节点的动作JSON对象具体实现由子类提供
public abstract JSONObject getUpdateAction(int actionId);
// 抽象方法用于从远程JSON对象设置节点的内容具体实现由子类提供
public abstract void setContentByRemoteJSON(JSONObject js);
// 抽象方法用于从本地JSON对象设置节点的内容具体实现由子类提供
public abstract void setContentByLocalJSON(JSONObject js);
// 抽象方法用于将节点的内容转换为本地JSON对象具体实现由子类提供
public abstract JSONObject getLocalJSONFromContent();
// 抽象方法,用于根据数据库游标获取节点的同步动作,具体实现由子类提供
public abstract int getSyncAction(Cursor c);
// 设置节点的GID
public void setGid(String gid) {
this.mGid = gid;
}
// 获取节点的GID
public String getGid() {
return this.mGid;
}
// 设置节点的名称
public void setName(String name) {
this.mName = name;
}
// 获取节点的名称
public String getName() {
return this.mName;
}
// 设置节点最后修改的时间戳
public void setLastModified(long lastModified) {
this.mLastModified = lastModified;
}
// 获取节点最后修改的时间戳
public long getLastModified() {
return this.mLastModified;
}
// 设置节点是否被删除的标志
public void setDeleted(boolean deleted) {
this.mDeleted = deleted;
}
// 获取节点是否被删除的标志
public boolean getDeleted() {
return this.mDeleted;
}
}
}

@ -14,134 +14,176 @@
* limitations under the License.
*/
java
// 导入所需的包
package net.micode.notes.gtask.data;
// ...(省略了导入语句,因为它们在前面的代码段中已给出)
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
import net.micode.notes.gtask.exception.ActionFailureException;
// SqlData类用于处理与数据库中的特定数据项相关的操作
public class SqlData {
// 定义日志标签,用于调试和日志记录
private static final String TAG = SqlData.class.getSimpleName();
// 定义一个无效ID常量用于标识未设置或无效的ID
private static final int INVALID_ID = -99999;
// 定义要从数据库中检索的列的投影数组
public static final String[] PROJECTION_DATA = {
// ...(省略了列名,它们在前面的代码段中已给出)
};
// 定义投影数组中各列的索引以便在Cursor中快速访问
// ...(省略了列索引常量,它们在前面的代码段中已给出)
// 私有成员变量,用于存储与数据项相关的状态和信息
// ...(省略了成员变量声明,它们在前面的代码段中已给出)
// 构造方法用于创建新的SqlData实例
// 当isCreate为true时表示这是一个新创建的数据项否则它表示一个已存在的数据项
public SqlData(Context context) {
// ...(省略了构造方法体,它已经在前面的代码段中给出)
}
// 构造方法用于从Cursor加载已存在的数据项
public SqlData(Context context, Cursor c) {
// ...(省略了构造方法体,它已经在前面的代码段中给出)
}
// 私有方法用于从Cursor加载数据到成员变量中
private void loadFromCursor(Cursor c) {
// ...(省略了方法体,它已经在前面的代码段中给出)
}
// 公共方法用于设置数据项的内容根据传入的JSONObject更新成员变量和差异数据
public void setContent(JSONObject js) throws JSONException {
// ...(省略了方法体,但保留了注释说明)
// 这个方法首先检查JSONObject中是否包含特定的键并根据这些键的值更新成员变量和差异数据mDiffDataValues
}
// 公共方法用于获取数据项的内容并将其作为JSONObject返回
public JSONObject getContent() throws JSONException {
// ...(省略了方法体,但保留了注释说明)
// 如果这是一个新创建的数据项尚未插入数据库则记录错误并返回null
// 否则将数据项的内容构建为JSONObject并返回
}
// 公共方法,用于提交数据项到数据库
// 如果isCreate为true则插入新数据项否则更新现有数据项
public void commit(long noteId, boolean validateVersion, long version) {
// ...(省略了方法体,但保留了注释说明)
// 这个方法根据isCreate的值决定是插入新数据项还是更新现有数据项
// 如果validateVersion为true则在更新时检查版本以避免冲突
// 提交成功后清除差异数据并将isCreate设置为false
}
// 公共方法用于获取数据项的ID
public long getId() {
return mDataId;
}
}
import org.json.JSONException;
import org.json.JSONObject;
public class SqlData {
private static final String TAG = SqlData.class.getSimpleName();
private static final int INVALID_ID = -99999;
public static final String[] PROJECTION_DATA = new String[] {
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
DataColumns.DATA3
};
public static final int DATA_ID_COLUMN = 0;
public static final int DATA_MIME_TYPE_COLUMN = 1;
public static final int DATA_CONTENT_COLUMN = 2;
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
private ContentResolver mContentResolver;
private boolean mIsCreate;
private long mDataId;
private String mDataMimeType;
private String mDataContent;
private long mDataContentData1;
private String mDataContentData3;
private ContentValues mDiffDataValues;
public SqlData(Context context) {
mContentResolver = context.getContentResolver();
mIsCreate = true;
mDataId = INVALID_ID;
mDataMimeType = DataConstants.NOTE;
mDataContent = "";
mDataContentData1 = 0;
mDataContentData3 = "";
mDiffDataValues = new ContentValues();
}
public SqlData(Context context, Cursor c) {
mContentResolver = context.getContentResolver();
mIsCreate = false;
loadFromCursor(c);
mDiffDataValues = new ContentValues();
}
private void loadFromCursor(Cursor c) {
mDataId = c.getLong(DATA_ID_COLUMN);
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
mDataContent = c.getString(DATA_CONTENT_COLUMN);
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
public void setContent(JSONObject js) throws JSONException {
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
if (mIsCreate || mDataId != dataId) {
mDiffDataValues.put(DataColumns.ID, dataId);
}
mDataId = dataId;
String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)
: DataConstants.NOTE;
if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {
mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);
}
mDataMimeType = dataMimeType;
String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";
if (mIsCreate || !mDataContent.equals(dataContent)) {
mDiffDataValues.put(DataColumns.CONTENT, dataContent);
}
mDataContent = dataContent;
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;
if (mIsCreate || mDataContentData1 != dataContentData1) {
mDiffDataValues.put(DataColumns.DATA1, dataContentData1);
}
mDataContentData1 = dataContentData1;
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
}
mDataContentData3 = dataContentData3;
}
public JSONObject getContent() throws JSONException {
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
JSONObject js = new JSONObject();
js.put(DataColumns.ID, mDataId);
js.put(DataColumns.MIME_TYPE, mDataMimeType);
js.put(DataColumns.CONTENT, mDataContent);
js.put(DataColumns.DATA1, mDataContentData1);
js.put(DataColumns.DATA3, mDataContentData3);
return js;
}
public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) {
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
try {
mDataId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
} else {
if (mDiffDataValues.size() > 0) {
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
} else {
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.VERSION + "=?)", new String[] {
String.valueOf(noteId), String.valueOf(version)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
}
mDiffDataValues.clear();
mIsCreate = false;
}
public long getId() {
return mDataId;
}
}

@ -14,6 +14,208 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名此处在net.micode.notes.ui包下
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
// AlarmAlertActivity类继承自Activity类同时实现了OnClickListener和OnDismissListener接口
// 意味着该类需要处理点击事件和对话框关闭事件
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 用于存储笔记的ID可能对应某个提醒关联的笔记
private long mNoteId;
// 用于存储笔记内容的摘要信息,长度有限制
private String mSnippet;
// 定义摘要的最大长度为60个字符
private static final int SNIPPET_PREW_MAX_LEN = 60;
// MediaPlayer对象用于播放提醒的声音
MediaPlayer mPlayer;
// 重写Activity的onCreate方法该方法在Activity创建时被调用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求去除Activity的标题栏使界面更简洁如果支持该特性的话
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前Activity的窗口对象
final Window win = getWindow();
// 设置窗口属性,使其在屏幕锁定时也能显示
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);
}
// 获取启动该Activity的Intent对象可从中获取传递的数据等信息
Intent intent = getIntent();
try {
// 从Intent携带的数据可能是Uri格式中解析出笔记的ID具体是取路径段中的第二个元素索引为1
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过DataUtils工具类根据笔记ID获取笔记的摘要内容
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;
}
// 创建一个MediaPlayer实例用于后续播放提醒声音
mPlayer = new MediaPlayer();
// 通过DataUtils工具类判断该笔记在数据库中是否可见是否有效等情况
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 如果笔记有效,显示操作对话框,供用户进行相关操作
showActionDialog();
// 播放提醒声音
playAlarmSound();
} else {
// 如果笔记无效直接结束该Activity
finish();
}
}
// 方法用于判断屏幕是否处于开启状态
private boolean isScreenOn() {
// 获取系统的电源管理服务对象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 调用其isScreenOn方法来判断屏幕状态并返回结果
return pm.isScreenOn();
}
// 方法用于播放提醒声音
private void playAlarmSound() {
// 通过RingtoneManager获取系统默认的闹钟铃声的Uri
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 获取系统设置中关于静音模式影响的音频流相关设置值
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
// 判断静音模式设置中是否影响闹钟音频流
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) {
// 如果影响设置MediaPlayer的音频流类型为对应的静音模式设置值
mPlayer.setAudioStreamType(silentModeStreams);
} else {
// 如果不影响,设置为正常的闹钟音频流类型
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为获取到的铃声Uri
mPlayer.setDataSource(this, url);
// 准备MediaPlayer使其进入可播放状态可能会涉及加载音频资源等操作
mPlayer.prepare();
// 设置为循环播放,以持续提醒
mPlayer.setLooping(true);
// 开始播放声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// 如果参数设置出现异常,打印异常堆栈信息
e.printStackTrace();
} catch (SecurityException e) {
// 如果出现安全相关异常,打印异常堆栈信息
e.printStackTrace();
} catch (IllegalStateException e) {
// 如果MediaPlayer状态出现异常打印异常堆栈信息
e.printStackTrace();
} catch (IOException e) {
// 如果在读取数据源等IO操作出现异常打印异常堆栈信息
e.printStackTrace();
}
}
// 方法用于创建并显示操作对话框,给用户提供相应的操作选项
private void showActionDialog() {
// 创建一个AlertDialog的构建器对象用于构建对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框的标题,这里使用了字符串资源中的应用名称
dialog.setTitle(R.string.app_name);
// 设置对话框的消息内容,显示之前获取的笔记摘要信息
dialog.setMessage(mSnippet);
// 设置对话框的确定按钮文本以及点击监听器实现了OnClickListener接口
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态,设置对话框的进入按钮文本以及点击监听器
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 构建并显示对话框并设置对话框关闭监听器实现了OnDismissListener接口
dialog.show().setOnDismissListener(this);
}
// 实现OnClickListener接口的方法处理对话框按钮点击事件
public void onClick(DialogInterface dialog, int which) {
switch (which) {
// 如果点击的是对话框的否定按钮(比如进入按钮等,根据具体设置而定)
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个Intent用于启动NoteEditActivity可能是进入笔记编辑界面
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置Intent的动作这里是查看动作
intent.setAction(Intent.ACTION_VIEW);
// 将笔记的ID通过Intent的额外数据传递给目标Activity
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动目标Activity
startActivity(intent);
break;
default:
break;
}
}
// 实现OnDismissListener接口的方法当对话框关闭时被调用
public void onDismiss(DialogInterface dialog) {
// 停止播放提醒声音,并释放相关资源
stopAlarmSound();
// 结束当前Activity
finish();
}
// 方法用于停止播放声音释放MediaPlayer资源
private void stopAlarmSound() {
if (mPlayer!= null) {
// 停止播放声音
mPlayer.stop();
// 释放MediaPlayer资源使其处于空闲状态
mPlayer.release();
// 将mPlayer对象置为null避免后续误操作
mPlayer = null;
}
}
}
=======
package net.micode.notes.tool;
import android.content.Context;
@ -342,3 +544,4 @@ public class BackupUtils {
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,6 +14,10 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.tool;
import android.content.ContentProviderOperation;
@ -34,19 +38,56 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
<<<<<<< HEAD
// DataUtils工具类提供了一系列用于操作笔记数据相关的实用方法
public class DataUtils {
// 用于日志记录的标签,方便在日志中识别该类输出的相关信息
public static final String TAG = "DataUtils";
// 批量删除笔记的方法根据给定的ContentResolver和要删除笔记的ID集合来执行删除操作
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 如果传入的ID集合为null记录日志并返回true可能表示无需进行删除操作的情况
=======
public class DataUtils {
public static final String TAG = "DataUtils";
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
<<<<<<< HEAD
// 如果ID集合为空记录日志并返回true表示没有实际要删除的笔记
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
<<<<<<< HEAD
// 创建一个用于存储内容提供器操作的列表,后续将批量执行这些操作来删除笔记
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历要删除的笔记ID集合
for (long id : ids) {
// 不允许删除系统根文件夹根据特定的ID判断如果是根文件夹则记录错误日志并跳过本次删除操作
if (id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
// 创建一个用于删除指定笔记的ContentProviderOperation构建器指定要删除的笔记的Uri
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 将构建好的操作添加到操作列表中
operationList.add(builder.build());
}
try {
// 通过ContentResolver批量执行操作列表中的删除操作并获取操作结果数组
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 如果结果数组为null或者长度为0或者第一个结果为null表示删除笔记失败记录日志并返回false
=======
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
@ -59,69 +100,156 @@ public class DataUtils {
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
<<<<<<< HEAD
// 如果成功执行删除操作返回true
return true;
} catch (RemoteException e) {
// 如果出现远程异常(比如与内容提供器通信出现问题等),记录详细的异常信息到日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在应用操作过程中出现异常(比如操作不符合要求等),记录详细的异常信息到日志
=======
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
<<<<<<< HEAD
// 将指定笔记移动到指定文件夹的方法,通过更新笔记的相关字段来实现移动操作
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
// 创建一个ContentValues对象用于存储要更新的列和对应的值
ContentValues values = new ContentValues();
// 设置笔记的新父文件夹ID目标文件夹ID
values.put(NoteColumns.PARENT_ID, desFolderId);
// 设置笔记的原始父文件夹ID源文件夹ID可能用于记录移动历史等用途
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
// 设置本地修改标志,可能表示该笔记在本地有过修改操作
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 通过ContentResolver执行更新操作根据笔记的ID更新对应的记录
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
// 批量将多个笔记移动到指定文件夹的方法,与单个笔记移动类似,不过是批量操作
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 如果传入的笔记ID集合为null记录日志并返回true可能表示无需进行移动操作的情况
=======
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);
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
<<<<<<< HEAD
// 创建一个用于存储内容提供器操作的列表,后续将批量执行这些操作来移动笔记
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历要移动的笔记ID集合
for (long id : ids) {
// 创建一个用于更新指定笔记的ContentProviderOperation构建器指定要更新的笔记的Uri
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 设置笔记的新父文件夹ID目标文件夹ID
builder.withValue(NoteColumns.PARENT_ID, folderId);
// 设置本地修改标志,可能表示该笔记在本地有过修改操作
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
// 将构建好的操作添加到操作列表中
=======
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
operationList.add(builder.build());
}
try {
<<<<<<< HEAD
// 通过ContentResolver批量执行操作列表中的更新操作并获取操作结果数组
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 如果结果数组为null或者长度为0或者第一个结果为null表示移动笔记失败记录日志并返回false
=======
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
<<<<<<< HEAD
// 如果成功执行移动操作返回true
return true;
} catch (RemoteException e) {
// 如果出现远程异常(比如与内容提供器通信出现问题等),记录详细的异常信息到日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在应用操作过程中出现异常(比如操作不符合要求等),记录详细的异常信息到日志
=======
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
<<<<<<< HEAD
* Notes#TYPE_SYSTEM
*/
public static int getUserFolderCount(ContentResolver resolver) {
// 通过ContentResolver查询笔记内容提供器统计符合条件的文件夹数量
// 查询的列只需要计数结果,所以使用"COUNT(*)"
// 查询条件是类型为文件夹NoteColumns.TYPE对应的是文件夹类型且父文件夹ID不是回收站文件夹ID
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
=======
* 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,
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 将游标移动到第一条记录(因为只有一条计数结果记录)
if (cursor.moveToFirst()) {
try {
// 获取计数结果游标中的第一列数据并赋值给count变量
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
// 关闭游标,释放资源
=======
if(cursor != null) {
if(cursor.moveToFirst()) {
try {
@ -129,6 +257,7 @@ public class DataUtils {
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
}
@ -136,7 +265,13 @@ public class DataUtils {
return count;
}
<<<<<<< HEAD
// 判断指定笔记在笔记数据库中是否可见(根据特定条件,比如不在回收站等)的方法
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 通过ContentResolver查询指定笔记的记录查询条件是类型为指定类型且父文件夹ID不是回收站文件夹ID
=======
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
@ -144,34 +279,79 @@ public class DataUtils {
null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该笔记存在且符合可见条件设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 判断指定笔记在笔记数据库中是否存在的方法(简单查询是否有对应记录)
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 通过ContentResolver查询指定笔记的记录不设置额外的查询条件
=======
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该笔记存在设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 判断指定数据(可能是某种关联数据等)在数据数据库中是否存在的方法(简单查询是否有对应记录)
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 通过ContentResolver查询指定数据的记录不设置额外的查询条件
=======
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示该数据存在设置exist为true
}
cursor.close();
return exist;
}
// 检查指定名称的文件夹在数据库中是否可见(根据特定条件,如不在回收站且名称匹配等)的方法
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 通过ContentResolver查询符合条件的文件夹记录查询条件是类型为文件夹、父文件夹ID不是回收站文件夹ID且摘要可能是名称相关字段匹配指定名称
=======
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
@ -182,22 +362,39 @@ public class DataUtils {
}
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
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;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (cursor!= null) {
// 如果查询到的记录数量大于0表示存在符合条件的文件夹设置exist为true
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
=======
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return exist;
}
<<<<<<< HEAD
// 获取指定文件夹下所有笔记对应的小部件相关属性集合的方法(可能用于展示小部件相关信息等)
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 通过ContentResolver查询指定文件夹下笔记的小部件ID和小部件类型信息
=======
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
@ -205,45 +402,94 @@ public class DataUtils {
null);
HashSet<AppWidgetAttribute> set = null;
<<<<<<< HEAD
// 如果游标不为null表示查询到了结果
if (c!= null) {
// 将游标移动到第一条记录
=======
if (c != null) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
<<<<<<< HEAD
// 创建一个AppWidgetAttribute对象用于存储小部件相关属性
AppWidgetAttribute widget = new AppWidgetAttribute();
// 从游标中获取小部件ID并赋值给widget对象
widget.widgetId = c.getInt(0);
// 从游标中获取小部件类型并赋值给widget对象
widget.widgetType = c.getInt(1);
// 将widget对象添加到集合中
set.add(widget);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
=======
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
<<<<<<< HEAD
// 关闭游标,释放资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
c.close();
}
return set;
}
<<<<<<< HEAD
// 根据笔记ID获取对应的电话号码的方法可能是与笔记关联的电话号码
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 通过ContentResolver查询指定笔记关联的数据表可能是通话记录相关的数据表获取电话号码列的值
// 查询条件是笔记ID匹配且MIME类型匹配可能用于确定数据类型
=======
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
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);
<<<<<<< HEAD
if (cursor!= null && cursor.moveToFirst()) {
try {
// 如果查询到结果且游标移动到第一条记录,获取电话号码字符串并返回
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
// 关闭游标,释放资源
=======
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
}
return "";
}
<<<<<<< HEAD
// 根据电话号码和通话日期获取对应的笔记ID的方法可能用于反向查找关联笔记
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 通过ContentResolver查询指定数据表可能是通话记录相关的数据表获取笔记ID列的值
// 查询条件是通话日期匹配、MIME类型匹配且电话号码匹配通过自定义函数PHONE_NUMBERS_EQUAL判断
=======
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
@ -251,6 +497,19 @@ public class DataUtils {
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
<<<<<<< HEAD
if (cursor!= null) {
if (cursor.moveToFirst()) {
try {
// 如果查询到结果且游标移动到第一条记录获取笔记ID并返回
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
// 如果获取数据出现越界异常,记录详细的异常信息到日志
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
// 关闭游标,释放资源
=======
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
@ -259,11 +518,15 @@ public class DataUtils {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
cursor.close();
}
return 0;
}
<<<<<<< HEAD
// 根据笔记ID获取对应的摘要信息的方法
=======
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
@ -293,3 +556,4 @@ public class DataUtils {
return snippet;
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,6 +14,195 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
package net.micode.notes.tool;
// GTaskStringUtils类从名称推测可能是用于处理与GTask相关的字符串操作的工具类
// 目前类中主要定义了一系列的字符串常量,方便在整个项目中统一使用这些特定的字符串标识
public class GTaskStringUtils {
// 表示GTask JSON数据中"action_id"字段对应的字符串常量可能用于在解析或构建GTask相关JSON数据时
// 作为键来获取或设置对应的值,代表某个操作的唯一标识符
public final static String GTASK_JSON_ACTION_ID = "action_id";
// 表示GTask JSON数据中"action_list"字段对应的字符串常量,可能用于表示操作相关的列表信息,
// 比如涉及到的任务列表等情况
public final static String GTASK_JSON_ACTION_LIST = "action_list";
// 表示GTask JSON数据中"action_type"字段对应的字符串常量,用于表示操作的类型,
// 后续有定义具体的操作类型值(如创建、获取全部等)
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// 表示GTask JSON数据中"action_type"字段具体取值为"create"时对应的字符串常量,
// 意味着对应的操作是创建类型的操作,比如创建任务、创建列表等
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
// 表示GTask JSON数据中"action_type"字段具体取值为"get_all"时对应的字符串常量,
// 意味着对应的操作是获取全部相关内容的操作,比如获取全部任务、获取全部列表等
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
// 表示GTask JSON数据中"action_type"字段具体取值为"move"时对应的字符串常量,
// 意味着对应的操作是移动相关内容的操作,比如移动任务到其他列表等情况
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
// 表示GTask JSON数据中"action_type"字段具体取值为"update"时对应的字符串常量,
// 意味着对应的操作是更新相关内容的操作,比如更新任务的属性等情况
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// 表示GTask JSON数据中"creator_id"字段对应的字符串常量,可能用于标识执行某个操作或者创建某个对象的用户的唯一标识符
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
// 表示GTask JSON数据中"child_entity"字段对应的字符串常量,可能用于表示某个实体(如任务、列表等)的子实体相关信息
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
// 表示GTask JSON数据中"client_version"字段对应的字符串常量,可能用于记录客户端的版本信息,
// 比如在与服务器交互等场景下,标识客户端的版本情况
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// 表示GTask JSON数据中"completed"字段对应的字符串常量,可能用于表示某个任务是否已完成的状态信息,
// 比如布尔值对应的字符串表示("true"或"false"等情况)
public final static String GTASK_JSON_COMPLETED = "completed";
// 表示GTask JSON数据中"current_list_id"字段对应的字符串常量,可能用于标识当前所在的任务列表的唯一标识符,
// 比如当前操作关联的是哪个具体的任务列表
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
// 表示GTask JSON数据中"default_list_id"字段对应的字符串常量,可能用于标识默认任务列表的唯一标识符,
// 例如新创建任务默认归属的列表等情况
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
// 表示GTask JSON数据中"deleted"字段对应的字符串常量,可能用于表示某个对象(如任务、列表等)是否已被删除的状态信息
public final static String GTASK_JSON_DELETED = "deleted";
// 表示GTask JSON数据中"dest_list"字段对应的字符串常量,可能用于在移动等操作中,表示目标任务列表相关信息,
// 比如移动任务到的目标列表等情况
public final static String GTASK_JSON_DEST_LIST = "dest_list";
// 表示GTask JSON数据中"dest_parent"字段对应的字符串常量,可能用于在移动或关联等操作中,
// 表示目标父对象(如任务的父任务、列表的父列表等)相关信息
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
// 表示GTask JSON数据中"dest_parent_type"字段对应的字符串常量,可能用于明确目标父对象的类型,
// 比如是任务类型还是列表类型等情况
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
// 表示GTask JSON数据中"entity_delta"字段对应的字符串常量,可能用于表示实体(如任务、列表等)的变化量相关信息,
// 比如属性的变化差值等情况,常用于更新操作的相关逻辑中
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
// 表示GTask JSON数据中"entity_type"字段对应的字符串常量,可能用于明确实体的类型,
// 比如是任务、列表还是其他自定义类型等情况
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
// 表示GTask JSON数据中"get_deleted"字段对应的字符串常量,可能用于获取已删除对象相关信息的操作标识,
// 比如请求获取已删除的任务、列表等情况
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
// 表示GTask JSON数据中"id"字段对应的字符串常量,通常用于表示各种对象(任务、列表、用户等)的唯一标识符
public final static String GTASK_JSON_ID = "id";
// 表示GTask JSON数据中"index"字段对应的字符串常量,可能用于表示某个对象在列表中的顺序索引等情况,
// 比如任务在任务列表中的排列顺序等
public final static String GTASK_JSON_INDEX = "index";
// 表示GTask JSON数据中"last_modified"字段对应的字符串常量,可能用于记录对象最后一次被修改的时间戳等信息,
// 方便进行数据同步、版本控制等操作
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
// 表示GTask JSON数据中"latest_sync_point"字段对应的字符串常量,可能用于标识最近一次数据同步的关键点信息,
// 比如同步的时间、版本号等情况,用于确保数据在不同端的一致性
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
// 表示GTask JSON数据中"list_id"字段对应的字符串常量,通常用于表示任务列表的唯一标识符,
// 在很多涉及任务与列表关联的操作中会用到
public final static String GTASK_JSON_LIST_ID = "list_id";
// 表示GTask JSON数据中"lists"字段对应的字符串常量,可能用于表示一组任务列表相关的数据结构,
// 比如包含多个任务列表的集合等情况
public final static String GTASK_JSON_LISTS = "lists";
// 表示GTask JSON数据中"name"字段对应的字符串常量,通常用于表示对象(任务、列表、用户等)的名称信息,
// 方便展示和区分不同的对象
public final static String GTASK_JSON_NAME = "name";
// 表示GTask JSON数据中"new_id"字段对应的字符串常量,可能用于在某些操作(如复制、移动后重新生成标识符等情况)下,
// 表示新生成的对象的唯一标识符
public final static String GTASK_JSON_NEW_ID = "new_id";
// 表示GTask JSON数据中"notes"字段对应的字符串常量,可能用于表示与任务、列表等相关的备注信息,
// 比如对任务的详细描述、对列表用途的说明等情况
public final static String GTASK_JSON_NOTES = "notes";
// 表示GTask JSON数据中"parent_id"字段对应的字符串常量,通常用于表示某个对象(如任务、列表等)的父对象的唯一标识符,
// 用于构建对象之间的层级关系等情况
public final static String GTASK_JSON_PARENT_ID = "parent_id";
// 表示GTask JSON数据中"prior_sibling_id"字段对应的字符串常量,可能用于表示在同一层级中,
// 当前对象之前的兄弟对象的唯一标识符,比如在任务列表中某个任务之前的相邻任务的标识等情况
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
// 表示GTask JSON数据中"results"字段对应的字符串常量,可能用于存储操作后的结果信息,
// 比如执行某个任务操作后返回的结果集等情况
public final static String GTASK_JSON_RESULTS = "results";
// 表示GTask JSON数据中"source_list"字段对应的字符串常量,可能用于在移动、复制等操作中,
// 表示源任务列表相关信息,即操作对象原来所在的列表等情况
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
// 表示GTask JSON数据中"tasks"字段对应的字符串常量,通常用于表示一组任务相关的数据结构,
// 比如包含多个任务的集合等情况
public final static String GTASK_JSON_TASKS = "tasks";
// 表示GTask JSON数据中"type"字段对应的字符串常量,用于表示对象的类型,
// 后续有定义具体的类型值(如任务、组等情况)
public final static String GTASK_JSON_TYPE = "type";
// 表示GTask JSON数据中"type"字段具体取值为"GROUP"时对应的字符串常量,意味着对应的对象类型是组,
// 比如任务分组等情况,用于区分不同类型的对象
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
// 表示GTask JSON数据中"type"字段具体取值为"TASK"时对应的字符串常量,意味着对应的对象类型是任务,
// 是最常见的操作对象类型之一
public final static String GTASK_JSON_TYPE_TASK = "TASK";
// 表示GTask JSON数据中"user"字段对应的字符串常量,可能用于表示与用户相关的信息,
// 比如操作的执行者、任务的创建者等用户相关情况
public final static String GTASK_JSON_USER = "user";
// 表示特定的文件夹前缀字符串常量可能用于标识是MIUI笔记相关的文件夹
// 在文件系统或者数据存储中方便区分不同来源、用途的文件夹
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
// 表示默认文件夹名称的字符串常量,可能用于在没有指定具体文件夹时,
// 作为默认的文件夹名称使用,比如新创建的笔记、任务等默认归属的文件夹
public final static String FOLDER_DEFAULT = "Default";
// 表示通话笔记文件夹名称的字符串常量,可能专门用于存放与通话记录相关的笔记内容,
// 方便对这类特定类型的笔记进行分类管理
public final static String FOLDER_CALL_NOTE = "Call_Note";
// 表示元数据文件夹名称的字符串常量,可能用于存放各种元数据相关的文件或信息,
// 比如数据的描述信息、配置信息等情况
public final static String FOLDER_META = "METADATA";
// 表示元数据中GTask ID头部信息的字符串常量可能用于在元数据结构中
// 标识GTask相关的唯一标识符所在的字段等情况
public final static String META_HEAD_GTASK_ID = "meta_gid";
// 表示元数据中笔记相关头部信息的字符串常量,可能用于在元数据结构中,
// 标识笔记相关内容所在的字段等情况
public final static String META_HEAD_NOTE = "meta_note";
// 表示元数据中数据相关头部信息的字符串常量,可能用于在元数据结构中,
// 标识其他通用数据相关内容所在的字段等情况
public final static String META_HEAD_DATA = "meta_data";
// 表示元数据中笔记名称相关的特定字符串常量,通常用于给出一个提示信息,
// 告知不要对这类特殊的元数据笔记进行更新和删除操作等情况
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
}
=======
package net.micode.notes.tool;
public class GTaskStringUtils {
@ -111,3 +300,4 @@ public class GTaskStringUtils {
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,6 +14,10 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.tool
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.tool;
import android.content.Context;
@ -22,6 +26,51 @@ import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
<<<<<<< HEAD
// ResourceParser类从名称来看是一个资源解析相关的工具类用于获取和处理各种与界面显示相关的资源如颜色、背景图片、字体样式等资源的相关操作
public class ResourceParser {
// 定义颜色相关的常量,用整数来表示不同的颜色选项,这里分别对应黄色、蓝色、白色、绿色、红色,方便后续根据这些常量来引用对应的颜色资源或进行相关逻辑判断
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
// 定义默认的背景颜色常量其值为YELLOW即黄色表示在没有特殊设置的情况下默认使用的背景颜色对应的标识
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;
public static final int TEXT_SUPER = 3;
// 定义默认的字体大小常量其值为TEXT_MEDIUM即中等字体大小表示在没有特殊设置的情况下默认使用的字体大小对应的标识
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
// 内部静态类NoteBgResources用于管理笔记编辑相关的背景资源如背景图片资源等将相关资源的获取逻辑封装在内部类中使代码结构更清晰
public static class NoteBgResources {
// 定义一个私有的静态整数数组用于存储笔记编辑页面背景的资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的编辑页面背景图片资源
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
};
// 定义一个私有的静态整数数组用于存储笔记编辑页面标题背景的资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的编辑页面标题背景图片资源
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
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取笔记编辑页面的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
public class ResourceParser {
public static final int YELLOW = 0;
@ -56,24 +105,82 @@ public class ResourceParser {
R.drawable.edit_title_red
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取笔记编辑页面标题的背景图片资源ID方便在代码中设置对应的标题背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
<<<<<<< HEAD
// 根据给定的上下文Context获取默认的背景颜色标识ID判断逻辑基于用户在偏好设置Preference中是否设置了特定的背景颜色相关选项
public static int getDefaultBgId(Context context) {
// 通过PreferenceManager获取默认的共享偏好设置对象然后检查是否设置了特定的背景颜色设置键对应的布尔值可能表示用户是否手动设置过背景颜色
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
// 如果用户设置过随机生成一个在背景资源数组长度范围内的索引值对应不同颜色的背景资源作为返回的背景颜色标识ID实现随机选择背景颜色的效果
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
// 如果用户没有设置过返回默认的背景颜色标识ID即前面定义的BG_DEFAULT_COLOR
=======
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
return BG_DEFAULT_COLOR;
}
}
<<<<<<< HEAD
// 内部静态类NoteItemBgResources用于管理笔记列表项相关的背景资源如不同位置的列表项背景图片资源等同样将相关资源的获取逻辑封装在内部类中便于代码的组织和维护
public static class NoteItemBgResources {
// 定义一个私有的静态整数数组用于存储列表项第一个元素比如列表中第一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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
};
// 定义一个私有的静态整数数组用于存储列表项中间元素非第一个和最后一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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
};
// 定义一个私有的静态整数数组用于存储列表项最后一个元素比如列表中最后一个笔记项的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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,
};
// 定义一个私有的静态整数数组用于存储列表项单独存在比如列表中只有一个笔记项时的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项第一个元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
public static class NoteItemBgResources {
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
@ -107,27 +214,58 @@ public class ResourceParser {
R.drawable.list_red_single
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项最后一个元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项单独存在时的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
<<<<<<< HEAD
// 根据传入的颜色标识ID对应前面定义的颜色常量获取列表项中间元素的背景图片资源ID方便在代码中设置对应的背景图片资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
<<<<<<< HEAD
// 获取文件夹的背景图片资源ID这里固定返回一个表示文件夹背景图片的资源ID可能所有文件夹使用相同的背景图片样式
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
<<<<<<< HEAD
// 内部静态类WidgetBgResources用于管理小部件相关的背景资源如不同尺寸小部件的背景图片资源等将相关资源的获取逻辑封装在内部类中以更好地组织代码结构
public static class WidgetBgResources {
// 定义一个私有的静态整数数组用于存储2倍尺寸小部件的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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,
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取2倍尺寸小部件的背景图片资源ID方便在代码中设置对应的小部件背景图片资源
=======
public static class WidgetBgResources {
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
@ -137,10 +275,23 @@ public class ResourceParser {
R.drawable.widget_2x_red,
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
<<<<<<< HEAD
// 定义一个私有的静态整数数组用于存储4倍尺寸小部件的背景图片资源ID每个元素对应一种颜色顺序与前面定义的颜色常量对应的对应背景图片资源
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
};
// 根据传入的颜色标识ID对应前面定义的颜色常量获取4倍尺寸小部件的背景图片资源ID方便在代码中设置对应的小部件背景图片资源
=======
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
@ -149,11 +300,25 @@ public class ResourceParser {
R.drawable.widget_4x_red
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
<<<<<<< HEAD
// 内部静态类TextAppearanceResources用于管理文本外观相关的资源如不同字体大小对应的样式资源等将相关资源的获取逻辑封装在内部类中使代码结构更清晰易读
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
};
// 根据传入的字体大小标识ID对应前面定义的字体大小常量获取对应的文本外观样式资源ID同时处理了ID超出资源数组长度的情况可能是由于数据异常等原因此时返回默认的字体大小对应的资源ID
=======
public static class TextAppearanceResources {
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
@ -162,6 +327,7 @@ public class ResourceParser {
R.style.TextAppearanceSuper
};
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
@ -174,8 +340,16 @@ public class ResourceParser {
return TEXTAPPEARANCE_RESOURCES[id];
}
<<<<<<< HEAD
// 获取文本外观样式资源数组的长度可用于判断传入的字体大小标识ID是否越界等情况也便于在其他相关逻辑中了解资源的数量情况
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,6 +14,10 @@
* limitations under the License.
*/
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.ui包通常用于存放与用户界面相关的类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.ui;
import android.app.Activity;
@ -39,6 +43,37 @@ import net.micode.notes.tool.DataUtils;
import java.io.IOException;
<<<<<<< HEAD
// AlarmAlertActivity类继承自Activity类意味着它是一个安卓中的Activity用于展示用户界面。
// 同时实现了OnClickListener和OnDismissListener接口用于处理对话框的点击和关闭事件
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 用于存储与提醒相关联的笔记的ID可能后续用于查询、操作该笔记的相关信息
private long mNoteId;
// 用于存储笔记的摘要信息,将展示在提醒界面等地方给用户查看
private String mSnippet;
// 定义摘要信息的最大长度为60个字符超过这个长度会进行截断处理
private static final int SNIPPET_PREW_MAX_LEN = 60;
// MediaPlayer对象用于播放提醒的声音比如闹钟铃声等
MediaPlayer mPlayer;
// 重写Activity的onCreate方法该方法在Activity被创建时调用用于进行初始化等相关操作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求去除Activity的标题栏使界面更加简洁仅在支持该特性的安卓版本和设备上生效
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前Activity的窗口对象后续用于设置窗口的各种属性
final Window win = getWindow();
// 设置窗口属性,使窗口在屏幕锁定时也能够显示出来,方便用户看到提醒信息
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
// 调用isScreenOn方法判断屏幕是否处于开启状态如果屏幕未开启
if (!isScreenOn()) {
// 给窗口添加一系列标志位,用于确保屏幕在必要时亮起、保持常亮,并且允许在屏幕锁定时进行相关操作,
// 同时设置布局相关的装饰属性,以保证界面能正确显示
=======
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
@ -55,12 +90,30 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn()) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
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);
}
<<<<<<< HEAD
// 获取启动该Activity的Intent对象Intent中可能携带了与提醒相关的数据比如笔记的相关信息等
Intent intent = getIntent();
try {
// 从Intent携带的数据中解析出笔记的ID具体是从Intent的数据Uri的路径段中获取第二个元素索引为1并转换为长整型
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过DataUtils工具类的方法根据笔记ID从内容提供器中获取笔记的摘要信息
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
// 如果获取到的摘要信息长度大于设定的最大长度SNIPPET_PREW_MAX_LEN
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
// 则进行截断处理截取前60个字符并添加一个特定的提示字符串从资源文件中获取否则保持原摘要信息不变
} catch (IllegalArgumentException e) {
// 如果在解析笔记ID或者获取摘要信息过程中出现参数异常比如格式不正确等原因打印异常堆栈信息并直接返回不再执行后续操作
=======
Intent intent = getIntent();
try {
@ -70,19 +123,82 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
} catch (IllegalArgumentException e) {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
e.printStackTrace();
return;
}
<<<<<<< HEAD
// 创建一个MediaPlayer实例用于后续播放提醒声音
mPlayer = new MediaPlayer();
// 通过DataUtils工具类判断该笔记在笔记数据库中是否可见可能涉及是否被删除、是否处于有效状态等条件判断
// 传入笔记ID以及笔记类型这里是普通笔记类型Notes.TYPE_NOTE进行判断
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 如果笔记可见即满足有效条件调用showActionDialog方法显示操作对话框供用户进行相应操作
showActionDialog();
// 调用playAlarmSound方法播放提醒声音
playAlarmSound();
} else {
// 如果笔记不可见直接结束当前Activity意味着不需要展示提醒相关的界面了
=======
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
} else {
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
finish();
}
}
<<<<<<< HEAD
// 私有方法,用于判断屏幕是否处于开启状态
private boolean isScreenOn() {
// 获取系统的电源管理服务对象,用于查询屏幕的电源状态等信息
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 调用电源管理服务对象的isScreenOn方法返回屏幕是否开启的布尔值结果
return pm.isScreenOn();
}
// 私有方法,用于播放提醒声音,比如播放闹钟铃声等
private void playAlarmSound() {
// 通过RingtoneManager获取系统默认的闹钟铃声的Uri用于指定MediaPlayer要播放的音频资源
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 从系统设置中获取静音模式影响的音频流相关设置值,用于判断在静音模式下闹钟声音的播放情况
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
// 判断静音模式设置中是否影响闹钟音频流如果影响对应位的值不为0
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) {
// 设置MediaPlayer的音频流类型为获取到的静音模式设置值按照系统静音模式的相关配置来播放声音
mPlayer.setAudioStreamType(silentModeStreams);
} else {
// 如果静音模式不影响闹钟音频流,设置为正常的闹钟音频流类型,确保闹钟声音正常播放
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为获取到的铃声Uri指定要播放的音频文件等资源
mPlayer.setDataSource(this, url);
// 准备MediaPlayer使其进入可播放状态这个过程可能涉及加载音频资源、初始化播放相关参数等操作
mPlayer.prepare();
// 设置MediaPlayer为循环播放模式使提醒声音能够持续播放直到用户手动停止或者其他相关逻辑结束播放
mPlayer.setLooping(true);
// 启动MediaPlayer开始播放提醒声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// 如果在设置数据源等过程中出现参数异常(比如参数不合法等原因),打印异常堆栈信息
e.printStackTrace();
} catch (SecurityException e) {
// 如果出现安全相关异常(比如没有权限访问音频资源等原因),打印异常堆栈信息
e.printStackTrace();
} catch (IllegalStateException e) {
// 如果MediaPlayer的状态出现异常比如在不恰当的状态下执行了某些操作等原因打印异常堆栈信息
e.printStackTrace();
} catch (IOException e) {
// 如果在读取数据源等IO操作过程中出现异常比如音频文件损坏、无法读取等原因打印异常堆栈信息
=======
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
@ -115,27 +231,63 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
e.printStackTrace();
}
}
<<<<<<< HEAD
// 私有方法,用于创建并显示操作对话框,给用户提供相应的操作选项,比如确认、进入编辑等操作
private void showActionDialog() {
// 创建一个AlertDialog的构建器对象用于方便地构建AlertDialog对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框的标题这里使用了字符串资源中的应用名称R.string.app_name对应的字符串作为标题
dialog.setTitle(R.string.app_name);
// 设置对话框的消息内容,展示之前获取到的笔记摘要信息,让用户了解是哪个笔记的提醒
dialog.setMessage(mSnippet);
// 设置对话框的确定按钮通常是一个积极的操作按钮文本以及点击监听器点击监听器使用当前类实现的OnClickListener接口
// 意味着点击该按钮会触发当前类中对应的onClick方法
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态设置对话框的另一个按钮可能是进入编辑等操作按钮文本以及点击监听器同样使用当前类实现的OnClickListener接口
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 构建并显示对话框同时设置对话框的关闭监听器使用当前类实现的OnDismissListener接口
// 意味着对话框关闭时会触发当前类中对应的onDismiss方法
dialog.show().setOnDismissListener(this);
}
// 实现OnClickListener接口的方法用于处理对话框按钮的点击事件根据点击的按钮不同执行相应的逻辑
public void onClick(DialogInterface dialog, int which) {
switch (which) {
// 如果点击的是对话框中的否定按钮(具体含义根据设置的文本而定,这里可能是进入编辑按钮等情况)
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个Intent用于启动NoteEditActivity可能是进入对应的笔记编辑界面方便用户对笔记进行编辑等操作
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置Intent的动作这里设置为查看ACTION_VIEW动作表明是查看相关的操作意图
intent.setAction(Intent.ACTION_VIEW);
// 通过Intent的额外数据传递机制将笔记的ID传递给目标ActivityNoteEditActivity以便目标Activity能获取并处理对应的笔记
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动目标Activity跳转到对应的笔记编辑界面
=======
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.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);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
startActivity(intent);
break;
default:
@ -143,6 +295,28 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
<<<<<<< HEAD
// 实现OnDismissListener接口的方法当对话框关闭时会被调用用于执行对话框关闭后的相关操作
public void onDismiss(DialogInterface dialog) {
// 调用stopAlarmSound方法停止播放提醒声音并释放相关的资源如MediaPlayer占用的内存等
stopAlarmSound();
// 结束当前Activity关闭提醒相关的界面完成整个提醒流程
finish();
}
// 私有方法用于停止播放提醒声音并释放MediaPlayer资源将其置为null避免后续出现问题
private void stopAlarmSound() {
if (mPlayer!= null) {
// 停止MediaPlayer的播放如果正在播放声音则立即停止
mPlayer.stop();
// 释放MediaPlayer占用的资源使其回到初始未使用状态
mPlayer.release();
// 将mPlayer对象置为null防止后续误操作该对象同时也符合资源释放后的正确使用习惯
mPlayer = null;
}
}
}
=======
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
@ -156,3 +330,4 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,97 +14,120 @@
* limitations under the License.
*/
// 声明当前类所在的包名这是Android项目结构的一部分用于组织代码和资源。
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.ui包通常与应用的用户界面相关模块放在一起不过从类名来看这个类更偏向于处理提醒相关的逻辑属于UI层与业务逻辑交互的部分
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.ui;
// 导入Android框架中的AlarmManager类用于设置闹钟提醒在特定时间触发。
import android.app.AlarmManager;
// 导入PendingIntent类它代表一个将要执行的动作的引用通常与AlarmManager或NotificationManager一起使用。
import android.app.PendingIntent;
// 导入BroadcastReceiver类它是一个可以接收并响应来自其他应用或系统的广播消息的组件。
import android.content.BroadcastReceiver;
// 导入ContentUris类它提供了一些静态方法用于操作URI特别是与内容提供者交互时生成或解析特定记录的URI。
import android.content.ContentUris;
// 导入Context类它提供了关于应用环境的全局信息是许多Android API调用的必需参数。
import android.content.Context;
// 导入Intent类它是Android中用于不同组件如Activity、Service、BroadcastReceiver之间通信的主要机制。
import android.content.Intent;
// 导入Cursor类它用于遍历查询数据库返回的结果集。
import android.database.Cursor;
// 导入Notes类这可能是自定义的用于封装与笔记相关的数据访问逻辑如数据库操作
import net.micode.notes.data.Notes;
// 导入NoteColumns类这通常是Notes类的一个内部类用于定义笔记数据表中的列名。
import net.micode.notes.data.Notes.NoteColumns;
<<<<<<< HEAD
// AlarmInitReceiver类继承自BroadcastReceiver用于接收系统广播消息在这里可能是用于处理与提醒初始化相关的广播事件
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义一个字符串数组用于指定查询数据库时要获取的列这里只获取笔记的ID和提醒日期两列信息方便后续操作使用
private static final String[] PROJECTION = new String[]{
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
// 定义一个常量表示查询结果中笔记ID列对应的索引位置方便后续从游标Cursor中获取对应的数据这里索引为0因为在PROJECTION数组中它排在第一位
private static final int COLUMN_ID = 0;
// 定义一个常量表示查询结果中提醒日期列对应的索引位置同样方便从游标中获取对应的数据这里索引为1因为在PROJECTION数组中它排在第二位
private static final int COLUMN_ALERTED_DATE = 1;
// 重写BroadcastReceiver的onReceive方法该方法会在接收到匹配的广播消息时被调用是处理广播逻辑的核心方法
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前系统时间的毫秒数,用于后续与笔记的提醒日期进行比较,判断哪些笔记的提醒时间已经到了或者即将到了
long currentDate = System.currentTimeMillis();
// 通过ContentResolver查询笔记内容提供器Notes.CONTENT_NOTE_URI获取满足条件的笔记数据
// 查询的列由PROJECTION数组指定查询条件是提醒日期大于当前日期并且笔记类型为普通笔记类型Notes.TYPE_NOTE
// 查询条件中的参数使用当前日期的字符串表示形式传入最后一个参数null表示不需要排序等额外设置
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[]{String.valueOf(currentDate)},
null);
// 如果查询结果游标不为null说明查询到了符合条件的笔记数据
if (c!= null) {
// 将游标移动到第一条记录位置,如果有数据则可以开始遍历获取每一条记录的信息
if (c.moveToFirst()) {
do {
// 从游标中获取当前笔记记录的提醒日期根据之前定义的列索引COLUMN_ALERTED_DATE并转换为长整型
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个Intent对象用于启动另一个广播接收者AlarmReceiver这个Intent携带了与当前笔记相关的信息
// 比如通过设置Data属性将当前笔记的ID添加到Uri中以便目标广播接收者能识别是哪个笔记的提醒操作
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个PendingIntent对象用于包装前面创建的Intent使得这个意图可以在未来某个时间点被触发
// 这里的参数0表示请求码通常用于区分不同的PendingIntent如果不需要区分可以设为0最后一个参数0表示标志位采用默认设置
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取系统的AlarmManager服务对象用于设置和管理闹钟提醒相关的操作
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 通过AlarmManager设置一个闹钟提醒使用RTC_WAKEUP模式表示在指定的绝对时间唤醒设备来触发提醒即使设备处于睡眠状态
// 指定提醒的时间为前面获取到的alertDate即笔记的提醒日期对应的时间戳以及对应的PendingIntent包含了要执行的意图操作
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext()); // 循环处理游标中的每一条记录,直到遍历完所有符合条件的笔记记录
}
// 关闭游标,释放查询占用的资源,避免内存泄漏等问题
c.close();
}
}
}
=======
/**
* AlarmInitReceiver BroadcastReceiver
*
* 广
* AlarmManager AlarmReceiver
*
*
* 1.
* 2.
* 3.
* - AlarmReceiver Intent URI
* - PendingIntent便广 Intent
* - 使 AlarmManager PendingIntent
*/
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义查询投影,只选择 ID 和 提醒日期 字段
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;
@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) },
null);
// 如果查询结果不为空,则遍历结果
if (c != null) {
if (c.moveToFirst()) {
do {
// 获取笔记的提醒日期
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个指向 AlarmReceiver 的 Intent并设置其数据为笔记的 URI
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个 PendingIntent以便在闹钟触发时广播该 Intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取 AlarmManager 实例,并设置闹钟
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭游标
c.close();
}
}
}
//AlarmInitReceiver 是一个 BroadcastReceiver用于根据数据库中笔记的提醒日期设置闹钟。
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,34 +14,45 @@
* limitations under the License.
*/
// 声明当前类所在的包名,这有助于组织代码和资源,并确保类的唯一性。
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.ui通常用于存放与应用用户界面相关的各类组件等代码
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.ui;
// 导入BroadcastReceiver类它是一个可以接收并响应来自其他应用或系统的广播消息的组件。
// 在Android中广播是一种在组件之间传递信息的机制。
import android.content.BroadcastReceiver;
// 导入Context类它提供了关于应用环境的全局信息。
// Context是一个抽象类它的实现类如Activity、Service等允许访问特定资源和类
// 以及调用应用级别的操作,如启动活动、广播和接收意图等。
import android.content.Context;
// 导入Intent类它是Android中用于不同组件如Activity、Service、BroadcastReceiver
// 之间通信以及请求动作的主要机制。Intent可以携带数据并指定要执行的动作。
import android.content.Intent;
// 定义一个名为AlarmReceiver的类继承自BroadcastReceiver
<<<<<<< HEAD
// AlarmReceiver类继承自BroadcastReceiverBroadcastReceiver用于接收系统或应用发出的广播消息
// 这里的AlarmReceiver大概率是专门用于接收与闹钟提醒相关广播的类进而做出相应的响应操作
public class AlarmReceiver extends BroadcastReceiver {
// 重写BroadcastReceiver的onReceive方法该方法会在接收到匹配的广播消息时被调用是实现广播接收逻辑的核心方法
@Override
public void onReceive(Context context, Intent intent) {
// 通过Intent的setClass方法重新设置这个Intent要启动的目标Activity类为AlarmAlertActivity
// 意味着原本接收到的广播意图将会被转换为启动AlarmAlertActivity的意图
// 通常是因为接收到闹钟提醒广播后需要展示对应的提醒界面而AlarmAlertActivity就是用于展示提醒相关界面的
intent.setClass(context, AlarmAlertActivity.class);
// 给Intent添加一个标志位Intent.FLAG_ACTIVITY_NEW_TASK这个标志位的作用是让即将启动的Activity在一个新的任务栈中启动
// 因为广播接收者的执行环境可能没有默认的任务栈关联添加此标志可以确保Activity能正常启动并显示避免出现启动相关的问题
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 使用传入的Context对象启动之前设置好的Intent也就是启动AlarmAlertActivity
// 这样就会从广播接收逻辑跳转到对应的提醒界面展示逻辑,完成闹钟提醒触发后展示提醒界面的流程
context.startActivity(intent);
}
}
=======
public class AlarmReceiver extends BroadcastReceiver {
// 重写onReceive方法用于接收广播并处理
@Override
public void onReceive(Context context, Intent intent) {
// 设置Intent的目标Activity为AlarmAlertActivity
intent.setClass(context, AlarmAlertActivity.class);
// 为Intent添加FLAG_ACTIVITY_NEW_TASK标志表示在新的任务栈中启动Activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 使用context启动目标Activity
context.startActivity(intent);
}
}
// AlarmReceiver是一个BroadcastReceiver子类用于接收广播并在接收到广播时启动AlarmAlertActivity。
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,77 +14,71 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义了类的包名
package net.micode.notes.ui;
import java.text.DateFormatSymbols; // 用于日期格式化的符号
import java.util.Calendar; // 用于日期和时间操作的类
import java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.R;
import android.content.Context; // Android的上下文对象用于访问应用程序的资源和类
import android.text.format.DateFormat; // 用于格式化日期和时间
import android.view.View; // 用于定义和操作视图的类
import android.widget.FrameLayout; // 用于布局的类,可以包含其他视图
import android.widget.NumberPicker; // 用于数字选择的控件
// DateTimePicker类继承自FrameLayout用于选择日期和时间
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
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; // 日期选择器的最大值
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; // 24小时制下小时选择器的最大值
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; // 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; // AM/PM选择器的最小值
private static final int AMPM_SPINNER_MAX_VAL = 1; // AM/PM选择器的最大值
// 定义NumberPicker控件
private final NumberPicker mDateSpinner; // 日期选择器
private final NumberPicker mHourSpinner; // 小时选择器
private final NumberPicker mMinuteSpinner; // 分钟选择器
private final NumberPicker mAmPmSpinner; // AM/PM选择器
private Calendar mDate; // 用于存储日期和时间的Calendar对象
// 用于显示日期的字符串数组
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;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
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 String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
// 一些状态标志
private boolean mIsAm; // 是否为上午
private boolean mIs24HourView; // 是否为24小时视图
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; // 是否启用
private boolean mInitialising; // 是否正在初始化
private boolean mIsAm;
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
// 定义日期时间改变监听器接口
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 当日期改变时的监听器
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当日期改变时更新Calendar对象和日期选择器
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl();
onDateTimeChanged();
}
};
// 当小时改变时的监听器
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当小时改变时更新Calendar对象和小时选择器
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
// 处理12小时制的情况
if (!mIs24HourView) {
// 如果从PM切换到AM日期减1
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
@ -94,14 +88,12 @@ public class DateTimePicker extends FrameLayout {
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
// 切换AM/PM
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);
@ -112,7 +104,6 @@ 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();
@ -124,11 +115,9 @@ public class DateTimePicker extends FrameLayout {
}
};
// 当分钟改变时的监听器
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当分钟改变时更新Calendar对象和分钟选择器
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
@ -155,11 +144,9 @@ public class DateTimePicker extends FrameLayout {
}
};
// 当AM/PM改变时的监听器
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当AM/PM改变时更新Calendar对象和AM/PM选择器
mIsAm = !mIsAm;
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
@ -170,357 +157,329 @@ public class DateTimePicker extends FrameLayout {
onDateTimeChanged();
}
};
}
public interface OnDateTimeChangedListener {
// 定义一个接口,用于通知日期时间改变的监听器
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
// DateTimePicker类的构造函数接收一个Context对象
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis()); // 调用另一个构造函数,传入当前时间
}
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
// DateTimePicker类的构造函数接收一个Context对象和一个时间戳
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context)); // 调用另一个构造函数传入是否为24小时制
}
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
// DateTimePicker类的构造函数接收一个Context对象、一个时间戳和一个布尔值表示是否为24小时制
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context); // 调用父类的构造函数
mDate = Calendar.getInstance(); // 获取当前日期和时间
mInitialising = true; // 设置初始化标志为true
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; // 判断当前时间是否为AM
// inflate布局将XML布局文件转换成View对象
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);
// 初始化AM/PM选择器并设置监听器
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); // 获取AM/PM的字符串数组
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();
// 设置是否为24小时制
set24HourView(is24HourView);
// 设置当前时间
setCurrentDate(date);
// 设置控件的启用状态
setEnabled(isEnabled());
// 设置内容描述
mInitialising = false; // 设置初始化标志为false
}
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
@Override
public void setEnabled(boolean enabled) {
// 设置控件的启用状态
if (mIsEnabled == enabled) {
return; // 如果已经是这个状态,则不进行操作
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.setEnabled(enabled); // 调用父类的方法设置启用状态
mDateSpinner.setEnabled(enabled); // 设置日期选择器的启用状态
mMinuteSpinner.setEnabled(enabled); // 设置分钟选择器的启用状态
mHourSpinner.setEnabled(enabled); // 设置小时选择器的启用状态
mAmPmSpinner.setEnabled(enabled); // 设置AM/PM选择器的启用状态
mIsEnabled = enabled; // 更新启用状态标志
}
@Override
public boolean isEnabled() {
// 返回控件的启用状态
return mIsEnabled;
}
@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;
}
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
// 获取当前日期的时间戳
return mDate.getTimeInMillis();
}
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @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); // 设置分钟
}
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* Get current year
*
* @return The current year
*/
public int getCurrentYear() {
// 获取当前年份
return mDate.get(Calendar.YEAR); // 从mDate中获取年份
}
/**
* Set the current date
*
* @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));
}
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
// 设置当前年份
if (!mInitialising && year == getCurrentYear()) {
return; // 如果不是初始化状态且年份未改变,则返回
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @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);
}
mDate.set(Calendar.YEAR, year); // 更新mDate中的年份
updateDateControl(); // 更新日期控件的显示
onDateTimeChanged(); // 通知日期时间已改变
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
// 获取当前月份
return mDate.get(Calendar.MONTH); // 从mDate中获取月份
}
/**
* Get current year
*
* @return The current year
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
// 设置当前月份
if (!mInitialising && month == getCurrentMonth()) {
return; // 如果不是初始化状态且月份未改变,则返回
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
return;
}
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
}
mDate.set(Calendar.MONTH, month); // 更新mDate中的月份
updateDateControl(); // 更新日期控件的显示
onDateTimeChanged(); // 通知日期时间已改变
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
// 获取当前日期
return mDate.get(Calendar.DAY_OF_MONTH); // 从mDate中获取日期
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
// 设置当前日期
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return; // 如果不是初始化状态且日期未改变,则返回
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); // 更新mDate中的日期
updateDateControl(); // 更新日期控件的显示
onDateTimeChanged(); // 通知日期时间已改变
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
// 获取当前小时24小时制范围为0到23
return mDate.get(Calendar.HOUR_OF_DAY); // 从mDate中获取小时
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
private int getCurrentHour() {
// 获取当前小时根据是否为24小时制返回不同的值
if (mIs24HourView) {
return getCurrentHourOfDay(); // 如果是24小时制直接返回当前小时
} else {
int hour = getCurrentHourOfDay(); // 获取当前小时
// 如果小时大于12返回小时减去12转换为12小时制
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
// 如果小时为0返回12否则返回当前小时
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay 24
*/
public void setCurrentHour(int hourOfDay) {
// 设置当前小时
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return; // 如果不是初始化状态且小时未改变,则返回
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); // 更新mDate中的小时
if (!mIs24HourView) {
// 如果不是24小时制更新AM/PM状态
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false; // 设置为PM
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY; // 转换为12小时制
}
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
mIsAm = true; // 设置为AM
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY; // 如果小时为0设置为12
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
updateAmPmControl(); // 更新AM/PM控件
}
mHourSpinner.setValue(hourOfDay); // 更新小时选择器的值
onDateTimeChanged(); // 通知日期时间已改变
}
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
// 获取当前分钟
return mDate.get(Calendar.MINUTE); // 从mDate中获取分钟
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
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;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
updateAmPmControl();
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
// 设置当前分钟
if (!mInitialising && minute == getCurrentMinute()) {
return; // 如果不是初始化状态且分钟未改变,则返回
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
mMinuteSpinner.setValue(minute); // 更新分钟选择器的值
mDate.set(Calendar.MINUTE, minute); // 更新mDate中的分钟
onDateTimeChanged(); // 通知日期时间已改变
}
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView() {
// 返回当前是否为24小时制
return mIs24HourView; // 返回24小时制状态
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
// 设置是否为24小时制或AM/PM模式
if (mIs24HourView == is24HourView) {
return; // 如果当前状态与传入状态相同,则返回
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView () {
return mIs24HourView;
}
mIs24HourView = is24HourView; // 更新24小时制状态
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); // 根据模式设置AM/PM选择器的可见性
int hour = getCurrentHourOfDay(); // 获取当前小时
updateHourControl(); // 更新小时控件
setCurrentHour(hour); // 设置当前小时
updateAmPmControl(); // 更新AM/PM控件
}
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); // 清空显示值
// 填充日期显示值
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); // 格式化日期
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
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();
}
mDateSpinner.setDisplayedValues(mDateDisplayValues); // 设置日期选择器的显示值
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); // 设置选择器的当前值为中间值
mDateSpinner.invalidate(); // 刷新选择器
}
private void updateAmPmControl() {
// 更新AM/PM选择器的显示
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE); // 隐藏AM/PM选择器
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM; // 根据当前状态设置索引
mAmPmSpinner.setValue(index); // 设置AM/PM选择器的值
mAmPmSpinner.setVisibility(View.VISIBLE); // 显示AM/PM选择器
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);
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);
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
}
}
private void updateHourControl() {
// 更新小时选择器的范围
if (mIs24HourView) {
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); // 设置12小时制下的最小值
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); // 设置12小时制下的最大值
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
// 设置日期时间改变的监听器
mOnDateTimeChangedListener = callback; // 更新监听器
}
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
private void onDateTimeChanged() {
// 通知日期时间已改变
if (mOnDateTimeChangedListener != null) {
// 如果监听器不为空,调用其方法
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}
}

@ -13,86 +13,78 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
import java.util.Calendar; // 导入用于日期和时间操作的类
package net.micode.notes.ui;
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.ui.DateTimePicker; // 导入DateTimePicker类
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; // 导入日期时间变化监听器接口
import java.util.Calendar;
import android.app.AlertDialog; // 导入AlertDialog类
import android.content.Context; // 导入上下文类
import android.content.DialogInterface; // 导入对话框接口
import android.content.DialogInterface.OnClickListener; // 导入对话框点击监听器接口
import android.text.format.DateFormat; // 导入日期格式化类
import android.text.format.DateUtils; // 导入日期工具类
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
// 自定义的日期时间选择对话框类继承自AlertDialog并实现OnClickListener接口
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance(); // 用于存储当前日期和时间
private boolean mIs24HourView; // 是否为24小时制
private OnDateTimeSetListener mOnDateTimeSetListener; // 日期时间设置监听器
private DateTimePicker mDateTimePicker; // 日期时间选择器实例
private Calendar mDate = Calendar.getInstance();
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker;
// 定义日期时间设置监听器接口
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date); // 设置日期时间的方法
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);
setView(mDateTimePicker);
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
// 当日期时间变化时更新mDate对象
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()); // 更新对话框标题
updateTitle(mDate.getTimeInMillis());
}
});
mDate.setTimeInMillis(date); // 设置mDate为传入的时间戳
mDate.set(Calendar.SECOND, 0); // 将秒数设置为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())); // 设置24小时制
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());
}
// 设置是否为24小时制
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView; // 更新24小时制状态
mIs24HourView = is24HourView;
}
// 设置日期时间设置监听器
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack; // 更新监听器
mOnDateTimeSetListener = callBack;
}
// 更新对话框标题
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_12HOUR; // 根据模式设置24小时或12小时格式
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;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
// 处理按钮点击事件
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
// 如果监听器不为空,调用其方法
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}

@ -14,54 +14,48 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
package net.micode.notes.ui;
import android.content.Context; // 导入上下文类
import android.view.Menu; // 导入菜单类
import android.view.MenuItem; // 导入菜单项类
import android.view.View; // 导入视图类
import android.view.View.OnClickListener; // 导入视图点击监听器接口
import android.widget.Button; // 导入按钮类
import android.widget.PopupMenu; // 导入弹出菜单类
import android.widget.PopupMenu.OnMenuItemClickListener; // 导入弹出菜单项点击监听器接口
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R; // 导入项目的资源文件
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;
// 构造函数接收上下文、按钮和菜单资源ID
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); // 从资源ID加载菜单项
// 设置按钮的点击事件,显示弹出菜单
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
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);
}
}
// 根据菜单项ID查找菜单项
public MenuItem findItem(int id) {
return mMenu.findItem(id); // 返回对应ID的菜单项
return mMenu.findItem(id);
}
// 设置按钮的标题
public void setTitle(CharSequence title) {
mButton.setText(title); // 更新按钮文本为指定标题
mButton.setText(title);
}
}
}

@ -14,75 +14,67 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
package net.micode.notes.ui;
import android.content.Context; // 导入上下文类
import android.database.Cursor; // 导入游标类,用于访问数据库查询结果
import android.view.View; // 导入视图类
import android.view.ViewGroup; // 导入视图组类
import android.widget.CursorAdapter; // 导入游标适配器类
import android.widget.LinearLayout; // 导入线性布局类
import android.widget.TextView; // 导入文本视图类
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.data.Notes; // 导入Notes类包含笔记数据模型
import net.micode.notes.data.Notes.NoteColumns; // 导入笔记列定义
// 自定义的文件夹列表适配器类继承自CursorAdapter
public class FoldersListAdapter extends CursorAdapter {
// 定义查询笔记数据库时需要的列
public static final String [] PROJECTION = {
NoteColumns.ID, // 笔记ID
NoteColumns.SNIPPET // 笔记摘要
NoteColumns.ID,
NoteColumns.SNIPPET
};
// 定义列索引常量
public static final int ID_COLUMN = 0; // ID列索引
public static final int NAME_COLUMN = 1; // 名称列索引
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
// 构造函数,接收上下文和游标
public FoldersListAdapter(Context context, Cursor c) {
super(context, c); // 调用父类构造函数
super(context, c);
// TODO Auto-generated constructor stub
}
// 创建新的列表项视图
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context); // 返回一个新的FolderListItem视图
return new FolderListItem(context);
}
// 绑定数据到列表项视图
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof 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); // 将文件夹名称绑定到视图
((FolderListItem) view).bind(folderName);
}
}
// 根据位置获取文件夹名称
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(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); // 返回文件夹名称
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
// 自定义的文件夹列表项视图类继承自LinearLayout
private class FolderListItem extends LinearLayout {
private TextView mName; // 用于显示文件夹名称的文本视图
private TextView mName;
// 构造函数,接收上下文
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this); // 从XML布局文件中加载视图
mName = (TextView) findViewById(R.id.tv_folder_name); // 获取文件夹名称文本视图
inflate(context, R.layout.folder_list_item, this);
mName = (TextView) findViewById(R.id.tv_folder_name);
}
// 绑定文件夹名称到视图
public void bind(String name) {
mName.setText(name); // 设置文本视图的文本
mName.setText(name);
}
}
}
}

@ -14,87 +14,88 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
import android.content.Context; // 导入上下文类
import android.graphics.Rect; // 导入矩形类
import android.text.Layout; // 导入文本布局类
import android.text.Selection; // 导入文本选择类
import android.text.Spanned; // 导入可变文本类
import android.text.TextUtils; // 导入文本工具类
import android.text.style.URLSpan; // 导入URL样式类
import android.util.AttributeSet; // 导入属性集类
import android.util.Log; // 导入日志工具类
import android.view.ContextMenu; // 导入上下文菜单类
import android.view.KeyEvent; // 导入按键事件类
import android.view.MenuItem; // 导入菜单项类
import android.view.MenuItem.OnMenuItemClickListener; // 导入菜单项点击监听器接口
import android.view.MotionEvent; // 导入触摸事件类
import android.widget.EditText; // 导入编辑文本类
import net.micode.notes.R; // 导入项目的资源文件
import java.util.HashMap; // 导入哈希映射类
import java.util.Map; // 导入映射类
// 自定义的编辑文本类继承自EditText
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText"; // 定义日志标签
private int mIndex; // 索引
private int mSelectionStartBeforeDelete; // 删除前的选择起始位置
private static final String TAG = "NoteEditText";
private int mIndex;
private int mSelectionStartBeforeDelete;
private static final String SCHEME_TEL = "tel:"; // 电话协议
private static final String SCHEME_HTTP = "http:"; // HTTP协议
private static final String SCHEME_EMAIL = "mailto:"; // 邮件协议
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>(); // 协议动作资源映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); // 电话协议动作
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); // HTTP协议动作
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); // 邮件协议动作
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);
}
/**
* {@link NoteEditActivity}
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
public interface OnTextViewChangeListener {
/**
* {@link KeyEvent#KEYCODE_DEL}
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
void onEditTextDelete(int index, String text);
/**
* {@link KeyEvent#KEYCODE_ENTER}
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
void onEditTextEnter(int index, String text);
/**
*
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener; // 文本变化监听器
private OnTextViewChangeListener mOnTextViewChangeListener;
public NoteEditText(Context context) {
super(context, null); // 调用父类构造函数
mIndex = 0; // 初始化索引
super(context, null);
mIndex = 0;
}
public void setIndex(int index) {
mIndex = index; // 设置索引
mIndex = index;
}
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener; // 设置文本变化监听器
mOnTextViewChangeListener = listener;
}
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle); // 调用父类构造函数
super(context, attrs, android.R.attr.editTextStyle);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); // 调用父类构造函数
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
@ -102,27 +103,22 @@ public class NoteEditText extends EditText {
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取触摸事件的X和Y坐标
int x = (int) event.getX();
int y = (int) event.getY();
// 调整坐标以适应总内边距和滚动
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
// 获取文本布局
Layout layout = getLayout();
// 获取Y坐标对应的行
int line = layout.getLineForVertical(y);
// 获取X坐标在行中的偏移量
int off = layout.getOffsetForHorizontal(line, x);
// 设置选择位置
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event); // 调用父类的onTouchEvent方法
return super.onTouchEvent(event);
}
@Override
@ -130,99 +126,92 @@ public class NoteEditText extends EditText {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
return false; // 如果有文本变化监听器,则消费事件
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
// 记录删除前的选择起始位置
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
return super.onKeyDown(keyCode, event); // 调用父类的onKeyDown方法
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// 处理按键释放事件
switch(keyCode) {
case KeyEvent.KEYCODE_DEL: // 如果是删除键
if (mOnTextViewChangeListener != null) { // 如果设置了文本变化监听器
if (0 == mSelectionStartBeforeDelete && mIndex != 0) { // 如果删除前的选择起始位置为0且索引不为0
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); // 调用监听器的删除方法
return true; // 消费事件
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted"); // 日志记录未设置监听器
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted"); // 日志记录未设置监听器
}
break;
case KeyEvent.KEYCODE_ENTER: // 如果是回车键
if (mOnTextViewChangeListener != null) { // 如果设置了文本变化监听器
int selectionStart = getSelectionStart(); // 获取选择起始位置
String text = getText().subSequence(selectionStart, length()).toString(); // 获取选择的文本
setText(getText().subSequence(0, selectionStart)); // 更新文本,移除选择的文本
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); // 调用监听器的添加方法
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted"); // 日志记录未设置监听器
}
break;
default:
break;
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event);
}
return super.onKeyUp(keyCode, event); // 调用父类的onKeyUp方法
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
// 处理焦点变化事件
if (mOnTextViewChangeListener != null) { // 如果设置了文本变化监听器
if (!focused && TextUtils.isEmpty(getText())) { // 如果失去焦点且文本为空
mOnTextViewChangeListener.onTextChange(mIndex, false); // 调用监听器的文本变化方法,参数为无文本
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true); // 调用监听器的文本变化方法,参数为有文本
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
super.onFocusChanged(focused, direction, previouslyFocusedRect); // 调用父类的onFocusChanged方法
}
@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) { // 如果有一个URL样式
int defaultResId = 0; // 默认资源ID
for(String schema: sSchemaActionResMap.keySet()) { // 遍历协议动作资源映射
if(urls[0].getURL().indexOf(schema) >= 0) { // 如果URL包含协议
defaultResId = sSchemaActionResMap.get(schema); // 获取对应的资源ID
break;
@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);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
}
if (defaultResId == 0) { // 如果未找到对应的资源ID
defaultResId = R.string.note_link_other; // 使用其他链接的资源ID
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
// 添加菜单项
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// 点击菜单项时的处理
urls[0].onClick(NoteEditText.this); // 调用URL样式的点击事件
return true; // 消费事件
}
});
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);
}
super.onCreateContextMenu(menu); // 调用父类的onCreateContextMenu方法
}
}

@ -14,19 +14,19 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
package net.micode.notes.ui;
import android.content.Context; // 导入上下文类
import android.database.Cursor; // 导入游标类
import android.text.TextUtils; // 导入文本工具类
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.data.Contact; // 导入联系人类
import net.micode.notes.data.Notes; // 导入笔记数据类
import net.micode.notes.data.Notes.NoteColumns; // 导入笔记列定义
import net.micode.notes.tool.DataUtils; // 导入数据工具类
public class NoteItemData {
// 定义查询笔记数据库时需要的列
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
@ -42,7 +42,6 @@ public class NoteItemData {
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;
@ -56,7 +55,6 @@ public class NoteItemData {
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
// 定义类的成员变量
private long mId;
private long mAlertDate;
private int mBgColorId;
@ -72,7 +70,6 @@ public class NoteItemData {
private String mName;
private String mPhoneNumber;
// 定义位置相关的成员变量
private boolean mIsLastItem;
private boolean mIsFirstItem;
private boolean mIsOnlyOneItem;
@ -80,7 +77,6 @@ public class NoteItemData {
private boolean mIsMultiNotesFollowingFolder;
public NoteItemData(Context context, Cursor cursor) {
// 从游标中获取数据并赋值给成员变量
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
@ -91,169 +87,138 @@ public class NoteItemData {
mParentId = cursor.getLong(PARENT_ID_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN);
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, ""); // 移除特殊标记
NoteEditActivity.TAG_UNCHECKED, "");
mType = cursor.getInt(TYPE_COLUMN);
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)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber; // 如果没有联系人名称,则使用电话号码
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = ""; // 如果没有名称,则设置为空字符串
mName = "";
}
checkPostion(cursor); // 检查位置
checkPostion(cursor);
}
private void checkPostion(Cursor cursor) {
// 检查是否是最后一项
mIsLastItem = cursor.isLast() ? true : false;
// 检查是否是第一项
mIsFirstItem = cursor.isFirst() ? true : false;
// 检查是否只有一项
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false; // 初始化多笔记标志为false
mIsOneNoteFollowingFolder = false; // 初始化单笔记标志为false
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
// 如果是笔记类型且不是第一项
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition(); // 获取当前位置
// 移动到前一项
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
// 如果前一项是文件夹或系统笔记
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
// 如果后面还有笔记
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true; // 设置多笔记标志为true
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true; // 设置单笔记标志为true
mIsOneNoteFollowingFolder = true;
}
}
// 移回当前位置
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
}
public boolean isOneFollowingFolder() {
// 返回是否有单个笔记跟随文件夹
return mIsOneNoteFollowingFolder;
}
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
public boolean isMultiFollowingFolder() {
// 返回是否有多个笔记跟随文件夹
return mIsMultiNotesFollowingFolder;
}
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
public boolean isLast() {
// 返回是否是最后一项
return mIsLastItem;
}
public boolean isLast() {
return mIsLastItem;
}
public String getCallName() {
// 返回通话记录中的联系人名称
return mName;
}
public String getCallName() {
return mName;
}
public boolean isFirst() {
// 返回是否是第一项
return mIsFirstItem;
}
public boolean isFirst() {
return mIsFirstItem;
}
public boolean isSingle() {
// 返回是否只有一项
return mIsOnlyOneItem;
}
public boolean isSingle() {
return mIsOnlyOneItem;
}
public long getId() {
// 返回笔记ID
return mId;
}
public long getId() {
return mId;
}
public long getAlertDate() {
// 返回提醒日期
return mAlertDate;
}
public long getAlertDate() {
return mAlertDate;
}
public long getCreatedDate() {
// 返回创建日期
return mCreatedDate;
}
public long getCreatedDate() {
return mCreatedDate;
}
public boolean hasAttachment() {
// 返回是否有附件
return mHasAttachment;
}
public boolean hasAttachment() {
return mHasAttachment;
}
public long getModifiedDate() {
// 返回修改日期
return mModifiedDate;
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorId() {
// 返回背景颜色ID
return mBgColorId;
}
public int getBgColorId() {
return mBgColorId;
}
public long getParentId() {
// 返回父ID通常指文件夹ID
return mParentId;
}
public long getParentId() {
return mParentId;
}
public int getNotesCount() {
// 返回笔记计数
return mNotesCount;
}
public int getNotesCount() {
return mNotesCount;
}
public long getFolderId () {
// 返回文件夹ID与getParentId方法相同
return mParentId;
}
public long getFolderId () {
return mParentId;
}
public int getType() {
// 返回笔记类型
return mType;
}
public int getType() {
return mType;
}
public int getWidgetType() {
// 返回小部件类型
return mWidgetType;
}
public int getWidgetType() {
return mWidgetType;
}
public int getWidgetId() {
// 返回小部件ID
return mWidgetId;
}
public int getWidgetId() {
return mWidgetId;
}
public String getSnippet() {
// 返回笔记摘要
return mSnippet;
}
public String getSnippet() {
return mSnippet;
}
public boolean hasAlert() {
// 返回是否有提醒即提醒日期是否大于0
return (mAlertDate > 0);
}
public boolean hasAlert() {
return (mAlertDate > 0);
}
public boolean isCallRecord() {
// 返回是否是通话记录,即是否在通话记录文件夹中且电话号码不为空
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
public static int getNoteType(Cursor cursor) {
// 从游标中获取笔记类型
return cursor.getInt(TYPE_COLUMN);
}
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
}

@ -14,182 +14,171 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
package net.micode.notes.ui;
import android.content.Context; // 导入Context类
import android.database.Cursor; // 导入Cursor类
import android.util.Log; // 导入Log类
import android.view.View; // 导入View类
import android.view.ViewGroup; // 导入ViewGroup类
import android.widget.CursorAdapter; // 导入CursorAdapter类
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import net.micode.notes.data.Notes; // 导入Notes类
import net.micode.notes.data.Notes;
import java.util.Collection; // 导入Collection接口
import java.util.HashMap; // 导入HashMap类
import java.util.HashSet; // 导入HashSet类
import java.util.Iterator; // 导入Iterator接口
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
// 自定义的笔记列表适配器类继承自CursorAdapter
public class NotesListAdapter extends CursorAdapter {
private static final String TAG = "NotesListAdapter"; // 定义日志标签
private Context mContext; // 上下文对象
private HashMap<Integer, Boolean> mSelectedIndex; // 存储选中状态的哈希映射
private int mNotesCount; // 笔记计数
private boolean mChoiceMode; // 是否处于选择模式
private static final String TAG = "NotesListAdapter";
private Context mContext;
private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount;
private boolean mChoiceMode;
// 定义AppWidgetAttribute内部类用于存储小部件属性
public static class AppWidgetAttribute {
public int widgetId; // 小部件ID
public int widgetType; // 小部件类型
public int widgetId;
public int widgetType;
};
// 构造函数,接收上下文对象
public NotesListAdapter(Context context) {
super(context, null); // 调用父类构造函数
mSelectedIndex = new HashMap<Integer, Boolean>(); // 初始化选中状态哈希映射
mContext = context; // 初始化上下文对象
mNotesCount = 0; // 初始化笔记计数为0
super(context, null);
mSelectedIndex = new HashMap<Integer, Boolean>();
mContext = context;
mNotesCount = 0;
}
// 新建视图
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context); // 返回新的NotesListItem视图
return new NotesListItem(context);
}
// 绑定视图
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) { // 如果视图是NotesListItem的实例
NoteItemData itemData = new NoteItemData(context, cursor); // 创建NoteItemData对象
((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); // 绑定数据到视图
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
}
}
// 设置选中项
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked); // 将位置和选中状态放入哈希映射
notifyDataSetChanged(); // 通知数据集改变
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
// 判断是否处于选择模式
public boolean isInChoiceMode() {
return mChoiceMode; // 返回选择模式状态
return mChoiceMode;
}
// 设置选择模式
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear(); // 清除选中状态哈希映射
mChoiceMode = mode; // 设置选择模式状态
mSelectedIndex.clear();
mChoiceMode = mode;
}
// 全选或全不选
public void selectAll(boolean checked) {
Cursor cursor = getCursor(); // 获取游标
for (int i = 0; i < getCount(); i++) { // 遍历所有项
if (cursor.moveToPosition(i)) { // 移动到当前位置
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { // 如果是笔记类型
setCheckedItem(i, checked); // 设置选中状态
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
setCheckedItem(i, checked);
}
}
}
}
// 获取选中的笔记ID集合
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>(); // 创建ID集合
for (Integer position : mSelectedIndex.keySet()) { // 遍历选中状态哈希映射
if (mSelectedIndex.get(position) == true) { // 如果选中
Long id = getItemId(position); // 获取ID
if (id == Notes.ID_ROOT_FOLDER) { // 如果是根文件夹ID
Log.d(TAG, "Wrong item id, should not happen"); // 日志记录错误
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Long id = getItemId(position);
if (id == Notes.ID_ROOT_FOLDER) {
Log.d(TAG, "Wrong item id, should not happen");
} else {
itemSet.add(id); // 添加到ID集合
itemSet.add(id);
}
}
}
return itemSet; // 返回ID集合
return itemSet;
}
// 获取选中的小部件属性集合
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>(); // 创建小部件属性集合
for (Integer position : mSelectedIndex.keySet()) { // 遍历选中状态哈希映射
if (mSelectedIndex.get(position) == true) { // 如果选中
Cursor c = (Cursor) getItem(position); // 获取游标
if (c != null) { // 如果游标不为空
AppWidgetAttribute widget = new AppWidgetAttribute(); // 创建小部件属性对象
NoteItemData item = new NoteItemData(mContext, c); // 创建NoteItemData对象
widget.widgetId = item.getWidgetId(); // 获取小部件ID
widget.widgetType = item.getWidgetType(); // 获取小部件类型
itemSet.add(widget); // 添加到小部件属性集合
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Cursor c = (Cursor) getItem(position);
if (c != null) {
AppWidgetAttribute widget = new AppWidgetAttribute();
NoteItemData item = new NoteItemData(mContext, c);
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
/**
* Don't close cursor here, only the adapter could close it
*/
} else {
Log.e(TAG, "Invalid cursor"); // 日志记录错误
return null; // 返回null
Log.e(TAG, "Invalid cursor");
return null;
}
}
}
return itemSet; // 返回小部件属性集合
}
}
public int getSelectedCount() {
// 获取选中项的数量
Collection<Boolean> values = mSelectedIndex.values(); // 获取选中状态值的集合
if (null == values) {
return 0; // 如果集合为空则返回0
return itemSet;
}
Iterator<Boolean> iter = values.iterator(); // 创建迭代器
int count = 0; // 初始化计数器
while (iter.hasNext()) {
if (true == iter.next()) { // 如果项被选中
count++; // 增加计数器
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
return 0;
}
Iterator<Boolean> iter = values.iterator();
int count = 0;
while (iter.hasNext()) {
if (true == iter.next()) {
count++;
}
}
return count;
}
return count; // 返回选中项的数量
}
public boolean isAllSelected() {
// 检查是否所有项都被选中
int checkedCount = getSelectedCount(); // 获取选中项的数量
return (checkedCount != 0 && checkedCount == mNotesCount); // 如果选中项的数量大于0且等于笔记总数则返回true
}
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
}
public boolean isSelectedItem(final int position) {
// 检查指定位置的项是否被选中
if (null == mSelectedIndex.get(position)) { // 如果指定位置没有选中状态
return false; // 返回false
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position);
}
return mSelectedIndex.get(position); // 返回选中状态
}
@Override
protected void onContentChanged() {
// 当内容变化时被调用
super.onContentChanged(); // 调用父类方法
calcNotesCount(); // 计算笔记数量
}
@Override
protected void onContentChanged() {
super.onContentChanged();
calcNotesCount();
}
@Override
public void changeCursor(Cursor cursor) {
// 当游标变化时被调用
super.changeCursor(cursor); // 调用父类方法
calcNotesCount(); // 计算笔记数量
}
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
calcNotesCount();
}
private void calcNotesCount() {
// 计算笔记的数量
mNotesCount = 0; // 初始化笔记计数器
for (int i = 0; i < getCount(); i++) { // 遍历所有项
Cursor c = (Cursor) getItem(i); // 获取游标
if (c != null) { // 如果游标不为空
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { // 如果项是笔记类型
mNotesCount++; // 增加笔记计数器
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
}
} else {
Log.e(TAG, "Invalid cursor");
return;
}
} else {
Log.e(TAG, "Invalid cursor"); // 日志记录无效游标错误
return; // 返回
}
}
}
}

@ -14,116 +14,109 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
package net.micode.notes.ui;
import android.content.Context; // 导入上下文类
import android.text.format.DateUtils; // 导入日期工具类
import android.view.View; // 导入视图类
import android.widget.CheckBox; // 导入复选框类
import android.widget.ImageView; // 导入图像视图类
import android.widget.LinearLayout; // 导入线性布局类
import android.widget.TextView; // 导入文本视图类
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.data.Notes; // 导入Notes类
import net.micode.notes.tool.DataUtils; // 导入数据工具类
import net.micode.notes.tool.ResourceParser.NoteItemBgResources; // 导入笔记项背景资源解析器类
// 自定义的笔记列表项类继承自LinearLayout
public class NotesListItem extends LinearLayout {
private ImageView mAlert; // 警告图标视图
private TextView mTitle; // 笔记标题文本视图
private TextView mTime; // 笔记时间文本视图
private TextView mCallName; // 通话记录联系人名称文本视图
private NoteItemData mItemData; // 笔记项数据
private CheckBox mCheckBox; // 复选框视图
private ImageView mAlert;
private TextView mTitle;
private TextView mTime;
private TextView mCallName;
private NoteItemData mItemData;
private CheckBox mCheckBox;
// 构造函数,接收上下文
public NotesListItem(Context context) {
super(context); // 调用父类构造函数
inflate(context, R.layout.note_item, this); // 从布局文件中填充视图
mAlert = (ImageView) findViewById(R.id.iv_alert_icon); // 获取警告图标视图
mTitle = (TextView) findViewById(R.id.tv_title); // 获取标题文本视图
mTime = (TextView) findViewById(R.id.tv_time); // 获取时间文本视图
mCallName = (TextView) findViewById(R.id.tv_name); // 获取联系人名称文本视图
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); // 获取复选框视图
super(context);
inflate(context, R.layout.note_item, this);
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
// 绑定数据到视图
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
if (choiceMode && data.getType() == Notes.TYPE_NOTE) { // 如果处于选择模式且数据类型为笔记
mCheckBox.setVisibility(View.VISIBLE); // 显示复选框
mCheckBox.setChecked(checked); // 设置复选框状态
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
} else {
mCheckBox.setVisibility(View.GONE); // 隐藏复选框
mCheckBox.setVisibility(View.GONE);
}
mItemData = data; // 绑定笔记项数据
// 如果是通话记录文件夹
mItemData = data;
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE); // 隐藏联系人名称
mAlert.setVisibility(View.VISIBLE); // 显示警告图标
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); // 设置标题文本外观
mTitle.setText(context.getString(R.string.call_record_folder_name) // 设置标题为通话记录文件夹名称
+ context.getString(R.string.format_folder_files_count, data.getNotesCount())); // 添加文件数量
mAlert.setImageResource(R.drawable.call_record); // 设置警告图标为通话记录图标
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { // 如果父ID是通话记录文件夹
mCallName.setVisibility(View.VISIBLE); // 显示联系人名称
mCallName.setText(data.getCallName()); // 设置联系人名称
mTitle.setTextAppearance(context, R.style.TextAppearanceSecondaryItem); // 设置标题文本外观
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); // 设置标题为格式化的摘要
// 根据是否有提醒设置警告图标的可见性
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock); // 设置警告图标为时钟图标
mAlert.setVisibility(View.VISIBLE); // 显示警告图标
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE); // 隐藏警告图标
mAlert.setVisibility(View.GONE);
}
} else { // 如果是普通笔记或文件夹
mCallName.setVisibility(View.GONE); // 隐藏联系人名称
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); // 设置标题文本外观
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
if (data.getType() == Notes.TYPE_FOLDER) { // 如果是文件夹类型
mTitle.setText(data.getSnippet() // 设置标题为文件夹摘要
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount())); // 添加文件数量
mAlert.setVisibility(View.GONE); // 隐藏警告图标
} else { // 如果是普通笔记类型
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); // 设置标题为格式化的摘要
// 根据是否有提醒设置警告图标的可见性
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock); // 设置警告图标为时钟图标
mAlert.setVisibility(View.VISIBLE); // 显示警告图标
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE); // 隐藏警告图标
mAlert.setVisibility(View.GONE);
}
}
}
// 设置时间文本为相对时间
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); // 设置时间为相对时间
setBackground(data); // 设置背景
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
setBackground(data);
}
private void setBackground(NoteItemData data) {
// 根据笔记类型和状态设置背景
int id = data.getBgColorId(); // 获取背景颜色ID
if (data.getType() == Notes.TYPE_NOTE) { // 如果是笔记类型
if (data.isSingle() || data.isOneFollowingFolder()) { // 如果是单个笔记或跟随文件夹的单个笔记
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); // 设置单个笔记背景
} else if (data.isLast()) { // 如果是最后一项
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); // 设置最后一项背景
} else if (data.isFirst() || data.isMultiFollowingFolder()) { // 如果是第一项或跟随文件夹的多个笔记
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); // 设置第一项背景
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); // 设置普通背景
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
setBackgroundResource(NoteItemBgResources.getFolderBgRes()); // 如果是文件夹类型,设置文件夹背景
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
public NoteItemData getItemData() {
// 返回笔记项数据
return mItemData;
}
}
}

@ -14,452 +14,375 @@
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
import android.accounts.Account; // 导入Account类
import android.accounts.AccountManager; // 导入AccountManager类
import android.app.ActionBar; // 导入ActionBar类
import android.app.AlertDialog; // 导入AlertDialog类
import android.content.BroadcastReceiver; // 导入BroadcastReceiver类
import android.content.ContentValues; // 导入ContentValues类
import android.content.Context; // 导入Context类
import android.content.DialogInterface; // 导入DialogInterface类
import android.content.Intent; // 导入Intent类
import android.content.IntentFilter; // 导入IntentFilter类
import android.content.SharedPreferences; // 导入SharedPreferences类
import android.os.Bundle; // 导入Bundle类
import android.preference.Preference; // 导入Preference类
import android.preference.Preference.OnPreferenceClickListener; // 导入PreferenceClickListener接口
import android.preference.PreferenceActivity; // 导入PreferenceActivity类
import android.preference.PreferenceCategory; // 导入PreferenceCategory类
import android.text.TextUtils; // 导入TextUtils类
import android.text.format.DateFormat; // 导入DateFormat类
import android.view.LayoutInflater; // 导入LayoutInflater类
import android.view.Menu; // 导入Menu类
import android.view.MenuItem; // 导入MenuItem类
import android.view.View; // 导入View类
import android.widget.Button; // 导入Button类
import android.widget.TextView; // 导入TextView类
import android.widget.Toast; // 导入Toast类
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.data.Notes; // 导入Notes类
import net.micode.notes.data.Notes.NoteColumns; // 导入NoteColumns类
import net.micode.notes.gtask.remote.GTaskSyncService; // 导入GTaskSyncService类
// 继承自PreferenceActivity的设置活动类
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; // 设置的名称
public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账户名称的键
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 最后同步时间的键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 设置背景颜色的键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账户的键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 权限过滤的键
private static final String AUTHORITIES_FILTER_KEY = "authorities";
private PreferenceCategory mAccountCategory; // 账户类别的PreferenceCategory
private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver; // 用于接收GTask同步状态的BroadcastReceiver
private GTaskReceiver mReceiver;
private Account[] mOriAccounts; // 原始账户数组
private Account[] mOriAccounts;
private boolean mHasAddedAccount; // 是否添加了账户
private boolean mHasAddedAccount;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle); // 调用父类的onCreate方法
super.onCreate(icicle);
/* using the app icon for navigation */
getActionBar().setDisplayHomeAsUpEnabled(true); // 设置ActionBar的返回键可用
addPreferencesFromResource(R.xml.preferences); // 从资源文件中添加偏好设置
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); // 获取同步账户的PreferenceCategory
mReceiver = new GTaskReceiver(); // 创建GTaskReceiver实例
IntentFilter filter = new IntentFilter(); // 创建IntentFilter
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); // 添加GTask服务广播的Action
registerReceiver(mReceiver, filter); // 注册BroadcastReceiver
mOriAccounts = null; // 初始化原始账户数组为null
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); // 从布局文件中填充设置头部视图
getListView().addHeaderView(header, null, true); // 将头部视图添加到ListView
getActionBar().setDisplayHomeAsUpEnabled(true);
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
}
@Override
protected void onResume() {
super.onResume(); // 调用父类的onResume方法
super.onResume();
// 如果用户添加了新账户,需要自动设置同步账户
// need to set sync account automatically if user has added a new
// account
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts(); // 获取Google账户数组
if (mOriAccounts != null && accounts.length > mOriAccounts.length) { // 如果新账户数组长度大于原始账户数组长度
for (Account accountNew : accounts) { // 遍历新账户数组
boolean found = false; // 初始化找到标志为false
for (Account accountOld : mOriAccounts) { // 遍历原始账户数组
if (TextUtils.equals(accountOld.name, accountNew.name)) { // 如果找到相同的账户
found = true; // 设置找到标志为true
break; // 跳出循环
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) { // 如果没有找到相同的账户
setSyncAccount(accountNew.name); // 设置同步账户
break; // 跳出循环
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
refreshUI(); // 刷新用户界面
refreshUI();
}
}
@Override
protected void onDestroy() {
// 检查mReceiver是否已经被实例化如果是则注销广播接收器
if (mReceiver != null) {
unregisterReceiver(mReceiver);
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
// 调用父类的onDestroy方法继续执行其他清理工作
super.onDestroy();
}
// 加载账户偏好设置
private void loadAccountPreference() {
// 清除mAccountCategory中的所有Preference
mAccountCategory.removeAll();
// 创建一个新的Preference对象
Preference accountPref = new Preference(this);
// 获取默认同步账户的名称
final String defaultAccount = getSyncAccountName(this);
// 设置Preference的标题
accountPref.setTitle(getString(R.string.preferences_account_title));
// 设置Preference的摘要信息
accountPref.setSummary(getString(R.string.preferences_account_summary));
// 设置Preference点击事件的监听器
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
// 如果没有正在进行的同步操作
if (!GTaskSyncService.isSyncing()) {
// 如果defaultAccount为空表示是第一次设置账户
if (TextUtils.isEmpty(defaultAccount)) {
showSelectAccountAlertDialog(); // 显示选择账户的AlertDialog
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
showChangeAccountConfirmAlertDialog();
}
} else {
// 如果账户已经设置,提示用户更换账户的风险
showChangeAccountConfirmAlertDialog();
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// set button state
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// set last sync time
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
// 如果正在进行同步操作显示一个Toast提示用户不能更改账户
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
lastSyncTimeView.setVisibility(View.GONE);
}
// 返回true表示已经处理了点击事件
return true;
}
});
}
// 将创建的Preference添加到mAccountCategory中
mAccountCategory.addPreference(accountPref);
}
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
// 加载同步按钮
private void loadSyncButton() {
// 通过findViewById获取同步按钮和最后同步时间的TextView
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据是否正在同步来设置按钮的状态
if (GTaskSyncService.isSyncing()) {
// 如果正在同步,设置按钮文本为取消同步,并设置点击事件为取消同步操作
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this); // 取消同步
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
});
} else {
// 如果没有同步操作,设置按钮文本为立即同步,并设置点击事件为开始同步操作
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this); // 开始同步
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
// 根据账户名称是否为空来启用或禁用同步按钮
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 设置最后同步时间的显示
if (GTaskSyncService.isSyncing()) {
// 如果正在同步,显示同步进度
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
// 如果没有同步操作,显示最后同步时间
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
// 刷新UI加载账户偏好和同步按钮
private void refreshUI() {
loadAccountPreference(); // 加载账户偏好
loadSyncButton(); // 加载同步按钮
}
// 显示选择账户的对话框
private void showSelectAccountAlertDialog() {
// 创建AlertDialog.Builder对象用于构建对话框
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 从布局文件中加载自定义标题视图
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
// 获取标题TextView并设置标题文本
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
// 获取副标题TextView并设置副标题文本
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
// 设置自定义标题
dialogBuilder.setCustomTitle(titleView);
// 设置正面按钮(未设置文本和点击事件,后续会处理)
dialogBuilder.setPositiveButton(null, null);
// 获取Google账户列表
Account[] accounts = getGoogleAccounts();
// 获取当前同步账户名称
String defAccount = getSyncAccountName(this);
// 保存原始账户列表和添加账户标志
mOriAccounts = accounts;
mHasAddedAccount = false;
// 如果有账户可供选择
if (accounts.length > 0) {
// 创建CharSequence数组用于存储账户名称
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items; // 用于后续选择
int checkedItem = -1; // 记录选中的账户索引
int index = 0; // 当前索引
// 遍历账户列表
for (Account account : accounts) {
// 如果账户名称与默认账户名称匹配,记录选中的索引
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
// 将账户名称添加到items数组中
items[index++] = account.name;
}
// 设置单选列表,用户可以选择账户
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 设置选中的同步账户
setSyncAccount(itemMapping[which].toString());
dialog.dismiss(); // 关闭对话框
refreshUI(); // 刷新UI
}
});
});
dialogBuilder.show();
}
// 从布局文件中加载添加账户的视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
// 将添加账户视图设置为对话框的内容视图
dialogBuilder.setView(addAccountView);
// 显示对话框
final AlertDialog dialog = dialogBuilder.show();
// 设置点击添加账户视图的事件
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true; // 设置已添加账户标志
// 创建意图以打开添加账户设置
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls" // 过滤器,指定要添加的账户类型
});
startActivityForResult(intent, -1); // 启动添加账户设置
dialog.dismiss(); // 关闭对话框
}
});
}
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
// 显示更改账户确认对话框
private void showChangeAccountConfirmAlertDialog() {
// 创建AlertDialog.Builder对象
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 从布局文件中加载自定义标题视图
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
// 获取标题TextView并设置标题文本
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this))); // 显示当前同步账户名称
// 获取副标题TextView并设置副标题文本
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
// 设置自定义标题
dialogBuilder.setCustomTitle(titleView);
// 创建菜单项数组
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account), // 更改账户
getString(R.string.preferences_menu_remove_account), // 移除账户
getString(R.string.preferences_menu_cancel) // 取消
};
// 设置菜单项的点击事件
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 根据用户选择的菜单项执行相应操作
if (which == 0) {
showSelectAccountAlertDialog(); // 显示选择账户对话框
} else if (which == 1) {
removeSyncAccount(); // 移除同步账户
refreshUI(); // 刷新UI
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
}
});
// 显示对话框
dialogBuilder.show();
}
editor.commit();
// clean up last sync time
setLastSyncTime(this, 0);
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
// 获取Google账户列表
private Account[] getGoogleAccounts() {
// 获取AccountManager实例
AccountManager accountManager = AccountManager.get(this);
// 返回所有Google账户
return accountManager.getAccountsByType("com.google");
}
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
// 设置同步账户
private void setSyncAccount(String account) {
// 检查当前同步账户是否与传入的账户不同
if (!getSyncAccountName(this).equals(account)) {
// 获取SharedPreferences实例用于存储偏好设置
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
// 如果传入的账户不为空,则保存账户名称;否则保存空字符串
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
editor.commit(); // 提交更改
// 清除最后同步时间
setLastSyncTime(this, 0);
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
// 清除本地与gtask相关的信息
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, ""); // 清空GTASK_ID
values.put(NoteColumns.SYNC_ID, 0); // 清空SYNC_ID
// 更新Notes.CONTENT_NOTE_URI中的数据
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start(); // 启动新线程执行清理操作
// 显示成功设置账户的Toast提示
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}).start();
}
}
// 移除同步账户
private void removeSyncAccount() {
// 获取SharedPreferences实例
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
// 如果存在同步账户名称,则移除
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
// 如果存在最后同步时间,则移除
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit(); // 提交更改
// 清除本地与gtask相关的信息
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, ""); // 清空GTASK_ID
values.put(NoteColumns.SYNC_ID, 0); // 清空SYNC_ID
// 更新Notes.CONTENT_NOTE_URI中的数据
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start(); // 启动新线程执行清理操作
}
// 获取当前同步账户名称
public static String getSyncAccountName(Context context) {
// 获取SharedPreferences实例
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 返回存储的同步账户名称,默认为空字符串
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
// 设置最后同步时间
public static void setLastSyncTime(Context context, long time) {
// 获取SharedPreferences实例
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
// 保存最后同步时间
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit(); // 提交更改
}
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
// 获取最后同步时间
public static long getLastSyncTime(Context context) {
// 获取SharedPreferences实例
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 返回存储的最后同步时间默认为0
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
private class GTaskReceiver extends BroadcastReceiver {
// 定义GTaskReceiver类继承自BroadcastReceiver
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
// 当接收到广播时调用
@Override
public void onReceive(Context context, Intent intent) {
refreshUI(); // 刷新UI
// 检查广播中是否包含同步状态信息
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
// 获取同步状态的TextView并更新其文本
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
// 处理选项菜单项的选择事件
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: // 如果点击的是返回按钮
// 创建返回到NotesListActivity的意图
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 清除上层活动
startActivity(intent); // 启动活动
return true; // 返回true表示事件已处理
default:
return false; // 返回false表示未处理该事件
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}
}

@ -14,10 +14,13 @@
* limitations under the License.
*/
// 包声明,定义了这个类所在的包路径
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.widget通常用于存放与桌面小部件Widget相关的代码逻辑
package net.micode.notes.widget;
// 导入所需的Android类和接口
=======
package net.micode.notes.widget;
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@ -28,7 +31,6 @@ import android.database.Cursor;
import android.util.Log;
import android.widget.RemoteViews;
// 导入应用特有的类和接口
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
@ -36,33 +38,66 @@ import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
// 定义一个抽象类NoteWidgetProvider继承自AppWidgetProvider用于管理便签Widget
<<<<<<< HEAD
// NoteWidgetProvider类继承自AppWidgetProvider是安卓中用于创建桌面小部件的基类这里定义为抽象类意味着它需要被具体的子类继承并实现一些抽象方法
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义一个字符串数组用于指定查询数据库时要获取的列信息这里包含笔记的ID、背景颜色ID以及摘要信息方便后续在小部件中展示相关内容
public static final String[] PROJECTION = new String[]{
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义一个常量表示查询结果中笔记ID列对应的索引位置方便后续从游标Cursor中准确获取该列的数据索引为0是因为在PROJECTION数组中它排在第一位
public static final int COLUMN_ID = 0;
// 定义一个常量表示查询结果中背景颜色ID列对应的索引位置用于从游标中获取背景颜色相关信息索引为1对应其在PROJECTION数组中的位置
public static final int COLUMN_BG_COLOR_ID = 1;
// 定义一个常量表示查询结果中摘要信息列对应的索引位置用于获取笔记的摘要内容索引为2表示其在PROJECTION数组中的顺序位置
public static final int COLUMN_SNIPPET = 2;
// 定义一个用于日志记录的标签字符串,方便在日志中识别该类输出的相关信息,便于调试和查看运行情况
private static final String TAG = "NoteWidgetProvider";
// 重写AppWidgetProvider的onDeleted方法该方法会在小部件被删除时被调用用于处理小部件删除后的相关逻辑
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 创建一个ContentValues对象用于存储要更新到数据库中的键值对数据
ContentValues values = new ContentValues();
// 将笔记对应的小部件ID设置为无效值AppWidgetManager.INVALID_APPWIDGET_ID表示该笔记与小部件的关联已解除
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历被删除的小部件ID数组对每个小部件ID执行以下操作
for (int i = 0; i < appWidgetIds.length; i++) {
// 通过ContentResolver更新笔记内容提供器Notes.CONTENT_NOTE_URI中的数据
// 更新条件是笔记的小部件ID与当前遍历到的小部件ID匹配将对应的笔记记录中的小部件ID设置为无效值
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
new String[]{String.valueOf(appWidgetIds[i])});
}
}
// 私有方法用于根据给定的小部件ID从数据库中获取该小部件相关的笔记信息如笔记ID、背景颜色、摘要等返回一个游标Cursor对象用于遍历查询结果
=======
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义查询数据库时使用的列名数组
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义数组中每个列的索引常量
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
// 定义日志标签
private static final String TAG = "NoteWidgetProvider";
// 当Widget被删除时调用的方法
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 为数据库更新准备ContentValues对象
ContentValues values = new ContentValues();
// 设置WIDGET_ID为无效值
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历所有被删除的Widget ID
for (int i = 0; i < appWidgetIds.length; i++) {
// 更新数据库中对应的Widget ID为无效
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
@ -70,99 +105,172 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 获取与特定Widget ID相关的便签信息
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
// 查询数据库,获取便签信息
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
<<<<<<< HEAD
new String[]{String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
}
// 受保护的方法用于更新小部件的显示内容它调用了另一个重载的update方法并传入默认的隐私模式参数false
=======
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
// 更新Widget的方法可以被外部调用
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用内部的update方法不启用隐私模式
update(context, appWidgetManager, appWidgetIds, false);
}
// 更新Widget的内部方法根据是否启用隐私模式来更新Widget
<<<<<<< HEAD
// 私有方法用于实际更新小部件的显示内容根据传入的小部件ID数组逐个更新对应的小部件显示情况同时可以根据隐私模式参数进行不同的显示设置
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历小部件ID数组对每个有效的小部件ID执行更新操作
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i]!= AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取默认的背景颜色ID通过ResourceParser工具类的方法来获取可能根据用户设置等情况返回相应的颜色标识
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化笔记摘要信息为空字符串,后续根据查询结果进行更新
String snippet = "";
// 创建一个Intent对象用于启动NoteEditActivity可能是点击小部件后跳转到笔记编辑页面等相关操作具体行为由后续设置决定
Intent intent = new Intent(context, NoteEditActivity.class);
// 设置Intent的标志位使得如果对应的Activity已经在栈顶就不会重新创建新的实例而是复用已有的实例
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 将当前小部件的ID通过Intent的额外数据传递机制传递给目标Activity以便目标Activity能识别是哪个小部件触发的操作
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
// 将小部件的类型通过Intent的额外数据传递给目标Activity具体类型由子类实现的抽象方法getWidgetType来确定
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 通过前面定义的方法获取当前小部件相关的笔记信息游标
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c!= null && c.moveToFirst()) {
// 如果查询到的笔记记录数量大于1说明存在多条笔记对应同一个小部件ID的异常情况记录错误日志并关闭游标直接返回不进行后续更新操作
=======
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历所有Widget ID
for (int i = 0; i < appWidgetIds.length; i++) {
// 如果Widget ID有效
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取默认背景ID
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化摘要字符串
String snippet = "";
// 创建Intent用于编辑便签
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 获取便签信息的Cursor
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
// 如果Cursor不为空且第一条数据有效
if (c != null && c.moveToFirst()) {
// 如果有多条数据与Widget ID匹配记录错误日志并返回
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
// 从Cursor中获取摘要和背景ID
<<<<<<< HEAD
// 获取笔记的摘要信息从游标中根据之前定义的列索引COLUMN_SNIPPET获取对应的数据
snippet = c.getString(COLUMN_SNIPPET);
// 获取笔记对应的背景颜色ID从游标中根据列索引COLUMN_BG_COLOR_ID获取对应的数据并更新之前获取的默认背景颜色ID
bgId = c.getInt(COLUMN_BG_COLOR_ID);
// 将笔记的ID通过Intent的额外数据传递给目标Activity以便在编辑页面等操作中能准确操作对应的笔记
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
// 设置Intent的动作这里设置为查看ACTION_VIEW动作表示点击小部件后可能是查看对应笔记的操作意图
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果没有查询到对应的笔记信息,设置摘要信息为一个默认的提示字符串,表示小部件暂无内容
snippet = context.getResources().getString(R.string.widget_havenot_content);
// 设置Intent的动作这里设置为插入或编辑ACTION_INSERT_OR_EDIT动作可能意味着点击小部件后可以进行新建或编辑笔记的操作
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 如果游标不为null关闭游标释放资源避免内存泄漏等问题
if (c!= null) {
c.close();
}
// 创建一个RemoteViews对象用于构建小部件的远程视图传入应用的包名和小部件的布局ID由子类实现的抽象方法getLayoutId来确定
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
// 设置小部件中背景图片资源的ID通过调用抽象方法getBgResourceId传入背景颜色ID来获取对应的资源ID以显示正确的背景图片
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
// 将背景颜色ID通过Intent的额外数据传递给目标Activity方便后续根据颜色进行相关操作或显示调整
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
// 生成一个PendingIntent对象用于包装前面创建的Intent使得点击小部件时能触发对应的操作根据隐私模式进行不同的设置
PendingIntent pendingIntent = null;
if (privacyMode) {
// 如果处于隐私模式,设置小部件文本显示的内容为一个特定的隐私提示字符串
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
// 创建一个PendingIntent用于启动NotesListActivity可能在隐私模式下点击小部件跳转到笔记列表页面等操作
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 如果不处于隐私模式,设置小部件文本显示的内容为之前获取或设置的笔记摘要信息
rv.setTextViewText(R.id.widget_text, snippet);
// 创建一个PendingIntent用于启动之前设置的Intent对应的Activity根据不同情况可能是编辑页面、查看页面等
=======
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果没有找到匹配的便签,设置摘要为默认字符串
snippet = context.getResources().getString(R.string.widget_havenot_content);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 关闭Cursor
if (c != null) {
c.close();
}
// 创建RemoteViews对象用于更新Widget视图
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
// 设置背景图片资源ID
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
// 生成PendingIntent用于启动宿主Activity
/**
* Generate the pending intent to start host for the widget
*/
PendingIntent pendingIntent = null;
if (privacyMode) {
// 如果启用隐私模式,显示隐私模式提示文本
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 否则,显示便签摘要
rv.setTextViewText(R.id.widget_text, snippet);
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 设置RemoteViews的PendingIntent
<<<<<<< HEAD
// 设置小部件文本视图的点击事件PendingIntent使得点击文本时能触发对应的操作
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 通过AppWidgetManager更新指定小部件ID对应的小部件显示内容应用前面设置好的RemoteViews对象
=======
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 更新Widget
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
// 抽象方法用于获取背景资源ID
<<<<<<< HEAD
// 抽象方法需要由具体的子类实现根据传入的背景颜色ID返回对应的背景资源ID用于设置小部件的背景图片等显示资源
protected abstract int getBgResourceId(int bgId);
// 抽象方法需要由具体的子类实现返回小部件对应的布局资源ID用于创建RemoteViews对象来构建小部件的远程视图显示
protected abstract int getLayoutId();
// 抽象方法需要由具体的子类实现返回小部件的类型信息具体类型由子类根据业务逻辑定义用于在Intent等操作中传递和区分不同类型的小部件
protected abstract int getWidgetType();
}
=======
protected abstract int getBgResourceId(int bgId);
// 抽象方法用于获取布局ID
protected abstract int getLayoutId();
// 抽象方法用于获取Widget类型
protected abstract int getWidgetType();
}
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,33 +14,70 @@
* limitations under the License.
*/
// 定义了一个名为NoteWidgetProvider_2x的类它继承自NoteWidgetProvider类
<<<<<<< HEAD
// 包声明表明该类属于net.micode.notes.widget包通常用于存放与桌面小部件相关的具体实现类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
<<<<<<< HEAD
// NoteWidgetProvider_2x类继承自NoteWidgetProvider抽象类意味着它需要实现NoteWidgetProvider中定义的抽象方法
// 它是专门针对某种特定尺寸从类名推测可能是2倍尺寸桌面小部件的具体实现类
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 重写父类的onUpdate方法该方法会在小部件需要更新时被调用例如小部件首次添加到桌面、系统要求更新小部件等情况
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法来执行小部件更新的通用逻辑将具体的更新操作交给父类中定义的update方法处理
// 这样可以复用父类中关于更新小部件显示内容、设置点击事件等相关的逻辑代码
super.update(context, appWidgetManager, appWidgetIds);
}
// 实现父类中定义的抽象方法getLayoutId用于返回该小部件对应的布局资源ID这里返回的是R.layout.widget_2x
// 意味着该小部件使用名为widget_2x的布局文件来进行界面显示布局定义不同的布局文件可以包含不同的视图控件、排列方式等
=======
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 覆盖了onUpdate方法该方法是AppWidgetProvider的一个回调方法用于当小部件需要更新时被调用
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法传入context、appWidgetManager和appWidgetIds参数
super.update(context, appWidgetManager, appWidgetIds);
}
// 覆盖了getLayoutId方法该方法用于返回小部件的布局资源ID
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getLayoutId() {
// 返回名为widget_2x的布局资源ID这个布局资源定义了小部件的外观
return R.layout.widget_2x;
}
// 覆盖了getBgResourceId方法该方法用于根据传入的背景ID返回对应的背景资源ID
<<<<<<< HEAD
// 实现父类中定义的抽象方法getBgResourceId根据传入的背景颜色IDbgId获取对应的背景资源ID
// 这里通过调用ResourceParser.WidgetBgResources类中的方法来获取具体是获取2倍尺寸小部件对应的背景资源ID
// 使得小部件能根据不同的背景颜色ID显示相应的背景图片等资源
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getBgResourceId(int bgId) {
// 调用ResourceParser.WidgetBgResources类的getWidget2xBgResource方法传入bgId参数返回对应的背景资源ID
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
// 覆盖了getWidgetType方法该方法用于返回小部件的类型
<<<<<<< HEAD
// 实现父类中定义的抽象方法getWidgetType返回该小部件的类型标识这里返回的是Notes.TYPE_WIDGET_2X
// 用于区分不同尺寸、不同类型的小部件,在应用中可以根据这个类型标识来进行不同的业务逻辑处理,比如不同的显示样式、功能操作等
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getWidgetType() {
// 返回Notes.TYPE_WIDGET_2X这是一个常量表示这是一个2x2大小的小部件
return Notes.TYPE_WIDGET_2X;
}
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -14,7 +14,10 @@
* limitations under the License.
*/
// 定义了一个名为NoteWidgetProvider_4x的类它继承自NoteWidgetProvider类
<<<<<<< HEAD
// 包声明表明该类所属的包名为net.micode.notes.widget通常用于存放与桌面小部件相关的具体实现类从类名NoteWidgetProvider_4x推测这是针对特定尺寸可能是4倍尺寸小部件的相关实现类
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
@ -24,32 +27,56 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
<<<<<<< HEAD
// NoteWidgetProvider_4x类继承自NoteWidgetProvider抽象类需要实现抽象类中定义的抽象方法以提供针对特定类型这里可能是4倍尺寸小部件的具体功能实现
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 重写父类的onUpdate方法该方法会在桌面小部件需要更新时被调用例如系统触发小部件更新如时间间隔到了、配置变化等情况或者用户手动触发小部件更新操作时
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 通过调用父类的update方法来执行小部件更新的通用逻辑复用父类中已定义好的更新操作流程比如设置小部件的显示内容、点击事件等相关逻辑
// 子类只需关注自身特有的部分(如特定布局、资源获取等)即可,这样可以提高代码的复用性和可维护性
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类中定义的抽象方法getLayoutId用于返回该4倍尺寸小部件对应的布局资源ID这里返回的是R.layout.widget_4x
// 意味着该小部件会使用名为widget_4x的布局文件来进行界面的显示布局设置这个布局文件中定义了小部件在桌面上展示的具体视图结构、控件摆放等内容
=======
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 覆盖了onUpdate方法该方法是AppWidgetProvider的一个回调方法用于当小部件需要更新时被调用
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法传入context、appWidgetManager和appWidgetIds参数
super.update(context, appWidgetManager, appWidgetIds);
}
// getLayoutId方法用于返回小部件的布局资源ID
// 注意:这个方法没有被标记为@Override这意味着它不是覆盖父类的方法而是NoteWidgetProvider_4x类中新增的方法
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
protected int getLayoutId() {
// 返回名为widget_4x的布局资源ID这个布局资源定义了小部件的外观
return R.layout.widget_4x;
}
// 覆盖了getBgResourceId方法该方法用于根据传入的背景ID返回对应的背景资源ID
<<<<<<< HEAD
// 重写父类中定义的抽象方法getBgResourceId该方法根据传入的背景颜色标识IDbgId来获取对应的背景资源ID
// 这里借助ResourceParser.WidgetBgResources类中的方法来获取具体是获取适用于4倍尺寸小部件的背景资源ID
// 从而可以根据不同的背景颜色设置来展示相应的背景图片等资源,使小部件的外观符合预期的设计要求
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getBgResourceId(int bgId) {
// 调用ResourceParser.WidgetBgResources类的getWidget4xBgResource方法传入bgId参数返回对应的背景资源ID
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
// 覆盖了getWidgetType方法该方法用于返回小部件的类型
<<<<<<< HEAD
// 重写父类中定义的抽象方法getWidgetType返回该小部件的类型标识这里返回的是Notes.TYPE_WIDGET_4X
// 这个类型标识可以用于在整个应用中区分不同尺寸、不同功能特性的小部件,比如在处理小部件相关的业务逻辑时,根据不同的类型执行不同的操作,
// 像显示不同的内容、提供不同的交互功能等,有助于实现小部件功能的差异化和定制化
=======
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee
@Override
protected int getWidgetType() {
// 返回Notes.TYPE_WIDGET_4X这是一个常量表示这是一个4x4大小的小部件
return Notes.TYPE_WIDGET_4X;
}
}
<<<<<<< HEAD
}
=======
}
>>>>>>> 2f825b58ba9952e086b1b4b76135e78208e7a2ee

@ -0,0 +1,410 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "UTLab.h"
#define N 32//每个染色体的基因数
//#define W 100//试卷总分值
#define TYPE -1//题型数量
//#define M 2 //目标数目
//#define TYPE -1//求最大还是求最小
#define GEN 100//迭代次数
#define POP_SIZE 30//种群大小
#define SELECTQUE_SIZE 250 //选择题题库总量
#define TKQUE_SIZE 200 //填空题题库总量
#define ZHGQUE_SIZE 50 //主观题题库总量
#define NUM 4//题目特征数目
#define NUMCH 6//章节数目
#define NUMD 3//难度数目
#define DRATE1 0.2 //难度系数为1的预定义比率
#define DRATE2 0.6
#define DRATE3 0.2
#define CHRATE1 0.1//预定义每章的分值比率
#define CHRATE2 0.2
#define CHRATE3 0.2
#define CHRATE4 0.2
#define CHRATE5 0.2
#define CHRATE6 0.1
#define RCH 0.5
#define RD 0.5
#define P_MUTATION 0.008//变异系数
#define P_CROSSOVER 0.8//交叉系
double OBJECTIVE[POP_SIZE+1];
double DRATE[NUMD+1]={0,DRATE1,DRATE2,DRATE3}; //预定义难度系数比例
double CHRATE[NUMCH+1]={0,CHRATE1,CHRATE2,CHRATE3,CHRATE4,CHRATE5,CHRATE6};//预定义章节比
double CHROMOSOME[POP_SIZE+1][N+1];//染色体数组
double q[POP_SIZE+1];//存储选题到的题号的数组
int SELECTQUESTION[SELECTQUE_SIZE+1][NUM];//选择题题库;1/分值2、章节3、难度
int TKQUESTION[TKQUE_SIZE+1][NUM];//填空题题库;1/分值2、章节3、难度
int ZHGQUESTION[ZHGQUE_SIZE+1][NUM];//主观题题库;1/分值2、章节3、难度
static double caucal(double x[]); //计算新染色体的章节和难度比
static void initialquestion(void);//初始化题库
static void initialrate(void);//初始化各比例
static void initialization(void);//初始化
static void evaluation(int gen);
static void objective_function(void);
static void selection(void);
static void crossover(void);
static void mutation(void);
static int constraint_check(double x[]);
static double caucal(double x[])
{int i,j,t,s,m;
double chrate[NUMCH+1],drate[NUMD+1];//定义各章节分值比和难度比
double da=0;
double ba=0;
for (j=0;j<=NUMCH;j++)
{chrate[j]=0;}
for (j=0;j<=NUMD;j++)
{drate[j]=0;}
for(j=1;j<=30;j++)
{t=int(x[j]);
if(j<=15)
{
s=SELECTQUESTION[t][2];
chrate[s]+=2;
m=SELECTQUESTION[t][3];
drate[m]+=2;
}
else if(j>15&&j<=25)
{
s=TKQUESTION[t][2];
chrate[s]+=2;
m=TKQUESTION[t][3];
drate[m]+=2;
}
else
{s=ZHGQUESTION[t][2];
chrate[s]+=10;
m=ZHGQUESTION[t][3];
drate[m]+=10;
}
}
for(j=1;j<=NUMCH;j++)
{chrate[j]=chrate[j]/100;
//printf("第%d个染色体第%d章的分值比是%f\n",i,j,chrate[i][j]);
ba+=fabs(double(chrate[j]-CHRATE[j]));
}
//printf("第个染色体章的分值比平均值%f\n",ba/NUMCH);
x[31]=ba/NUMCH;
for(j=1;j<=NUMD;j++)
{drate[j]=drate[j]/100;
//printf("第%d个染色体第%d难度的分值比是%f\n",i,j,drate[i][j]);
da+=fabs(double(drate[j]-DRATE[j]));
}
//printf("第染色体难度差的平均值是%f\n",da/NUMD);
x[32]=da/NUMD;
return RCH*x[31]+RD*x[32];
}
static void objective_function(void)
{
int i;
for(i = 1; i <= POP_SIZE; i++) {
OBJECTIVE[i] =RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32];
}
}
static void initialquestion(void)
{ int i,j;
for(i=1;i<=SELECTQUE_SIZE+1;i++)//将选择题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)SELECTQUESTION[i][j]=2;
if(j==2)SELECTQUESTION[i][j]=int(myu(1,7));
if(j==3)SELECTQUESTION[i][j]=int(myu(1,4));
}
for(i=1;i<=TKQUE_SIZE+1;i++)//将填空题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)TKQUESTION[i][j]=2;
if(j==2)TKQUESTION[i][j]=int(myu(1,7));
if(j==3)TKQUESTION[i][j]=int(myu(1,4));
}
for(i=1;i<=ZHGQUE_SIZE+1;i++)//将填空题输入题库
for(j=1;j<=NUM;j++)
{
if(j==1)ZHGQUESTION[i][j]=10;
if(j==2)ZHGQUESTION[i][j]=int(myu(1,7));
if(j==3)ZHGQUESTION[i][j]=int(myu(1,4));
}
}
static void initialrate(void)//初始化章节和难度比例
{ int i,j,t,s,x;
double chrate[POP_SIZE+1][NUMCH+1],drate[POP_SIZE+1][NUMD+1];//定义各章节分值比和难度比
double da=0;
double ba=0;
for (i=1;i<=POP_SIZE;i++)
{for (j=1;j<=NUMCH;j++)
{chrate[i][j]=0;}
}
for (i=1;i<=POP_SIZE;i++)
{for (j=1;j<=NUMD;j++)
{drate[i][j]=0;}
}
for(i=1; i<=POP_SIZE; i++)
{
for(j=1;j<=30;j++)
{t=CHROMOSOME[i][j];
if(j<=15)
{
s=SELECTQUESTION[t][2];
chrate[i][s]+=2;
x=SELECTQUESTION[t][3];
drate[i][x]+=2;
}
else if(j>15&&j<=25)
{
s=TKQUESTION[t][2];
chrate[i][s]+=2;
x=TKQUESTION[t][3];
drate[i][x]+=2;
}
else
{s=ZHGQUESTION[t][2];
chrate[i][s]+=10;
x=ZHGQUESTION[t][3];
drate[i][x]+=10;
}
}
for(j=1;j<=NUMCH;j++)
{chrate[i][j]=chrate[i][j]/100;
//printf("第%d个染色体第%d章的分值比是%f\n",i,j,chrate[i][j]);
ba+=fabs(double(chrate[i][j]-CHRATE[j]));
}
//printf("第%d个染色体章的分值比平均值%f\n",i,ba/NUMCH);
CHROMOSOME[i][31]=ba/NUMCH;
ba=0;
for(j=1;j<=NUMD;j++)
{drate[i][j]=drate[i][j]/100;
//printf("第%d个染色体第%d难度的分值比是%f\n",i,j,drate[i][j]);
da+=fabs(double(drate[i][j]-DRATE[j]));
}
//printf("第%d个染色体难度差的平均值是%f\n",i,da/NUMD);
CHROMOSOME[i][32]=da/NUMD;
da=0;
}
}
static void initialization(void)//初始化染色体操作
{
int i,j;
for(i=1; i<=POP_SIZE; i++)//初始化染色体,待讨论!
{
for(j=1;j<=15;j++)//选择选择题
CHROMOSOME[i][j]=int(myu(1,SELECTQUE_SIZE+1));
for(j=16;j<=25;j++)//选择填空题
CHROMOSOME[i][j]=int(myu(1,TKQUE_SIZE+1));
for(;j<=30;j++)//select postive question
CHROMOSOME[i][j]=int(myu(1,ZHGQUE_SIZE+1));
}
}
static int constraint_check(double x[])
{
int n;
for(n=1;n<=30;n++) if(x[n]<=0) return 0;
return 1;
}
void main()
{
int i, j,t,k;
double a;
q[0]=0.05; a=0.05;//定义的比率
for(i=1; i<=POP_SIZE; i++) {a=a*0.95; q[i]=q[i-1]+a;}
initialquestion();
initialization();
initialrate();
evaluation(0);
for(i=1; i<=GEN; i++) {
selection();
crossover();
mutation();
evaluation(i);
t=1; k=1;
for(j=1;j<=POP_SIZE;j++)
if(t>RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32])
{
t=RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32];
k=j;
}
printf("第%d代的最佳章节比是%f,最佳难度比是%f\n",i,CHROMOSOME[k][31],CHROMOSOME[k][32]);
}
printf("题号 分值 章节 难度\n");
for(i=1; i<=1; i++)//输出生成的试卷
for(j=1;j<=30;j++)
{t=CHROMOSOME[i][j];
if(j<=15)
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",SELECTQUESTION[t][k]);
printf("\n");
}
else if(j>15&&j<=25)
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",TKQUESTION[t][k]);
printf("\n");
}
else
{ printf("%d\t",t);
for(k=1;k<NUM;k++)
printf("%d\t",ZHGQUESTION[t][k]);
printf("\n");
}
}
a=0;
for(i=1;i<=POP_SIZE;i++)
a+=(1-RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32]);
printf("最佳适应度为%f\n",a/0.3);
}
static void evaluation(int gen)
{
double a;
int i, j, label;
objective_function();//计算目标函数值
if(gen==0){
OBJECTIVE[0]=OBJECTIVE[1];
for(j = 1; j <= N; j++) CHROMOSOME[0][j]=CHROMOSOME[1][j];
}
for(i=0; i<POP_SIZE; i++){
label=0; a=OBJECTIVE[i];
for(j=i+1; j<=POP_SIZE; j++)
if((TYPE*a)<(TYPE*OBJECTIVE[j])) {
a=OBJECTIVE[j];
label=j;
}
if(label!=0) {
{
a=OBJECTIVE[i];
OBJECTIVE[i]=OBJECTIVE[label];
OBJECTIVE[label]=a;
}
for(j=1; j<=N; j++) {
a=CHROMOSOME[i][j];
CHROMOSOME[i][j]=CHROMOSOME[label][j];
CHROMOSOME[label][j]=a;
}
}
}
}
static void selection()
{
double r, temp[POP_SIZE+1][N+1];
int i, j, k;
for(i=1; i<=POP_SIZE; i++) {
r=myu(0, q[POP_SIZE]);
for(j=0; j<=POP_SIZE; j++) {
if(r<=q[j]) {
for(k=1; k<=N; k++) temp[i][k]=CHROMOSOME[j][k];
break;
}
}
}
for(i=1; i<=POP_SIZE; i++)
for(k=1; k<=N; k++)
CHROMOSOME[i][k]=temp[i][k];
}
static void crossover()
{
int i, j, jj, k, pop;
double a,r;
double x[N+1], y[N+1];
pop=POP_SIZE/2;
for(i=1; i<=pop; i++) {
if(myu(0,1)>P_CROSSOVER) continue;
j=(int)myu(1,16);
jj=(int)myu(1,16);
r=myu(0,1);
for(k=1; k<=15; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(SELECTQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(SELECTQUE_SIZE+1);
}
j=(int)myu(16,26);
jj=(int)myu(16,26);
r=myu(0,1);
for(k=16; k<=26; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(TKQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(SELECTQUE_SIZE+1);
}
j=(int)myu(26,31);
jj=(int)myu(26,31);
r=myu(0,1);
for(k=26; k<=30; k++) {
x[k]=int(r*CHROMOSOME[j][k]+(1-r)*CHROMOSOME[jj][k])%(ZHGQUE_SIZE+1);
y[k]=int(r*CHROMOSOME[jj][k]+(1-r)*CHROMOSOME[j][k])%(ZHGQUE_SIZE+1);
}
a=caucal(x);
if((constraint_check(x)==1)&&a<=(RCH*CHROMOSOME[j][31]+RD*CHROMOSOME[j][32]))
for(k=1; k<=N; k++) CHROMOSOME[j][k]=x[k];
a=caucal(y);
if((constraint_check(y)==1)&&a<=(RCH*CHROMOSOME[jj][31]+RD*CHROMOSOME[jj][32]))
for(k=1; k<=N; k++) CHROMOSOME[jj][k]=y[k];
}
}
static void mutation(void)
{
int i, j, k;
double x[N+1], y[N+1], infty, direction[N+1],a;
double INFTY=10, precision=0.0001;
for(i=1; i<=POP_SIZE; i++) {
if(myu(0,1)>P_MUTATION) continue;
for(k=1; k<=N; k++) x[k] = CHROMOSOME[i][k];
for(k=1; k<=30; k++)
if(myu(0,1)<0.5) direction[k]=myu(-1,1);
else direction[k]=0;
infty=myu(0,INFTY);
while(infty>precision) {
for(j=1; j<=30; j++) y[j]=int(x[j]+infty*direction[j]);
a=caucal(y);
if(constraint_check(y)==1&&a<=(RCH*CHROMOSOME[i][31]+RD*CHROMOSOME[i][32])) {
for(k=1; k<=N; k++) CHROMOSOME[i][k]=y[k];
break;
}
infty=myu(0,infty);
}
}
}
Loading…
Cancel
Save