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.
xiaomi-Notes/GTaskClient.java

767 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 API进行交互的客户端。
public class GTaskClient {
// 定义一个静态常量TAG用于日志记录其值为当前类的简单名称。
private static final String TAG = GTaskClient.class.getSimpleName();
// 定义一个静态常量GTASK_URL表示Google Tasks服务的基础URL。
private static final String GTASK_URL = "https://mail.google.com/tasks/";
// 定义一个静态常量GTASK_GET_URL表示用于获取任务的URL。
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
// 定义一个静态常量GTASK_POST_URL表示用于发布添加/更新任务的URL。
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
// 定义一个静态的GTaskClient实例mInstance用于实现单例模式。
private static GTaskClient mInstance = null;
// 定义一个DefaultHttpClient实例mHttpClient用于发送HTTP请求。
private DefaultHttpClient mHttpClient;
// 定义一个字符串mGetUrl存储用于获取任务的URL。
private String mGetUrl;
// 定义一个字符串mPostUrl存储用于发布任务的URL。
private String mPostUrl;
// 定义一个长整型mClientVersion用于存储客户端版本信息初始化为-1表示未设置。
private long mClientVersion;
// 定义一个布尔型mLoggedin用于标记用户是否已登录初始化为false。
private boolean mLoggedin;
// 定义一个长整型mLastLoginTime用于存储用户最后登录的时间初始化为0。
private long mLastLoginTime;
// 定义一个整型mActionId可能用于标识请求或操作的ID初始化为1。
private int mActionId;
// 定义一个Account类型的mAccount用于存储用户账户信息初始化为null。
private Account mAccount;
// 定义一个JSONArray类型的mUpdateArray可能用于存储需要更新的任务信息初始化为null。
private JSONArray mUpdateArray;
// 私有的构造方法防止外部直接创建GTaskClient实例这是实现单例模式的一部分。
private GTaskClient() {
mHttpClient = null; // 初始化mHttpClient为null可能后续会有具体的初始化逻辑。
mGetUrl = GTASK_GET_URL; // 将mGetUrl设置为预定义的获取任务URL。
mPostUrl = GTASK_POST_URL; // 将mPostUrl设置为预定义的发布任务URL。
mClientVersion = -1; // 初始化客户端版本号为-1。
mLoggedin = false; // 初始化登录状态为未登录。
mLastLoginTime = 0; // 初始化最后登录时间为0。
mActionId = 1; // 初始化操作ID为1。
mAccount = null; // 初始化账户信息为null。
mUpdateArray = null; // 初始化更新数组为null。
}
// 定义一个公开的静态同步方法getInstance用于获取GTaskClient的实例。
// 这个方法实现了单例模式确保整个应用中只有一个GTaskClient实例。
public static synchronized GTaskClient getInstance() {
if (mInstance == null) { // 如果实例为null则创建一个新的实例。
mInstance = new GTaskClient();
}
return mInstance; // 返回GTaskClient实例。
}
// 定义一个公开的方法login它接受一个Activity作为参数返回一个布尔值表示登录是否成功。
public boolean login(Activity activity) {
// 定义一个常量interval表示登录有效期为5分钟以毫秒为单位
final long interval = 1000 * 60 * 5;
// 如果上次登录时间加上有效期小于当前时间则认为登录已过期将登录状态设置为false。
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
// 如果当前已登录但账户名称与NotesPreferenceActivity中获取的同步账户名称不匹配
// 则认为需要重新登录将登录状态设置为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();
// 调用loginGoogleAccount方法尝试登录Google账户获取认证令牌。
String authToken = loginGoogleAccount(activity, false);
// 如果认证令牌为null表示登录Google账户失败打印错误日志并返回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"))) {
// 构造自定义域的Google Tasks 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;
}
}
// 如果使用自定义域登录失败则尝试使用Google官方的URL进行登录。
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
// 尝试使用Google官方的URL和认证令牌登录Google Tasks。
if (!tryToLoginGtask(activity, authToken)) {
// 如果登录失败则返回false。
return false;
}
}
// 将登录状态设置为true并返回true表示登录成功。
mLoggedin = true;
return true;
}
// 定义一个私有方法loginGoogleAccount它接受一个Activity对象和一个布尔值invalidateToken作为参数
// 返回一个字符串表示的Google账户认证令牌。
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
// 声明一个字符串变量authToken用于存储认证令牌。
String authToken;
// 通过Activity获取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或authTokenType
// 它需要与你的Google API项目配置相匹配。然而"goanna_mobile"并不是一个标准的Google API scope
// 这里可能是示例代码或特定应用的自定义值。
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
// 从Future中获取结果这个结果是一个Bundle包含了认证令牌等信息。
Bundle authTokenBundle = accountManagerFuture.getResult();
// 从Bundle中提取认证令牌。
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
// 如果invalidateToken为true则使当前认证令牌失效并递归调用loginGoogleAccount方法重新获取令牌。
// 注意这里的递归调用可能会导致无限循环如果invalidateToken始终为true且没有额外的退出条件。
// 在实际应用中,通常不会在获取令牌后立即使其失效,除非有特定的需求。
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false);
}
} catch (Exception e) {
// 如果在获取认证令牌的过程中发生异常则打印错误日志并将authToken设置为null。
Log.e(TAG, "get auth token failed");
authToken = null;
}
// 返回认证令牌。如果获取失败则为null。
return authToken;
}
// 定义一个私有方法tryToLoginGtask它尝试使用提供的authToken登录到Google TasksGtask
// 如果登录失败,它会尝试使令牌失效并重新获取一个新的令牌,然后再次尝试登录。
private boolean tryToLoginGtask(Activity activity, String authToken) {
// 首先尝试使用提供的authToken登录Gtask。
if (!loginGtask(authToken)) {
// 如果登录失败可能是因为authToken已经过期。
// 接下来,我们将使该令牌失效,并尝试重新获取一个新的令牌。
authToken = loginGoogleAccount(activity, true);
// 如果重新获取令牌失败即返回null则打印错误日志并返回false。
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// 使用新获取的authToken再次尝试登录Gtask。
if (!loginGtask(authToken)) {
// 如果仍然登录失败则打印错误日志并返回false。
Log.e(TAG, "login gtask failed");
return false;
}
}
// 如果登录成功无论是初次尝试还是重新获取令牌后的尝试则返回true。
return true;
}
// 定义一个私有方法loginGtask它使用提供的authToken尝试登录到Google Tasks。
private boolean loginGtask(String authToken) {
// 设置HTTP连接的超时时间10秒和套接字超时时间15秒
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
// 创建一个HttpClient实例并使用前面设置的参数进行配置。
mHttpClient = new DefaultHttpClient(httpParameters);
// 创建一个CookieStore实例并将其设置到HttpClient中以便管理HTTP cookies。
BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore);
// 禁用HTTP/1.1的"Expect: 100-continue"机制,以提高性能。
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// 构造登录Google Tasks的URL其中包含authToken作为查询参数。
String loginUrl = mGetUrl + "?auth=" + authToken;
// 创建一个HttpGet请求用于向登录URL发送请求。
HttpGet httpGet = new HttpGet(loginUrl);
// 执行HttpGet请求并获取响应。
HttpResponse response = null;
try {
response = mHttpClient.execute(httpGet);
} catch (Exception e) {
// 如果在执行请求时发生异常则打印错误日志并返回false。
Log.e(TAG, "httpget gtask_url failed");
return false;
}
// 从HttpClient的CookieStore中获取所有的cookies。
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
// 遍历所有的cookies检查是否存在名为"GTL"的认证cookie。
for (Cookie cookie : cookies) {
if (cookie.getName().contains("GTL")) {
hasAuthCookie = true;
}
}
// 如果没有找到认证cookie则打印警告日志。
if (!hasAuthCookie) {
Log.w(TAG, "it seems that there is no auth cookie");
}
// 从HTTP响应中获取响应体的内容。
String resString = getResponseContent(response.getEntity());
// 定义JavaScript字符串的开始和结束标记用于从响应内容中提取JavaScript代码。
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
// 在响应内容中查找JavaScript代码的开始和结束位置。
int begin = resString.indexOf(jsBegin);
int end = resString.lastIndexOf(jsEnd);
// 如果找到了JavaScript代码的开始和结束位置并且它们是有效的则提取JavaScript代码。
String jsString = null;
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
// 将提取的JavaScript代码解析为JSONObject并尝试从中获取客户端版本信息。
try {
JSONObject js = new JSONObject(jsString);
mClientVersion = js.getLong("v");
} catch (JSONException e) {
// 如果在解析JSON时发生异常则打印错误日志打印堆栈跟踪并返回false。
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
} catch (Exception e) {
// 简单地捕获所有其他异常这里应该是不会发生的因为前面已经对jsString进行了有效性检查
// 但为了代码的健壮性还是打印错误日志并返回false。
Log.e(TAG, "Unexpected exception");
return false;
}
// 如果一切顺利则返回true表示登录成功。
return true;
}
// 定义一个私有方法用于获取一个动作ID并每次调用时自增
private int getActionId() {
return mActionId++; // 返回当前的动作ID并将成员变量mActionId的值自增
}
// 定义一个私有方法用于创建一个HttpPost对象
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl); // 使用成员变量mPostUrl创建HttpPost对象
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=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); // 打印日志,显示内容编码
}
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); // 创建一个Inflater对象用于解压缩
input = new InflaterInputStream(entity.getContent(), inflater); // 使用InflaterInputStream包装输入流
}
try {
InputStreamReader isr = new InputStreamReader(input); // 创建输入流读取器
BufferedReader br = new BufferedReader(isr); // 创建缓冲读取器
StringBuilder sb = new StringBuilder(); // 创建字符串构建器,用于拼接读取的行
while (true) { // 循环读取输入流中的每一行
String buff = br.readLine(); // 读取一行
if (buff == null) { // 如果读取到null表示已到达输入流的末尾
return sb.toString(); // 返回拼接好的字符串
}
sb = sb.append(buff); // 将读取的行追加到字符串构建器中
}
} finally {
input.close(); // 关闭输入流
}
}
// 定义一个私有方法用于发送POST请求并返回JSON响应
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<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>(); // 创建一个链表,用于存储键值对
list.add(new BasicNameValuePair("r", js.toString())); // 将JSON对象转换为字符串并添加到链表中键为"r"
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 创建UrlEncodedFormEntity对象用于POST请求的数据体
httpPost.setEntity(entity); // 设置HttpPost的数据体
// 执行POST请求
HttpResponse response = mHttpClient.execute(httpPost); // 使用HttpClient执行POST请求
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) { // 如果发生IO异常
Log.e(TAG, e.toString()); // 打印错误日志
e.printStackTrace(); // 打印异常堆栈
throw new NetworkFailureException("postRequest failed"); // 抛出网络请求失败的异常
} catch (JSONException e) { // 如果发生JSON解析异常
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"); // 抛出发送请求时发生错误的异常
}
}
// 定义一个方法,用于创建任务,可能会抛出网络失败异常
public void createTask(Task task) throws NetworkFailureException {
// 提交所有待处理的更新到服务器
commitUpdate();
try {
// 创建一个JSON对象用于POST请求
JSONObject jsPost = new JSONObject();
// 创建一个JSON数组用于存放动作列表
JSONArray actionList = new JSONArray();
// 向动作列表中添加创建任务的动作getActionId()可能是获取一个唯一的动作ID
actionList.put(task.getCreateAction(getActionId()));
// 将动作列表添加到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 添加客户端版本号到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 发送POST请求到服务器并接收响应
JSONObject jsResponse = postRequest(jsPost);
// 从响应结果中获取第一个结果对象
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处理异常记录错误日志
Log.e(TAG, e.toString());
e.printStackTrace();
// 抛出自定义的动作失败异常
throw new ActionFailureException("create task: handing jsonobject failed");
}
}
// 定义一个方法,用于创建任务列表,可能会抛出网络失败异常
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
// 提交所有待处理的更新到服务器
commitUpdate();
try {
// 创建一个JSON对象用于POST请求
JSONObject jsPost = new JSONObject();
// 创建一个JSON数组用于存放动作列表
JSONArray actionList = new JSONArray();
// 向动作列表中添加创建任务列表的动作getActionId()可能是获取一个唯一的动作ID
actionList.put(tasklist.getCreateAction(getActionId()));
// 将动作列表添加到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 添加客户端版本号到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 发送POST请求到服务器并接收响应
JSONObject jsResponse = postRequest(jsPost);
// 从响应结果中获取第一个结果对象
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
// 从结果对象中获取新创建任务列表的ID并设置给任务列表对象
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
// 捕获JSON处理异常记录错误日志
Log.e(TAG, e.toString());
e.printStackTrace();
// 抛出自定义的动作失败异常
throw new ActionFailureException("create tasklist: handing jsonobject failed");
}
}
// 定义一个方法,用于提交所有待处理的更新到服务器,可能会抛出网络失败异常
public void commitUpdate() throws NetworkFailureException {
// 如果存在待处理的更新
if (mUpdateArray != null) {
try {
// 创建一个JSON对象用于POST请求
JSONObject jsPost = new JSONObject();
// 将待处理的更新动作列表添加到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// 添加客户端版本号到JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 发送POST请求到服务器不需要接收响应可能是只需要服务器处理更新
postRequest(jsPost);
// 清空待处理的更新数组
mUpdateArray = null;
} catch (JSONException e) {
// 捕获JSON处理异常记录错误日志
Log.e(TAG, e.toString());
e.printStackTrace();
// 抛出自定义的动作失败异常
throw new ActionFailureException("commit update: handing jsonobject failed");
}
}
}
// 定义一个方法,用于添加或更新节点,如果节点不为空
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 {
// 创建一个JSON对象用于POST请求
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这里似乎有些重复可能是特定逻辑需要
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) {
// 记录异常并抛出自定义异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("move task: handing jsonobject failed");
}
}
// 定义一个方法,用于删除节点
public void deleteNode(Node node) throws NetworkFailureException {
// 在删除节点前提交所有待处理的更新
commitUpdate();
try {
// 创建一个JSON对象用于POST请求
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) {
// 记录异常并抛出自定义异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("delete node: handing jsonobject failed");
}
}
// 定义一个方法,用于获取任务列表
public JSONArray getTaskLists() throws NetworkFailureException {
// 如果未登录,则抛出异常
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
try {
// 创建一个HttpGet请求
HttpGet httpGet = new HttpGet(mGetUrl);
// 执行请求并获取响应
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);
int end = resString.lastIndexOf(jsEnd);
String jsString = null;
// 如果找到了开始和结束位置则提取JSON字符串
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
// 将JSON字符串转换为JSON对象
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 JSONArray getTaskList(String listGid) throws NetworkFailureException {
// 提交更新操作可能是在执行网络请求前进行一些准备工作比如更新UI或检查状态
commitUpdate();
try {
// 创建一个JSONObject对象用于构建要发送的JSON数据
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具体ID通过getActionId()方法获取
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
// 设置列表的全局唯一标识符
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);
// 设置是否获取已删除的任务这里设置为false即不获取
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
// 将动作对象添加到动作列表中
actionList.put(action);
// 将动作列表添加到要发送的JSON数据中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 在要发送的JSON数据中设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 发送POST请求并接收响应的JSONObject对象
JSONObject jsResponse = postRequest(jsPost);
// 从响应的JSONObject对象中获取任务列表的JSONArray对象并返回
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);
} catch (JSONException e) {
// 捕获JSON处理异常记录错误日志
Log.e(TAG, e.toString());
// 打印堆栈跟踪信息
e.printStackTrace();
// 抛出动作失败异常说明在处理JSON对象时出错
throw new ActionFailureException("get task list: handing jsonobject failed");
}
}
// 定义一个方法,用于获取同步账户对象
public Account getSyncAccount() {
// 返回账户对象
return mAccount;
}
// 定义一个方法,用于重置更新数组
public void resetUpdateArray() {
// 将更新数组设置为null可能用于清空或重置状态
mUpdateArray = null;
}