You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
text123/GTaskClient.java

824 lines
36 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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<Bundle> 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<Cookie> 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 = ")}</script>";
// 查找响应内容中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<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
// 将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 = ")}</script>";
// 查找响应内容中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;
}}