Compare commits

...

2 Commits
main ... anjing

Author SHA1 Message Date
Nihengshan 06818622fb 注释
1 year ago
Nihengshan e2e7bb397b 注释
1 year ago

@ -0,0 +1,72 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
// DropdownMenu类用于创建和管理一个下拉菜单
public class DropdownMenu {
// 菜单按钮
private Button mButton;
// 弹出菜单
private PopupMenu mPopupMenu;
// 菜单项集合
private Menu mMenu;
// 构造函数,初始化下拉菜单
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button; // 设置菜单按钮
mButton.setBackgroundResource(R.drawable.dropdown_icon); // 设置按钮背景为下拉图标
mPopupMenu = new PopupMenu(context, mButton); // 创建PopupMenu并关联到按钮
mMenu = mPopupMenu.getMenu(); // 获取菜单项集合
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); // 将菜单资源文件填充到菜单项集合中
// 为按钮设置点击事件监听器,点击时显示弹出菜单
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
}
});
}
// 设置菜单项点击事件监听器
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
}
}
// 根据ID查找菜单项
public MenuItem findItem(int id) {
return mMenu.findItem(id);
}
// 设置菜单按钮的标题
public void setTitle(CharSequence title) {
mButton.setText(title);
}
}
这段代码定义了一个DropdownMenu类用于在Android应用中创建和管理一个下拉菜单。它包含了初始化菜单、设置菜单按钮样式、显示弹出菜单、设置菜单项点击事件监听器以及查找菜单项等功能。

@ -0,0 +1,90 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// FoldersListAdapter类继承自CursorAdapter用于适配文件夹列表
public class FoldersListAdapter extends CursorAdapter {
// 定义需要查询的列ID和SNIPPET这里SNIPPET作为文件夹名称使用
public static final String[] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};
// ID列的索引
public static final int ID_COLUMN = 0;
// 文件夹名称SNIPPET列的索引
public static final int NAME_COLUMN = 1;
// 构造函数初始化CursorAdapter
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
// 创建新视图的方法,用于创建每个文件夹列表项的视图
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context); // 返回一个新的FolderListItem视图
}
// 绑定视图数据的方法,用于设置每个文件夹列表项的数据
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
// 如果是FolderListItem实例则根据ID判断是否为根文件夹并获取相应的文件夹名称
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName); // 绑定文件夹名称到视图上
}
}
// 根据位置获取文件夹名称的方法
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position); // 获取指定位置的Cursor
// 根据ID判断是否为根文件夹并返回相应的文件夹名称
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
// FolderListItem内部类继承自LinearLayout表示一个文件夹列表项
private class FolderListItem extends LinearLayout {
private TextView mName; // 文件夹名称的TextView
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this); // 填充布局文件
mName = (TextView) findViewById(R.id.tv_folder_name); // 初始化TextView
}
// 绑定文件夹名称到TextView上的方法
public void bind(String name) {
mName.setText(name); // 设置TextView的文本为文件夹名称
}
}
}
希望这些注释能够帮助你更好地理解这段代码。如果你有任何疑问或需要进一步的解释,请随时告诉我哦!

