|  |  | /*
 | 
						
						
						
							|  |  |  * 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;
 | 
						
						
						
							|  |  | } |