/* * 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; // GTaskClient类,用于与Google Tasks服务进行交互 public class GTaskClient { // 用于日志记录的标签,方便在日志中定位该类相关的信息 private static final String TAG = GTaskClient.class.getSimpleName(); // Google Tasks服务的基本URL private static final String GTASK_URL = "https://mail.google.com/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"; // 单例实例,用于确保整个应用中只有一个GTaskClient实例 private static GTaskClient mInstance = null; // 默认的HTTP客户端,用于发送HTTP请求 private DefaultHttpClient mHttpClient; // 实际使用的获取任务数据的URL private String mGetUrl; // 实际使用的提交任务数据的URL private String mPostUrl; // 客户端版本号 private long mClientVersion; // 标记是否已经登录 private boolean mLoggedin; // 最后一次登录的时间 private long mLastLoginTime; // 操作ID,可能用于标识不同的操作 private int mActionId; // 当前登录的账户 private Account mAccount; // 用于存储更新任务数据的JSON数组 private JSONArray mUpdateArray; // 私有构造函数,确保该类只能通过getInstance方法创建实例 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单例实例的方法,使用synchronized关键字确保线程安全 public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } // 登录到Google Tasks服务的方法 public boolean login(Activity activity) { // 设定cookie的过期时间为5分钟 final long interval = 1000 * 60 * 5; // 如果距离上次登录时间超过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(); // 登录Google账户获取认证令牌 String authToken = loginGoogleAccount(activity, false); // 如果获取认证令牌失败,打印错误日志并返回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"))) { // 构建自定义的URL 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"; // 尝试使用自定义URL登录Google Tasks if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; } } // 如果使用自定义URL登录失败,尝试使用Google官方URL登录 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; // 如果使用官方URL登录失败,返回false if (!tryToLoginGtask(activity, authToken)) { return false; } } // 登录成功,标记为已登录并返回true mLoggedin = true; return true; } // 登录Google账户获取认证令牌的方法 private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; // 获取账户管理器 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; // 查找与设置中账户名称相同的账户 for (Account a : accounts) { if (a.name.equals(accountName)) { account = a; break; } } // 如果没有找到匹配的账户,打印错误日志并返回null 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 Bundle authTokenBundle = accountManagerFuture.getResult(); // 从Bundle中获取认证令牌 authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 如果需要使令牌失效 if (invalidateToken) { // 使认证令牌失效 accountManager.invalidateAuthToken("com.google", authToken); // 重新登录获取新的认证令牌 loginGoogleAccount(activity, false); } } catch (Exception e) { // 如果获取认证令牌失败,打印错误日志并将authToken设为null Log.e(TAG, "get auth token failed"); authToken = null; } // 返回获取到的认证令牌 return authToken; } // 尝试登录Google Tasks服务的方法 private boolean tryToLoginGtask(Activity activity, String authToken) { // 首先尝试使用当前的认证令牌登录Google Tasks if (!loginGtask(authToken)) { // 如果登录失败,可能是认证令牌过期,使当前令牌失效并重新获取 authToken = loginGoogleAccount(activity, true); // 如果重新获取认证令牌失败 if (authToken == null) { // 记录错误日志,提示登录Google账户失败 Log.e(TAG, "login google account failed"); // 返回false表示登录失败 return false; } // 使用新获取的认证令牌再次尝试登录Google Tasks if (!loginGtask(authToken)) { // 记录错误日志,提示登录Google Tasks失败 Log.e(TAG, "login gtask failed"); // 返回false表示登录失败 return false; } } // 如果登录成功,返回true return true; } // 具体执行登录Google Tasks服务的方法 private boolean loginGtask(String authToken) { // 设置连接超时时间为10秒 int timeoutConnection = 10000; // 设置套接字超时时间为15秒 int timeoutSocket = 15000; // 创建一个HttpParams对象,用于设置HTTP请求的参数 HttpParams httpParameters = new BasicHttpParams(); // 设置连接超时参数 HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); // 设置套接字超时参数 HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); // 使用设置好参数的HttpParams创建一个DefaultHttpClient对象 mHttpClient = new DefaultHttpClient(httpParameters); // 创建一个BasicCookieStore对象,用于存储HTTP请求中的cookie BasicCookieStore localBasicCookieStore = new BasicCookieStore(); // 将创建的BasicCookieStore对象设置到HttpClient中 mHttpClient.setCookieStore(localBasicCookieStore); // 设置Http协议参数,不使用Expect-Continue机制 HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 开始登录Google Tasks try { // 构建登录URL,将认证令牌附加到URL中 String loginUrl = mGetUrl + "?auth=" + authToken; // 创建一个HttpGet请求对象,用于发送GET请求到登录URL HttpGet httpGet = new HttpGet(loginUrl); // 执行HttpGet请求并获取响应 HttpResponse response = null; response = mHttpClient.execute(httpGet); // 从HttpClient的cookie存储中获取所有的cookie List cookies = mHttpClient.getCookieStore().getCookies(); // 标记是否存在认证cookie boolean hasAuthCookie = false; // 遍历所有的cookie for (Cookie cookie : cookies) { // 如果cookie的名称包含"GTL",表示找到了认证cookie if (cookie.getName().contains("GTL")) { hasAuthCookie = true; } } // 如果没有找到认证cookie if (!hasAuthCookie) { // 记录警告日志,提示似乎没有认证cookie Log.w(TAG, "it seems that there is no auth cookie"); } // 获取响应内容 String resString = getResponseContent(response.getEntity()); // 定义用于提取JSON数据的起始和结束字符串 String jsBegin = "_setup("; String jsEnd = ")}"; // 查找响应内容中JSON数据的起始位置 int begin = resString.indexOf(jsBegin); // 查找响应内容中JSON数据的结束位置 int end = resString.lastIndexOf(jsEnd); // 用于存储提取的JSON字符串 String jsString = null; // 如果找到了起始和结束位置,并且起始位置在结束位置之前 if (begin!= -1 && end!= -1 && begin < end) { // 提取JSON字符串 jsString = resString.substring(begin + jsBegin.length(), end); } // 将提取的JSON字符串转换为JSONObject对象 JSONObject js = new JSONObject(jsString); // 从JSONObject中获取客户端版本号,并赋值给成员变量mClientVersion mClientVersion = js.getLong("v"); } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 返回false表示登录失败 return false; } catch (Exception e) { // 捕获所有其他异常 Log.e(TAG, "httpget gtask_url failed"); // 返回false表示登录失败 return false; } // 如果整个登录过程没有发生异常,返回true表示登录成功 return true; } // 获取并递增操作ID的方法 private int getActionId() { // 返回当前的操作ID,并将其自增1 return mActionId++; } // 创建一个HttpPost请求对象的方法 private HttpPost createHttpPost() { // 创建一个针对mPostUrl的HttpPost请求对象 HttpPost httpPost = new HttpPost(mPostUrl); // 设置请求头的Content-Type为application/x-www-form-urlencoded,字符编码为utf-8 httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); // 设置请求头的AT字段为1 httpPost.setHeader("AT", "1"); // 返回创建好的HttpPost请求对象 return httpPost; } // 从HttpEntity中获取响应内容的方法 private String getResponseContent(HttpEntity entity) throws IOException { // 用于存储响应内容的编码格式 String contentEncoding = null; // 如果HttpEntity有内容编码 if (entity.getContentEncoding()!= null) { // 获取内容编码的值 contentEncoding = entity.getContentEncoding().getValue(); // 记录日志,打印编码信息 Log.d(TAG, "encoding: " + contentEncoding); } // 获取HttpEntity的输入流 InputStream input = entity.getContent(); // 如果编码格式是gzip if (contentEncoding!= null && contentEncoding.equalsIgnoreCase("gzip")) { // 将输入流包装为GZIPInputStream,用于解压缩gzip格式的数据 input = new GZIPInputStream(entity.getContent()); } // 如果编码格式是deflate else if (contentEncoding!= null && contentEncoding.equalsIgnoreCase("deflate")) { // 创建一个Inflater对象,用于解压缩deflate格式的数据 Inflater inflater = new Inflater(true); // 将输入流包装为InflaterInputStream,用于解压缩deflate格式的数据 input = new InflaterInputStream(entity.getContent(), inflater); } try { // 使用InputStreamReader将输入流转换为字符流 InputStreamReader isr = new InputStreamReader(input); // 使用BufferedReader来读取字符流,提高读取效率 BufferedReader br = new BufferedReader(isr); // 创建一个StringBuilder用于拼接读取到的内容 StringBuilder sb = new StringBuilder(); // 循环读取每一行内容 while (true) { // 读取一行内容 String buff = br.readLine(); // 如果读取到的内容为null,说明已经读取到文件末尾 if (buff == null) { // 返回拼接好的内容 return sb.toString(); } // 将读取到的内容追加到StringBuilder中 sb = sb.append(buff); } } finally { // 确保无论是否发生异常,都关闭输入流,释放资源 input.close(); } } // 发送POST请求并返回JSON响应的方法 private JSONObject postRequest(JSONObject js) throws NetworkFailureException { // 如果尚未登录 if (!mLoggedin) { // 记录错误日志,提示需要先登录 Log.e(TAG, "please login first"); // 抛出ActionFailureException异常,提示未登录 throw new ActionFailureException("not logged in"); } // 创建一个HttpPost请求对象 HttpPost httpPost = createHttpPost(); try { // 创建一个LinkedList,用于存储请求参数 LinkedList list = new LinkedList(); // 将JSON对象转换为字符串,并添加到请求参数中 list.add(new BasicNameValuePair("r", js.toString())); // 将请求参数封装为UrlEncodedFormEntity,设置字符编码为UTF-8 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 将封装好的实体设置到HttpPost请求中 httpPost.setEntity(entity); // 执行POST请求,获取响应 HttpResponse response = mHttpClient.execute(httpPost); // 从响应的实体中获取响应内容 String jsString = getResponseContent(response.getEntity()); // 将响应内容转换为JSONObject对象并返回 return new JSONObject(jsString); } catch (ClientProtocolException e) { // 如果发生客户端协议异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出NetworkFailureException异常,提示postRequest失败 throw new NetworkFailureException("postRequest failed"); } catch (IOException e) { // 如果发生I/O异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出NetworkFailureException异常,提示postRequest失败 throw new NetworkFailureException("postRequest failed"); } catch (JSONException e) { // 如果发生JSON解析异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出ActionFailureException异常,提示无法将响应内容转换为JSONObject throw new ActionFailureException("unable to convert response content to jsonobject"); } catch (Exception e) { // 如果发生其他未知异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出ActionFailureException异常,提示发送请求时发生错误 throw new ActionFailureException("error occurs when posting request"); } } // 创建任务的方法,可能会抛出NetworkFailureException异常 public void createTask(Task task) throws NetworkFailureException { // 提交更新,可能是对之前的操作进行一些收尾或者准备工作 commitUpdate(); try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象,用于存储操作列表 JSONArray actionList = new JSONArray(); // 构建操作列表 // 获取任务的创建操作信息(包含操作ID等),并添加到actionList中 actionList.put(task.getCreateAction(getActionId())); // 将操作列表放入jsPost的"action_list"字段中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本号 // 将当前的客户端版本号放入jsPost的"client_version"字段中 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 // 发送包含操作列表和客户端版本号的POST请求,并获取响应 JSONObject jsResponse = postRequest(jsPost); // 从响应中获取结果 // 从响应的"results"数组中获取第一个结果,并转换为JSONObject JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 设置任务的全局唯一标识符(Gid) // 从结果中获取新生成的任务ID,并设置到任务对象中 task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示创建任务时处理JSONObject失败 throw new ActionFailureException("create task: handing jsonobject failed"); } } // 创建任务列表的方法,可能会抛出NetworkFailureException异常 public void createTaskList(TaskList tasklist) throws NetworkFailureException { // 提交更新,可能是对之前的操作进行一些收尾或者准备工作 commitUpdate(); try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象,用于存储操作列表 JSONArray actionList = new JSONArray(); // 构建操作列表 // 获取任务列表的创建操作信息(包含操作ID等),并添加到actionList中 actionList.put(tasklist.getCreateAction(getActionId())); // 将操作列表放入jsPost的"action_list"字段中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本号 // 将当前的客户端版本号放入jsPost的"client_version"字段中 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 // 发送包含操作列表和客户端版本号的POST请求,并获取响应 JSONObject jsResponse = postRequest(jsPost); // 从响应中获取结果 // 从响应的"results"数组中获取第一个结果,并转换为JSONObject JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 设置任务列表的全局唯一标识符(Gid) // 从结果中获取新生成的任务列表ID,并设置到任务列表对象中 tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示创建任务列表时处理JSONObject失败 throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } // 提交更新的方法,可能会抛出NetworkFailureException异常 public void commitUpdate() throws NetworkFailureException { // 如果mUpdateArray不为空,说明有更新内容需要提交 if (mUpdateArray!= null) { try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 设置操作列表 // 将包含更新操作的JSONArray放入jsPost的"action_list"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); // 设置客户端版本号 // 将当前的客户端版本号放入jsPost的"client_version"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 // 调用postRequest方法发送包含更新操作和客户端版本号的POST请求 postRequest(jsPost); // 清空mUpdateArray,表明更新已提交 mUpdateArray = null; } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示提交更新时处理JSONObject失败 throw new ActionFailureException("commit update: handing jsonobject failed"); } } } // 添加更新节点的方法,可能会抛出NetworkFailureException异常 public void addUpdateNode(Node node) throws NetworkFailureException { // 如果传入的节点不为空 if (node!= null) { // 为了避免更新项过多导致错误,设置最大更新项为10 if (mUpdateArray!= null && mUpdateArray.length() > 10) { // 如果更新数组中已有超过10个更新项,先提交当前的更新 commitUpdate(); } // 如果更新数组为空,创建一个新的JSONArray if (mUpdateArray == null) mUpdateArray = new JSONArray(); // 将节点的更新操作(包含操作ID)添加到更新数组中 mUpdateArray.put(node.getUpdateAction(getActionId())); } } // 移动任务的方法,可能会抛出NetworkFailureException异常 public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { // 提交之前的更新,确保在进行移动任务操作前,之前的所有更新都已处理 commitUpdate(); try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象,用于存储操作列表 JSONArray actionList = new JSONArray(); // 创建一个JSONObject对象,用于描述移动任务的操作 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()); // 如果任务的前一个父任务和当前父任务相同,且任务有前一个兄弟任务 if (preParent == curParent && task.getPriorSibling()!= null) { // 仅当在同一任务列表内移动且不是第一个任务时,设置前一个兄弟任务的ID action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling().getGid()); } // 设置源任务列表的ID action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); // 设置目标父任务列表的ID 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的"action_list"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 // 调用postRequest方法发送包含移动任务操作和客户端版本号的POST请求 postRequest(jsPost); } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示移动任务时处理JSONObject失败 throw new ActionFailureException("move task: handing jsonobject failed"); } } // 删除节点的方法,可能会抛出NetworkFailureException异常 public void deleteNode(Node node) throws NetworkFailureException { // 提交之前的更新,确保在进行删除操作前,之前的所有更新都已处理 commitUpdate(); try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象,用于存储操作列表 JSONArray actionList = new JSONArray(); // 设置操作列表 // 将节点标记为已删除 node.setDeleted(true); // 将节点的更新操作(包含操作ID)添加到操作列表中 actionList.put(node.getUpdateAction(getActionId())); // 将操作列表放入jsPost的"action_list"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本号 // 将当前的客户端版本号放入jsPost的"client_version"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); // 发送POST请求 // 调用postRequest方法发送包含删除操作和客户端版本号的POST请求 postRequest(jsPost); // 清空mUpdateArray,表明删除操作已提交 mUpdateArray = null; } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示删除节点时处理JSONObject失败 throw new ActionFailureException("delete node: handing jsonobject failed"); } } // 获取任务列表的方法,可能会抛出NetworkFailureException异常 public JSONArray getTaskLists() throws NetworkFailureException { // 如果尚未登录 if (!mLoggedin) { // 记录错误日志,提示需要先登录 Log.e(TAG, "please login first"); // 抛出ActionFailureException异常,提示未登录 throw new ActionFailureException("not logged in"); } try { // 创建一个HttpGet请求对象,用于获取任务列表信息 HttpGet httpGet = new HttpGet(mGetUrl); // 执行HttpGet请求并获取响应 HttpResponse response = null; response = mHttpClient.execute(httpGet); // 获取响应内容 String resString = getResponseContent(response.getEntity()); // 定义用于提取JSON数据的起始和结束字符串 String jsBegin = "_setup("; String jsEnd = ")}"; // 查找响应内容中JSON数据的起始位置 int begin = resString.indexOf(jsBegin); // 查找响应内容中JSON数据的结束位置 int end = resString.lastIndexOf(jsEnd); // 用于存储提取的JSON字符串 String jsString = null; // 如果找到了起始和结束位置,并且起始位置在结束位置之前 if (begin!= -1 && end!= -1 && begin < end) { // 提取JSON字符串 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) { // 如果发生客户端协议异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出NetworkFailureException异常,提示获取任务列表时HttpGet请求失败 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { // 如果发生I/O异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出NetworkFailureException异常,提示获取任务列表时HttpGet请求失败 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { // 如果发生JSON解析异常 Log.e(TAG, e.toString()); // 打印异常堆栈信息,方便调试 e.printStackTrace(); // 抛出ActionFailureException异常,提示获取任务列表时处理JSONObject失败 throw new ActionFailureException("get task lists: handing jasonobject failed"); } } // 根据任务列表的全局唯一标识符(Gid)获取任务列表中所有任务的方法,可能会抛出NetworkFailureException异常 public JSONArray getTaskList(String listGid) throws NetworkFailureException { // 提交之前的更新,确保在进行获取任务列表操作前,之前的所有更新都已处理 commitUpdate(); try { // 创建一个用于POST请求的JSONObject对象 JSONObject jsPost = new JSONObject(); // 创建一个JSONArray对象,用于存储操作列表 JSONArray actionList = new JSONArray(); // 创建一个JSONObject对象,用于描述获取任务列表中所有任务的操作 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()); // 设置要获取任务的任务列表的Gid action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); // 设置不获取已删除的任务 action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 将描述操作的JSONObject添加到操作列表中 actionList.put(action); // 将操作列表放入jsPost的"action_list"字段 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置客户端版本号 // 将当前的客户端版本号放入jsPost的"client_version"字段 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异常,提示获取任务列表时处理JSONObject失败 throw new ActionFailureException("get task list: handing jsonobject failed"); } } // 获取当前同步账户的方法 public Account getSyncAccount() { // 返回当前存储的同步账户对象 return mAccount; } // 重置更新数组的方法 public void resetUpdateArray() { // 将存储更新操作的JSONArray对象设置为null,即清空更新数组 mUpdateArray = null; }}