精读报告代码注释

dev
王晓慧 10 months ago
parent 178c1a8748
commit c86cb92990

@ -0,0 +1,153 @@
/*
* 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;
/*GTask
*
* private void showNotification(int tickerId, String content)
* protected Integer doInBackground(Void... unused) 线
* protected void onProgressUpdate(String... progress) 使 线
* protected void onPostExecute(Integer result) Handler UI使doInBackground UI
*/
// 这是一个继承自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;
// Google任务管理器用于执行实际的同步操作
private GTaskManager mTaskManager;
// 同步完成的监听器
private OnCompleteListener mOnCompleteListener;
/*
*
* context
* listener
*/
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance();
}
// 取消同步操作的方法
public void cancelSync() {
mTaskManager.cancelSync();
}
// 发布进度更新的方法
public void publishProgess(String message) { // 发布进度单位系统将会调用onProgressUpdate()方法更新这些值
publishProgress(new String[] {
message
});
}
/*
*
* tickerId TickerID
* 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; // 点击清除按钮或点击通知后会自动消失
PendingIntent pendingIntent; //一个描述了想要启动一个Activity、Broadcast或是Service的意图
// 根据不同的通知状态设置不同的Intent
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0); //如果同步不成功那么从系统取得一个用于启动一个NotesPreferenceActivity的PendingIntent对象
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0); //如果同步成功那么从系统取得一个用于启动一个NotesListActivity的PendingIntent对象
}
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);//通过NotificationManager对象的notify方法来执行一个notification的消息
}
/*
*
*
*/
@Override
protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext))); //利用getString,将把 NotesPreferenceActivity.getSyncAccountName(mContext))的字符串内容传进sync_progress_login中
return mTaskManager.sync(mContext, this); //进行后台同步具体操作
}
/*
* publishProgress
* progress
*/
@Override
protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) { //instanceof 判断mContext是否是GTaskSyncService的实例
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
}
}
/*
*
* result
*/
@Override
protected void onPostExecute(Integer result) { //用于在执行完后台任务后更新UI,显示结果
// 根据不同的状态显示不同的通知
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) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} 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) {
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
}
// 如果设置了完成监听器则在一个新线程中调用其onComplete方法
if (mOnCompleteListener != null) {
new Thread(new Runnable() {
public void run() { //完成后的操作使用onComplete()将所有值都重新初始化,相当于完成一次操作
mOnCompleteListener.onComplete();
}
}).start();
}
}
}

