Compare commits

..

No commits in common. 'main' and 'a_branch' have entirely different histories.

1
.gitignore vendored

@ -1 +0,0 @@
*/~$*

@ -36,59 +36,38 @@ public class Contact {
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
/**
*
*
*
*
* @param context 访
* @param phoneNumber
* @return null
*/
public static String getContact(Context context, String phoneNumber) {
// 检查缓存是否已初始化,未初始化则进行初始化
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 检查缓存中是否含有指定电话号码的联系人名称,如果有直接返回
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
public static String getContact(Context context, String phoneNumber) {
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 构建查询选型字符串,用于匹配电话号码
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
// 通过系统接口查询匹配的联系人信息
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
// 如果查询到匹配的联系人信息
if (cursor != null && cursor.moveToFirst()) {
try {
// 获取联系人名称
String name = cursor.getString(0);
// 将名称缓存
sContactCache.put(phoneNumber, name);
// 返回联系人名称
return name;
} catch (IndexOutOfBoundsException e) {
// 如果出现索引越界异常记录日志并返回null
Log.e(TAG, " Cursor get string error " + e.toString());
if (cursor != null && cursor.moveToFirst()) {
try {
String name = cursor.getString(0);
sContactCache.put(phoneNumber, name);
return name;
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, " Cursor get string error " + e.toString());
return null;
} finally {
cursor.close();
}
} else {
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
} finally {
// 关闭游标,释放资源
cursor.close();
}
} else {
// 如果没有匹配的联系人记录日志并返回null
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
}
}
}

@ -210,23 +210,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
super(context, DB_NAME, null, DB_VERSION);
}
/**
*
*
* @param db SQL
*
* SQL
*
* 使
*/
public void createNoteTable(SQLiteDatabase db) {
// 执行SQL语句以创建笔记表
db.execSQL(CREATE_NOTE_TABLE_SQL);
// 重新创建笔记表的触发器
reCreateNoteTableTriggers(db);
// 创建系统文件夹,确保系统笔记有存储位置
createSystemFolder(db);
// 记录日志,表明笔记表已成功创建
Log.d(TAG, "note table has been created");
}

