/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.micode.notes.gtask.remote; import android.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; public class GTaskClient { private static final String TAG = GTaskClient.class.getSimpleName(); 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; private DefaultHttpClient mHttpClient; private String mGetUrl; private String mPostUrl; private long mClientVersion; private boolean mLoggedin; private long mLastLoginTime; private int mActionId; private Account mAccount; private JSONArray mUpdateArray; private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; mClientVersion = -1; mLoggedin = false; mLastLoginTime = 0; mActionId = 1; mAccount = null; mUpdateArray = null; } public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } public boolean login(Activity activity) { // 假设cookie在5分钟后过期,之后需要重新登录 final long interval = 1000 * 60 * 5; // 5分钟的毫秒数 // 如果最后一次登录时间加上5分钟小于当前时间,则认为登录已过期 if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; // 设置登录状态为未登录 } // 如果已经登录,但同步账号名称与当前活动(Activity)中设置的同步账号名称不一致,则需要重新登录 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(); // 尝试登录Google账号,参数false可能表示某种模式或选项(例如不使用静默登录) String authToken = loginGoogleAccount(activity, false); // 如果获取到的authToken为空,表示登录Google账号失败 if (authToken == null) { Log.e(TAG, "login google account failed"); // 记录错误日志:登录Google账号失败 return false; // 返回false,表示登录失败 } // 如果账号名称不以gmail.com或googlemail.com结尾,则可能需要使用自定义域的URL进行登录 if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { // 构建自定义域的URL StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); int index = mAccount.name.indexOf('@') + 1; // 找到@符号的位置,并加1以获取域名部分的开始索引 String suffix = mAccount.name.substring(index); // 获取域名部分 url.append(suffix + "/"); // 拼接域名到URL mGetUrl = url.toString() + "ig"; // 设置获取数据的URL mPostUrl = url.toString() + "r/ig"; // 设置提交数据的URL // 尝试使用自定义域的URL和authToken登录Google任务 if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; // 如果登录成功,设置登录状态为已登录 } } // 如果使用自定义域登录失败或账号是gmail/googlemail,则尝试使用Google官方的URL登录 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; // 设置获取数据的官方URL mPostUrl = GTASK_POST_URL; // 设置提交数据的官方URL // 尝试使用官方的URL和authToken登录Google任务 if (!tryToLoginGtask(activity, authToken)) { return false; // 如果登录失败,返回false } } // 无论通过哪种方式,只要登录成功,就设置登录状态为已登录,并返回true mLoggedin = true; return true; } private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; // 声明认证令牌变量 // 获取AccountManager实例,用于管理账号信息 AccountManager accountManager = AccountManager.get(activity); // 获取设备上所有类型为"com.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; // 遍历所有Google账号,找到与设置中同步账号名称相匹配的账号 for (Account a : accounts) { if (a.name.equals(accountName)) { account = a; break; } } // 如果找到了匹配的账号,则将其赋值给mAccount变量 if (account != null) { mAccount = account; } else { // 如果没有找到匹配的账号,则记录错误日志并返回null Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } // 通过AccountManager获取认证令牌 // 注意:"goanna_mobile"可能是一个特定于应用的scope或权限字符串,它应该与你在Google API Console中配置的一致 AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); try { // 获取认证令牌的结果 Bundle authTokenBundle = accountManagerFuture.getResult(); // 从结果中获取认证令牌 authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 如果需要使令牌失效(例如,因为令牌可能已过期或不再安全) if (invalidateToken) { // 使指定类型的账号的认证令牌失效 // 注意:这里应该使用与获取令牌时相同的accountType,即"com.google" // 但由于代码中的这个调用可能是一个错误(因为它使用了"com.google"作为scope而不是accountType), // 在实际应用中应该检查并修正这一点。正确的做法可能是使用与获取令牌时相同的scope或accountType。 // 然而,由于我们不知道"goanna_mobile"的确切含义,这里我们假设它应该与getAuthToken调用中的一致。 // 但为了注释的清晰性,我们指出这里可能存在的问题。 accountManager.invalidateAuthToken("com.google", authToken); // 递归调用loginGoogleAccount方法以获取新的令牌(这次不使用invalidateToken标志) // 注意:在实际应用中,递归调用可能不是最佳做法,因为它可能导致堆栈溢出如果调用链太长。 // 一种更好的做法可能是使用循环或重新设计逻辑以避免递归。 loginGoogleAccount(activity, false); // 注意:由于这个递归调用没有返回值,并且我们也没有在这里处理它的结果, // 因此这个递归调用实际上对当前的authToken变量没有影响。 // 这可能是一个逻辑错误,需要根据你的应用需求进行修正。 } } catch (Exception e) { // 如果获取认证令牌失败,则记录错误日志并将authToken设置为null Log.e(TAG, "get auth token failed"); authToken = null; } // 返回认证令牌(可能是null,如果获取失败) return authToken; } // 尝试使用提供的认证令牌登录Google Tasks private boolean tryToLoginGtask(Activity activity, String authToken) { // 首先尝试使用传入的认证令牌登录 if (!loginGtask(authToken)) { // 如果登录失败,可能是因为认证令牌已过期 // 因此,我们先使令牌失效,然后尝试重新获取新的认证令牌 authToken = loginGoogleAccount(activity, true); // true可能表示强制刷新令牌 if (authToken == null) { // 如果无法获取新的认证令牌,记录错误并返回false Log.e(TAG, "login google account failed"); return false; } // 使用新获取的认证令牌再次尝试登录Google Tasks if (!loginGtask(authToken)) { // 如果仍然登录失败,记录错误并返回false Log.e(TAG, "login gtask failed"); return false; } } // 如果登录成功,返回true return true; } // 使用提供的认证令牌登录Google Tasks private boolean loginGtask(String authToken) { // 设置HTTP连接的超时时间 int timeoutConnection = 10000; // 连接超时时间10秒 int timeoutSocket = 15000; // 套接字超时时间15秒 HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); mHttpClient = new DefaultHttpClient(httpParameters); // 创建HTTP客户端 // 设置cookie存储 BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); // 禁用“Expect/Continue”握手 HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 构造登录URL并发送HTTP GET请求 try { String loginUrl = mGetUrl + "?auth=" + authToken; // mGetUrl是Google Tasks的登录URL HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = mHttpClient.execute(httpGet); // 执行请求 // 检查响应中的cookie,特别是包含"GTL"的cookie,这是Google Tasks的认证cookie List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; for (Cookie cookie : cookies) { if (cookie.getName().contains("GTL")) { hasAuthCookie = true; } } if (!hasAuthCookie) { // 如果没有找到认证cookie,记录警告 Log.w(TAG, "it seems that there is no auth cookie"); } // 解析响应内容,获取客户端版本信息 String resString = getResponseContent(response.getEntity()); // 获取响应内容 String jsBegin = "_setup("; // 响应内容中JavaScript对象的开始标记 String jsEnd = ")}"; // 响应内容中JavaScript对象的结束标记 int begin = resString.indexOf(jsBegin); int end = resString.lastIndexOf(jsEnd); String jsString = null; if (begin != -1 && end != -1 && begin < end) { jsString = resString.substring(begin + jsBegin.length(), end); // 提取JavaScript对象字符串 } JSONObject js = new JSONObject(jsString); // 将字符串转换为JSON对象 mClientVersion = js.getLong("v"); // 从JSON对象中获取客户端版本信息 } catch (JSONException e) { // 解析JSON时发生异常,记录错误并返回false Log.e(TAG, e.toString()); e.printStackTrace(); return false; } catch (Exception e) { // 捕获所有其他异常,记录错误并返回false Log.e(TAG, "httpget gtask_url failed"); return false; } // 如果一切顺利,返回true return true; } // 定义一个私有方法,用于获取一个动作ID,并且每次调用此方法时动作ID自增 private int getActionId() { return mActionId++; // 返回当前mActionId的值,然后将mActionId自增1 } // 定义一个私有方法,用于创建一个HttpPost对象 private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); // 使用成员变量mPostUrl创建一个HttpPost对象 httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); // 设置Content-Type头部为表单数据格式,字符集为utf-8 httpPost.setHeader("AT", "1"); // 设置AT头部为1,可能是用于认证或跟踪请求的自定义头部 return httpPost; // 返回创建好的HttpPost对象 } // 定义一个私有方法,用于从HttpEntity中获取响应内容 private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; // 定义一个字符串变量,用于存储内容编码 if (entity.getContentEncoding() != null) { // 如果HttpEntity有内容编码 contentEncoding = entity.getContentEncoding().getValue(); // 获取内容编码的值 Log.d(TAG, "encoding: " + contentEncoding); // 使用Log打印出内容编码 } InputStream input = entity.getContent(); // 从HttpEntity获取输入流 // 根据内容编码选择合适的输入流处理方式 if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { input = new GZIPInputStream(entity.getContent()); // 如果是gzip编码,使用GZIPInputStream解压缩 } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { Inflater inflater = new Inflater(true); // 如果是deflate编码,创建一个Inflater对象 input = new InflaterInputStream(entity.getContent(), inflater); // 使用InflaterInputStream解压缩 } try { InputStreamReader isr = new InputStreamReader(input); // 创建InputStreamReader,用于将输入流转换为字符流 BufferedReader br = new BufferedReader(isr); // 创建BufferedReader,用于读取字符流 StringBuilder sb = new StringBuilder(); // 创建一个StringBuilder,用于拼接读取到的字符 while (true) { // 循环读取字符流直到结束 String buff = br.readLine(); // 读取一行字符 if (buff == null) { // 如果读取到null,表示已经读取到流末尾 return sb.toString(); // 返回拼接好的字符串 } sb = sb.append(buff); // 将读取到的字符追加到StringBuilder中 } } finally { input.close(); // 无论是否出现异常,都关闭输入流 } } // 定义一个私有方法,用于发送POST请求并返回JSON格式的响应 // 参数js是一个包含要发送数据的JSONObject // 如果网络请求失败或响应无法解析为JSONObject,则抛出异常 private JSONObject postRequest(JSONObject js) throws NetworkFailureException { // 如果未登录,则记录错误日志并抛出ActionFailureException异常 if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } // 创建一个HttpPost对象 HttpPost httpPost = createHttpPost(); try { // 创建一个LinkedList来存储要发送的表单数据 LinkedList list = new LinkedList(); // 将JSONObject转换为字符串,并添加到表单数据中 list.add(new BasicNameValuePair("r", js.toString())); // 使用表单数据和UTF-8编码创建一个UrlEncodedFormEntity对象 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 将实体设置到HttpPost对象中 httpPost.setEntity(entity); // 执行HttpPost请求 HttpResponse response = mHttpClient.execute(httpPost); // 从响应中获取实体,并转换为字符串 String jsString = getResponseContent(response.getEntity()); // 将字符串解析为JSONObject并返回 return new JSONObject(jsString); } catch (ClientProtocolException e) { // 如果客户端协议异常,记录错误日志,打印堆栈跟踪,并抛出NetworkFailureException异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (IOException e) { // 如果IO异常,记录错误日志,打印堆栈跟踪,并抛出NetworkFailureException异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new NetworkFailureException("postRequest failed"); } catch (JSONException e) { // 如果JSON解析异常,记录错误日志,打印堆栈跟踪,并抛出ActionFailureException异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("unable to convert response content to jsonobject"); } catch (Exception e) { // 如果其他异常,记录错误日志,打印堆栈跟踪,并抛出ActionFailureException异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("error occurs when posting request"); } } // 定义一个公共方法,用于创建任务 // 参数task是一个Task对象,包含了要创建的任务信息 // 如果网络请求失败或JSON处理失败,则抛出异常 public void createTask(Task task) throws NetworkFailureException { // 提交更新(可能是同步数据或检查状态等) commitUpdate(); try { // 创建一个JSONObject来存储要发送的数据 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray来存储动作列表 JSONArray actionList = new JSONArray(); // 将任务的创建动作添加到动作列表中 // getActionId()方法用于获取一个唯一的动作ID actionList.put(task.getCreateAction(getActionId())); // 将动作列表添加到JSONObject中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 将客户端版本添加到JSONObject中 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求并获取响应 JSONObject jsResponse = postRequest(jsPost); // 从响应的results数组中获取第一个结果对象 JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 从结果对象中获取新任务的ID,并设置到任务对象中 task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { // 如果JSON处理异常,记录错误日志,打印堆栈跟踪,并抛出ActionFailureException异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("create task: handing jsonobject failed"); } } // 定义一个方法用于创建任务列表,接收一个TaskList对象作为参数,并声明可能抛出NetworkFailureException异常 public void createTaskList(TaskList tasklist) throws NetworkFailureException { // 调用commitUpdate方法,可能抛出NetworkFailureException异常 commitUpdate(); try { // 创建一个新的JSONObject对象用于存储POST请求的数据 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象用于存储动作列表 JSONArray actionList = new JSONArray(); // 将创建任务列表的动作添加到动作列表中,getActionId()方法可能用于获取动作的唯一标识符 actionList.put(tasklist.getCreateAction(getActionId())); // 将动作列表添加到POST数据中,键为GTASK_JSON_ACTION_LIST jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 将客户端版本添加到POST数据中,键为GTASK_JSON_CLIENT_VERSION jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求,并接收响应 JSONObject jsResponse = postRequest(jsPost); // 从响应中提取结果数组的第一个元素,并转换为JSONObject JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 从结果中提取新创建的任务列表ID,并设置给tasklist对象 tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { // 捕获JSON处理异常,记录错误日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常,指示处理JSON对象失败 throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } // 定义一个方法用于提交更新,可能抛出NetworkFailureException异常 public void commitUpdate() throws NetworkFailureException { // 检查是否有待提交的更新(mUpdateArray不为null) if (mUpdateArray != null) { try { // 创建一个新的JSONObject对象用于存储POST请求的数据 JSONObject jsPost = new JSONObject(); // 将待提交的更新动作列表添加到POST数据中,键为GTASK_JSON_ACTION_LIST jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); // 将客户端版本添加到POST数据中,键为GTASK_JSON_CLIENT_VERSION jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求,提交更新(注意这里没有处理响应) postRequest(jsPost); // 清空待提交的更新数组,表示更新已提交 mUpdateArray = null; } catch (JSONException e) { // 捕获JSON处理异常,记录错误日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常,指示处理JSON对象失败 throw new ActionFailureException("commit update: handing jsonobject failed"); } } } // 添加一个更新节点到更新数组,如果更新数组已满(超过10个元素),则先提交更新 public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // 检查更新数组是否存在且长度超过10 if (mUpdateArray != null && mUpdateArray.length() > 10) { // 提交当前积累的更新 commitUpdate(); } // 如果更新数组为空,则初始化它 if (mUpdateArray == null) { mUpdateArray = new JSONArray(); } // 将节点的更新动作添加到更新数组中 mUpdateArray.put(node.getUpdateAction(getActionId())); } } // 移动一个任务从一个任务列表到另一个任务列表(或在该任务列表内移动) public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { // 提交当前积累的更新 commitUpdate(); try { // 创建POST请求的JSON对象 JSONObject jsPost = new JSONObject(); // 创建动作列表的JSON数组 JSONArray actionList = new JSONArray(); // 创建动作对象的JSON对象 JSONObject action = new JSONObject(); // 设置动作类型为移动 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); // 设置动作ID action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置要移动的任务ID action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); // 如果是在同一个任务列表内移动且不是第一个任务,则设置前一个兄弟任务的ID if (preParent == curParent && task.getPriorSibling() != null) { action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); } // 设置源任务列表ID action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); // 设置目标父任务列表ID action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); // 如果是在不同的任务列表之间移动,则也设置目标列表ID(这里可能有些冗余,因为dest_parent通常足以确定位置) if (preParent != curParent) { action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } // 将动作添加到动作列表中 actionList.put(action); // 将动作列表添加到POST数据中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 postRequest(jsPost); } catch (JSONException e) { // 捕获JSON处理异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常 throw new ActionFailureException("move task: handling jsonObject failed"); } } // 删除一个节点(可能是任务或其他可删除项) public void deleteNode(Node node) throws NetworkFailureException { // 提交当前积累的更新 commitUpdate(); try { // 创建POST请求的JSON对象 JSONObject jsPost = new JSONObject(); // 创建动作列表的JSON数组 JSONArray actionList = new JSONArray(); // 标记节点为已删除 node.setDeleted(true); // 将节点的更新动作添加到动作列表中 actionList.put(node.getUpdateAction(getActionId())); // 将动作列表添加到POST数据中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 postRequest(jsPost); // 清空更新数组,因为更新已提交 mUpdateArray = null; } catch (JSONException e) { // 捕获JSON处理异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常 throw new ActionFailureException("delete node: handling jsonObject failed"); } } // 获取所有任务列表 public JSONArray getTaskLists() throws NetworkFailureException { // 检查是否已登录 if (!mLoggedin) { Log.e(TAG, "please login first"); // 抛出自定义的ActionFailureException异常 throw new ActionFailureException("not logged in"); } try { // 创建HTTP GET请求 HttpGet httpGet = new HttpGet(mGetUrl); // 执行GET请求并获取响应 HttpResponse response = mHttpClient.execute(httpGet); // 从响应中获取任务列表的JSON字符串 String resString = getResponseContent(response.getEntity()); // 定义JSON字符串的起始和结束标记 String jsBegin = "_setup("; String jsEnd = ")}"; // 查找起始和结束标记的位置 int begin = resString.indexOf(jsBegin); int end = resString.lastIndexOf(jsEnd); // 提取JSON字符串 String jsString = null; if (begin != -1 && end != -1 && begin < end) { jsString = resString.substring(begin + jsBegin.length(), end); } // 将JSON字符串转换为JSONObject JSONObject js = new JSONObject(jsString); // 从JSONObject中获取任务列表的JSONArray return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); } catch (ClientProtocolException e) { // 捕获HTTP协议异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的NetworkFailureException异常 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { // 捕获IO异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的NetworkFailureException异常 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { // 捕获JSON处理异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常 throw new ActionFailureException("get task lists: handling jsonObject failed"); } } // 获取指定任务列表中的所有任务 public JSONArray getTaskList(String listGid) throws NetworkFailureException { // 提交当前积累的更新 commitUpdate(); try { // 创建POST请求的JSON对象 JSONObject jsPost = new JSONObject(); // 创建动作列表的JSON数组 JSONArray actionList = new JSONArray(); // 创建动作对象的JSON对象 JSONObject action = new JSONObject(); // 设置动作类型为获取所有任务 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); // 设置动作ID action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置要获取的任务列表ID action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); // 设置是否包含已删除的任务(这里设置为不包含) action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 将动作添加到动作列表中 actionList.put(action); // 将动作列表添加到POST数据中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求并获取响应 JSONObject jsResponse = postRequest(jsPost); // 从响应中获取任务的JSONArray return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); } catch (JSONException e) { // 捕获JSON处理异常并记录日志 Log.e(TAG, e.toString()); e.printStackTrace(); // 抛出自定义的ActionFailureException异常 throw new ActionFailureException("get task list: handling jsonObject failed"); } } public Account getSyncAccount() { return mAccount; } public void resetUpdateArray() { mUpdateArray = null; } }