pbg7l8cy5 1 month ago
parent ada8cdece0
commit 449aab321a

@ -28,89 +28,99 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
// GTaskASyncTask类继承自AsyncTask用于在后台执行Google任务同步操作
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
// 定义一个静态常量用于标识同步任务的通知ID
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
// 定义一个接口,用于在异步任务完成时回调
public interface OnCompleteListener {
// 定义一个方法,在异步任务完成时被调用
void onComplete();
}
// 应用上下文对象
private Context mContext;
// 通知管理器,用于显示和取消通知
private NotificationManager mNotifiManager;
// 任务管理器,用于处理同步任务
private GTaskManager mTaskManager;
// 异步任务完成时的回调监听器
private OnCompleteListener mOnCompleteListener;
// 构造方法,初始化上下文、回调监听器、通知管理器和任务管理器
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance();
.getSystemService(Context.NOTIFICATION_SERVICE);// 从系统服务获取通知管理器
mTaskManager = GTaskManager.getInstance();// 获取任务管理器的实例
}
// 取消同步操作
public void cancelSync() {
mTaskManager.cancelSync();
mTaskManager.cancelSync();// 调用任务管理器的取消同步方法
}
// 发布进度更新
public void publishProgess(String message) {
publishProgress(new String[] {
message
});
});// 调用AsyncTask的publishProgress方法来更新进度
}
private void showNotification(int tickerId, String content) {
// 显示通知
private void showNotification(int tickerId, String content) { // 创建一个新的通知对象
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS;
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.defaults = Notification.DEFAULT_LIGHTS;// 设置通知的默认灯
notification.flags = Notification.FLAG_AUTO_CANCEL;// 设置通知标志为自动取消
// 根据通知类型设置不同的PendingIntent
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
// 如果不是成功通知则点击通知后打开NotesPreferenceActivity
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0);
} else {
// 如果是成功通知则点击通知后打开NotesListActivity
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0);
}
// 设置通知的内容
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
// 通过通知管理器显示通知
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}
@Override
protected Integer doInBackground(Void... unused) {
// 发布登录进度的通知
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
// 执行同步操作,并返回同步结果
return mTaskManager.sync(mContext, this);
}
@Override
protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]);
protected void onProgressUpdate(String... progress) { // 显示同步进行中的通知
showNotification(R.string.ticker_syncing, progress[0]);// 如果mContext是GTaskSyncService的实例则通过它发送广播广播的内容是progress数组的第一个元素
if (mContext instanceof GTaskSyncService) {
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
}
}
@Override
protected void onPostExecute(Integer result) {
if (result == GTaskManager.STATE_SUCCESS) {
protected void onPostExecute(Integer result) {// 根据同步结果result执行不同的操作
if (result == GTaskManager.STATE_SUCCESS) { // 如果同步成功,显示成功的通知,并更新最后同步时间
showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {// 如果发生网络错误,显示网络错误的通知
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) {// 如果发生内部错误,显示内部错误的通知
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal));
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) {
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) { // 如果同步被取消,显示取消的通知
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
}
// 如果mOnCompleteListener不为null启动一个新线程来调用它的onComplete方法
if (mOnCompleteListener != null) {
new Thread(new Runnable() {

@ -62,35 +62,37 @@ import java.util.zip.InflaterInputStream;
public class GTaskClient {
// 类标签,用于日志输出
private static final String TAG = GTaskClient.class.getSimpleName();
// Google Tasks服务的基URL
private static final String GTASK_URL = "https://mail.google.com/tasks/";
// 用于获取数据的URL
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
// 用于提交数据的URL
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
// GTaskClient的单例实例
private static GTaskClient mInstance = null;
// HTTP客户端用于发送请求
private DefaultHttpClient mHttpClient;
// 当前使用的获取数据的URL
private String mGetUrl;
// 当前使用的提交数据的URL
private String mPostUrl;
// 客户端版本号
private long mClientVersion;
// 登录状态
private boolean mLoggedin;
// 最后一次登录时间
private long mLastLoginTime;
// 操作ID用于标识不同的操作
private int mActionId;
// 当前登录的账户
private Account mAccount;
// 更新数据数组
private JSONArray mUpdateArray;
private GTaskClient() {
mHttpClient = null;
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
@ -109,15 +111,17 @@ public class GTaskClient {
return mInstance;
}
public boolean login(Activity activity) {
public boolean login(Activity activity) { // 假设cookie在5分钟后过期需要重新登录
// we suppose that the cookie would expire after 5 minutes
// then we need to re-login
final long interval = 1000 * 60 * 5;
// 切换账户后需要重新登录
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
// need to re-login after account switch
// 如果已经登录直接返回true
if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) {
@ -128,14 +132,15 @@ public class GTaskClient {
Log.d(TAG, "already logged in");
return true;
}
// 更新最后一次登录时间
mLastLoginTime = System.currentTimeMillis();
// 获取Google账户的认证令牌
String authToken = loginGoogleAccount(activity, false);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// 如果账户不是gmail.com或googlemail.com使用自定义域名登录
// login with custom domain if necessary
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
@ -159,29 +164,33 @@ public class GTaskClient {
return false;
}
}
// 设置登录状态为true并返回true表示登录成功
mLoggedin = true;
return true;
}
// 登录Google账户的函数接收一个Activity实例和一个标志位来决定是否使当前的token无效
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken;
AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google");
String authToken;// 用于存储获取到的认证token
AccountManager accountManager = AccountManager.get(activity);// 获取AccountManager实例
Account[] accounts = accountManager.getAccountsByType("com.google");// 获取所有Google类型的账户
// 如果没有可用的Google账户则记录错误日志并返回null
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account");
return null;
}
// 从设置中获取同步账户的名称
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
// 遍历所有账户,找到与设置中的账户名匹配的账户
for (Account a : accounts) {
if (a.name.equals(accountName)) {
account = a;
break;
}
}
// 如果找到了匹配的账户则将其存储在mAccount变量中否则记录错误日志并返回null
if (account != null) {
mAccount = account;
} else {
@ -190,60 +199,66 @@ public class GTaskClient {
}
// get the token now
// 获取认证token
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
Bundle authTokenBundle = accountManagerFuture.getResult();// 获取结果
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);// 从结果中获取token
// 如果需要使token无效则调用invalidateAuthToken并递归调用loginGoogleAccount以获取新的token
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false);
}
} catch (Exception e) {
// 如果获取token失败则记录错误日志并将authToken设置为null
Log.e(TAG, "get auth token failed");
authToken = null;
}
return authToken;
return authToken;// 返回获取到的token
}
// 尝试使用Google账户登录Google Tasks的函数
private boolean tryToLoginGtask(Activity activity, String authToken) {
// 尝试使用提供的token登录Google Tasks
if (!loginGtask(authToken)) {
// 如果登录失败可能是因为token已过期因此使token无效并尝试重新登录
// maybe the auth token is out of date, now let's invalidate the
// token and try again
authToken = loginGoogleAccount(activity, true);
if (authToken == null) {
if (authToken == null) {// 如果重新登录失败则记录错误日志并返回false
Log.e(TAG, "login google account failed");
return false;
}
// 使用新的token再次尝试登录Google Tasks
if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed");
return false;
}
}
return true;
return true;// 登录成功返回true
}
// 使用提供的token登录Google Tasks的函数
private boolean loginGtask(String authToken) {
// 设置HTTP连接的超时时间
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
mHttpClient = new DefaultHttpClient(httpParameters);// 创建HttpClient实例
BasicCookieStore localBasicCookieStore = new BasicCookieStore();// 创建Cookie存储实例
mHttpClient.setCookieStore(localBasicCookieStore);// 设置HttpClient的Cookie存储
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);// 禁用Expect-Continue握手
// login gtask
// 构造登录Google Tasks的URL
try {
String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl);
HttpGet httpGet = new HttpGet(loginUrl);// 创建HttpGet请求
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
response = mHttpClient.execute(httpGet);// 执行HttpGet请求
// get the cookie now
// 检查响应中的Cookie看是否有认证Cookie
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
@ -255,7 +270,7 @@ public class GTaskClient {
Log.w(TAG, "it seems that there is no auth cookie");
}
// get the client version
// 解析响应内容,获取客户端版本信息
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -265,25 +280,25 @@ public class GTaskClient {
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString);
mClientVersion = js.getLong("v");
JSONObject js = new JSONObject(jsString);// 解析JSON字符串
mClientVersion = js.getLong("v");// 获取客户端版本
} catch (JSONException e) {
Log.e(TAG, e.toString());
Log.e(TAG, e.toString());// 如果解析JSON时出错则记录错误日志并返回false
e.printStackTrace();
return false;
} catch (Exception e) {
} catch (Exception e) { // 捕获所有异常并记录错误日志返回false
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");
return false;
}
return true;
return true;// 登录成功返回true
}
// 获取一个唯一的动作ID的函数
private int getActionId() {
return mActionId++;
}
// 创建一个HttpPost请求的函数
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -292,89 +307,112 @@ public class GTaskClient {
}
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")) {
// 根据内容编码进行解压缩
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { // 如果是gzip编码使用GZIPInputStream解压缩
input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {// 如果是deflate编码使用InflaterInputStream解压缩
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);
}
// 读取输入流并转换为字符串
try {
// 创建InputStreamReader指定UTF-8编码
InputStreamReader isr = new InputStreamReader(input);
// 创建BufferedReader以高效读取文本
BufferedReader br = new BufferedReader(isr);
// 创建StringBuilder以构建响应内容字符串·
StringBuilder sb = new StringBuilder();
while (true) {
String buff = br.readLine();
if (buff == null) {
return sb.toString();
}
} // 将读取的行追加到StringBuilder
sb = sb.append(buff);
}
} finally {
} finally { // 确保输入流在完成后被关闭
input.close();
}
}
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
private JSONObject postRequest(JSONObject js) throws NetworkFailureException { // 检查用户是否已登录,如果未登录,则抛出异常
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
// 创建HTTP POST请求对象
HttpPost httpPost = createHttpPost();
try {
try { // 创建参数列表,用于存储请求参数
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
// 将JSON对象转换为字符串并作为参数添加到列表
list.add(new BasicNameValuePair("r", js.toString()));
// 创建UrlEncodedFormEntity对象用于封装请求参数
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
// 设置请求的实体内容
httpPost.setEntity(entity);
// execute the post
// 执行HTTP POST请求获取响应
HttpResponse response = mHttpClient.execute(httpPost);
// 获取响应内容,并将其转换为字符串
String jsString = getResponseContent(response.getEntity());
// 将响应字符串转换为JSON对象
return new JSONObject(jsString);
} catch (ClientProtocolException e) {
// 处理HTTP协议异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");
} catch (IOException e) {
// 处理I/O异常
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");
} 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 {
// 创建用于POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 创建动作列表
JSONArray actionList = new JSONArray();
// action_list
// 获取任务的创建动作,并添加到动作列表
actionList.put(task.getCreateAction(getActionId()));
// 将动作列表添加到POST请求的JSON对象
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
// 设置客户端版本信息
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
// 发送POST请求并获取响应
JSONObject jsResponse = postRequest(jsPost);
// 从响应中获取结果并设置任务的GID
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
@ -387,10 +425,10 @@ public class GTaskClient {
}
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate();
commitUpdate(); // 提交之前的更新
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject jsPost = new JSONObject(); // 创建用于POST请求的JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表
// action_list
actionList.put(tasklist.getCreateAction(getActionId()));
@ -413,19 +451,21 @@ public class GTaskClient {
}
public void commitUpdate() throws NetworkFailureException {
// 检查是否有更新数组
if (mUpdateArray != null) {
try {
try { // 创建用于POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// action_list
// 将更新数组添加到POST请求的JSON对象
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// client_version
// 设置客户端版本信息
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");
@ -433,40 +473,41 @@ public class GTaskClient {
}
}
public void addUpdateNode(Node node) throws NetworkFailureException {
public void addUpdateNode(Node node) throws NetworkFailureException { // 检查节点是否为空
if (node != null) {
// too many update items may result in an error
// set max to 10 items
// 检查更新数组中的项目数量超过10个则先提交更新
if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate();
}
// 如果更新数组为空,则初始化
if (mUpdateArray == null)
mUpdateArray = new JSONArray();
mUpdateArray = new JSONArray(); // 将节点的更新动作添加到更新数组
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
}
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();
commitUpdate(); // 提交之前的更新
try {
JSONObject jsPost = new JSONObject();
// 创建动作列表
JSONArray actionList = new JSONArray();
// 创建动作对象
JSONObject action = new JSONObject();
// action_list
// 设置动作类型为移动
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid());
if (preParent == curParent && task.getPriorSibling() != null) {
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
// put prioring_sibing_id only if moving within the tasklist and
// it is not the first one
action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());
}
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid());
} // 设置源任务列表的GID
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); // 设置目标父任务的GID
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); // 如果在不同任务列表间移动则设置目标任务列表的GID
if (preParent != curParent) {
// put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
@ -487,91 +528,99 @@ public class GTaskClient {
}
public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate();
commitUpdate();// 先提交之前的所有更新
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject jsPost = new JSONObject(); // 创建用于POST请求的JSON对象
JSONArray actionList = new JSONArray(); // 创建动作列表的JSONArray
// action_list
// 将节点标记为已删除,并获取更新动作,添加到动作列表中
node.setDeleted(true);
actionList.put(node.getUpdateAction(getActionId()));
actionList.put(node.getUpdateAction(getActionId())); // 将动作列表添加到POST请求的JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 添加客户端版本信息到POST请求的JSON对象中
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 发送POST请求
postRequest(jsPost);
mUpdateArray = null;
mUpdateArray = null; // 清空更新数组
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("delete node: handing jsonobject failed");
Log.e(TAG, e.toString()); // 如果处理JSON时发生异常记录错误日志
e.printStackTrace(); // 打印异常堆栈信息
throw new ActionFailureException("delete node: handing jsonobject failed"); // 抛出动作失败异常
}
}
public JSONArray getTaskLists() throws NetworkFailureException {
public JSONArray getTaskLists() throws NetworkFailureException { // 检查是否已登录
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
Log.e(TAG, "please login first"); // 如果未登录,记录错误日志
throw new ActionFailureException("not logged in"); // 抛出动作失败异常
}
try {
try { // 创建HTTP GET请求
HttpGet httpGet = new HttpGet(mGetUrl);
HttpResponse response = null;
HttpResponse response = null; // 执行HTTP GET请求获取响应
response = mHttpClient.execute(httpGet);
// 获取响应内容
// get the task list
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;
String jsString = null; // 提取JSON字符串
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
} // 将提取的JSON字符串转换为JSONObject.
JSONObject js = new JSONObject(jsString);
// 从JSONObject中获取任务列表的JSONArray
return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS);
} catch (ClientProtocolException e) {
} catch (ClientProtocolException e) { // 如果HTTP协议错误记录错误日志
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");
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");
Log.e(TAG, e.toString()); // 如果I/O错误记录错误日志
e.printStackTrace(); // 打印异常堆栈信息
throw new ActionFailureException("get task lists: handing jasonobject failed"); // 抛出网络失败异常
}
}
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
commitUpdate(); // 先提交之前的所有更新
try {
// 创建用于POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 创建动作列表的JSONArray
JSONArray actionList = new JSONArray();
// 创建动作的JSONObject
JSONObject action = new JSONObject();
// action_list
// action_list // 设置动作类型为获取所有任务
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); // 设置动作ID
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); // 设置任务列表ID
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); // 设置是否获取已删除任务,此处为否
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); // 将动作添加到动作列表中
actionList.put(action); // 将动作列表添加到POST请求的JSON对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
// 添加客户端版本信息
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost);
// 发送POST请求并获取响应
JSONObject jsResponse = postRequest(jsPost); // 从响应中获取任务数组
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("get task list: handing jsonobject failed");
Log.e(TAG, e.toString()); // 记录JSON异常
e.printStackTrace(); // 打印异常堆栈信息
throw new ActionFailureException("get task list: handing jsonobject failed"); // 抛出操作失败异常
}
}