@ -0,0 +1,694 @@
/*
* 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;
/* GTaskClientGoogle
*
*/
public class GTaskClient {
// 日志标签
private static final String TAG = GTaskClient.class.getSimpleName();
// Google任务服务的基础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";
// 单例模式实例
private static GTaskClient mInstance = null;
// HTTP客户端
private DefaultHttpClient mHttpClient;
// GET请求URL
private String mGetUrl;
// POST请求URL
private String mPostUrl;
// 客户端版本号
private long mClientVersion;
// 是否已登录
private boolean mLoggedin;
// 最后登录时间
private long mLastLoginTime;
// 操作ID用于标识一次操作
private int mActionId;
// 用户账户信息
private Account mAccount;
// 用于存储更新数据的JSON数组
private JSONArray mUpdateArray;
// GTaskClient的私有构造方法初始化各种属性。
private GTaskClient() {
mHttpClient = null;
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
mClientVersion = -1;
mLoggedin = false;
mLastLoginTime = 0;
mActionId = 1;
mAccount = null;
mUpdateArray = null;
}
/*
* 使 getInstance()
* mInstance
*/
public static synchronized GTaskClient getInstance() {
// 确保仅创建一个实例
if (mInstance == null) {
mInstance = new GTaskClient();
}
return mInstance;
}
/*
* activity
*
* 使URL使URL
* truefalse
*/
public boolean login(Activity activity) {
// 检查登录是否过期判断距离最后一次登录操作是否超过5分钟
final long interval = 1000 * 60 * 5;// 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();// 更新最后登录时间,改为系统当前的时间
// 尝试登录Google账户
String authToken = loginGoogleAccount(activity, false);// 判断是否登录到谷歌账户
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"; // 设置用户对应的getUrl
mPostUrl = url.toString() + "r/ig"; // 设置用户对应的postUrl
// 尝试使用自定义域名登录
if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true;
}
}
// 如果使用自定义域名登录失败则尝试使用官方URL登录
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
if (!tryToLoginGtask(activity, authToken)) {
return false;
}
}
// 登录成功
mLoggedin = true;
return true;
}
/*
* 使
* activity
* invalidateToken
* null
*/
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken; // 令牌,是登录操作保证安全性的一个方法
// 获取账户管理器和所有Google账户
AccountManager accountManager = AccountManager.get(activity);// AccountManager这个类给用户提供了集中注册账号的接口
Account[] accounts = accountManager.getAccountsByType("com.google");// 获取全部以com.google结尾的account
// 检查是否有可用的Google账户
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account");
return null;
}
// 根据设置中的账户名选择账户
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
// 遍历获得的accounts信息寻找已经记录过的账户信息
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;
}
// 获取授权令牌
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
//如果是invalidateToken那么需要调用invalidateAuthToken(String, String)方法废除这个无效token
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false);
}
} catch (Exception e) {
Log.e(TAG, "get auth token failed");
authToken = null;
}
return authToken;
}
/* 使Gtask
* activity UI
* authToken
* truefalse
*/
private boolean tryToLoginGtask(Activity activity, String authToken) {
// 首次尝试登录Gtask
if (!loginGtask(authToken)) {
// 如果失败,尝试吊销令牌并重新获取后再次登录
authToken = loginGoogleAccount(activity, true);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// 使用新令牌再次尝试登录Gtask
if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed");
return false;
}
}
return true;
}
/* Gtask
* authToken
* truefalse
*/
private boolean loginGtask(String authToken) {
// 设置HTTP连接参数
int timeoutConnection = 10000;
int timeoutSocket = 15000; //socket是一种通信连接实现数据的交换的端口
HttpParams httpParameters = new BasicHttpParams(); //实例化一个新的HTTP参数类
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);//设置连接超时时间
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);//设置设置端口超时时间
mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore(); //设置本地cookie
mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// 使用授权令牌登录Gtask
try {
String loginUrl = mGetUrl + "?auth=" + authToken; //设置登录的url
HttpGet httpGet = new HttpGet(loginUrl); //通过登录的uri实例化网页上资源的查找
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// 检查是否获取到授权Cookie
// 获取CookieStore里存放的cookie,看如果存有“GTL”则说明有验证成功的有效的cookie
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
if (cookie.getName().contains("GTL")) {
hasAuthCookie = true;
}
}
if (!hasAuthCookie) {
Log.w(TAG, "it seems that there is no auth cookie");
}
// 获取client的内容具体操作是在返回的Content中截取从_setup(开始到)}</script>中间的字符串内容也就是gtask_url的内容
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
int begin = resString.indexOf(jsBegin);
int end = resString.lastIndexOf(jsEnd);
String jsString = null;
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString);
mClientVersion = js.getLong("v");
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
} catch (Exception e) {
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");
return false;
}
return true;
}
/* ID
* ID
*/
private int getActionId() {
return mActionId++;
}
/*
* 使HttpPost
* httpPost
*/
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
httpPost.setHeader("AT", "1");
return httpPost;
}
/* HttpEntity
* entity Http
*
* IOException
*/
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
// 通过URL得到HttpEntity对象如果不为空则使用getContent方法创建一个流将数据从网络都过来
if (entity.getContentEncoding() != null) {
contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding);
}
InputStream input = entity.getContent();
// 根据内容编码类型,对输入流进行解压
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {// GZIP是使用DEFLATE进行压缩数据的另一个压缩库
input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {// DEFLATE是一个无专利的压缩算法它可以实现无损数据压缩
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);
}
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();
}
}
/* POSTJSONObject
* js JSON
* JSONObject
* NetworkFailureException
*/
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {//未登录
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
//实例化一个httpPost的对象用来向服务器传输数据在这里就是发送请求而请求的内容在js里
HttpPost httpPost = createHttpPost();
try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString()));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); //UrlEncodedFormEntity()的形式比较单一,是普通的键值对
httpPost.setEntity(entity);
// 执行POST请求
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString);
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");
} catch (IOException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new NetworkFailureException("postRequest failed");
} catch (JSONException e) {
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");
}
}
/*
* .gtask.data.TaskTask
* jsonTask,jsPost
* postRequest
* 使task.setGidtasknew_ID
*/
public void createTask(Task task) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
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);
// 发送请求并处理响应
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("create task: handing jsonobject failed");
}
}
/*
* tasklist
* NetworkFailureException
*/
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
// 提交更新
commitUpdate();
try {
// 创建POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 动作列表
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));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("create tasklist: handing jsonobject failed");
}
}
/*
*
* 使JSONObject使jsPost.putPutUpdateArrayClientVersion
* 使postRequestjspost,
*/
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) {
try {
// 创建POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 添加更新的动作列表
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// 添加客户端版本信息
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("commit update: handing jsonobject failed");
}
}
}
/*
* node
* NetworkFailureException
*/
public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) {
// 若更新节点过多,则提交当前更新
if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate();
}
if (mUpdateArray == null)
// 创建更新节点的数组
mUpdateArray = new JSONArray();
// 添加节点更新动作
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
}
/*
* task,tasktask
* getGidtaskgid
* JSONObject.put(String name, Object value)task
* postRequest
* task
* preParent
* curParent
* NetworkFailureException
*/
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
// 提交当前更新
commitUpdate();
try {
// 创建POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 动作列表
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());
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid());
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()); //设置移动前所属列表
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); //设置当前所属列表
if (preParent != curParent) {
// 如果跨任务列表移动添加目标任务列表ID
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
}
actionList.put(action);
//最后将ACTION_LIST加入到jsPost中
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");
}
}
/*
*
* JSON
* 使postRequest
* node
* NetworkFailureException
*/
public void deleteNode(Node node) throws NetworkFailureException {
// 提交当前更新
commitUpdate();
try {
// 创建POST请求的JSON对象
JSONObject jsPost = new JSONObject();
// 动作列表
JSONArray actionList = new JSONArray();
// 添加删除节点的动作
node.setDeleted(true);
actionList.put(node.getUpdateAction(getActionId())); //这里会获取到删除操作的ID加入到actionLiast中
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");
}
}
/*
*
* GetURI使getResponseContent
* "_setup(")}</script>GTASK_JSON_LISTS
* JSONArray JSON
* NetworkFailureException
*/
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);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// 从响应中提取任务列表
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
int begin = resString.indexOf(jsBegin);
int end = resString.lastIndexOf(jsEnd);
String jsString = null;
if (begin != -1 && end != -1 && begin < end) {
jsString = resString.substring(begin + jsBegin.length(), end);
}
JSONObject js = new JSONObject(jsString);
//获取GTASK_JSON_LISTS
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");
}
}
/* TASKListgid,
*
* listGid
* JSONArray JSON
* NetworkFailureException
*/
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
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());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); //这里设置为传入的listGid
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 发送请求并处理响应
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
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");
}
}
/*
* Account
*/
public Account getSyncAccount() {
return mAccount;
}
/*
*
*/
public void resetUpdateArray() {
mUpdateArray = null;
}
}