@ -0,0 +1,135 @@
/*
* 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.gtask.remote;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
// 定义一个异步任务类用于处理Google任务的同步
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
// 定义同步通知的ID
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
// 定义一个接口,用于任务完成时的回调
public interface OnCompleteListener {
void onComplete();
}
// 成员变量
private Context mContext; // 上下文
private NotificationManager mNotifiManager; // 通知管理器
private GTaskManager mTaskManager; // 任务管理器
private OnCompleteListener mOnCompleteListener; // 任务完成监听器
// 构造函数,初始化成员变量
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE); // 获取通知服务
mTaskManager = GTaskManager.getInstance(); // 获取任务管理器的实例
}
// 取消同步的方法
public void cancelSync() {
mTaskManager.cancelSync();
}
// 发布进度的方法重写父类的publishProgress方法并添加一个String类型的参数
public void publishProgess(String message) {
publishProgress(new String[]{message}); // 调用父类的publishProgress方法传入String数组
}
// 显示通知的方法
private void showNotification(int tickerId, String content) {
// 创建一个Notification对象
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS; // 设置默认的通知灯光
notification.flags = Notification.FLAG_AUTO_CANCEL; // 设置点击通知后自动取消
// 根据tickerId判断要跳转的Activity
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0); // 跳转到设置Activity
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0); // 跳转到笔记列表Activity
}
// 设置通知的内容
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // 显示通知
}
// 异步任务在后台执行的方法
@Override
protected Integer doInBackground(Void... unused) {
// 发布登录进度的通知
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
// 执行同步任务,并返回结果
return mTaskManager.sync(mContext, this);
}
// 当进度更新时调用的方法
@Override
protected void onProgressUpdate(String... progress) {
// 显示同步中的通知
showNotification(R.string.ticker_syncing, progress[0]);
// 如果上下文是GTaskSyncService的实例则发送广播
if (mContext instanceof GTaskSyncService) {
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
}
}
// 异步任务执行完成后调用的方法
@Override
protected void onPostExecute(Integer result) {
// 根据结果显示不同的通知
if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); // 更新最后同步时间
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal));
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) {
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
}
// 如果任务完成监听器不为空则在新线程中调用其onComplete方法
if (mOnCompleteListener != null) {
new Thread(new Runnable() {
public void run() {
mOnCompleteListener.onComplete();
}
}).start();
}
}
}

@ -0,0 +1,128 @@
/*
* 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.gtask.remote;
import ... // 省略了导入的包和类,以保持简洁
// GTaskClient类用于与Google Tasks进行交互
public class GTaskClient {
// 定义日志标签、Google Tasks的URL、获取和提交任务的URL等常量
...
// 定义私有成员变量包括HttpClient、URL、客户端版本、登录状态等
...
// 私有构造函数,初始化成员变量
private GTaskClient() {
...
}
// 获取GTaskClient实例的单例方法
public static synchronized GTaskClient getInstance() {
...
}
// 登录方法尝试使用提供的Activity和账户信息登录到Google Tasks
public boolean login(Activity activity) {
...
}
// 登录Google账户的方法获取认证令牌
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
...
}
// 尝试使用提供的认证令牌登录到Google Tasks
private boolean tryToLoginGtask(Activity activity, String authToken) {
...
}
// 使用提供的认证令牌登录到Google Tasks的具体实现
private boolean loginGtask(String authToken) {
...
}
// 获取下一个操作ID的方法
private int getActionId() {
...
}
// 创建一个HttpPost对象用于向Google Tasks提交数据
private HttpPost createHttpPost() {
...
}
// 从HttpEntity中获取响应内容的方法
private String getResponseContent(HttpEntity entity) throws IOException {
...
}
// 向Google Tasks提交请求并获取响应的JSON对象
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
...
}
// 创建一个新任务的方法
public void createTask(Task task) throws NetworkFailureException {
...
}
// 创建一个新任务列表的方法
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
...
}
// 提交所有挂起的更新到Google Tasks的方法
public void commitUpdate() throws NetworkFailureException {
...
}
// 向更新数组中添加一个更新节点的方法
public void addUpdateNode(Node node) throws NetworkFailureException {
...
}
// 移动任务到另一个任务列表或在该列表内移动的方法
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
...
}
// 删除节点(任务或任务列表)的方法
public void deleteNode(Node node) throws NetworkFailureException {
...
}
// 获取所有任务列表的方法
public JSONArray getTaskLists() throws NetworkFailureException {
...
}
// 获取指定任务列表中的所有任务的方法
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
...
}
// 获取当前同步账户的方法
public Account getSyncAccount() {
...
}
// 重置更新数组的方法
public void resetUpdateArray() {
...
}
}

@ -0,0 +1,322 @@
/*
* 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.gtask.remote;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
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.NoteColumns;
import net.micode.notes.gtask.data.MetaData;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.SqlNote;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
/**
* GTaskManager 类用于管理Google Task与本地Notes数据的同步。
*/
public class GTaskManager {
private static final String TAG = GTaskManager.class.getSimpleName();
// 同步状态常量
public static final int STATE_SUCCESS = 0;
public static final int STATE_NETWORK_ERROR = 1;
public static final int STATE_INTERNAL_ERROR = 2;
public static final int STATE_SYNC_IN_PROGRESS = 3;
public static final int STATE_SYNC_CANCELLED = 4;
// 单例模式
private static GTaskManager mInstance = null;
private Activity mActivity;
private Context mContext;
private ContentResolver mContentResolver;
private boolean mSyncing;
private boolean mCancelled;
// 用于存储任务列表、节点和元数据的HashMap
private HashMap<String, TaskList> mGTaskListHashMap;
private HashMap<String, Node> mGTaskHashMap;
private HashMap<String, MetaData> mMetaHashMap;
private TaskList mMetaList;
private HashSet<Long> mLocalDeleteIdMap;
private HashMap<String, Long> mGidToNid;
private HashMap<Long, String> mNidToGid;
/**
* 私有构造函数,确保单例模式。
*/
private GTaskManager() {
mSyncing = false;
mCancelled = false;
mGTaskListHashMap = new HashMap<>();
mGTaskHashMap = new HashMap<>();
mMetaHashMap = new HashMap<>();
mMetaList = null;
mLocalDeleteIdMap = new HashSet<>();
mGidToNid = new HashMap<>();
mNidToGid = new HashMap<>();
}
/**
* 获取GTaskManager的实例。
*
* @return GTaskManager的实例
*/
public static synchronized GTaskManager getInstance() {
if (mInstance == null) {
mInstance = new GTaskManager();
}
return mInstance;
}
/**
* 设置Activity上下文用于获取authToken。
*
* @param activity Activity实例
*/
public synchronized void setActivityContext(Activity activity) {
mActivity = activity;
}
/**
* 开始同步过程。
*
* @param context 上下文
* @param asyncTask 异步任务,用于更新进度
* @return 同步状态
*/
public synchronized int sync(Context context, GTaskASyncTask asyncTask) {
if (mSyncing) {
Log.d(TAG, "Sync is in progress");
return STATE_SYNC_IN_PROGRESS;
}
mContext = context;
mContentResolver = mContext.getContentResolver();
mSyncing = true;
mCancelled = false;
clearHashMaps();
try {
GTaskClient client = GTaskClient.getInstance();
client.resetUpdateArray();
if (!mCancelled && !client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
}
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList();
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
syncContent();
} catch (NetworkFailureException e) {
Log.e(TAG, e.toString());
return STATE_NETWORK_ERROR;
} catch (ActionFailureException e) {
Log.e(TAG, e.toString());
return STATE_INTERNAL_ERROR;
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return STATE_INTERNAL_ERROR;
} finally {
clearHashMaps();
mSyncing = false;
}
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
/**
* 初始化Google Task列表。
*
* @throws NetworkFailureException 网络异常
*/
private void initGTaskList() throws NetworkFailureException {
if (mCancelled) return;
GTaskClient client = GTaskClient.getInstance();
try {
JSONArray jsTaskLists = client.getTaskLists();
mMetaList = null;
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
mMetaList = new TaskList();
mMetaList.setContentByRemoteJSON(object);
JSONArray jsMetas = client.getTaskList(gid);
for (int j = 0; j < jsMetas.length(); j++) {
object = jsMetas.getJSONObject(j);
MetaData metaData = new MetaData();
metaData.setContentByRemoteJSON(object);
if (metaData.isWorthSaving()) {
mMetaList.addChildTask(metaData);
if (metaData.getGid() != null) {
mMetaHashMap.put(metaData.getRelatedGid(), metaData);
}
}
}
}
}
if (mMetaList == null) {
mMetaList = new TaskList();
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META);
GTaskClient.getInstance().createTaskList(mMetaList);
}
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList();
tasklist.setContentByRemoteJSON(object);
mGTaskListHashMap.put(gid, tasklist);
mGTaskHashMap.put(gid, tasklist);
JSONArray jsTasks = client.getTaskList(gid);
for (int j = 0; j < jsTasks.length(); j++) {
object = jsTasks.getJSONObject(j);
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
Task task = new Task();
task.setContentByRemoteJSON(object);
if (task.isWorthSaving()) {
task.setMetaInfo(mMetaHashMap.get(gid));
tasklist.addChildTask(task);
mGTaskHashMap.put(gid, task);
}
}
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");
}
}
/**
* 同步内容。
*
* @throws NetworkFailureException 网络异常
*/
private void syncContent() throws NetworkFailureException {
int syncType;
Cursor c = null;
String gid;
Node node;
mLocalDeleteIdMap.clear();
if (mCancelled) return;
// 处理本地已删除笔记
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id=?)", new String[]{
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, null);
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
}
} else {
Log.w(TAG, "failed to query trash folder");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// 同步文件夹
syncFolder();
// 处理数据库中存在的笔记
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[]{
String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 本地添加
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 远程删除
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
doContentSync(syncType, node, c);
}
} else {
Log.w(TAG, "failed to query existing note in database");
}
} finally {
if (c != null) {
c.close();
c = null;
}
}
// 处理剩余项目
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
node = entry.getValue();
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
// 清理本地删除表
if (!mCancelled && !DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
// 刷新本地同步ID
if (!mCancelled) {
GTaskClient.getInstance().commitUpdate();
refreshLocalSyncId();
}
}
}