@ -85,314 +85,206 @@ public class NotesProvider extends ContentProvider {
return true;
}
@Override
/**
* queryURI
*
* @param uri 访URI
* @param projection
* @param selection where
* @param selectionArgs where
* @param sortOrder
* @return null
*/
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
// 获取可读的数据库实例
SQLiteDatabase db = mHelper.getReadableDatabase();
// 用于存储URI中的id部分如果有的话
String id = null;
// 根据URI类型执行不同的查询
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 查询NOTE表
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
// 从URI路径段中获取note的id
id = uri.getPathSegments().get(1);
// 查询NOTE表中的特定id项
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_DATA:
// 查询DATA表
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_DATA_ITEM:
// 从URI路径段中获取data的id
id = uri.getPathSegments().get(1);
// 查询DATA表中的特定id项
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
// 确保没有指定排序顺序和投影,因为这是搜索专用查询
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
SQLiteDatabase db = mHelper.getReadableDatabase();
String id = null;
switch (mMatcher.match(uri)) {
case URI_NOTE:
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_DATA:
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
// 初始化搜索字符串
String searchString = null;
// 根据URI类型获取搜索字符串
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
String searchString = null;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
} else {
searchString = uri.getQueryParameter("pattern");
}
} else {
searchString = uri.getQueryParameter("pattern");
}
// 如果搜索字符串为空则返回null
if (TextUtils.isEmpty(searchString)) {
return null;
}
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
// 格式化搜索字符串,添加通配符
searchString = String.format("%%%s%%", searchString);
// 执行原始查询以获取搜索结果
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
// 记录查询异常
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default:
// 如果URI不匹配任何已知类型抛出非法参数异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果返回的光标不为空设置通知URI以便数据变化时可以发送通知
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
try {
searchString = String.format("%%%s%%", searchString);
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
// 返回查询结果光标
return c;
}
@Override
/**
* URI
*
* @param uri URI
* @param values ContentValues
* @return URI
*/
public Uri insert(Uri uri, ContentValues values) {
// 获取可写数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 初始化数据ID和笔记ID为0
long dataId = 0, noteId = 0, insertedId = 0;
// 根据URI类型执行不同的插入操作
switch (mMatcher.match(uri)) {
// 处理笔记URI
case URI_NOTE:
// 执行插入操作并获取新记录的ID
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
// 处理数据URI
case URI_DATA:
// 检查ContentValues中是否包含笔记ID
if (values.containsKey(DataColumns.NOTE_ID)) {
// 获取笔记ID
noteId = values.getAsLong(DataColumns.NOTE_ID);
} else {
// 如果没有笔记ID则记录错误信息
Log.d(TAG, "Wrong data format without note id:" + values.toString());
}
// 执行插入操作并获取新记录的ID
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
// 默认情况下,抛出异常
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果笔记ID有效则通知相应的URI发生了改变
// Notify the note uri
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 如果数据ID有效则通知相应的URI发生了改变
// Notify the data uri
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
// 返回更新后的URI附加新插入数据的ID
return ContentUris.withAppendedId(uri, insertedId);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 初始化删除记录数
int count = 0;
// 用于存储ID
String id = null;
// 获取可写数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否删除了数据
boolean deleteData = false;
// 根据URI类型执行不同的删除操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 对于URI_NOTE添加附加的删除条件
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
// 执行删除操作
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 获取ID
id = uri.getPathSegments().get(1);
// 小于等于0的ID是系统文件夹不允许删除
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
}
// 构造删除语句并执行
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 执行数据删除
count = db.delete(TABLE.DATA, selection, selectionArgs);
// 标记数据已删除
deleteData = true;
break;
case URI_DATA_ITEM:
// 获取ID
id = uri.getPathSegments().get(1);
// 构造删除语句并执行
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
// 标记数据已删除
deleteData = true;
break;
default:
// 对于未知URI抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果有数据被删除则通知系统更新UI
if (count > 0) {
if (deleteData) {
// 如果删除了数据则通知系统刷新Notes.CONTENT_NOTE_URI对应的UI
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知系统刷新被删除数据对应的UI
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回删除的记录数
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 初始化更新数量和笔记ID
int count = 0;
String id = null;
// 获取可写数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否更新了数据表
boolean updateData = false;
// 根据URI类型执行不同的更新操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 在更新笔记前增加版本号
increaseNoteVersion(-1, selection, selectionArgs);
// 更新笔记表
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 获取具体笔记ID
id = uri.getPathSegments().get(1);
// 增加指定笔记的版本号
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
// 更新笔记表指定ID
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 更新数据表
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM:
// 获取具体数据ID
id = uri.getPathSegments().get(1);
// 更新数据表指定ID
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
// 抛出异常表示未知的URI
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果有数据被更新,则发送内容改变广播
if (count > 0) {
if (updateData) {
// 如果更新了数据表,则发送数据表改变广播
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 发送URI对应的数据改变广播
getContext().getContentResolver().notifyChange(uri, null);
}
// 返回更新的行数
return count;
}
/**
*
*
* @param selection null
* @return null
*
*
* nullAND
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
*
*
* @param id ID
* @param selection
* @param selectionArgs
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
// 创建一个字符串构建器来拼接SQL语句
StringBuilder sql = new StringBuilder(120);
// 拼接SQL的UPDATE部分
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
// 拼接SQL的SET部分将版本号加1
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
// 根据ID和选择条件决定是否添加WHERE子句
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
// 如果ID大于0则直接指定ID作为WHERE子句的一部分
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
// 如果有选择条件,则解析选择条件并替换占位符
if (!TextUtils.isEmpty(selection)) {
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
@ -401,7 +293,6 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel
sql.append(selectString);
}
// 执行SQL语句更新笔记的版本号
mHelper.getWritableDatabase().execSQL(sql.toString());
}

@ -137,60 +137,46 @@ public class BackupUtils {
}
/**
*
*
* @param folderId ID
* @param ps
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// 查询属于此文件夹的便签
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
// 移动光标到第一行
if (notesCursor.moveToFirst()) {
// 遍历每一行便签
do {
// 打印便签的最后修改日期
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于此便签的数据
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
// 关闭光标
notesCursor.close();
}
}
/**
* 便
*
* @param noteId 便ID
* @param ps 便
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
// 查询便笺的数据
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
// 当查询结果不为空时
if (dataCursor != null) {
// 如果查询结果有数据
if (dataCursor.moveToFirst()) {
do {
// 获取当前行的MIME类型
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
// 根据MIME类型处理数据
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// 打印电话号码
// Print phone number
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
@ -199,17 +185,16 @@ public class BackupUtils {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// 打印通话日期
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// 打印通话附件位置
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
// 打印普通便笺内容
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
@ -218,10 +203,9 @@ public class BackupUtils {
}
} while (dataCursor.moveToNext());
}
// 关闭Cursor
dataCursor.close();
}
// 在便笺之间打印一行分隔符
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
@ -231,29 +215,21 @@ public class BackupUtils {
}
}
/**
*
* PrintStream
*
*
* @return STATE_SD_CARD_UNMOUONTEDSDSTATE_SYSTEM_ERRORSTATE_SUCCESS
* Note will be exported as text which is user readable
*/
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取用于导出的PrintStream对象
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 导出文件夹及其笔记
// First export folder and its notes
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
@ -264,9 +240,9 @@ public class BackupUtils {
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// 打印文件夹名称
// Print folder's name
String folderName = "";
if (folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
@ -281,21 +257,20 @@ public class BackupUtils {
folderCursor.close();
}
// 导出根文件夹中的笔记
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
// 打印笔记日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于此笔记的数据
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
@ -307,91 +282,61 @@ public class BackupUtils {
return STATE_SUCCESS;
}
/**
* PrintStream
* SDPrintStream
*
* @return PrintStreamnull
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
private PrintStream getExportToTextPrintStream() {
// 在SD卡上生成文件
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
// 如果文件创建失败则记录错误并返回null
Log.e(TAG, "create file to exported failed");
return null;
}
// 保存文件名和目录路径以便后续使用
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
// 创建FileOutputStream并基于它创建PrintStream对象
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
// 如果文件未找到则记录异常并返回null
e.printStackTrace();
return null;
} catch (NullPointerException e) {
// 如果出现空指针异常则记录异常并返回null
e.printStackTrace();
return null;
}
// 返回创建的PrintStream对象
return ps;
}
}
/**
* SD
* SD
*
*
* @param context
* @param filePathResId ID
* @param fileNameFormatResId ID
* @return FileIOnull
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 使用StringBuilder拼接文件的完整路径
StringBuilder sb = new StringBuilder();
// 获取并拼接SD卡挂载点的路径
sb.append(Environment.getExternalStorageDirectory());
// 获取文件路径字符串资源并拼接到sb
sb.append(context.getString(filePathResId));
// 创建文件目录对象
File filedir = new File(sb.toString());
// 获取文件名格式字符串资源支持日期格式化并拼接到sb
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// 创建文件对象
File file = new File(sb.toString());
try {
// 如果目录不存在,则创建目录
if (!filedir.exists()) {
filedir.mkdir();
}
// 如果文件不存在,则创建文件
if (!file.exists()) {
file.createNewFile();
}
// 返回创建的文件对象
return file;
} catch (SecurityException e) {
// 处理安全异常,例如没有写入权限
e.printStackTrace();
} catch (IOException e) {
// 处理IO异常例如文件系统错误
e.printStackTrace();
}
// 如果发生异常则返回null
return null;
}
}

@ -37,101 +37,57 @@ import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";
/**
* 便
*
* @param resolver ContentResolver
* @param ids 便ID
* @return truefalse
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 检查ids是否为null
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
// 检查ids集合是否为空
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
// 初始化操作列表用于批量操作
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历ids集合为每个id构建删除操作
for (long id : ids) {
// 避免删除系统根目录
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
// 使用ContentProviderOperation构建删除操作并添加到操作列表中
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
// 尝试执行批量删除操作
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 检查删除结果
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
// 处理远程异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 处理操作应用异常
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
// 如果发生异常则返回false
return false;
}
/**
* 便
* 便IDID便
*
* @param resolver 访便
* @param id 便
* @param srcFolderId 便ID
* @param desFolderId 便ID
*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
// 创建一个新的内容值对象,用于存储将要更新的数据
ContentValues values = new ContentValues();
// 设置便签的新父ID即将便签移动到的目标文件夹
values.put(NoteColumns.PARENT_ID, desFolderId);
// 记录便签原始所在的文件夹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);
}
/**
* 便
*
* @param resolver
* @param ids 便ID
* @param folderId ID
* @return truefalse
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 检查ids参数是否为null
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
// 初始化操作列表用于批量操作
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历每个便签ID构建更新操作
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
@ -140,12 +96,10 @@ public class DataUtils {
operationList.add(builder.build());
}
// 尝试执行批量操作
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 检查操作结果
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "move notes to folder failed, ids:" + ids.toString());
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
@ -157,247 +111,139 @@ public class DataUtils {
return false;
}
/**
* Uri
*
* @param resolver ContentResolver
* @return
* 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,
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
if (cursor != null) {
// 移动到查询结果的首行
if (cursor.moveToFirst()) {
if(cursor != null) {
if(cursor.moveToFirst()) {
try {
// 获取查询结果中的文件夹数量
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
// 日志记录:获取文件夹数量失败
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
// 关闭游标以释放资源
cursor.close();
}
}
}
// 返回用户文件夹的数量
return count;
}
/**
*
*
* ID
*
* @param resolver ContentResolver 访
* @param noteId ID
* @param type
* @return truefalse
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 根据笔记ID和类型查询笔记信息确保笔记不在回收站文件夹中。
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
// 初始化存在标志为false。
boolean exist = false;
// 如果查询结果不为空,处理结果。
if (cursor != null) {
// 如果查询结果包含数据将exist设置为true。
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标以释放资源。
cursor.close();
}
// 返回判断结果。
return exist;
}
/**
*
*
* @param resolver ContentResolver
* @param noteId ID
* @return truefalse
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 根据笔记ID查询数据库以检查笔记是否存在
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
// 初始化 exist 标志为 false
boolean exist = false;
// 如果查询结果不为空,则处理结果
if (cursor != null) {
// 如果查询返回的行数大于零,则表示笔记存在
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭游标以释放资源
cursor.close();
}
// 返回笔记是否存在结果
return exist;
}
/**
* ID
*
* @param resolver ContentResolver
* @param dataId ID
* @return truefalse
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 根据指定ID查询数据库中的笔记
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
// 初始化是否存在标志为false
boolean exist = false;
// 如果查询结果不为空,则处理查询结果
if (cursor != null) {
// 如果查询结果数量大于0则笔记存在
if (cursor.getCount() > 0) {
exist = true;
}
// 关闭Cursor以释放资源
cursor.close();
}
// 返回笔记是否存在
return exist;
}
/**
*
*
*
*
* @param resolver 访
* @param name
* @return truefalse
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 查询类型为文件夹且不在回收站中的文件夹,并根据名称进行筛选
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
// 初始化存在性标志为false
boolean exist = false;
// 如果游标不为空,表示查询有结果
if(cursor != null) {
// 如果游标计数大于0表示存在匹配的文件夹
if(cursor.getCount() > 0) {
exist = true;
}
// 关闭游标,释放资源
cursor.close();
}
// 返回存在性标志
return exist;
}
/**
* ID
*
*
* @param resolver 访
* @param folderId ID
* @return HashSet
* null
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 查询属于指定文件夹的笔记基于笔记的内容URI和文件夹ID
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
// 初始化一个集合来存储笔记小部件的属性
HashSet<AppWidgetAttribute> set = null;
if (c != null) {
// 如果查询结果不为空且有数据,开始处理查询结果
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
// 创建笔记小部件属性对象,并从查询结果中提取数据
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
// 将提取的数据添加到集合中
set.add(widget);
} catch (IndexOutOfBoundsException e) {
// 捕获并记录可能的查询结果索引越界异常
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
// 关闭查询结果的游标,释放资源
c.close();
}
// 返回处理结果集合
return set;
}
/**
* 便ID
* Notes便ID
*
* @param resolver ContentResolver访
* @param noteId 便ID便
* @return
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 查询Notes的内容提供者返回满足条件的Cursor对象
// 参数查询的URI、投影的列、选择条件和对应的参数、分组方式
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);
// 检查Cursor是否有效并且移动到第一个数据位置
if (cursor != null && cursor.moveToFirst()) {
try {
// 返回电话号码索引0表示查询结果的第一列
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
// 捕获IndexOutOfBoundsException记录日志
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
// 确保关闭Cursor以释放资源
cursor.close();
}
}
// 如果Cursor为空或查询不到数据返回空字符串
return "";
}
/**
* Note ID
*
* @param resolver ContentResolver访ContentProvider
* @param phoneNumber
* @param callDate
* @return Note ID0
*/
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 查询通话记录的Note ID
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
@ -405,80 +251,45 @@ public class DataUtils {
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
// 检查查询结果
if (cursor != null) {
// 如果查询结果中存在数据
if (cursor.moveToFirst()) {
try {
// 返回第一条记录的Note ID
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
// 处理索引越界异常
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
// 关闭Cursor对象
cursor.close();
}
// 如果查询结果为空或者发生异常则返回0
return 0;
}
/**
* 便ID便
*
* @param resolver ContentResolver访
* @param noteId 便ID
* @return 便便IllegalArgumentException
*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 查询指定ID的便签的摘要信息
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
// 检查查询结果
if (cursor != null) {
// 初始化摘要信息
String snippet = "";
// 如果查询结果中有数据
if (cursor.moveToFirst()) {
// 获取摘要信息
snippet = cursor.getString(0);
}
// 关闭游标以释放资源
cursor.close();
// 返回摘要信息
return snippet;
}
// 如果查询结果为空,抛出异常
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
/**
*
*
*
*
* @param snippet
* @return
*/
public static String getFormattedSnippet(String snippet) {
// 检查代码片段是否为null
if (snippet != null) {
// 去除字符串首尾的空白字符
snippet = snippet.trim();
// 查找第一个换行符的位置
int index = snippet.indexOf('\n');
// 如果找到了换行符
if (index != -1) {
// 截取换行符之前的部分
snippet = snippet.substring(0, index);
}
}
// 返回格式化后的代码片段
return snippet;
}
}

@ -48,17 +48,12 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
@Override
protected void onCreate(Bundle savedInstanceState) {
// 调用父类的onCreate方法
super.onCreate(savedInstanceState);
// 隐藏标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前窗口
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
@ -66,32 +61,24 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
// 获取启动该Activity的Intent
Intent intent = getIntent();
// 尝试从Intent中提取便签ID并获取便签的预览内容
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
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;
} catch (IllegalArgumentException e) {
// 如果发生异常,打印错误信息并返回
e.printStackTrace();
return;
}
// 初始化MediaPlayer对象
mPlayer = new MediaPlayer();
// 检查便签数据库中是否存在该便签
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 如果存在,显示操作对话框并播放提醒声音
showActionDialog();
playAlarmSound();
} else {
// 如果不存在关闭该Activity
finish();
}
}
@ -101,34 +88,21 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
return pm.isScreenOn();
}
/**
*
* URI
*
* 使MediaPlayer
*/
private void playAlarmSound() {
// 获取设备的默认闹钟铃声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) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置音频数据源为刚才获取的闹钟铃声URI
mPlayer.setDataSource(this, url);
// 准备MediaPlayer播放器
mPlayer.prepare();
// 设置播放器进行循环播放
mPlayer.setLooping(true);
// 开始播放闹钟声音
mPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
@ -145,52 +119,26 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
/**
*
* mSnippet
*
* 使
* OnDismissListener
*/
private void showActionDialog() {
// 创建一个对话框构建器
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框标题为应用名称
dialog.setTitle(R.string.app_name);
// 设置对话框内容为mSnippet变量的值
dialog.setMessage(mSnippet);
// 设置对话框的正向按钮,并使用当前对象作为点击事件监听器
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于点亮状态,添加一个负向按钮,并使用当前对象作为点击事件监听器
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 显示对话框,并设置对话框关闭时的监听器为当前对象
dialog.show().setOnDismissListener(this);
}
/**
*
*
* @param dialog
* @param which
*/
public void onClick(DialogInterface dialog, int which) {
// 根据点击的按钮类型执行相应的逻辑
switch (which) {
// 如果点击的是对话框的负面按钮
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个指向NoteEditActivity的意图
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置意图动作为查看,表示即将查看或编辑一个便签
intent.setAction(Intent.ACTION_VIEW);
// 将便签的唯一标识符添加到意图中以便NoteEditActivity可以识别要编辑的便签
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动NoteEditActivity
startActivity(intent);
break;
default:
// 对于其他按钮,默认情况下不执行任何操作
break;
}
}

@ -40,10 +40,7 @@ public class AlarmInitReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前时间戳,用于后续判断备忘录是否需要提醒
long currentDate = System.currentTimeMillis();
// 通过ContentResolver查询满足条件的备忘录条目
// 查询条件:未被标记为已提醒且类型为普通备忘录
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
@ -51,25 +48,17 @@ public class AlarmInitReceiver extends BroadcastReceiver {
null);
if (c != null) {
// 如果有满足条件的备忘录条目
if (c.moveToFirst()) {
do {
// 获取备忘录条目的提醒时间戳
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个Intent用于发送广播到AlarmReceiver
Intent sender = new Intent(context, AlarmReceiver.class);
// 将备忘录条目的ID附加到Intent的数据中
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个PendingIntent用于延迟执行操作
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取AlarmManager服务
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 设置一个精确的闹钟时间使用RTC_WAKEUP类型确保设备在必要时唤醒
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭Cursor以释放资源
c.close();
}
}

@ -149,59 +149,38 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 当活动返回结果时,检查结果是否为成功
// 只有当返回结果为成功,并且请求码为打开或新建笔记时,才进行数据更新
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
// 更新笔记列表适配器的数据源为null触发UI重新拉取数据
mNotesListAdapter.changeCursor(null);
} else {
// 其他情况交由父类处理
super.onActivityResult(requestCode, resultCode, data);
}
}
/**
*
* raw便
*/
private void setAppInfoFromRawRes() {
// 获取SharedPreferences实例用于存储应用的首选项
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
// 检查是否已经添加过介绍,如果已经添加,则不执行后续操作
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
// 使用StringBuilder来拼接读取的介绍文本
StringBuilder sb = new StringBuilder();
// 定义InputStream实例用于读取raw资源
InputStream in = null;
try {
// 打开raw资源文件
in = getResources().openRawResource(R.raw.introduction);
in = getResources().openRawResource(R.raw.introduction);
if (in != null) {
// 将InputStream包装为InputStreamReader以便按字符读取
InputStreamReader isr = new InputStreamReader(in);
// 再将InputStreamReader包装为BufferedReader提高读取效率
BufferedReader br = new BufferedReader(isr);
char[] buf = new char[1024];
char [] buf = new char[1024];
int len = 0;
// 循环读取文件内容,直到读取结束
while ((len = br.read(buf)) > 0) {
sb.append(buf, 0, len);
}
} else {
// 如果无法打开raw资源文件记录错误日志并退出方法
Log.e(TAG, "Read introduction file error");
return;
}
} catch (IOException e) {
// 捕获IOException异常记录堆栈信息并退出方法
e.printStackTrace();
return;
} finally {
// 确保关闭InputStream资源避免资源泄露
if (in != null) {
if(in != null) {
try {
in.close();
} catch (IOException e) {
@ -211,17 +190,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
// 创建一个空的工作便笺,用于存储应用介绍文本
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED);
// 设置工作便笺的文本内容为刚才读取的介绍文本
note.setWorkingText(sb.toString());
// 尝试保存便笺如果保存成功更新SharedPreferences标记表示已经添加过介绍
if (note.saveNote()) {
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();
} else {
// 如果保存便笺失败,记录错误日志并退出方法
Log.e(TAG, "Save introduction note error");
return;
}
@ -261,78 +236,50 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private ActionMode mActionMode;
private MenuItem mMoveMenu;
/**
*
*
* @param mode
* @param menu
* @return true
*/
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// 充气布局到menu对象以便后续添加菜单项
getMenuInflater().inflate(R.menu.note_list_options, menu);
// 找到删除菜单项,并设置点击监听器为当前对象
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
// 找到移动菜单项,并先设置为不可见,后续根据条件设置可见性
mMoveMenu = menu.findItem(R.id.move);
// 如果当前选中的笔记在通话记录文件夹下或者用户没有创建任何文件夹,则隐藏移动菜单项
if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
mMoveMenu.setVisible(false);
} else {
// 否则,显示移动菜单项,并设置点击监听器为当前对象
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
// 设置当前操作模式
mActionMode = mode;
// 设置笔记列表适配器的选择模式为true允许项目选择
mNotesListAdapter.setChoiceMode(true);
// 禁用长按可点击,防止弹出上下文菜单
mNotesListView.setLongClickable(false);
// 隐藏添加新笔记的按钮,因为当前在操作模式下
mAddNewNote.setVisibility(View.GONE);
// 创建自定义视图并充气布局,用于操作模式的自定义视图
View customView = LayoutInflater.from(NotesListActivity.this)
.inflate(R.layout.note_list_dropdown_menu, null);
// 设置操作模式的自定义视图为刚才创建的视图
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
// 初始化下拉菜单对象,并设置其各项属性
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
// 设置下拉菜单项的点击监听器,用于全选和取消全选笔记
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {
// 根据适配器当前的选择状态来选择或取消选择所有项目
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
// 更新菜单项的显示状态
updateMenu();
return true;
}
});
return true;
}
/**
*
*/
private void updateMenu() {
// 获取已选择的笔记数量
int selectedCount = mNotesListAdapter.getSelectedCount();
// 更新下拉菜单标题,使用资源文件中的格式,并填入已选择的笔记数量
// Update dropdown menu
String format = getResources().getString(R.string.menu_select_title, selectedCount);
mDropDownMenu.setTitle(format);
// 查找“全选”菜单项,并根据当前选择状态更新其标题和选中状态
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);
if (item != null) {
// 如果所有项目都被选中,则将“全选”菜单项设为选中状态,并修改其标题为“取消全选”
if (mNotesListAdapter.isAllSelected()) {
item.setChecked(true);
item.setTitle(R.string.menu_deselect_all);
} else {
// 否则,将“全选”菜单项设为未选中状态,并恢复其标题为“全选”
item.setChecked(false);
item.setTitle(R.string.menu_select_all);
}
@ -365,76 +312,53 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
updateMenu();
}
/**
*
*
* @param item
* @return truefalse
*/
public boolean onMenuItemClick(MenuItem item) {
// 检查是否有选中的便签如果没有选中任何便签则提示用户并返回true
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show();
return true;
}
// 根据点击的菜单项ID进行不同的操作
switch (item.getItemId()) {
case R.id.delete:
// 创建确认删除对话框
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_notes,
mNotesListAdapter.getSelectedCount()));
mNotesListAdapter.getSelectedCount()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
}
});
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.move:
// 启动查询目标文件夹活动
startQueryDestinationFolders();
break;
default:
// 如果菜单项ID不匹配任何已知选项则返回false
return false;
}
// 如果处理了菜单项则返回true
return true;
}
}
private class NewNoteOnTouchListener implements OnTouchListener {
/**
* 便
*
*
* @param v
* @param event
* @return truefalse
*/
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// 获取窗口管理器的默认显示对象
Display display = getWindowManager().getDefaultDisplay();
// 获取屏幕高度
int screenHeight = display.getHeight();
// 获取新建便笺视图的高度
int newNoteViewHeight = mAddNewNote.getHeight();
// 计算屏幕高度减去新建便笺视图高度的差值
int start = screenHeight - newNoteViewHeight;
// 计算事件Y坐标
int eventY = start + (int) event.getY();
// 如果状态为子文件夹编辑状态,需要减去标题栏的高度
/**
* Minus TitleBar's height
*/
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
@ -448,65 +372,45 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* Notice that, if the background of the button changes, the formula should
* also change. This is very bad, just for the UI designer's strong requirement.
*/
// 处理"新建便笺"按钮透明部分的触摸事件
if (event.getY() < (event.getX() * (-0.12) + 94)) {
// 获取列表视图的最后一个子视图
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
// 初始化原始Y坐标和分发Y坐标
mOriginY = (int) event.getY();
mDispatchY = eventY;
// 设置事件位置为分发Y坐标
event.setLocation(event.getX(), mDispatchY);
// 标记为需要分发事件
mDispatch = true;
// 分发触摸事件到列表视图并返回结果
return mNotesListView.dispatchTouchEvent(event);
}
}
break;
}
case MotionEvent.ACTION_MOVE: {
// 如果事件需要分发则更新分发Y坐标并重新设置事件位置
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
// 分发触摸事件到列表视图并返回结果
return mNotesListView.dispatchTouchEvent(event);
}
break;
}
default: {
// 在触摸事件结束时,重置分发标记并分发最后一个事件到列表视图
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
// 分发触摸事件到列表视图并返回结果
return mNotesListView.dispatchTouchEvent(event);
}
break;
}
}
// 如果事件没有被处理则返回false
return false;
}
};
/**
*
* ID
*
*/
private void startAsyncNotesListQuery() {
// 根据当前文件夹ID选择适当的查询条件
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
// 启动背景查询处理程序执行笔记列表查询
// 参数包括查询标识符、外部上下文、查询的URI、投影数组、选择条件和参数以及排序顺序
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
String.valueOf(mCurrentFolderId)
@ -518,77 +422,43 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
super(contentResolver);
}
/**
*
*
* @param token
* @param cookie 使
* @param cursor
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// 根据不同的token值执行相应的处理
switch (token) {
// 当token为获取笔记列表的查询标识时
case FOLDER_NOTE_LIST_QUERY_TOKEN:
// 更新笔记列表的适配器,以显示新的查询结果
mNotesListAdapter.changeCursor(cursor);
break;
// 当token为获取文件夹列表的查询标识时
case FOLDER_LIST_QUERY_TOKEN:
// 检查查询结果是否有效且包含数据
if (cursor != null && cursor.getCount() > 0) {
// 显示文件夹列表菜单,传递查询结果作为参数
showFolderListMenu(cursor);
} else {
// 记录查询失败的日志信息
Log.e(TAG, "Query folder failed");
}
break;
// 对于其他token值不做任何处理
default:
return;
}
}
}
/**
*
*
* @param cursor
*/
private void showFolderListMenu(Cursor cursor) {
// 创建一个对话框构建器
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
// 设置对话框标题为“选择文件夹”
builder.setTitle(R.string.menu_title_select_folder);
// 创建一个文件夹列表适配器,并使用传入的游标填充数据
final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor);
// 设置适配器和对话框项点击监听器
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
/**
*
*
* @param dialog
* @param which
*/
public void onClick(DialogInterface dialog, int which) {
// 批量移动选中的便签到点击的文件夹
DataUtils.batchMoveToFolder(mContentResolver,
mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));
// 显示移动便签到文件夹的提示信息
Toast.makeText(
NotesListActivity.this,
getString(R.string.format_move_notes_to_folder,
mNotesListAdapter.getSelectedCount(),
adapter.getFolderName(NotesListActivity.this, which)),
Toast.LENGTH_SHORT).show();
// 结束当前的动作模式
mModeCallBack.finishActionMode();
}
});
// 显示对话框
builder.show();
}
@ -599,51 +469,30 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
/**
* 便便
* 便
* 便便
*/
private void batchDelete() {
// 创建一个异步任务,用于在后台进行删除操作
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
/**
* 线
* @param unused 使
* @return 便
*/
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
// 获取当前选中的便签小部件
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
// 根据是否为同步模式决定处理方式
if (!isSyncMode()) {
// 如果不是同步模式,直接删除便签
// if not synced, delete notes directly
if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
.getSelectedItemIds())) {
// 删除成功,不需额外处理
} else {
// 删除失败,记录错误日志
Log.e(TAG, "Delete notes error, should not happens");
}
} else {
// 如果是同步模式,将删除的便签移动到回收站文件夹
// in sync mode, we'll move the deleted note into the trash
// folder
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {
// 移动失败,记录错误日志
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
// 返回涉及的便签小部件集合,用于后续更新小部件视图
return widgets;
}
/**
* 线便
* @param widgets 便
*/
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
// 遍历每个便签小部件,更新其视图
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
@ -652,44 +501,28 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
// 结束操作模式
mModeCallBack.finishActionMode();
}
}.execute();
}
/**
*
*
*
*
*
* @param folderId ID
*/
private void deleteFolder(long folderId) {
// 检查是否尝试删除根文件夹,记录错误并退出
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
// 使用HashSet存储待删除的文件夹ID
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
// 获取与文件夹关联的小部件集合
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver, folderId);
// 根据同步模式选择删除方式
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver,
folderId);
if (!isSyncMode()) {
// 如果不是同步模式,直接删除文件夹
// if not synced, delete folder directly
DataUtils.batchDeleteNotes(mContentResolver, ids);
} else {
// 如果是同步模式,将删除的文件夹移动到回收站
// in sync mode, we'll move the deleted folder into the trash folder
DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);
}
// 如果存在关联的小部件,遍历并更新小部件
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
@ -707,36 +540,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
/**
* UI
*
* @param data NoteItemData
*/
private void openFolder(NoteItemData data) {
// 设置当前文件夹ID
mCurrentFolderId = data.getId();
// 启动异步查询以获取笔记列表
startAsyncNotesListQuery();
// 如果打开的是通话记录文件夹
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
// 设置状态为通话记录文件夹状态
mState = ListEditState.CALL_RECORD_FOLDER;
// 隐藏添加新笔记按钮,因为通话记录文件夹可能不支持添加新笔记
mAddNewNote.setVisibility(View.GONE);
} else {
// 否则,设置状态为子文件夹状态
mState = ListEditState.SUB_FOLDER;
}
// 设置标题栏文本,如果是通话记录文件夹,则使用资源文件中的名称
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mTitleBar.setText(R.string.call_record_folder_name);
} else {
// 否则,使用文件夹的摘要作为标题
mTitleBar.setText(data.getSnippet());
}
// 显示标题栏
mTitleBar.setVisibility(View.VISIBLE);
}
@ -762,70 +579,43 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
/**
*
*
* @param create truefalse
*/
private void showCreateOrModifyFolderDialog(final boolean create) {
// 使用AlertDialog.Builder创建对话框
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 加载对话框的布局视图
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
// 找到对话框中的编辑文本视图
final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
// 显示软键盘
showSoftInput();
// 根据是否创建文件夹来设置不同的标题和初始名称
if (!create) {
// 修改文件夹名称的情况下
if (mFocusNoteDataItem != null) {
// 设置编辑文本的初始值为当前文件夹的名称
etName.setText(mFocusNoteDataItem.getSnippet());
// 设置对话框标题为“更改文件夹名称”
builder.setTitle(getString(R.string.menu_folder_change_name));
} else {
// 如果没有选中的文件夹项,则记录错误并退出方法
Log.e(TAG, "The long click data item is null");
return;
}
} else {
// 创建文件夹的情况下
// 编辑文本初始值为空
etName.setText("");
// 设置对话框标题为“创建文件夹”
builder.setTitle(this.getString(R.string.menu_create_folder));
}
// 设置“确定”按钮,但不设置点击事件处理
builder.setPositiveButton(android.R.string.ok, null);
// 设置“取消”按钮,并在点击时隐藏软键盘
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName);
}
});
// 设置对话框的视图为自定义视图并显示
final Dialog dialog = builder.setView(view).show();
// 找到对话框中的“确定”按钮
final Button positive = (Button)dialog.findViewById(android.R.id.button1);
// 为“确定”按钮设置点击事件处理
positive.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// 隐藏软键盘
hideSoftInput(etName);
// 获取输入的文件夹名称
String name = etName.getText().toString();
// 检查是否已存在同名文件夹
if (DataUtils.checkVisibleFolderName(mContentResolver, name)) {
// 如果已存在,显示提示信息并返回
Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),
Toast.LENGTH_LONG).show();
etName.setSelection(0, etName.length());
return;
}
// 如果是修改文件夹名称,且输入非空,则更新数据库
if (!create) {
if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
@ -837,23 +627,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
String.valueOf(mFocusNoteDataItem.getId())
});
}
// 如果是创建文件夹,且输入非空,则插入数据库
} else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
// 关闭对话框
dialog.dismiss();
}
});
// 如果输入的文件夹名称为空,则禁用“确定”按钮
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
}
// 监听编辑文本的变化,以启用或禁用“确定”按钮
/**
* When the name edit text is null, disable the positive button
*/
etName.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
@ -861,7 +650,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 根据输入文本的长度来启用或禁用“确定”按钮
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
} else {
@ -877,72 +665,45 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
@Override
/**
* 退
* 退
*/
public void onBackPressed() {
// 根据当前状态选择相应的后退行为
switch (mState) {
case SUB_FOLDER:
// 当前在子文件夹中,返回上级文件夹
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹ID
mState = ListEditState.NOTE_LIST; // 更改状态为笔记列表
startAsyncNotesListQuery(); // 开始异步查询笔记列表
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
break;
case CALL_RECORD_FOLDER:
// 当前在通话录音文件夹中,返回笔记列表
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹ID
mState = ListEditState.NOTE_LIST; // 更改状态为笔记列表
mAddNewNote.setVisibility(View.VISIBLE); // 显示添加新笔记按钮
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
startAsyncNotesListQuery(); // 开始异步查询笔记列表
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
mAddNewNote.setVisibility(View.VISIBLE);
mTitleBar.setVisibility(View.GONE);
startAsyncNotesListQuery();
break;
case NOTE_LIST:
// 当前在笔记列表中,调用默认的后退行为
super.onBackPressed();
break;
default:
// 默认情况,不执行任何操作
break;
}
}
/**
*
*
* @param appWidgetId
* @param appWidgetType
*
* 广
* ID广
*
*/
private void updateWidget(int appWidgetId, int appWidgetType) {
// 创建一个更新小部件的意图
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 根据小部件类型,设置不同的小部件提供者类
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (appWidgetType == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
// 如果小部件类型不受支持,则记录错误并退出方法
Log.e(TAG, "Unspported widget type");
return;
}
// 将需要更新的小部件ID添加到意图中
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
});
// 发送广播意图,触发小部件更新
sendBroadcast(intent);
// 设置方法的返回结果为成功
setResult(RESULT_OK, intent);
}
@ -967,19 +728,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
public boolean onContextItemSelected(MenuItem item) {
// 如果当前焦点的数据项为空则记录错误并返回false
if (mFocusNoteDataItem == null) {
Log.e(TAG, "The long click data item is null");
return false;
}
// 根据菜单项的ID进行不同的操作
switch (item.getItemId()) {
case MENU_FOLDER_VIEW:
// 打开当前焦点的数据项对应的文件夹
openFolder(mFocusNoteDataItem);
break;
case MENU_FOLDER_DELETE:
// 创建一个确认删除的对话框
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
@ -987,7 +744,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 确认删除当前焦点的数据项对应的文件夹
deleteFolder(mFocusNoteDataItem.getId());
}
});
@ -995,94 +751,67 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.show();
break;
case MENU_FOLDER_CHANGE_NAME:
// 显示修改文件夹名称的对话框
showCreateOrModifyFolderDialog(false);
break;
default:
// 其他情况不做操作
break;
}
return true;
}
/**
*
*
*
* @param menu
* @return true
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 清空当前菜单以准备新的菜单
menu.clear();
if (mState == ListEditState.NOTE_LIST) {
// 加载笔记列表菜单
getMenuInflater().inflate(R.menu.note_list, menu);
// 根据同步状态设置菜单项标题
// set sync or sync_cancel
menu.findItem(R.id.menu_sync).setTitle(
GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync);
} else if (mState == ListEditState.SUB_FOLDER) {
// 加载子文件夹菜单
getMenuInflater().inflate(R.menu.sub_folder, menu);
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
// 加载通话记录文件夹菜单
getMenuInflater().inflate(R.menu.call_record_folder, menu);
} else {
// 当状态不正确时记录错误日志
Log.e(TAG, "Wrong state:" + mState);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 根据选中的菜单项执行相应的逻辑
switch (item.getItemId()) {
case R.id.menu_new_folder: {
// 显示创建或修改文件夹对话框
showCreateOrModifyFolderDialog(true);
break;
}
case R.id.menu_export_text: {
// 导出便签到文本
exportNoteToText();
break;
}
case R.id.menu_sync: {
// 处理同步菜单项
if (isSyncMode()) {
// 根据菜单标题决定是开始同步还是取消同步
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this);
} else {
GTaskSyncService.cancelSync(this);
}
} else {
// 进入设置页面
startPreferenceActivity();
}
break;
}
case R.id.menu_setting: {
// 进入设置页面
startPreferenceActivity();
break;
}
case R.id.menu_new_note: {
// 创建新的便签
createNewNote();
break;
}
case R.id.menu_search: {
// 处理搜索请求
case R.id.menu_search:
onSearchRequested();
break;
}
default:
break;
}
@ -1095,35 +824,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
/**
* 便
* 使BackupUtils便
* 线线
*/
private void exportNoteToText() {
// 获取BackupUtils的实例用于执行备份操作
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
// 使用AsyncTask在后台执行导出操作
new AsyncTask<Void, Void, Integer>() {
/**
* 线
* BackupUtilsexportToText
*/
@Override
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
/**
*
*
*
* @param result STATE_SD_CARD_UNMOUONTEDSTATE_SUCCESSSTATE_SYSTEM_ERROR
*/
@Override
protected void onPostExecute(Integer result) {
// SD卡未挂载时的处理
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
@ -1132,8 +843,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} // 导出成功时的处理
else if (result == BackupUtils.STATE_SUCCESS) {
} else if (result == BackupUtils.STATE_SUCCESS) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.success_sdcard_export));
@ -1142,8 +852,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
.getExportedTextFileName(), backup.getExportedTextFileDir()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} // 系统错误时的处理
else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
} else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
@ -1208,20 +917,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
/**
*
*
*
*
*/
private void startQueryDestinationFolders() {
// 定义查询条件的SQL语句模板
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
// 根据当前状态调整查询条件
selection = (mState == ListEditState.NOTE_LIST) ? selection:
"(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";
// 启动背景查询操作
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
Notes.CONTENT_NOTE_URI,
@ -1235,41 +935,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
NoteColumns.MODIFIED_DATE + " DESC");
}
/**
*
*
*
*
*
* @param parent
* @param view
* @param position
* @param id ID
* @return false
*/
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// 检查当前视图是否为NotesListItem类型
if (view instanceof NotesListItem) {
// 获取长按的列表项的数据
mFocusNoteDataItem = ((NotesListItem) view).getItemData();
// 如果是单个笔记且不在选择模式中
if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) {
// 尝试启动选择模式
if (mNotesListView.startActionMode(mModeCallBack) != null) {
// 选择当前项并给予触觉反馈
mModeCallBack.onItemCheckedStateChanged(null, position, id, true);
mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
} else {
// 如果启动选择模式失败,则记录错误日志
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
// 如果是文件夹,则设置文件夹的上下文菜单监听器
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
// 不拦截事件,允许其他监听器处理
return false;
}
}

