From 8b31f5f248ffec62353da71f2e858f5aaddedcd1 Mon Sep 17 00:00:00 2001 From: zhangqing <1770666340@qq.com> Date: Wed, 24 Dec 2025 00:17:57 +0800 Subject: [PATCH] 1 --- .../notes/gtask/remote/GTaskASyncTask.java | 155 +-- .../notes/gtask/remote/GTaskClient.java | 646 +++++------- .../notes/gtask/remote/GTaskManager.java | 850 ++++++++-------- .../notes/gtask/remote/GTaskSyncService.java | 938 +----------------- 4 files changed, 723 insertions(+), 1866 deletions(-) diff --git a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index c7d62d1..0219e09 100644 --- a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -27,183 +27,112 @@ import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; -/** - * GTaskASyncTask 类 - * 继承自AsyncTask,用于在后台执行Google Tasks同步操作 - * 提供同步进度通知和同步结果处理功能 - * - * AsyncTask参数说明: - * Void - 执行任务时传入的参数类型(不需要参数) - * String - 进度更新时使用的参数类型(进度消息) - * Integer - 任务执行结果的类型(同步状态码) - */ -public class GTaskASyncTask extends AsyncTask { - /** - * 同步通知的唯一ID - * 用于标识Google Tasks同步相关的通知,避免与其他通知冲突 - */ +public class GTaskASyncTask extends AsyncTask { + // 同步通知的ID,确保唯一性 private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - /** - * 同步完成监听器接口 - * 用于在同步完成后执行回调操作 - */ + // 同步完成回调接口 public interface OnCompleteListener { - /** - * 同步完成时的回调方法 - */ - void onComplete(); + void onComplete(); // 同步完成时调用 } - // 上下文对象 - private Context mContext; - // 通知管理器,用于显示同步进度通知 - private NotificationManager mNotifiManager; - // Google Tasks管理器,执行实际的同步操作 - private GTaskManager mTaskManager; - // 同步完成监听器 - private OnCompleteListener mOnCompleteListener; + private Context mContext; // 上下文对象 + private NotificationManager mNotifiManager; // 通知管理器 + private GTaskManager mTaskManager; // 任务管理器 + private OnCompleteListener mOnCompleteListener; // 完成监听器 - /** - * 构造函数 - * - * @param context 上下文对象 - * @param listener 同步完成监听器 - */ + // 构造函数 public GTaskASyncTask(Context context, OnCompleteListener listener) { - mContext = context; - mOnCompleteListener = listener; - // 获取通知管理器服务 + mContext = context; // 保存上下文 + mOnCompleteListener = listener; // 保存完成监听器 mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - // 获取Google Tasks管理器单例 - mTaskManager = GTaskManager.getInstance(); + .getSystemService(Context.NOTIFICATION_SERVICE); // 获取通知管理器服务 + mTaskManager = GTaskManager.getInstance(); // 获取任务管理器单例 } - /** - * 取消同步操作 - * 调用GTaskManager的cancelSync方法取消正在进行的同步 - */ + // 取消同步操作 public void cancelSync() { - mTaskManager.cancelSync(); + mTaskManager.cancelSync(); // 委托给任务管理器取消同步 } - /** - * 发布同步进度 - * 包装publishProgress方法,简化进度更新操作 - * - * @param message 进度消息 - */ + // 发布同步进度 public void publishProgess(String message) { publishProgress(new String[] { - message + message // 发布单个进度消息 }); } - /** - * 显示通知 - * 根据同步状态显示不同类型的通知 - * - * @param tickerId 通知标题资源ID - * @param content 通知内容文本 - */ + // 显示通知 private void showNotification(int tickerId, String content) { // 创建通知对象 Notification notification = new Notification(R.drawable.notification, mContext - .getString(tickerId), System.currentTimeMillis()); - notification.defaults = Notification.DEFAULT_LIGHTS; // 默认灯光效果 - notification.flags = Notification.FLAG_AUTO_CANCEL; // 点击后自动取消 + .getString(tickerId), System.currentTimeMillis()); // 设置图标、文本和时间戳 + notification.defaults = Notification.DEFAULT_LIGHTS; // 设置默认灯光效果 + notification.flags = Notification.FLAG_AUTO_CANCEL; // 设置点击后自动取消 PendingIntent pendingIntent; - // 根据通知类型设置不同的点击意图 if (tickerId != R.string.ticker_success) { - // 同步失败或进行中:点击跳转到设置页面 + // 如果不是成功状态,点击通知跳转到设置页面 pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); } else { - // 同步成功:点击跳转到笔记列表页面 + // 如果是成功状态,点击通知跳转到笔记列表页面 pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } - // 设置通知的详细内容 - notification.setLatestEventInfo(mContext, - mContext.getString(R.string.app_name), // 应用名称作为标题 - content, // 同步状态作为内容 - pendingIntent); // 点击意图 - - // 显示通知 - mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); + // 设置通知的详细信息 + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, + pendingIntent); // 设置标题、内容和点击意图 + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // 显示通知 } - /** - * 后台执行同步任务 - * AsyncTask的核心方法,在后台线程中执行 - * - * @param unused 可变参数(未使用) - * @return 同步结果状态码 - */ + // 后台执行同步任务 @Override protected Integer doInBackground(Void... unused) { - // 发布登录进度消息 + // 发布登录进度 publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); - - // 执行同步操作,将当前异步任务作为进度更新回调 - return mTaskManager.sync(mContext, this); + .getSyncAccountName(mContext))); // 显示正在登录指定账户的进度 + return mTaskManager.sync(mContext, this); // 执行同步操作并返回结果状态 } - /** - * 进度更新回调 - * 在UI线程中执行,用于更新同步进度 - * - * @param progress 进度消息数组 - */ + // 更新同步进度 @Override protected void onProgressUpdate(String... progress) { - // 显示同步进行中的通知 - showNotification(R.string.ticker_syncing, progress[0]); - - // 如果上下文是GTaskSyncService,发送广播通知进度 + showNotification(R.string.ticker_syncing, progress[0]); // 显示同步中的通知 if (mContext instanceof GTaskSyncService) { + // 如果上下文是同步服务,发送广播通知进度 ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } - /** - * 同步任务执行完成回调 - * 在UI线程中执行,处理同步结果 - * - * @param result 同步结果状态码 - */ + // 同步完成后处理结果 @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()); + 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)); + 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)); + 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)); + .getString(R.string.error_sync_cancelled)); // 显示取消通知 } - // 调用同步完成监听器 + // 通知监听器同步已完成 if (mOnCompleteListener != null) { new Thread(new Runnable() { public void run() { - // 在新线程中执行完成回调,避免阻塞UI线程 - mOnCompleteListener.onComplete(); + mOnCompleteListener.onComplete(); // 在新线程中调用完成回调 } }).start(); } diff --git a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskClient.java index 34cfbc4..10fac3e 100644 --- a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -60,698 +60,534 @@ import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; -/** - * GTaskClient 类 - * Google Tasks API客户端,负责与Google Tasks服务器进行通信 - * 使用单例模式,提供登录、创建任务、更新任务、获取任务列表等功能 - */ + public class GTaskClient { - // 日志标签 - private static final String TAG = GTaskClient.class.getSimpleName(); + private static final String TAG = GTaskClient.class.getSimpleName(); // 日志标签 - // Google Tasks基础URL(用于普通访问) + // Google Tasks API URL常量 private static final String GTASK_URL = "https://mail.google.com/tasks/"; - // Google Tasks获取数据的URL - private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - // Google Tasks提交数据的URL - private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - - // 单例实例 - private static GTaskClient mInstance = null; - - // HTTP客户端,用于发送网络请求 - private DefaultHttpClient mHttpClient; - // 获取数据的URL(可能根据账户域名变化) - private String mGetUrl; - // 提交数据的URL(可能根据账户域名变化) - private String mPostUrl; - // 客户端版本号(从服务器获取) - private long mClientVersion; - // 登录状态标志 - private boolean mLoggedin; - // 最后登录时间戳 - private long mLastLoginTime; - // 动作ID计数器(用于标识每个请求) - private int mActionId; - // 当前同步的Google账户 - private Account mAccount; - // 待提交的更新数组(批量更新用) - private JSONArray mUpdateArray; - - /** - * 私有构造函数 - * 初始化所有字段为默认值 - */ + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; // 获取数据URL + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; // 提交数据URL + + private static GTaskClient mInstance = null; // 单例实例 + + // HTTP客户端和相关参数 + private DefaultHttpClient mHttpClient; // HTTP客户端实例 + private String mGetUrl; // 动态生成的GET URL + private String mPostUrl; // 动态生成的POST URL + private long mClientVersion; // 客户端版本号,从服务器获取 + private boolean mLoggedin; // 登录状态标志 + private long mLastLoginTime; // 上次登录时间 + private int mActionId; // 操作ID计数器,用于生成唯一的操作ID + private Account mAccount; // 当前登录的Google账户 + private JSONArray mUpdateArray; // 批量更新操作的JSON数组 + private GTaskClient() { + // 初始化所有成员变量 mHttpClient = null; - mGetUrl = GTASK_GET_URL; // 默认获取URL - mPostUrl = GTASK_POST_URL; // 默认提交URL - mClientVersion = -1; // 未获取版本号 - mLoggedin = false; // 未登录状态 - mLastLoginTime = 0; // 最后登录时间为0 - mActionId = 1; // 动作ID从1开始 - mAccount = null; // 未设置账户 - mUpdateArray = null; // 无待提交更新 + mGetUrl = GTASK_GET_URL; // 默认GET URL + mPostUrl = GTASK_POST_URL; // 默认POST URL + mClientVersion = -1; // 未初始化的版本号 + mLoggedin = false; // 初始状态为未登录 + mLastLoginTime = 0; // 初始登录时间为0 + mActionId = 1; // 操作ID从1开始 + mAccount = null; // 初始账户为空 + mUpdateArray = null; // 初始更新数组为空 } - /** - * 获取GTaskClient单例实例 - * - * @return GTaskClient单例实例 - */ + // 获取单例实例 public static synchronized GTaskClient getInstance() { if (mInstance == null) { - mInstance = new GTaskClient(); + mInstance = new GTaskClient(); // 第一次调用时创建实例 } return mInstance; } - /** - * 登录到Google Tasks - * 处理Google账户认证和Tasks服务登录 - * - * @param activity 活动上下文,用于账户管理器交互 - * @return 登录是否成功 - */ + // 登录方法,返回登录是否成功 public boolean login(Activity activity) { - // 假设Cookie在5分钟后过期,需要重新登录 - final long interval = 1000 * 60 * 5; // 5分钟 + // 检查Cookie是否过期(假设5分钟后过期) + final long interval = 1000 * 60 * 5; // 5分钟的毫秒数 if (mLastLoginTime + interval < System.currentTimeMillis()) { - mLoggedin = false; // 超过5分钟,标记为未登录 + mLoggedin = false; // 如果超过5分钟,标记为未登录 } - // 切换账户后需要重新登录 + // 检查账户是否切换,需要重新登录 if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { - mLoggedin = false; // 账户不匹配,标记为未登录 + mLoggedin = false; // 账户已切换,需要重新登录 } - // 如果已经登录,直接返回成功 if (mLoggedin) { - Log.d(TAG, "already logged in"); + Log.d(TAG, "already logged in"); // 已经登录,直接返回 return true; } - // 记录当前时间为最后登录时间 - mLastLoginTime = System.currentTimeMillis(); - - // 获取Google账户认证令牌 - String authToken = loginGoogleAccount(activity, false); + mLastLoginTime = System.currentTimeMillis(); // 更新最后登录时间 + String authToken = loginGoogleAccount(activity, false); // 获取Google账户授权令牌 if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; // 获取认证令牌失败 + Log.e(TAG, "login google account failed"); // 获取授权令牌失败 + return false; } - // 如果账户不是gmail.com或googlemail.com,使用自定义域名登录 + // 如果不是gmail.com或googlemail.com账户,尝试使用自定义域名登录 if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { - StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); - int index = mAccount.name.indexOf('@') + 1; - String suffix = mAccount.name.substring(index); - url.append(suffix + "/"); - mGetUrl = url.toString() + "ig"; // 设置自定义获取URL - mPostUrl = url.toString() + "r/ig"; // 设置自定义提交URL - - // 尝试使用自定义域名登录 - if (tryToLoginGtask(activity, authToken)) { + StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); // 构建自定义域名URL + int index = mAccount.name.indexOf('@') + 1; // 找到@符号位置 + String suffix = mAccount.name.substring(index); // 提取域名后缀 + url.append(suffix + "/"); // 添加域名后缀 + mGetUrl = url.toString() + "ig"; // 设置自定义GET URL + mPostUrl = url.toString() + "r/ig"; // 设置自定义POST URL + + if (tryToLoginGtask(activity, authToken)) { // 尝试使用自定义域名登录 mLoggedin = true; // 登录成功 } } - // 如果自定义域名登录失败或不是自定义域名,尝试使用官方URL登录 + // 如果自定义域名登录失败,尝试使用Google官方URL登录 if (!mLoggedin) { - mGetUrl = GTASK_GET_URL; // 恢复默认获取URL - mPostUrl = GTASK_POST_URL; // 恢复默认提交URL - if (!tryToLoginGtask(activity, authToken)) { + mGetUrl = GTASK_GET_URL; // 重置为默认GET URL + mPostUrl = GTASK_POST_URL; // 重置为默认POST URL + if (!tryToLoginGtask(activity, authToken)) { // 尝试使用官方URL登录 return false; // 登录失败 } } mLoggedin = true; // 标记为已登录 - return true; // 登录成功 + return true; // 登录成功 } - /** - * 登录Google账户并获取认证令牌 - * - * @param activity 活动上下文 - * @param invalidateToken 是否使旧令牌失效 - * @return 认证令牌,失败返回null - */ + // 登录Google账户并获取授权令牌 private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; - AccountManager accountManager = AccountManager.get(activity); - - // 获取所有Google账户 - Account[] accounts = accountManager.getAccountsByType("com.google"); + AccountManager accountManager = AccountManager.get(activity); // 获取账户管理器 + Account[] accounts = accountManager.getAccountsByType("com.google"); // 获取所有Google账户 if (accounts.length == 0) { - Log.e(TAG, "there is no available google account"); - return null; // 没有可用的Google账户 + Log.e(TAG, "there is no available google account"); // 没有可用的Google账户 + return null; } - // 从设置中获取同步账户名称 + // 获取设置中配置的同步账户名 String accountName = NotesPreferenceActivity.getSyncAccountName(activity); Account account = null; - - // 查找与设置中名称匹配的账户 for (Account a : accounts) { - if (a.name.equals(accountName)) { + if (a.name.equals(accountName)) { // 查找匹配的账户 account = a; break; } } - if (account != null) { mAccount = account; // 设置当前账户 } else { - Log.e(TAG, "unable to get an account with the same name in the settings"); - return null; // 找不到匹配的账户 + Log.e(TAG, "unable to get an account with the same name in the settings"); // 未找到匹配账户 + return null; } - // 获取认证令牌 + // 获取授权令牌 AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, - "goanna_mobile", null, activity, null, null); + "goanna_mobile", null, activity, null, null); // 请求授权令牌 try { - Bundle authTokenBundle = accountManagerFuture.getResult(); - authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); - - // 如果需要使令牌失效 - if (invalidateToken) { - accountManager.invalidateAuthToken("com.google", authToken); - // 重新获取新令牌 - return loginGoogleAccount(activity, false); + Bundle authTokenBundle = accountManagerFuture.getResult(); // 获取结果 + authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 提取授权令牌 + if (invalidateToken) { // 如果需要使令牌失效 + accountManager.invalidateAuthToken("com.google", authToken); // 使当前令牌失效 + loginGoogleAccount(activity, false); // 重新获取令牌 } } catch (Exception e) { - Log.e(TAG, "get auth token failed"); - authToken = null; // 获取令牌失败 + Log.e(TAG, "get auth token failed"); // 获取授权令牌失败 + authToken = null; } - return authToken; + return authToken; // 返回授权令牌 } - /** - * 尝试登录Google Tasks服务 - * - * @param activity 活动上下文 - * @param authToken 认证令牌 - * @return 登录是否成功 - */ + // 尝试登录Google Tasks private boolean tryToLoginGtask(Activity activity, String authToken) { - // 第一次尝试登录 - if (!loginGtask(authToken)) { - // 令牌可能已过期,使令牌失效并重新获取 - authToken = loginGoogleAccount(activity, true); + if (!loginGtask(authToken)) { // 第一次登录尝试 + // 授权令牌可能已过期,尝试重新获取令牌并登录 + authToken = loginGoogleAccount(activity, true); // 重新获取授权令牌 if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; // 重新获取令牌失败 + Log.e(TAG, "login google account failed"); // 重新获取令牌失败 + return false; } - // 使用新令牌再次尝试登录 - if (!loginGtask(authToken)) { - Log.e(TAG, "login gtask failed"); - return false; // 再次登录失败 + if (!loginGtask(authToken)) { // 使用新令牌再次尝试登录 + Log.e(TAG, "login gtask failed"); // 登录失败 + return false; } } return true; // 登录成功 } - /** - * 实际执行Google Tasks登录 - * 发送HTTP请求获取Cookie和客户端版本 - * - * @param authToken 认证令牌 - * @return 登录是否成功 - */ + // 实际的Google Tasks登录逻辑 private boolean loginGtask(String authToken) { - // 设置HTTP连接参数 - int timeoutConnection = 10000; // 连接超时10秒 - int timeoutSocket = 15000; // 套接字超时15秒 - HttpParams httpParameters = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); - HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); - - // 创建HTTP客户端 - mHttpClient = new DefaultHttpClient(httpParameters); - BasicCookieStore localBasicCookieStore = new BasicCookieStore(); + int timeoutConnection = 10000; // 连接超时时间(10秒) + int timeoutSocket = 15000; // Socket超时时间(15秒) + HttpParams httpParameters = new BasicHttpParams(); // 创建HTTP参数 + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); // 设置连接超时 + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); // 设置Socket超时 + mHttpClient = new DefaultHttpClient(httpParameters); // 创建HTTP客户端 + BasicCookieStore localBasicCookieStore = new BasicCookieStore(); // 创建Cookie存储 mHttpClient.setCookieStore(localBasicCookieStore); // 设置Cookie存储 HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 禁用Expect-Continue // 登录Google Tasks try { - String loginUrl = mGetUrl + "?auth=" + authToken; - HttpGet httpGet = new HttpGet(loginUrl); + String loginUrl = mGetUrl + "?auth=" + authToken; // 构建登录URL + HttpGet httpGet = new HttpGet(loginUrl); // 创建GET请求 HttpResponse response = null; - response = mHttpClient.execute(httpGet); // 执行GET请求 + response = mHttpClient.execute(httpGet); // 执行HTTP请求 - // 检查是否获取到认证Cookie - List cookies = mHttpClient.getCookieStore().getCookies(); + // 检查Cookie中是否包含认证信息 + List cookies = mHttpClient.getCookieStore().getCookies(); // 获取所有Cookie boolean hasAuthCookie = false; for (Cookie cookie : cookies) { - if (cookie.getName().contains("GTL")) { - hasAuthCookie = true; // 找到认证Cookie + if (cookie.getName().contains("GTL")) { // 查找包含"GTL"的Cookie(Google Tasks认证) + hasAuthCookie = true; } } if (!hasAuthCookie) { - Log.w(TAG, "it seems that there is no auth cookie"); + Log.w(TAG, "it seems that there is no auth cookie"); // 警告:未找到认证Cookie } - // 从响应中解析客户端版本号 - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - int begin = resString.indexOf(jsBegin); - int end = resString.lastIndexOf(jsEnd); + // 从响应中提取客户端版本号 + String resString = getResponseContent(response.getEntity()); // 获取响应内容 + String jsBegin = "_setup("; // JavaScript响应开始标记 + String jsEnd = ")}"; // JavaScript响应结束标记 + int begin = resString.indexOf(jsBegin); // 查找开始位置 + int end = resString.lastIndexOf(jsEnd); // 查找结束位置 String jsString = null; - if (begin != -1 && end != -1 && begin < end) { - jsString = resString.substring(begin + jsBegin.length(), end); + if (begin != -1 && end != -1 && begin < end) { // 确保找到有效位置 + jsString = resString.substring(begin + jsBegin.length(), end); // 提取JavaScript字符串 } - JSONObject js = new JSONObject(jsString); + JSONObject js = new JSONObject(jsString); // 解析为JSON对象 mClientVersion = js.getLong("v"); // 获取客户端版本号 - } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON解析异常 e.printStackTrace(); - return false; // JSON解析失败 + return false; } catch (Exception e) { - // 捕获所有异常 - Log.e(TAG, "httpget gtask_url failed"); - return false; // HTTP请求失败 + // 捕获所有其他异常 + Log.e(TAG, "httpget gtask_url failed"); // HTTP GET请求失败 + return false; } return true; // 登录成功 } - /** - * 获取下一个动作ID - * 每次调用递增,确保每个请求有唯一ID - * - * @return 动作ID - */ + // 获取下一个操作ID private int getActionId() { - return mActionId++; + return mActionId++; // 返回当前操作ID并递增 } - /** - * 创建HTTP POST请求对象 - * 设置必要的请求头 - * - * @return 配置好的HttpPost对象 - */ + // 创建HTTP POST请求对象 private HttpPost createHttpPost() { - HttpPost httpPost = new HttpPost(mPostUrl); - // 设置请求头 - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - httpPost.setHeader("AT", "1"); // 认证令牌头 + HttpPost httpPost = new HttpPost(mPostUrl); // 创建POST请求 + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); // 设置内容类型 + httpPost.setHeader("AT", "1"); // 设置AT头部(认证令牌) return httpPost; } - /** - * 从HTTP响应实体获取内容 - * 支持GZIP和Deflate压缩格式 - * - * @param entity HTTP响应实体 - * @return 响应内容字符串 - * @throws IOException 读取响应内容时发生错误 - */ + // 从HTTP实体获取响应内容,支持GZIP和Deflate压缩 private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { - contentEncoding = entity.getContentEncoding().getValue(); - Log.d(TAG, "encoding: " + contentEncoding); + contentEncoding = entity.getContentEncoding().getValue(); // 获取内容编码 + Log.d(TAG, "encoding: " + contentEncoding); // 记录编码类型 } - InputStream input = entity.getContent(); - // 根据压缩格式创建相应的输入流 + InputStream input = entity.getContent(); // 获取输入流 if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { - input = new GZIPInputStream(entity.getContent()); // GZIP解压 + input = new GZIPInputStream(entity.getContent()); // 如果是GZIP编码,使用GZIP输入流 } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { - Inflater inflater = new Inflater(true); - input = new InflaterInputStream(entity.getContent(), inflater); // Deflate解压 + Inflater inflater = new Inflater(true); // 创建Inflater对象 + input = new InflaterInputStream(entity.getContent(), inflater); // 使用Inflater输入流 } try { - // 读取响应内容 - InputStreamReader isr = new InputStreamReader(input); - BufferedReader br = new BufferedReader(isr); - StringBuilder sb = new StringBuilder(); + InputStreamReader isr = new InputStreamReader(input); // 创建输入流读取器 + BufferedReader br = new BufferedReader(isr); // 创建缓冲读取器 + StringBuilder sb = new StringBuilder(); // 创建字符串构建器 while (true) { - String buff = br.readLine(); + String buff = br.readLine(); // 读取一行 if (buff == null) { - return sb.toString(); // 读取完成 + return sb.toString(); // 读取完成,返回内容 } - sb = sb.append(buff); + sb = sb.append(buff); // 添加到字符串构建器 } } finally { input.close(); // 确保关闭输入流 } } - /** - * 发送POST请求到Google Tasks服务器 - * - * @param js 要发送的JSON对象 - * @return 服务器响应的JSON对象 - * @throws NetworkFailureException 网络请求失败 - */ + // 发送POST请求到Google Tasks API private JSONObject postRequest(JSONObject js) throws NetworkFailureException { - // 检查登录状态 if (!mLoggedin) { - Log.e(TAG, "please login first"); + Log.e(TAG, "please login first"); // 未登录错误 throw new ActionFailureException("not logged in"); } - HttpPost httpPost = createHttpPost(); + HttpPost httpPost = createHttpPost(); // 创建POST请求 try { - // 准备请求参数 - LinkedList list = new LinkedList(); - list.add(new BasicNameValuePair("r", js.toString())); // JSON数据作为参数 - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); - httpPost.setEntity(entity); + LinkedList list = new LinkedList(); // 创建参数列表 + list.add(new BasicNameValuePair("r", js.toString())); // 添加JSON参数 + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 创建URL编码实体 + httpPost.setEntity(entity); // 设置请求实体 // 执行POST请求 - HttpResponse response = mHttpClient.execute(httpPost); - String jsString = getResponseContent(response.getEntity()); - return new JSONObject(jsString); // 解析响应为JSON对象 + HttpResponse response = mHttpClient.execute(httpPost); // 执行请求 + String jsString = getResponseContent(response.getEntity()); // 获取响应内容 + return new JSONObject(jsString); // 解析为JSON对象并返回 } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // HTTP协议异常 e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (IOException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // IO异常 e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON解析异常 e.printStackTrace(); throw new ActionFailureException("unable to convert response content to jsonobject"); } catch (Exception e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // 其他异常 e.printStackTrace(); throw new ActionFailureException("error occurs when posting request"); } } - /** - * 在Google Tasks中创建新任务 - * - * @param task 要创建的任务对象 - * @throws NetworkFailureException 网络请求失败 - */ + // 创建任务 public void createTask(Task task) throws NetworkFailureException { - commitUpdate(); // 先提交所有待更新 + commitUpdate(); // 提交之前的更新 try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON + JSONArray actionList = new JSONArray(); // 创建操作列表 - // 构建动作列表 - actionList.put(task.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + // 添加创建任务的操作 + actionList.put(task.getCreateAction(getActionId())); // 获取任务的创建操作 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 添加操作列表 // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送请求 - JSONObject jsResponse = postRequest(jsPost); - // 从响应中提取新任务的ID + JSONObject jsResponse = postRequest(jsPost); // 发送POST请求 JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 获取结果数组的第一个元素 + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务的GID } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("create task: handing jsonobject failed"); } } - /** - * 在Google Tasks中创建新任务列表 - * - * @param tasklist 要创建的任务列表对象 - * @throws NetworkFailureException 网络请求失败 - */ + // 创建任务列表 public void createTaskList(TaskList tasklist) throws NetworkFailureException { - commitUpdate(); // 先提交所有待更新 + commitUpdate(); // 提交之前的更新 try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON + JSONArray actionList = new JSONArray(); // 创建操作列表 - // 构建动作列表 - actionList.put(tasklist.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + // 添加创建任务列表的操作 + actionList.put(tasklist.getCreateAction(getActionId())); // 获取任务列表的创建操作 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 添加操作列表 // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送请求 - JSONObject jsResponse = postRequest(jsPost); - // 从响应中提取新任务列表的ID + JSONObject jsResponse = postRequest(jsPost); // 发送POST请求 JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 获取结果数组的第一个元素 + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务列表的GID } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } - /** - * 提交所有待更新的操作 - * 批量提交更新,提高效率 - * - * @throws NetworkFailureException 网络请求失败 - */ + // 提交批量更新 public void commitUpdate() throws NetworkFailureException { - if (mUpdateArray != null) { + if (mUpdateArray != null) { // 如果有待提交的更新 try { - JSONObject jsPost = new JSONObject(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON - // 添加动作列表 + // 添加操作列表 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // 发送请求 - postRequest(jsPost); + postRequest(jsPost); // 发送请求 mUpdateArray = null; // 清空更新数组 - } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("commit update: handing jsonobject failed"); } } } - /** - * 添加节点更新到待提交数组 - * 当更新项过多时自动提交 - * - * @param node 要更新的节点 - * @throws NetworkFailureException 网络请求失败 - */ + // 添加节点到更新数组 public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { - // 更新项过多时自动提交(最多10项) + // 如果更新数组过大(超过10个),先提交当前更新 if (mUpdateArray != null && mUpdateArray.length() > 10) { - commitUpdate(); + commitUpdate(); // 提交当前更新 } - // 初始化更新数组(如果为空) - if (mUpdateArray == null) + if (mUpdateArray == null) // 如果更新数组为空,创建新数组 mUpdateArray = new JSONArray(); - // 添加更新动作 - mUpdateArray.put(node.getUpdateAction(getActionId())); + mUpdateArray.put(node.getUpdateAction(getActionId())); // 添加节点的更新操作 } } - /** - * 移动任务到新的任务列表 - * - * @param task 要移动的任务 - * @param preParent 原始父任务列表 - * @param curParent 目标父任务列表 - * @throws NetworkFailureException 网络请求失败 - */ + // 移动任务 public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { - commitUpdate(); // 先提交所有待更新 + commitUpdate(); // 提交之前的更新 try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON + JSONArray actionList = new JSONArray(); // 创建操作列表 + JSONObject action = new JSONObject(); // 创建移动操作 - // 构建移动动作 + // 构建移动操作 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); - - // 在同一任务列表内移动时,设置前兄弟节点ID + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); // 设置操作类型为移动 + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置操作ID + action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); // 设置任务ID if (preParent == curParent && task.getPriorSibling() != null) { + // 如果是在同一个任务列表中移动,且任务不是第一个,设置前一个兄弟节点的ID action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); } - - action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); - action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); - - // 在不同任务列表间移动时,设置目标列表ID + action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); // 设置源列表ID + action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); // 设置目标父节点ID if (preParent != curParent) { + // 如果是在不同任务列表之间移动,设置目标列表ID action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } - - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + actionList.put(action); // 将操作添加到操作列表 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 添加操作列表到请求 // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // 发送请求 - postRequest(jsPost); + postRequest(jsPost); // 发送请求 } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("move task: handing jsonobject failed"); } } - /** - * 删除节点(任务或任务列表) - * - * @param node 要删除的节点 - * @throws NetworkFailureException 网络请求失败 - */ + // 删除节点 public void deleteNode(Node node) throws NetworkFailureException { - commitUpdate(); // 先提交所有待更新 + commitUpdate(); // 提交之前的更新 try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON + JSONArray actionList = new JSONArray(); // 创建操作列表 - // 设置节点为删除状态 - node.setDeleted(true); - // 构建更新动作 - actionList.put(node.getUpdateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + // 添加删除操作 + node.setDeleted(true); // 标记节点为已删除 + actionList.put(node.getUpdateAction(getActionId())); // 获取节点的更新操作(包含删除标记) + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 添加操作列表 // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // 发送请求 - postRequest(jsPost); + postRequest(jsPost); // 发送请求 mUpdateArray = null; // 清空更新数组 - } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("delete node: handing jsonobject failed"); } } - /** - * 获取所有任务列表 - * - * @return 任务列表的JSON数组 - * @throws NetworkFailureException 网络请求失败 - */ + // 获取所有任务列表 public JSONArray getTaskLists() throws NetworkFailureException { - // 检查登录状态 if (!mLoggedin) { - Log.e(TAG, "please login first"); + Log.e(TAG, "please login first"); // 未登录错误 throw new ActionFailureException("not logged in"); } try { - HttpGet httpGet = new HttpGet(mGetUrl); + HttpGet httpGet = new HttpGet(mGetUrl); // 创建GET请求 HttpResponse response = null; - response = mHttpClient.execute(httpGet); // 执行GET请求 - - // 解析响应内容 - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - int begin = resString.indexOf(jsBegin); - int end = resString.lastIndexOf(jsEnd); + response = mHttpClient.execute(httpGet); // 执行请求 + + // 从响应中提取任务列表数据 + String resString = getResponseContent(response.getEntity()); // 获取响应内容 + String jsBegin = "_setup("; // JavaScript响应开始标记 + String jsEnd = ")}"; // JavaScript响应结束标记 + int begin = resString.indexOf(jsBegin); // 查找开始位置 + int end = resString.lastIndexOf(jsEnd); // 查找结束位置 String jsString = null; - if (begin != -1 && end != -1 && begin < end) { - jsString = resString.substring(begin + jsBegin.length(), end); + if (begin != -1 && end != -1 && begin < end) { // 确保找到有效位置 + jsString = resString.substring(begin + jsBegin.length(), end); // 提取JavaScript字符串 } - JSONObject js = new JSONObject(jsString); - // 提取任务列表数组 - return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); - + JSONObject js = new JSONObject(jsString); // 解析为JSON对象 + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); // 返回任务列表数组 } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // HTTP协议异常 e.printStackTrace(); throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // IO异常 e.printStackTrace(); throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON解析异常 e.printStackTrace(); throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - /** - * 获取指定任务列表的所有任务 - * - * @param listGid 任务列表的Google ID - * @return 任务的JSON数组 - * @throws NetworkFailureException 网络请求失败 - */ + // 获取特定任务列表的所有任务 public JSONArray getTaskList(String listGid) throws NetworkFailureException { - commitUpdate(); // 先提交所有待更新 + commitUpdate(); // 提交之前的更新 try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); + JSONObject jsPost = new JSONObject(); // 创建POST请求JSON + JSONArray actionList = new JSONArray(); // 创建操作列表 + JSONObject action = new JSONObject(); // 创建获取所有任务的操作 - // 构建获取所有任务的动作 + // 构建获取所有任务的操作 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); - action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 不获取已删除的任务 - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); // 设置操作类型为获取所有 + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置操作ID + action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); // 设置任务列表ID + action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 设置不获取已删除的任务 + actionList.put(action); // 将操作添加到操作列表 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 添加操作列表到请求 // 添加客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // 发送请求并获取响应 - JSONObject jsResponse = postRequest(jsPost); - return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); - + JSONObject jsResponse = postRequest(jsPost); // 发送请求 + return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); // 返回任务数组 } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // JSON处理异常 e.printStackTrace(); throw new ActionFailureException("get task list: handing jsonobject failed"); } } - /** - * 获取当前同步的账户 - * - * @return 当前同步的Google账户 - */ + // 获取当前同步账户 public Account getSyncAccount() { return mAccount; } - /** - * 重置更新数组 - * 清空所有待提交的更新 - */ + // 重置更新数组 public void resetUpdateArray() { mUpdateArray = null; } diff --git a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java index 080c0c8..75a7f19 100644 --- a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -1,5 +1,3 @@ -[file name]: GTaskManager.java -[file content begin] /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * @@ -49,96 +47,74 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; -/** - * Google Tasks 同步管理器 - * 负责本地笔记数据和Google Tasks之间的双向同步 - */ + public class GTaskManager { - private static final String TAG = GTaskManager.class.getSimpleName(); + 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; // 用于获取认证令牌的Activity - private Context mContext; // 应用上下文 - private ContentResolver mContentResolver; // 内容解析器 - - // 同步状态标志 - private boolean mSyncing; // 是否正在同步 - private boolean mCancelled; // 是否已取消 - - // 数据存储结构 - private HashMap mGTaskListHashMap; // 任务列表映射(gid -> TaskList) - private HashMap mGTaskHashMap; // 任务节点映射(gid -> Node) - private HashMap mMetaHashMap; // 元数据映射(相关gid -> MetaData) - private TaskList mMetaList; // 元数据任务列表 - - // ID映射管理 - private HashSet mLocalDeleteIdMap; // 本地删除的笔记ID集合 - private HashMap mGidToNid; // Google ID到本地ID的映射 - private HashMap mNidToGid; // 本地ID到Google ID的映射 - - /** - * 私有构造函数 - 单例模式 - */ + 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; // 是否已取消同步的标志 + + // 数据映射和缓存 + private HashMap mGTaskListHashMap; // GID到任务列表的映射 + private HashMap mGTaskHashMap; // GID到节点的映射 + private HashMap mMetaHashMap; // GID到元数据的映射 + private TaskList mMetaList; // 元数据任务列表 + private HashSet mLocalDeleteIdMap; // 本地待删除的笔记ID集合 + private HashMap mGidToNid; // GID到本地笔记ID的映射 + private HashMap mNidToGid; // 本地笔记ID到GID的映射 + 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(); + // 初始化所有成员变量 + mSyncing = false; // 初始状态为非同步中 + mCancelled = false; // 初始状态为未取消 + mGTaskListHashMap = new HashMap(); // 初始化任务列表映射 + mGTaskHashMap = new HashMap(); // 初始化节点映射 + mMetaHashMap = new HashMap(); // 初始化元数据映射 + mMetaList = null; // 初始化元数据列表为空 + mLocalDeleteIdMap = new HashSet(); // 初始化本地删除ID集合 + mGidToNid = new HashMap(); // 初始化GID到NID映射 + mNidToGid = new HashMap(); // 初始化NID到GID映射 } - /** - * 获取单例实例 - */ + // 获取单例实例 public static synchronized GTaskManager getInstance() { if (mInstance == null) { - mInstance = new GTaskManager(); + mInstance = new GTaskManager(); // 第一次调用时创建实例 } return mInstance; } - /** - * 设置Activity上下文(用于Google账户认证) - * @param activity 当前Activity - */ + // 设置活动上下文 public synchronized void setActivityContext(Activity activity) { - mActivity = activity; + mActivity = activity; // 保存活动上下文,用于获取授权令牌 } - /** - * 主同步方法 - * @param context 上下文 - * @param asyncTask 异步任务,用于进度更新 - * @return 同步状态代码 - */ + // 同步方法,返回同步状态 public int sync(Context context, GTaskASyncTask asyncTask) { - // 检查是否已在同步中 - if (mSyncing) { - Log.d(TAG, "Sync is in progress"); - return STATE_SYNC_IN_PROGRESS; + if (mSyncing) { // 如果已经在同步中 + Log.d(TAG, "Sync is in progress"); // 记录日志 + return STATE_SYNC_IN_PROGRESS; // 返回同步进行中状态 } // 初始化同步环境 - mContext = context; - mContentResolver = mContext.getContentResolver(); - mSyncing = true; - mCancelled = false; + mContext = context; // 保存应用上下文 + mContentResolver = mContext.getContentResolver(); // 获取内容解析器 + mSyncing = true; // 设置同步标志 + mCancelled = false; // 重置取消标志 - // 清空所有数据存储 + // 清空所有数据缓存 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -147,84 +123,79 @@ public class GTaskManager { mNidToGid.clear(); try { - GTaskClient client = GTaskClient.getInstance(); - client.resetUpdateArray(); + GTaskClient client = GTaskClient.getInstance(); // 获取GTask客户端实例 + client.resetUpdateArray(); // 重置更新数组 - // 步骤1: 登录Google Tasks - if (!mCancelled) { - if (!client.login(mActivity)) { - throw new NetworkFailureException("login google task failed"); + // 登录Google Tasks + if (!mCancelled) { // 如果未取消 + if (!client.login(mActivity)) { // 尝试登录 + throw new NetworkFailureException("login google task failed"); // 登录失败抛出异常 } } - // 步骤2: 从Google获取任务列表 - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); - initGTaskList(); + // 从Google Tasks获取任务列表 + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); // 发布进度 + initGTaskList(); // 初始化任务列表 - // 步骤3: 执行内容同步 - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); - syncContent(); + // 执行内容同步 + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); // 发布进度 + syncContent(); // 同步内容 } catch (NetworkFailureException e) { - Log.e(TAG, e.toString()); - return STATE_NETWORK_ERROR; + Log.e(TAG, e.toString()); // 记录网络异常日志 + return STATE_NETWORK_ERROR; // 返回网络错误状态 } catch (ActionFailureException e) { - Log.e(TAG, e.toString()); - return STATE_INTERNAL_ERROR; + Log.e(TAG, e.toString()); // 记录操作异常日志 + return STATE_INTERNAL_ERROR; // 返回内部错误状态 } catch (Exception e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // 记录其他异常日志 e.printStackTrace(); - return STATE_INTERNAL_ERROR; + return STATE_INTERNAL_ERROR; // 返回内部错误状态 } finally { - // 清理资源 + // 无论成功或失败,都清理数据缓存 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); mLocalDeleteIdMap.clear(); mGidToNid.clear(); mNidToGid.clear(); - mSyncing = false; + mSyncing = false; // 重置同步标志 } - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; // 根据取消标志返回相应状态 } - /** - * 初始化Google任务列表 - * 从Google Tasks API获取所有任务列表和任务 - */ + // 初始化Google Tasks列表 private void initGTaskList() throws NetworkFailureException { - if (mCancelled) + if (mCancelled) // 如果已取消,直接返回 return; - GTaskClient client = GTaskClient.getInstance(); + GTaskClient client = GTaskClient.getInstance(); // 获取客户端实例 try { - // 获取所有任务列表 - JSONArray jsTaskLists = client.getTaskLists(); + JSONArray jsTaskLists = client.getTaskLists(); // 获取任务列表JSON数组 - // 1. 先初始化元数据列表(用于存储笔记的详细内容) - mMetaList = null; + // 首先初始化元数据列表 + 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); + JSONObject object = jsTaskLists.getJSONObject(i); // 获取单个任务列表对象 + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取GID + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); // 获取名称 - // 查找元数据文件夹(特殊文件夹,存储笔记的元数据) + // 检查是否是元数据文件夹 if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { - mMetaList = new TaskList(); - mMetaList.setContentByRemoteJSON(object); + mMetaList = new TaskList(); // 创建元数据任务列表 + mMetaList.setContentByRemoteJSON(object); // 从远程JSON设置内容 - // 加载元数据任务 - JSONArray jsMetas = client.getTaskList(gid); + // 加载元数据 + JSONArray jsMetas = client.getTaskList(gid); // 获取元数据任务列表 for (int j = 0; j < jsMetas.length(); j++) { - object = (JSONObject) jsMetas.getJSONObject(j); - MetaData metaData = new MetaData(); - metaData.setContentByRemoteJSON(object); - if (metaData.isWorthSaving()) { - mMetaList.addChildTask(metaData); + object = (JSONObject) jsMetas.getJSONObject(j); // 获取单个元数据对象 + MetaData metaData = new MetaData(); // 创建元数据对象 + metaData.setContentByRemoteJSON(object); // 从远程JSON设置内容 + if (metaData.isWorthSaving()) { // 如果值得保存 + mMetaList.addChildTask(metaData); // 添加到元数据列表 if (metaData.getGid() != null) { - // 以相关GID为键存储元数据 - mMetaHashMap.put(metaData.getRelatedGid(), metaData); + mMetaHashMap.put(metaData.getRelatedGid(), metaData); // 添加到元数据映射 } } } @@ -233,659 +204,618 @@ public class GTaskManager { // 如果元数据列表不存在,则创建 if (mMetaList == null) { - mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); - GTaskClient.getInstance().createTaskList(mMetaList); + mMetaList = new TaskList(); // 创建新的元数据列表 + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); // 设置名称 + GTaskClient.getInstance().createTaskList(mMetaList); // 在服务器上创建 } - // 2. 初始化普通任务列表(MIUI相关的文件夹) + // 初始化任务列表 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); + JSONObject object = jsTaskLists.getJSONObject(i); // 获取单个任务列表对象 + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取GID + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); // 获取名称 - // 只处理MIUI相关的文件夹(排除元数据文件夹) + // 检查是否是MIUI文件夹(排除元数据文件夹) 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); + TaskList tasklist = new TaskList(); // 创建任务列表 + tasklist.setContentByRemoteJSON(object); // 从远程JSON设置内容 + mGTaskListHashMap.put(gid, tasklist); // 添加到任务列表映射 + mGTaskHashMap.put(gid, tasklist); // 添加到节点映射 - // 加载该列表下的所有任务 - JSONArray jsTasks = client.getTaskList(gid); + // 加载任务列表中的任务 + JSONArray jsTasks = client.getTaskList(gid); // 获取任务列表中的任务 for (int j = 0; j < jsTasks.length(); j++) { - object = (JSONObject) 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); + object = (JSONObject) jsTasks.getJSONObject(j); // 获取单个任务对象 + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取任务GID + Task task = new Task(); // 创建任务 + task.setContentByRemoteJSON(object); // 从远程JSON设置内容 + if (task.isWorthSaving()) { // 如果值得保存 + task.setMetaInfo(mMetaHashMap.get(gid)); // 设置元数据信息 + tasklist.addChildTask(task); // 添加到任务列表 + mGTaskHashMap.put(gid, task); // 添加到节点映射 } } } } } catch (JSONException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // 记录JSON异常日志 e.printStackTrace(); - throw new ActionFailureException("initGTaskList: handing JSONObject failed"); + throw new ActionFailureException("initGTaskList: handing JSONObject failed"); // 抛出操作失败异常 } } - /** - * 同步内容 - 核心同步逻辑 - * 比较本地和远程数据,执行相应的同步操作 - */ + // 同步内容 private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; + int syncType; // 同步类型 + Cursor c = null; // 数据库游标 + String gid; // GID + Node node; // 节点 - mLocalDeleteIdMap.clear(); + mLocalDeleteIdMap.clear(); // 清空本地删除ID集合 - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } - // 阶段1: 处理本地已删除的笔记(在回收站中) + // 处理本地已删除的笔记 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); + gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); // 执行删除远程同步 } - // 记录本地删除的ID,后续批量删除 - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 添加到本地删除集合 } } else { - Log.w(TAG, "failed to query trash folder"); + Log.w(TAG, "failed to query trash folder"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } - // 阶段2: 先同步文件夹(因为笔记需要引用文件夹ID) - syncFolder(); + // 首先同步文件夹 + syncFolder(); // 同步文件夹 - // 阶段3: 处理数据库中存在的笔记(不在回收站中) + // 处理数据库中存在的笔记 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); // 根据修改时间判断同步类型 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 添加到GID到NID映射 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 添加到NID到GID映射 + syncType = node.getSyncAction(c); // 获取同步类型 } else { - // 根据是否有gid判断是本地新增还是远程删除 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增(无gid) + // 本地添加(GID为空) syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程已删除 + // 远程删除(GID存在但节点不在远程) syncType = Node.SYNC_ACTION_DEL_LOCAL; } } - doContentSync(syncType, node, c); + doContentSync(syncType, node, c); // 执行内容同步 } } else { - Log.w(TAG, "failed to query existing note in database"); + Log.w(TAG, "failed to query existing note in database"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } - // 阶段4: 处理剩余的项目(远程新增,本地不存在) + // 遍历剩余的项目(远程存在但本地不存在) Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); node = entry.getValue(); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); // 执行添加本地同步 } - // 清理本地删除表 + // mCancelled可能被其他线程设置,所以需要逐个检查 + // 清除本地删除表中的记录 if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { - throw new ActionFailureException("failed to batch-delete local deleted notes"); + throw new ActionFailureException("failed to batch-delete local deleted notes"); // 批量删除失败 } } - // 刷新本地同步ID(使用远程的最后修改时间) + // 刷新本地同步ID if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); + GTaskClient.getInstance().commitUpdate(); // 提交更新 + refreshLocalSyncId(); // 刷新本地同步ID } } - /** - * 同步文件夹 - * 先同步文件夹,因为笔记需要引用文件夹ID - */ + // 同步文件夹 private void syncFolder() throws NetworkFailureException { - Cursor c = null; - String gid; - Node node; - int syncType; + Cursor c = null; // 数据库游标 + String gid; // GID + Node node; // 节点 + int syncType; // 同步类型 - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } - // 1. 根文件夹同步 + // 处理根文件夹 try { + // 查询根文件夹 c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); if (c != null) { - c.moveToNext(); - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - // 双方都存在 - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); - mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // 系统文件夹只更新远程名称(如果需要) - if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + c.moveToNext(); // 移动到第一条记录 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); // 添加到GID到NID映射 + mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); // 添加到NID到GID映射 + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); // 执行更新远程同步 } else { - // 仅本地存在 - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); // 执行添加远程同步 } } else { - Log.w(TAG, "failed to query root folder"); + Log.w(TAG, "failed to query root folder"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } - // 2. 通话记录文件夹同步 + // 处理通话记录文件夹 try { + // 查询通话记录文件夹 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); + new String[] { + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + }, null); if (c != null) { - if (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); - mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // 系统文件夹只更新远程名称(如果需要) - if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + if (c.moveToNext()) { // 移动到第一条记录 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); // 添加到GID到NID映射 + mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); // 添加到NID到GID映射 + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); // 执行更新远程同步 } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); // 执行添加远程同步 } } } else { - Log.w(TAG, "failed to query call note folder"); + Log.w(TAG, "failed to query call note folder"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } - // 3. 本地存在的普通文件夹同步 + // 处理本地存在的文件夹 try { + // 查询非回收站中的文件夹 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_FOLDER), 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); // 根据时间戳判断同步类型 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 添加到GID到NID映射 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 添加到NID到GID映射 + syncType = node.getSyncAction(c); // 获取同步类型 } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增(无gid) + // 本地添加(GID为空) syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程已删除 + // 远程删除(GID存在但节点不在远程) syncType = Node.SYNC_ACTION_DEL_LOCAL; } } - doContentSync(syncType, node, c); + doContentSync(syncType, node, c); // 执行内容同步 } } else { - Log.w(TAG, "failed to query existing folder"); + Log.w(TAG, "failed to query existing folder"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } - // 4. 远程新增的文件夹(本地不存在) + // 处理远程添加的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); - gid = entry.getKey(); - node = entry.getValue(); - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + gid = entry.getKey(); // 获取GID + node = entry.getValue(); // 获取节点 + if (mGTaskHashMap.containsKey(gid)) { // 如果节点在节点映射中 + mGTaskHashMap.remove(gid); // 从映射中移除 + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); // 执行添加本地同步 } } - // 提交所有更新到Google Tasks - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); + if (!mCancelled) // 如果未取消 + GTaskClient.getInstance().commitUpdate(); // 提交更新 } - /** - * 执行具体的同步操作 - * @param syncType 同步类型 - * @param node 远程节点 - * @param c 本地数据游标 - */ + // 执行内容同步 private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } MetaData meta; - // 根据同步类型执行相应操作 - switch (syncType) { + switch (syncType) { // 根据同步类型执行相应操作 case Node.SYNC_ACTION_ADD_LOCAL: - addLocalNode(node); // 远程新增,添加到本地 + addLocalNode(node); // 添加本地节点 break; case Node.SYNC_ACTION_ADD_REMOTE: - addRemoteNode(node, c); // 本地新增,添加到远程 + addRemoteNode(node, c); // 添加远程节点 break; case Node.SYNC_ACTION_DEL_LOCAL: - // 远程已删除,从本地删除 - meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); + meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); // 根据GID获取元数据 if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 + GTaskClient.getInstance().deleteNode(meta); // 删除远程元数据 } - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 记录删除的本地ID + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 添加到本地删除集合 break; case Node.SYNC_ACTION_DEL_REMOTE: - // 本地已删除(在回收站),从远程删除 - meta = mMetaHashMap.get(node.getGid()); + meta = mMetaHashMap.get(node.getGid()); // 根据GID获取元数据 if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 + GTaskClient.getInstance().deleteNode(meta); // 删除远程元数据 } - GTaskClient.getInstance().deleteNode(node); // 删除主任务 + GTaskClient.getInstance().deleteNode(node); // 删除远程节点 break; case Node.SYNC_ACTION_UPDATE_LOCAL: - updateLocalNode(node, c); // 远程有更新,更新本地 + updateLocalNode(node, c); // 更新本地节点 break; case Node.SYNC_ACTION_UPDATE_REMOTE: - updateRemoteNode(node, c); // 本地有更新,更新远程 + updateRemoteNode(node, c); // 更新远程节点 break; case Node.SYNC_ACTION_UPDATE_CONFLICT: - // 冲突情况:目前简单使用本地更新覆盖远程 - // TODO: 可以考虑更复杂的冲突解决策略,如合并或让用户选择 - updateRemoteNode(node, c); + // 合并两个修改可能是一个好主意 + // 现在简单地使用本地更新 + updateRemoteNode(node, c); // 更新远程节点 break; case Node.SYNC_ACTION_NONE: - break; // 无变化,无需同步 + break; // 无操作 case Node.SYNC_ACTION_ERROR: default: - throw new ActionFailureException("unknown sync action type"); + throw new ActionFailureException("unkown sync action type"); // 抛出未知同步类型异常 } } - /** - * 添加本地节点(远程新增) - * @param node 远程节点 - */ + // 添加本地节点 private void addLocalNode(Node node) throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } SqlNote sqlNote; - // 根据节点类型处理 - if (node instanceof TaskList) { - // 处理任务列表(文件夹) - if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { - sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 根文件夹 - } else if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { - sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 通话记录文件夹 + if (node instanceof TaskList) { // 如果是任务列表 + if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + // 默认文件夹(根文件夹) + sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 创建根文件夹SQL笔记 + } else if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + // 通话记录文件夹 + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 创建通话记录文件夹SQL笔记 } else { - sqlNote = new SqlNote(mContext); - sqlNote.setContent(node.getLocalJSONFromContent()); - sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 普通文件夹的父级是根文件夹 + // 普通文件夹 + sqlNote = new SqlNote(mContext); // 创建新的SQL笔记 + sqlNote.setContent(node.getLocalJSONFromContent()); // 设置内容 + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 设置父ID为根文件夹 } - } else { - // 处理任务(笔记) - sqlNote = new SqlNote(mContext); - JSONObject js = node.getLocalJSONFromContent(); + } else { // 如果是任务 + sqlNote = new SqlNote(mContext); // 创建新的SQL笔记 + JSONObject js = node.getLocalJSONFromContent(); // 获取本地JSON try { - // 处理可能存在的ID冲突 + // 处理笔记ID冲突 if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); if (note.has(NoteColumns.ID)) { - long id = note.getLong(NoteColumns.ID); + long id = note.getLong(NoteColumns.ID); // 获取笔记ID if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // ID已存在,移除以便生成新ID - note.remove(NoteColumns.ID); + // ID不可用,需要创建新的ID + note.remove(NoteColumns.ID); // 移除ID } } } - // 处理数据项的ID冲突 + // 处理数据ID冲突 if (js.has(GTaskStringUtils.META_HEAD_DATA)) { JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (data.has(DataColumns.ID)) { - long dataId = data.getLong(DataColumns.ID); + long dataId = data.getLong(DataColumns.ID); // 获取数据ID if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // 数据ID已存在,移除以便生成新ID - data.remove(DataColumns.ID); + // 数据ID不可用,需要创建新的ID + data.remove(DataColumns.ID); // 移除ID } } } } } catch (JSONException e) { - Log.w(TAG, e.toString()); + Log.w(TAG, e.toString()); // 记录警告日志 e.printStackTrace(); } - sqlNote.setContent(js); + sqlNote.setContent(js); // 设置内容 - // 查找父文件夹的本地ID - Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); + // 获取父节点ID + Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); // 根据父节点GID获取本地ID if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot add local node"); + Log.e(TAG, "cannot find task's parent id locally"); // 记录错误日志 + throw new ActionFailureException("cannot add local node"); // 抛出添加本地节点失败异常 } - sqlNote.setParentId(parentId.longValue()); + sqlNote.setParentId(parentId.longValue()); // 设置父ID } // 创建本地节点 - sqlNote.setGtaskId(node.getGid()); // 设置Google任务ID + sqlNote.setGtaskId(node.getGid()); // 设置GTask ID sqlNote.commit(false); // 提交到数据库 - // 更新ID映射关系 - mGidToNid.put(node.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), node.getGid()); + // 更新GID-NID映射 + mGidToNid.put(node.getGid(), sqlNote.getId()); // 添加到GID到NID映射 + mNidToGid.put(sqlNote.getId(), node.getGid()); // 添加到NID到GID映射 - // 更新远程元数据 - updateRemoteMeta(node.getGid(), sqlNote); + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); // 更新远程元数据 } - /** - * 更新本地节点 - * @param node 远程节点 - * @param c 本地数据游标 - */ + // 更新本地节点 private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } SqlNote sqlNote; - // 更新本地笔记 - sqlNote = new SqlNote(mContext, c); - sqlNote.setContent(node.getLocalJSONFromContent()); + // 本地更新笔记 + sqlNote = new SqlNote(mContext, c); // 从游标创建SQL笔记 + sqlNote.setContent(node.getLocalJSONFromContent()); // 设置内容 - // 确定父文件夹ID - Long parentId = (node instanceof Task) ? - mGidToNid.get(((Task) node).getParent().getGid()) : - new Long(Notes.ID_ROOT_FOLDER); + // 获取父节点ID + Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) + : new Long(Notes.ID_ROOT_FOLDER); // 如果是任务则根据父节点GID获取本地ID,否则使用根文件夹ID if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot update local node"); + Log.e(TAG, "cannot find task's parent id locally"); // 记录错误日志 + throw new ActionFailureException("cannot update local node"); // 抛出更新本地节点失败异常 } - sqlNote.setParentId(parentId.longValue()); - sqlNote.commit(true); // 提交更新 + sqlNote.setParentId(parentId.longValue()); // 设置父ID + sqlNote.commit(true); // 提交到数据库 // 更新元数据信息 - updateRemoteMeta(node.getGid(), sqlNote); + updateRemoteMeta(node.getGid(), sqlNote); // 更新远程元数据 } - /** - * 添加远程节点(本地新增) - * @param node 远程节点(可能为null) - * @param c 本地数据游标 - */ + // 添加远程节点 private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; + SqlNote sqlNote = new SqlNote(mContext, c); // 从游标创建SQL笔记 + Node n; // 远程节点 // 远程更新 - if (sqlNote.isNoteType()) { - // 处理笔记 - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); + if (sqlNote.isNoteType()) { // 如果是笔记类型 + Task task = new Task(); // 创建任务 + task.setContentByLocalJSON(sqlNote.getContent()); // 从本地JSON设置内容 - // 查找父任务列表的Google ID - String parentGid = mNidToGid.get(sqlNote.getParentId()); + // 获取父节点GID + String parentGid = mNidToGid.get(sqlNote.getParentId()); // 根据父节点本地ID获取GID if (parentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot add remote task"); + Log.e(TAG, "cannot find task's parent tasklist"); // 记录错误日志 + throw new ActionFailureException("cannot add remote task"); // 抛出添加远程任务失败异常 } - mGTaskListHashMap.get(parentGid).addChildTask(task); + mGTaskListHashMap.get(parentGid).addChildTask(task); // 添加到父任务列表 - GTaskClient.getInstance().createTask(task); // 创建远程任务 - n = (Node) task; + GTaskClient.getInstance().createTask(task); // 在服务器上创建任务 + n = (Node) task; // 转换为节点 // 添加元数据 - updateRemoteMeta(task.getGid(), sqlNote); - } else { - // 处理文件夹 + updateRemoteMeta(task.getGid(), sqlNote); // 更新远程元数据 + } else { // 如果是文件夹类型 TaskList tasklist = null; - // 构建文件夹名称 - String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; + // 如果文件夹已存在,需要跳过 + String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; // 文件夹名称前缀 if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) - folderName += GTaskStringUtils.FOLDER_DEFAULT; + folderName += GTaskStringUtils.FOLDER_DEFAULT; // 默认文件夹 else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) - folderName += GTaskStringUtils.FOLDER_CALL_NOTE; + folderName += GTaskStringUtils.FOLDER_CALL_NOTE; // 通话记录文件夹 else - folderName += sqlNote.getSnippet(); + folderName += sqlNote.getSnippet(); // 普通文件夹 - // 检查是否已存在同名文件夹(防止重复创建) + // 在任务列表中查找匹配的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); - String gid = entry.getKey(); - TaskList list = entry.getValue(); + String gid = entry.getKey(); // 获取GID + TaskList list = entry.getValue(); // 获取任务列表 - if (list.getName().equals(folderName)) { - tasklist = list; // 已存在,复用 + if (list.getName().equals(folderName)) { // 如果名称匹配 + tasklist = list; // 设置任务列表 if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); + mGTaskHashMap.remove(gid); // 从节点映射中移除 } break; } } - // 不存在则创建新文件夹 + // 没有匹配的文件夹,现在可以添加 if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); // 创建远程任务列表 - mGTaskListHashMap.put(tasklist.getGid(), tasklist); + tasklist = new TaskList(); // 创建新的任务列表 + tasklist.setContentByLocalJSON(sqlNote.getContent()); // 从本地JSON设置内容 + GTaskClient.getInstance().createTaskList(tasklist); // 在服务器上创建任务列表 + mGTaskListHashMap.put(tasklist.getGid(), tasklist); // 添加到任务列表映射 } - n = (Node) tasklist; + n = (Node) tasklist; // 转换为节点 } // 更新本地笔记 - sqlNote.setGtaskId(n.getGid()); // 设置Google ID - sqlNote.commit(false); + sqlNote.setGtaskId(n.getGid()); // 设置GTask ID + sqlNote.commit(false); // 提交到数据库 sqlNote.resetLocalModified(); // 重置本地修改标志 - sqlNote.commit(true); + sqlNote.commit(true); // 再次提交到数据库 - // 更新ID映射 - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); + // GID-ID映射 + mGidToNid.put(n.getGid(), sqlNote.getId()); // 添加到GID到NID映射 + mNidToGid.put(sqlNote.getId(), n.getGid()); // 添加到NID到GID映射 } - /** - * 更新远程节点 - * @param node 远程节点 - * @param c 本地数据游标 - */ + // 更新远程节点 private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } - SqlNote sqlNote = new SqlNote(mContext, c); + SqlNote sqlNote = new SqlNote(mContext, c); // 从游标创建SQL笔记 // 远程更新 - node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); // 添加到更新队列 + node.setContentByLocalJSON(sqlNote.getContent()); // 从本地JSON设置内容 + GTaskClient.getInstance().addUpdateNode(node); // 添加到更新数组 // 更新元数据 - updateRemoteMeta(node.getGid(), sqlNote); + updateRemoteMeta(node.getGid(), sqlNote); // 更新远程元数据 - // 检查是否需要移动任务到其他列表 - if (sqlNote.isNoteType()) { - Task task = (Task) node; - TaskList preParentList = task.getParent(); // 原父列表 + // 如果需要,移动任务 + if (sqlNote.isNoteType()) { // 如果是笔记类型 + Task task = (Task) node; // 转换为任务 + TaskList preParentList = task.getParent(); // 获取之前的父任务列表 - String curParentGid = mNidToGid.get(sqlNote.getParentId()); + // 获取当前的父任务列表GID + String curParentGid = mNidToGid.get(sqlNote.getParentId()); // 根据父节点本地ID获取GID if (curParentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot update remote task"); + Log.e(TAG, "cannot find task's parent tasklist"); // 记录错误日志 + throw new ActionFailureException("cannot update remote task"); // 抛出更新远程任务失败异常 } - TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 新父列表 + TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 获取当前父任务列表 - // 如果父列表发生变化,则移动任务 + // 如果父任务列表发生变化 if (preParentList != curParentList) { - preParentList.removeChildTask(task); - curParentList.addChildTask(task); - GTaskClient.getInstance().moveTask(task, preParentList, curParentList); + preParentList.removeChildTask(task); // 从之前的父任务列表移除 + curParentList.addChildTask(task); // 添加到当前的父任务列表 + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // 在服务器上移动任务 } } // 清除本地修改标志 - sqlNote.resetLocalModified(); - sqlNote.commit(true); + sqlNote.resetLocalModified(); // 重置本地修改标志 + sqlNote.commit(true); // 提交到数据库 } - /** - * 更新远程元数据 - * @param gid Google任务ID - * @param sqlNote 本地笔记对象 - */ + // 更新远程元数据 private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { - if (sqlNote != null && sqlNote.isNoteType()) { - MetaData metaData = mMetaHashMap.get(gid); - if (metaData != null) { - // 更新现有元数据 - metaData.setMeta(gid, sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(metaData); - } else { - // 创建新元数据 - metaData = new MetaData(); - metaData.setMeta(gid, sqlNote.getContent()); - mMetaList.addChildTask(metaData); - mMetaHashMap.put(gid, metaData); - GTaskClient.getInstance().createTask(metaData); + if (sqlNote != null && sqlNote.isNoteType()) { // 如果是笔记类型 + MetaData metaData = mMetaHashMap.get(gid); // 根据GID获取元数据 + if (metaData != null) { // 如果元数据存在 + metaData.setMeta(gid, sqlNote.getContent()); // 设置元数据 + GTaskClient.getInstance().addUpdateNode(metaData); // 添加到更新数组 + } else { // 如果元数据不存在 + metaData = new MetaData(); // 创建新的元数据 + metaData.setMeta(gid, sqlNote.getContent()); // 设置元数据 + mMetaList.addChildTask(metaData); // 添加到元数据列表 + mMetaHashMap.put(gid, metaData); // 添加到元数据映射 + GTaskClient.getInstance().createTask(metaData); // 在服务器上创建元数据任务 } } } - /** - * 刷新本地同步ID(使用远程最后修改时间) - * 用于下次同步时判断哪些项目需要更新 - */ + // 刷新本地同步ID private void refreshLocalSyncId() throws NetworkFailureException { - if (mCancelled) { + if (mCancelled) { // 如果已取消,直接返回 return; } - // 重新获取最新的Google任务列表 - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - initGTaskList(); + // 获取最新的GTask列表 + mGTaskHashMap.clear(); // 清空节点映射 + mGTaskListHashMap.clear(); // 清空任务列表映射 + mMetaHashMap.clear(); // 清空元数据映射 + initGTaskList(); // 重新初始化任务列表 Cursor c = null; 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) }, NoteColumns.TYPE + " DESC"); if (c != null) { while (c.moveToNext()) { - String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - Node node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - // 使用远程的最后修改时间作为本地同步ID - ContentValues values = new ContentValues(); - values.put(NoteColumns.SYNC_ID, node.getLastModified()); + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取GID + Node node = mGTaskHashMap.get(gid); // 根据GID获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除 + ContentValues values = new ContentValues(); // 创建内容值 + values.put(NoteColumns.SYNC_ID, node.getLastModified()); // 设置同步ID为最后修改时间 + // 更新数据库中的同步ID mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(SqlNote.ID_COLUMN)), values, null, null); } else { - Log.e(TAG, "something is missed"); - throw new ActionFailureException("some local items don't have gid after sync"); + Log.e(TAG, "something is missed"); // 记录错误日志 + throw new ActionFailureException( + "some local items don't have gid after sync"); // 抛出同步后缺少GID异常 } } } else { - Log.w(TAG, "failed to query local note to refresh sync id"); + Log.w(TAG, "failed to query local note to refresh sync id"); // 记录警告日志 } } finally { if (c != null) { - c.close(); - c = null; + c.close(); // 关闭游标 + c = null; // 清空游标引用 } } } - /** - * 获取同步账户信息 - * @return 同步账户名称 - */ + // 获取同步账户名称 public String getSyncAccount() { - return GTaskClient.getInstance().getSyncAccount().name; + return GTaskClient.getInstance().getSyncAccount().name; // 从GTask客户端获取同步账户名称 } - /** - * 取消同步 - */ + // 取消同步 public void cancelSync() { - mCancelled = true; + mCancelled = true; // 设置取消标志 } -} -[file content end] \ No newline at end of file +} \ No newline at end of file diff --git a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskSyncService.java index 9a11e32..ad238ec 100644 --- a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -1,5 +1,3 @@ -[file name]: GTaskSyncService.java -[file content begin] /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * @@ -25,949 +23,113 @@ import android.content.Intent; import android.os.Bundle; import android.os.IBinder; -// Google任务同步服务类 public class GTaskSyncService extends Service { - // 动作类型键名 - public final static String ACTION_STRING_NAME = "sync_action_type"; + // 同步操作类型常量 + public final static String ACTION_STRING_NAME = "sync_action_type"; // Intent中操作类型的键名 + + // 同步操作类型值 + public final static int ACTION_START_SYNC = 0; // 开始同步 + public final static int ACTION_CANCEL_SYNC = 1; // 取消同步 + public final static int ACTION_INVALID = 2; // 无效操作 - // 同步动作常量 - public final static int ACTION_START_SYNC = 0; // 开始同步 - public final static int ACTION_CANCEL_SYNC = 1; // 取消同步 - public final static int ACTION_INVALID = 2; // 无效动作 - // 广播相关常量 - public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - - // 静态同步任务实例 - private static GTaskASyncTask mSyncTask = null; - // 同步进度信息 - private static String mSyncProgress = ""; + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; // 广播名称 + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; // 是否正在同步的广播键 + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; // 进度消息的广播键 + + private static GTaskASyncTask mSyncTask = null; // 同步任务实例 + private static String mSyncProgress = ""; // 同步进度消息 // 开始同步方法 private void startSync() { - // 检查是否已有同步任务在运行 - if (mSyncTask == null) { - // 创建新的同步任务 + if (mSyncTask == null) { // 如果没有正在进行的同步任务 + // 创建新的同步任务,并设置完成监听器 mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { public void onComplete() { - // 同步完成后清理资源 - mSyncTask = null; - sendBroadcast(""); + mSyncTask = null; // 同步完成,清空任务引用 + sendBroadcast(""); // 发送空广播通知同步完成 stopSelf(); // 停止服务 } }); - sendBroadcast(""); + sendBroadcast(""); // 发送广播通知开始同步 mSyncTask.execute(); // 执行同步任务 } } // 取消同步方法 private void cancelSync() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); + if (mSyncTask != null) { // 如果有正在进行的同步任务 + mSyncTask.cancelSync(); // 取消同步 } } @Override public void onCreate() { - // 服务创建时初始化 - mSyncTask = null; + mSyncTask = null; // 服务创建时初始化同步任务为空 } @Override public int onStartCommand(Intent intent, int flags, int startId) { - Bundle bundle = intent.getExtras(); - // 根据Intent中的动作类型执行相应操作 - if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { - switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { + Bundle bundle = intent.getExtras(); // 获取Intent中的附加数据 + if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { // 如果包含操作类型 + switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { // 根据操作类型执行相应操作 case ACTION_START_SYNC: - startSync(); // 开始同步 + startSync(); // 开始同步 break; case ACTION_CANCEL_SYNC: - cancelSync(); // 取消同步 + cancelSync(); // 取消同步 break; default: - break; + break; // 无效操作,不做任何处理 } - return START_STICKY; // 服务被杀死后自动重启 + return START_STICKY; // 返回粘性服务标志,系统会在服务被杀死后尝试重新创建 } - return super.onStartCommand(intent, flags, startId); + return super.onStartCommand(intent, flags, startId); // 默认处理 } @Override public void onLowMemory() { - // 低内存时取消同步以释放资源 - if (mSyncTask != null) { - mSyncTask.cancelSync(); + if (mSyncTask != null) { // 在低内存情况下 + mSyncTask.cancelSync(); // 取消同步任务以释放资源 } } + // 服务绑定方法(未实现) public IBinder onBind(Intent intent) { - // 绑定服务(此服务不支持绑定) - return null; + return null; // 不提供绑定服务功能 } - // 发送广播更新同步状态 + // 发送广播方法 public void sendBroadcast(String msg) { - mSyncProgress = msg; - Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); - // 添加同步状态信息 - intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); - intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); - sendBroadcast(intent); + mSyncProgress = msg; // 更新同步进度消息 + Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); // 创建广播Intent + intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); // 添加是否正在同步的附加信息 + intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); // 添加进度消息 + sendBroadcast(intent); // 发送广播 } // 静态方法:从Activity启动同步 public static void startSync(Activity activity) { - GTaskManager.getInstance().setActivityContext(activity); // 设置Activity上下文 - Intent intent = new Intent(activity, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); + GTaskManager.getInstance().setActivityContext(activity); // 设置活动上下文 + Intent intent = new Intent(activity, GTaskSyncService.class); // 创建启动服务的Intent + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); // 设置操作为开始同步 activity.startService(intent); // 启动服务 } // 静态方法:取消同步 public static void cancelSync(Context context) { - Intent intent = new Intent(context, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); - context.startService(intent); + Intent intent = new Intent(context, GTaskSyncService.class); // 创建启动服务的Intent + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); // 设置操作为取消同步 + context.startService(intent); // 启动服务 } - // 检查是否正在同步 + // 静态方法:检查是否正在同步 public static boolean isSyncing() { - return mSyncTask != null; + return mSyncTask != null; // 根据同步任务是否存在判断是否正在同步 } - // 获取同步进度信息 + // 静态方法:获取当前同步进度消息 public static String getProgressString() { - return mSyncProgress; - } -} -[file content end] - -[file name]: GTaskManager.java -[file content begin] -/* - * 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; - -// Google任务管理器:负责本地和远程数据的同步 -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; // 用于获取认证令牌的Activity - private Context mContext; // 应用上下文 - private ContentResolver mContentResolver; // 内容解析器 - - // 同步状态标志 - private boolean mSyncing; // 是否正在同步 - private boolean mCancelled; // 是否已取消 - - // 数据存储结构 - private HashMap mGTaskListHashMap; // 任务列表映射(gid -> TaskList) - private HashMap mGTaskHashMap; // 任务节点映射(gid -> Node) - private HashMap mMetaHashMap; // 元数据映射(相关gid -> MetaData) - private TaskList mMetaList; // 元数据任务列表 - - // 本地和远程ID映射 - private HashSet mLocalDeleteIdMap; // 本地删除的笔记ID集合 - private HashMap mGidToNid; // Google ID到本地ID的映射 - private HashMap mNidToGid; // 本地ID到Google ID的映射 - - // 私有构造函数 - 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(); - } - - // 获取单例实例 - public static synchronized GTaskManager getInstance() { - if (mInstance == null) { - mInstance = new GTaskManager(); - } - return mInstance; - } - - // 设置Activity上下文(用于Google账户认证) - public synchronized void setActivityContext(Activity activity) { - mActivity = activity; - } - - // 主同步方法 - public 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; - - // 清空所有数据存储 - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - - try { - GTaskClient client = GTaskClient.getInstance(); - client.resetUpdateArray(); - - // 步骤1: 登录Google Tasks - if (!mCancelled) { - if (!client.login(mActivity)) { - throw new NetworkFailureException("login google task failed"); - } - } - - // 步骤2: 从Google获取任务列表 - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); - initGTaskList(); - - // 步骤3: 执行内容同步 - 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 { - // 清理资源 - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - mSyncing = false; - } - - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; - } - - // 初始化Google任务列表 - 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 = (JSONObject) 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); - - // 只处理MIUI相关的文件夹(排除元数据文件夹) - 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 = (JSONObject) 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"); - } - } - - // 同步内容(核心同步逻辑) - private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; - - mLocalDeleteIdMap.clear(); - - if (mCancelled) { - return; - } - - // 阶段1: 处理本地已删除的笔记(在回收站中) - 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)); // 记录本地删除ID - } - } else { - Log.w(TAG, "failed to query trash folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 阶段2: 同步文件夹 - syncFolder(); - - // 阶段3: 处理数据库中存在的笔记(不在回收站中) - 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) { - // 本地新增(无gid) - 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; - } - } - - // 阶段4: 处理剩余的项目(远程新增) - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - node = entry.getValue(); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - - // 清理本地删除表 - if (!mCancelled) { - if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { - throw new ActionFailureException("failed to batch-delete local deleted notes"); - } - } - - // 刷新本地同步ID - if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); - } - } - - // 同步文件夹 - private void syncFolder() throws NetworkFailureException { - Cursor c = null; - String gid; - Node node; - int syncType; - - if (mCancelled) { - return; - } - - // 根文件夹同步 - try { - c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); - if (c != null) { - c.moveToNext(); - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - // 双方都存在 - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); - mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // 系统文件夹只更新远程名称(如果需要) - if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - // 仅本地存在 - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } else { - Log.w(TAG, "failed to query root folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 通话记录文件夹同步 - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); - if (c != null) { - if (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); - mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // 系统文件夹只更新远程名称(如果需要) - if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } - } else { - Log.w(TAG, "failed to query call note folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 本地存在的普通文件夹同步 - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type=? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_FOLDER), 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) { - // 本地新增(无gid) - 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 folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 远程新增的文件夹 - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - gid = entry.getKey(); - node = entry.getValue(); - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - } - - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); - } - - // 执行具体的同步操作 - private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - MetaData meta; - // 根据同步类型执行相应操作 - switch (syncType) { - case Node.SYNC_ACTION_ADD_LOCAL: - addLocalNode(node); // 远程新增,添加到本地 - break; - case Node.SYNC_ACTION_ADD_REMOTE: - addRemoteNode(node, c); // 本地新增,添加到远程 - break; - case Node.SYNC_ACTION_DEL_LOCAL: - // 远程已删除,从本地删除 - meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); - if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 - } - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 记录删除的本地ID - break; - case Node.SYNC_ACTION_DEL_REMOTE: - // 本地已删除(在回收站),从远程删除 - meta = mMetaHashMap.get(node.getGid()); - if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 - } - GTaskClient.getInstance().deleteNode(node); // 删除主任务 - break; - case Node.SYNC_ACTION_UPDATE_LOCAL: - updateLocalNode(node, c); // 远程有更新,更新本地 - break; - case Node.SYNC_ACTION_UPDATE_REMOTE: - updateRemoteNode(node, c); // 本地有更新,更新远程 - break; - case Node.SYNC_ACTION_UPDATE_CONFLICT: - // 冲突情况:目前简单使用本地更新覆盖远程 - updateRemoteNode(node, c); - break; - case Node.SYNC_ACTION_NONE: - break; // 无变化,无需同步 - case Node.SYNC_ACTION_ERROR: - default: - throw new ActionFailureException("unknown sync action type"); - } - } - - // 添加本地节点(远程新增) - private void addLocalNode(Node node) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote; - // 根据节点类型处理 - if (node instanceof TaskList) { - // 处理任务列表(文件夹) - if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { - sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 根文件夹 - } else if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { - sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 通话记录文件夹 - } else { - sqlNote = new SqlNote(mContext); - sqlNote.setContent(node.getLocalJSONFromContent()); - sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 普通文件夹的父级是根文件夹 - } - } else { - // 处理任务(笔记) - sqlNote = new SqlNote(mContext); - JSONObject js = node.getLocalJSONFromContent(); - try { - // 处理可能存在的ID冲突 - if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - if (note.has(NoteColumns.ID)) { - long id = note.getLong(NoteColumns.ID); - if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // ID已存在,移除以便生成新ID - note.remove(NoteColumns.ID); - } - } - } - - // 处理数据项的ID冲突 - if (js.has(GTaskStringUtils.META_HEAD_DATA)) { - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - if (data.has(DataColumns.ID)) { - long dataId = data.getLong(DataColumns.ID); - if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // 数据ID已存在,移除以便生成新ID - data.remove(DataColumns.ID); - } - } - } - } - } catch (JSONException e) { - Log.w(TAG, e.toString()); - e.printStackTrace(); - } - sqlNote.setContent(js); - - // 查找父文件夹的本地ID - Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot add local node"); - } - sqlNote.setParentId(parentId.longValue()); - } - - // 创建本地节点 - sqlNote.setGtaskId(node.getGid()); // 设置Google任务ID - sqlNote.commit(false); // 提交到数据库 - - // 更新ID映射关系 - mGidToNid.put(node.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), node.getGid()); - - // 更新远程元数据 - updateRemoteMeta(node.getGid(), sqlNote); - } - - // 更新本地节点 - private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote; - // 更新本地笔记 - sqlNote = new SqlNote(mContext, c); - sqlNote.setContent(node.getLocalJSONFromContent()); - - // 确定父文件夹ID - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) : new Long(Notes.ID_ROOT_FOLDER); - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot update local node"); - } - sqlNote.setParentId(parentId.longValue()); - sqlNote.commit(true); // 提交更新 - - // 更新元数据信息 - updateRemoteMeta(node.getGid(), sqlNote); - } - - // 添加远程节点(本地新增) - private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; - - // 远程更新 - if (sqlNote.isNoteType()) { - // 处理笔记 - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); - - // 查找父任务列表的Google ID - String parentGid = mNidToGid.get(sqlNote.getParentId()); - if (parentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot add remote task"); - } - mGTaskListHashMap.get(parentGid).addChildTask(task); - - GTaskClient.getInstance().createTask(task); // 创建远程任务 - n = (Node) task; - - // 添加元数据 - updateRemoteMeta(task.getGid(), sqlNote); - } else { - // 处理文件夹 - TaskList tasklist = null; - - // 构建文件夹名称 - String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; - if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) - folderName += GTaskStringUtils.FOLDER_DEFAULT; - else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) - folderName += GTaskStringUtils.FOLDER_CALL_NOTE; - else - folderName += sqlNote.getSnippet(); - - // 检查是否已存在同名文件夹 - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - String gid = entry.getKey(); - TaskList list = entry.getValue(); - - if (list.getName().equals(folderName)) { - tasklist = list; // 已存在,复用 - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - } - break; - } - } - - // 不存在则创建新文件夹 - if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); // 创建远程任务列表 - mGTaskListHashMap.put(tasklist.getGid(), tasklist); - } - n = (Node) tasklist; - } - - // 更新本地笔记 - sqlNote.setGtaskId(n.getGid()); // 设置Google ID - sqlNote.commit(false); - sqlNote.resetLocalModified(); // 重置本地修改标志 - sqlNote.commit(true); - - // 更新ID映射 - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); - } - - // 更新远程节点 - private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - - // 远程更新 - node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); // 添加到更新队列 - - // 更新元数据 - updateRemoteMeta(node.getGid(), sqlNote); - - // 检查是否需要移动任务到其他列表 - if (sqlNote.isNoteType()) { - Task task = (Task) node; - TaskList preParentList = task.getParent(); // 原父列表 - - String curParentGid = mNidToGid.get(sqlNote.getParentId()); - if (curParentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot update remote task"); - } - TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 新父列表 - - // 如果父列表发生变化,则移动任务 - if (preParentList != curParentList) { - preParentList.removeChildTask(task); - curParentList.addChildTask(task); - GTaskClient.getInstance().moveTask(task, preParentList, curParentList); - } - } - - // 清除本地修改标志 - sqlNote.resetLocalModified(); - sqlNote.commit(true); - } - - // 更新远程元数据 - private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { - if (sqlNote != null && sqlNote.isNoteType()) { - MetaData metaData = mMetaHashMap.get(gid); - if (metaData != null) { - // 更新现有元数据 - metaData.setMeta(gid, sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(metaData); - } else { - // 创建新元数据 - metaData = new MetaData(); - metaData.setMeta(gid, sqlNote.getContent()); - mMetaList.addChildTask(metaData); - mMetaHashMap.put(gid, metaData); - GTaskClient.getInstance().createTask(metaData); - } - } - } - - // 刷新本地同步ID(使用远程最后修改时间) - private void refreshLocalSyncId() throws NetworkFailureException { - if (mCancelled) { - return; - } - - // 重新获取最新的Google任务列表 - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - initGTaskList(); - - Cursor c = null; - 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) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - Node node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - // 使用远程的最后修改时间作为本地同步ID - ContentValues values = new ContentValues(); - values.put(NoteColumns.SYNC_ID, node.getLastModified()); - mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - c.getLong(SqlNote.ID_COLUMN)), values, null, null); - } else { - Log.e(TAG, "something is missed"); - throw new ActionFailureException("some local items don't have gid after sync"); - } - } - } else { - Log.w(TAG, "failed to query local note to refresh sync id"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - } - - // 获取同步账户信息 - public String getSyncAccount() { - return GTaskClient.getInstance().getSyncAccount().name; - } - - // 取消同步 - public void cancelSync() { - mCancelled = true; + return mSyncProgress; // 返回同步进度消息 } -} -[file content end] \ No newline at end of file +} \ No newline at end of file