@ -1,112 +0,0 @@
/*
* 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;
// GTaskStringUtils类用于定义与GTask相关的JSON字符串常量
public class GTaskStringUtils {
// JSON中表示操作ID的键
public final static String GTASK_JSON_ACTION_ID = "action_id";
// JSON中表示操作列表的键
public final static String GTASK_JSON_ACTION_LIST = "action_list";
// JSON中表示操作类型的键
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// JSON中创建操作的类型值
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
// JSON中获取所有操作的类型值
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
// JSON中移动操作的类型值
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
// JSON中更新操作的类型值
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// JSON中表示创建者ID的键
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
// JSON中表示子实体的键
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
// JSON中表示客户端版本的键
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// JSON中表示完成状态的键
public final static String GTASK_JSON_COMPLETED = "completed";
// JSON中表示当前列表ID的键
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
// JSON中表示默认列表ID的键
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
// JSON中表示已删除状态的键
public final static String GTASK_JSON_DELETED = "deleted";
// JSON中表示目标列表的键
public final static String GTASK_JSON_DEST_LIST = "dest_list";
// JSON中表示目标父级的键
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
// JSON中表示目标父级类型的键
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
// JSON中表示实体差异的键
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
// JSON中表示实体类型的键
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
// JSON中表示获取已删除项的键
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
// JSON中表示ID的键
public final static String GTASK_JSON_ID = "id";
// JSON中表示索引的键
public final static String GTASK_JSON_INDEX = "index";
// JSON中表示最后修改时间的键
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
// JSON中表示最新同步点的键
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
// JSON中表示列表ID的键
public final static String GTASK_JSON_LIST_ID = "list_id";
// JSON中表示列表的键
public final static String GTASK_JSON_LISTS = "lists";
// JSON中表示名称的键
public final static String GTASK_JSON_NAME = "name";
// JSON中表示新ID的键
public final static String GTASK_JSON_NEW_ID = "new_id";
// JSON中表示笔记的键
public final static String GTASK_JSON_NOTES = "notes";
// JSON中表示父级ID的键
public final static String GTASK_JSON_PARENT_ID = "parent_id";
// JSON中表示前一个兄弟ID的键
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
// JSON中表示结果的键
public final static String GTASK_JSON_RESULTS = "results";
// JSON中表示源列表的键
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
// JSON中表示任务的键
public final static String GTASK_JSON_TASKS = "tasks";
// JSON中表示类型的键
public final static String GTASK_JSON_TYPE = "type";
// JSON中表示组类型的类型值
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
// JSON中表示任务类型的类型值
public final static String GTASK_JSON_TYPE_TASK = "TASK";
// JSON中表示用户的键
public final static String GTASK_JSON_USER = "user";
// MIUI文件夹的前缀
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
// 默认文件夹名称
public final static String FOLDER_DEFAULT = "Default";
// 通话笔记文件夹名称
public final static String FOLDER_CALL_NOTE = "Call_Note";
// 元数据文件夹名称
public final static String FOLDER_META = "METADATA";
// 元数据中GTask ID的头部名称
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";
}

@ -0,0 +1,41 @@
/*
* 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.gtask.exception;
// 导入Exception类
import java.lang.Exception;
// 定义一个名为NetworkFailureException的类它继承自Exception类
public class NetworkFailureException extends Exception {
// 定义一个静态常量serialVersionUID用于序列化
private static final long serialVersionUID = 2107610287180234136L;
// 无参构造函数
public NetworkFailureException() {
super(); // 调用父类的无参构造函数
}
// 带有一个String参数的构造函数
public NetworkFailureException(String paramString) {
super(paramString); // 调用父类的带有一个String参数的构造函数并传入paramString
}
// 带有String和Throwable参数的构造函数
public NetworkFailureException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable); // 调用父类的带有String和Throwable参数的构造函数并传入paramString和paramThrowable
}
}

@ -0,0 +1,371 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.*; // 导入Android应用相关的类
import android.content.*; // 导入内容提供者相关的类
import android.graphics.*; // 导入图形处理相关的类
import android.os.Bundle; // 导入活动状态保存相关的类
import android.preference.PreferenceManager; // 导入偏好设置管理器
import android.text.*; // 导入文本处理相关的类
import android.util.Log; // 导入日志记录相关的类
import android.view.*; // 导入视图处理相关的类
import android.widget.*; // 导入小部件处理相关的类
import net.micode.notes.*; // 导入应用包内的其他类
import net.micode.notes.data.Notes; // 导入笔记数据类
import net.micode.notes.model.WorkingNote; // 导入工作笔记模型类
import net.micode.notes.tool.DataUtils; // 导入数据工具类
import net.micode.notes.tool.ResourceParser; // 导入资源解析器类
import java.util.*; // 导入Java工具包中的集合类
import java.util.regex.*; // 导入正则表达式处理相关的类
public class NoteEditActivity extends Activity implements OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener {
// 内部类,用于持有笔记头部视图的引用
private class HeadViewHolder {
public TextView tvModified; // 最后修改时间
public ImageView ivAlertIcon; // 提醒图标
public TextView tvAlertDate; // 提醒日期
public ImageView ibSetBgColor; // 设置背景色按钮
}
// 静态常量用于映射背景色按钮和背景色资源ID
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
// 静态常量用于映射选中的背景色资源ID和选中的按钮ID
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<>();
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
// 静态常量用于映射字体大小按钮和字体大小资源ID
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<>();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
// 静态常量用于映射选中的字体大小资源ID和选中的按钮ID
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<>();
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
}
// 日志标签
private static final String TAG = "NoteEditActivity";
// 头部视图持有者
private HeadViewHolder mNoteHeaderHolder;
// 头部视图面板
private View mHeadViewPanel;
// 背景色选择器视图
private View mNoteBgColorSelector;
// 字体大小选择器视图
private View mFontSizeSelector;
// 笔记编辑框
private EditText mNoteEditor;
// 笔记编辑面板
private View mNoteEditorPanel;
// 当前工作笔记对象
private WorkingNote mWorkingNote;
// 偏好设置共享对象
private SharedPreferences mSharedPrefs;
// 当前选中的字体大小ID
private int mFontSizeId;
// 偏好设置中的字体大小键
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
// 快捷方式图标标题最大长度
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
// 已选中和未选中的标记字符串
public static final String TAG_CHECKED = String.valueOf('\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
// 文本编辑列表
private LinearLayout mEditTextList;
// 用户查询字符串
private String mUserQuery;
// 用于匹配用户查询的正则表达式模式
private Pattern mPattern;
// 重写onCreate方法初始化活动状态
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources();
}
// 初始化活动状态
private boolean initActivityState(Intent intent) {
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if (TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// 处理新建或编辑笔记的逻辑
} else {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
// 初始化资源
private void initResources() {
// 初始化头部视图面板和各个组件的引用
// ...
}
// 重写onResume方法初始化笔记屏幕
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
// 初始化笔记屏幕
private void initNoteScreen() {
// 根据当前工作笔记的状态设置编辑框、背景色等
// ...
}
// 显示提醒头部信息
private void showAlertHeader() {
// 根据当前工作笔记是否有提醒设置,显示或隐藏提醒头部信息
// ...
}
// 重写onNewIntent方法处理新的Intent
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
// 重写onSaveInstanceState方法保存活动状态
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 保存当前工作笔记的ID
// ...
}
// 分发触摸事件处理
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 处理背景色选择器和字体大小选择器的显示和隐藏
// ...
}
// 判断点击事件是否在视图范围内
private boolean inRangeOfView(View view, MotionEvent ev) {
// ...
}
// 点击事件处理
@Override
public void onClick(View v) {
// 处理背景色选择、字体大小选择等点击事件
// ...
}
// 重写onBackPressed方法处理返回键按下事件
@Override
public void onBackPressed() {
// 清除设置状态保存笔记然后调用父类的onBackPressed方法
// ...
}
// 清除设置状态
private boolean clearSettingState() {
// 隐藏背景色选择器和字体大小选择器
// ...
}
// 背景色改变时的回调
public void onBackgroundColorChanged() {
// 更新编辑框和头部视图面板的背景色
// ...
}
// 准备选项菜单
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 清除并重新创建选项菜单
// ...
}
// 选项菜单项点击事件处理
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 处理新建笔记、删除笔记、设置字体大小等选项菜单项点击事件
// ...
}
// 设置提醒
private void setReminder() {
// 显示日期时间选择器,设置提醒时间
// ...
}
// 分享笔记
private void sendTo(Context context, String info) {
// 创建分享Intent分享笔记内容
// ...
}
// 创建新笔记
private void createNewNote() {
// 保存当前编辑的笔记然后创建并启动一个新的NoteEditActivity实例
// ...
}
// 删除当前笔记
private void deleteCurrentNote() {
// 删除当前工作笔记,并处理同步模式下的特殊情况
// ...
}
// 判断是否为同步模式
private boolean isSyncMode() {
// ...
}
// 时钟提醒改变时的回调
public void onClockAlertChanged(long date, boolean set) {
// 设置或取消提醒时钟
// ...
}
// 小部件改变时的回调
public void onWidgetChanged() {
// 更新小部件
// ...
}
// 文本编辑删除事件处理
public void onEditTextDelete(int index, String text) {
// 处理文本编辑列表中的删除事件
// ...
}
// 文本编辑回车事件处理
public void onEditTextEnter(int index, String text) {
// 处理文本编辑列表中的回车事件
// ...
}
// 切换到列表模式
private void switchToListMode(String text) {
// 将编辑框的内容转换为列表模式
// ...
}
// 高亮显示用户查询结果
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
// ...
}
// 获取列表项视图
private View getListItem(String item, int index) {
// ...
}
// 文本改变事件处理
public void onTextChange(int index, boolean hasText) {
// 处理文本编辑列表中的文本改变事件
// ...
}
// 待办事项列表模式改变时的回调
public void onCheckListModeChanged(int oldMode, int newMode) {
// 根据新的模式更新视图
// ...
}
// 获取工作文本
private boolean getWorkingText() {
// ...
}
// 保存笔记
private boolean saveNote() {
// 保存当前工作笔记
// ...
}
// 发送到桌面
private void sendToDesktop() {
// 创建桌面快捷方式
// ...
}
// 生成快捷方式图标标题
private String makeShortcutIconTitle(String content) {
}
// 显示Toast消息
private void showToast(int resId) {
}
private void showToast(int resId, int duration) {
}
}

@ -0,0 +1,216 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text. style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
// 自定义EditText组件用于笔记应用中的文本编辑
public class NoteEditText extends EditText {
// 日志标签
private static final String TAG = "NoteEditText";
// 当前文本编辑框的索引
private int mIndex;
// 删除键按下前选中的文本起始位置
private int mSelectionStartBeforeDelete;
// URL方案的常量定义
private static final String SCHEME_TEL = "tel:"; // 电话
private static final String SCHEME_HTTP = "http:"; // HTTP
private static final String SCHEME_EMAIL = "mailto:"; // 电子邮件
// URL方案与资源ID的映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); // 电话链接资源ID
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); // 网页链接资源 ID
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); // 电子邮件链接资源ID
}
// 文本变化监听器接口
public interface OnTextViewChangeListener {
// 当按下删除键且文本为空时删除当前文本编辑框
void onEditTextDelete(int index, String text);
// 当按下回车键时在当前文本编辑框后添加新的文本编辑 框
void onEditTextEnter(int index, String text);
// 当文本变化时隐藏或显示选项菜单项
void onTextChange(int index, boolean hasText);
}
// 文本变化监听器实例
private OnTextViewChangeListener mOnTextViewChangeListener;
// 构造函数1仅通过上下文构造
public NoteEditText(Context context) {
super(context, null);
mIndex = 0; // 初始化索引为0
}
// 设置当前文本编辑框的索引
public void setIndex(int index) {
mIndex = index;
}
// 设置文本变化监听器
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
m OnTextViewChangeListener = listener;
}
// 构造函数2通过上下文和属性集构造
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
// 构造函数3通过上下文、属性集和默认样式构造
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 重写触摸事件处理方法
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX(); // 触摸点X坐标
int y = (int) event.getY(); // 触摸点Y坐标
x -= getTotalPaddingLeft(); // 减去左边距
y -= getTotalPaddingTop(); // 减去上边距
x += getScrollX(); // 加上水平滚动偏移
y += getScrollY(); // 加上 垂直滚动偏移
Layout layout = getLayout(); // 获取文本布局
int line = layout.getLineForVertical(y); // 获取触摸点所在的行
int off = layout.getOffsetForHorizontal(line, x); // 获取触摸点在该行的字符偏移量
Selection.setSelection(getText(), off); // 设置文本选中位置
break;
}
return super.onTouchEvent(event); // 调用父类方法处理 其他触摸事件
}
// 重写按键按下事件处理方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER: // 回车键按下时
if (mOnTextViewChangeListener != null) {
return false; // 如果设置了监听器,则不处理回车键事件
}
break;
case KeyEvent.KEYCODE_DEL: // 删除键按下时
mSelectionStartBeforeDelete = getSelectionStart(); // 记录删除键按下前的选中起始位置
break;
default:
break;
}
return super.onKeyDown(keyCode, event); // 调用父类方法处理其他按键事件
}
// 重写按键弹起事件处理方法
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DEL: // 删除键弹起时
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
// 如果删除键按下前没有选中文本且当前文本编辑框不是第一个,则删除当前文本编辑框
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted"); // 如果没有设置监听器,则记录日志
}
break;
case KeyEvent.KEYCODE_ENTER: // 回车键弹起时
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart(); // 获取当前选中起始位置
String text = getText().subSequence(selectionStart, length()).toString(); // 获取选中位置到文本末尾的字符串
setText(getText().subSequence(0 , selectionStart)); // 截取选中位置之前的文本作为新内容
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); // 在当前文本编辑框后添加新的文本编辑框并传入文本内容
} else {
Log.d(TAG , "OnTextViewChangeListener was not seted"); // 如果没有设置监听器,则记录日志
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event); // 调用父类方法处理其他按键弹起事件
}
// 重写焦点变化事件处理方法
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 如果失去焦点且文本为空 ,则隐藏选项菜单项
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 如果获得焦点或文本不为空,则显示选项菜单项
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect); // 调用父类方法处理其他焦点变化事件
}
// 重写 创建上下文菜单方法
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) { // 如果文本是Spanned类型支持富文本
int selStart = getSelectionStart(); // 获取选中起始位置
int selEnd = getSelectionEnd(); // 获取选中结束位置
int min = Math.min(selStart, selEnd); // 取选中范围的起始位置
int max = Math.max(selStart, selEnd); // 取选中范围的结束位置
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); // 获取选中范围内的URLSpan对象数组
if (urls.length == 1
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}

@ -0,0 +1,213 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
// NoteItemData类用于表示笔记项的数据
public class NoteItemData {
// 投影数组,指定从数据库查询时需要的列
static final String[] PROJECTION = new String[]{
NoteColumns.ID, // 笔记项的ID
NoteColumns.ALERTED_DATE, // 提醒日期
NoteColumns.BG_COLOR_ID, // 背景颜色ID
NoteColumns.CREATED_DATE, // 创建日期
NoteColumns.HAS_ATTACHMENT, // 是否有附件
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.NOTES_COUNT, // 笔记数量(可能是该笔记下的子笔记数量)
NoteColumns.PARENT_ID, // 父ID可能是文件夹ID
NoteColumns.SNIPPET, // 摘要
NoteColumns.TYPE, // 类型
NoteColumns.WIDGET_ID, // 控件ID
NoteColumns.WIDGET_TYPE, // 控件类型
};
// 列的索引用于从Cursor中获取数据
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
// 笔记项的属性
private long mId; // ID
private long mAlertDate; // 提醒日期
private int mBgColorId; // 背景颜色ID
private long mCreatedDate; // 创建日期
private boolean mHasAttachment; // 是否有附件
private long mModifiedDate; // 修改日期
private int mNotesCount; // 笔记数量
private long mParentId; // 父ID
private String mSnippet; // 摘要
private int mType; // 类型
private int mWidgetId; // 控件ID
private int mWidgetType; // 控件类型
private String mName; // 名称(对于通话记录,这是联系人的名称)
private String mPhoneNumber; // 电话号码(对于通话记录)
// 以下属性用于标识笔记项在列表中的位置
private boolean mIsLastItem; // 是否是最后一个项
private boolean mIsFirstItem; // 是否是第一个项
private boolean mIsOnlyOneItem; // 是否是唯一一个项
private boolean mIsOneNoteFollowingFolder; // 是否是一个笔记跟在文件夹后
private boolean mIsMultiNotesFollowingFolder; // 是否是多个笔记跟在文件夹后
// 构造函数根据上下文和Cursor初始化NoteItemData对象
public NoteItemData(Context context, Cursor cursor) {
// 从Cursor中获取数据并赋值给相应的属性
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN);
// 清除摘要中的已检查和未检查标签
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPhoneNumber = "";
// 如果是通话记录,则获取电话号码和联系人名称
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = "";
}
// 检查笔记项在列表中的位置
checkPostion(cursor);
}
// 检查笔记项在列表中的位置
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
// 如果是笔记类型且不是第一个项,则检查前一个项是否是文件夹或系统类型
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true;
}
}
// 确保能够移回原来的位置
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
// 获取是否是一个笔记跟在文件夹后
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
// 获取是否是多个笔记跟在文件夹后
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
// 获取是否是最后一个项
public boolean isLast() {
return mIsLastItem;
}
// 获取联系人名称(对于通话记录)
public String getCallName() {
return mName;
}
// 获取是否是第一个项
public boolean isFirst() {
return mIsFirstItem;
}
// 获取是否是唯一一个项
public boolean isSingle() {
return mIsOnlyOneItem;
}
// 获取ID
public long getId() {
return mId;
}
// 获取提醒日期
public long getAlertDate() {
return mAlertDate;
}
// 获取创建日期
public long getCreatedDate() {
return mCreatedDate;
}
// 获取是否有附件
public boolean hasAttachment() {
return mHasAttachment;
}
// 获取修改日期
public long getModifiedDate() {
return mModifiedDate;
}
// 获取背景颜色ID
public int getBgColorId() {
return mBgColorId;
}
// 获取父ID
public long getParentId() {
return mParentId;
}
// 获取笔记数量
public int getNotesCount() {
return mNotesCount;
}
// 获取文件夹ID

@ -0,0 +1,272 @@
【java】
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
// 常量定义
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
private static final int FOLDER_LIST_QUERY_TOKEN = 1;
private static final int MENU_FOLDER_DELETE = 0;
private static final int MENU_FOLDER_VIEW = 1;
private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
// 枚举类定义列表编辑状态
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
// 成员变量定义
private ListEditState mState;
private BackgroundQueryHandler mBackgroundQueryHandler;
private NotesListAdapter mNotesListAdapter;
private ListView mNotesListView;
private Button mAddNewNote;
private boolean mDispatch;
private int mOriginY;
private int mDispatchY;
private TextView mTitleBar;
private long mCurrentFolderId;
private ContentResolver mContentResolver;
private ModeCallback mModeCallBack;
private static final String TAG = "NotesListActivity";
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
private NoteItemData mFocusNoteDataItem;
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_list);
initResources();
// 插入应用介绍
setAppInfoFromRawRes();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
// 从资源文件中读取介绍信息并保存为笔记
}
}
@Override
protected void onStart() {
super.onStart();
startAsyncNotesListQuery();
}
private void initResources() {
// 初始化资源
}
// 回调接口实现
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
// 实现ListView的多选模式监听器接口
}
private class NewNoteOnTouchListener implements OnTouchListener {
// 实现新笔记按钮的触摸监听器接口
}
private void startAsyncNotesListQuery() {
// 异步查询笔记列表
}
private final class BackgroundQueryHandler extends AsyncQueryHandler {
// 异步查询处理器
}
private void showFolderListMenu(Cursor cursor) {
// 显示文件夹列表菜单
}
private void createNewNote() {
// 创建新笔记
}
private void batchDelete() {
// 批量删除笔记
}
private void deleteFolder(long folderId) {
// 删除文件夹
}
private void openNode(NoteItemData data) {
// 打开笔记节点
}
private void openFolder(NoteItemData data) {
// 打开文件夹
}
@Override
public void onClick(View v) {
// 按钮点击事件处理
}
private void showSoftInput() {
// 显示软键盘
}
private void hideSoftInput(View view) {
// 隐藏软键盘
}
private void showCreateOrModifyFolderDialog(final boolean create) {
// 显示创建或修改文件夹对话框
}
@Override
public void onBackPressed() {
// 后退按钮事件处理
}
private void updateWidget(int appWidgetId, int appWidgetType) {
// 更新小部件
}
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
// 实现创建上下文菜单监听器接口
};
@Override
public void onContextMenuClosed(Menu menu) {
// 上下文菜单关闭事件处理
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// 上下文菜单项点击事件处理
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 准备选项菜单
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 选项菜单项点击事件处理
}
@Override
public boolean onSearchRequested() {
// 搜索请求事件处理
}
private void exportNoteToText() {
// 导出笔记到文本文件
}
private boolean isSyncMode() {
// 判断是否处于同步模式
}
private void startPreferenceActivity() {
// 启动设置活动
}
private class OnListItemClickListener implements OnItemClickListener {
// 实现列表项点击监听器接口
}
private void startQueryDestinationFolders() {
// 开始查询目标文件夹
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// 列表项长按事件处理
}
}
Loading…
Cancel
Save