diff --git a/README.md b/README.md
deleted file mode 100644
index ed00340..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# xiaomi
-
diff --git a/doc/泛读报告.docx b/doc/泛读报告.docx
new file mode 100644
index 0000000..ab5ee3e
Binary files /dev/null and b/doc/泛读报告.docx differ
diff --git a/src/data/1/number one.php b/src/data/1/number one.php
new file mode 100644
index 0000000..4634c3a
--- /dev/null
+++ b/src/data/1/number one.php
@@ -0,0 +1,31 @@
+
+
+
+
+ 标记应用
+
+
+
+
+ HTML5页面
+ ";
+ echo "alert('".$str1."');";//
+ echo "";
+ ?>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/data/1/six.php b/src/data/1/six.php
new file mode 100644
index 0000000..ad2fed1
--- /dev/null
+++ b/src/data/1/six.php
@@ -0,0 +1,12 @@
+
+
+
+
+ 第一个动态页面
+
+
+你好!欢迎使用PHP.";
+?>
+
+
\ No newline at end of file
diff --git a/src/data/BackupUtils.java b/src/data/BackupUtils.java
new file mode 100644
index 0000000..3d3d818
--- /dev/null
+++ b/src/data/BackupUtils.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package net.micode.notes.tool;
+
+ import android.content.Context;
+ import android.database.Cursor;
+ import android.os.Environment;
+ import android.text.TextUtils;
+ import android.text.format.DateFormat;
+ import android.util.Log;
+
+ import net.micode.notes.R;
+ 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 java.io.File;
+ import java.io.FileNotFoundException;
+ import java.io.FileOutputStream;
+ import java.io.IOException;
+ import java.io.PrintStream;
+
+ /**
+ * 备份工具类,用于将笔记数据导出为文本文件
+ */
+ public class BackupUtils {
+ private static final String TAG = "BackupUtils";
+ // 单例模式相关
+ private static BackupUtils sInstance;
+
+ /**
+ * 获取BackupUtils实例(单例模式)
+ * @param context 上下文对象
+ * @return BackupUtils实例
+ */
+ public static synchronized BackupUtils getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new BackupUtils(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * 定义备份或恢复操作的状态码
+ */
+ // SD卡未挂载
+ public static final int STATE_SD_CARD_UNMOUONTED = 0;
+ // 备份文件不存在
+ public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
+ // 数据被破坏(可能被其他程序修改)
+ public static final int STATE_DATA_DESTROIED = 2;
+ // 系统错误导致备份或恢复失败
+ public static final int STATE_SYSTEM_ERROR = 3;
+ // 备份或恢复成功
+ public static final int STATE_SUCCESS = 4;
+
+ private TextExport mTextExport; // 文本导出工具
+
+ /**
+ * 私有构造函数
+ * @param context 上下文对象
+ */
+ private BackupUtils(Context context) {
+ mTextExport = new TextExport(context);
+ }
+
+ /**
+ * 检查外部存储是否可用
+ * @return 外部存储是否可用
+ */
+ private static boolean externalStorageAvailable() {
+ return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
+ }
+
+ /**
+ * 导出数据到文本文件
+ * @return 操作状态码
+ */
+ public int exportToText() {
+ return mTextExport.exportToText();
+ }
+
+ /**
+ * 获取导出的文本文件名
+ * @return 文件名
+ */
+ public String getExportedTextFileName() {
+ return mTextExport.mFileName;
+ }
+
+ /**
+ * 获取导出的文本文件目录
+ * @return 文件目录
+ */
+ public String getExportedTextFileDir() {
+ return mTextExport.mFileDirectory;
+ }
+
+ /**
+ * 内部类:文本导出工具
+ */
+ private static class TextExport {
+ // 笔记查询的列名数组
+ private static final String[] NOTE_PROJECTION = {
+ NoteColumns.ID,
+ NoteColumns.MODIFIED_DATE,
+ NoteColumns.SNIPPET,
+ NoteColumns.TYPE
+ };
+
+ // 笔记ID列索引
+ private static final int NOTE_COLUMN_ID = 0;
+ // 笔记修改日期列索引
+ private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
+ // 笔记片段列索引
+ private static final int NOTE_COLUMN_SNIPPET = 2;
+
+ // 数据查询的列名数组
+ private static final String[] DATA_PROJECTION = {
+ DataColumns.CONTENT,
+ DataColumns.MIME_TYPE,
+ DataColumns.DATA1,
+ DataColumns.DATA2,
+ DataColumns.DATA3,
+ DataColumns.DATA4,
+ };
+
+ // 数据内容列索引
+ private static final int DATA_COLUMN_CONTENT = 0;
+ // MIME类型列索引
+ private static final int DATA_COLUMN_MIME_TYPE = 1;
+ // 通话日期列索引
+ private static final int DATA_COLUMN_CALL_DATE = 2;
+ // 电话号码列索引
+ private static final int DATA_COLUMN_PHONE_NUMBER = 4;
+
+ // 文本格式数组(从资源文件中获取)
+ private final String [] TEXT_FORMAT;
+ // 文本格式索引常量
+ private static final int FORMAT_FOLDER_NAME = 0; // 文件夹名称格式
+ private static final int FORMAT_NOTE_DATE = 1; // 笔记日期格式
+ private static final int FORMAT_NOTE_CONTENT = 2; // 笔记内容格式
+
+ private Context mContext; // 上下文对象
+ private String mFileName; // 导出文件名
+ private String mFileDirectory; // 导出文件目录
+
+ /**
+ * 构造函数
+ * @param context 上下文对象
+ */
+ public TextExport(Context context) {
+ // 从资源文件中获取文本格式
+ TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
+ mContext = context;
+ mFileName = "";
+ mFileDirectory = "";
+ }
+
+ /**
+ * 获取指定格式的文本
+ * @param id 格式索引
+ * @return 格式文本
+ */
+ private String getFormat(int id) {
+ return TEXT_FORMAT[id];
+ }
+
+ /**
+ * 将指定文件夹的笔记导出为文本
+ * @param folderId 文件夹ID
+ * @param ps 打印流
+ */
+ private void exportFolderToText(String folderId, PrintStream ps) {
+ // 查询属于该文件夹的所有笔记
+ 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 {
+ // 打印笔记的最后修改日期
+ ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
+ mContext.getString(R.string.format_datetime_mdhm),
+ notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)));
+ // 查询属于该笔记的所有数据
+ String noteId = notesCursor.getString(NOTE_COLUMN_ID);
+ exportNoteToText(noteId, ps);
+ } while (notesCursor.moveToNext());
+ }
+ notesCursor.close();
+ }
+ }
+
+ /**
+ * 将指定笔记导出为文本
+ * @param noteId 笔记ID
+ * @param ps 打印流
+ */
+ 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 {
+ String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
+ if (DataConstants.CALL_NOTE.equals(mimeType)) {
+ // 打印电话号码
+ String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
+ long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
+ String location = dataCursor.getString(DATA_COLUMN_CONTENT);
+
+ if (!TextUtils.isEmpty(phoneNumber)) {
+ ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
+ phoneNumber));
+ }
+ // 打印通话日期
+ ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
+ .format(mContext.getString(R.string.format_datetime_mdhm),
+ callDate)));
+ // 打印通话附件位置
+ 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),
+ content));
+ }
+ }
+ } while (dataCursor.moveToNext());
+ }
+ dataCursor.close();
+ }
+ // 在笔记之间打印分隔线
+ try {
+ ps.write(new byte[] {
+ Character.LINE_SEPARATOR, Character.LETTER_NUMBER
+ });
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+
+ /**
+ * 将笔记导出为用户可读的文本文件
+ * @return 操作状态码
+ */
+ public int exportToText() {
+ // 检查SD卡是否可用
+ if (!externalStorageAvailable()) {
+ Log.d(TAG, "Media was not mounted");
+ return STATE_SD_CARD_UNMOUONTED;
+ }
+
+ // 获取打印流
+ PrintStream ps = getExportToTextPrintStream();
+ if (ps == null) {
+ Log.e(TAG, "get print stream error");
+ return STATE_SYSTEM_ERROR;
+ }
+
+ // 首先导出文件夹及其笔记
+ Cursor folderCursor = mContext.getContentResolver().query(
+ Notes.CONTENT_NOTE_URI,
+ NOTE_PROJECTION,
+ "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
+
+ if (folderCursor != null) {
+ if (folderCursor.moveToFirst()) {
+ do {
+ // 打印文件夹名称
+ String folderName = "";
+ 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);
+ }
+ if (!TextUtils.isEmpty(folderName)) {
+ ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
+ }
+ String folderId = folderCursor.getString(NOTE_COLUMN_ID);
+ exportFolderToText(folderId, ps);
+ } while (folderCursor.moveToNext());
+ }
+ folderCursor.close();
+ }
+
+ // 导出根文件夹中的笔记
+ Cursor noteCursor = mContext.getContentResolver().query(
+ Notes.CONTENT_NOTE_URI,
+ NOTE_PROJECTION,
+ 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)));
+ // 查询属于该笔记的所有数据
+ String noteId = noteCursor.getString(NOTE_COLUMN_ID);
+ exportNoteToText(noteId, ps);
+ } while (noteCursor.moveToNext());
+ }
+ noteCursor.close();
+ }
+ ps.close();
+
+ return STATE_SUCCESS;
+ }
+
+ /**
+ * 获取指向导出文本文件的打印流
+ * @return 打印流
+ */
+ private PrintStream getExportToTextPrintStream() {
+ // 生成SD卡上的文件
+ File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
+ R.string.file_name_txt_format);
+ if (file == 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 fos = new FileOutputStream(file);
+ ps = new PrintStream(fos);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ return null;
+ }
+ return ps;
+ }
+ }
+
+ /**
+ * 在SD卡上生成用于存储导出数据的文本文件
+ * @param context 上下文对象
+ * @param filePathResId 文件路径资源ID
+ * @param fileNameFormatResId 文件名格式资源ID
+ * @return 生成的文件对象
+ */
+ private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
+ StringBuilder sb = new StringBuilder();
+ // 获取外部存储目录
+ sb.append(Environment.getExternalStorageDirectory());
+ // 添加文件路径
+ sb.append(context.getString(filePathResId));
+ File filedir = new File(sb.toString());
+ // 添加文件名(包含当前日期)
+ 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) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+ }
\ No newline at end of file
diff --git a/src/data/DataUtils.java b/src/data/DataUtils.java
new file mode 100644
index 0000000..77e734b
--- /dev/null
+++ b/src/data/DataUtils.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package net.micode.notes.tool;
+
+ import android.content.ContentProviderOperation;
+ import android.content.ContentProviderResult;
+ import android.content.ContentResolver;
+ import android.content.ContentUris;
+ import android.content.ContentValues;
+ import android.content.OperationApplicationException;
+ import android.database.Cursor;
+ import android.os.RemoteException;
+ import android.util.Log;
+
+ import net.micode.notes.data.Notes;
+ import net.micode.notes.data.Notes.CallNote;
+ import net.micode.notes.data.Notes.NoteColumns;
+ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
+
+ import java.util.ArrayList;
+ import java.util.HashSet;
+
+ /**
+ * 数据操作工具类,提供对笔记数据的各种操作
+ */
+ public class DataUtils {
+ private static final String TAG = "DataUtils";
+
+ /**
+ * 批量删除笔记
+ * @param resolver ContentResolver对象
+ * @param ids 要删除的笔记ID集合
+ * @return 是否删除成功
+ */
+ public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) {
+ if (ids == null) {
+ Log.d(TAG, "the ids is null");
+ return true;
+ }
+ if (ids.size() == 0) {
+ Log.d(TAG, "no id is in the hashset");
+ return true;
+ }
+
+ // 创建批量操作列表
+ ArrayList operationList = new ArrayList();
+ for (long id : ids) {
+ if(id == Notes.ID_ROOT_FOLDER) {
+ Log.e(TAG, "Don't delete system folder root");
+ continue; // 跳过系统根文件夹
+ }
+ // 构建删除操作
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
+ 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()));
+ }
+ return false;
+ }
+
+ /**
+ * 移动笔记到指定文件夹
+ * @param resolver ContentResolver对象
+ * @param id 笔记ID
+ * @param srcFolderId 源文件夹ID
+ * @param desFolderId 目标文件夹ID
+ */
+ public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
+ ContentValues values = new ContentValues();
+ values.put(NoteColumns.PARENT_ID, desFolderId); // 设置新父文件夹ID
+ values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); // 记录原始父文件夹ID
+ values.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为已修改
+ resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
+ }
+
+ /**
+ * 批量移动笔记到指定文件夹
+ * @param resolver ContentResolver对象
+ * @param ids 要移动的笔记ID集合
+ * @param folderId 目标文件夹ID
+ * @return 是否移动成功
+ */
+ public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids,
+ long folderId) {
+ if (ids == null) {
+ Log.d(TAG, "the ids is null");
+ return true;
+ }
+
+ ArrayList operationList = new ArrayList();
+ for (long id : ids) {
+ // 构建更新操作
+ ContentProviderOperation.Builder builder = ContentProviderOperation
+ .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
+ builder.withValue(NoteColumns.PARENT_ID, folderId); // 设置新父文件夹ID
+ builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); // 标记为已修改
+ 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()));
+ }
+ return false;
+ }
+
+ /**
+ * 获取用户文件夹数量(不包括系统文件夹)
+ * @param resolver ContentResolver对象
+ * @return 用户文件夹数量
+ */
+ public static int getUserFolderCount(ContentResolver resolver) {
+ Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
+ new String[] { "COUNT(*)" },
+ NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
+ new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
+ null);
+
+ int count = 0;
+ 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;
+ }
+
+ /**
+ * 检查指定笔记在数据库中是否可见(不在回收站中)
+ * @param resolver ContentResolver对象
+ * @param noteId 笔记ID
+ * @param type 笔记类型
+ * @return 是否可见
+ */
+ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
+ Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
+ null,
+ NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
+ new String [] {String.valueOf(type)},
+ null);
+
+ boolean exist = false;
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ exist = true;
+ }
+ cursor.close();
+ }
+ return exist;
+ }
+
+ /**
+ * 检查笔记是否存在于数据库中
+ * @param resolver ContentResolver对象
+ * @param noteId 笔记ID
+ * @return 是否存在
+ */
+ public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
+ Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
+ null, null, null, null);
+
+ boolean exist = false;
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ exist = true;
+ }
+ cursor.close();
+ }
+ return exist;
+ }
+
+ /**
+ * 检查数据项是否存在于数据库中
+ * @param resolver ContentResolver对象
+ * @param dataId 数据项ID
+ * @return 是否存在
+ */
+ public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
+ Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
+ null, null, null, null);
+
+ boolean exist = false;
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ exist = true;
+ }
+ cursor.close();
+ }
+ return exist;
+ }
+
+ /**
+ * 检查指定名称的文件夹是否已存在(不在回收站中)
+ * @param resolver ContentResolver对象
+ * @param name 文件夹名称
+ * @return 是否存在
+ */
+ public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
+ Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
+ NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
+ " AND " + NoteColumns.SNIPPET + "=?",
+ new String[] { name }, null);
+ boolean exist = false;
+ if(cursor != null) {
+ if(cursor.getCount() > 0) {
+ exist = true;
+ }
+ cursor.close();
+ }
+ return exist;
+ }
+
+ /**
+ * 获取文件夹关联的小部件属性集合
+ * @param resolver ContentResolver对象
+ * @param folderId 文件夹ID
+ * @return 小部件属性集合
+ */
+ public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) {
+ Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
+ new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
+ NoteColumns.PARENT_ID + "=?",
+ new String[] { String.valueOf(folderId) },
+ null);
+
+ HashSet set = null;
+ if (c != null) {
+ if (c.moveToFirst()) {
+ set = new HashSet();
+ do {
+ try {
+ AppWidgetAttribute widget = new AppWidgetAttribute();
+ widget.widgetId = c.getInt(0); // 小部件ID
+ widget.widgetType = c.getInt(1); // 小部件类型
+ set.add(widget);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(TAG, e.toString());
+ }
+ } while (c.moveToNext());
+ }
+ c.close();
+ }
+ return set;
+ }
+
+ /**
+ * 根据笔记ID获取通话号码
+ * @param resolver ContentResolver对象
+ * @param noteId 笔记ID
+ * @return 通话号码
+ */
+ public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
+ Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
+ new String [] { CallNote.PHONE_NUMBER },
+ CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
+ new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
+ null);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ try {
+ return cursor.getString(0);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(TAG, "Get call number fails " + e.toString());
+ } finally {
+ cursor.close();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * 根据电话号码和通话日期获取笔记ID
+ * @param resolver ContentResolver对象
+ * @param phoneNumber 电话号码
+ * @param callDate 通话日期
+ * @return 笔记ID
+ */
+ public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
+ Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
+ new String [] { CallNote.NOTE_ID },
+ CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ + CallNote.PHONE_NUMBER + ",?)",
+ new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
+ null);
+
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ try {
+ return cursor.getLong(0);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(TAG, "Get call note id fails " + e.toString());
+ }
+ }
+ cursor.close();
+ }
+ return 0;
+ }
+
+ /**
+ * 根据笔记ID获取内容片段
+ * @param resolver ContentResolver对象
+ * @param noteId 笔记ID
+ * @return 内容片段
+ * @throws IllegalArgumentException 如果笔记不存在
+ */
+ public static String getSnippetById(ContentResolver resolver, long noteId) {
+ 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) {
+ if (snippet != null) {
+ snippet = snippet.trim(); // 去除首尾空格
+ int index = snippet.indexOf('\n');
+ if (index != -1) {
+ snippet = snippet.substring(0, index); // 只保留第一行
+ }
+ }
+ return snippet;
+ }
+ }
\ No newline at end of file
diff --git a/src/data/GTaskStringUtils.java b/src/data/GTaskStringUtils.java
new file mode 100644
index 0000000..7b1c26f
--- /dev/null
+++ b/src/data/GTaskStringUtils.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package net.micode.notes.tool;
+
+ /**
+ * Google Task 相关字符串常量工具类
+ * 包含与Google Task API交互时使用的所有JSON键名和固定值
+ */
+ public class GTaskStringUtils {
+
+ // ====================== JSON字段键名常量 ======================
+
+ /** 操作ID字段名 */
+ public final static String GTASK_JSON_ACTION_ID = "action_id";
+
+ /** 操作列表字段名 */
+ public final static String GTASK_JSON_ACTION_LIST = "action_list";
+
+ /** 操作类型字段名 */
+ public final static String GTASK_JSON_ACTION_TYPE = "action_type";
+
+ /** 创建者ID字段名 */
+ public final static String GTASK_JSON_CREATOR_ID = "creator_id";
+
+ /** 子实体字段名 */
+ public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
+
+ /** 客户端版本字段名 */
+ public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
+
+ /** 完成状态字段名 */
+ public final static String GTASK_JSON_COMPLETED = "completed";
+
+ /** 当前列表ID字段名 */
+ public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
+
+ /** 默认列表ID字段名 */
+ public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
+
+ /** 删除状态字段名 */
+ public final static String GTASK_JSON_DELETED = "deleted";
+
+ /** 目标列表字段名 */
+ public final static String GTASK_JSON_DEST_LIST = "dest_list";
+
+ /** 目标父节点字段名 */
+ public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
+
+ /** 目标父节点类型字段名 */
+ public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
+
+ /** 实体变更字段名 */
+ public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
+
+ /** 实体类型字段名 */
+ public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
+
+ /** 获取已删除项字段名 */
+ public final static String GTASK_JSON_GET_DELETED = "get_deleted";
+
+ /** ID字段名 */
+ public final static String GTASK_JSON_ID = "id";
+
+ /** 索引字段名 */
+ public final static String GTASK_JSON_INDEX = "index";
+
+ /** 最后修改时间字段名 */
+ public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
+
+ /** 最新同步点字段名 */
+ public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
+
+ /** 列表ID字段名 */
+ public final static String GTASK_JSON_LIST_ID = "list_id";
+
+ /** 列表集合字段名 */
+ public final static String GTASK_JSON_LISTS = "lists";
+
+ /** 名称字段名 */
+ public final static String GTASK_JSON_NAME = "name";
+
+ /** 新ID字段名 */
+ public final static String GTASK_JSON_NEW_ID = "new_id";
+
+ /** 笔记字段名 */
+ public final static String GTASK_JSON_NOTES = "notes";
+
+ /** 父ID字段名 */
+ public final static String GTASK_JSON_PARENT_ID = "parent_id";
+
+ /** 前一个兄弟节点ID字段名 */
+ public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
+
+ /** 结果字段名 */
+ public final static String GTASK_JSON_RESULTS = "results";
+
+ /** 源列表字段名 */
+ public final static String GTASK_JSON_SOURCE_LIST = "source_list";
+
+ /** 任务字段名 */
+ public final static String GTASK_JSON_TASKS = "tasks";
+
+ /** 类型字段名 */
+ public final static String GTASK_JSON_TYPE = "type";
+
+ // ====================== 操作类型常量 ======================
+
+ /** 创建操作类型 */
+ public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
+
+ /** 获取全部操作类型 */
+ public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
+
+ /** 移动操作类型 */
+ public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
+
+ /** 更新操作类型 */
+ public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
+
+ // ====================== 实体类型常量 ======================
+
+ /** 组类型 */
+ public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
+
+ /** 任务类型 */
+ public final static String GTASK_JSON_TYPE_TASK = "TASK";
+
+ // ====================== 其他字段 ======================
+
+ /** 用户字段名 */
+ public final static String GTASK_JSON_USER = "user";
+
+ // ====================== MIUI特定常量 ======================
+
+ /** 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";
+
+ // ====================== 元数据相关常量 ======================
+
+ /** Google Task ID元数据头 */
+ 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";
+ }
\ No newline at end of file
diff --git a/src/data/ResourceParser.java b/src/data/ResourceParser.java
new file mode 100644
index 0000000..5f1e112
--- /dev/null
+++ b/src/data/ResourceParser.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package net.micode.notes.tool;
+
+ import android.content.Context;
+ import android.preference.PreferenceManager;
+
+ import net.micode.notes.R;
+ import net.micode.notes.ui.NotesPreferenceActivity;
+
+ /**
+ * 资源解析工具类
+ * 用于管理笔记应用中的各种资源ID和样式
+ */
+ 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;
+
+ /** 默认背景颜色标识 */
+ 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;
+
+ /** 默认字体大小 */
+ public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
+
+ /**
+ * 笔记背景资源类
+ * 包含笔记编辑界面和标题的背景资源
+ */
+ public static class NoteBgResources {
+ // 笔记编辑界面背景资源数组
+ private final static int [] BG_EDIT_RESOURCES = new int [] {
+ R.drawable.edit_yellow, // 黄色背景
+ R.drawable.edit_blue, // 蓝色背景
+ R.drawable.edit_white, // 白色背景
+ R.drawable.edit_green, // 绿色背景
+ R.drawable.edit_red // 红色背景
+ };
+
+ // 笔记标题背景资源数组
+ 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
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getNoteBgResource(int id) {
+ return BG_EDIT_RESOURCES[id];
+ }
+
+ /**
+ * 获取笔记标题背景资源ID
+ * @param id 颜色标识
+ * @return 对应的标题背景资源ID
+ */
+ public static int getNoteTitleBgResource(int id) {
+ return BG_EDIT_TITLE_RESOURCES[id];
+ }
+ }
+
+ /**
+ * 获取默认背景ID
+ * @param context 上下文对象
+ * @return 背景颜色标识
+ */
+ 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 {
+ // 返回默认背景颜色
+ return BG_DEFAULT_COLOR;
+ }
+ }
+
+ /**
+ * 笔记列表项背景资源类
+ * 包含列表项不同位置的背景资源
+ */
+ public static class NoteItemBgResources {
+ // 列表第一项背景资源数组
+ private final static int [] BG_FIRST_RESOURCES = new int [] {
+ R.drawable.list_yellow_up, // 黄色第一项背景
+ R.drawable.list_blue_up, // 蓝色第一项背景
+ R.drawable.list_white_up, // 白色第一项背景
+ R.drawable.list_green_up, // 绿色第一项背景
+ R.drawable.list_red_up // 红色第一项背景
+ };
+
+ // 列表中间项背景资源数组
+ 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 // 红色中间项背景
+ };
+
+ // 列表最后一项背景资源数组
+ 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 // 红色最后一项背景
+ };
+
+ // 列表单项背景资源数组(当列表只有一项时使用)
+ 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 // 红色单项背景
+ };
+
+ /**
+ * 获取列表第一项背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getNoteBgFirstRes(int id) {
+ return BG_FIRST_RESOURCES[id];
+ }
+
+ /**
+ * 获取列表最后一项背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getNoteBgLastRes(int id) {
+ return BG_LAST_RESOURCES[id];
+ }
+
+ /**
+ * 获取列表单项背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getNoteBgSingleRes(int id) {
+ return BG_SINGLE_RESOURCES[id];
+ }
+
+ /**
+ * 获取列表中间项背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getNoteBgNormalRes(int id) {
+ return BG_NORMAL_RESOURCES[id];
+ }
+
+ /**
+ * 获取文件夹背景资源
+ * @return 文件夹背景资源ID
+ */
+ public static int getFolderBgRes() {
+ return R.drawable.list_folder;
+ }
+ }
+
+ /**
+ * 小部件背景资源类
+ * 包含不同尺寸小部件的背景资源
+ */
+ public static class WidgetBgResources {
+ // 2x小部件背景资源数组
+ private final static int [] BG_2X_RESOURCES = new int [] {
+ R.drawable.widget_2x_yellow, // 黄色2x小部件背景
+ R.drawable.widget_2x_blue, // 蓝色2x小部件背景
+ R.drawable.widget_2x_white, // 白色2x小部件背景
+ R.drawable.widget_2x_green, // 绿色2x小部件背景
+ R.drawable.widget_2x_red // 红色2x小部件背景
+ };
+
+ /**
+ * 获取2x小部件背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getWidget2xBgResource(int id) {
+ return BG_2X_RESOURCES[id];
+ }
+
+ // 4x小部件背景资源数组
+ private final static int [] BG_4X_RESOURCES = new int [] {
+ R.drawable.widget_4x_yellow, // 黄色4x小部件背景
+ R.drawable.widget_4x_blue, // 蓝色4x小部件背景
+ R.drawable.widget_4x_white, // 白色4x小部件背景
+ R.drawable.widget_4x_green, // 绿色4x小部件背景
+ R.drawable.widget_4x_red // 红色4x小部件背景
+ };
+
+ /**
+ * 获取4x小部件背景资源
+ * @param id 颜色标识
+ * @return 对应的背景资源ID
+ */
+ public static int getWidget4xBgResource(int id) {
+ return BG_4X_RESOURCES[id];
+ }
+ }
+
+ /**
+ * 文本外观资源类
+ * 包含不同字体大小的样式资源
+ */
+ public static class TextAppearanceResources {
+ // 文本外观样式资源数组
+ private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
+ R.style.TextAppearanceNormal, // 正常字体样式
+ R.style.TextAppearanceMedium, // 中等字体样式
+ R.style.TextAppearanceLarge, // 大号字体样式
+ R.style.TextAppearanceSuper // 超大号字体样式
+ };
+
+ /**
+ * 获取文本外观资源
+ * @param id 字体大小标识
+ * @return 对应的样式资源ID
+ */
+ public static int getTexAppearanceResource(int id) {
+ /**
+ * HACKME: 修复在SharedPreference中存储资源ID的bug
+ * 当ID大于资源数组长度时,返回默认字体大小
+ */
+ if (id >= TEXTAPPEARANCE_RESOURCES.length) {
+ return BG_DEFAULT_FONT_SIZE;
+ }
+ return TEXTAPPEARANCE_RESOURCES[id];
+ }
+
+ /**
+ * 获取文本外观资源数量
+ * @return 可用的文本外观样式数量
+ */
+ public static int getResourcesSize() {
+ return TEXTAPPEARANCE_RESOURCES.length;
+ }
+ }
+ }
\ No newline at end of file