From 5c2c3cf9103e796718f33fb31f9f4becda9354cb Mon Sep 17 00:00:00 2001 From: zhangzhuwei Date: Wed, 8 Jan 2025 21:17:20 +0800 Subject: [PATCH] remote --- .../notes/gtask/remote/GTaskASyncTask.java | 238 ++-- .../notes/gtask/remote/GTaskClient.java | 746 +++++++----- .../notes/gtask/remote/GTaskManager.java | 1065 +++++++++-------- .../notes/gtask/remote/GTaskSyncService.java | 316 +++-- 4 files changed, 1324 insertions(+), 1041 deletions(-) diff --git a/src/java/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/java/net/micode/notes/gtask/remote/GTaskASyncTask.java index b3b61e7..a5a8fc9 100644 --- a/src/java/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/java/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -1,4 +1,3 @@ - /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * @@ -6,7 +5,7 @@ * 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 + * 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, @@ -15,109 +14,132 @@ * limitations under the License. */ -package net.micode.notes.gtask.remote; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesListActivity; -import net.micode.notes.ui.NotesPreferenceActivity; - - -public class GTaskASyncTask extends AsyncTask { - - private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - - public interface OnCompleteListener { - void onComplete(); - } - - private Context mContext; - - private NotificationManager mNotifiManager; - - private GTaskManager mTaskManager; - - private OnCompleteListener mOnCompleteListener; - - public GTaskASyncTask(Context context, OnCompleteListener listener) { - mContext = context; - mOnCompleteListener = listener; - mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - mTaskManager = GTaskManager.getInstance(); - } - - public void cancelSync() { - mTaskManager.cancelSync(); - } - - public void publishProgess(String message) { - publishProgress(new String[] { - message - }); - } - - 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; - 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); - } - - @Override - protected Integer doInBackground(Void... unused) { - publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); - return mTaskManager.sync(mContext, this); - } - - @Override - protected void onProgressUpdate(String... progress) { - showNotification(R.string.ticker_syncing, progress[0]); - if (mContext instanceof GTaskSyncService) { - ((GTaskSyncService) mContext).sendBroadcast(progress[0]); - } - } - - @Override - protected void onPostExecute(Integer result) { - if (result == GTaskManager.STATE_SUCCESS) { - showNotification(R.string.ticker_success, mContext.getString( - R.string.success_sync_account, mTaskManager.getSyncAccount())); - NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); - } else if (result == GTaskManager.STATE_NETWORK_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); - } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); - } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { - showNotification(R.string.ticker_cancel, mContext - .getString(R.string.error_sync_cancelled)); - } - if (mOnCompleteListener != null) { - new Thread(new Runnable() { - - public void run() { - mOnCompleteListener.onComplete(); - } - }).start(); - } - } -} + package net.micode.notes.gtask.remote; + + import android.app.Notification; + import android.app.NotificationManager; + import android.app.PendingIntent; + import android.content.Context; + import android.content.Intent; + import android.os.AsyncTask; + + import net.micode.notes.R; + import net.micode.notes.ui.NotesListActivity; + import net.micode.notes.ui.NotesPreferenceActivity; + + /** + * 异步任务类,用于同步Google任务。 + */ + public class GTaskASyncTask extends AsyncTask { + + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; // 同步通知的ID + + /** + * 完成监听器接口。 + */ + public interface OnCompleteListener { + void onComplete(); // 当同步完成时调用 + } + + private Context mContext; // 应用程序上下文 + private NotificationManager mNotifiManager; // 通知管理器 + private GTaskManager mTaskManager; // Google任务管理器 + private OnCompleteListener mOnCompleteListener; // 完成监听器 + + /** + * 构造函数。 + * @param context 上下文对象。 + * @param listener 完成监听器。 + */ + public GTaskASyncTask(Context context, OnCompleteListener listener) { + mContext = context; + mOnCompleteListener = listener; + mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mTaskManager = GTaskManager.getInstance(); + } + + /** + * 取消同步。 + */ + public void cancelSync() { + mTaskManager.cancelSync(); + } + + /** + * 发布进度信息。 + * @param message 进度信息。 + */ + public void publishProgess(String message) { + publishProgress(new String[] { 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; // 设置通知自动取消 + PendingIntent pendingIntent; // 待决意图 + + // 根据tickerId设置不同的待决意图 + 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); // 显示通知 + } + + /** + * 执行同步操作。 + * @return 同步结果状态。 + */ + @Override + protected Integer doInBackground(Void... unused) { + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext))); + return mTaskManager.sync(mContext, this); // 同步任务 + } + + /** + * 更新进度信息。 + * @param progress 进度信息数组。 + */ + @Override + protected void onProgressUpdate(String... progress) { + showNotification(R.string.ticker_syncing, progress[0]); // 显示同步中的通知 + if (mContext instanceof GTaskSyncService) { + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); // 如果上下文是GTaskSyncService,发送广播 + } + } + + /** + * 同步完成后的操作。 + * @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()); // 设置最后同步时间 + } else if (result == GTaskManager.STATE_NETWORK_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { + showNotification(R.string.ticker_cancel, mContext.getString(R.string.error_sync_cancelled)); + } + // 如果设置了完成监听器,则在新线程中调用onComplete方法 + if (mOnCompleteListener != null) { + new Thread(new Runnable() { + public void run() { + mOnCompleteListener.onComplete(); + } + }).start(); + } + } + } \ No newline at end of file diff --git a/src/java/net/micode/notes/gtask/remote/GTaskClient.java b/src/java/net/micode/notes/gtask/remote/GTaskClient.java index c67dfdf..ad4ead0 100644 --- a/src/java/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/java/net/micode/notes/gtask/remote/GTaskClient.java @@ -62,34 +62,43 @@ import java.util.zip.InflaterInputStream; public class GTaskClient { + // 类名的简写,用于日志输出 private static final String TAG = GTaskClient.class.getSimpleName(); + // Google任务的URL地址 private static final String GTASK_URL = "https://mail.google.com/tasks/"; - private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - 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; - private String mPostUrl; + // 客户端版本号 private long mClientVersion; + // 登录状态 private boolean mLoggedin; + // 上次登录时间 private long mLastLoginTime; + // 动作ID private int mActionId; + // 账户信息 private Account mAccount; + // 更新数组 private JSONArray mUpdateArray; + // 私有构造函数,防止外部直接创建对象 private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -102,6 +111,7 @@ public class GTaskClient { mUpdateArray = null; } + // 获取单例对象 public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); @@ -109,34 +119,37 @@ public class GTaskClient { return mInstance; } + // 登录方法 public boolean login(Activity activity) { - // we suppose that the cookie would expire after 5 minutes - // then we need to re-login + // 假设cookie会在5分钟后过期,需要重新登录 final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - // need to re-login after account switch + // 切换账户后需要重新登录 if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } + // 如果已经登录,则直接返回 if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } + // 更新登录时间 mLastLoginTime = System.currentTimeMillis(); + // 获取Google账户的认证令牌 String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - // login with custom domain if necessary + // 如果账户不是Gmail或Googlemail,则尝试使用自定义域名登录 if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); @@ -146,12 +159,13 @@ public class GTaskClient { mGetUrl = url.toString() + "ig"; mPostUrl = url.toString() + "r/ig"; + // 尝试使用自定义域名登录Google任务 if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; } } - // try to login with google official url + // 如果登录失败,则尝试使用Google官方URL登录 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; @@ -160,426 +174,502 @@ public class GTaskClient { } } + // 登录成功 mLoggedin = true; return true; } + // 获取Google账户的认证令牌 private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; + // 获取账户管理器 AccountManager accountManager = AccountManager.get(activity); + // 获取所有Google账户 Account[] accounts = accountManager.getAccountsByType("com.google"); + // 如果没有Google账户,则返回null if (accounts.length == 0) { Log.e(TAG, "there is no available google account"); return null; } + // 获取同步账户的名称 String accountName = NotesPreferenceActivity.getSyncAccountName(activity); Account account = null; + // 查找与设置中账户名称相同的账户 for (Account a : accounts) { if (a.name.equals(accountName)) { account = a; break; } } + // 如果找到账户,则保存账户信息 if (account != null) { mAccount = account; } else { + // 如果没有找到账户,则返回null Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } - // get the token now + // 获取认证令牌 AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); try { + // 获取认证令牌的Bundle对象 Bundle authTokenBundle = accountManagerFuture.getResult(); + // 获取认证令牌 authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); + // 如果需要,则使令牌无效并重新登录 if (invalidateToken) { accountManager.invalidateAuthToken("com.google", authToken); loginGoogleAccount(activity, false); } } catch (Exception e) { + // 如果获取认证令牌失败,则返回null Log.e(TAG, "get auth token failed"); authToken = null; } return authToken; } +} - private boolean tryToLoginGtask(Activity activity, String authToken) { - if (!loginGtask(authToken)) { - // maybe the auth token is out of date, now let's invalidate the - // token and try again - authToken = loginGoogleAccount(activity, true); - if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; - } - - if (!loginGtask(authToken)) { - Log.e(TAG, "login gtask failed"); - return false; - } + // 尝试使用给定的认证令牌登录Google任务 +private boolean tryToLoginGtask(Activity activity, String authToken) { + // 如果使用给定的认证令牌登录失败 + if (!loginGtask(authToken)) { + // 可能认证令牌已过期,现在使令牌无效并再次尝试登录 + authToken = loginGoogleAccount(activity, true); + // 如果获取认证令牌失败 + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; } - return true; - } - - private boolean loginGtask(String authToken) { - int timeoutConnection = 10000; - int timeoutSocket = 15000; - HttpParams httpParameters = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); - HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); - mHttpClient = new DefaultHttpClient(httpParameters); - BasicCookieStore localBasicCookieStore = new BasicCookieStore(); - mHttpClient.setCookieStore(localBasicCookieStore); - HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - - // login gtask - try { - String loginUrl = mGetUrl + "?auth=" + authToken; - HttpGet httpGet = new HttpGet(loginUrl); - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the cookie now - List cookies = mHttpClient.getCookieStore().getCookies(); - boolean hasAuthCookie = false; - for (Cookie cookie : cookies) { - if (cookie.getName().contains("GTL")) { - hasAuthCookie = true; - } - } - if (!hasAuthCookie) { - Log.w(TAG, "it seems that there is no auth cookie"); - } - // get the client version - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - 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); - } - JSONObject js = new JSONObject(jsString); - mClientVersion = js.getLong("v"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return false; - } catch (Exception e) { - // simply catch all exceptions - Log.e(TAG, "httpget gtask_url failed"); + // 如果使用新的认证令牌登录失败 + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); return false; } - - return true; - } - - private int getActionId() { - return mActionId++; } + return true; +} - private HttpPost createHttpPost() { - HttpPost httpPost = new HttpPost(mPostUrl); - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - httpPost.setHeader("AT", "1"); - return httpPost; - } - - private String getResponseContent(HttpEntity entity) throws IOException { - String contentEncoding = null; - if (entity.getContentEncoding() != null) { - contentEncoding = entity.getContentEncoding().getValue(); - Log.d(TAG, "encoding: " + contentEncoding); - } - - InputStream input = entity.getContent(); - if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { - input = new GZIPInputStream(entity.getContent()); - } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { - Inflater inflater = new Inflater(true); - input = new InflaterInputStream(entity.getContent(), inflater); - } - - try { - InputStreamReader isr = new InputStreamReader(input); - BufferedReader br = new BufferedReader(isr); - StringBuilder sb = new StringBuilder(); - - while (true) { - String buff = br.readLine(); - if (buff == null) { - return sb.toString(); - } - sb = sb.append(buff); +// 使用给定的认证令牌登录Google任务 +private boolean loginGtask(String authToken) { + // 设置连接超时和套接字超时的时间 + int timeoutConnection = 10000; + int timeoutSocket = 15000; + HttpParams httpParameters = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); + // 创建HTTP客户端 + mHttpClient = new DefaultHttpClient(httpParameters); + // 创建本地Cookie存储 + BasicCookieStore localBasicCookieStore = new BasicCookieStore(); + mHttpClient.setCookieStore(localBasicCookieStore); + // 设置HTTP协议参数 + HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); + + // 登录Google任务 + try { + // 构建登录URL + String loginUrl = mGetUrl + "?auth=" + authToken; + HttpGet httpGet = new HttpGet(loginUrl); + // 执行HTTP GET请求 + HttpResponse response = mHttpClient.execute(httpGet); + + // 获取Cookie + List cookies = mHttpClient.getCookieStore().getCookies(); + boolean hasAuthCookie = false; + for (Cookie cookie : cookies) { + // 检查是否存在认证Cookie + if (cookie.getName().contains("GTL")) { + hasAuthCookie = true; } - } finally { - input.close(); } - } - - private JSONObject postRequest(JSONObject js) throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); + if (!hasAuthCookie) { + Log.w(TAG, "it seems that there is no auth cookie"); } - HttpPost httpPost = createHttpPost(); - try { - LinkedList list = new LinkedList(); - list.add(new BasicNameValuePair("r", js.toString())); - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); - httpPost.setEntity(entity); - - // execute the post - HttpResponse response = mHttpClient.execute(httpPost); - String jsString = getResponseContent(response.getEntity()); - return new JSONObject(jsString); - - } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("postRequest failed"); - } catch (IOException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("postRequest failed"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("unable to convert response content to jsonobject"); - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("error occurs when posting request"); + // 获取客户端版本 + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + 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); } + JSONObject js = new JSONObject(jsString); + mClientVersion = js.getLong("v"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } catch (Exception e) { + // 捕获所有异常 + Log.e(TAG, "httpget gtask_url failed"); + return false; } - public void createTask(Task task) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(task.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + return true; +} - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); +// 获取动作ID +private int getActionId() { + // 返回并递增动作ID + return mActionId++; +} - // post - JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); +// 创建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"); + return httpPost; +} - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("create task: handing jsonobject failed"); - } +// 获取HTTP响应的内容 +private String getResponseContent(HttpEntity entity) throws IOException { + String contentEncoding = null; + // 获取内容编码 + if (entity.getContentEncoding() != null) { + contentEncoding = entity.getContentEncoding().getValue(); + Log.d(TAG, "encoding: " + contentEncoding); } - public void createTaskList(TaskList tasklist) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(tasklist.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // post - JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("create tasklist: handing jsonobject failed"); - } + InputStream input = entity.getContent(); + // 根据内容编码解压缩响应内容 + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { + input = new GZIPInputStream(entity.getContent()); + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater); } - public void commitUpdate() throws NetworkFailureException { - if (mUpdateArray != null) { - try { - JSONObject jsPost = new JSONObject(); + try { + // 读取响应内容 + InputStreamReader isr = new InputStreamReader(input); + BufferedReader br = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); - // action_list - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - mUpdateArray = null; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("commit update: handing jsonobject failed"); + while (true) { + String buff = br.readLine(); + if (buff == null) { + return sb.toString(); } + sb = sb.append(buff); } + } finally { + // 关闭输入流 + input.close(); } +} - public void addUpdateNode(Node node) throws NetworkFailureException { - if (node != null) { - // too many update items may result in an error - // set max to 10 items - if (mUpdateArray != null && mUpdateArray.length() > 10) { - commitUpdate(); - } - - if (mUpdateArray == null) - mUpdateArray = new JSONArray(); - mUpdateArray.put(node.getUpdateAction(getActionId())); - } + // 发送POST请求 +private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + // 如果未登录,则抛出异常 + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); } - public void moveTask(Task task, TaskList preParent, TaskList curParent) - throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); - - // action_list - 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()); - if (preParent == curParent && task.getPriorSibling() != null) { - // put prioring_sibing_id only if moving within the tasklist and - // it is not the first one - 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()); - if (preParent != curParent) { - // put the dest_list only if moving between tasklists - action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); - } - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + // 创建HTTP POST请求 + HttpPost httpPost = createHttpPost(); + try { + // 创建参数列表 + LinkedList list = new LinkedList(); + list.add(new BasicNameValuePair("r", js.toString())); + // 创建URL编码的表单实体 + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); + httpPost.setEntity(entity); + + // 执行POST请求 + HttpResponse response = mHttpClient.execute(httpPost); + // 获取响应内容 + String jsString = getResponseContent(response.getEntity()); + // 将响应内容转换为JSON对象 + return new JSONObject(jsString); + + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("unable to convert response content to jsonobject"); + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("error occurs when posting request"); + } +} - postRequest(jsPost); +// 创建任务 +public void createTask(Task task) throws NetworkFailureException { + // 提交更新 + commitUpdate(); + try { + // 创建JSON对象 + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // 添加创建任务的动作 + actionList.put(task.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // 设置客户端版本 + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // 发送POST请求 + JSONObject jsResponse = postRequest(jsPost); + // 获取响应结果 + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + // 设置任务的GID + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create task: handing jsonobject failed"); + } +} - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("move task: handing jsonobject failed"); - } +// 创建任务列表 +public void createTaskList(TaskList tasklist) throws NetworkFailureException { + // 提交更新 + commitUpdate(); + try { + // 创建JSON对象 + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // 添加创建任务列表的动作 + actionList.put(tasklist.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // 设置客户端版本 + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // 发送POST请求 + JSONObject jsResponse = postRequest(jsPost); + // 获取响应结果 + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + // 设置任务列表的GID + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create tasklist: handing jsonobject failed"); } +} - public void deleteNode(Node node) throws NetworkFailureException { - commitUpdate(); +// 提交更新 +public void commitUpdate() throws NetworkFailureException { + // 如果存在更新数组 + if (mUpdateArray != null) { try { + // 创建JSON对象 JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - // action_list - node.setDeleted(true); - actionList.put(node.getUpdateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + // 添加更新动作列表 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - // client_version + // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + // 发送POST请求 postRequest(jsPost); + // 清空更新数组 mUpdateArray = null; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - throw new ActionFailureException("delete node: handing jsonobject failed"); + throw new ActionFailureException("commit update: handing jsonobject failed"); } } +} - public JSONArray getTaskLists() throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); +// 添加更新节点 +public void addUpdateNode(Node node) throws NetworkFailureException { + // 如果节点不为空 + if (node != null) { + // 如果更新项过多,则提交更新 + if (mUpdateArray != null && mUpdateArray.length() > 10) { + commitUpdate(); } - try { - HttpGet httpGet = new HttpGet(mGetUrl); - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the task list - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - 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); - } - JSONObject js = new JSONObject(jsString); - return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); - } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (IOException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("get task lists: handing jasonobject failed"); + // 如果更新数组为空,则创建新的数组 + if (mUpdateArray == null) + mUpdateArray = new JSONArray(); + // 添加更新动作 + mUpdateArray.put(node.getUpdateAction(getActionId())); + } +} + +// 移动任务 +public void moveTask(Task task, TaskList preParent, TaskList curParent) + throws NetworkFailureException { + // 提交更新 + commitUpdate(); + try { + // 创建JSON对象 + JSONObject jsPost = new JSONObject(); + 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()); + 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()); + if (preParent != curParent) { + // 如果在不同任务列表之间移动,则添加目标列表的ID + action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); + } + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // 设置客户端版本 + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // 发送POST请求 + postRequest(jsPost); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("move task: handing jsonobject failed"); } +} - public JSONArray getTaskList(String listGid) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); - - // action_list - 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); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + // 删除节点 +public void deleteNode(Node node) throws NetworkFailureException { + // 提交所有挂起的更新 + commitUpdate(); + try { + // 创建JSON对象来构建请求体 + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // 设置节点为已删除,并添加更新动作到动作列表中 + 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); + // 清空更新数组 + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("delete node: handing jsonobject failed"); + } +} - JSONObject jsResponse = postRequest(jsPost); - return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("get task list: handing jsonobject failed"); - } +// 获取任务列表 +public JSONArray getTaskLists() throws NetworkFailureException { + // 如果未登录,则抛出异常 + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); } - public Account getSyncAccount() { - return mAccount; + try { + // 创建HTTP GET请求 + HttpGet httpGet = new HttpGet(mGetUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // 获取任务列表 + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + 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); + } + JSONObject js = new JSONObject(jsString); + // 返回任务列表的JSON数组 + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task lists: handing jasonobject failed"); } +} - public void resetUpdateArray() { - mUpdateArray = null; +// 获取特定任务列表中的任务 +public JSONArray getTaskList(String listGid) throws NetworkFailureException { + // 提交所有挂起的更新 + commitUpdate(); + try { + // 创建JSON对象来构建请求体 + JSONObject jsPost = new JSONObject(); + 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); + + // 设置客户端版本号 + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // 发送请求并获取响应 + JSONObject jsResponse = postRequest(jsPost); + // 返回任务的JSON数组 + return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task list: handing jsonobject failed"); } } + +// 获取用于同步的账户 +public Account getSyncAccount() { + return mAccount; +} + +// 重置更新数组 +public void resetUpdateArray() { + mUpdateArray = null; +} diff --git a/src/java/net/micode/notes/gtask/remote/GTaskManager.java b/src/java/net/micode/notes/gtask/remote/GTaskManager.java index 74e5b7e..e93a074 100644 --- a/src/java/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/java/net/micode/notes/gtask/remote/GTaskManager.java @@ -53,48 +53,60 @@ import java.util.Map; * @Package: net.micode.notes.gtask.remote * @ClassName: GTaskManager * @Description: 该类实现了便签管理者类,提供同步便签内容和文件夹,添加、更新本地和远端节点等功能 - * @Author: 郑鲲鹏 + * @Author: 张铸玮 * @Date: 2023-12-18 0:17 */ public class GTaskManager { + // 类名的简写,用于日志输出 private static final String TAG = GTaskManager.class.getSimpleName(); + // 同步状态常量 public static final int STATE_SUCCESS = 0; - public static final int STATE_NETWORK_ERROR = 1; - public static final int STATE_INTERNAL_ERROR = 2; - public static final int STATE_SYNC_IN_PROGRESS = 3; - public static final int STATE_SYNC_CANCELLED = 4; + // 单例对象 private static GTaskManager mInstance = null; + // 当前活动,用于获取认证令牌 private Activity mActivity; + // 上下文对象 private Context mContext; + // 内容解析器 private ContentResolver mContentResolver; + // 同步状态标志 private boolean mSyncing; + // 取消同步标志 private boolean mCancelled; + // 任务列表映射表 private HashMap mGTaskListHashMap; + // 任务映射表 private HashMap mGTaskHashMap; + // 元数据映射表 private HashMap mMetaHashMap; + // 元数据列表 private TaskList mMetaList; + // 本地删除ID集合 private HashSet mLocalDeleteIdMap; + // GID到NID的映射表 private HashMap mGidToNid; + // NID到GID的映射表 private HashMap mNidToGid; + // 私有构造函数,防止外部直接创建对象 private GTaskManager() { mSyncing = false; mCancelled = false; @@ -107,6 +119,7 @@ public class GTaskManager { mNidToGid = new HashMap(); } + // 获取单例对象 public static synchronized GTaskManager getInstance() { if (mInstance == null) { mInstance = new GTaskManager(); @@ -114,12 +127,15 @@ public class GTaskManager { return mInstance; } + // 设置活动上下文 public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken + // 用于获取认证令牌 mActivity = activity; } + // 同步任务 public int sync(Context context, GTaskASyncTask asyncTask) { + // 如果正在同步,则返回同步进行中的状态 if (mSyncing) { Log.d(TAG, "Sync is in progress"); return STATE_SYNC_IN_PROGRESS; @@ -128,6 +144,7 @@ public class GTaskManager { mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; + // 清空映射表和集合 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -139,18 +156,18 @@ public class GTaskManager { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // login google task + // 登录Google任务 if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // get the task list from google + // 获取任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - // do content sync work + // 执行内容同步操作 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); } catch (NetworkFailureException e) { @@ -164,6 +181,7 @@ public class GTaskManager { e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { + // 清空映射表和集合 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -173,29 +191,34 @@ public class GTaskManager { mSyncing = false; } + // 返回同步结果状态 return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } + // 初始化Google任务列表 private void initGTaskList() throws NetworkFailureException { + // 如果取消同步,则直接返回 if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); try { + // 获取任务列表的JSON数组 JSONArray jsTaskLists = client.getTaskLists(); - // init meta list first + // 首先初始化元数据列表 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); - // load meta data + // 加载元数据 JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { object = (JSONObject) jsMetas.getJSONObject(j); @@ -211,7 +234,7 @@ public class GTaskManager { } } - // create meta list if not existed + // 如果元数据列表不存在,则创建 if (mMetaList == null) { mMetaList = new TaskList(); mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX @@ -219,12 +242,13 @@ public class GTaskManager { GTaskClient.getInstance().createTaskList(mMetaList); } - // init task list + // 初始化任务列表 for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + // 如果是任务列表,则进行初始化 if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { @@ -233,7 +257,7 @@ public class GTaskManager { mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - // load tasks + // 加载任务 JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -259,571 +283,668 @@ public class GTaskManager { * @method syncContent * @description 该方法实现本地便签文本同步操作 * @date: 2023-12-18 0:01 - * @author: 郑鲲鹏 + * @author: 张铸玮 * @return void */ - private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; - - mLocalDeleteIdMap.clear(); - - if (mCancelled) { - return; - } - - // for local deleted note - 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); - } + // 同步内容 +private void syncContent() throws NetworkFailureException { + int syncType; + Cursor c = null; + String gid; + Node node; + + // 清空本地删除ID集合 + mLocalDeleteIdMap.clear(); + + // 如果取消同步,则直接返回 + if (mCancelled) { + return; + } - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + // 同步本地删除的笔记 + 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 + 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); } - } else { - Log.w(TAG, "failed to query trash folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; + + // 将笔记ID添加到本地删除ID集合中 + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); } + } else { + Log.w(TAG, "failed to query trash folder"); } + } finally { + // 关闭游标 + if (c != null) { + c.close(); + c = null; + } + } - // sync folder first - syncFolder(); - - // for note existing in database - 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); + // 同步文件夹 + syncFolder(); + + // 同步数据库中存在的笔记 + try { + // 查询数据库中存在的笔记 + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + // 获取笔记的GID + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 获取对应的节点 + node = mGTaskHashMap.get(gid); + if (node != null) { + // 从映射表中移除节点 + mGTaskHashMap.remove(gid); + // 更新GID到NID的映射表 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + // 更新NID到GID的映射表 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + // 获取同步类型 + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地添加 + syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // remote delete - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } + // 远程删除 + 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; + // 执行同步操作 + doContentSync(syncType, node, c); } + } else { + Log.w(TAG, "failed to query existing note in database"); } - // go through remaining items - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - node = entry.getValue(); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } finally { + // 关闭游标 + if (c != null) { + c.close(); + c = null; } + } - // mCancelled can be set by another thread, so we neet to check one by - // one - // clear local delete table - if (!mCancelled) { - if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { - throw new ActionFailureException("failed to batch-delete local deleted notes"); - } - } + // 遍历剩余的节点 + 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) { + return; + } - // refresh local sync id - if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); + // 清空本地删除表 + if (!mCancelled) { + if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { + throw new ActionFailureException("failed to batch-delete local deleted notes"); } + } + // 刷新本地同步ID + if (!mCancelled) { + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); } +} /** * @method syncFolder * @description 该方法实现本地文件夹同步操作 * @date: 2023-12-18 0:04 - * @author: 郑鲲鹏 + * @author: 张铸玮 * @return void */ - private void syncFolder() throws NetworkFailureException { - Cursor c = null; - String gid; - Node node; - int syncType; + // 同步文件夹 +private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + Node node; + int syncType; + + // 如果取消同步,则直接返回 + if (mCancelled) { + return; + } - 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 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 获取对应的节点 + node = mGTaskHashMap.get(gid); + if (node != null) { + // 从映射表中移除节点 + mGTaskHashMap.remove(gid); + // 更新GID到NID的映射表 + mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); + // 更新NID到GID的映射表 + 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; } + } - // for root folder - 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(); + // 同步通话记录文件夹 + 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 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); - // for system folder, only update remote name if necessary + // 更新GID到NID的映射表 + mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); + // 更新NID到GID的映射表 + mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // 如果通话记录文件夹名称发生变化,则更新远程文件夹名称 if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + 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 root folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; } + } else { + Log.w(TAG, "failed to query call note folder"); } - - // for call-note folder - 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); - // for system folder, only update remote name if - // necessary - 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; - } + } finally { + // 关闭游标 + if (c != null) { + c.close(); + c = null; } + } - // for local existing folders - 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); + // 同步数据库中存在的文件夹 + 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 + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 获取对应的节点 + node = mGTaskHashMap.get(gid); + if (node != null) { + // 从映射表中移除节点 + mGTaskHashMap.remove(gid); + // 更新GID到NID的映射表 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + // 更新NID到GID的映射表 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + // 获取同步类型 + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地添加 + syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // remote delete - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } + // 远程删除 + 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; + // 执行同步操作 + doContentSync(syncType, node, c); } + } else { + Log.w(TAG, "failed to query existing folder"); } - - // for remote add folders - 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); - } + } finally { + // 关闭游标 + if (c != null) { + c.close(); + c = null; } + } - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); + // 同步远程添加的文件夹 + 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(); +} + /** * @method doContentSync * @description 该方法通过接收到不同类型的syncType来进行对应便签内容同步操作 * @date: 2023-12-18 0:08 - * @author: 郑鲲鹏 + * @author: 张铸玮 * @return void */ - private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } + // 执行内容同步操作 +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)); - 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: - // merging both modifications maybe a good idea - // right now just use local update simply - updateRemoteNode(node, c); - break; - case Node.SYNC_ACTION_NONE: - break; - case Node.SYNC_ACTION_ERROR: - default: - throw new ActionFailureException("unkown sync action type"); - } + 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); + } + // 将节点ID添加到本地删除ID集合中 + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + 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; - } +// 添加本地节点 +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); - } + 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); - JSONObject js = node.getLocalJSONFromContent(); - try { - 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)) { - // the id is not available, have to create a new one - note.remove(NoteColumns.ID); - } + 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); } } + } - 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)) { - // the data id is not available, have to create - // a new one - data.remove(DataColumns.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); - - 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()); - } - - // create the local node - sqlNote.setGtaskId(node.getGid()); - sqlNote.commit(false); - - // update gid-nid mapping - mGidToNid.put(node.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), node.getGid()); - - // update meta - updateRemoteMeta(node.getGid(), sqlNote); - } - - private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; + } catch (JSONException e) { + Log.w(TAG, e.toString()); + e.printStackTrace(); } + sqlNote.setContent(js); - SqlNote sqlNote; - // update the note locally - sqlNote = new SqlNote(mContext, c); - sqlNote.setContent(node.getLocalJSONFromContent()); - - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) - : new Long(Notes.ID_ROOT_FOLDER); + // 设置父节点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 update local node"); + throw new ActionFailureException("cannot add local node"); } sqlNote.setParentId(parentId.longValue()); - sqlNote.commit(true); - - // update meta info - updateRemoteMeta(node.getGid(), sqlNote); } - private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } + // 创建本地节点 + sqlNote.setGtaskId(node.getGid()); + sqlNote.commit(false); - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; + // 更新GID-NID映射 + mGidToNid.put(node.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), node.getGid()); - // update remotely - if (sqlNote.isNoteType()) { - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); +} - 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); +// 更新本地节点 +private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { + // 如果取消同步,则直接返回 + if (mCancelled) { + return; + } - GTaskClient.getInstance().createTask(task); - n = (Node) task; + 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); - // add meta - updateRemoteMeta(task.getGid(), sqlNote); - } else { - TaskList tasklist = null; - - // we need to skip folder if it has already existed - 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; - } - } + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); +} - // no match we can add now - if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); - mGTaskListHashMap.put(tasklist.getGid(), tasklist); - } - n = (Node) tasklist; + // 添加远程节点 +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()); + + // 获取父任务列表的GID + 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(); - // update local note - sqlNote.setGtaskId(n.getGid()); - sqlNote.commit(false); - sqlNote.resetLocalModified(); - sqlNote.commit(true); + if (list.getName().equals(folderName)) { + tasklist = list; + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + } + break; + } + } - // gid-id mapping - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); + // 如果文件夹不存在,则创建 + if (tasklist == null) { + tasklist = new TaskList(); + tasklist.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().createTaskList(tasklist); + mGTaskListHashMap.put(tasklist.getGid(), tasklist); + } + n = (Node) tasklist; } - private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } + // 更新本地笔记 + sqlNote.setGtaskId(n.getGid()); + sqlNote.commit(false); + sqlNote.resetLocalModified(); + sqlNote.commit(true); - SqlNote sqlNote = new SqlNote(mContext, c); + // 更新GID-NID映射 + mGidToNid.put(n.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), n.getGid()); +} - // update remotely - node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); +// 更新远程节点 +private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + // 如果取消同步,则直接返回 + if (mCancelled) { + return; + } - // update meta - updateRemoteMeta(node.getGid(), sqlNote); + // 创建本地笔记对象 + SqlNote sqlNote = new SqlNote(mContext, c); - // move task if necessary - if (sqlNote.isNoteType()) { - Task task = (Task) node; - TaskList preParentList = task.getParent(); + // 更新远程节点内容 + node.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(node); - 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); + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); - if (preParentList != curParentList) { - preParentList.removeChildTask(task); - curParentList.addChildTask(task); - GTaskClient.getInstance().moveTask(task, preParentList, curParentList); - } - } + // 如果是任务类型,检查是否需要移动任务 + if (sqlNote.isNoteType()) { + Task task = (Task) node; + TaskList preParentList = task.getParent(); - // clear local modified flag - sqlNote.resetLocalModified(); - sqlNote.commit(true); - } + // 获取当前父任务列表的GID + 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); - 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 (preParentList != curParentList) { + preParentList.removeChildTask(task); + curParentList.addChildTask(task); + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } - private void refreshLocalSyncId() throws NetworkFailureException { - if (mCancelled) { - return; + // 清除本地修改标志 + 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); } + } +} - // get the latest gtask list - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - initGTaskList(); +// 刷新本地同步ID +private void refreshLocalSyncId() throws NetworkFailureException { + // 如果取消同步,则直接返回 + if (mCancelled) { + return; + } - 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); - 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"); - } + // 获取最新的任务列表 + 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()) { + // 获取笔记的GID + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); + Node node = mGTaskHashMap.get(gid); + if (node != null) { + // 更新本地笔记的同步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; } + } 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 String getSyncAccount() { + return GTaskClient.getInstance().getSyncAccount().name; +} - public void cancelSync() { - mCancelled = true; - } +// 取消同步 +public void cancelSync() { + mCancelled = true; } diff --git a/src/java/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/java/net/micode/notes/gtask/remote/GTaskSyncService.java index 6cd7f61..5a3d793 100644 --- a/src/java/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/java/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -14,148 +14,198 @@ * limitations under the License. */ -package net.micode.notes.gtask.remote; - -import android.app.Activity; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -/** - * + package net.micode.notes.gtask.remote; + + import android.app.Activity; + import android.app.Service; + import android.content.Context; + import android.content.Intent; + import android.os.Bundle; + import android.os.IBinder; + + /** + * GTaskSyncService 类实现了 GTask 同步服务的功能,提供开启同步服务、发送同步通知等功能。 + * * @ProjectName: minode * @Package: net.micode.notes.gtask.remote * @ClassName: GTaskSyncService - * @Description: 该类实现了Gtask同步服务的功能,提供开启同步服务、发送同步通知等功能 - * @Author: 郑鲲鹏 + * @Description: 该类实现了 GTask 同步服务的功能,提供开启同步服务、发送同步通知等功能 + * @Author: 张铸玮 * @Date: 2023-12-17 23:38 - */ -public class GTaskSyncService extends Service { - public final static String ACTION_STRING_NAME = "sync_action_type"; - - 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 class GTaskSyncService extends Service { + // 定义广播的 Action 字符串 + public final static String ACTION_STRING_NAME = "sync_action_type"; + + // 定义同步操作的类型常量 + 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 = ""; + /** - * @method startSync - * @description 该方法用于开启Gtask备忘录同步工作 + * startSync 方法用于开启 GTask 备忘录同步工作。 + * * @date: 2023-12-17 23:42 - * @author: 郑鲲鹏 - * @return void + * @author: 张铸玮 */ - private void startSync() { - if (mSyncTask == null) { - mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { - public void onComplete() { - mSyncTask = null; - sendBroadcast(""); - stopSelf(); - } - }); - sendBroadcast(""); - // 使同步任务以单线程队列方式开启运行 - // 2023-12-17 23:46 - mSyncTask.execute(); - } - } - + private void startSync() { + // 如果同步任务为空,则创建并启动同步任务 + if (mSyncTask == null) { + mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + public void onComplete() { + // 同步完成时,将同步任务置空并发送广播通知 + mSyncTask = null; + sendBroadcast(""); + stopSelf(); + } + }); + // 发送广播通知同步开始 + sendBroadcast(""); + // 以单线程队列方式启动同步任务 + mSyncTask.execute(); + } + } + /** - * @method cancelSync - * @description 该方法用于取消Gtask备忘录同步工作 + * cancelSync 方法用于取消 GTask 备忘录同步工作。 + * * @date: 2023-12-17 23:49 - * @author: 郑鲲鹏 - * @return void + * @author: 张铸玮 */ - private void cancelSync() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } - } - - @Override - public void onCreate() { - mSyncTask = null; - } - + private void cancelSync() { + // 如果同步任务不为空,则取消同步任务 + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + @Override + public void onCreate() { + // 在服务创建时,将同步任务置空 + mSyncTask = null; + } + /** - * @method onStartCommand - * @description 该方法用于接受其他组件传递的消息使之重启GtaskSyncService + * onStartCommand 方法用于接收其他组件传递的消息,使 GTaskSyncService 重启。 + * * @date: 2023-12-17 23:53 - * @author: 郑鲲鹏 - * @return int + * @author: 张铸玮 */ - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { - switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { - // 开始同步或取消同步 - // 2023-12-17 23:52 - case ACTION_START_SYNC: - startSync(); - break; - case ACTION_CANCEL_SYNC: - cancelSync(); - break; - default: - break; - } - return START_STICKY; - } - return super.onStartCommand(intent, flags, startId); - } - - @Override - public void onLowMemory() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } - } - - public IBinder onBind(Intent intent) { - 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); - } - - public static void startSync(Activity activity) { - GTaskManager.getInstance().setActivityContext(activity); - Intent intent = new Intent(activity, GTaskSyncService.class); - 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); - } - - public static boolean isSyncing() { - return mSyncTask != null; - } - - public static String getProgressString() { - return mSyncProgress; - } -} + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // 获取传递过来的 Bundle 对象 + Bundle bundle = intent.getExtras(); + // 如果 Bundle 对象不为空且包含同步操作类型键,则根据操作类型执行相应操作 + if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { + switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { + // 开始同步 + case ACTION_START_SYNC: + startSync(); + break; + // 取消同步 + case ACTION_CANCEL_SYNC: + cancelSync(); + break; + default: + break; + } + // 返回 START_STICKY,表示如果服务被杀死,系统会尝试重新创建服务 + return START_STICKY; + } + // 如果 Bundle 对象为空或不包含同步操作类型键,则调用父类的 onStartCommand 方法 + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onLowMemory() { + // 在内存不足时,取消同步任务 + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + public IBinder onBind(Intent intent) { + // 返回 null,表示不支持绑定 + return null; + } + + /** + * sendBroadcast 方法用于发送广播通知。 + * + * @param msg 同步进度消息 + */ + public void sendBroadcast(String msg) { + // 更新同步进度字符串 + mSyncProgress = msg; + // 创建 Intent 对象 + 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); + } + + /** + * startSync 静态方法用于启动 GTask 同步服务。 + * + * @param activity Activity 对象 + */ + public static void startSync(Activity activity) { + // 设置 Activity 上下文 + GTaskManager.getInstance().setActivityContext(activity); + // 创建 Intent 对象 + Intent intent = new Intent(activity, GTaskSyncService.class); + // 将同步操作类型设置为开始同步 + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); + // 启动服务 + activity.startService(intent); + } + + /** + * cancelSync 静态方法用于取消 GTask 同步服务。 + * + * @param context Context 对象 + */ + public static void cancelSync(Context context) { + // 创建 Intent 对象 + Intent intent = new Intent(context, GTaskSyncService.class); + // 将同步操作类型设置为取消同步 + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); + // 启动服务 + context.startService(intent); + } + + /** + * isSyncing 静态方法用于判断是否正在同步。 + * + * @return 是否正在同步 + */ + public static boolean isSyncing() { + // 如果同步任务不为空,则表示正在同步 + return mSyncTask != null; + } + + /** + * getProgressString 静态方法用于获取同步进度字符串。 + * + * @return 同步进度字符串 + */ + public static String getProgressString() { + // 返回同步进度字符串 + return mSyncProgress; + } + } \ No newline at end of file