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.
read/src/net/micode/notes/tool/BackupUtils.java

819 lines
39 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.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class BackupUtils {
private static final String TAG = "BackupUtils"; // 定义日志标签,用于识别日志输出的来源
private static BackupUtils sInstance; // 声明一个静态实例变量,用于实现单例模式
// 提供一个公共的静态方法用于获取BackupUtils的单例实例
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context); // 如果实例不存在,则新建一个实例
}
return sInstance; // 返回单例实例
}
// 定义一系列状态码,用于表示备份或恢复的状态
public static final int STATE_SD_CARD_UNMOUONTED = 0; // SD卡未挂载
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; // 备份文件不存在
public static final int STATE_DATA_DESTROIED = 2; // 数据格式不正确,可能被其他程序更改
public static final int STATE_SYSTEM_ERROR = 3; // 系统错误,导致备份或恢复失败
public static final int STATE_SUCCESS = 4; // 备份或恢复成功
private TextExport mTextExport; // TextExport对象用于将数据导出到文本
// BackupUtils的私有构造函数传入Context对象
private BackupUtils(Context context) {
mTextExport = new TextExport(context); // 初始化TextExport对象
}
// 检查外部存储SD卡是否可用
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); // 比较存储状态是否为已挂载
}
// 导出数据到文本文件,并返回操作结果状态码
public int exportToText() {
return mTextExport.exportToText(); // 调用TextExport对象的exportToText方法
}
// 获取导出文本文件的文件名
public String getExportedTextFileName() {
return mTextExport.mFileName; // 返回TextExport对象中保存的文件名
}
// 获取导出文本文件的目录路径
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory; // 返回TextExport对象中保存的文件目录
}
// 内部类TextExport封装了将笔记数据导出到文本文件的逻辑
private static class TextExport {
// 定义查询笔记信息时需要的字段
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 定义NOTE_PROJECTION数组中各个字段的索引
private static final int NOTE_COLUMN_ID = 0;
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
private static final int NOTE_COLUMN_SNIPPET = 2;
// 定义查询笔记数据时需要的字段
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 定义DATA_PROJECTION数组中各个字段的索引
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
private static final int DATA_COLUMN_CALL_DATE = 2;
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
// 用于格式化导出文本的字符串数组
private final String[] TEXT_FORMAT;
// 定义格式化字符串数组中的索引
private static final int FORMAT_FOLDER_NAME = 0;
private static final int FORMAT_NOTE_DATE = 1;
private static final int FORMAT_NOTE_CONTENT = 2;
private Context mContext; // 应用程序上下文
private String mFileName; // 导出文本文件的文件名
private String mFileDirectory; // 导出文本文件的目录
// TextExport类的构造函数初始化上下文和格式化字符串数组
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); // 从资源文件中获取格式化字符串数组
mContext = context; // 保存上下文对象
mFileName = ""; // 初始化文件名为空字符串
mFileDirectory = ""; // 初始化文件目录为空字符串
}
// 根据索引获取格式化字符串
private String getFormat(int id) {
return TEXT_FORMAT[id]; // 返回格式化字符串数组中指定索引的字符串
}
// 导出指定文件夹ID的笔记到文本
private void exportFolderToText(String folderId, PrintStream ps) {
// 查询属于该文件夹的笔记
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// 打印笔记的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于该笔记的数据
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close(); // 关闭游标
}
}
// 导出指定ID的笔记到文本
private void exportNoteToText(String noteId, PrintStream ps) {
// 查询笔记数据
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// 如果MIME类型为通话笔记则打印电话号码、通话日期和位置
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
// 如果MIME类型为普通笔记则打印笔记内容
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close(); // 关闭游标
}
// 打印笔记之间的分隔符
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
// 执行导出操作,将笔记数据导出为用户可读的文本格式
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted"); // 如果SD卡未挂载则记录日志并返回状态码
return STATE_SD_CARD_UNMOUONTED;
}
PrintStream ps = getExportToTextPrintStream(); // 获取指向导出文本文件的PrintStream对象
if (ps == null) {
Log.e(TAG, "get print stream error"); // 如果获取PrintStream失败则记录日志并返回状态码
return STATE_SYSTEM_ERROR;
}
// 查询所有文件夹和其笔记,除了垃圾箱文件夹和通话记录文件夹
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// 打印文件夹名称
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name); // 如果是通话记录文件夹,则使用资源文件中的名称
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); // 否则,使用查询到的文件夹名称
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId
package net.micode.notes.gtask.remote; // 定义当前类所在的包
import android.accounts.Account; // 引入账户类,表示一个用户账户
import android.accounts.AccountManager; // 导入账户管理类,用于管理账户及其相关操作
import android.accounts.AccountManagerFuture; // 导入异步获取账户的类
import android.app.Activity; // 引入Activity类代表应用的一个界面
import android.os.Bundle; // 引入Bundle类用于存储数据的容器
import android.text.TextUtils; // 导入工具类,主要用于判断字符串是否为空
import android.util.Log; // 导入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; // 引入HTTP实体类表示请求或响应的消息体
import org.apache.http.HttpResponse; // 引入HTTP响应类表示HTTP请求的响应
import org.apache.http.client.ClientProtocolException; // 引入客户端协议异常类
import org.apache.http.client.entity.UrlEncodedFormEntity; // 引入URL编码的表单实体类
import org.apache.http.client.methods.HttpGet; // 引入HTTP GET请求类
import org.apache.http.client.methods.HttpPost; // 引入HTTP POST请求类
import org.apache.http.cookie.Cookie; // 引入Cookie类表示HTTP Cookie
import org.apache.http.impl.client.BasicCookieStore; // 引入基本的Cookie存储类
import org.apache.http.impl.client.DefaultHttpClient; // 引入默认的HTTP客户端类
import org.apache.http.message.BasicNameValuePair; // 引入基本名称值对类,用于表单参数
import org.apache.http.params.BasicHttpParams; // 引入基本HTTP参数类
import org.apache.http.params.HttpConnectionParams; // 引入HTTP连接参数类
import org.apache.http.params.HttpParams; // 引入HTTP参数类
import org.apache.http.params.HttpProtocolParams; // 引入HTTP协议参数类
import org.json.JSONArray; // 引入JSON数组类
import org.json.JSONException; // 引入JSON异常类
import org.json.JSONObject; // 引入JSON对象类
import java.io.BufferedReader; // 引入缓冲读取器类
import java.io.IOException; // 引入IO异常类
import java.io.InputStream; // 引入输入流类
import java.io.InputStreamReader; // 引入输入流阅读器类
import java.util.LinkedList; // 引入链表类
import java.util.List; // 引入列表接口
import java.util.zip.GZIPInputStream; // 引入GZIP输入流类
import java.util.zip.Inflater; // 引入解压缩类
import java.util.zip.InflaterInputStream; // 引入解压缩输入流类
// GTaskClient类负责与Google任务系统的网络交互
public class GTaskClient {
private static final String TAG = GTaskClient.class.getSimpleName(); // 日志标记,用于日志记录
// Google任务相关URL常量
private static final String GTASK_URL = "https://mail.google.com/tasks/";
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
private static GTaskClient mInstance = null; // 单例实例
private DefaultHttpClient mHttpClient; // 默认HTTP客户端
private String mGetUrl; // GET请求URL
private String mPostUrl; // POST请求URL
private long mClientVersion; // 客户端版本号
private boolean mLoggedin; // 登录状态
private long mLastLoginTime; // 上次登录时间
private int mActionId; // 动作ID
private Account mAccount; // 当前账户信息
private JSONArray mUpdateArray; // 更新操作数组
// 私有构造函数初始化GTaskClient实例
private GTaskClient() {
mHttpClient = null;
mGetUrl = GTASK_GET_URL; // 初始化GET请求URL
mPostUrl = GTASK_POST_URL; // 初始化POST请求URL
mClientVersion = -1; // 初始化客户端版本
mLoggedin = false; // 初始化未登录状态
mLastLoginTime = 0; // 初始化上次登录时间
mActionId = 1; // 初始化动作ID
mAccount = null; // 初始化账户信息
mUpdateArray = null; // 初始化更新数组
}
// 获取GTaskClient单例实例
public static synchronized GTaskClient getInstance() {
if (mInstance == null) {
mInstance = new GTaskClient(); // 创建新实例
}
return mInstance; // 返回单例实例
}
// 登录方法
public boolean login(Activity activity) {
// 假设cookie在5分钟后过期需要重新登录
final long interval = 1000 * 60 * 5;
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false; // 登录过期
}
// 当账户切换时需要重新登录
if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity.getSyncAccountName(activity))) {
mLoggedin = false; // 切换账户后未登录
}
if (mLoggedin) {
Log.d(TAG, "already logged in"); // 已经登录
return true; // 返回登录成功
}
mLastLoginTime = System.currentTimeMillis(); // 更新最后登录时间
String authToken = loginGoogleAccount(activity, false); // 登录Google账户
if (authToken == null) {
Log.e(TAG, "login google account failed"); // 登录失败
return false; // 返回登录失败
}
// 登录自定义域名
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase().endsWith("googlemail.com"))) {
// 构建URL
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
int index = mAccount.name.indexOf('@') + 1;
String suffix = mAccount.name.substring(index);
url.append(suffix + "/");
mGetUrl = url.toString() + "ig"; // 更新GET请求URL
mPostUrl = url.toString() + "r/ig"; // 更新POST请求URL
// 尝试以自定义域名登录
if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true; // 登录成功
}
}
// 尝试使用Google官方URL登录
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL; // 恢复默认GET请求URL
mPostUrl = GTASK_POST_URL; // 恢复默认POST请求URL
if (!tryToLoginGtask(activity, authToken)) { // 尝试登录
return false; // 登录失败
}
}
mLoggedin = true; // 登录状态更新
return true; // 返回登录成功
}
// 登录Google账户
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken; // 认证令牌
AccountManager accountManager = AccountManager.get(activity); // 获取账户管理器
Account[] accounts = accountManager.getAccountsByType("com.google"); // 获取Google账户
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account"); // 没有可用的Google账户
return null; // 返回null
}
String accountName = NotesPreferenceActivity.getSyncAccountName(activity); // 获取预设的账户名
Account account = null;
for (Account a : accounts) {
if (a.name.equals(accountName)) { // 查找匹配的账户
account = a;
break;
}
}
if (account != null) {
mAccount = account; // 设置当前账户
} else {
Log.e(TAG, "unable to get an account with the same name in the settings"); // 未找到匹配的账户
return null; // 返回null
}
// 现在获取token
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); // 获取账户的认证令牌
try {
Bundle authTokenBundle = accountManagerFuture.getResult(); // 获取结果
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 提取认证令牌
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken); // 使令牌失效
loginGoogleAccount(activity, false); // 重新登录
}
} catch (Exception e) {
Log.e(TAG, "get auth token failed"); // 获取令牌失败
authToken = null; // 令牌为null
}
return authToken; // 返回认证令牌
}
// 尝试登录GTask
private boolean tryToLoginGtask(Activity activity, String authToken) {
if (!loginGtask(authToken)) { // 登录GTask
// 可能认证令牌过期,令牌失效后重试
authToken = loginGoogleAccount(activity, true); // 重新获取令牌
if (authToken == null) {
Log.e(TAG, "login google account failed"); // 登录失败
return false; // 返回失败
}
if (!loginGtask(authToken)) { // 重试登录GTask
Log.e(TAG, "login gtask failed"); // 登录GTask失败
return false; // 返回失败
}
}
return true; // 登录成功
}
// 登录GTask
private boolean loginGtask(String authToken) {
int timeoutConnection = 10000; // 连接超时设置
int timeoutSocket = 15000; // 套接字超时设置
HttpParams httpParameters = new BasicHttpParams(); // 创建HTTP参数
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); // 设置连接超时
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); // 设置套接字超时
mHttpClient = new DefaultHttpClient(httpParameters); // 初始化HTTP客户端
BasicCookieStore localBasicCookieStore = new BasicCookieStore(); // 初始化Cookie存储
mHttpClient.setCookieStore(localBasicCookieStore); // 设置Cookie存储
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 禁用ExpectContinue
// 登录GTask
try {
String loginUrl = mGetUrl + "?auth=" + authToken; // 构建登录URL
HttpGet httpGet = new HttpGet(loginUrl); // 创建GET请求
HttpResponse response = null;
response = mHttpClient.execute(httpGet); // 执行GET请求
// 获取Cookie
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies(); // 获取当前Cookie
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
if (cookie.getName().contains("GTL")) { // 检查是否存在授权Cookie
hasAuthCookie = true;
}
}
if (!hasAuthCookie) {
Log.w(TAG, "it seems that there is no auth cookie"); // 授权Cookie不存在
}
// 获取客户端版本
String resString = getResponseContent(response.getEntity()); // 获取响应内容
String jsBegin = "_setup("; // JavaScript开始标识
String jsEnd = ")}</script>"; // JavaScript结束标识
int begin = resString.indexOf(jsBegin); // 查找开始索引
int end = resString.lastIndexOf(jsEnd); // 查找结束索引
String jsString = null;
if (begin != -1 && end != -1 && begin < end) { // 提取JavaScript内容
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString); // 创建JSON对象
mClientVersion = js.getLong("v"); // 获取客户端版本
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON解析异常
e.printStackTrace(); // 打印异常堆栈
return false; // 返回失败
} catch (Exception e) {
// 捕获所有异常
Log.e(TAG, "httpget gtask_url failed"); // GET请求失败
return false; // 返回失败
}
return true; // 登录成功
}
// 获取下一个动作ID
private int getActionId() {
return mActionId++; // 返回并自增动作ID
}
// 创建HTTP POST请求
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl); // 创建POST请求
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); // 设置请求头
httpPost.setHeader("AT", "1"); // 设置自定义请求头
return httpPost; // 返回POST请求
}
// 获取响应内容
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null; // 内容编码
if (entity.getContentEncoding() != null) {
contentEncoding = entity.getContentEncoding().getValue(); // 获取编码类型
Log.d(TAG, "encoding: " + contentEncoding); // 记录编码类型
}
InputStream input = entity.getContent(); // 获取输入流
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
input = new GZIPInputStream(entity.getContent()); // 解压GZIP流
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {
Inflater inflater = new Inflater(true); // 创建Inflater对象
input = new InflaterInputStream(entity.getContent(), inflater); // 解压DEFLATE流
}
try {
InputStreamReader isr = new InputStreamReader(input); // 创建输入流阅读器
BufferedReader br = new BufferedReader(isr); // 创建缓冲读取器
StringBuilder sb = new StringBuilder(); // 创建字符串构建器
while (true) {
String buff = br.readLine(); // 逐行读取
if (buff == null) {
return sb.toString(); // 返回读取的内容
}
sb = sb.append(buff); // 将内容添加到构建器
}
} finally {
input.close(); // 关闭输入流
}
}
// POST请求方法
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first"); // 未登录
throw new ActionFailureException("not logged in"); // 抛出异常
}
HttpPost httpPost = createHttpPost(); // 创建POST请求
try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>(); // 创建参数列表
list.add(new BasicNameValuePair("r", js.toString())); // 添加请求参数
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); // 创建表单实体
httpPost.setEntity(entity); // 设置POST实体
// 执行POST请求
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity()); // 获取响应内容
return new JSONObject(jsString); // 返回JSON对象
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString()); // 记录协议异常
e.printStackTrace(); // 打印异常堆栈
throw new NetworkFailureException("postRequest failed"); // 抛出网络失败异常
} catch (IOException e) {
Log.e(TAG, e.toString()); // 记录IO异常
e.printStackTrace(); // 打印异常堆栈
throw new NetworkFailureException("postRequest failed"); // 抛出网络失败异常
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("unable to convert response content to 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 {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表数组
// 添加创建动作到动作列表
actionList.put(task.getCreateAction(getActionId())); // 获取创建动作并添加
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置动作列表
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 执行POST请求
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 获取结果
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务ID
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("create task: handing jsonobject failed"); // 抛出操作失败异常
}
}
// 创建任务列表方法
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate(); // 提交更新
try {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表数组
// 添加创建动作到动作列表
actionList.put(tasklist.getCreateAction(getActionId())); // 获取创建动作并添加
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置动作列表
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 执行POST请求
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_RESULTS).get(0); // 获取结果
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); // 设置任务列表ID
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("create tasklist: handing jsonobject failed"); // 抛出操作失败异常
}
}
// 提交更新方法
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) { // 如果有更新数组
try {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
// 设置动作列表
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); // 执行POST请求
mUpdateArray = null; // 清空更新数组
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
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 {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表
JSONObject action = new JSONObject(); // 创建动作对象
// 设置移动任务的动作
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置动作ID
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); // 设置任务ID
if (preParent == curParent && task.getPriorSibling() != null) {
// 仅当在任务列表内移动且不是第一个时设置前一个兄弟节点ID
action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());
}
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); // 设置源列表ID
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); // 设置目标父任务列表ID
if (preParent != curParent) {
// 仅当在不同任务列表间移动时设置目标列表ID
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
}
actionList.put(action); // 将动作添加到动作列表
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置动作列表
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); // 执行POST请求
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("move task: handing jsonobject failed"); // 抛出操作失败异常
}
}
// 删除节点方法
public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate(); // 提交更新
try {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表
// 删除节点
node.setDeleted(true); // 设置节点为已删除
actionList.put(node.getUpdateAction(getActionId())); // 添加更新动作到列表
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置动作列表
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); // 执行POST请求
mUpdateArray = null; // 清空更新数组
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
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 = new HttpGet(mGetUrl); // 创建GET请求
HttpResponse response = null; // 声明响应对象
response = mHttpClient.execute(httpGet); // 执行GET请求并获取响应
// 获取任务列表
String resString = getResponseContent(response.getEntity()); // 读取响应内容
String jsBegin = "_setup("; // JS数据开始标识
String jsEnd = ")}</script>"; // JS数据结束标识
int begin = resString.indexOf(jsBegin); // 查找开始索引
int end = resString.lastIndexOf(jsEnd); // 查找结束索引
String jsString = null; // 存储提取的JS字符串
if (begin != -1 && end != -1 && begin < end) { // 检查索引有效性
jsString = resString.substring(begin + jsBegin.length(), end); // 提取JS字符串
}
JSONObject js = new JSONObject(jsString); // 创建JSON对象
// 返回任务列表的JSONArray
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()); // 记录IO异常
e.printStackTrace(); // 打印异常堆栈
throw new NetworkFailureException("gettasklists: httpget failed"); // 抛出网络失败异常
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("get task lists: handing jasonobject failed"); // 抛出操作失败异常
}
}
// 获取指定任务列表的任务方法
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate(); // 提交更新操作
try {
JSONObject jsPost = new JSONObject(); // 创建JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表
JSONObject action = new JSONObject(); // 创建单个动作对象
// 设置动作列表
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); // 动作类型为获取所有任务
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置动作ID
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); // 设置任务列表ID
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 不获取已删除的任务
actionList.put(action); // 将动作添加到动作列表
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); // 设置动作列表到JSON对象
// 设置客户端版本
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost); // 执行POST请求并获取响应
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); // 返回任务数组
} catch (JSONException e) {
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈
throw new ActionFailureException("get task list: handing jsonobject failed"); // 抛出操作失败异常
}
}
// 获取当前同步账户的方法
public Account getSyncAccount() {
return mAccount; // 返回当前账户信息
}
// 重置更新数组的方法
public void resetUpdateArray() {
mUpdateArray = null; // 将更新数组设置为null
}
}