@ -48,31 +48,15 @@ public class NotesListItem extends LinearLayout {
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
/**
*
*
* /
*
*
* @param context 访
* @param data
* @param choiceMode
* @param checked
*/
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);
} else {
// 否则,隐藏复选框
mCheckBox.setVisibility(View.GONE);
}
// 存储当前项的数据
mItemData = data;
// 如果数据ID为通话记录文件夹ID则设置相应的图标和文本
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
@ -81,7 +65,6 @@ public class NotesListItem extends LinearLayout {
+ 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为通话记录文件夹ID则设置相应的文本和图标
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
@ -93,12 +76,13 @@ public class NotesListItem extends LinearLayout {
mAlert.setVisibility(View.GONE);
}
} else {
// 对于其他类型的数据,根据其类型设置相应的文本和图标
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
@ -110,28 +94,14 @@ public class NotesListItem extends LinearLayout {
}
}
}
// 设置时间文本为相对于修改时间的相对时间跨度
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 根据数据设置项的背景
setBackground(data);
}
/**
* NoteItemData
* ID
*
*
* @param data NoteItemData//
*/
private void setBackground(NoteItemData data) {
// 获取笔记项的背景颜色资源ID
int id = data.getBgColorId();
// 判断笔记项的类型
if (data.getType() == Notes.TYPE_NOTE) {
// 如果是笔记类型,根据笔记的状态选择背景资源
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
@ -142,7 +112,6 @@ public class NotesListItem extends LinearLayout {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
// 如果不是笔记类型(即文件夹类型),设置默认的文件夹背景资源
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}

Loading…
Cancel
Save