From a402f29dc0ed3881e4b0ba23aa7995f537c5e0c3 Mon Sep 17 00:00:00 2001 From: Barmoby <2500584134@qq.com> Date: Fri, 14 Apr 2023 12:41:13 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ActionFailureException.java | 35 + .../GTaskASyncTask.java | 129 +++ .../GTaskClient.java | 585 +++++++++++++ .../GTaskManager.java | 800 ++++++++++++++++++ .../GTaskSyncService.java | 128 +++ .../NetworkFailureException.java | 35 + Exception_model包注释_ 马嘉序/Note.java | 253 ++++++ .../TaskList.java | 346 ++++++++ .../WorkingNote.java | 368 ++++++++ 9 files changed, 2679 insertions(+) create mode 100644 Exception_model包注释_ 马嘉序/ActionFailureException.java create mode 100644 Exception_model包注释_ 马嘉序/GTaskASyncTask.java create mode 100644 Exception_model包注释_ 马嘉序/GTaskClient.java create mode 100644 Exception_model包注释_ 马嘉序/GTaskManager.java create mode 100644 Exception_model包注释_ 马嘉序/GTaskSyncService.java create mode 100644 Exception_model包注释_ 马嘉序/NetworkFailureException.java create mode 100644 Exception_model包注释_ 马嘉序/Note.java create mode 100644 Exception_model包注释_ 马嘉序/TaskList.java create mode 100644 Exception_model包注释_ 马嘉序/WorkingNote.java diff --git a/Exception_model包注释_ 马嘉序/ActionFailureException.java b/Exception_model包注释_ 马嘉序/ActionFailureException.java new file mode 100644 index 0000000..9bad726 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/ActionFailureException.java @@ -0,0 +1,35 @@ +/* + * 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.exception; + +public class ActionFailureException extends RuntimeException { + private static final long serialVersionUID = 4425249765923293627L; + /*表示一个序列化类的版本号。当进行序列化和反序列化时,JVM 会检查类的 serialVersionUID 是否相同,如果不同就会抛出 + InvalidClassException 异常。这样可以确保反序列化的类与序列化时的类版本相同,避免因版本不同导致的问题。*/ + + public ActionFailureException() { + super(); //直接调用父类构造函数,即理解为Exception() + } + + public ActionFailureException(String paramString) { + super(paramString); //理解为Exception(paramString) + } + + public ActionFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); //理解为Exception(paramString, paramThrowable) + } +} diff --git a/Exception_model包注释_ 马嘉序/GTaskASyncTask.java b/Exception_model包注释_ 马嘉序/GTaskASyncTask.java new file mode 100644 index 0000000..bc567f1 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/GTaskASyncTask.java @@ -0,0 +1,129 @@ + +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesListActivity; +import net.micode.notes.ui.NotesPreferenceActivity; + + +public class GTaskASyncTask extends AsyncTask { + + 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(); + } + + public void cancelSync() { + mTaskManager.cancelSync(); + } + + public void publishProgess(String message) { //发布进度 + publishProgress(new String[] { + message + }); + } + + private void showNotification(int tickerId, String content) { //提示当前同步状态 + PendingIntent pendingIntent; + if (tickerId != R.string.ticker_success) { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), 0); + + } else { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), 0); + } + + + Notification.Builder builder = new Notification.Builder(mContext) + .setAutoCancel(true) + .setContentTitle(mContext.getString(R.string.app_name)) + .setContentText(content) + .setContentIntent(pendingIntent) + .setWhen(System.currentTimeMillis()) + .setOngoing(true); + Notification notification=builder.getNotification(); + 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]); + if (mContext instanceof GTaskSyncService) { + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); + } + } + + @Override + protected void onPostExecute(Integer 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) { + 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)); + } + if (mOnCompleteListener != null) { + new Thread(new Runnable() { + + public void run() { + mOnCompleteListener.onComplete(); + } + }).start(); + } + } +} diff --git a/Exception_model包注释_ 马嘉序/GTaskClient.java b/Exception_model包注释_ 马嘉序/GTaskClient.java new file mode 100644 index 0000000..25860af --- /dev/null +++ b/Exception_model包注释_ 马嘉序/GTaskClient.java @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.ui.NotesPreferenceActivity; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + + +public class GTaskClient { + private static final String TAG = GTaskClient.class.getSimpleName(); + + private static final String GTASK_URL = "https://mail.google.com/tasks/"; + + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; + + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; + + private static GTaskClient mInstance = null; + + private DefaultHttpClient mHttpClient; + + private String mGetUrl; + + private String mPostUrl; + + private long mClientVersion; + + private boolean mLoggedin; + + private long mLastLoginTime; + + private int mActionId; + + private Account mAccount; + + private JSONArray mUpdateArray; + + private GTaskClient() { + mHttpClient = null; + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + mClientVersion = -1; + mLoggedin = false; + mLastLoginTime = 0; + mActionId = 1; + mAccount = null; + mUpdateArray = null; + } + + public static synchronized GTaskClient getInstance() { + if (mInstance == null) { + mInstance = new GTaskClient(); + } + return mInstance; //返回实例化对象,若未定义则创建对象 + } + + public boolean login(Activity activity) { //登录操作 + // 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; //超过5分钟则重新登录 + } + + // need to re-login after account switch + if (mLoggedin + && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity + .getSyncAccountName(activity))) { + mLoggedin = false; + } + + if (mLoggedin) { + Log.d(TAG, "already logged in"); + return true; + } + + mLastLoginTime = System.currentTimeMillis(); //更新最后登录时间 + String authToken = loginGoogleAccount(activity, false); //是否登录到谷歌账户 + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + // login with custom domain if necessary 使用域名登录 + if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() + .endsWith("googlemail.com"))) { + StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); + int index = mAccount.name.indexOf('@') + 1; + String suffix = mAccount.name.substring(index); + url.append(suffix + "/"); + mGetUrl = url.toString() + "ig"; + mPostUrl = url.toString() + "r/ig"; + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true; + } + } + + // try to login with google official url 官方URL登录 + if (!mLoggedin) { + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) { + return false; + } + } + + mLoggedin = true; + return true; + } + + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + String authToken; + AccountManager accountManager = AccountManager.get(activity); + Account[] accounts = accountManager.getAccountsByType("com.google"); + + 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; + } + } + if (account != null) { + mAccount = account; + } else { + Log.e(TAG, "unable to get an account with the same name in the settings"); + return null; + } + + // get the token now 获取通行许可 + AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, + "goanna_mobile", null, activity, null, null); + try { + Bundle authTokenBundle = accountManagerFuture.getResult(); + authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); + if (invalidateToken) { + accountManager.invalidateAuthToken("com.google", authToken); + loginGoogleAccount(activity, false); + } + } catch (Exception e) { + Log.e(TAG, "get auth token failed"); + authToken = null; + } + + return authToken; + } + + private boolean tryToLoginGtask(Activity activity, String authToken) { //尝试登录GTask + if (!loginGtask(authToken)) { + // maybe the auth token is out of date, now let's invalidate the + // token and try again + authToken = loginGoogleAccount(activity, true); + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); + return false; + } + } + return true; + } + + private boolean loginGtask(String authToken) { //登录具体操作 + 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); + + // login gtask + try { + String loginUrl = mGetUrl + "?auth=" + authToken; //设置登录URL + HttpGet httpGet = new HttpGet(loginUrl); //实例化登录URL + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the cookie now + List 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"); + } + + // get the client version + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + 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; + } + + private int getActionId() { + return mActionId++; + } + + 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; + } + + private String getResponseContent(HttpEntity entity) throws IOException { + String contentEncoding = null; + if (entity.getContentEncoding() != null) { + contentEncoding = entity.getContentEncoding().getValue(); + Log.d(TAG, "encoding: " + contentEncoding); + } + + InputStream input = entity.getContent(); + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { + input = new GZIPInputStream(entity.getContent()); + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("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(); + } + } + + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + HttpPost httpPost = createHttpPost(); + try { + LinkedList list = new LinkedList(); + list.add(new BasicNameValuePair("r", js.toString())); + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); + httpPost.setEntity(entity); + + // execute the 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"); + } + } + + public void createTask(Task task) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(task.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // post + JSONObject jsResponse = postRequest(jsPost); + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create task: handing jsonobject failed"); + } + } + + public void createTaskList(TaskList tasklist) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(tasklist.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client version + 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"); + } + } + + public void commitUpdate() throws NetworkFailureException { + if (mUpdateArray != null) { + try { + JSONObject jsPost = new JSONObject(); + + // action_list + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("commit update: handing jsonobject failed"); + } + } + } + + public void addUpdateNode(Node node) throws NetworkFailureException { + if (node != null) { + // too many update items may result in an error + // set max to 10 items + if (mUpdateArray != null && mUpdateArray.length() > 10) { + commitUpdate(); + } + + if (mUpdateArray == null) + mUpdateArray = new JSONArray(); + mUpdateArray.put(node.getUpdateAction(getActionId())); + } + } + + public void moveTask(Task task, TaskList preParent, TaskList curParent) + throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + 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) { + // 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()); + if (preParent != curParent) { + // put the dest_list only if moving between tasklists + action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); + } + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("move task: handing jsonobject failed"); + } + } + + public void deleteNode(Node node) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + node.setDeleted(true); + actionList.put(node.getUpdateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("delete node: handing jsonobject failed"); + } + } + + public JSONArray getTaskLists() throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + try { + HttpGet httpGet = new HttpGet(mGetUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the task list + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + 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); + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task lists: handing jasonobject failed"); + } + } + + public JSONArray getTaskList(String listGid) throws NetworkFailureException { + 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_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); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + 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"); + } + } + + public Account getSyncAccount() { + return mAccount; + } + + public void resetUpdateArray() { + mUpdateArray = null; + } +} diff --git a/Exception_model包注释_ 马嘉序/GTaskManager.java b/Exception_model包注释_ 马嘉序/GTaskManager.java new file mode 100644 index 0000000..d2b4082 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/GTaskManager.java @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.data.MetaData; +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.SqlNote; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +public class 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; + + private static GTaskManager mInstance = null; + + private Activity mActivity; + + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mSyncing; + + private boolean mCancelled; + + private HashMap mGTaskListHashMap; + + private HashMap mGTaskHashMap; + + private HashMap mMetaHashMap; + + private TaskList mMetaList; + + private HashSet mLocalDeleteIdMap; + + private HashMap mGidToNid; + + private HashMap mNidToGid; + + private GTaskManager() { + mSyncing = false; + mCancelled = false; + mGTaskListHashMap = new HashMap(); + mGTaskHashMap = new HashMap(); + mMetaHashMap = new HashMap(); + mMetaList = null; + mLocalDeleteIdMap = new HashSet(); + mGidToNid = new HashMap(); + mNidToGid = new HashMap(); + } + + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); + } + return mInstance; + } + + public synchronized void setActivityContext(Activity activity) { + // used for getting authtoken + 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(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + + try { + GTaskClient client = GTaskClient.getInstance(); + client.resetUpdateArray(); + + // login google task + 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)); + 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 { + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + mSyncing = false; + } + + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; + } + + private void initGTaskList() throws NetworkFailureException { + if (mCancelled) + return; + GTaskClient client = GTaskClient.getInstance(); + try { + 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)) { + 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) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); + } + } + } + } + } + + // create meta list if not existed + if (mMetaList == null) { + mMetaList = new TaskList(); + 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)) { + 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); + mGTaskHashMap.put(gid, task); + } + } + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("initGTaskList: handing JSONObject failed"); + } + } + + private void syncContent() throws NetworkFailureException { + int syncType; + Cursor c = null; + String gid; + Node node; + + mLocalDeleteIdMap.clear(); + + if (mCancelled) { + return; + } + + // for local deleted note + 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; + } + } + + // sync folder first + syncFolder(); + + // for note existing in database + 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); + 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) { + // 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 note in database"); + } + + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // go through remaining items + Iterator> iter = mGTaskHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + 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"); + } + } + + // refresh local sync id + if (!mCancelled) { + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); + } + + } + + private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + 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 (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 (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); + 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) { + // 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> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry 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(); + } + + 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); + } + 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"); + } + } + + private void addLocalNode(Node node) throws NetworkFailureException { + if (mCancelled) { + return; + } + + 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); + } 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 { + sqlNote = new SqlNote(mContext); + JSONObject js = node.getLocalJSONFromContent(); + try { + 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 + 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)) { + // the data id is not available, have to create + // a new one + data.remove(DataColumns.ID); + } + } + } + + } + } catch (JSONException e) { + Log.w(TAG, e.toString()); + e.printStackTrace(); + } + sqlNote.setContent(js); + + 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()); + sqlNote.commit(false); + + // update gid-nid mapping + mGidToNid.put(node.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), node.getGid()); + + // update meta + 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()); + + 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); + + // update meta info + updateRemoteMeta(node.getGid(), sqlNote); + } + + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + Node n; + + // update remotely + if (sqlNote.isNoteType()) { + Task task = new Task(); + task.setContentByLocalJSON(sqlNote.getContent()); + + 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); + + 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; + else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) + folderName += GTaskStringUtils.FOLDER_CALL_NOTE; + else + folderName += sqlNote.getSnippet(); + + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry 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; + } + } + + // no match we can add now + if (tasklist == null) { + tasklist = new TaskList(); + tasklist.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().createTaskList(tasklist); + mGTaskListHashMap.put(tasklist.getGid(), tasklist); + } + n = (Node) tasklist; + } + + // update local note + sqlNote.setGtaskId(n.getGid()); + sqlNote.commit(false); + sqlNote.resetLocalModified(); + sqlNote.commit(true); + + // gid-id mapping + mGidToNid.put(n.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), n.getGid()); + } + + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + + // update remotely + 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(); + + 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); + } + } + + // clear local modified flag + sqlNote.resetLocalModified(); + sqlNote.commit(true); + } + + 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); + } + } + } + + private void refreshLocalSyncId() throws NetworkFailureException { + if (mCancelled) { + return; + } + + // get the latest gtask list + mGTaskHashMap.clear(); + mGTaskListHashMap.clear(); + mMetaHashMap.clear(); + initGTaskList(); + + 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()) { + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); + Node node = mGTaskHashMap.get(gid); + if (node != null) { + 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; + } + } + } + + public String getSyncAccount() { + return GTaskClient.getInstance().getSyncAccount().name; + } + + public void cancelSync() { + mCancelled = true; + } +} diff --git a/Exception_model包注释_ 马嘉序/GTaskSyncService.java b/Exception_model包注释_ 马嘉序/GTaskSyncService.java new file mode 100644 index 0000000..cca36f7 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/GTaskSyncService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.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(); + } + } + + @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(); + } + } + + 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); + } + + 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); + } + + 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; + } +} diff --git a/Exception_model包注释_ 马嘉序/NetworkFailureException.java b/Exception_model包注释_ 马嘉序/NetworkFailureException.java new file mode 100644 index 0000000..2e62437 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/NetworkFailureException.java @@ -0,0 +1,35 @@ +/* + * 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.exception; + +public class NetworkFailureException extends Exception { + private static final long serialVersionUID = 2107610287180234136L; + /*表示一个序列化类的版本号。当进行序列化和反序列化时,JVM 会检查类的 serialVersionUID 是否相同,如果不同就会抛出 + InvalidClassException 异常。这样可以确保反序列化的类与序列化时的类版本相同,避免因版本不同导致的问题。*/ + + public NetworkFailureException() { + super(); //直接调用父类构造函数,即理解为Exception() + } + + public NetworkFailureException(String paramString) { + super(paramString); //理解为Exception(paramString) + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); //理解为Exception(paramString, paramThrowable) + } +} diff --git a/Exception_model包注释_ 马嘉序/Note.java b/Exception_model包注释_ 马嘉序/Note.java new file mode 100644 index 0000000..6706cf6 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/Note.java @@ -0,0 +1,253 @@ +/* + * 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.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + + +public class Note { + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + private static final String TAG = "Note"; + /** + * Create a new note id for adding a new note to databases + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() { + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + } + + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + } + + public long getTextDataId() { + return mNoteData.mTextDataId; + } + + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + } + + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + } + + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData { + private long mTextDataId; + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues; + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + return null; + } + } +} diff --git a/Exception_model包注释_ 马嘉序/TaskList.java b/Exception_model包注释_ 马嘉序/TaskList.java new file mode 100644 index 0000000..61b1dd9 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/TaskList.java @@ -0,0 +1,346 @@ +/* + * 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.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class TaskList extends Node { //对抽象类Node的拓展 + private static final String TAG = TaskList.class.getSimpleName(); + + private int mIndex; //这是一个指针 + + private ArrayList mChildren; //定义数组变量mChildren,存储Task类型系列数据 + + public TaskList() { //构造函数初始化 + super(); //引用父类构造函数,理解为Node() + mChildren = new ArrayList(); + mIndex = 1; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); //生成JSONObject类型变量js + + //下面代码中string内容参照GTaskStringUtils中共同阅读 + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { //指捕捉JSONException异常 + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + String name = folder.getString(NoteColumns.SNIPPET); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE); + else + Log.e(TAG, "invalid system folder"); + } else { + Log.e(TAG, "error type"); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + try { + JSONObject js = new JSONObject(); + JSONObject folder = new JSONObject(); + + String folderName = getName(); + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + folder.put(NoteColumns.SNIPPET, folderName); + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public int getSyncAction(Cursor c) { + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public int getChildTaskCount() { + return mChildren.size(); //返回TaskList数组mChildren的大小 + } + + public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1)); + //每一次数组变化时,setPriorSibling都要紧跟更改 + task.setParent(this); + } + } + return ret; //返回布尔值——是否成功添加任务 + } + + public boolean addChildTask(Task task, int index) { //即添加新任务 + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "add child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (task != null && pos == -1) { + mChildren.add(index, task); + + // update the task list + Task preTask = null; + Task afterTask = null; + if (index != 0) + preTask = mChildren.get(index - 1); + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + task.setPriorSibling(preTask); + if (afterTask != null) + afterTask.setPriorSibling(task); + } + + return true; + } + + public boolean removeChildTask(Task task) { //删除TaskList数组中的成员 + boolean ret = false; + int index = mChildren.indexOf(task); + if (index != -1) { + ret = mChildren.remove(task); + + if (ret) { + // reset prior sibling and parent + task.setPriorSibling(null); + task.setParent(null); + + // update the task list + if (index != mChildren.size()) { + mChildren.get(index).setPriorSibling( + index == 0 ? null : mChildren.get(index - 1)); + } + } + } + return ret; //布尔值——是否删除成功 + } + + public boolean moveChildTask(Task task, int index) { //将TaskList中含有的某个成员移动至index位置 + + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "move child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list"); + return false; + } + + if (pos == index) + return true; + return (removeChildTask(task) && addChildTask(task, index)); + //在移除成员和添加成员均成功时返回真值 + } + + public Task findChildTaskByGid(String gid) { //按gid寻找Task + for (int i = 0; i < mChildren.size(); i++) { + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); //返回指定Task的index + } + + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + return mChildren.get(index); //返回指定index的Task + } + + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) + return task; //返回指定gid的Task + } + return null; + } + + public ArrayList getChildTaskList() { + return this.mChildren; + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return this.mIndex; + } +} diff --git a/Exception_model包注释_ 马嘉序/WorkingNote.java b/Exception_model包注释_ 马嘉序/WorkingNote.java new file mode 100644 index 0000000..be081e4 --- /dev/null +++ b/Exception_model包注释_ 马嘉序/WorkingNote.java @@ -0,0 +1,368 @@ +/* + * 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.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + + +public class WorkingNote { + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0; + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + } + } + + public boolean existInDatabase() { + return mNoteId > 0; + } + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) { + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) { + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + } + + public String getContent() { + return mContent; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() { + return mBgColorId; + } + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() { + return mMode; + } + + public long getNoteId() { + return mNoteId; + } + + public long getFolderId() { + return mFolderId; + } + + public int getWidgetId() { + return mWidgetId; + } + + public int getWidgetType() { + return mWidgetType; + } + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged(); + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set); + + /** + * Call when user create note from widget + */ + void onWidgetChanged(); + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode); + } +} From cb2a01674170c2f240945ae21c9215a042827b5b Mon Sep 17 00:00:00 2001 From: Barmoby <2500584134@qq.com> Date: Thu, 27 Apr 2023 21:43:36 +0800 Subject: [PATCH 2/4] =?UTF-8?q?UML=E4=BA=A4=E4=BA=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ActionFailureException.java | 35 - .../GTaskASyncTask.java | 129 --- .../GTaskClient.java | 585 ------------- .../GTaskManager.java | 800 ------------------ .../GTaskSyncService.java | 128 --- .../NetworkFailureException.java | 35 - Exception_model包注释_ 马嘉序/Note.java | 253 ------ .../TaskList.java | 346 -------- .../WorkingNote.java | 368 -------- 9 files changed, 2679 deletions(-) delete mode 100644 Exception_model包注释_ 马嘉序/ActionFailureException.java delete mode 100644 Exception_model包注释_ 马嘉序/GTaskASyncTask.java delete mode 100644 Exception_model包注释_ 马嘉序/GTaskClient.java delete mode 100644 Exception_model包注释_ 马嘉序/GTaskManager.java delete mode 100644 Exception_model包注释_ 马嘉序/GTaskSyncService.java delete mode 100644 Exception_model包注释_ 马嘉序/NetworkFailureException.java delete mode 100644 Exception_model包注释_ 马嘉序/Note.java delete mode 100644 Exception_model包注释_ 马嘉序/TaskList.java delete mode 100644 Exception_model包注释_ 马嘉序/WorkingNote.java diff --git a/Exception_model包注释_ 马嘉序/ActionFailureException.java b/Exception_model包注释_ 马嘉序/ActionFailureException.java deleted file mode 100644 index 9bad726..0000000 --- a/Exception_model包注释_ 马嘉序/ActionFailureException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.exception; - -public class ActionFailureException extends RuntimeException { - private static final long serialVersionUID = 4425249765923293627L; - /*表示一个序列化类的版本号。当进行序列化和反序列化时,JVM 会检查类的 serialVersionUID 是否相同,如果不同就会抛出 - InvalidClassException 异常。这样可以确保反序列化的类与序列化时的类版本相同,避免因版本不同导致的问题。*/ - - public ActionFailureException() { - super(); //直接调用父类构造函数,即理解为Exception() - } - - public ActionFailureException(String paramString) { - super(paramString); //理解为Exception(paramString) - } - - public ActionFailureException(String paramString, Throwable paramThrowable) { - super(paramString, paramThrowable); //理解为Exception(paramString, paramThrowable) - } -} diff --git a/Exception_model包注释_ 马嘉序/GTaskASyncTask.java b/Exception_model包注释_ 马嘉序/GTaskASyncTask.java deleted file mode 100644 index bc567f1..0000000 --- a/Exception_model包注释_ 马嘉序/GTaskASyncTask.java +++ /dev/null @@ -1,129 +0,0 @@ - -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.micode.notes.gtask.remote; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesListActivity; -import net.micode.notes.ui.NotesPreferenceActivity; - - -public class GTaskASyncTask extends AsyncTask { - - 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(); - } - - public void cancelSync() { - mTaskManager.cancelSync(); - } - - public void publishProgess(String message) { //发布进度 - publishProgress(new String[] { - message - }); - } - - private void showNotification(int tickerId, String content) { //提示当前同步状态 - PendingIntent pendingIntent; - if (tickerId != R.string.ticker_success) { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesPreferenceActivity.class), 0); - - } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), 0); - } - - - Notification.Builder builder = new Notification.Builder(mContext) - .setAutoCancel(true) - .setContentTitle(mContext.getString(R.string.app_name)) - .setContentText(content) - .setContentIntent(pendingIntent) - .setWhen(System.currentTimeMillis()) - .setOngoing(true); - Notification notification=builder.getNotification(); - 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]); - if (mContext instanceof GTaskSyncService) { - ((GTaskSyncService) mContext).sendBroadcast(progress[0]); - } - } - - @Override - protected void onPostExecute(Integer 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) { - 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)); - } - if (mOnCompleteListener != null) { - new Thread(new Runnable() { - - public void run() { - mOnCompleteListener.onComplete(); - } - }).start(); - } - } -} diff --git a/Exception_model包注释_ 马嘉序/GTaskClient.java b/Exception_model包注释_ 马嘉序/GTaskClient.java deleted file mode 100644 index 25860af..0000000 --- a/Exception_model包注释_ 马嘉序/GTaskClient.java +++ /dev/null @@ -1,585 +0,0 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.micode.notes.gtask.remote; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerFuture; -import android.app.Activity; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.gtask.data.Node; -import net.micode.notes.gtask.data.Task; -import net.micode.notes.gtask.data.TaskList; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.gtask.exception.NetworkFailureException; -import net.micode.notes.tool.GTaskStringUtils; -import net.micode.notes.ui.NotesPreferenceActivity; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.LinkedList; -import java.util.List; -import java.util.zip.GZIPInputStream; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; - - -public class GTaskClient { - private static final String TAG = GTaskClient.class.getSimpleName(); - - private static final String GTASK_URL = "https://mail.google.com/tasks/"; - - private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - - private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - - private static GTaskClient mInstance = null; - - private DefaultHttpClient mHttpClient; - - private String mGetUrl; - - private String mPostUrl; - - private long mClientVersion; - - private boolean mLoggedin; - - private long mLastLoginTime; - - private int mActionId; - - private Account mAccount; - - private JSONArray mUpdateArray; - - private GTaskClient() { - mHttpClient = null; - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - mClientVersion = -1; - mLoggedin = false; - mLastLoginTime = 0; - mActionId = 1; - mAccount = null; - mUpdateArray = null; - } - - public static synchronized GTaskClient getInstance() { - if (mInstance == null) { - mInstance = new GTaskClient(); - } - return mInstance; //返回实例化对象,若未定义则创建对象 - } - - public boolean login(Activity activity) { //登录操作 - // 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; //超过5分钟则重新登录 - } - - // need to re-login after account switch - if (mLoggedin - && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity - .getSyncAccountName(activity))) { - mLoggedin = false; - } - - if (mLoggedin) { - Log.d(TAG, "already logged in"); - return true; - } - - mLastLoginTime = System.currentTimeMillis(); //更新最后登录时间 - String authToken = loginGoogleAccount(activity, false); //是否登录到谷歌账户 - if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; - } - - // login with custom domain if necessary 使用域名登录 - if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() - .endsWith("googlemail.com"))) { - StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); - int index = mAccount.name.indexOf('@') + 1; - String suffix = mAccount.name.substring(index); - url.append(suffix + "/"); - mGetUrl = url.toString() + "ig"; - mPostUrl = url.toString() + "r/ig"; - - if (tryToLoginGtask(activity, authToken)) { - mLoggedin = true; - } - } - - // try to login with google official url 官方URL登录 - if (!mLoggedin) { - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - if (!tryToLoginGtask(activity, authToken)) { - return false; - } - } - - mLoggedin = true; - return true; - } - - private String loginGoogleAccount(Activity activity, boolean invalidateToken) { - String authToken; - AccountManager accountManager = AccountManager.get(activity); - Account[] accounts = accountManager.getAccountsByType("com.google"); - - 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; - } - } - if (account != null) { - mAccount = account; - } else { - Log.e(TAG, "unable to get an account with the same name in the settings"); - return null; - } - - // get the token now 获取通行许可 - AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, - "goanna_mobile", null, activity, null, null); - try { - Bundle authTokenBundle = accountManagerFuture.getResult(); - authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); - if (invalidateToken) { - accountManager.invalidateAuthToken("com.google", authToken); - loginGoogleAccount(activity, false); - } - } catch (Exception e) { - Log.e(TAG, "get auth token failed"); - authToken = null; - } - - return authToken; - } - - private boolean tryToLoginGtask(Activity activity, String authToken) { //尝试登录GTask - if (!loginGtask(authToken)) { - // maybe the auth token is out of date, now let's invalidate the - // token and try again - authToken = loginGoogleAccount(activity, true); - if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; - } - - if (!loginGtask(authToken)) { - Log.e(TAG, "login gtask failed"); - return false; - } - } - return true; - } - - private boolean loginGtask(String authToken) { //登录具体操作 - 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); - - // login gtask - try { - String loginUrl = mGetUrl + "?auth=" + authToken; //设置登录URL - HttpGet httpGet = new HttpGet(loginUrl); //实例化登录URL - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the cookie now - List 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"); - } - - // get the client version - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - 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; - } - - private int getActionId() { - return mActionId++; - } - - 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; - } - - private String getResponseContent(HttpEntity entity) throws IOException { - String contentEncoding = null; - if (entity.getContentEncoding() != null) { - contentEncoding = entity.getContentEncoding().getValue(); - Log.d(TAG, "encoding: " + contentEncoding); - } - - InputStream input = entity.getContent(); - if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { - input = new GZIPInputStream(entity.getContent()); - } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("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(); - } - } - - private JSONObject postRequest(JSONObject js) throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); - } - - HttpPost httpPost = createHttpPost(); - try { - LinkedList list = new LinkedList(); - list.add(new BasicNameValuePair("r", js.toString())); - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); - httpPost.setEntity(entity); - - // execute the 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"); - } - } - - public void createTask(Task task) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(task.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // post - JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("create task: handing jsonobject failed"); - } - } - - public void createTaskList(TaskList tasklist) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(tasklist.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client version - 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"); - } - } - - public void commitUpdate() throws NetworkFailureException { - if (mUpdateArray != null) { - try { - JSONObject jsPost = new JSONObject(); - - // action_list - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - mUpdateArray = null; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("commit update: handing jsonobject failed"); - } - } - } - - public void addUpdateNode(Node node) throws NetworkFailureException { - if (node != null) { - // too many update items may result in an error - // set max to 10 items - if (mUpdateArray != null && mUpdateArray.length() > 10) { - commitUpdate(); - } - - if (mUpdateArray == null) - mUpdateArray = new JSONArray(); - mUpdateArray.put(node.getUpdateAction(getActionId())); - } - } - - public void moveTask(Task task, TaskList preParent, TaskList curParent) - throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - 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) { - // 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()); - if (preParent != curParent) { - // put the dest_list only if moving between tasklists - action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); - } - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("move task: handing jsonobject failed"); - } - } - - public void deleteNode(Node node) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - node.setDeleted(true); - actionList.put(node.getUpdateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - mUpdateArray = null; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("delete node: handing jsonobject failed"); - } - } - - public JSONArray getTaskLists() throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); - } - - try { - HttpGet httpGet = new HttpGet(mGetUrl); - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the task list - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - 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); - return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); - } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (IOException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("get task lists: handing jasonobject failed"); - } - } - - public JSONArray getTaskList(String listGid) throws NetworkFailureException { - 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_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); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - 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"); - } - } - - public Account getSyncAccount() { - return mAccount; - } - - public void resetUpdateArray() { - mUpdateArray = null; - } -} diff --git a/Exception_model包注释_ 马嘉序/GTaskManager.java b/Exception_model包注释_ 马嘉序/GTaskManager.java deleted file mode 100644 index d2b4082..0000000 --- a/Exception_model包注释_ 马嘉序/GTaskManager.java +++ /dev/null @@ -1,800 +0,0 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.micode.notes.gtask.remote; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.data.MetaData; -import net.micode.notes.gtask.data.Node; -import net.micode.notes.gtask.data.SqlNote; -import net.micode.notes.gtask.data.Task; -import net.micode.notes.gtask.data.TaskList; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.gtask.exception.NetworkFailureException; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; - - -public class 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; - - private static GTaskManager mInstance = null; - - private Activity mActivity; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mSyncing; - - private boolean mCancelled; - - private HashMap mGTaskListHashMap; - - private HashMap mGTaskHashMap; - - private HashMap mMetaHashMap; - - private TaskList mMetaList; - - private HashSet mLocalDeleteIdMap; - - private HashMap mGidToNid; - - private HashMap mNidToGid; - - private GTaskManager() { - mSyncing = false; - mCancelled = false; - mGTaskListHashMap = new HashMap(); - mGTaskHashMap = new HashMap(); - mMetaHashMap = new HashMap(); - mMetaList = null; - mLocalDeleteIdMap = new HashSet(); - mGidToNid = new HashMap(); - mNidToGid = new HashMap(); - } - - public static synchronized GTaskManager getInstance() { - if (mInstance == null) { - mInstance = new GTaskManager(); - } - return mInstance; - } - - public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken - 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(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - - try { - GTaskClient client = GTaskClient.getInstance(); - client.resetUpdateArray(); - - // login google task - 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)); - 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 { - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - mSyncing = false; - } - - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; - } - - private void initGTaskList() throws NetworkFailureException { - if (mCancelled) - return; - GTaskClient client = GTaskClient.getInstance(); - try { - 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)) { - 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) { - mMetaHashMap.put(metaData.getRelatedGid(), metaData); - } - } - } - } - } - - // create meta list if not existed - if (mMetaList == null) { - mMetaList = new TaskList(); - 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)) { - 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); - mGTaskHashMap.put(gid, task); - } - } - } - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("initGTaskList: handing JSONObject failed"); - } - } - - private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; - - mLocalDeleteIdMap.clear(); - - if (mCancelled) { - return; - } - - // for local deleted note - 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; - } - } - - // sync folder first - syncFolder(); - - // for note existing in database - 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); - 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) { - // 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 note in database"); - } - - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // go through remaining items - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - 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"); - } - } - - // refresh local sync id - if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); - } - - } - - private void syncFolder() throws NetworkFailureException { - Cursor c = null; - String gid; - 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 (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 (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); - 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) { - // 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> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry 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(); - } - - 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); - } - 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"); - } - } - - private void addLocalNode(Node node) throws NetworkFailureException { - if (mCancelled) { - return; - } - - 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); - } 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 { - sqlNote = new SqlNote(mContext); - JSONObject js = node.getLocalJSONFromContent(); - try { - 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 - 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)) { - // the data id is not available, have to create - // a new one - data.remove(DataColumns.ID); - } - } - } - - } - } catch (JSONException e) { - Log.w(TAG, e.toString()); - e.printStackTrace(); - } - sqlNote.setContent(js); - - 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()); - sqlNote.commit(false); - - // update gid-nid mapping - mGidToNid.put(node.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), node.getGid()); - - // update meta - 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()); - - 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); - - // update meta info - updateRemoteMeta(node.getGid(), sqlNote); - } - - private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; - - // update remotely - if (sqlNote.isNoteType()) { - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); - - 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); - - 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; - else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) - folderName += GTaskStringUtils.FOLDER_CALL_NOTE; - else - folderName += sqlNote.getSnippet(); - - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry 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; - } - } - - // no match we can add now - if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); - mGTaskListHashMap.put(tasklist.getGid(), tasklist); - } - n = (Node) tasklist; - } - - // update local note - sqlNote.setGtaskId(n.getGid()); - sqlNote.commit(false); - sqlNote.resetLocalModified(); - sqlNote.commit(true); - - // gid-id mapping - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); - } - - private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - - // update remotely - 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(); - - 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); - } - } - - // clear local modified flag - sqlNote.resetLocalModified(); - sqlNote.commit(true); - } - - 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); - } - } - } - - private void refreshLocalSyncId() throws NetworkFailureException { - if (mCancelled) { - return; - } - - // get the latest gtask list - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - initGTaskList(); - - 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()) { - String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - Node node = mGTaskHashMap.get(gid); - if (node != null) { - 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; - } - } - } - - public String getSyncAccount() { - return GTaskClient.getInstance().getSyncAccount().name; - } - - public void cancelSync() { - mCancelled = true; - } -} diff --git a/Exception_model包注释_ 马嘉序/GTaskSyncService.java b/Exception_model包注释_ 马嘉序/GTaskSyncService.java deleted file mode 100644 index cca36f7..0000000 --- a/Exception_model包注释_ 马嘉序/GTaskSyncService.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.micode.notes.gtask.remote; - -import android.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(); - } - } - - @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(); - } - } - - 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); - } - - 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); - } - - 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; - } -} diff --git a/Exception_model包注释_ 马嘉序/NetworkFailureException.java b/Exception_model包注释_ 马嘉序/NetworkFailureException.java deleted file mode 100644 index 2e62437..0000000 --- a/Exception_model包注释_ 马嘉序/NetworkFailureException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.exception; - -public class NetworkFailureException extends Exception { - private static final long serialVersionUID = 2107610287180234136L; - /*表示一个序列化类的版本号。当进行序列化和反序列化时,JVM 会检查类的 serialVersionUID 是否相同,如果不同就会抛出 - InvalidClassException 异常。这样可以确保反序列化的类与序列化时的类版本相同,避免因版本不同导致的问题。*/ - - public NetworkFailureException() { - super(); //直接调用父类构造函数,即理解为Exception() - } - - public NetworkFailureException(String paramString) { - super(paramString); //理解为Exception(paramString) - } - - public NetworkFailureException(String paramString, Throwable paramThrowable) { - super(paramString, paramThrowable); //理解为Exception(paramString, paramThrowable) - } -} diff --git a/Exception_model包注释_ 马嘉序/Note.java b/Exception_model包注释_ 马嘉序/Note.java deleted file mode 100644 index 6706cf6..0000000 --- a/Exception_model包注释_ 马嘉序/Note.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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.model; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.net.Uri; -import android.os.RemoteException; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; - -import java.util.ArrayList; - - -public class Note { - private ContentValues mNoteDiffValues; - private NoteData mNoteData; - private static final String TAG = "Note"; - /** - * Create a new note id for adding a new note to databases - */ - public static synchronized long getNewNoteId(Context context, long folderId) { - // Create a new note in the database - ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis(); - values.put(NoteColumns.CREATED_DATE, createdTime); - values.put(NoteColumns.MODIFIED_DATE, createdTime); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - values.put(NoteColumns.PARENT_ID, folderId); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - - long noteId = 0; - try { - noteId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - noteId = 0; - } - if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId); - } - return noteId; - } - - public Note() { - mNoteDiffValues = new ContentValues(); - mNoteData = new NoteData(); - } - - public void setNoteValue(String key, String value) { - mNoteDiffValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - public void setTextData(String key, String value) { - mNoteData.setTextData(key, value); - } - - public void setTextDataId(long id) { - mNoteData.setTextDataId(id); - } - - public long getTextDataId() { - return mNoteData.mTextDataId; - } - - public void setCallDataId(long id) { - mNoteData.setCallDataId(id); - } - - public void setCallData(String key, String value) { - mNoteData.setCallData(key, value); - } - - public boolean isLocalModified() { - return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); - } - - public boolean syncNote(Context context, long noteId) { - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - if (!isLocalModified()) { - return true; - } - - /** - * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and - * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the - * note data info - */ - if (context.getContentResolver().update( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, - null) == 0) { - Log.e(TAG, "Update note error, should not happen"); - // Do not return, fall through - } - mNoteDiffValues.clear(); - - if (mNoteData.isLocalModified() - && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { - return false; - } - - return true; - } - - private class NoteData { - private long mTextDataId; - - private ContentValues mTextDataValues; - - private long mCallDataId; - - private ContentValues mCallDataValues; - - private static final String TAG = "NoteData"; - - public NoteData() { - mTextDataValues = new ContentValues(); - mCallDataValues = new ContentValues(); - mTextDataId = 0; - mCallDataId = 0; - } - - boolean isLocalModified() { - return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; - } - - void setTextDataId(long id) { - if(id <= 0) { - throw new IllegalArgumentException("Text data id should larger than 0"); - } - mTextDataId = id; - } - - void setCallDataId(long id) { - if (id <= 0) { - throw new IllegalArgumentException("Call data id should larger than 0"); - } - mCallDataId = id; - } - - void setCallData(String key, String value) { - mCallDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - void setTextData(String key, String value) { - mTextDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - Uri pushIntoContentResolver(Context context, long noteId) { - /** - * Check for safety - */ - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - ArrayList operationList = new ArrayList(); - ContentProviderOperation.Builder builder = null; - - if(mTextDataValues.size() > 0) { - mTextDataValues.put(DataColumns.NOTE_ID, noteId); - if (mTextDataId == 0) { - mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mTextDataValues); - try { - setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new text data fail with noteId" + noteId); - mTextDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mTextDataId)); - builder.withValues(mTextDataValues); - operationList.add(builder.build()); - } - mTextDataValues.clear(); - } - - if(mCallDataValues.size() > 0) { - mCallDataValues.put(DataColumns.NOTE_ID, noteId); - if (mCallDataId == 0) { - mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mCallDataValues); - try { - setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new call data fail with noteId" + noteId); - mCallDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mCallDataId)); - builder.withValues(mCallDataValues); - operationList.add(builder.build()); - } - mCallDataValues.clear(); - } - - if (operationList.size() > 0) { - try { - ContentProviderResult[] results = context.getContentResolver().applyBatch( - Notes.AUTHORITY, operationList); - return (results == null || results.length == 0 || results[0] == null) ? null - : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } - } - return null; - } - } -} diff --git a/Exception_model包注释_ 马嘉序/TaskList.java b/Exception_model包注释_ 马嘉序/TaskList.java deleted file mode 100644 index 61b1dd9..0000000 --- a/Exception_model包注释_ 马嘉序/TaskList.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 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.data; - -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; - - -public class TaskList extends Node { //对抽象类Node的拓展 - private static final String TAG = TaskList.class.getSimpleName(); - - private int mIndex; //这是一个指针 - - private ArrayList mChildren; //定义数组变量mChildren,存储Task类型系列数据 - - public TaskList() { //构造函数初始化 - super(); //引用父类构造函数,理解为Node() - mChildren = new ArrayList(); - mIndex = 1; - } - - public JSONObject getCreateAction(int actionId) { - JSONObject js = new JSONObject(); //生成JSONObject类型变量js - - //下面代码中string内容参照GTaskStringUtils中共同阅读 - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // index - js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); - entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, - GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate tasklist-create jsonobject"); - } - - return js; - } - - public JSONObject getUpdateAction(int actionId) { - JSONObject js = new JSONObject(); - - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // id - js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - } catch (JSONException e) { //指捕捉JSONException异常 - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate tasklist-update jsonobject"); - } - - return js; - } - - public void setContentByRemoteJSON(JSONObject js) { - if (js != null) { - try { - // id - if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { - setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); - } - - // last_modified - if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { - setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); - } - - // name - if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { - setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); - } - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to get tasklist content from jsonobject"); - } - } - } - - public void setContentByLocalJSON(JSONObject js) { - if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { - Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); - } - - try { - JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - - if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - String name = folder.getString(NoteColumns.SNIPPET); - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); - } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { - if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); - else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE); - else - Log.e(TAG, "invalid system folder"); - } else { - Log.e(TAG, "error type"); - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - } - - public JSONObject getLocalJSONFromContent() { - try { - JSONObject js = new JSONObject(); - JSONObject folder = new JSONObject(); - - String folderName = getName(); - if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) - folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), - folderName.length()); - folder.put(NoteColumns.SNIPPET, folderName); - if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) - || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) - folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - else - folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - - js.put(GTaskStringUtils.META_HEAD_NOTE, folder); - - return js; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return null; - } - } - - public int getSyncAction(Cursor c) { - try { - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // there is no local update - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // no update both side - return SYNC_ACTION_NONE; - } else { - // apply remote to local - return SYNC_ACTION_UPDATE_LOCAL; - } - } else { - // validate gtask id - if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { - Log.e(TAG, "gtask id doesn't match"); - return SYNC_ACTION_ERROR; - } - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only - return SYNC_ACTION_UPDATE_REMOTE; - } else { - // for folder conflicts, just apply local modification - return SYNC_ACTION_UPDATE_REMOTE; - } - } - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - - return SYNC_ACTION_ERROR; - } - - public int getChildTaskCount() { - return mChildren.size(); //返回TaskList数组mChildren的大小 - } - - public boolean addChildTask(Task task) { - boolean ret = false; - if (task != null && !mChildren.contains(task)) { - ret = mChildren.add(task); - if (ret) { - // need to set prior sibling and parent - task.setPriorSibling(mChildren.isEmpty() ? null : mChildren - .get(mChildren.size() - 1)); - //每一次数组变化时,setPriorSibling都要紧跟更改 - task.setParent(this); - } - } - return ret; //返回布尔值——是否成功添加任务 - } - - public boolean addChildTask(Task task, int index) { //即添加新任务 - if (index < 0 || index > mChildren.size()) { - Log.e(TAG, "add child task: invalid index"); - return false; - } - - int pos = mChildren.indexOf(task); - if (task != null && pos == -1) { - mChildren.add(index, task); - - // update the task list - Task preTask = null; - Task afterTask = null; - if (index != 0) - preTask = mChildren.get(index - 1); - if (index != mChildren.size() - 1) - afterTask = mChildren.get(index + 1); - - task.setPriorSibling(preTask); - if (afterTask != null) - afterTask.setPriorSibling(task); - } - - return true; - } - - public boolean removeChildTask(Task task) { //删除TaskList数组中的成员 - boolean ret = false; - int index = mChildren.indexOf(task); - if (index != -1) { - ret = mChildren.remove(task); - - if (ret) { - // reset prior sibling and parent - task.setPriorSibling(null); - task.setParent(null); - - // update the task list - if (index != mChildren.size()) { - mChildren.get(index).setPriorSibling( - index == 0 ? null : mChildren.get(index - 1)); - } - } - } - return ret; //布尔值——是否删除成功 - } - - public boolean moveChildTask(Task task, int index) { //将TaskList中含有的某个成员移动至index位置 - - if (index < 0 || index >= mChildren.size()) { - Log.e(TAG, "move child task: invalid index"); - return false; - } - - int pos = mChildren.indexOf(task); - if (pos == -1) { - Log.e(TAG, "move child task: the task should in the list"); - return false; - } - - if (pos == index) - return true; - return (removeChildTask(task) && addChildTask(task, index)); - //在移除成员和添加成员均成功时返回真值 - } - - public Task findChildTaskByGid(String gid) { //按gid寻找Task - for (int i = 0; i < mChildren.size(); i++) { - Task t = mChildren.get(i); - if (t.getGid().equals(gid)) { - return t; - } - } - return null; - } - - public int getChildTaskIndex(Task task) { - return mChildren.indexOf(task); //返回指定Task的index - } - - public Task getChildTaskByIndex(int index) { - if (index < 0 || index >= mChildren.size()) { - Log.e(TAG, "getTaskByIndex: invalid index"); - return null; - } - return mChildren.get(index); //返回指定index的Task - } - - public Task getChilTaskByGid(String gid) { - for (Task task : mChildren) { - if (task.getGid().equals(gid)) - return task; //返回指定gid的Task - } - return null; - } - - public ArrayList getChildTaskList() { - return this.mChildren; - } - - public void setIndex(int index) { - this.mIndex = index; - } - - public int getIndex() { - return this.mIndex; - } -} diff --git a/Exception_model包注释_ 马嘉序/WorkingNote.java b/Exception_model包注释_ 马嘉序/WorkingNote.java deleted file mode 100644 index be081e4..0000000 --- a/Exception_model包注释_ 马嘉序/WorkingNote.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * 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.model; - -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.tool.ResourceParser.NoteBgResources; - - -public class WorkingNote { - // Note for the working note - private Note mNote; - // Note Id - private long mNoteId; - // Note content - private String mContent; - // Note mode - private int mMode; - - private long mAlertDate; - - private long mModifiedDate; - - private int mBgColorId; - - private int mWidgetId; - - private int mWidgetType; - - private long mFolderId; - - private Context mContext; - - private static final String TAG = "WorkingNote"; - - private boolean mIsDeleted; - - private NoteSettingChangedListener mNoteSettingStatusListener; - - public static final String[] DATA_PROJECTION = new String[] { - DataColumns.ID, - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - }; - - public static final String[] NOTE_PROJECTION = new String[] { - NoteColumns.PARENT_ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - NoteColumns.MODIFIED_DATE - }; - - private static final int DATA_ID_COLUMN = 0; - - private static final int DATA_CONTENT_COLUMN = 1; - - private static final int DATA_MIME_TYPE_COLUMN = 2; - - private static final int DATA_MODE_COLUMN = 3; - - private static final int NOTE_PARENT_ID_COLUMN = 0; - - private static final int NOTE_ALERTED_DATE_COLUMN = 1; - - private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - - private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - - private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - - // New note construct - private WorkingNote(Context context, long folderId) { - mContext = context; - mAlertDate = 0; - mModifiedDate = System.currentTimeMillis(); - mFolderId = folderId; - mNote = new Note(); - mNoteId = 0; - mIsDeleted = false; - mMode = 0; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - } - - // Existing note construct - private WorkingNote(Context context, long noteId, long folderId) { - mContext = context; - mNoteId = noteId; - mFolderId = folderId; - mIsDeleted = false; - mNote = new Note(); - loadNote(); - } - - private void loadNote() { - Cursor cursor = mContext.getContentResolver().query( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, - null, null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); - mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); - mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); - mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); - mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); - } - cursor.close(); - } else { - Log.e(TAG, "No note with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note with id " + mNoteId); - } - loadNoteData(); - } - - private void loadNoteData() { - Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, - DataColumns.NOTE_ID + "=?", new String[] { - String.valueOf(mNoteId) - }, null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - do { - String type = cursor.getString(DATA_MIME_TYPE_COLUMN); - if (DataConstants.NOTE.equals(type)) { - mContent = cursor.getString(DATA_CONTENT_COLUMN); - mMode = cursor.getInt(DATA_MODE_COLUMN); - mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); - } else if (DataConstants.CALL_NOTE.equals(type)) { - mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); - } else { - Log.d(TAG, "Wrong note type with type:" + type); - } - } while (cursor.moveToNext()); - } - cursor.close(); - } else { - Log.e(TAG, "No data with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); - } - } - - public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, - int widgetType, int defaultBgColorId) { - WorkingNote note = new WorkingNote(context, folderId); - note.setBgColorId(defaultBgColorId); - note.setWidgetId(widgetId); - note.setWidgetType(widgetType); - return note; - } - - public static WorkingNote load(Context context, long id) { - return new WorkingNote(context, id, 0); - } - - public synchronized boolean saveNote() { - if (isWorthSaving()) { - if (!existInDatabase()) { - if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { - Log.e(TAG, "Create new note fail with id:" + mNoteId); - return false; - } - } - - mNote.syncNote(mContext, mNoteId); - - /** - * Update widget content if there exist any widget of this note - */ - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE - && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - return true; - } else { - return false; - } - } - - public boolean existInDatabase() { - return mNoteId > 0; - } - - private boolean isWorthSaving() { - if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) - || (existInDatabase() && !mNote.isLocalModified())) { - return false; - } else { - return true; - } - } - - public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { - mNoteSettingStatusListener = l; - } - - public void setAlertDate(long date, boolean set) { - if (date != mAlertDate) { - mAlertDate = date; - mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); - } - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onClockAlertChanged(date, set); - } - } - - public void markDeleted(boolean mark) { - mIsDeleted = mark; - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - } - - public void setBgColorId(int id) { - if (id != mBgColorId) { - mBgColorId = id; - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onBackgroundColorChanged(); - } - mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); - } - } - - public void setCheckListMode(int mode) { - if (mMode != mode) { - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); - } - mMode = mode; - mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); - } - } - - public void setWidgetType(int type) { - if (type != mWidgetType) { - mWidgetType = type; - mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); - } - } - - public void setWidgetId(int id) { - if (id != mWidgetId) { - mWidgetId = id; - mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); - } - } - - public void setWorkingText(String text) { - if (!TextUtils.equals(mContent, text)) { - mContent = text; - mNote.setTextData(DataColumns.CONTENT, mContent); - } - } - - public void convertToCallNote(String phoneNumber, long callDate) { - mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); - mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); - mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); - } - - public boolean hasClockAlert() { - return (mAlertDate > 0 ? true : false); - } - - public String getContent() { - return mContent; - } - - public long getAlertDate() { - return mAlertDate; - } - - public long getModifiedDate() { - return mModifiedDate; - } - - public int getBgColorResId() { - return NoteBgResources.getNoteBgResource(mBgColorId); - } - - public int getBgColorId() { - return mBgColorId; - } - - public int getTitleBgResId() { - return NoteBgResources.getNoteTitleBgResource(mBgColorId); - } - - public int getCheckListMode() { - return mMode; - } - - public long getNoteId() { - return mNoteId; - } - - public long getFolderId() { - return mFolderId; - } - - public int getWidgetId() { - return mWidgetId; - } - - public int getWidgetType() { - return mWidgetType; - } - - public interface NoteSettingChangedListener { - /** - * Called when the background color of current note has just changed - */ - void onBackgroundColorChanged(); - - /** - * Called when user set clock - */ - void onClockAlertChanged(long date, boolean set); - - /** - * Call when user create note from widget - */ - void onWidgetChanged(); - - /** - * Call when switch between check list mode and normal mode - * @param oldMode is previous mode before change - * @param newMode is new mode - */ - void onCheckListModeChanged(int oldMode, int newMode); - } -} From 4918167681a43f58979510e0af24052689075445 Mon Sep 17 00:00:00 2001 From: Barmoby <2500584134@qq.com> Date: Thu, 27 Apr 2023 21:49:11 +0800 Subject: [PATCH 3/4] =?UTF-8?q?UML=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UML新建功能交互图/口令使用.png | Bin 0 -> 24572 bytes UML新建功能交互图/口令添加.png | Bin 0 -> 27465 bytes UML新建功能交互图/口令解除.png | Bin 0 -> 20014 bytes UML新建功能交互图/标识设置.png | Bin 0 -> 21711 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 UML新建功能交互图/口令使用.png create mode 100644 UML新建功能交互图/口令添加.png create mode 100644 UML新建功能交互图/口令解除.png create mode 100644 UML新建功能交互图/标识设置.png diff --git a/UML新建功能交互图/口令使用.png b/UML新建功能交互图/口令使用.png new file mode 100644 index 0000000000000000000000000000000000000000..af64fdaab9157173065a7cc696c80853308e46e5 GIT binary patch literal 24572 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�}bD5A_@gY4E9*{wel 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 YwERvo)i+xYJfW1q)78&qol`;+03wrTO#lD@ literal 0 HcmV?d00001 From 3bf3d2c91e5516df8c79fcfd7dbe4108e636ea30 Mon Sep 17 00:00:00 2001 From: Barmoby <2500584134@qq.com> Date: Thu, 27 Apr 2023 21:52:53 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Exception_model包注释_ 马嘉序.zip | Bin 0 -> 25477 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Exception_model包注释_ 马嘉序.zip diff --git a/Exception_model包注释_ 马嘉序.zip b/Exception_model包注释_ 马嘉序.zip new file mode 100644 index 0000000000000000000000000000000000000000..d24ce0c40cb2f5d4a9c2bd843d40782384fc7d60 GIT binary patch literal 25477 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 Ql&8Bb3;+PB_v6)n0S#B8ivR!s literal 0 HcmV?d00001