@ -87,19 +87,20 @@ public class GTaskManager {
private HashMap<Long, String> mNidToGid;
private GTaskManager() {
mSyncing = false;
mCancelled = false;
mGTaskListHashMap = new HashMap<String, TaskList>();
private GTaskManager() { //对象初始化函数
mSyncing = false; //正在同步,flase代表未执行
mCancelled = false; //全局标识flase代表可以执行
mGTaskListHashMap = new HashMap<String, TaskList>(); //<>代表Java的泛型,就是创建一个用类型作为参数的类。
mGTaskHashMap = new HashMap<String, Node>();
mMetaHashMap = new HashMap<String, MetaData>();
mMetaList = null;
mLocalDeleteIdMap = new HashSet<Long>();
mGidToNid = new HashMap<String, Long>();
mNidToGid = new HashMap<Long, String>();
mGidToNid = new HashMap<String, Long>(); //GoogleID to NodeID??
mNidToGid = new HashMap<Long, String>(); //NodeID to GoogleID???通过hashmap散列表建立映射
}
public static synchronized GTaskManager getInstance() {
public static synchronized GTaskManager getInstance() {//可能运行在多线程环境下,使用语言级同步--synchronized
if (mInstance == null) {
mInstance = new GTaskManager();
}
@ -107,19 +108,22 @@ public class GTaskManager {
}
public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken
// 保存Activity实例用于后续获取认证令牌
mActivity = activity;
}
public int sync(Context context, GTaskASyncTask asyncTask) {
// 检查是否正在同步,如果是,则记录日志并返回同步进行中的状态码
if (mSyncing) {
Log.d(TAG, "Sync is in progress");
return STATE_SYNC_IN_PROGRESS;
}
// 初始化同步相关的上下文和变量
mContext = context;
mContentResolver = mContext.getContentResolver();
mSyncing = true;
mCancelled = false;
// 清空所有映射和列表,为新的同步操作做准备
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
@ -128,34 +132,42 @@ public class GTaskManager {
mNidToGid.clear();
try {
// 获取GTaskClient单例并重置更新数组
GTaskClient client = GTaskClient.getInstance();
client.resetUpdateArray();
// login google task
// 如果同步未取消尝试登录Google任务服务
if (!mCancelled) {
if (!client.login(mActivity)) {
// 登录失败,抛出网络失败异常
throw new NetworkFailureException("login google task failed");
}
}
// get the task list from google
// 发布初始化任务列表的进度信息
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
// 初始化Google任务列表
initGTaskList();
// do content sync work
// 发布同步内容的进度信息
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
// 执行内容同步
syncContent();
} catch (NetworkFailureException e) {
// 捕获网络失败异常,记录错误日志并返回网络错误状态码
Log.e(TAG, e.toString());
return STATE_NETWORK_ERROR;
} catch (ActionFailureException e) {
// 捕获操作失败异常,记录错误日志并返回内部错误状态码
Log.e(TAG, e.toString());
return STATE_INTERNAL_ERROR;
} catch (Exception e) {
// 捕获其他异常,记录错误日志、打印堆栈跟踪并返回内部错误状态码
Log.e(TAG, e.toString());
e.printStackTrace();
return STATE_INTERNAL_ERROR;
} finally {
// 无论是否发生异常都清空所有映射和列表并设置同步标志为false
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
@ -165,34 +177,35 @@ public class GTaskManager {
mSyncing = false;
}
// 根据是否取消同步,返回相应的状态码
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
private void initGTaskList() throws NetworkFailureException {
// 如果同步已被取消,直接返回
if (mCancelled)
return;
// 获取GTaskClient单例
GTaskClient client = GTaskClient.getInstance();
try {
// 从客户端获取任务列表的JSONArray
JSONArray jsTaskLists = client.getTaskLists();
// init meta list first
// 首先初始化元数据列表
mMetaList = null;
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name
.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
// 检查是否为元数据文件夹
if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
mMetaList = new TaskList();
mMetaList.setContentByRemoteJSON(object);
// load meta data
// 加载元数据
JSONArray jsMetas = client.getTaskList(gid);
for (int j = 0; j < jsMetas.length(); j++) {
object = (JSONObject) jsMetas.getJSONObject(j);
MetaData metaData = new MetaData();
metaData.setContentByRemoteJSON(object);
// 如果元数据值得保存,添加到元数据列表并更新映射
if (metaData.isWorthSaving()) {
mMetaList.addChildTask(metaData);
if (metaData.getGid() != null) {
@ -202,36 +215,33 @@ public class GTaskManager {
}
}
}
// create meta list if not existed
// 如果元数据列表不存在,则创建它
if (mMetaList == null) {
mMetaList = new TaskList();
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META);
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META);
GTaskClient.getInstance().createTaskList(mMetaList);
}
// init task list
// 初始化普通任务列表
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
// 忽略元数据文件夹,处理其他任务列表
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META)) {
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList();
tasklist.setContentByRemoteJSON(object);
// 更新任务列表映射
mGTaskListHashMap.put(gid, tasklist);
mGTaskHashMap.put(gid, tasklist);
// load tasks
// 加载任务
JSONArray jsTasks = client.getTaskList(gid);
for (int j = 0; j < jsTasks.length(); j++) {
object = (JSONObject) jsTasks.getJSONObject(j);
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
Task task = new Task();
task.setContentByRemoteJSON(object);
// 如果任务值得保存,设置元信息并添加到任务列表
if (task.isWorthSaving()) {
task.setMetaInfo(mMetaHashMap.get(gid));
tasklist.addChildTask(task);
@ -241,102 +251,104 @@ public class GTaskManager {
}
}
} catch (JSONException e) {
// 捕获JSON解析异常记录错误日志打印堆栈跟踪并抛出操作失败异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");
}
}
private void syncContent() throws NetworkFailureException {
private void syncContent() throws NetworkFailureException { // 定义同步类型变量、游标、Google任务ID和节点对象
int syncType;
Cursor c = null;
String gid;
Node node;
// 清空本地删除的ID映射为同步做准备
mLocalDeleteIdMap.clear();
// 检查同步是否已被取消,如果取消则直接返回,不进行后续操作
if (mCancelled) {
return;
}
// 处理本地删除的笔记
// for local deleted note
try {
try { // 查询非系统类型且父ID为垃圾文件夹的笔记即本地已删除的笔记
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id=?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, null);
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
while (c.moveToNext()) { // 获取Google任务ID
gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 根据Google任务ID获取节点对象
node = mGTaskHashMap.get(gid);
if (node != null) {
if (node != null) { // 如果节点存在,从映射中移除并执行远程删除同步
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
}
// 将本地删除的笔记ID添加到映射中用于后续批量删除
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
}
} else {
} else { // 如果查询失败,记录警告日志
Log.w(TAG, "failed to query trash folder");
}
} finally {
// 确保游标被关闭,避免资源泄漏
if (c != null) {
c.close();
c = null;
}
}
// sync folder first
// 同步文件夹,确保文件夹结构是最新的
syncFolder();
// for note existing in database
try {
// 处理数据库中现有的笔记
try { // 查询类型为笔记且父ID不是垃圾文件夹的笔记即需要同步的现有笔记
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
while (c.moveToNext()) { // 获取Google任务ID
gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 根据Google任务ID获取节点对象
node = mGTaskHashMap.get(gid);
if (node != null) {
if (node != null) { // 如果节点存在从映射中移除并更新GID与NID的映射关系
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
syncType = node.getSyncAction(c); // 获取同步类型
} else { // 如果节点不存在,判断是本地新增还是远程删除
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// local add
// local add // 本地新增,需要添加到远程
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// remote delete
// 远程删除,需要从本地删除
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
// 远程删除,需要从本地删除
doContentSync(syncType, node, c);
}
} else {
} else { // 如果查询失败,记录警告日志
Log.w(TAG, "failed to query existing note in database");
}
} finally {
} finally { // 确保游标被关闭,避免资源泄漏
if (c != null) {
c.close();
c = null;
}
}
// go through remaining items
// 处理剩余的项目,即那些在本地没有对应项的远程节点
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
node = entry.getValue();
node = entry.getValue(); // 将这些远程节点添加到本地
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
// mCancelled can be set by another thread, so we neet to check one by
// one
// clear local delete table
// 检查同步是否已被取消,如果未取消,则执行以下操作
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
@ -352,130 +364,154 @@ public class GTaskManager {
}
private void syncFolder() throws NetworkFailureException {
Cursor c = null;
String gid;
Node node;
int syncType;
Cursor c = null; // 用于数据库查询的游标
String gid; // Google任务ID
Node node; // 节点对象,代表文件夹或笔记
int syncType; // 同步类型
// 检查同步是否已被取消,如果取消则直接返回,不进行后续操作
if (mCancelled) {
return;
}
// for root folder
// 同步根文件夹
try {
// 查询根文件夹的信息
c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);
if (c != null) {
c.moveToNext();
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (c != null && c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取根文件夹的Google任务ID
node = mGTaskHashMap.get(gid); // 根据ID获取对应的节点对象
if (node != null) {
// 如果节点存在,更新映射关系并检查是否需要更新远程名称
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// for system folder, only update remote name if necessary
// 只有在远程名称与预期不符时才更新远程名称
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
// 如果节点不存在,添加到远程
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);
}
} else {
// 如果查询失败,记录警告日志
Log.w(TAG, "failed to query root folder");
}
} finally {
// 确保游标被关闭,避免资源泄漏
if (c != null) {
c.close();
c = null;
}
}
// for call-note folder
// 同步通话记录文件夹
try {
// 查询通话记录文件夹的信息
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",
new String[] {
String.valueOf(Notes.ID_CALL_RECORD_FOLDER)
}, null);
if (c != null) {
if (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (c != null && c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取通话记录文件夹的Google任务ID
node = mGTaskHashMap.get(gid); // 根据ID获取对应的节点对象
if (node != null) {
// 如果节点存在,更新映射关系并检查是否需要更新远程名称
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);
mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
// for system folder, only update remote name if
// necessary
// 只有在远程名称与预期不符时才更新远程名称
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_CALL_NOTE))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
} else {
// 如果节点不存在,添加到远程
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);
}
}
} else {
// 如果查询失败,记录警告日志
Log.w(TAG, "failed to query call note folder");
}
} finally {
// 确保游标被关闭,避免资源泄漏
if (c != null) {
c.close();
c = null;
}
}
}
// for local existing folders
try {
// 查询所有非垃圾箱文件夹的笔记
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
// 遍历查询结果
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取Google任务ID
node = mGTaskHashMap.get(gid); // 根据ID获取对应的节点对象
if (node != null) {
// 如果节点存在,更新映射关系并获取同步类型
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
// 如果节点不存在根据GTASK_ID判断是本地新增还是远程删除
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// local add
// 本地新增
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// remote delete
// 远程删除
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
// 执行内容同步
doContentSync(syncType, node, c);
}
} else {
// 如果查询失败,记录警告日志
Log.w(TAG, "failed to query existing folder");
}
} finally {
// 确保游标被关闭,避免资源泄漏
if (c != null) {
c.close();
c = null;
}
}
// for remote add folders
// 同步远程新增的文件夹
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
gid = entry.getKey();
node = entry.getValue();
gid = entry.getKey(); // 获取Google任务ID
node = entry.getValue(); // 获取对应的任务列表节点
if (mGTaskHashMap.containsKey(gid)) {
// 如果本地映射中包含该ID说明是远程新增的文件夹
mGTaskHashMap.remove(gid);
// 执行内容同步,添加到本地
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
}
// 如果同步未被取消,提交更新
if (!mCancelled)
GTaskClient.getInstance().commitUpdate();
}
/**
*
* @param syncType
* @param node
* @param c
* @throws NetworkFailureException
*/
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -484,12 +520,15 @@ public class GTaskManager {
MetaData meta;
switch (syncType) {
case Node.SYNC_ACTION_ADD_LOCAL:
// 添加本地节点
addLocalNode(node);
break;
case Node.SYNC_ACTION_ADD_REMOTE:
// 添加远程节点
addRemoteNode(node, c);
break;
case Node.SYNC_ACTION_DEL_LOCAL:
// 删除本地节点
meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
@ -497,6 +536,7 @@ public class GTaskManager {
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
break;
case Node.SYNC_ACTION_DEL_REMOTE:
// 删除远程节点
meta = mMetaHashMap.get(node.getGid());
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
@ -504,21 +544,24 @@ public class GTaskManager {
GTaskClient.getInstance().deleteNode(node);
break;
case Node.SYNC_ACTION_UPDATE_LOCAL:
// 更新本地节点
updateLocalNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_REMOTE:
// 更新远程节点
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_CONFLICT:
// merging both modifications maybe a good idea
// right now just use local update simply
// 处理更新冲突,目前简单使用本地更新
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_NONE:
// 无操作
break;
case Node.SYNC_ACTION_ERROR:
default:
throw new ActionFailureException("unkown sync action type");
// 未知同步操作类型,抛出异常
throw new ActionFailureException("unknown sync action type");
}
}
@ -529,6 +572,7 @@ public class GTaskManager {
SqlNote sqlNote;
if (node instanceof TaskList) {
// 如果节点是任务列表,根据名称判断是否为特殊文件夹
if (node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
@ -541,15 +585,17 @@ public class GTaskManager {
sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
}
} else {
// 如果节点是任务创建新的SqlNote并设置内容
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();
try {
// 检查并处理ID冲突
if (js.has(GTaskStringUtils.META_HEAD_NOTE)) {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (note.has(NoteColumns.ID)) {
long id = note.getLong(NoteColumns.ID);
if (DataUtils.existInNoteDatabase(mContentResolver, id)) {
// the id is not available, have to create a new one
// ID不可用需要创建新的ID
note.remove(NoteColumns.ID);
}
}
@ -562,13 +608,11 @@ public class GTaskManager {
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
// the data id is not available, have to create
// a new one
// 数据ID不可用需要创建新的ID
data.remove(DataColumns.ID);
}
}
}
}
} catch (JSONException e) {
Log.w(TAG, e.toString());
@ -576,13 +620,13 @@ public class GTaskManager {
}
sqlNote.setContent(js);
// 设置父节点ID
Long parentId = mGidToNid.get(((Task) node).getParent().getGid());
if (parentId == null) {
Log.e(TAG, "cannot find task's parent id locally");
throw new ActionFailureException("cannot add local node");
}
sqlNote.setParentId(parentId.longValue());
}
// create the local node
sqlNote.setGtaskId(node.getGid());
@ -596,16 +640,18 @@ public class GTaskManager {
updateRemoteMeta(node.getGid(), sqlNote);
}
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote;
// update the note locally
// 更新本地笔记
sqlNote = new SqlNote(mContext, c);
sqlNote.setContent(node.getLocalJSONFromContent());
// 获取父节点ID
Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())
: new Long(Notes.ID_ROOT_FOLDER);
if (parentId == null) {
@ -615,10 +661,16 @@ public class GTaskManager {
sqlNote.setParentId(parentId.longValue());
sqlNote.commit(true);
// update meta info
// 更新远程元信息
updateRemoteMeta(node.getGid(), sqlNote);
}
/**
*
* @param node
* @param c
* @throws NetworkFailureException
*/
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -627,11 +679,12 @@ public class GTaskManager {
SqlNote sqlNote = new SqlNote(mContext, c);
Node n;
// update remotely
// 更新远程服务器
if (sqlNote.isNoteType()) {
Task task = new Task();
task.setContentByLocalJSON(sqlNote.getContent());
// 获取父任务列表的GID
String parentGid = mNidToGid.get(sqlNote.getParentId());
if (parentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist");
@ -642,12 +695,12 @@ public class GTaskManager {
GTaskClient.getInstance().createTask(task);
n = (Node) task;
// add meta
// 添加元信息
updateRemoteMeta(task.getGid(), sqlNote);
} else {
TaskList tasklist = null;
// we need to skip folder if it has already existed
// 跳过已存在的文件夹
String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)
folderName += GTaskStringUtils.FOLDER_DEFAULT;
@ -671,7 +724,7 @@ public class GTaskManager {
}
}
// no match we can add now
// 如果没有匹配的文件夹,则添加新的任务列表
if (tasklist == null) {
tasklist = new TaskList();
tasklist.setContentByLocalJSON(sqlNote.getContent());
@ -681,17 +734,23 @@ public class GTaskManager {
n = (Node) tasklist;
}
// update local note
// 更新本地笔记
sqlNote.setGtaskId(n.getGid());
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// gid-id mapping
// 更新GID和ID的映射
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
}
/**
*
* @param node
* @param c
* @throws NetworkFailureException
*/
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -699,18 +758,19 @@ public class GTaskManager {
SqlNote sqlNote = new SqlNote(mContext, c);
// update remotely
// 使用本地JSON内容更新远程节点
node.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(node);
// update meta
// 更新远程元信息
updateRemoteMeta(node.getGid(), sqlNote);
// move task if necessary
// 如果是笔记类型,可能需要移动任务
if (sqlNote.isNoteType()) {
Task task = (Task) node;
TaskList preParentList = task.getParent();
// 获取当前父任务列表的GID
String curParentGid = mNidToGid.get(sqlNote.getParentId());
if (curParentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist");
@ -718,6 +778,7 @@ public class GTaskManager {
}
TaskList curParentList = mGTaskListHashMap.get(curParentGid);
// 如果父任务列表发生变化,则移动任务
if (preParentList != curParentList) {
preParentList.removeChildTask(task);
curParentList.addChildTask(task);
@ -725,18 +786,26 @@ public class GTaskManager {
}
}
// clear local modified flag
// 清除本地修改标志并提交更改
sqlNote.resetLocalModified();
sqlNote.commit(true);
}
/**
*
* @param gid
* @param sqlNote
* @throws NetworkFailureException
*/
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) {
MetaData metaData = mMetaHashMap.get(gid);
if (metaData != null) {
// 更新现有元信息
metaData.setMeta(gid, sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(metaData);
} else {
// 创建新的元信息
metaData = new MetaData();
metaData.setMeta(gid, sqlNote.getContent());
mMetaList.addChildTask(metaData);
@ -746,12 +815,13 @@ public class GTaskManager {
}
}
private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) {
return;
}
// get the latest gtask list
// 清除旧的数据映射并重新初始化远程任务列表
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
@ -759,30 +829,36 @@ public class GTaskManager {
Cursor c = null;
try {
// 查询所有非系统且非垃圾箱的笔记
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
// 获取笔记的GID
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid);
if (node != null) {
// 更新本地数据库中的同步ID为远程节点的最后修改时间
mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues();
values.put(NoteColumns.SYNC_ID, node.getLastModified());
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else {
// 如果找不到对应的远程节点,记录错误并抛出异常
Log.e(TAG, "something is missed");
throw new ActionFailureException(
"some local items don't have gid after sync");
}
}
} else {
// 如果查询失败,记录警告
Log.w(TAG, "failed to query local note to refresh sync id");
}
} finally {
// 确保游标被关闭以避免资源泄漏
if (c != null) {
c.close();
c = null;
@ -790,6 +866,7 @@ public class GTaskManager {
}
}
public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name;
}

@ -42,17 +42,31 @@ public class GTaskSyncService extends Service {
private static String mSyncProgress = "";
public class GTaskSyncService extends Service {
// 定义广播动作和 extras 的键
public final static String ACTION_STRING_NAME = "sync_action_type";
public final static int ACTION_START_SYNC = 0;
public final static int ACTION_CANCEL_SYNC = 1;
public final static int ACTION_INVALID = 2;
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.sync_broadcast";
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "is_syncing";
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
// 同步任务和进度信息
private static GTaskASyncTask mSyncTask = null;
private static String mSyncProgress = "";
private void startSync() {
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {
mSyncTask = null;
sendBroadcast("");
stopSelf();
sendBroadcast(""); // 发送同步完成广播
stopSelf(); // 停止服务
}
});
sendBroadcast("");
mSyncTask.execute();
sendBroadcast(""); // 发送同步开始广播
mSyncTask.execute(); // 执行同步任务
}
}
@ -64,7 +78,7 @@ public class GTaskSyncService extends Service {
@Override
public void onCreate() {
mSyncTask = null;
mSyncTask = null; // 初始化同步任务为空
}
@Override
@ -73,15 +87,15 @@ public class GTaskSyncService extends Service {
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
case ACTION_START_SYNC:
startSync();
startSync(); // 处理开始同步动作
break;
case ACTION_CANCEL_SYNC:
cancelSync();
cancelSync(); // 处理取消同步动作
break;
default:
break;
}
return START_STICKY;
return START_STICKY; // 确保服务被持续运行
}
return super.onStartCommand(intent, flags, startId);
}
@ -89,40 +103,62 @@ public class GTaskSyncService extends Service {
@Override
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
mSyncTask.cancelSync(); // 低内存时取消同步任务
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
return null; // 不支持绑定
}
/**
* 广
* @param msg
*/
public void sendBroadcast(String msg) {
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent);
sendBroadcast(intent); // 发送广播
}
/**
* Activity
* @param activity Activity
*/
public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
activity.startService(intent);
activity.startService(intent); // 启动服务
}
/**
* Context
* @param context Context
*/
public static void cancelSync(Context context) {
Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
context.startService(intent);
context.startService(intent); // 启动服务以发送取消动作
}
/**
*
* @return true false
*/
public static boolean isSyncing() {
return mSyncTask != null;
}
/**
*
* @return
*/
public static String getProgressString() {
return mSyncProgress;
}
}

Loading…
Cancel
Save