diff --git a/src/net/micode/notes/data/GClient.java b/src/net/micode/notes/data/GClient.java new file mode 100644 index 0000000..64f4a48 --- /dev/null +++ b/src/net/micode/notes/data/GClient.java @@ -0,0 +1,650 @@ +package net.micode.notes.gtask.remote; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.gtask.data.Node; +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.GTaskStringUtils; +import net.micode.notes.ui.NotesPreferenceActivity; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * GTaskClient类用于与Google Tasks进行交互,包括登录、创建任务、创建任务列表、移动任务以及删除节点等操作。 + */ +public class GTaskClient { + private static final String TAG = GTaskClient.class.getSimpleName(); // 日志标签 + + 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"; // 获取数据的URL + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; // 提交数据的URL + + private static GTaskClient mInstance = null; // 单例实例 + private DefaultHttpClient mHttpClient; // HTTP客户端 + private String mGetUrl; // 获取数据的具体URL + private String mPostUrl; // 提交数据的具体URL + private long mClientVersion; // 客户端版本号 + private boolean mLoggedin; // 是否已登录 + private long mLastLoginTime; // 上次登录时间 + private int mActionId; // 操作ID + private Account mAccount; // 当前使用的账户 + private JSONArray mUpdateArray; // 更新操作列表 + + /** + * 私有构造函数,用于初始化GTaskClient对象。 + */ + private GTaskClient() { + mHttpClient = null; + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + mClientVersion = -1; + mLoggedin = false; + mLastLoginTime = 0; + mActionId = 1; + mAccount = null; + mUpdateArray = null; + } + + /** + * 获取GTaskClient的单例实例。 + * @return GTaskClient的单例实例 + */ + public static synchronized GTaskClient getInstance() { + if (mInstance == null) { + mInstance = new GTaskClient(); + } + return mInstance; + } + + /** + * 登录Google账户,如果需要则重新登录。 + * @param activity 当前的Activity实例 + * @return 如果登录成功则返回true,否则返回false + */ + public boolean login(Activity activity) { + // 设置cookie过期时间为5分钟,如果超过这个时间则重新登录 + final long interval = 1000 * 60 * 5; + if (mLastLoginTime + interval < System.currentTimeMillis()) { + mLoggedin = false; + } + + // 如果账户发生切换,则需要重新登录 + if (mLoggedin + && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity + .getSyncAccountName(activity))) { + mLoggedin = false; + } + + // 如果已经登录,直接返回true + if (mLoggedin) { + Log.d(TAG, "already logged in"); + return true; + } + + mLastLoginTime = System.currentTimeMillis(); // 更新上次登录时间 + String authToken = loginGoogleAccount(activity, false); // 获取认证令牌 + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + // 如果账户不在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"; + mPostUrl = url.toString() + "r/ig"; + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true; + } + } + + // 尝试使用Google官方URL登录 + if (!mLoggedin) { + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) { + return false; + } + } + + mLoggedin = true; // 登录成功 + return true; + } + + /** + * 登录Google账户,获取认证令牌。 + * @param activity 当前的Activity实例 + * @param invalidateToken 是否使当前令牌失效 + * @return 认证令牌字符串 + */ + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + String authToken; + AccountManager accountManager = AccountManager.get(activity); // 获取AccountManager实例 + Account[] accounts = accountManager.getAccountsByType("com.google"); // 获取所有Google账户 + + if (accounts.length == 0) { // 没有可用的Google账户 + 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 { + 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); + try { + 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; + } + + return authToken; + } + + /** + * 尝试登录Google Tasks。 + * @param activity 当前的Activity实例 + * @param authToken 认证令牌 + * @return 如果登录成功则返回true,否则返回false + */ + 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; + } + + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); + return false; + } + } + return true; + } + + /** + * 使用给定的认证令牌登录Google Tasks。 + * @param authToken 认证令牌 + * @return 如果登录成功则返回true,否则返回false + */ + private boolean loginGtask(String authToken) { + int timeoutConnection = 10000; // 连接超时时间 + int timeoutSocket = 15000; // 套接字超时时间 + HttpParams httpParameters = new BasicHttpParams(); // HTTP参数 + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); // 设置连接超时时间 + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); // 设置套接字超时时间 + 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; // 构建登录URL + HttpGet httpGet = new HttpGet(loginUrl); // 创建HttpGet请求 + HttpResponse response = mHttpClient.execute(httpGet); // 执行请求 + + // 获取认证cookie + List cookies = mHttpClient.getCookieStore().getCookies(); + boolean hasAuthCookie = false; + for (Cookie cookie : cookies) { + if (cookie.getName().contains("GTL")) { // 检查是否存在认证cookie + hasAuthCookie = true; + } + } + if (!hasAuthCookie) { + Log.w(TAG, "it seems that there is no auth cookie"); + } + + // 获取客户端版本号 + String resString = getResponseContent(response.getEntity()); // 获取响应内容 + String jsBegin = "_setup("; // JSON数据开始标记 + String jsEnd = ")}"; // JSON数据结束标记 + 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); // 提取JSON字符串 + } + JSONObject js = new JSONObject(jsString); // 将JSON字符串转换为JSONObject + 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; + } + + return true; + } + + /** + * 获取下一个操作ID。 + * @return 下一个操作ID + */ + private int getActionId() { + return mActionId++; + } + + /** + * 创建一个HttpPost请求。 + * @return HttpPost请求对象 + */ + private HttpPost createHttpPost() { + HttpPost httpPost = new HttpPost(mPostUrl); // 创建HttpPost请求 + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); // 设置请求头 + httpPost.setHeader("AT", "1"); + return httpPost; + } + + /** + * 获取HTTP响应内容。 + * @param entity HTTP响应实体 + * @return 响应内容字符串 + * @throws IOException 如果发生IO错误 + */ + 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")) { // 如果是gzip编码 + input = new GZIPInputStream(entity.getContent()); // 使用GZIPInputStream解码 + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { // 如果是deflate编码 + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater); // 使用InflaterInputStream解码 + } + + try { + InputStreamReader isr = new InputStreamReader(input); // 创建InputStreamReader + BufferedReader br = new BufferedReader(isr); // 创建BufferedReader + StringBuilder sb = new StringBuilder(); + + // 读取响应内容 + String buff; + while ((buff = br.readLine()) != null) { + sb.append(buff); // 将每一行内容追加到StringBuilder中 + } + return sb.toString(); + } finally { + input.close(); // 关闭输入流 + } + } + + /** + * 提交一个POST请求到Google Tasks。 + * @param js 包含操作的JSONObject + * @return 服务器返回的JSONObject + * @throws NetworkFailureException 如果发生网络错误 + */ + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); // 如果未登录,抛出异常 + } + + HttpPost httpPost = createHttpPost(); // 创建HttpPost请求 + try { + LinkedList list = new LinkedList(); + list.add(new BasicNameValuePair("r", js.toString())); // 将JSONObject转换为字符串并添加到请求参数中 + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 创建UrlEncodedFormEntity + httpPost.setEntity(entity); // 设置请求实体 + + // 执行POST请求 + HttpResponse response = mHttpClient.execute(httpPost); + String jsString = getResponseContent(response.getEntity()); // 获取响应内容 + return new JSONObject(jsString); // 将响应内容转换为JSONObject + + } 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"); // 如果发生IO错误,抛出异常 + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("unable to convert response content to jsonobject"); // 如果无法将响应内容转换为JSONObject,抛出异常 + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("error occurs when posting request"); // 如果发生其他错误,抛出异常 + } + } + + /** + * 创建一个新的任务。 + * @param task 要创建的任务对象 + * @throws NetworkFailureException 如果发生网络错误 + */ + public void createTask(Task task) throws NetworkFailureException { + commitUpdate(); // 提交所有待更新的操作 + try { + 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); // 获取响应结果 + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务ID + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create task: handing jsonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 创建一个新的任务列表。 + * @param tasklist 要创建的任务列表对象 + * @throws NetworkFailureException 如果发生网络错误 + */ + public void createTaskList(TaskList tasklist) throws NetworkFailureException { + commitUpdate(); // 提交所有待更新的操作 + try { + 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); // 获取响应结果 + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务列表ID + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create tasklist: handing jsonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 提交所有待更新的操作。 + * @throws NetworkFailureException 如果发生网络错误 + */ + public void commitUpdate() throws NetworkFailureException { + if (mUpdateArray != null) { // 如果有待更新的操作 + try { + JSONObject jsPost = new JSONObject(); + + // 设置操作列表 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); + + // 设置客户端版本号 + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); // 提交POST请求 + mUpdateArray = null; // 清空待更新的操作列表 + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("commit update: handing jsonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + } + + /** + * 添加一个更新操作到待更新的操作列表中。 + * @param node 要更新的节点对象 + * @throws NetworkFailureException 如果发生网络错误 + */ + public void addUpdateNode(Node node) throws NetworkFailureException { + if (node != null) { + // 如果待更新的操作数量超过10个,则先提交这些操作 + if (mUpdateArray != null && mUpdateArray.length() > 10) { + commitUpdate(); + } + + if (mUpdateArray == null) { + mUpdateArray = new JSONArray(); // 如果操作列表为空,则创建一个新的JSONArray + } + 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(); // 提交所有待更新的操作 + try { + 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); + + postRequest(jsPost); // 提交POST请求 + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("move task: handing jsonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 删除一个节点。 + * @param node 要删除的节点对象 + * @throws NetworkFailureException 如果发生网络错误 + */ + public void deleteNode(Node node) throws NetworkFailureException { + commitUpdate(); // 提交所有待更新的操作 + try { + 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); // 提交POST请求 + mUpdateArray = null; // 清空待更新的操作列表 + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("delete node: handing jsonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 获取所有任务列表。 + * @return 包含所有任务列表的JSONArray + * @throws NetworkFailureException 如果发生网络错误 + */ + public JSONArray getTaskLists() throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); // 如果未登录,抛出异常 + } + + try { + HttpGet httpGet = new HttpGet(mGetUrl); // 创建HttpGet请求 + HttpResponse response = mHttpClient.execute(httpGet); // 执行请求 + + // 获取任务列表数据 + String resString = getResponseContent(response.getEntity()); // 获取响应内容 + String jsBegin = "_setup("; // JSON数据开始标记 + String jsEnd = ")}"; // JSON数据结束标记 + 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); // 提取JSON字符串 + } + JSONObject js = new JSONObject(jsString); // 将JSON字符串转换为JSONObject + 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"); // 如果发生IO错误,抛出异常 + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task lists: handing jasonobject failed"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 获取指定ID的任务列表中的所有任务。 + * @param listGid 任务列表的ID + * @return 包含指定任务列表中所有任务的JSONArray + * @throws NetworkFailureException 如果发生网络错误 + */ + public JSONArray getTaskList(String listGid) throws NetworkFailureException { + commitUpdate(); // 提交所有待更新的操作 + try { + 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); + + // 提交POST请求 + 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"); // 如果发生JSON处理错误,抛出异常 + } + } + + /** + * 获取当前同步的账户。 + * @return 当前同步的账户对象 + */ + public Account getSyncAccount() { + return mAccount; + } + + /** + * 重置待更新的操作列表。 + */ + public void resetUpdateArray() { + mUpdateArray = null; + } +}