@ -0,0 +1,904 @@
/*
* 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;
public class GTaskManager {
// GTaskManager类的标签用于日志输出等。
private static final String TAG = GTaskManager.class.getSimpleName();
// 任务状态:成功。
public static final int STATE_SUCCESS = 0;
// 任务状态:网络错误。
public static final int STATE_NETWORK_ERROR = 1;
// 任务状态:内部错误。
public static final int STATE_INTERNAL_ERROR = 2;
// 任务状态:同步进行中。
public static final int STATE_SYNC_IN_PROGRESS = 3;
// 任务状态:同步已取消。
public static final int STATE_SYNC_CANCELLED = 4;
// GTaskManager的单例实例。
private static GTaskManager mInstance = null;
// 关联的Activity对象。
private Activity mActivity;
// 上下文对象。
private Context mContext;
// 内容解析器。
private ContentResolver mContentResolver;
// 标记是否正在同步。
private boolean mSyncing;
// 标记是否已取消同步。
private boolean mCancelled;
// 保存任务列表的HashMap键为列表ID值为任务列表对象。
private HashMap<String, TaskList> mGTaskListHashMap;
// 保存任务的HashMap键为任务ID值为任务对象。
private HashMap<String, Node> mGTaskHashMap;
// 保存元数据的HashMap键为元数据ID值为元数据对象。
private HashMap<String, MetaData> mMetaHashMap;
// 元数据列表。
private TaskList mMetaList;
// 本地删除任务ID的集合。
private HashSet<Long> mLocalDeleteIdMap;
// 保存任务全局ID到本地ID的映射的HashMap。
private HashMap<String, Long> mGidToNid;
// 保存本地ID到任务全局ID的映射的HashMap。
private HashMap<Long, String> mNidToGid;
// GTaskManager的私有构造函数初始化各种状态和映射。
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>();
}
/* GTaskManager
* GTaskManager
* GTaskManager
*/
public static synchronized GTaskManager getInstance() { //可能运行在多线程环境下,使用语言级同步--synchronized
if (mInstance == null) {
mInstance = new GTaskManager();
}
return mInstance;
}
/*
*
* activity
*/
public synchronized void setActivityContext(Activity activity) {
mActivity = activity;
}
/*
* Google
* context
* asyncTask
*
*/
public int sync(Context context, GTaskASyncTask asyncTask) { //核心函数
if (mSyncing) {
Log.d(TAG, "Sync is in progress"); //创建日志文件调试信息debug
return STATE_SYNC_IN_PROGRESS;
}
mContext = context;
mContentResolver = mContext.getContentResolver();
mSyncing = true;
mCancelled = false;
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
try {
GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例,client--客户机
client.resetUpdateArray(); //JSONArray类型reset即置为NULL
// 尝试登录 Google 任务服务
if (!mCancelled) {
if (!client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
}
}
// 初始化 Google 任务列表
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList(); //获取Google上的JSONtasklist转为本地TaskList
// 执行内容同步工作
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
syncContent();
} catch (NetworkFailureException e) { //分为两种异常,此类异常为网络异常
Log.e(TAG, e.toString()); //创建日志文件调试信息error
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 {
// 无论成功或失败,最后都清理数据结构
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
mSyncing = false;
}
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
/* GTask
* GTaskClient
* NetworkFailureException
*
* NetworkFailureException
*/
private void initGTaskList() throws NetworkFailureException {
// 检查是否取消了操作
if (mCancelled)
return;
// 获取 GTask 客户端实例
GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例client应指远端客户机
try {
// 从客户端获取任务列表数组
JSONArray jsTaskLists = client.getTaskLists();
// 初始化元数据列表
mMetaList = null; //TaskList类型
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i); //JSONObject与JSONArray一个为对象一个为数组。此处取出单个JASONObject
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)) {
mMetaList = new TaskList(); //MetaList意为元表,Tasklist类型此处为初始化
mMetaList.setContentByRemoteJSON(object); //将JSON中部分数据复制到自己定义的对象中相对应的数据name->mname...
// 加载元数据
JSONArray jsMetas = client.getTaskList(gid);
for (int j = 0; j < jsMetas.length(); j++) {
object = (JSONObject) jsMetas.getJSONObject(j);
MetaData metaData = new MetaData(); //继承自Node
metaData.setContentByRemoteJSON(object);
if (metaData.isWorthSaving()) { //if not worth to savemetadata将不加入mMetaList
mMetaList.addChildTask(metaData);
if (metaData.getGid() != null) {
mMetaHashMap.put(metaData.getRelatedGid(), metaData);
}
}
}
}
}
// 如果元数据列表不存在,则创建新的元数据列表
if (mMetaList == null) {
mMetaList = new TaskList();
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META);
GTaskClient.getInstance().createTaskList(mMetaList);
}
// 初始化任务列表
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); //通过getString函数传入本地某个标志数据的名称获取其在远端的名称。
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
// 创建并初始化除元数据之外的其他任务列表
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList(); //继承自Node
tasklist.setContentByRemoteJSON(object);
mGTaskListHashMap.put(gid, tasklist);
mGTaskHashMap.put(gid, tasklist);
// 加载任务
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);
mGTaskHashMap.put(gid, task);
}
}
}
}
} catch (JSONException e) {
// 处理 JSON 解析异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");
}
}
/*
*
* ID
* NetworkFailureException
*/
private void syncContent() throws NetworkFailureException { //本地内容同步操作
int syncType;
Cursor c = null; //数据库指针
String gid;
Node node; //Node包含Sync_Action的不同类型
// 清除本地删除映射表
mLocalDeleteIdMap.clear(); //HashSet<Long>类型
// 如果操作已被取消,则直接返回
if (mCancelled) {
return;
}
// 处理本地删除的笔记
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)
}, null);
if (c != null) {
while (c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
// 从映射表中移除
mGTaskHashMap.remove(gid);
// 执行内容同步
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
}
// 添加到本地删除映射表
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
}
} else {
Log.w(TAG, "failed to query trash folder");
}
} finally {
if (c != null) {
// 关闭游标
c.close();
c = null;
}
}
// 首先同步文件夹信息
syncFolder();
// 处理数据库中存在的笔记
try {
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);
node = mGTaskHashMap.get(gid);
if (node != null) {
// 从映射表中移除
mGTaskHashMap.remove(gid);
// 更新ID映射
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
// 获取同步动作
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 如果没有GTask ID则视为本地新增
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 如果有GTask ID但本地不存在则视为远程删除
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
// 执行内容同步
doContentSync(syncType, node, c);
}
} else {
Log.w(TAG, "failed to query existing note in database");
}
} finally {
if (c != null) {
// 关闭游标
c.close();
c = null;
}
}
// 处理剩余项目
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator(); //Iterator迭代器
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
node = entry.getValue();
// 将剩余项目作为本地新增处理
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
// 检查是否取消操作清理本地删除表并更新本地同步ID
if (!mCancelled) {
// 批量删除本地已删除的笔记,如果失败则抛出异常
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
}
// 刷新本地同步ID
if (!mCancelled) {
// 提交更新
GTaskClient.getInstance().commitUpdate();
// 刷新本地同步ID
refreshLocalSyncId();
}
}
/*
*
*
* NetworkFailureException
*/
private void syncFolder() throws NetworkFailureException {
Cursor c = null;
String gid;
Node node;
int syncType;
if (mCancelled) {
return;
}
// 同步根文件夹
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 (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// 仅当系统文件夹的名称需要更新时执行内容同步
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;
}
}
// 同步通话记录文件夹
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 (node != null) {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);
mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
// 仅当系统文件夹的名称需要更新时执行内容同步
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;
}
}
// 同步本地已存在的文件夹
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);
// 判断节点是否为空,不为空则更新,为空则根据情况添加或删除
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 {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 本地添加
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 远程删除
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;
}
}
// 同步远程添加的文件夹
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();
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
}
if (!mCancelled)
GTaskClient.getInstance().commitUpdate();
}
/*
* syncType
* node
* c 使
* NetworkFailureException
*/
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
// 检查是否已取消同步操作
if (mCancelled) {
return;
}
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);
}
// 记录已删除的本地节点ID
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);
}
// 直接从本地数据库删除节点
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");
}
}
/*
* SqlNote
* SqlNote
* JSON ID ID
* SqlNote ID
* node null
* NetworkFailureException
*/
private void addLocalNode(Node node) throws NetworkFailureException {
// 如果操作已取消,则直接返回,不执行添加操作
if (mCancelled) {
return;
}
SqlNote sqlNote;
if (node instanceof TaskList) {
// 处理任务列表节点,根据节点名称设置特殊的 SqlNote 属性
if (node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
} else if (node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) {
sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER);
} else {
sqlNote = new SqlNote(mContext);
sqlNote.setContent(node.getLocalJSONFromContent());
sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
}
} else {
// 处理任务节点,从节点内容创建 JSON 对象,并根据需要调整 ID 字段
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();
try {
// 检查并处理 JSON 对象中的 Note 和 Data 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)) {
// 如果笔记 ID 已存在,则移除该 ID
note.remove(NoteColumns.ID);
}
}
}
if (js.has(GTaskStringUtils.META_HEAD_DATA)) {
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
// 如果数据 ID 已存在,则移除该 ID
data.remove(DataColumns.ID);
}
}
}
}
} catch (JSONException e) {
Log.w(TAG, e.toString());
e.printStackTrace();
}
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());
}
// 提交 SqlNote 到数据库,并更新 ID 映射关系
sqlNote.setGtaskId(node.getGid());
sqlNote.commit(false);
mGidToNid.put(node.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), node.getGid());
// 更新远程元数据
updateRemoteMeta(node.getGid(), sqlNote);
}
/*
* node
* c
* NetworkFailureException
*/
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote;
// 根据节点内容更新本地数据库中的笔记
sqlNote = new SqlNote(mContext, c);
sqlNote.setContent(node.getLocalJSONFromContent());
// 确定父节点ID任务则查找父任务ID否则默认为根文件夹ID
Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())
: new Long(Notes.ID_ROOT_FOLDER);
if (parentId == null) {
Log.e(TAG, "cannot find task's parent id locally");
throw new ActionFailureException("cannot update local node");
}
sqlNote.setParentId(parentId.longValue());
sqlNote.commit(true);
// 更新远程元数据
updateRemoteMeta(node.getGid(), sqlNote);
}
/*
* node
* c
* NetworkFailureException
*/
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote = new SqlNote(mContext, c); //从本地mContext中获取内容
Node n;
// 如果是任务类型,则远程创建任务;否则,根据条件判断是否需要创建新的任务列表
if (sqlNote.isNoteType()) {
Task task = new Task();
task.setContentByLocalJSON(sqlNote.getContent());
// 查找任务所属的任务列表ID
String parentGid = mNidToGid.get(sqlNote.getParentId());
if (parentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist"); //调试信息
throw new ActionFailureException("cannot add remote task");
}
mGTaskListHashMap.get(parentGid).addChildTask(task); //在本地生成的GTaskList中增加子结点
//登录远程服务器创建Task
GTaskClient.getInstance().createTask(task);
n = (Node) task;
// 更新远程元数据
updateRemoteMeta(task.getGid(), sqlNote);
} else {
TaskList tasklist = null;
// 判断是否需要创建新的任务列表
String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)
folderName += GTaskStringUtils.FOLDER_DEFAULT;
else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER)
folderName += GTaskStringUtils.FOLDER_CALL_NOTE;
else
folderName += sqlNote.getSnippet();
// 在已有的任务列表中查找匹配的条目
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
String gid = entry.getKey();
TaskList list = entry.getValue();
if (list.getName().equals(folderName)) {
tasklist = list;
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
}
break;
}
}
// 如果没有找到匹配的任务列表,则创建新的任务列表
if (tasklist == null) {
tasklist = new TaskList();
tasklist.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().createTaskList(tasklist);
mGTaskListHashMap.put(tasklist.getGid(), tasklist);
}
n = (Node) tasklist;
}
// 更新本地数据库中的笔记信息
sqlNote.setGtaskId(n.getGid());
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// 更新GID和ID的映射关系
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
}
/*
*
* node
* c
* NetworkFailureException
*/
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
// 检查是否已取消操作
if (mCancelled) {
return;
}
// 从数据库游标中创建 SqlNote 对象
SqlNote sqlNote = new SqlNote(mContext, c);
// 使用本地 JSON 格式更新远程节点内容
node.setContentByLocalJSON(sqlNote.getContent());
// 将节点添加到更新队列
GTaskClient.getInstance().addUpdateNode(node);
// 更新元数据
updateRemoteMeta(node.getGid(), sqlNote);
// 如果是笔记类型,检查并移动任务
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");
throw new ActionFailureException("cannot update remote task");
}
// 获取当前父任务列表对象
TaskList curParentList = mGTaskListHashMap.get(curParentGid);
// 如果任务的父任务列表发生变化,则移动任务
if (preParentList != curParentList) {
preParentList.removeChildTask(task);
curParentList.addChildTask(task);
GTaskClient.getInstance().moveTask(task, preParentList, curParentList);
}
}
// 重置本地修改标志,并提交更改
sqlNote.resetLocalModified();
//commit到本地数据库
sqlNote.commit(true);
}
/*
* gid
* sqlNote SqlNote
* 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);
mMetaHashMap.put(gid, metaData);
GTaskClient.getInstance().createTask(metaData);
}
}
}
/* ID
* gtaskID
* gtask IDActionFailureException
* NetworkFailureException
* ActionFailureException gtask ID
*/
private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) {
return;
}
// 清空现有的gtask列表和元数据准备获取最新的数据
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
initGTaskList();
Cursor c = null;
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)
}, NoteColumns.TYPE + " DESC"); //query语句五个参数NoteColumns.TYPE + " DESC"-----为按类型递减顺序返回查询结果。new String[] {String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)}------为选择参数。"(type<>? AND parent_id<>?)"-------指明返回行过滤器。SqlNote.PROJECTION_NOTE--------应返回的数据列的名字。Notes.CONTENT_NOTE_URI--------contentProvider包含所有数据集所对应的uri
if (c != null) {
while (c.moveToNext()) {
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid);
if (node != null) {
mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues(); //在ContentValues中创建键值对。准备通过contentResolver写入数据
values.put(NoteColumns.SYNC_ID, node.getLastModified());
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, //进行批量更改选择参数为NULL应该可以用insert替换参数分别为表名和需要更新的value对象。
c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else {
// 如果查询到的笔记没有对应的gtask ID则抛出异常
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 {
// 释放Cursor资源
if (c != null) {
c.close();
c = null;
}
}
}
/*
*
*/
public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name;
}
/*
*
*/
public void cancelSync() {
mCancelled = true;
}
}

@ -0,0 +1,168 @@
/*
* 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.
*/
/* GTaskSyncServiceGoogle
*/
package net.micode.notes.gtask.remote;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public class GTaskSyncService extends Service {
// 同步操作的类型
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.remote.gtask_sync_service";
// 广播中是否正在同步的标志
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
// 广播中的同步进度消息
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("");
mSyncTask.execute();
}
}
/*
*/
private void cancelSync() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
/* null
*/
@Override
public void onCreate() {
mSyncTask = null;
}
/*
*
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
case ACTION_START_SYNC:
startSync();
break;
case ACTION_CANCEL_SYNC:
cancelSync();
break;
default:
break;
}
return START_STICKY;
}
return super.onStartCommand(intent, flags, startId);
}
/*
*/
@Override
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
// 服务绑定时返回null此服务不提供绑定功能
public IBinder onBind(Intent intent) {
return null;
}
/* 广
* 广
*/
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);
}
/* 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);
}
/* 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);
}
/*
*
*/
public static boolean isSyncing() {
return mSyncTask != null;
}
/*
*
*/
public static String getProgressString() {
return mSyncProgress;
}
}
Loading…
Cancel
Save