|
|
/*
|
|
|
* 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 Tasks(Gtask)。
|
|
|
// 如果登录失败,它会尝试使令牌失效并重新获取一个新的令牌,然后再次尝试登录。
|
|
|
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;
|
|
|
} |