diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java index fb79578..15504be 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -1,45 +1,33 @@ /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * // 版权声明,指定代码的版权所有者和代码编写的时间范围 * * Licensed under the Apache License, Version 2.0 (the "License"); - * // 声明该代码是根据Apache License 2.0版本授权的 * 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 - * // 许可证的URL链接 * * 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; -// 指定该Java文件的包路径 public class ActionFailureException extends RuntimeException { - // 声明一个名为ActionFailureException的公共类,该类继承自RuntimeException private static final long serialVersionUID = 4425249765923293627L; - // 声明一个常量serialVersionUID,用于序列化时保持版本的兼容性 public ActionFailureException() { super(); - // 默认构造函数,调用父类RuntimeException的无参构造函数 } public ActionFailureException(String paramString) { super(paramString); - // 带有一个字符串参数的构造函数,调用父类RuntimeException的带字符串参数的构造函数 } public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); - // 带有一个字符串和一个Throwable参数的构造函数,调用父类RuntimeException的相应构造函数 } } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java index 6d438dd..b08cfb1 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -14,29 +14,20 @@ * limitations under the License. */ -// 该类所属的包名,表明这个类是位于net.micode.notes.gtask.exception包下,用于存放相关的异常类 package net.micode.notes.gtask.exception; -// NetworkFailureException类继承自Exception,用于表示网络相关的操作出现故障时抛出的异常情况 public class NetworkFailureException extends Exception { - // 序列化版本号,用于在对象序列化和反序列化过程中确保版本的兼容性,这里是一个固定的长整型数值 private static final long serialVersionUID = 2107610287180234136L; - // 无参构造函数,调用父类(Exception)的无参构造函数,用于创建一个默认的NetworkFailureException实例,不附带任何详细信息 public NetworkFailureException() { super(); } - // 带有一个字符串参数的构造函数,该字符串参数通常用于传递异常相关的详细描述信息, - // 调用父类(Exception)的对应构造函数,将传入的字符串作为异常的详细消息 public NetworkFailureException(String paramString) { super(paramString); } - // 带有一个字符串参数和一个Throwable参数的构造函数,字符串参数用于传递异常相关的详细描述信息, - // Throwable参数通常用于关联引起当前异常的其他异常(例如底层网络库抛出的原始异常等), - // 调用父类(Exception)的对应构造函数进行初始化,将两个参数传递给父类构造函数 public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } -} \ No newline at end of file +} diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index 7669523..426b9ee 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -1,15 +1,17 @@ /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * - * 根据Apache License, Version 2.0(以下简称“许可证”)授权; - * 除非遵守许可证,否则不得使用此文件。 - * 你可以在以下网址获得许可证的副本: + * 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; @@ -25,79 +27,95 @@ import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; -// 异步任务类,用于执行GTask同步操作 +/** + * GTaskASyncTask 是一个用于在后台执行 Google Task 同步操作的异步任务类。 + * 它继承自 Android 的 AsyncTask 类,处理同步过程中的通知显示和结果处理。 + */ public class GTaskASyncTask extends AsyncTask { - // 定义GTASK同步通知的ID + // 定义 Google Task 同步的通知 ID private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - // 定义完成监听接口 + /** + * 定义接口 OnCompleteListener,用于在同步完成后执行特定的操作 + */ public interface OnCompleteListener { void onComplete(); } - // 上下文对象 + // 上下文环境 private Context mContext; + // 通知管理器 private NotificationManager mNotifiManager; - // 任务管理器 + + // Google Task 管理器 private GTaskManager mTaskManager; - // 完成监听器 + + // 同步完成后的监听器 private OnCompleteListener mOnCompleteListener; - // 构造函数,初始化上下文、通知管理器、任务管理器和完成监听器 + /** + * 构造函数,初始化上下文环境、监听器、通知管理器和 Google Task 管理器 + * @param context 应用程序的上下文环境 + * @param listener 同步完成后的监听器 + */ public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; - mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mTaskManager = GTaskManager.getInstance(); } - // 取消同步的方法 + /** + * 取消同步操作 + */ public void cancelSync() { mTaskManager.cancelSync(); } - // 发布进度消息的方法 + /** + * 发布进度信息,显示在通知栏中 + * @param message 进度信息 + */ public void publishProgess(String message) { - publishProgress(new String[] { - message - }); + publishProgress(new String[]{message}); } - // 显示通知的方法 + /** + * 显示通知的方法 + * @param tickerId 通知的标题资源 ID + * @param content 通知的内容 + */ private void showNotification(int tickerId, String content) { - // 创建通知对象 - Notification notification = new Notification(R.drawable.notification, mContext - .getString(tickerId), System.currentTimeMillis()); + Notification notification = new Notification(R.drawable.notification, mContext.getString(tickerId), System.currentTimeMillis()); notification.defaults = Notification.DEFAULT_LIGHTS; notification.flags = Notification.FLAG_AUTO_CANCEL; - // 根据tickerId创建不同的PendingIntent PendingIntent pendingIntent; if (tickerId != R.string.ticker_success) { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesPreferenceActivity.class), 0); + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), 0); + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } - // 设置通知的最新事件信息 - notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, - pendingIntent); - // 发送通知 + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } - // 后台执行同步操作的方法 + /** + * 在后台线程中执行同步任务 + * @param unused 无用参数 + * @return 同步任务的结果状态码 + */ @Override protected Integer doInBackground(Void... unused) { - publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext))); return mTaskManager.sync(mContext, this); } - // 更新进度的方法 + /** + * 当同步过程中有进度更新时调用此方法,在通知栏中显示进度信息 + * @param progress 更新的进度信息 + */ @Override protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); @@ -106,30 +124,28 @@ public class GTaskASyncTask extends AsyncTask { } } - // 执行完毕后的方法 + /** + * 同步任务完成后调用此方法,根据结果状态码进行不同的处理 + * @param result 同步任务的结果状态码 + */ @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())); + 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)); + 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(); } } -} \ No newline at end of file +} diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java index db1a192..01ffcb8 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -1,5 +1,21 @@ +/* + * 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; @@ -7,7 +23,7 @@ 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; @@ -15,7 +31,7 @@ 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; @@ -33,7 +49,7 @@ 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; @@ -43,40 +59,39 @@ import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - -// GTaskClient类用于与Google Tasks进行交互,实现诸如登录、任务和任务列表的创建、更新、移动、删除以及获取相关数据等功能 + + +// GTaskClient 类用于与 Google Tasks 远程服务进行交互 public class GTaskClient { private static final String TAG = GTaskClient.class.getSimpleName(); - - // Google Tasks的基础URL + private static final String GTASK_URL = "https://mail.google.com/tasks/"; - // 用于获取Google Tasks数据的URL + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - // 用于向Google Tasks发送POST请求的URL + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - + private static GTaskClient mInstance = null; - - // 用于发送HTTP请求的HttpClient对象 + private DefaultHttpClient mHttpClient; - // 获取数据的具体URL + private String mGetUrl; - // 发送POST请求的具体URL + private String mPostUrl; - // 客户端版本号 + private long mClientVersion; - // 标记是否已登录 + private boolean mLoggedin; - // 上次登录的时间戳 + private long mLastLoginTime; - // 用于标识操作的ID,每次操作自增 + private int mActionId; - // 当前使用的账户 + private Account mAccount; - // 用于暂存更新操作相关数据的JSON数组 + private JSONArray mUpdateArray; - - // 私有构造函数,初始化相关成员变量 + + // 私有构造函数,确保只能通过 getInstance 方法获取实例 private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -88,44 +103,44 @@ public class GTaskClient { mAccount = null; mUpdateArray = null; } - - // 获取GTaskClient的单例实例 + + // 获取 GTaskClient 的单例实例 public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } - - // 执行登录操作 + + // 登录 Google Tasks,如果已登录则检查登录状态是否过期或账户是否切换 public boolean login(Activity activity) { - // 假设Cookie在5分钟后过期,若距离上次登录时间超过5分钟,则需要重新登录 + // we suppose that the cookie would expire after 5 minutes + // then we need to re-login final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - - // 如果已登录,但当前账户与设置中的同步账户不一致,也需要重新登录 + + // need to re-login after account switch if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } - + if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } - + mLastLoginTime = System.currentTimeMillis(); - // 登录Google账户获取授权令牌 String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - - // 如果账户不是以gmail.com或googlemail.com结尾(可能是自定义域名),则使用自定义域名相关的URL进行登录 + + // 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/"); @@ -134,13 +149,13 @@ public class GTaskClient { url.append(suffix + "/"); mGetUrl = url.toString() + "ig"; mPostUrl = url.toString() + "r/ig"; - + if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; } } - - // 如果使用自定义域名登录失败,则尝试使用Google官方URL登录 + + // try to login with google official url if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; @@ -148,25 +163,22 @@ public class GTaskClient { return false; } } - + mLoggedin = true; return true; } - - // 登录Google账户获取授权令牌 + + // 获取 Google 账户的认证令牌,如果需要则失效当前令牌 private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; - // 获取账户管理器 AccountManager accountManager = AccountManager.get(activity); - // 获取所有Google类型的账户 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) { @@ -181,15 +193,14 @@ public class GTaskClient { 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); } @@ -197,20 +208,21 @@ public class GTaskClient { Log.e(TAG, "get auth token failed"); authToken = null; } - + return authToken; } - - // 尝试登录Google Tasks + + // 尝试登录 Google Tasks,如果第一次失败则尝试失效认证令牌后重新登录 private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { - // 如果登录失败,可能是授权令牌过期,先使令牌失效再重新尝试登录Google账户获取新令牌,然后再次登录Google Tasks + // 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; @@ -218,12 +230,11 @@ public class GTaskClient { } return true; } - - // 实际执行登录Google Tasks的操作,获取相关信息(如客户端版本号等) + + // 使用认证令牌登录 Google Tasks 并获取客户端版本 private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; - // 设置HTTP请求的参数,包括连接超时和读取超时时间 HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); @@ -231,15 +242,15 @@ public class GTaskClient { BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - - // 登录Google Tasks,发送带有授权令牌的GET请求 + + // login gtask try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - - // 获取登录后的Cookie信息,检查是否包含认证相关的Cookie(名称包含"GTL") + + // get the cookie now List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; for (Cookie cookie : cookies) { @@ -250,8 +261,8 @@ public class GTaskClient { if (!hasAuthCookie) { Log.w(TAG, "it seems that there is no auth cookie"); } - - // 从响应中获取客户端版本号等信息,通过解析返回的JavaScript代码中的相关数据 + + // get the client version String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -268,35 +279,35 @@ public class GTaskClient { e.printStackTrace(); return false; } catch (Exception e) { - // 捕获其他所有异常,若发生异常则登录失败 + // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; } - + return true; } - - // 获取下一个操作的ID + + // 获取并递增一个唯一的动作 ID private int getActionId() { return mActionId++; } - - // 创建用于发送POST请求的HttpPost对象,并设置相关请求头 + + // 创建一个用于发送 POST 请求的 HttpPost 对象,并设置必要的头部信息 private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("AT", "1"); return httpPost; } - - // 从HTTP实体中获取响应内容,根据内容编码(如gzip、deflate等)进行相应的解压处理 + + // 从 HttpEntity 中读取响应内容,并根据内容编码进行解压 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()); @@ -304,12 +315,12 @@ public class GTaskClient { 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) { @@ -321,26 +332,26 @@ public class GTaskClient { input.close(); } } - - // 发送POST请求,将JSON数据发送到Google Tasks服务器,并处理响应返回的JSON数据 + + // 发送 POST 请求并获取响应的 JSONObject 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); - - // 执行POST请求并获取响应,然后解析响应内容为JSONObject返回 + + // 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(); @@ -359,73 +370,73 @@ public class GTaskClient { throw new ActionFailureException("error occurs when posting request"); } } - - // 创建一个任务并发送到Google Tasks服务器 + + // 创建一个新的任务,并发送相应的 POST 请求到 Google Tasks 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请求并处理响应,获取新创建任务的ID并设置到任务对象中 + + // 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"); } } - - // 创建一个任务列表并发送到Google Tasks服务器 + + // 创建一个新的任务列表,并发送相应的 POST 请求到 Google Tasks 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请求并处理响应,获取新创建任务列表的ID并设置到任务列表对象中 + + // 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"); } } - - // 提交更新操作,将暂存的更新操作数组发送到服务器 + + // 提交更新列表中的所有动作到 Google Tasks 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) { @@ -435,25 +446,23 @@ public class GTaskClient { } } } - - // 添加一个节点的更新操作到暂存的更新操作数组中,限制最多10个更新项 - // 将节点的更新操作添加到更新数组中,如果更新数组中的元素超过10个,则先提交更新 + + // 添加一个节点的更新动作到更新列表中 public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { - // 过多的更新项可能会导致错误,所以设置最大更新项数量为10个 + // too many update items may result in an error + // set max to 10 items if (mUpdateArray != null && mUpdateArray.length() > 10) { commitUpdate(); } - - // 如果更新数组为空,则创建一个新的JSONArray对象用于存放更新操作 + if (mUpdateArray == null) mUpdateArray = new JSONArray(); - // 将节点的更新操作(通过节点获取,操作ID使用当前的操作ID)添加到更新数组中 mUpdateArray.put(node.getUpdateAction(getActionId())); } } - - // 移动任务,将任务从一个任务列表移动到另一个任务列表(可能是同一个列表内移动位置) + + // 移动一个任务到另一个任务列表,并发送相应的 POST 请求到 Google Tasks public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); @@ -461,92 +470,75 @@ public class GTaskClient { 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); - // 设置操作的唯一ID,通过获取下一个可用的操作ID来赋值 action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - // 设置要移动的任务的全局唯一ID action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); - // 如果移动前后的父任务列表相同(即在同一个任务列表内移动),并且任务不是该列表中的第一个任务, - // 则设置其前一个兄弟任务的ID,用于确定移动后的顺序 if (preParent == curParent && task.getPriorSibling() != null) { + // 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()); } - // 设置任务原来所在的任务列表的全局唯一ID(源列表) action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); - // 设置任务要移动到的目标任务列表的父级任务列表的全局唯一ID(目标父列表) action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); - // 如果移动是在不同的任务列表之间进行,则设置目标任务列表的全局唯一ID(目标列表) if (preParent != curParent) { + // put the dest_list only if moving between tasklists action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } - // 将这个移动操作添加到操作列表中 actionList.put(action); - // 将操作列表添加到要发送的JSON数据对象中,对应预定义的操作列表的JSON键 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // 设置客户端版本号,对应预定义的客户端版本号的JSON键 + + // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // 发送POST请求,将包含移动任务操作信息的JSON数据发送到服务器进行处理 + postRequest(jsPost); - + } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果JSON处理出现异常,抛出操作失败异常,并说明是移动任务时处理JSON对象失败 throw new ActionFailureException("move task: handing jsonobject failed"); } } - - // 删除节点(比如任务、任务列表等),通过设置节点的删除标记为true,并将其更新操作发送到服务器来实现删除 + + // 删除一个节点,并发送相应的 POST 请求到 Google Tasks 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())); - // 将操作列表添加到要发送的JSON数据对象中,对应预定义的操作列表的JSON键 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // 设置客户端版本号,对应预定义的客户端版本号的JSON键 + + // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // 发送POST请求,将包含删除节点操作信息的JSON数据发送到服务器进行处理 + postRequest(jsPost); - // 发送成功后,将更新数组置空,因为相关更新操作已提交完成 mUpdateArray = null; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果JSON处理出现异常,抛出操作失败异常,并说明是删除节点时处理JSON对象失败 throw new ActionFailureException("delete node: handing jsonobject failed"); } } - - // 获取所有的任务列表信息,前提是已经登录成功,否则会抛出异常提示需要先登录 + + // 获取所有任务列表,并返回相应的 JSONArray public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } - + try { - // 创建一个HTTP GET请求对象,用于获取任务列表数据,请求的URL是之前设置好的获取数据的URL HttpGet httpGet = new HttpGet(mGetUrl); HttpResponse response = null; - // 执行HTTP GET请求,获取服务器的响应 response = mHttpClient.execute(httpGet); - - // 从响应的实体中获取内容,并进行处理,提取出包含任务列表信息的JSON数据部分 + + // get the task list String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -557,70 +549,58 @@ public class GTaskClient { jsString = resString.substring(begin + jsBegin.length(), end); } JSONObject js = new JSONObject(jsString); - // 从解析后的JSON对象中获取任务列表数组并返回,对应预定义的任务列表的JSON键 return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); } catch (ClientProtocolException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果HTTP协议相关操作出现异常,抛出网络失败异常,并说明是获取任务列表时HTTP GET请求失败 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果输入输出操作出现异常,抛出网络失败异常,并说明是获取任务列表时HTTP GET请求失败 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果JSON处理出现异常,抛出操作失败异常,并说明是获取任务列表时处理JSON对象失败 throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - - // 根据给定的任务列表全局唯一ID,获取该任务列表中的任务信息,需要先提交之前暂存的更新操作 + + // 获取指定任务列表中的所有任务,并返回相应的 JSONArray 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); - // 设置操作的唯一ID,通过获取下一个可用的操作ID来赋值 action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - // 设置要获取任务的任务列表的全局唯一ID action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); - // 设置是否获取已删除的任务,这里设置为false,表示不获取已删除任务 action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); - // 将这个获取任务操作添加到操作列表中 actionList.put(action); - // 将操作列表添加到要发送的JSON数据对象中,对应预定义的操作列表的JSON键 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // 设置客户端版本号,对应预定义的客户端版本号的JSON键 + + // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // 发送POST请求,将包含获取任务列表中任务操作信息的JSON数据发送到服务器进行处理 + JSONObject jsResponse = postRequest(jsPost); - // 从服务器响应的JSON数据中获取任务数组并返回,对应预定义的任务的JSON键 return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); - // 如果JSON处理出现异常,抛出操作失败异常,并说明是获取任务列表时处理JSON对象失败 throw new ActionFailureException("get task list: handing jsonobject failed"); } } - - // 获取当前用于同步的账户信息 + + // 获取当前同步的 Google 账户 public Account getSyncAccount() { return mAccount; } - - // 重置更新数组,即将其置为空,一般用于清除之前暂存的更新操作相关数据 -public void resetUpdateArray() { - mUpdateArray = null; + + // 重置更新列表 + public void resetUpdateArray() { + mUpdateArray = null; + } } \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java index 0df9b30..774f9a6 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -47,49 +47,47 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; -// GTaskManager类用于管理与Google Tasks的同步操作,包括初始化任务列表、同步内容、处理各种同步类型的操作等功能 +// 这是一个管理Google任务同步的类 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; - // 关联的Activity,用于获取授权令牌等操作 private Activity mActivity; - // 应用上下文 + private Context mContext; - // 用于操作内容提供器,进行数据查询、更新等操作 + private ContentResolver mContentResolver; - // 标记是否正在同步 + private boolean mSyncing; - // 标记同步是否被取消 + private boolean mCancelled; - // 存储Google Tasks任务列表的哈希表,以任务列表的全局唯一ID(GID)为键,任务列表对象为值 + private HashMap mGTaskListHashMap; - // 存储Google Tasks节点(任务、任务列表等)的哈希表,以节点的全局唯一ID(GID)为键,节点对象为值 + private HashMap mGTaskHashMap; - // 存储元数据的哈希表,以相关的全局唯一ID为键,元数据对象为值 + private HashMap mMetaHashMap; - // 元数据对应的任务列表 + private TaskList mMetaList; - // 存储本地已删除记录的ID集合,用于后续清理本地数据 + private HashSet mLocalDeleteIdMap; - // 存储从Google Tasks的全局唯一ID(GID)到本地记录ID(NID)的映射关系 + private HashMap mGidToNid; - // 存储从本地记录ID(NID)到Google Tasks的全局唯一ID(GID)的映射关系 + private HashMap mNidToGid; - // 私有构造函数,初始化相关成员变量 + // 私有构造函数,防止外部实例化 private GTaskManager() { mSyncing = false; mCancelled = false; @@ -110,13 +108,12 @@ public class GTaskManager { return mInstance; } - // 设置关联的Activity上下文,主要用于获取授权令牌等相关操作 + // 设置活动上下文,用于获取认证令牌 public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken mActivity = activity; } - // 执行同步操作,与Google Tasks进行数据同步 + // 同步Google任务与本地数据库 public int sync(Context context, GTaskASyncTask asyncTask) { if (mSyncing) { Log.d(TAG, "Sync is in progress"); @@ -126,7 +123,6 @@ public class GTaskManager { mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; - // 清除之前同步相关的数据缓存,如任务列表、节点、元数据等的哈希表以及ID映射关系等 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -138,18 +134,18 @@ public class GTaskManager { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // 登录Google Tasks,如果取消同步则不再进行登录操作,如果登录失败则抛出网络异常 + // 登录Google任务 if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // 发布同步进度信息,初始化任务列表阶段 + // 从Google获取任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - // 发布同步进度信息,进行内容同步阶段 + // 同步内容 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); } catch (NetworkFailureException e) { @@ -163,7 +159,6 @@ public class GTaskManager { e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { - // 无论同步是否成功,最终都清除同步相关的数据缓存 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -176,28 +171,26 @@ public class GTaskManager { return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } - // 初始化Google Tasks任务列表相关信息,包括获取任务列表、元数据列表以及加载任务等操作 + // 初始化Google任务列表 private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); try { - // 从Google Tasks获取任务列表信息,返回JSON数组形式的数据 JSONArray jsTaskLists = client.getTaskLists(); - // 先初始化元数据列表,查找名为特定前缀加上"meta"的任务列表作为元数据列表 + // 初始化元数据列表 mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - if (name - .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); - // 加载元数据,获取元数据列表对应的任务列表中的具体元数据信息 + // 加载元数据 JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { object = (JSONObject) jsMetas.getJSONObject(j); @@ -213,29 +206,26 @@ public class GTaskManager { } } - // 如果元数据列表不存在,则创建一个新的元数据列表,并发送到Google Tasks服务器创建对应的任务列表 + // 如果元数据列表不存在则创建 if (mMetaList == null) { mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } - // 初始化普通任务列表,遍历获取到的任务列表信息,创建任务列表对象并加载对应的任务信息 + // 初始化任务列表 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)) { + 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); - // 加载任务,获取当前任务列表对应的任务信息 + // 加载任务 JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -257,7 +247,7 @@ public class GTaskManager { } } - // 进行内容同步操作,处理本地与Google Tasks之间的数据同步,包括本地删除、文件夹同步、数据库中已有记录的同步等情况 + // 同步内容到Google任务或本地数据库 private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -270,12 +260,9 @@ public class GTaskManager { return; } - // 处理本地已删除的笔记(记录),查询本地回收站文件夹中的记录,对比Google Tasks中的对应节点进行相应的同步操作 + // 同步本地删除的笔记 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); + 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); @@ -297,15 +284,12 @@ public class GTaskManager { } } - // 同步文件夹相关信息 + // 同步文件夹 syncFolder(); - // 处理数据库中已存在的笔记(记录),根据其在Google Tasks中的对应情况确定同步类型,然后进行相应的同步操作 + // 同步本地存在的笔记 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"); + 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); @@ -317,10 +301,10 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增情况 + // 本地新增 syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程已删除情况 + // 远程删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } @@ -337,7 +321,7 @@ public class GTaskManager { } } - // 处理剩余的在Google Tasks中存在但本地未处理的节点,进行相应的同步操作(一般是添加到本地) + // 同步剩余的Google任务 Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -345,14 +329,14 @@ public class GTaskManager { doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - // mCancelled可能被其他线程设置,所以需要逐个检查,清除本地已删除记录的相关数据(如果同步未取消) + // 清除本地删除的条目 if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - // 刷新本地同步ID(如果同步未取消),提交更新并进行相关操作来更新本地记录的同步ID + // 刷新本地同步ID if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); @@ -360,7 +344,7 @@ public class GTaskManager { } - // 同步文件夹相关信息,包括根文件夹、通话记录文件夹、本地已存在的文件夹以及远程新增的文件夹等情况的处理 + // 同步文件夹到Google任务或本地数据库 private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -371,10 +355,9 @@ public class GTaskManager { return; } - // 处理根文件夹的同步情况,根据其在Google Tasks中的对应情况进行相应的同步操作(添加、更新等) + // 同步根文件夹 try { - c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); + 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); @@ -383,9 +366,8 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // 对于系统文件夹,仅在必要时更新远程名称 - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + // 更新远程名称(如果需要) + 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); @@ -400,101 +382,68 @@ public class GTaskManager { } } - // for call-note文件夹相关的同步操作处理 - // 查询通话记录文件夹在本地数据库中的信息,根据其在Google Tasks中的对应情况进行相应的同步操作(添加、更新等) - // 如果查询到该文件夹信息存在(即游标c不为空且能移动到下一条记录),则进一步处理 + // 同步通话记录文件夹 try { - // 通过ContentResolver查询本地数据库中通话记录文件夹的记录信息,指定查询条件为_id等于通话记录文件夹的特定ID - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(Notes.ID_CALL_RECORD_FOLDER) - }, null); + 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()) { - // 获取该文件夹在Google Tasks中对应的全局唯一ID(GID) gid = c.getString(SqlNote.GTASK_ID_COLUMN); - // 根据GID从存储Google Tasks节点的哈希表中获取对应的节点对象 node = mGTaskHashMap.get(gid); if (node != null) { - // 如果节点存在,从哈希表中移除该节点(表示已处理) mGTaskHashMap.remove(gid); - // 在本地ID到Google Tasks的GID映射表中记录该文件夹的映射关系,键为GID,值为通话记录文件夹的本地ID mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); - // 在Google Tasks的GID到本地ID映射表中记录映射关系,键为本地ID,值为GID mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // 对于系统文件夹,仅在其名称与预期的通话记录文件夹名称不一致时(即可能有更新情况),进行远程更新操作 - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE)) + // 更新远程名称(如果需要) + if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); } else { - // 如果节点不存在,则执行添加远程节点的操作(意味着本地有该文件夹记录,但在Google Tasks中还未创建对应节点) 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; } } - // 处理本地已存在的文件夹的同步情况 - // 查询本地除回收站外的文件夹记录信息,根据其在Google Tasks中的对应情况进行相应的同步操作 + // 同步本地存在的文件夹 try { - // 通过ContentResolver查询本地数据库中符合条件的文件夹记录信息,条件为类型是文件夹且父ID不等于回收站文件夹的ID - 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"); + 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()) { - // 获取文件夹在Google Tasks中对应的全局唯一ID(GID) gid = c.getString(SqlNote.GTASK_ID_COLUMN); - // 根据GID从存储Google Tasks节点的哈希表中获取对应的节点对象 node = mGTaskHashMap.get(gid); if (node != null) { - // 如果节点存在,从哈希表中移除该节点(表示已处理) mGTaskHashMap.remove(gid); - // 在本地ID到Google Tasks的GID映射表中记录该文件夹的映射关系,键为GID,值为文件夹的本地ID mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); - // 在Google Tasks的GID到本地ID映射表中记录映射关系,键为本地ID,值为GID mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - // 获取该节点对应的同步操作类型(根据节点和游标信息判断是添加、更新、删除等哪种类型) syncType = node.getSyncAction(c); } else { - // 如果节点不存在,根据其GID是否为空字符串判断是本地新增还是远程删除情况,进而确定同步类型 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增情况,同步类型设置为添加远程节点 + // 本地新增 syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程删除情况,同步类型设置为删除本地节点 + // 远程删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } - // 根据确定的同步类型执行相应的内容同步操作 doContentSync(syncType, node, c); } } else { - // 如果查询失败,打印警告日志 Log.w(TAG, "failed to query existing folder"); } } finally { - // 无论查询是否成功,最终都要关闭游标,释放资源,并将游标置空 if (c != null) { c.close(); c = null; } } - // 处理远程新增文件夹的同步情况 - // 遍历存储Google Tasks任务列表的哈希表,对于在Google - // Tasks中存在但本地未处理(还在mGTaskHashMap中)的文件夹,进行添加本地节点的同步操作 + // 同步远程新增的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -506,12 +455,11 @@ public class GTaskManager { } } - // 如果同步未被取消,提交Google Tasks客户端的更新操作(将之前暂存的更新操作发送到服务器执行) if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } - // 根据给定的同步类型执行具体的内容同步操作 + // 执行内容同步操作 private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -519,16 +467,12 @@ public class GTaskManager { MetaData meta; switch (syncType) { - // 同步类型为添加本地节点,调用相应方法进行本地节点添加操作 case Node.SYNC_ACTION_ADD_LOCAL: addLocalNode(node); break; - // 同步类型为添加远程节点,调用相应方法进行远程节点添加操作,并传入节点和游标信息 case Node.SYNC_ACTION_ADD_REMOTE: addRemoteNode(node, c); break; - // 同步类型为删除本地节点,先从元数据哈希表中获取对应的元数据,若存在则调用Google Tasks客户端删除该元数据节点, - // 同时将本地记录的ID添加到本地已删除记录集合中 case Node.SYNC_ACTION_DEL_LOCAL: meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); if (meta != null) { @@ -536,8 +480,6 @@ public class GTaskManager { } mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; - // 同步类型为删除远程节点,先从元数据哈希表中获取对应节点的元数据,若存在则调用Google Tasks客户端删除该元数据节点, - // 然后再删除该节点本身 case Node.SYNC_ACTION_DEL_REMOTE: meta = mMetaHashMap.get(node.getGid()); if (meta != null) { @@ -545,84 +487,65 @@ public class GTaskManager { } GTaskClient.getInstance().deleteNode(node); break; - // 同步类型为更新本地节点,调用相应方法进行本地节点更新操作,并传入节点和游标信息 case Node.SYNC_ACTION_UPDATE_LOCAL: updateLocalNode(node, c); break; - // 同步类型为更新远程节点,调用相应方法进行远程节点更新操作,并传入节点和游标信息 case Node.SYNC_ACTION_UPDATE_REMOTE: updateRemoteNode(node, c); break; - // 同步类型为更新冲突,目前简单采用本地更新的方式(可能后续可以考虑合并修改等更好的处理方式),调用远程节点更新方法 case Node.SYNC_ACTION_UPDATE_CONFLICT: - // merging both modifications maybe a good idea - // right now just use local update simply + // 合并修改可能是更好的做法 + // 目前我们简单地使用本地更新 updateRemoteNode(node, c); break; - // 同步类型为无操作,直接跳过不做任何处理 case Node.SYNC_ACTION_NONE: break; - // 同步类型为错误或其他未知类型,抛出操作失败异常,表示遇到了未知的同步操作类型 case Node.SYNC_ACTION_ERROR: default: throw new ActionFailureException("unkown sync action type"); } } - // 添加本地节点的操作方法 + // 在本地添加Google任务节点 private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; - // 如果节点是任务列表类型(TaskList) if (node instanceof TaskList) { - // 如果节点名称是默认根文件夹的名称,创建一个对应根文件夹的SqlNote对象,传入上下文和根文件夹的本地ID - if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + 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对象,传入上下文和通话记录文件夹的本地ID + } else if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); } else { - // 其他普通任务列表情况,创建一个新的SqlNote对象,传入上下文 sqlNote = new SqlNote(mContext); - // 设置SqlNote的内容为节点的本地JSON格式内容(从节点获取) sqlNote.setContent(node.getLocalJSONFromContent()); - // 设置父ID为根文件夹的本地ID sqlNote.setParentId(Notes.ID_ROOT_FOLDER); } } else { - // 如果节点是普通任务(Task)类型,创建一个新的SqlNote对象,传入上下文 sqlNote = new SqlNote(mContext); JSONObject js = node.getLocalJSONFromContent(); try { - // 如果节点的本地JSON数据中包含特定的笔记头部信息(META_HEAD_NOTE) 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); - // 检查该笔记ID在本地笔记数据库中是否已存在,如果存在则移除该ID(可能需要重新生成新的ID) if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // the id is not available, have to create a new one + // ID不可用,必须创建一个新的 note.remove(NoteColumns.ID); } } } - // 如果节点的本地JSON数据中包含特定的数据头部信息(META_HEAD_DATA) 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); - // 检查该数据ID在本地数据数据库中是否已存在,如果存在则移除该ID(可能需要重新生成新的ID) if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // the data id is not available, have to create - // a new one + // Data ID不可用,必须创建一个新的 data.remove(DataColumns.ID); } } @@ -633,61 +556,52 @@ public class GTaskManager { Log.w(TAG, e.toString()); e.printStackTrace(); } - // 设置SqlNote的内容为处理后的JSON数据 sqlNote.setContent(js); - // 获取任务节点的父节点在本地ID到Google Tasks的GID映射表中的对应父ID,如果不存在则抛出异常表示找不到本地父ID Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); throw new ActionFailureException("cannot add local node"); } - // 设置SqlNote的父ID为获取到的父节点的本地ID sqlNote.setParentId(parentId.longValue()); } - // 设置SqlNote的Google Tasks全局唯一ID(GID)为节点的GID + // 创建本地节点 sqlNote.setGtaskId(node.getGid()); - // 将SqlNote对象提交保存到本地数据库(传入参数false可能表示不触发某些额外的相关操作) sqlNote.commit(false); - // 在本地ID到Google Tasks的GID映射表中记录该节点的映射关系,键为节点的GID,值为SqlNote的本地ID + // 更新GID-NID映射 mGidToNid.put(node.getGid(), sqlNote.getId()); - // 在Google Tasks的GID到本地ID映射表中记录映射关系,键为SqlNote的本地ID,值为节点的GID mNidToGid.put(sqlNote.getId(), node.getGid()); - // 更新远程元数据,传入节点的GID和对应的SqlNote对象 + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); } - // 更新本地节点的操作方法 + // 更新本地Google任务节点 private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; - // 根据传入的上下文和游标信息创建一个新的SqlNote对象,用于更新本地节点信息 + // 更新本地笔记 sqlNote = new SqlNote(mContext, c); - // 设置SqlNote的内容为节点的本地JSON格式内容(从节点获取) sqlNote.setContent(node.getLocalJSONFromContent()); - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) - : new Long(Notes.ID_ROOT_FOLDER); + 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的父ID为获取到的父节点的本地ID sqlNote.setParentId(parentId.longValue()); - // 将更新后的SqlNote对象提交保存到本地数据库(传入参数true可能表示触发某些额外的相关操作) sqlNote.commit(true); - // 更新远程元数据,传入节点的GID和对应的SqlNote对象 + // 更新元数据信息 updateRemoteMeta(node.getGid(), sqlNote); } - // 添加远程节点的操作方法 + // 在Google任务中添加新的节点 private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -696,10 +610,9 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // 如果SqlNote对应的是笔记类型(即普通任务) + // 远程更新 if (sqlNote.isNoteType()) { Task task = new Task(); - // 设置任务的内容为从本地SqlNote获取的JSON格式内容 task.setContentByLocalJSON(sqlNote.getContent()); String parentGid = mNidToGid.get(sqlNote.getParentId()); @@ -707,20 +620,17 @@ public class GTaskManager { Log.e(TAG, "cannot find task's parent tasklist"); throw new ActionFailureException("cannot add remote task"); } - // 将任务添加到对应的父任务列表中(通过本地ID到Google Tasks的GID映射表找到父任务列表) mGTaskListHashMap.get(parentGid).addChildTask(task); - // 通过Google Tasks客户端创建该任务(发送到服务器创建对应的任务节点) GTaskClient.getInstance().createTask(task); n = (Node) task; - // 更新远程元数据,传入任务的GID和对应的SqlNote对象 + // 添加元数据 updateRemoteMeta(task.getGid(), sqlNote); } else { TaskList tasklist = null; - // 构建文件夹名称,先添加特定的前缀,然后根据SqlNote的本地ID判断是否是根文件夹或通话记录文件夹,添加相应的后缀名称, - // 否则添加SqlNote的摘要信息作为后缀 + // 如果文件夹已经存在则跳过 String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; @@ -729,179 +639,131 @@ public class GTaskManager { else folderName += sqlNote.getSnippet(); - // 遍历存储Google Tasks任务列表的哈希表(mGTaskListHashMap)的迭代器 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { - // 获取哈希表中的每一个键值对,键是任务列表的全局唯一ID(GID),值是对应的任务列表对象(TaskList) Map.Entry entry = iter.next(); String gid = entry.getKey(); TaskList list = entry.getValue(); - // 判断当前任务列表的名称是否与之前构建的文件夹名称(folderName)相等 if (list.getName().equals(folderName)) { - // 如果相等,说明找到了对应的任务列表,将其赋值给tasklist变量 tasklist = list; - // 如果在存储Google Tasks节点的哈希表(mGTaskHashMap)中包含该任务列表的GID,说明已经处理过,将其移除 if (mGTaskHashMap.containsKey(gid)) { mGTaskHashMap.remove(gid); } - // 找到匹配的任务列表后就可以结束循环了 break; } } - // 如果经过上述查找没有找到匹配的任务列表(tasklist仍为null),则创建一个新的任务列表对象 + // 如果没有匹配项则可以添加 if (tasklist == null) { tasklist = new TaskList(); - // 设置新任务列表的内容为从本地SqlNote获取的JSON格式内容 tasklist.setContentByLocalJSON(sqlNote.getContent()); - // 通过Google Tasks客户端创建该任务列表(发送到服务器创建对应的任务列表节点) GTaskClient.getInstance().createTaskList(tasklist); - // 将新创建的任务列表添加到存储Google Tasks任务列表的哈希表中,键为任务列表的GID,值为任务列表对象 mGTaskListHashMap.put(tasklist.getGid(), tasklist); } - // 将创建或找到的任务列表对象转换为Node类型并赋值给n(因为Node是更通用的节点类型,TaskList是一种特殊的Node) n = (Node) tasklist; } - // 更新本地笔记相关信息 - // 设置本地SqlNote的Google Tasks全局唯一ID(GID)为n的GID,即与远程任务列表或任务对应的GID保持一致 + // 更新本地笔记 sqlNote.setGtaskId(n.getGid()); - // 将SqlNote对象提交保存到本地数据库(传入参数false可能表示不触发某些额外的相关操作,此处用于保存基本的关联信息等) sqlNote.commit(false); - // 重置本地修改标志(可能表示将本地记录标记为已与远程同步,不再是本地修改状态) sqlNote.resetLocalModified(); - // 再次提交保存SqlNote对象到本地数据库(传入参数true可能表示触发一些更新相关的额外操作,确保修改标志等信息更新成功) sqlNote.commit(true); - // gid-id映射更新 - // 在本地ID到Google Tasks的GID映射表(mGidToNid)中记录该节点(n)的映射关系,键为节点的GID,值为SqlNote的本地ID + // GID-ID映射 mGidToNid.put(n.getGid(), sqlNote.getId()); - // 在Google Tasks的GID到本地ID映射表(mNidToGid)中记录映射关系,键为SqlNote的本地ID,值为节点的GID mNidToGid.put(sqlNote.getId(), n.getGid()); } - // 更新远程节点的操作方法 + // 在Google任务中更新节点 private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } - // 根据传入的上下文和游标信息创建一个新的SqlNote对象,用于后续操作 SqlNote sqlNote = new SqlNote(mContext, c); - // 远程更新操作 - // 设置远程节点(node)的内容为从本地SqlNote获取的JSON格式内容,即将本地修改同步到远程节点 + // 远程更新 node.setContentByLocalJSON(sqlNote.getContent()); - // 通过Google Tasks客户端将更新后的节点添加到更新队列中(后续会批量提交更新到服务器) GTaskClient.getInstance().addUpdateNode(node); - // 更新远程元数据,传入节点的GID和对应的SqlNote对象 + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); - // 如果SqlNote对应的是笔记类型(即普通任务),则可能需要处理任务移动相关操作 + // 如果需要移动任务 if (sqlNote.isNoteType()) { Task task = (Task) node; - // 获取任务当前的父任务列表 TaskList preParentList = task.getParent(); - // 通过本地ID到Google Tasks的GID映射表,根据本地记录的父ID获取当前任务在远程对应的父任务列表的GID String curParentGid = mNidToGid.get(sqlNote.getParentId()); if (curParentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); throw new ActionFailureException("cannot update remote task"); } - // 根据获取到的GID从存储Google Tasks任务列表的哈希表中获取当前任务在远程对应的父任务列表对象 TaskList curParentList = mGTaskListHashMap.get(curParentGid); - // 如果当前任务的前后父任务列表不一致,说明任务需要在远程进行移动操作 if (preParentList != curParentList) { - // 先从原父任务列表中移除该任务 preParentList.removeChildTask(task); - // 将任务添加到新的父任务列表中 curParentList.addChildTask(task); - // 通过Google Tasks客户端执行任务移动操作,告知服务器任务在远程的位置变更 GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } - // 清除本地修改标志,标记本地记录已同步到远程,不再处于本地修改状态 + // 清除本地修改标志 sqlNote.resetLocalModified(); - // 将更新后的SqlNote对象提交保存到本地数据库(传入参数true可能表示触发一些更新相关的额外操作,确保修改标志等信息更新成功) sqlNote.commit(true); } - // 更新远程元数据的操作方法 + // 更新Google任务中的元数据 private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { - // 从存储元数据的哈希表(mMetaHashMap)中根据GID获取对应的元数据对象(MetaData) MetaData metaData = mMetaHashMap.get(gid); if (metaData != null) { - // 如果元数据对象存在,设置其元数据内容,传入GID和从本地SqlNote获取的JSON格式内容 metaData.setMeta(gid, sqlNote.getContent()); - // 通过Google Tasks客户端将更新后的元数据添加到更新队列中(后续会批量提交更新到服务器) GTaskClient.getInstance().addUpdateNode(metaData); } else { - // 如果元数据对象不存在,则创建一个新的元数据对象 metaData = new MetaData(); - // 设置新元数据对象的元数据内容,传入GID和从本地SqlNote获取的JSON格式内容 metaData.setMeta(gid, sqlNote.getContent()); - // 将新创建的元数据对象添加到元数据列表(mMetaList)中作为子任务(从逻辑上关联起来) mMetaList.addChildTask(metaData); - // 将新创建的元数据对象添加到存储元数据的哈希表中,键为GID,值为元数据对象 mMetaHashMap.put(gid, metaData); - // 通过Google Tasks客户端创建该元数据任务(发送到服务器创建对应的元数据节点) GTaskClient.getInstance().createTask(metaData); } } } - // 刷新本地同步ID的操作方法 + // 刷新本地同步ID private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - // 为了获取最新的Google Tasks列表信息,先清空之前存储相关信息的哈希表 + // 获取最新的Google任务列表 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); - // 重新初始化Google Tasks列表信息(重新获取任务列表、元数据等相关内容) initGTaskList(); Cursor c = null; try { - // 通过ContentResolver查询本地数据库中除系统类型且不在回收站的笔记记录信息,按照类型降序排列 - 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"); + 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()) { - // 获取笔记在Google Tasks中对应的全局唯一ID(GID) String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - // 根据GID从存储Google Tasks节点的哈希表中获取对应的节点对象 Node node = mGTaskHashMap.get(gid); if (node != null) { - // 如果节点存在,从哈希表中移除该节点(表示已处理) mGTaskHashMap.remove(gid); - // 创建一个ContentValues对象用于存放要更新的数据,此处将同步ID设置为节点的最后修改时间 ContentValues values = new ContentValues(); values.put(NoteColumns.SYNC_ID, node.getLastModified()); - // 通过ContentResolver更新本地数据库中对应笔记的记录,设置同步ID字段的值 - mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - c.getLong(SqlNote.ID_COLUMN)), values, null, null); + 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"); + 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; @@ -909,13 +771,13 @@ public class GTaskManager { } } - // 获取同步账户名称的方法,通过Google Tasks客户端获取同步账户对象,并返回其名称 + // 获取同步账户 public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } - // 取消同步的方法,将表示同步是否取消的标志(mCancelled)设置为true,其他相关操作可以根据该标志来判断是否停止执行 + // 取消同步 public void cancelSync() { mCancelled = true; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java index 054b9da..e052e82 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -13,125 +13,107 @@ * 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; - -// GTaskSyncService类继承自Service,用于管理与Google Tasks的同步相关操作,包括启动同步、取消同步以及广播同步状态等功能 + +// GTaskSyncService 是一个用于同步任务的服务类 public class GTaskSyncService extends Service { - - // 用于在Intent中传递同步操作类型的字符串常量,作为键来标识具体的操作类型信息 + // 定义同步操作的 action 类型名称 public final static String ACTION_STRING_NAME = "sync_action_type"; - - // 表示启动同步操作的整数值常量,用于在根据操作类型进行判断时识别启动同步的意图 + + // 定义开始同步的 action 类型 public final static int ACTION_START_SYNC = 0; - - // 表示取消同步操作的整数值常量,用于在根据操作类型进行判断时识别取消同步的意图 + + // 定义取消同步的 action 类型 public final static int ACTION_CANCEL_SYNC = 1; - - // 表示无效操作类型的整数值常量,当获取到的操作类型不匹配已知的有效类型时使用该值来标识 + + // 定义无效的 action 类型 public final static int ACTION_INVALID = 2; - - // 用于广播同步服务相关状态信息的Intent的动作名称,其他组件可以通过注册监听该动作来接收同步服务的广播消息 + + // 定义同步服务广播名称 public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - - // 在广播Intent中用于传递是否正在同步状态的键名,对应的值为布尔类型,表示同步操作是否正在进行 + + // 定义同步服务广播中是否正在同步的键名 public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - - // 在广播Intent中用于传递同步进度消息的键名,对应的值为字符串类型,可用于展示同步操作的进度相关文本信息 + + // 定义同步服务广播中进度信息的键名 public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - - // 静态变量,用于存储正在执行的同步任务对象(GTaskASyncTask类型),方便在不同方法中对同步任务进行控制和判断状态 + + // 静态变量,用于存储同步任务实例 private static GTaskASyncTask mSyncTask = null; - - // 静态变量,用于存储同步进度相关的消息字符串,可通过广播传递给其他关注同步进度的组件 + + // 静态变量,用于存储同步进度信息 private static String mSyncProgress = ""; - - // 启动同步的私有方法,用于创建并执行同步任务 + + // 启动同步任务的方法 private void startSync() { - // 如果当前没有正在执行的同步任务(mSyncTask为null) if (mSyncTask == null) { - // 创建一个新的GTaskASyncTask对象,传入当前服务上下文(this)以及一个完成监听器(OnCompleteListener) mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { - // 当同步任务完成时会调用的回调方法 public void onComplete() { - // 将正在执行的同步任务对象置为null,表示同步已结束 mSyncTask = null; - // 发送一个空消息的广播(可能用于通知其他组件同步已完成等状态变化) sendBroadcast(""); - // 停止当前服务(因为同步任务已完成,服务的主要工作完成,可以停止自身运行) stopSelf(); } }); - // 发送一个空消息的广播(可能在创建任务后就先通知其他组件同步即将开始等情况) sendBroadcast(""); - // 执行同步任务,开始真正的同步操作流程 mSyncTask.execute(); } } - - // 取消同步的私有方法,用于取消正在执行的同步任务(如果存在的话) + + // 取消同步任务的方法 private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - - // 服务创建时调用的方法,在这里将正在执行的同步任务对象初始化为null,确保服务启动时处于初始状态 + + // 服务创建时的回调方法 @Override public void onCreate() { mSyncTask = null; } - - // 当服务通过startService()方法启动时会调用此方法,用于处理传入的Intent并根据不同的操作意图执行相应操作 + + // 服务启动命令的回调方法 @Override public int onStartCommand(Intent intent, int flags, int startId) { - // 从传入的Intent中获取附加的Bundle数据(通常用于传递额外的参数信息) Bundle bundle = intent.getExtras(); - // 如果获取到的Bundle不为null且包含表示操作类型的键(ACTION_STRING_NAME) if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { - // 根据操作类型的值进行不同的操作分支判断 switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { - // 如果操作类型是启动同步(ACTION_START_SYNC) case ACTION_START_SYNC: startSync(); break; - // 如果操作类型是取消同步(ACTION_CANCEL_SYNC) case ACTION_CANCEL_SYNC: cancelSync(); break; - // 其他未知或不处理的操作类型,直接跳过不做操作 default: break; } - // 返回START_STICKY表示服务在被系统意外终止后会尝试重新创建并保持启动状态(常用于需要持续运行的服务场景) return START_STICKY; } - // 如果不符合上述条件或者操作类型无效等情况,调用父类的onStartCommand方法进行默认处理 return super.onStartCommand(intent, flags, startId); } - - // 当系统内存不足时会调用此方法,在这里用于取消正在执行的同步任务(如果存在的话),释放内存资源 + + // 服务内存不足时的回调方法 @Override public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - - // 用于服务绑定操作的方法,这里返回null表示不支持绑定操作(如果需要支持绑定,需要返回一个有效的IBinder对象) - @Override + + // 返回服务的 IBinder 对象 public IBinder onBind(Intent intent) { return null; } - - // 发送广播的公共方法,用于向其他组件广播同步服务的相关状态信息,包括同步进度消息、是否正在同步等 + + // 发送同步进度广播的方法 public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -139,29 +121,28 @@ public class GTaskSyncService extends Service { intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); } - - // 静态公共方法,用于在外部(比如Activity中)启动同步服务进行同步操作,需要传入对应的Activity作为上下文来启动服务 + + // 静态方法,用于启动同步服务 public static void startSync(Activity activity) { - // 设置GTaskManager的Activity上下文(可能用于获取相关授权等操作,与同步操作相关联) GTaskManager.getInstance().setActivityContext(activity); Intent intent = new Intent(activity, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } - - // 静态公共方法,用于在外部(比如其他组件中)取消正在进行的同步操作,需要传入上下文(Context)来启动服务发送取消同步的意图 + + // 静态方法,用于取消同步服务 public static void cancelSync(Context context) { Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } - - // 静态公共方法,用于判断当前是否正在进行同步操作,通过检查正在执行的同步任务对象是否为null来返回相应的布尔值结果 + + // 检查是否正在同步的方法 public static boolean isSyncing() { return mSyncTask != null; } - - // 静态公共方法,用于获取当前的同步进度相关的字符串消息,返回存储同步进度消息的静态变量的值 + + // 获取同步进度信息的方法 public static String getProgressString() { return mSyncProgress; } diff --git a/src/Notes-master/src/net/micode/notes/model/Note.java b/src/Notes-master/src/net/micode/notes/model/Note.java index 6706cf6..6ca854c 100644 --- a/src/Notes-master/src/net/micode/notes/model/Note.java +++ b/src/Notes-master/src/net/micode/notes/model/Note.java @@ -33,7 +33,7 @@ import net.micode.notes.data.Notes.TextNote; import java.util.ArrayList; - +// 代表一个笔记,包含笔记的基本信息和笔记数据 public class Note { private ContentValues mNoteDiffValues; private NoteData mNoteData; @@ -70,36 +70,44 @@ public class Note { 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); } + // 设置文本笔记的数据ID public void setTextDataId(long id) { mNoteData.setTextDataId(id); } + // 获取文本笔记的数据ID public long getTextDataId() { return mNoteData.mTextDataId; } + // 设置通话笔记的数据ID 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); @@ -130,6 +138,7 @@ public class Note { return true; } + // 笔记数据类,包含文本数据和通话数据 private class NoteData { private long mTextDataId; @@ -148,10 +157,12 @@ public class Note { mCallDataId = 0; } + // 判断笔记数据是否被本地修改过 boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } + // 设置文本数据ID void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); @@ -159,6 +170,7 @@ public class Note { mTextDataId = id; } + // 设置通话数据ID void setCallDataId(long id) { if (id <= 0) { throw new IllegalArgumentException("Call data id should larger than 0"); @@ -166,18 +178,21 @@ public class Note { 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 @@ -250,4 +265,4 @@ public class Note { return null; } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java index be081e4..f77e6e6 100644 --- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java +++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java @@ -13,16 +13,16 @@ * 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; @@ -30,8 +30,8 @@ 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; @@ -41,27 +41,28 @@ public class WorkingNote { 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, @@ -71,7 +72,8 @@ public class WorkingNote { DataColumns.DATA3, DataColumns.DATA4, }; - + + // 数据库查询笔记信息的列 public static final String[] NOTE_PROJECTION = new String[] { NoteColumns.PARENT_ID, NoteColumns.ALERTED_DATE, @@ -80,28 +82,28 @@ public class WorkingNote { 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; @@ -113,8 +115,8 @@ public class WorkingNote { mMode = 0; mWidgetType = Notes.TYPE_WIDGET_INVALIDE; } - - // Existing note construct + + // 已有笔记构造方法 private WorkingNote(Context context, long noteId, long folderId) { mContext = context; mNoteId = noteId; @@ -123,12 +125,13 @@ public class WorkingNote { 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); @@ -145,13 +148,14 @@ public class WorkingNote { } 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 { @@ -173,7 +177,8 @@ public class WorkingNote { 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); @@ -182,11 +187,13 @@ public class WorkingNote { 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()) { @@ -195,9 +202,9 @@ public class WorkingNote { return false; } } - + mNote.syncNote(mContext, mNoteId); - + /** * Update widget content if there exist any widget of this note */ @@ -211,11 +218,13 @@ public class WorkingNote { return false; } } - + + // 判断笔记是否存在于数据库中 public boolean existInDatabase() { return mNoteId > 0; } - + + // 判断笔记是否值得保存 private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -224,11 +233,13 @@ public class WorkingNote { return true; } } - + + // 设置笔记信息改变监听器 public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } - + + // 设置闹钟提醒日期 public void setAlertDate(long date, boolean set) { if (date != mAlertDate) { mAlertDate = date; @@ -238,7 +249,8 @@ public class WorkingNote { mNoteSettingStatusListener.onClockAlertChanged(date, set); } } - + + // 标记笔记是否已删除 public void markDeleted(boolean mark) { mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -246,7 +258,8 @@ public class WorkingNote { mNoteSettingStatusListener.onWidgetChanged(); } } - + + // 设置笔记背景颜色ID public void setBgColorId(int id) { if (id != mBgColorId) { mBgColorId = id; @@ -256,7 +269,8 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } - + + // 设置笔记的检查列表模式 public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { @@ -266,98 +280,115 @@ public class WorkingNote { 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)); } } - + + // 设置笔记的窗口小部件ID 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; } - + + // 获取笔记背景颜色资源ID public int getBgColorResId() { return NoteBgResources.getNoteBgResource(mBgColorId); } - + + // 获取笔记背景颜色ID public int getBgColorId() { return mBgColorId; } - + + // 获取笔记标题背景颜色资源ID public int getTitleBgResId() { return NoteBgResources.getNoteTitleBgResource(mBgColorId); } - + + // 获取检查列表模式 public int getCheckListMode() { return mMode; } - + + // 获取笔记ID public long getNoteId() { return mNoteId; } - + + // 获取笔记所在的文件夹ID public long getFolderId() { return mFolderId; } - + + // 获取窗口小部件ID 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 @@ -365,4 +396,4 @@ public class WorkingNote { */ void onCheckListModeChanged(int oldMode, int newMode); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index ffae194..c6a1095 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -1,112 +1,110 @@ -以下是带有中文注释的代码: - -```java /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * - * 根据Apache License, Version 2.0(以下简称“许可证”)授权; - * 除非遵守许可证,否则不得使用此文件。 - * 你可以在以下网址获得许可证的副本: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * 除非适用法律要求或书面同意,否则按“原样”提供软件 - * 在许可证下分发,不附带任何明示或暗示的保证或条件。 - * 请参阅许可证,了解有关权限和限制的具体语言。 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - + package net.micode.notes.tool; - + import android.content.Context; import android.database.Cursor; import android.os.Environment; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; - + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; - -// 备份工具类,用于将笔记数据备份到文本文件中。 + +// 备份工具类,用于将笔记数据导出为文本文件 public class BackupUtils { private static final String TAG = "BackupUtils"; - // 单例模式 + // 单例实例 private static BackupUtils sInstance; - - // 获取BackupUtils的单例,如果不存在则创建。 + + // 获取BackupUtils的单例实例 public static synchronized BackupUtils getInstance(Context context) { if (sInstance == null) { sInstance = new BackupUtils(context); } return sInstance; } - + /** - * 以下状态码用于表示备份或恢复的状态。 + * 以下状态码表示备份或恢复的状态 */ // SD卡未挂载 - public static final int STATE_SD_CARD_UNMOUONTED = 0; + public static final int STATE_SD_CARD_UNMOUONTED = 0; // 备份文件不存在 - public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; - // 数据格式不正确,可能被其他程序更改 - public static final int STATE_DATA_DESTROIED = 2; - // 运行时异常导致备份或恢复失败 - public static final int STATE_SYSTEM_ERROR = 3; + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // 数据损坏或格式不正确 + public static final int STATE_DATA_DESTROIED = 2; + // 系统错误导致备份或恢复失败 + public static final int STATE_SYSTEM_ERROR = 3; // 备份或恢复成功 - public static final int STATE_SUCCESS = 4; - + public static final int STATE_SUCCESS = 4; + private TextExport mTextExport; - - // 私有构造函数,用于创建BackupUtils实例。 + + // 构造函数,初始化TextExport实例 private BackupUtils(Context context) { mTextExport = new TextExport(context); } - - // 检查外部存储是否可用。 + + // 检查外部存储是否可用 private static boolean externalStorageAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - - // 导出笔记数据到文本文件。 + + // 导出笔记数据为文本文件 public int exportToText() { return mTextExport.exportToText(); } - - // 获取导出的文本文件名。 + + // 获取导出的文本文件名 public String getExportedTextFileName() { return mTextExport.mFileName; } - - // 获取导出的文本文件目录。 + + // 获取导出的文本文件目录 public String getExportedTextFileDir() { return mTextExport.mFileDirectory; } - - // 内部类,用于将笔记数据导出到文本。 + + // 文本导出类,用于将笔记数据导出为文本格式 private static class TextExport { - // 笔记数据投影数组。 private static final String[] NOTE_PROJECTION = { NoteColumns.ID, NoteColumns.MODIFIED_DATE, NoteColumns.SNIPPET, NoteColumns.TYPE }; - - // 数据列索引。 + private static final int NOTE_COLUMN_ID = 0; + private static final int NOTE_COLUMN_MODIFIED_DATE = 1; + private static final int NOTE_COLUMN_SNIPPET = 2; - - // 数据数据投影数组。 + private static final String[] DATA_PROJECTION = { DataColumns.CONTENT, DataColumns.MIME_TYPE, @@ -115,56 +113,53 @@ public class BackupUtils { DataColumns.DATA3, DataColumns.DATA4, }; - - // 数据列索引。 + private static final int DATA_COLUMN_CONTENT = 0; + private static final int DATA_COLUMN_MIME_TYPE = 1; + private static final int DATA_COLUMN_CALL_DATE = 2; + private static final int DATA_COLUMN_PHONE_NUMBER = 4; - - // 导出文本的格式数组。 - private final String[] TEXT_FORMAT; - private static final int FORMAT_FOLDER_NAME = 0; - private static final int FORMAT_NOTE_DATE = 1; - private static final int FORMAT_NOTE_CONTENT = 2; - - // 上下文对象,用于访问资源和内容解析器。 + + private final String [] TEXT_FORMAT; + private static final int FORMAT_FOLDER_NAME = 0; + private static final int FORMAT_NOTE_DATE = 1; + private static final int FORMAT_NOTE_CONTENT = 2; + private Context mContext; - // 导出文件的名称和目录。 private String mFileName; private String mFileDirectory; - - // 构造函数,初始化上下文和格式数组。 + + // 构造函数,初始化导出格式和上下文 public TextExport(Context context) { TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); mContext = context; mFileName = ""; mFileDirectory = ""; } - - // 根据ID获取对应的格式字符串。 + + // 根据ID获取导出格式字符串 private String getFormat(int id) { return TEXT_FORMAT[id]; } - - /** - * 将指定文件夹ID的笔记导出到文本。 - */ + + // 导出指定文件夹下的笔记到文本文件 private void exportFolderToText(String folderId, PrintStream ps) { - // 查询属于该文件夹的笔记。 + // 查询属于该文件夹的笔记 Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { - folderId + folderId }, null); - + if (notesCursor != null) { if (notesCursor.moveToFirst()) { do { - // 打印笔记最后修改日期。 + // 打印笔记的最后修改日期 ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // 查询属于该笔记的数据。 + // 查询属于该笔记的数据 String noteId = notesCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (notesCursor.moveToNext()); @@ -172,16 +167,14 @@ public class BackupUtils { notesCursor.close(); } } - - /** - * 将指定ID的笔记导出到打印流。 - */ + + // 导出指定笔记到文本输出流 private void exportNoteToText(String noteId, PrintStream ps) { Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { - noteId + noteId }, null); - + if (dataCursor != null) { if (dataCursor.moveToFirst()) { do { @@ -191,7 +184,7 @@ public class BackupUtils { String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); String location = dataCursor.getString(DATA_COLUMN_CONTENT); - + if (!TextUtils.isEmpty(phoneNumber)) { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); @@ -216,7 +209,7 @@ public class BackupUtils { } dataCursor.close(); } - // 打印笔记之间的分隔线。 + // 打印笔记之间的分隔符 try { ps.write(new byte[] { Character.LINE_SEPARATOR, Character.LETTER_NUMBER @@ -225,36 +218,33 @@ public class BackupUtils { Log.e(TAG, e.toString()); } } - - /** - * 将笔记导出为用户可读的文本。 - */ + + // 将笔记数据导出为用户可读的文本文件 public int exportToText() { if (!externalStorageAvailable()) { - Log.d(TAG, "媒体未挂载"); + Log.d(TAG, "Media was not mounted"); return STATE_SD_CARD_UNMOUONTED; } - + PrintStream ps = getExportToTextPrintStream(); if (ps == null) { - Log.e(TAG, "获取打印流出错"); + Log.e(TAG, "get print stream error"); return STATE_SYSTEM_ERROR; } - // 首先导出文件夹及其笔记。 + // 首先导出文件夹及其笔记 Cursor folderCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, - null, null); - + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); + if (folderCursor != null) { if (folderCursor.moveToFirst()) { do { // 打印文件夹名称 String folderName = ""; - if (folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { folderName = mContext.getString(R.string.call_record_folder_name); } else { folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); @@ -268,22 +258,21 @@ public class BackupUtils { } folderCursor.close(); } - - // 导出根文件夹中的笔记。 - // 导出根文件夹中的笔记 + + // 导出根文件夹下的笔记 Cursor noteCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, - NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0", null, null); - + NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + + "=0", null, null); + if (noteCursor != null) { if (noteCursor.moveToFirst()) { do { - // 打印笔记最后修改日期 ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // 查询属于这条笔记的数据 + // 查询属于该笔记的数据 String noteId = noteCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (noteCursor.moveToNext()); @@ -291,20 +280,16 @@ public class BackupUtils { noteCursor.close(); } ps.close(); - - // 返回成功状态码 + return STATE_SUCCESS; } - - /** - * 获取指向导出文本文件的打印流 - */ + + // 获取指向导出文本文件的PrintStream private PrintStream getExportToTextPrintStream() { - // 生成存储导出数据的文本文件 File file = generateFileMountedOnSDcard(mContext, R.string.file_path, R.string.file_name_txt_format); if (file == null) { - Log.e(TAG, "创建导出文件失败"); + Log.e(TAG, "create file to exported failed"); return null; } mFileName = file.getName(); @@ -323,34 +308,33 @@ public class BackupUtils { return ps; } } - -/** - * 在SD卡上生成用于存储导入数据的文本文件 - */ -private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { - StringBuilder sb = new StringBuilder(); - sb.append(Environment.getExternalStorageDirectory()); - sb.append(context.getString(filePathResId)); - File filedir = new File(sb.toString()); - sb.append(context.getString( - fileNameFormatResId, - DateFormat.format(context.getString(R.string.format_date_ymd), - System.currentTimeMillis()))); - File file = new File(sb.toString()); - - try { - if (!filedir.exists()) { - filedir.mkdir(); - } - if (!file.exists()) { - file.createNewFile(); + + // 生成用于存储导入数据的文本文件 + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); + sb.append(context.getString(filePathResId)); + File filedir = new File(sb.toString()); + sb.append(context.getString( + fileNameFormatResId, + DateFormat.format(context.getString(R.string.format_date_ymd), + System.currentTimeMillis()))); + File file = new File(sb.toString()); + + try { + if (!filedir.exists()) { + filedir.mkdir(); + } + if (!file.exists()) { + file.createNewFile(); + } + return file; + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - return file; - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + + return null; } - - return null; -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index d0c852a..7de4569 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -1,16 +1,17 @@ - /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * - * 根据Apache License, Version 2.0(以下简称“许可证”)授权; - * 除非遵守许可证,否则不得使用此文件。 - * 你可以在以下网址获得许可证的副本: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * 除非适用法律要求或书面同意,否则按“原样”提供软件 - * 在许可证下分发,不附带任何明示或暗示的保证或条件。 - * 请参阅许可证,了解有关权限和限制的具体语言。 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package net.micode.notes.tool; @@ -33,25 +34,25 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; -// 数据工具类,提供对笔记数据的操作函数 +// 数据工具类,提供批量删除笔记、移动笔记到文件夹等操作 public class DataUtils { public static final String TAG = "DataUtils"; // 批量删除笔记 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { if (ids == null) { - Log.d(TAG, "id集合为空"); + Log.d(TAG, "the ids is null"); return true; } if (ids.size() == 0) { - Log.d(TAG, "id集合中没有元素"); + Log.d(TAG, "no id is in the hashset"); return true; } ArrayList operationList = new ArrayList(); for (long id : ids) { - if (id == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "不要删除系统根文件夹"); + if(id == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Don't delete system folder root"); continue; } ContentProviderOperation.Builder builder = ContentProviderOperation @@ -61,7 +62,7 @@ public class DataUtils { try { ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "删除笔记失败, ids:" + ids.toString()); + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } return true; @@ -86,7 +87,7 @@ public class DataUtils { public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { if (ids == null) { - Log.d(TAG, "id集合为空"); + Log.d(TAG, "the ids is null"); return true; } @@ -102,7 +103,7 @@ public class DataUtils { try { ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "移动笔记失败, ids:" + ids.toString()); + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } return true; @@ -115,22 +116,22 @@ public class DataUtils { } /** - * 获取除了系统文件夹外的所有文件夹数量 + * 获取用户文件夹(非系统文件夹)的数量 */ public static int getUserFolderCount(ContentResolver resolver) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) }, + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, null); int count = 0; - if (cursor != null) { - if (cursor.moveToFirst()) { + if(cursor != null) { + if(cursor.moveToFirst()) { try { count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "获取文件夹数量失败:" + e.toString()); + Log.e(TAG, "get folder count failed:" + e.toString()); } finally { cursor.close(); } @@ -139,12 +140,12 @@ public class DataUtils { return count; } - // 检查笔记在数据库中是否可见 + // 检查指定类型的笔记是否可见 public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String[] { String.valueOf(type) }, + new String [] {String.valueOf(type)}, null); boolean exist = false; @@ -157,7 +158,7 @@ public class DataUtils { return exist; } - // 检查笔记在数据库中是否存在 + // 检查笔记是否存在于数据库中 public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); @@ -172,7 +173,7 @@ public class DataUtils { return exist; } - // 检查数据在数据库中是否存在 + // 检查数据是否存在于数据库中 public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); @@ -187,16 +188,16 @@ public class DataUtils { return exist; } - // 检查文件夹名称是否可见 + // 检查指定名称的用户文件夹是否存在 public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", new String[] { name }, null); boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { + if(cursor != null) { + if(cursor.getCount() > 0) { exist = true; } cursor.close(); @@ -204,7 +205,7 @@ public class DataUtils { return exist; } - // 获取文件夹笔记的小部件属性 + // 获取指定文件夹下的笔记小部件信息 public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, @@ -234,80 +235,74 @@ public class DataUtils { // 根据笔记ID获取通话号码 public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.PHONE_NUMBER }, - CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", - new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, - null); - - - - + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.PHONE_NUMBER }, + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + null); - // 如果游标不为空并且移动到第一行,则尝试获取通话号码 if (cursor != null && cursor.moveToFirst()) { try { - return cursor.getString(0); // 返回获取到的字符串 + return cursor.getString(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "获取通话号码失败:" + e.toString()); // 记录异常信息 + Log.e(TAG, "Get call number fails " + e.toString()); } finally { - cursor.close(); // 确保游标被关闭 + cursor.close(); } } - return ""; // 如果失败,返回空字符串 + return ""; + } - // 根据电话号码和通话日期获取笔记ID + // 根据通话号码和通话日期获取笔记ID public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String[] { CallNote.NOTE_ID }, + new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", - new String[] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, + + CallNote.PHONE_NUMBER + ",?)", + new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, null); - // 如果游标不为空并且移动到第一行,则尝试获取笔记ID if (cursor != null) { if (cursor.moveToFirst()) { try { - return cursor.getLong(0); // 返回获取到的长整型数 + return cursor.getLong(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "获取通话笔记ID失败:" + e.toString()); // 记录异常信息 + Log.e(TAG, "Get call note id fails " + e.toString()); } } - cursor.close(); // 确保游标被关闭 + cursor.close(); } - return 0; // 如果失败,返回0 + return 0; } - // 根据笔记ID获取摘要 + // 根据笔记ID获取笔记摘要 public static String getSnippetById(ContentResolver resolver, long noteId) { Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { NoteColumns.SNIPPET }, + new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", - new String[] { String.valueOf(noteId) }, + new String [] { String.valueOf(noteId)}, null); - // 如果游标不为空并且移动到第一行,则尝试获取摘要 if (cursor != null) { String snippet = ""; if (cursor.moveToFirst()) { - snippet = cursor.getString(0); // 获取摘要 + snippet = cursor.getString(0); } - cursor.close(); // 确保游标被关闭 - return snippet; // 返回摘要 + cursor.close(); + return snippet; } - throw new IllegalArgumentException("未找到ID为:" + noteId + "的笔记"); // 如果失败,抛出异常 + throw new IllegalArgumentException("Note is not found with id: " + noteId); } - // 获取格式化后的摘要 + // 格式化笔记摘要 public static String getFormattedSnippet(String snippet) { if (snippet != null) { - snippet = snippet.trim(); // 去除首尾空白 - int index = snippet.indexOf('\n'); // 查找换行符 + snippet = snippet.trim(); + int index = snippet.indexOf('\n'); if (index != -1) { - snippet = snippet.substring(0, index); // 截取换行符之前的字符串 + snippet = snippet.substring(0, index); } } - return snippet; // 返回格式化后的摘要 + return snippet; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java index 1b42c6f..8903dd7 100644 --- a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java @@ -13,148 +13,102 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.tool; - -// GTaskStringUtils类,主要用于定义一些与Google Tasks相关操作中使用的字符串常量,方便在代码中统一引用和管理 + +// 此类用于定义和存储GTask相关的JSON字段名常量 public class GTaskStringUtils { - - // 表示操作的唯一ID的JSON键名,用于在与Google Tasks交互的JSON数据中标识某个具体操作的ID + public final static String GTASK_JSON_ACTION_ID = "action_id"; - - // 表示操作列表的JSON键名,用于在与Google Tasks交互的JSON数据中存放一组操作的集合(通常是一个JSON数组) + public final static String GTASK_JSON_ACTION_LIST = "action_list"; - - // 表示操作类型的JSON键名,用于在与Google Tasks交互的JSON数据中指定某个操作具体是什么类型的操作,比如创建、获取、移动等 + public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - - // 表示创建操作类型的具体值,对应创建相关的操作,例如创建任务、创建任务列表等操作时使用该值来标识操作类型 + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - - // 表示获取所有(任务等)操作类型的具体值,用于获取某个任务列表下的所有任务等情况时标识操作类型 + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - - // 表示移动操作类型的具体值,用于在移动任务、任务列表等位置时标识操作类型 + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - - // 表示更新操作类型的具体值,用于对任务、任务列表等进行更新操作时标识操作类型 + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - - // 表示创建者ID的JSON键名,用于在与Google Tasks交互的JSON数据中存放创建某个任务、任务列表等的用户ID信息 + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - - // 表示子实体的JSON键名,可能用于在涉及包含子元素的结构(如任务列表包含多个任务等情况)时,标识子实体相关信息 + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - - // 表示客户端版本号的JSON键名,用于在与Google Tasks交互的JSON数据中告知服务器当前客户端的版本信息,便于进行兼容性等相关处理 + public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - - // 表示是否完成的JSON键名,可能用于任务等实体中,标识该任务是否已经完成的状态信息 + public final static String GTASK_JSON_COMPLETED = "completed"; - - // 表示当前列表ID的JSON键名,可能用于在涉及多列表切换、关联等场景时,标识当前所在的任务列表的ID + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - - // 表示默认列表ID的JSON键名,用于标识某个用户、某个应用场景下的默认任务列表的ID信息 + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - - // 表示是否已删除的JSON键名,用于在任务、任务列表等实体中标识其是否已经被删除的状态信息 + public final static String GTASK_JSON_DELETED = "deleted"; - - // 表示目标列表的JSON键名,在进行移动操作等涉及改变所属列表的操作时,用于指定要移动到的目标任务列表的ID + public final static String GTASK_JSON_DEST_LIST = "dest_list"; - - // 表示目标父级的JSON键名,在进行移动操作、关联操作等涉及改变父级元素的情况时,用于指定要移动到或关联的目标父级元素的相关信息(比如任务列表等) + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - - // 表示目标父级类型的JSON键名,可能用于进一步明确目标父级元素的具体类型(例如是任务列表类型还是其他类型等) + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - - // 表示实体差异的JSON键名,可能用于在对比、更新实体时,标识实体之间发生变化的部分相关信息 + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - - // 表示实体类型的JSON键名,用于在与Google Tasks交互的JSON数据中明确某个实体(如任务、任务列表等)具体是什么类型 + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - - // 表示是否获取已删除的JSON键名,在进行查询操作时,用于指定是否要获取已经被标记为删除的任务、任务列表等信息 + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - - // 表示唯一ID的JSON键名,通常用于标识任务、任务列表等实体在Google Tasks系统中的全局唯一标识符 + public final static String GTASK_JSON_ID = "id"; - - // 表示索引的JSON键名,可能用于在任务列表中标识某个任务的排列顺序等索引相关信息 + public final static String GTASK_JSON_INDEX = "index"; - - // 表示最后修改时间的JSON键名,用于记录任务、任务列表等实体最后一次被修改的时间信息,便于进行同步、更新等操作时的判断 + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - - // 表示最新同步点的JSON键名,可能用于记录与Google Tasks进行数据同步时的最新同步时间点等相关信息,便于后续判断同步范围、增量等情况 + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - - // 表示列表ID的JSON键名,常用于标识某个具体的任务列表的ID信息,与其他相关操作配合使用,比如获取某个列表下的任务等操作 + public final static String GTASK_JSON_LIST_ID = "list_id"; - - // 表示任务列表(复数形式)的JSON键名,用于在与Google Tasks交互的JSON数据中存放多个任务列表信息的集合(通常是一个JSON数组) + public final static String GTASK_JSON_LISTS = "lists"; - - // 表示名称的JSON键名,用于任务、任务列表等实体中,存放它们的名称信息,方便展示、识别等操作 + public final static String GTASK_JSON_NAME = "name"; - - // 表示新ID的JSON键名,可能在某些创建、更新操作后,用于存放新生成的实体ID信息(比如创建任务后返回的新任务ID等情况) + public final static String GTASK_JSON_NEW_ID = "new_id"; - - // 表示备注(笔记)的JSON键名,可能用于存放与任务相关的一些备注、说明等文本信息 + public final static String GTASK_JSON_NOTES = "notes"; - - // 表示父级ID的JSON键名,用于在任务、任务列表等实体中标识其所属的父级元素的ID信息,建立层级关系 + public final static String GTASK_JSON_PARENT_ID = "parent_id"; - - // 表示前一个兄弟节点(任务等)ID的JSON键名,在任务列表中用于标识某个任务之前相邻的兄弟任务的ID,常用于排序、移动等操作场景 + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - - // 表示操作结果的JSON键名,在执行某些操作(如批量操作等)后,用于存放操作的结果信息(通常是一个JSON数组) + public final static String GTASK_JSON_RESULTS = "results"; - - // 表示源列表的JSON键名,在进行移动操作等涉及改变所属列表的操作时,用于指定操作前所在的原始任务列表的ID + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - - // 表示任务(复数形式)的JSON键名,用于在与Google Tasks交互的JSON数据中存放多个任务信息的集合(通常是一个JSON数组) + public final static String GTASK_JSON_TASKS = "tasks"; - - // 表示类型的JSON键名,用于在与Google - // Tasks交互的JSON数据中明确某个实体(如任务、任务列表等)具体是什么类型,和GTASK_JSON_ENTITY_TYPE作用类似但使用场景可能稍有不同 + public final static String GTASK_JSON_TYPE = "type"; - - // 表示分组类型的具体值,用于标识某个实体是分组类型(例如任务分组等情况) + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - - // 表示任务类型的具体值,用于明确某个实体是任务类型,方便在代码中进行类型判断等操作 + public final static String GTASK_JSON_TYPE_TASK = "TASK"; - - // 表示用户的JSON键名,可能用于存放与操作相关的用户信息,比如执行操作的用户账号等情况 + public final static String GTASK_JSON_USER = "user"; - - // MIUI系统中笔记相关的文件夹前缀字符串,用于在名称等地方标识该文件夹是属于MIUI笔记应用相关的文件夹 + public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - - // 表示默认文件夹名称的字符串常量,用于标识默认的任务列表、文件夹等的名称 + public final static String FOLDER_DEFAULT = "Default"; - - // 表示通话记录笔记文件夹名称的字符串常量,用于明确该文件夹是存放通话记录相关笔记的 + public final static String FOLDER_CALL_NOTE = "Call_Note"; - - // 表示元数据文件夹名称的字符串常量,用于标识存放元数据相关内容的文件夹名称 + public final static String FOLDER_META = "METADATA"; - - // 表示元数据中Google Tasks ID的键名,可能用于在元数据结构中关联对应的Google Tasks实体的ID信息 + public final static String META_HEAD_GTASK_ID = "meta_gid"; - - // 表示元数据中笔记相关内容的键名,用于在元数据结构中存放与笔记相关的具体信息 + public final static String META_HEAD_NOTE = "meta_note"; - - // 表示元数据中数据相关内容的键名,用于在元数据结构中存放一些额外的数据信息(可能是和任务、笔记等相关的数据集合等情况) + public final static String META_HEAD_DATA = "meta_data"; - - // 表示元数据笔记名称的字符串常量,同时提示不要更新和删除该元数据笔记(可能是具有特殊用途的固定元数据相关说明) + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; + } \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java b/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java index d98a327..b813795 100644 --- a/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java +++ b/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java @@ -13,71 +13,66 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// 该类所属的包名,表明这个类位于net.micode.notes.tool包下,用于提供一些资源相关的解析和获取功能 + package net.micode.notes.tool; - + import android.content.Context; import android.preference.PreferenceManager; - + import net.micode.notes.R; import net.micode.notes.ui.NotesPreferenceActivity; - -// ResourceParser类,主要用于对应用中的各种资源(如背景图片、文本样式等)进行管理和提供获取方法,方便在不同的界面等场景中使用 + public class ResourceParser { - - // 定义颜色相关的常量,用整数表示不同的颜色选项,方便在代码中进行统一的颜色选择和判断,这里分别对应黄色、蓝色、白色、绿色、红色 - public static final int YELLOW = 0; - public static final int BLUE = 1; - public static final int WHITE = 2; - public static final int GREEN = 3; - public static final int RED = 4; - - // 定义默认的背景颜色常量,其值为黄色(对应上面定义的颜色常量中的YELLOW),表示在没有特殊设置时默认使用的背景颜色 + + // 定义笔记背景颜色的常量 + public static final int YELLOW = 0; + public static final int BLUE = 1; + public static final int WHITE = 2; + public static final int GREEN = 3; + public static final int RED = 4; + + // 默认笔记背景颜色 public static final int BG_DEFAULT_COLOR = YELLOW; - - // 定义文本大小相关的常量,用整数表示不同的文本大小选项,方便在代码中统一处理文本大小相关的设置和获取,这里分别对应小、中、大、超大文本尺寸 - public static final int TEXT_SMALL = 0; - public static final int TEXT_MEDIUM = 1; - public static final int TEXT_LARGE = 2; - public static final int TEXT_SUPER = 3; - - // 定义默认的字体大小常量,其值为中等大小(对应上面定义的文本大小常量中的TEXT_MEDIUM),表示在没有特殊设置时默认使用的字体大小 + + // 定义笔记文本大小的常量 + public static final int TEXT_SMALL = 0; + public static final int TEXT_MEDIUM = 1; + public static final int TEXT_LARGE = 2; + public static final int TEXT_SUPER = 3; + + // 默认笔记背景文本大小 public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; - - // 内部静态类NoteBgResources,用于管理笔记编辑界面相关的背景资源(图片资源) + + // 用于获取编辑笔记时的背景资源 public static class NoteBgResources { - // 定义一个私有的静态整型数组,存储笔记编辑界面背景图片资源的ID,对应不同颜色的编辑界面背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_EDIT_RESOURCES = new int[] { - R.drawable.edit_yellow, - R.drawable.edit_blue, - R.drawable.edit_white, - R.drawable.edit_green, - R.drawable.edit_red + private final static int [] BG_EDIT_RESOURCES = new int [] { + R.drawable.edit_yellow, + R.drawable.edit_blue, + R.drawable.edit_white, + R.drawable.edit_green, + R.drawable.edit_red }; - - // 定义一个私有的静态整型数组,存储笔记编辑界面标题背景图片资源的ID,对应不同颜色的编辑界面标题背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_EDIT_TITLE_RESOURCES = new int[] { - R.drawable.edit_title_yellow, - R.drawable.edit_title_blue, - R.drawable.edit_title_white, - R.drawable.edit_title_green, - R.drawable.edit_title_red + + private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { + R.drawable.edit_title_yellow, + R.drawable.edit_title_blue, + R.drawable.edit_title_white, + R.drawable.edit_title_green, + R.drawable.edit_title_red }; - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取笔记编辑界面的背景图片资源ID,方便在设置编辑界面背景时使用 + + // 根据ID获取编辑笔记的背景资源 public static int getNoteBgResource(int id) { return BG_EDIT_RESOURCES[id]; } - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取笔记编辑界面标题的背景图片资源ID,方便在设置编辑界面标题背景时使用 + + // 根据ID获取编辑笔记标题的背景资源 public static int getNoteTitleBgResource(int id) { return BG_EDIT_TITLE_RESOURCES[id]; } } - - // 根据传入的上下文(Context)获取默认的背景图片ID,逻辑是先检查是否在偏好设置中设置了自定义背景颜色(通过特定的偏好设置键来判断), - // 如果设置了,则随机选择一个背景资源ID(从NoteBgResources中定义的资源数组长度范围内随机),否则返回默认的背景颜色ID(BG_DEFAULT_COLOR) + + // 获取默认笔记背景颜色ID public static int getDefaultBgId(Context context) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { @@ -86,114 +81,106 @@ public class ResourceParser { return BG_DEFAULT_COLOR; } } - - // 内部静态类NoteItemBgResources,用于管理笔记列表项相关的背景资源(图片资源) + + // 用于获取笔记列表项的背景资源 public static class NoteItemBgResources { - // 定义一个私有的静态整型数组,存储笔记列表项第一个元素(可能是列表头部等情况)的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_FIRST_RESOURCES = new int[] { - R.drawable.list_yellow_up, - R.drawable.list_blue_up, - R.drawable.list_white_up, - R.drawable.list_green_up, - R.drawable.list_red_up + private final static int [] BG_FIRST_RESOURCES = new int [] { + R.drawable.list_yellow_up, + R.drawable.list_blue_up, + R.drawable.list_white_up, + R.drawable.list_green_up, + R.drawable.list_red_up }; - - // 定义一个私有的静态整型数组,存储笔记列表项中间元素(正常的列表项情况)的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_NORMAL_RESOURCES = new int[] { - R.drawable.list_yellow_middle, - R.drawable.list_blue_middle, - R.drawable.list_white_middle, - R.drawable.list_green_middle, - R.drawable.list_red_middle + + private final static int [] BG_NORMAL_RESOURCES = new int [] { + R.drawable.list_yellow_middle, + R.drawable.list_blue_middle, + R.drawable.list_white_middle, + R.drawable.list_green_middle, + R.drawable.list_red_middle, }; - - // 定义一个私有的静态整型数组,存储笔记列表项最后一个元素(可能是列表尾部等情况)的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_LAST_RESOURCES = new int[] { - R.drawable.list_yellow_down, - R.drawable.list_blue_down, - R.drawable.list_white_down, - R.drawable.list_green_down, - R.drawable.list_red_down, + + private final static int [] BG_LAST_RESOURCES = new int [] { + R.drawable.list_yellow_down, + R.drawable.list_blue_down, + R.drawable.list_white_down, + R.drawable.list_green_down, + R.drawable.list_red_down, }; - - // 定义一个私有的静态整型数组,存储单个笔记(可能是独立展示等情况)的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_SINGLE_RESOURCES = new int[] { - R.drawable.list_yellow_single, - R.drawable.list_blue_single, - R.drawable.list_white_single, - R.drawable.list_green_single, - R.drawable.list_red_single + + private final static int [] BG_SINGLE_RESOURCES = new int [] { + R.drawable.list_yellow_single, + R.drawable.list_blue_single, + R.drawable.list_white_single, + R.drawable.list_green_single, + R.drawable.list_red_single }; - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取笔记列表项第一个元素的背景图片资源ID,方便在设置列表项背景时使用 + + // 根据ID获取笔记列表第一项的背景资源 public static int getNoteBgFirstRes(int id) { return BG_FIRST_RESOURCES[id]; } - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取笔记列表项最后一个元素的背景图片资源ID,方便在设置列表项背景时使用 + + // 根据ID获取笔记列表最后一项的背景资源 public static int getNoteBgLastRes(int id) { return BG_LAST_RESOURCES[id]; } - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取单个笔记的背景图片资源ID,方便在设置单个笔记背景时使用 + + // 根据ID获取单个笔记项的背景资源 public static int getNoteBgSingleRes(int id) { return BG_SINGLE_RESOURCES[id]; } - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取笔记列表项中间元素(正常列表项)的背景图片资源ID,方便在设置列表项背景时使用 + + // 根据ID获取笔记列表中间项的背景资源 public static int getNoteBgNormalRes(int id) { return BG_NORMAL_RESOURCES[id]; } - - // 获取文件夹的背景图片资源ID,用于设置文件夹在列表等展示场景中的背景图片 + + // 获取文件夹背景资源 public static int getFolderBgRes() { return R.drawable.list_folder; } } - - // 内部静态类WidgetBgResources,用于管理桌面小部件相关的背景资源(图片资源) + + // 用于获取小部件的背景资源 public static class WidgetBgResources { - // 定义一个私有的静态整型数组,存储2x尺寸桌面小部件的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_2X_RESOURCES = new int[] { - R.drawable.widget_2x_yellow, - R.drawable.widget_2x_blue, - R.drawable.widget_2x_white, - R.drawable.widget_2x_green, - R.drawable.widget_2x_red, + private final static int [] BG_2X_RESOURCES = new int [] { + R.drawable.widget_2x_yellow, + R.drawable.widget_2x_blue, + R.drawable.widget_2x_white, + R.drawable.widget_2x_green, + R.drawable.widget_2x_red, }; - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取2x尺寸桌面小部件的背景图片资源ID,方便在设置小部件背景时使用 + + // 根据ID获取2x小部件的背景资源 public static int getWidget2xBgResource(int id) { return BG_2X_RESOURCES[id]; } - - // 定义一个私有的静态整型数组,存储4x尺寸桌面小部件的背景图片资源ID,对应不同颜色的背景图片,顺序与前面定义的颜色常量顺序一致 - private final static int[] BG_4X_RESOURCES = new int[] { - R.drawable.widget_4x_yellow, - R.drawable.widget_4x_blue, - R.drawable.widget_4x_white, - R.drawable.widget_4x_green, - R.drawable.widget_4x_red + + private final static int [] BG_4X_RESOURCES = new int [] { + R.drawable.widget_4x_yellow, + R.drawable.widget_4x_blue, + R.drawable.widget_4x_white, + R.drawable.widget_4x_green, + R.drawable.widget_4x_red }; - - // 根据传入的颜色ID(对应前面定义的颜色常量)获取4x尺寸桌面小部件的背景图片资源ID,方便在设置小部件背景时使用 + + // 根据ID获取4x小部件的背景资源 public static int getWidget4xBgResource(int id) { return BG_4X_RESOURCES[id]; } } - - // 内部静态类TextAppearanceResources,用于管理文本外观相关的资源(主要是文本样式资源) + + // 用于获取文本外观资源 public static class TextAppearanceResources { - // 定义一个私有的静态整型数组,存储不同文本外观样式的资源ID,对应不同大小的文本外观样式,顺序与前面定义的文本大小常量顺序有一定关联 - private final static int[] TEXTAPPEARANCE_RESOURCES = new int[] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper }; - - // 根据传入的文本外观资源ID获取对应的文本外观资源ID,如果传入的ID大于资源数组的长度(可能是由于存储或获取出现异常情况), - // 则返回默认的字体大小对应的资源ID(BG_DEFAULT_FONT_SIZE),以避免出现资源获取错误 + + // 根据ID获取文本外观资源 public static int getTexAppearanceResource(int id) { /** * HACKME: Fix bug of store the resource id in shared preference. @@ -205,8 +192,8 @@ public class ResourceParser { } return TEXTAPPEARANCE_RESOURCES[id]; } - - // 获取文本外观资源数组的长度,可用于判断资源数量或者进行一些边界相关的操作判断等 + + // 获取文本外观资源的数量 public static int getResourcesSize() { return TEXTAPPEARANCE_RESOURCES.length; } diff --git a/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java b/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java index 85723be..5f40071 100644 --- a/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java @@ -39,21 +39,29 @@ import net.micode.notes.tool.DataUtils; import java.io.IOException; - +/** + * 闹钟提醒活动类,继承自Activity,实现了OnClickListener和OnDismissListener接口。 + * 该活动用于在闹钟响起时显示提醒对话框,并播放默认的闹钟声音。 + */ public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { - private long mNoteId; - private String mSnippet; - private static final int SNIPPET_PREW_MAX_LEN = 60; - MediaPlayer mPlayer; - + private long mNoteId; // 保存笔记的ID + private String mSnippet; // 保存笔记的摘要 + private static final int SNIPPET_PREW_MAX_LEN = 60; // 笔记摘要的最大长度 + MediaPlayer mPlayer; // 用于播放闹钟声音的MediaPlayer实例 + + /** + * 创建活动时调用的方法。 + * @param savedInstanceState 保存的实例状态数据包 + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); + requestWindowFeature(Window.FEATURE_NO_TITLE); // 请求无标题窗口 - final Window win = getWindow(); - win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + final Window win = getWindow(); // 获取当前窗口 + win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // 设置窗口在锁屏时显示 + // 如果屏幕未点亮,则添加更多标志以保持屏幕点亮并允许在锁屏时操作 if (!isScreenOn()) { win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON @@ -61,98 +69,123 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); } - Intent intent = getIntent(); + Intent intent = getIntent(); // 获取启动该活动的Intent try { - mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); - mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); + mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); // 从Intent中提取笔记ID + mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); // 根据笔记ID获取摘要 + // 如果摘要长度超过最大限制,则截取前SNIPPET_PREW_MAX_LEN个字符并添加省略号 mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) : mSnippet; } catch (IllegalArgumentException e) { - e.printStackTrace(); - return; + e.printStackTrace(); // 打印异常信息 + return; // 如果发生异常,则结束该方法 } - mPlayer = new MediaPlayer(); + mPlayer = new MediaPlayer(); // 创建MediaPlayer实例 + // 检查数据库中是否存在指定ID的笔记 if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { - showActionDialog(); - playAlarmSound(); + showActionDialog(); // 显示操作对话框 + playAlarmSound(); // 播放闹钟声音 } else { - finish(); + finish(); // 如果笔记不存在,则结束该活动 } } + /** + * 检查屏幕是否点亮。 + * @return 如果屏幕点亮则返回true,否则返回false + */ private boolean isScreenOn() { - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - return pm.isScreenOn(); + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); // 获取电源管理服务 + return pm.isScreenOn(); // 返回屏幕是否点亮的状态 } + /** + * 播放闹钟声音的方法。 + */ private void playAlarmSound() { + // 获取实际的默认闹钟铃声URI Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); + // 获取当前设置的静音模式影响的音频流类型 int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); + // 如果静音模式影响了闹钟音频流,则设置MediaPlayer的音频流类型为silentModeStreams + // 否则,设置为AudioManager.STREAM_ALARM if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { mPlayer.setAudioStreamType(silentModeStreams); } else { mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } try { - mPlayer.setDataSource(this, url); - mPlayer.prepare(); - mPlayer.setLooping(true); - mPlayer.start(); + mPlayer.setDataSource(this, url); // 设置MediaPlayer的数据源为闹钟铃声URI + mPlayer.prepare(); // 准备MediaPlayer + mPlayer.setLooping(true); // 设置MediaPlayer循环播放 + mPlayer.start(); // 开始播放闹钟声音 } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + e.printStackTrace(); // 打印异常信息 } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + e.printStackTrace(); // 打印异常信息 } catch (IllegalStateException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + e.printStackTrace(); // 打印异常信息 } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + e.printStackTrace(); // 打印异常信息 } } + /** + * 显示操作对话框的方法。 + */ private void showActionDialog() { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(R.string.app_name); - dialog.setMessage(mSnippet); - dialog.setPositiveButton(R.string.notealert_ok, this); + AlertDialog.Builder dialog = new AlertDialog.Builder(this); // 创建AlertDialog.Builder实例 + dialog.setTitle(R.string.app_name); // 设置对话框标题为应用名称 + dialog.setMessage(mSnippet); // 设置对话框消息为笔记摘要 + dialog.setPositiveButton(R.string.notealert_ok, this); // 设置确定按钮及其监听器 + // 如果屏幕已点亮,则添加进入按钮及其监听器 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); } - dialog.show().setOnDismissListener(this); + dialog.show().setOnDismissListener(this); // 显示对话框并设置对话框消失监听器 } + /** + * 实现OnClickListener接口的方法,处理对话框按钮点击事件。 + * @param dialog 触发点击事件的对话框 + * @param which 点击的按钮的ID + */ public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_NEGATIVE: - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, mNoteId); - startActivity(intent); + Intent intent = new Intent(this, NoteEditActivity.class); // 创建Intent准备跳转到笔记编辑活动 + intent.setAction(Intent.ACTION_VIEW); // 设置Intent动作类型为ACTION_VIEW + intent.putExtra(Intent.EXTRA_UID, mNoteId); // 添加笔记ID作为附加信息 + startActivity(intent); // 启动笔记编辑活动 break; default: break; } } + /** + * 实现OnDismissListener接口的方法,处理对话框消失事件。 + * @param dialog 消失的对话框 + */ public void onDismiss(DialogInterface dialog) { - stopAlarmSound(); - finish(); + stopAlarmSound(); // 停止播放闹钟声音 + finish(); // 结束该活动 } + /** + * 停止并释放MediaPlayer的方法。 + */ private void stopAlarmSound() { if (mPlayer != null) { - mPlayer.stop(); - mPlayer.release(); - mPlayer = null; + mPlayer.stop(); // 停止MediaPlayer播放 + mPlayer.release(); // 释放MediaPlayer资源 + mPlayer = null; // 将mPlayer置为null } } } diff --git a/src/Notes-master/src/net/micode/notes/ui/AlarmInitReceiver.java b/src/Notes-master/src/net/micode/notes/ui/AlarmInitReceiver.java index f221202..f8bb98a 100644 --- a/src/Notes-master/src/net/micode/notes/ui/AlarmInitReceiver.java +++ b/src/Notes-master/src/net/micode/notes/ui/AlarmInitReceiver.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -23,21 +23,24 @@ import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; - + import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; - - + +// 该广播接收器用于初始化闹钟,查询即将提醒的笔记并设置闹钟 public class AlarmInitReceiver extends BroadcastReceiver { - + + // 查询笔记数据库时使用的列投影 private static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.ALERTED_DATE }; - + + // 列索引常量,用于从查询结果中提取数据 private static final int COLUMN_ID = 0; private static final int COLUMN_ALERTED_DATE = 1; - + + // 接收广播时执行的方法,查询数据库并设置闹钟 @Override public void onReceive(Context context, Intent intent) { long currentDate = System.currentTimeMillis(); @@ -46,7 +49,7 @@ public class AlarmInitReceiver extends BroadcastReceiver { NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, new String[] { String.valueOf(currentDate) }, null); - + if (c != null) { if (c.moveToFirst()) { do { @@ -62,4 +65,4 @@ public class AlarmInitReceiver extends BroadcastReceiver { c.close(); } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/AlarmReceiver.java b/src/Notes-master/src/net/micode/notes/ui/AlarmReceiver.java index 54e503b..3d2b949 100644 --- a/src/Notes-master/src/net/micode/notes/ui/AlarmReceiver.java +++ b/src/Notes-master/src/net/micode/notes/ui/AlarmReceiver.java @@ -13,18 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; - + +// 一个继承自BroadcastReceiver的类,用于接收闹钟触发的广播 public class AlarmReceiver extends BroadcastReceiver { + // 当接收到广播时,该方法会被调用 @Override public void onReceive(Context context, Intent intent) { + // 设置Intent的目标Activity为AlarmAlertActivity intent.setClass(context, AlarmAlertActivity.class); + // 添加标志以允许启动新的Activity intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动AlarmAlertActivity context.startActivity(intent); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/DateTimePicker.java b/src/Notes-master/src/net/micode/notes/ui/DateTimePicker.java index 496b0cd..9601ff7 100644 --- a/src/Notes-master/src/net/micode/notes/ui/DateTimePicker.java +++ b/src/Notes-master/src/net/micode/notes/ui/DateTimePicker.java @@ -1,485 +1,745 @@ -/* - * 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.ui; - -import java.text.DateFormatSymbols; -import java.util.Calendar; - -import net.micode.notes.R; - - -import android.content.Context; -import android.text.format.DateFormat; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.NumberPicker; - -public class DateTimePicker extends FrameLayout { - - private static final boolean DEFAULT_ENABLE_STATE = true; - - private static final int HOURS_IN_HALF_DAY = 12; - private static final int HOURS_IN_ALL_DAY = 24; - private static final int DAYS_IN_ALL_WEEK = 7; - private static final int DATE_SPINNER_MIN_VAL = 0; - private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; - private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; - private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; - private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; - private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; - private static final int MINUT_SPINNER_MIN_VAL = 0; - private static final int MINUT_SPINNER_MAX_VAL = 59; - private static final int AMPM_SPINNER_MIN_VAL = 0; - private static final int AMPM_SPINNER_MAX_VAL = 1; - - private final NumberPicker mDateSpinner; - private final NumberPicker mHourSpinner; - private final NumberPicker mMinuteSpinner; - private final NumberPicker mAmPmSpinner; - private Calendar mDate; - - private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; - - private boolean mIsAm; - - private boolean mIs24HourView; - - private boolean mIsEnabled = DEFAULT_ENABLE_STATE; - - private boolean mInitialising; - - private OnDateTimeChangedListener mOnDateTimeChangedListener; - - private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); - updateDateControl(); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - boolean isDateChanged = false; - Calendar cal = Calendar.getInstance(); - if (!mIs24HourView) { - if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || - oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - mIsAm = !mIsAm; - updateAmPmControl(); - } - } else { - if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - } - int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); - mDate.set(Calendar.HOUR_OF_DAY, newHour); - onDateTimeChanged(); - if (isDateChanged) { - setCurrentYear(cal.get(Calendar.YEAR)); - setCurrentMonth(cal.get(Calendar.MONTH)); - setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); - } - } - }; - - private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int minValue = mMinuteSpinner.getMinValue(); - int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; - if (oldVal == maxValue && newVal == minValue) { - offset += 1; - } else if (oldVal == minValue && newVal == maxValue) { - offset -= 1; - } - if (offset != 0) { - mDate.add(Calendar.HOUR_OF_DAY, offset); - mHourSpinner.setValue(getCurrentHour()); - updateDateControl(); - int newHour = getCurrentHourOfDay(); - if (newHour >= HOURS_IN_HALF_DAY) { - mIsAm = false; - updateAmPmControl(); - } else { - mIsAm = true; - updateAmPmControl(); - } - } - mDate.set(Calendar.MINUTE, newVal); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mIsAm = !mIsAm; - if (mIsAm) { - mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); - } else { - mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); - } - updateAmPmControl(); - onDateTimeChanged(); - } - }; - - public interface OnDateTimeChangedListener { - void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute); - } - - public DateTimePicker(Context context) { - this(context, System.currentTimeMillis()); - } - - public DateTimePicker(Context context, long date) { - this(context, date, DateFormat.is24HourFormat(context)); - } - - public DateTimePicker(Context context, long date, boolean is24HourView) { - super(context); - mDate = Calendar.getInstance(); - mInitialising = true; - mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; - inflate(context, R.layout.datetime_picker, this); - - mDateSpinner = (NumberPicker) findViewById(R.id.date); - mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); - mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); - mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); - - mHourSpinner = (NumberPicker) findViewById(R.id.hour); - mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); - mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); - mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); - mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); - mMinuteSpinner.setOnLongPressUpdateInterval(100); - mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); - - String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); - mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); - mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); - mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); - mAmPmSpinner.setDisplayedValues(stringsForAmPm); - mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); - - // update controls to initial state - updateDateControl(); - updateHourControl(); - updateAmPmControl(); - - set24HourView(is24HourView); - - // set to current time - setCurrentDate(date); - - setEnabled(isEnabled()); - - // set the content descriptions - mInitialising = false; - } - - @Override - public void setEnabled(boolean enabled) { - if (mIsEnabled == enabled) { - return; - } - super.setEnabled(enabled); - mDateSpinner.setEnabled(enabled); - mMinuteSpinner.setEnabled(enabled); - mHourSpinner.setEnabled(enabled); - mAmPmSpinner.setEnabled(enabled); - mIsEnabled = enabled; - } - - @Override - public boolean isEnabled() { - return mIsEnabled; - } - - /** - * Get the current date in millis - * - * @return the current date in millis - */ - public long getCurrentDateInTimeMillis() { - return mDate.getTimeInMillis(); - } - - /** - * Set the current date - * - * @param date The current date in millis - */ - public void setCurrentDate(long date) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(date); - setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); - } - - /** - * Set the current date - * - * @param year The current year - * @param month The current month - * @param dayOfMonth The current dayOfMonth - * @param hourOfDay The current hourOfDay - * @param minute The current minute - */ - public void setCurrentDate(int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - setCurrentYear(year); - setCurrentMonth(month); - setCurrentDay(dayOfMonth); - setCurrentHour(hourOfDay); - setCurrentMinute(minute); - } - - /** - * Get current year - * - * @return The current year - */ - public int getCurrentYear() { - return mDate.get(Calendar.YEAR); - } - - /** - * Set current year - * - * @param year The current year - */ - public void setCurrentYear(int year) { - if (!mInitialising && year == getCurrentYear()) { - return; - } - mDate.set(Calendar.YEAR, year); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current month in the year - * - * @return The current month in the year - */ - public int getCurrentMonth() { - return mDate.get(Calendar.MONTH); - } - - /** - * Set current month in the year - * - * @param month The month in the year - */ - public void setCurrentMonth(int month) { - if (!mInitialising && month == getCurrentMonth()) { - return; - } - mDate.set(Calendar.MONTH, month); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current day of the month - * - * @return The day of the month - */ - public int getCurrentDay() { - return mDate.get(Calendar.DAY_OF_MONTH); - } - - /** - * Set current day of the month - * - * @param dayOfMonth The day of the month - */ - public void setCurrentDay(int dayOfMonth) { - if (!mInitialising && dayOfMonth == getCurrentDay()) { - return; - } - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current hour in 24 hour mode, in the range (0~23) - * @return The current hour in 24 hour mode - */ - public int getCurrentHourOfDay() { - return mDate.get(Calendar.HOUR_OF_DAY); - } - - private int getCurrentHour() { - if (mIs24HourView){ - return getCurrentHourOfDay(); - } else { - int hour = getCurrentHourOfDay(); - if (hour > HOURS_IN_HALF_DAY) { - return hour - HOURS_IN_HALF_DAY; - } else { - return hour == 0 ? HOURS_IN_HALF_DAY : hour; - } - } - } - - /** - * Set current hour in 24 hour mode, in the range (0~23) - * - * @param hourOfDay - */ - public void setCurrentHour(int hourOfDay) { - if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { - return; - } - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - if (!mIs24HourView) { - if (hourOfDay >= HOURS_IN_HALF_DAY) { - mIsAm = false; - if (hourOfDay > HOURS_IN_HALF_DAY) { - hourOfDay -= HOURS_IN_HALF_DAY; - } - } else { - mIsAm = true; - if (hourOfDay == 0) { - hourOfDay = HOURS_IN_HALF_DAY; - } - } - updateAmPmControl(); - } - mHourSpinner.setValue(hourOfDay); - onDateTimeChanged(); - } - - /** - * Get currentMinute - * - * @return The Current Minute - */ - public int getCurrentMinute() { - return mDate.get(Calendar.MINUTE); - } - - /** - * Set current minute - */ - public void setCurrentMinute(int minute) { - if (!mInitialising && minute == getCurrentMinute()) { - return; - } - mMinuteSpinner.setValue(minute); - mDate.set(Calendar.MINUTE, minute); - onDateTimeChanged(); - } - - /** - * @return true if this is in 24 hour view else false. - */ - public boolean is24HourView () { - return mIs24HourView; - } - - /** - * Set whether in 24 hour or AM/PM mode. - * - * @param is24HourView True for 24 hour mode. False for AM/PM mode. - */ - public void set24HourView(boolean is24HourView) { - if (mIs24HourView == is24HourView) { - return; - } - mIs24HourView = is24HourView; - mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); - int hour = getCurrentHourOfDay(); - updateHourControl(); - setCurrentHour(hour); - updateAmPmControl(); - } - - private void updateDateControl() { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); - mDateSpinner.setDisplayedValues(null); - for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { - cal.add(Calendar.DAY_OF_YEAR, 1); - mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); - } - mDateSpinner.setDisplayedValues(mDateDisplayValues); - mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); - mDateSpinner.invalidate(); - } - - private void updateAmPmControl() { - if (mIs24HourView) { - mAmPmSpinner.setVisibility(View.GONE); - } else { - int index = mIsAm ? Calendar.AM : Calendar.PM; - mAmPmSpinner.setValue(index); - mAmPmSpinner.setVisibility(View.VISIBLE); - } - } - - private void updateHourControl() { - if (mIs24HourView) { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); - } else { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); - } - } - - /** - * Set the callback that indicates the 'Set' button has been pressed. - * @param callback the callback, if null will do nothing - */ - public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { - mOnDateTimeChangedListener = callback; - } - - private void onDateTimeChanged() { - if (mOnDateTimeChangedListener != null) { - mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), - getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); - } - } -} +/* + * 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.ui; + +import java.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; + +// DateTimePicker类继承自FrameLayout,用于显示日期和时间选择控件 +public class DateTimePicker extends FrameLayout { + + // 默认控件是否可用 + private static final boolean DEFAULT_ENABLE_STATE = true; + + // 半天中的小时数、全天中的小时数、一周中的天数等常量 + private static final int HOURS_IN_HALF_DAY = 12; + private static final int HOURS_IN_ALL_DAY = 24; + private static final int DAYS_IN_ALL_WEEK = 7; + private static final int DATE_SPINNER_MIN_VAL = 0; + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; + private static final int MINUT_SPINNER_MIN_VAL = 0; + private static final int MINUT_SPINNER_MAX_VAL = 59; + private static final int AMPM_SPINNER_MIN_VAL = 0; + private static final int AMPM_SPINNER_MAX_VAL = 1; + + // 日期、小时、分钟、AM/PM选择器控件 + private final NumberPicker mDateSpinner; + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mAmPmSpinner; + // 当前日期时间 + private Calendar mDate; + + // 日期显示值数组 + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + + // 当前是否为AM + private boolean mIsAm; + + // 是否为24小时制视图 + private boolean mIs24HourView; + + // 控件是否可用 + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + + // 初始化标志 + private boolean mInitialising; + + // 日期时间改变监听器 + private OnDateTimeChangedListener mOnDateTimeChangedListener; + + // 日期选择器值改变监听器 + private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); + updateDateControl(); + onDateTimeChanged(); + } + }; + + // 小时选择器值改变监听器 + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + if (!mIs24HourView) { + if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || + oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + mIsAm = !mIsAm; + updateAmPmControl(); + } + } else { + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + mDate.set(Calendar.HOUR_OF_DAY, newHour); + onDateTimeChanged(); + if (isDateChanged) { + setCurrentYear(cal.get(Calendar.YEAR)); + setCurrentMonth(cal.get(Calendar.MONTH)); + setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); + } + } + }; + + // 分钟选择器值改变监听器 + private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + offset -= 1; + } + if (offset != 0) { + mDate.add(Calendar.HOUR_OF_DAY, offset); + mHourSpinner.setValue(getCurrentHour()); + updateDateControl(); + int newHour = getCurrentHourOfDay(); + if (newHour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + updateAmPmControl(); + } else { + mIsAm = true; + updateAmPmControl(); + } + } + mDate.set(Calendar.MINUTE, newVal); + onDateTimeChanged(); + } + }; + + // AM/PM选择器值改变监听器 + private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mIsAm = !mIsAm; + if (mIsAm) { + mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); + } else { + mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); + } + updateAmPmControl(); + onDateTimeChanged(); + } + }; + + // 日期时间改变监听器接口 + public interface OnDateTimeChangedListener { + void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute); + } + + // 构造函数,默认使用当前时间 + public DateTimePicker(Context context) { + this(context, System.currentTimeMillis()); + } + + // 构造函数,使用指定时间 + public DateTimePicker(Context context, long date) { + this(context, date, DateFormat.is24HourFormat(context)); + } + + // 构造函数,使用指定时间和是否为24小时制 + public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + mDate = Calendar.getInstance(); + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + + mDateSpinner = (NumberPicker) findViewById(R.id.date); + mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); + mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); + mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + + mHourSpinner = (NumberPicker) findViewById(R.id.hour); + mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); + mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); + mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); + mMinuteSpinner.setOnLongPressUpdateInterval(100); + mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + + String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); + mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); + mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); + mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); + mAmPmSpinner.setDisplayedValues(stringsForAmPm); + mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); + + // 更新控件到初始状态 + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + set24HourView(is24HourView); + + // 设置到当前时间 + setCurrentDate(date); + + setEnabled(isEnabled()); + + // 设置内容描述 + mInitialising = false; + } + + // 设置控件是否可用 + @Override + public void setEnabled(boolean enabled) { + if (mIsEnabled == enabled) { + return; + } + super.setEnabled(enabled); + mDateSpinner.setEnabled(enabled); + mMinuteSpinner.setEnabled(enabled); + mHourSpinner.setEnabled(enabled); + mAmPmSpinner.setEnabled(enabled); + mIsEnabled = enabled; + } + + // 获取控件是否可用 + @Override + public boolean isEnabled() { + return mIsEnabled; + } + + + +``` +/* + * 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.ui; + +import java.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; + +/** + * DateTimePicker 类用于显示和选择日期和时间。 + */ +public class DateTimePicker extends FrameLayout { + + private static final boolean DEFAULT_ENABLE_STATE = true; + + private static final int HOURS_IN_HALF_DAY = 12; + private static final int HOURS_IN_ALL_DAY = 24; + private static final int DAYS_IN_ALL_WEEK = 7; + private static final int DATE_SPINNER_MIN_VAL = 0; + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; + private static final int MINUT_SPINNER_MIN_VAL = 0; + private static final int MINUT_SPINNER_MAX_VAL = 59; + private static final int AMPM_SPINNER_MIN_VAL = 0; + private static final int AMPM_SPINNER_MAX_VAL = 1; + + private final NumberPicker mDateSpinner; + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mAmPmSpinner; + private Calendar mDate; + + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + + private boolean mIsAm; + + private boolean mIs24HourView; + + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + + private boolean mInitialising; + + private OnDateTimeChangedListener mOnDateTimeChangedListener; + + private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); + updateDateControl(); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + if (!mIs24HourView) { + if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || + oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + mIsAm = !mIsAm; + updateAmPmControl(); + } + } else { + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + mDate.set(Calendar.HOUR_OF_DAY, newHour); + onDateTimeChanged(); + if (isDateChanged) { + setCurrentYear(cal.get(Calendar.YEAR)); + setCurrentMonth(cal.get(Calendar.MONTH)); + setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); + } + } + }; + + private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + offset -= 1; + } + if (offset != 0) { + mDate.add(Calendar.HOUR_OF_DAY, offset); + mHourSpinner.setValue(getCurrentHour()); + updateDateControl(); + int newHour = getCurrentHourOfDay(); + if (newHour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + updateAmPmControl(); + } else { + mIsAm = true; + updateAmPmControl(); + } + } + mDate.set(Calendar.MINUTE, newVal); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mIsAm = !mIsAm; + if (mIsAm) { + mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); + } else { + mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); + } + updateAmPmControl(); + onDateTimeChanged(); + } + }; + + /** + * OnDateTimeChangedListener 接口用于监听日期和时间的变化。 + */ + public interface OnDateTimeChangedListener { + void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute); + } + + /** + * 构造函数,使用当前时间初始化 DateTimePicker。 + */ + public DateTimePicker(Context context) { + this(context, System.currentTimeMillis()); + } + + /** + * 构造函数,使用指定时间初始化 DateTimePicker。 + */ + public DateTimePicker(Context context, long date) { + this(context, date, DateFormat.is24HourFormat(context)); + } + + /** + * 构造函数,使用指定时间和24小时制设置初始化 DateTimePicker。 + */ + public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + mDate = Calendar.getInstance(); + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + + mDateSpinner = (NumberPicker) findViewById(R.id.date); + mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); + mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); + mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + + mHourSpinner = (NumberPicker) findViewById(R.id.hour); + mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); + mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); + mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); + mMinuteSpinner.setOnLongPressUpdateInterval(100); + mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + + String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); + mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); + mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); + mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); + mAmPmSpinner.setDisplayedValues(stringsForAmPm); + mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); + + // 更新控件到初始状态 + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + set24HourView(is24HourView); + + // 设置为当前时间 + setCurrentDate(date); + + setEnabled(isEnabled()); + + // 设置内容描述 + mInitialising = false; + } + + /** + * 设置控件是否启用。 + */ + @Override + public void setEnabled(boolean enabled) { + if (mIsEnabled == enabled) { + return; + } + super.setEnabled(enabled); + mDateSpinner.setEnabled(enabled); + mMinuteSpinner.setEnabled(enabled); + mHourSpinner.setEnabled(enabled); + mAmPmSpinner.setEnabled(enabled); + mIsEnabled = enabled; + } + + /** + * 获取控件是否启用。 + */ + @Override + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * 获取当前日期的毫秒数。 + */ + public long getCurrentDateInTimeMillis() { + return mDate.getTimeInMillis(); + } + + /** + * 设置当前日期。 + */ + public void setCurrentDate(long date) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + } + + /** + * 设置当前日期。 + */ + public void setCurrentDate(int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + setCurrentYear(year); + setCurrentMonth(month); + setCurrentDay(dayOfMonth); + setCurrentHour(hourOfDay); + setCurrentMinute(minute); + } + + /** + * 获取当前年份。 + */ + public int getCurrentYear() { + return mDate.get(Calendar.YEAR); + } + + /** + * 设置当前年份。 + */ + public void setCurrentYear(int year) { + if (!mInitialising && year == getCurrentYear()) { + return; + } + mDate.set(Calendar.YEAR, year); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * 获取当前月份。 + */ + public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); + } + + /** + * 设置当前月份。 + */ + public void setCurrentMonth(int month) { + if (!mInitialising && month == getCurrentMonth()) { + return; + } + mDate.set(Calendar.MONTH, month); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * 获取当前日期。 + */ + public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); + } + + /** + * 设置当前日期。 + */ + public void setCurrentDay(int dayOfMonth) { + if (!mInitialising && dayOfMonth == getCurrentDay()) { + return; + } + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * 获取当前小时(24小时制)。 + */ + public int getCurrentHourOfDay() { + return mDate.get(Calendar.HOUR_OF_DAY); + } + + private int getCurrentHour() { + if (mIs24HourView){ + return getCurrentHourOfDay(); + } else { + int hour = getCurrentHourOfDay(); + if (hour > HOURS_IN_HALF_DAY) { + return hour - HOURS_IN_HALF_DAY; + } else { + return hour == 0 ? HOURS_IN_HALF_DAY : hour; + } + } + } + + /** + * 设置当前小时(24小时制)。 + */ + public void setCurrentHour(int hourOfDay) { + if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { + return; + } + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + if (!mIs24HourView) { + if (hourOfDay >= HOURS_IN_HALF_DAY) { + mIsAm = false; + if (hourOfDay > HOURS_IN_HALF_DAY) { + hourOfDay -= HOURS_IN_HALF_DAY; + } + } else { + mIsAm = true; + if (hourOfDay == 0) { + hourOfDay = HOURS_IN_HALF_DAY; + } + } + updateAmPmControl(); + } + mHourSpinner.setValue(hourOfDay); + onDateTimeChanged(); + } + + /** + * 获取当前分钟。 + */ + public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); + } + + /** + * 设置当前分钟。 + */ + public void setCurrentMinute(int minute) { + if (!mInitialising && minute == getCurrentMinute()) { + return; + } + mMinuteSpinner.setValue(minute); + mDate.set(Calendar.MINUTE, minute); + onDateTimeChanged(); + } + + /** + * 判断是否为24小时制。 + */ + public boolean is24HourView () { + return mIs24HourView; + } + + /** + * 设置是否为24小时制。 + */ + public void set24HourView(boolean is24HourView) { + if (mIs24HourView == is24HourView) { + return; + } + mIs24HourView = is24HourView; + mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); + int hour = getCurrentHourOfDay(); + updateHourControl(); + setCurrentHour(hour); + updateAmPmControl(); + } + + /** + * 更新日期控件的显示。 + */ + private void updateDateControl() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); + mDateSpinner.setDisplayedValues(null); + for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { + cal.add(Calendar.DAY_OF_YEAR, 1); + mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); + } + mDateSpinner.setDisplayedValues(mDateDisplayValues); + mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); + mDateSpinner.invalidate(); + } + + /** + * 更新AM/PM控件的显示。 + */ + private void updateAmPmControl() { + if (mIs24HourView) { + mAmPmSpinner.setVisibility(View.GONE); + } else { + int index = mIsAm ? Calendar.AM : Calendar.PM; + mAmPmSpinner.setValue(index); + mAmPmSpinner.setVisibility(View.VISIBLE); + } + } + + /** + * 更新小时控件的显示。 + */ + private void updateHourControl() { + if (mIs24HourView) { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); + } else { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); + } + } + + /** + * 设置日期时间变化的监听器。 + */ + public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { + mOnDateTimeChangedListener = callback; + } + + /** + * 当日期或时间发生变化时调用此方法。 + */ + private void onDateTimeChanged() { + if (mOnDateTimeChangedListener != null) { + mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), + getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); + } + } +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/DateTimePickerDialog.java b/src/Notes-master/src/net/micode/notes/ui/DateTimePickerDialog.java index 2c47ba4..e3c2eb4 100644 --- a/src/Notes-master/src/net/micode/notes/ui/DateTimePickerDialog.java +++ b/src/Notes-master/src/net/micode/notes/ui/DateTimePickerDialog.java @@ -13,33 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import java.util.Calendar; - + import net.micode.notes.R; import net.micode.notes.ui.DateTimePicker; import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; - + import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.text.format.DateFormat; import android.text.format.DateUtils; - + +// 自定义的日期时间选择对话框,继承自AlertDialog并实现了OnClickListener接口 public class DateTimePickerDialog extends AlertDialog implements OnClickListener { - + + // 存储当前选择的日期时间 private Calendar mDate = Calendar.getInstance(); + // 是否使用24小时制 private boolean mIs24HourView; + // 回调接口,当日期时间设置完成后调用 private OnDateTimeSetListener mOnDateTimeSetListener; + // 日期时间选择器控件 private DateTimePicker mDateTimePicker; - + + // 定义日期时间设置完成后的回调接口 public interface OnDateTimeSetListener { void OnDateTimeSet(AlertDialog dialog, long date); } - + + // 构造函数,初始化对话框并设置初始日期时间 public DateTimePickerDialog(Context context, long date) { super(context); mDateTimePicker = new DateTimePicker(context); @@ -63,15 +70,18 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener set24HourView(DateFormat.is24HourFormat(this.getContext())); updateTitle(mDate.getTimeInMillis()); } - + + // 设置是否使用24小时制显示时间 public void set24HourView(boolean is24HourView) { mIs24HourView = is24HourView; } - + + // 设置日期时间选择完成后的回调监听器 public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { mOnDateTimeSetListener = callBack; } - + + // 更新对话框的标题以显示当前选择的日期时间 private void updateTitle(long date) { int flag = DateUtils.FORMAT_SHOW_YEAR | @@ -80,11 +90,12 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); } - + + // 处理用户点击对话框按钮的事件,如果是确认按钮则调用回调监听器 public void onClick(DialogInterface arg0, int arg1) { if (mOnDateTimeSetListener != null) { mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); } } - + } \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/DropdownMenu.java b/src/Notes-master/src/net/micode/notes/ui/DropdownMenu.java index 613dc74..eb009a2 100644 --- a/src/Notes-master/src/net/micode/notes/ui/DropdownMenu.java +++ b/src/Notes-master/src/net/micode/notes/ui/DropdownMenu.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.Context; import android.view.Menu; import android.view.MenuItem; @@ -24,14 +24,16 @@ import android.view.View.OnClickListener; import android.widget.Button; import android.widget.PopupMenu; import android.widget.PopupMenu.OnMenuItemClickListener; - + import net.micode.notes.R; - + +// 下拉菜单类,用于在按钮点击时显示一个弹出菜单 public class DropdownMenu { - private Button mButton; - private PopupMenu mPopupMenu; - private Menu mMenu; - + private Button mButton; // 用于触发下拉菜单的按钮 + private PopupMenu mPopupMenu; // 弹出菜单对象 + private Menu mMenu; // 菜单对象 + + // 构造函数,初始化下拉菜单,设置按钮背景,并将菜单资源加载到弹出菜单中 public DropdownMenu(Context context, Button button, int menuId) { mButton = button; mButton.setBackgroundResource(R.drawable.dropdown_icon); @@ -44,18 +46,21 @@ public class DropdownMenu { } }); } - + + // 设置下拉菜单项点击监听器 public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } - + + // 根据菜单项的ID查找菜单项 public MenuItem findItem(int id) { return mMenu.findItem(id); } - + + // 设置按钮的标题 public void setTitle(CharSequence title) { mButton.setText(title); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/FoldersListAdapter.java b/src/Notes-master/src/net/micode/notes/ui/FoldersListAdapter.java index 96b77da..1c6a78a 100644 --- a/src/Notes-master/src/net/micode/notes/ui/FoldersListAdapter.java +++ b/src/Notes-master/src/net/micode/notes/ui/FoldersListAdapter.java @@ -28,26 +28,31 @@ import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; - +// 自定义的 CursorAdapter 用于显示文件夹列表 public class FoldersListAdapter extends CursorAdapter { + // 定义查询文件夹时需要的列 public static final String [] PROJECTION = { NoteColumns.ID, NoteColumns.SNIPPET }; + // 列索引常量 public static final int ID_COLUMN = 0; public static final int NAME_COLUMN = 1; + // 构造函数,初始化 FoldersListAdapter public FoldersListAdapter(Context context, Cursor c) { super(context, c); // TODO Auto-generated constructor stub } + // 创建一个新的视图项 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new FolderListItem(context); } + // 绑定数据到视图项 @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof FolderListItem) { @@ -57,24 +62,28 @@ public class FoldersListAdapter extends CursorAdapter { } } + // 根据位置获取文件夹名称 public String getFolderName(Context context, int position) { Cursor cursor = (Cursor) getItem(position); return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); } + // 内部类,定义文件夹列表项的视图结构 private class FolderListItem extends LinearLayout { private TextView mName; + // 构造函数,初始化 FolderListItem 视图 public FolderListItem(Context context) { super(context); inflate(context, R.layout.folder_list_item, this); mName = (TextView) findViewById(R.id.tv_folder_name); } + // 绑定文件夹名称到视图 public void bind(String name) { mName.setText(name); } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java index 96a9ff8..3e45406 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.app.Activity; import android.app.AlarmManager; import android.app.AlertDialog; @@ -51,7 +51,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.TextNote; @@ -64,26 +64,28 @@ import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; - + import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - - + +// 笔记编辑界面的Activity类 public class NoteEditActivity extends Activity implements OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener { + // 用于存储笔记头部视图的ViewHolder类 private class HeadViewHolder { public TextView tvModified; - + public ImageView ivAlertIcon; - + public TextView tvAlertDate; - + public ImageView ibSetBgColor; } - + + // 背景色选择按钮和颜色ID的映射 private static final Map sBgSelectorBtnsMap = new HashMap(); static { sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); @@ -92,7 +94,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); } - + + // 背景色选择后的显示标记和颜色ID的映射 private static final Map sBgSelectorSelectionMap = new HashMap(); static { sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); @@ -101,7 +104,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); } - + + // 字体大小选择按钮和字体大小ID的映射 private static final Map sFontSizeBtnsMap = new HashMap(); static { sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); @@ -109,7 +113,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); } - + + // 字体大小选择后的显示标记和字体大小ID的映射 private static final Map sFontSelectorSelectionMap = new HashMap(); static { sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); @@ -117,54 +122,52 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); } - + private static final String TAG = "NoteEditActivity"; - + private HeadViewHolder mNoteHeaderHolder; - + private View mHeadViewPanel; - + private View mNoteBgColorSelector; - + private View mFontSizeSelector; - + private EditText mNoteEditor; - + private View mNoteEditorPanel; - + private WorkingNote mWorkingNote; - + private SharedPreferences mSharedPrefs; private int mFontSizeId; - + private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; - + private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; - + public static final String TAG_CHECKED = String.valueOf('\u221A'); public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); - + private LinearLayout mEditTextList; - + private String mUserQuery; private Pattern mPattern; - + + // 初始化Activity视图 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.note_edit); - + if (savedInstanceState == null && !initActivityState(getIntent())) { finish(); return; } initResources(); } - - /** - * Current activity may be killed when the memory is low. Once it is killed, for another time - * user load this activity, we should restore the former state - */ + + // 当Activity被系统杀死后,恢复其状态 @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); @@ -178,25 +181,19 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.d(TAG, "Restoring from killed activity"); } } - + + // 初始化Activity状态,根据Intent决定加载笔记或创建新笔记 private boolean initActivityState(Intent intent) { - /** - * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, - * then jump to the NotesListActivity - */ mWorkingNote = null; if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); mUserQuery = ""; - - /** - * Starting from the searched result - */ + if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); } - + if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { Intent jump = new Intent(this, NotesListActivity.class); startActivity(jump); @@ -215,7 +212,6 @@ public class NoteEditActivity extends Activity implements OnClickListener, WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { - // New note long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); @@ -223,8 +219,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, Notes.TYPE_WIDGET_INVALIDE); int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, ResourceParser.getDefaultBgId(this)); - - // Parse call-record note + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); if (callDate != 0 && phoneNumber != null) { @@ -249,7 +244,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); } - + getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); @@ -261,13 +256,15 @@ public class NoteEditActivity extends Activity implements OnClickListener, mWorkingNote.setOnSettingStatusChangedListener(this); return true; } - + + // Activity恢复时初始化笔记显示 @Override protected void onResume() { super.onResume(); initNoteScreen(); } - + + // 初始化笔记显示界面,包括背景颜色、字体大小、修改日期等 private void initNoteScreen() { mNoteEditor.setTextAppearance(this, TextAppearanceResources .getTexAppearanceResource(mFontSizeId)); @@ -282,19 +279,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, } mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - + mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR)); - - /** - * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker - * is not ready - */ + showAlertHeader(); } - + + // 显示或隐藏提醒头部信息 private void showAlertHeader() { if (mWorkingNote.hasClockAlert()) { long time = System.currentTimeMillis(); @@ -311,28 +305,26 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); }; } - + + // 处理新的Intent,可能需要重新加载笔记或创建新笔记 @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); initActivityState(intent); } - + + // 保存Activity状态,在系统需要恢复Activity时使用 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - /** - * For new note without note id, we should firstly save it to - * generate a id. If the editing note is not worth saving, there - * is no id which is equivalent to create new note - */ if (!mWorkingNote.existInDatabase()) { saveNote(); } outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); } - + + // 处理触摸事件,用于隐藏颜色和字体大小选择面板 @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mNoteBgColorSelector.getVisibility() == View.VISIBLE @@ -340,7 +332,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteBgColorSelector.setVisibility(View.GONE); return true; } - + if (mFontSizeSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mFontSizeSelector, ev)) { mFontSizeSelector.setVisibility(View.GONE); @@ -348,7 +340,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return super.dispatchTouchEvent(ev); } - + + // 判断触摸事件是否发生在指定视图内 private boolean inRangeOfView(View view, MotionEvent ev) { int []location = new int[2]; view.getLocationOnScreen(location); @@ -362,7 +355,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return true; } - + + // 初始化视图资源 private void initResources() { mHeadViewPanel = findViewById(R.id.note_title); mNoteHeaderHolder = new HeadViewHolder(); @@ -378,7 +372,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, ImageView iv = (ImageView) findViewById(id); iv.setOnClickListener(this); } - + mFontSizeSelector = findViewById(R.id.font_size_selector); for (int id : sFontSizeBtnsMap.keySet()) { View view = findViewById(id); @@ -386,17 +380,13 @@ public class NoteEditActivity extends Activity implements OnClickListener, }; mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; } mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); } - + + // 暂停Activity时保存笔记并清理设置状态 @Override protected void onPause() { super.onPause(); @@ -405,7 +395,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } clearSettingState(); } - + + // 更新桌面小部件显示 private void updateWidget() { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { @@ -416,21 +407,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.e(TAG, "Unspported widget type"); return; } - + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { mWorkingNote.getWidgetId() }); - + sendBroadcast(intent); setResult(RESULT_OK, intent); } - + + // 处理视图点击事件,包括颜色和字体大小选择 public void onClick(View v) { int id = v.getId(); if (id == R.id.btn_set_bg_color) { mNoteBgColorSelector.setVisibility(View.VISIBLE); findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - - View.VISIBLE); + View.VISIBLE); } else if (sBgSelectorBtnsMap.containsKey(id)) { findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( View.GONE); @@ -451,17 +443,19 @@ public class NoteEditActivity extends Activity implements OnClickListener, mFontSizeSelector.setVisibility(View.GONE); } } - + + // 处理返回键事件,如果设置面板可见则隐藏,否则保存笔记后返回 @Override public void onBackPressed() { if(clearSettingState()) { return; } - + saveNote(); super.onBackPressed(); } - + + // 清理设置面板状态 private boolean clearSettingState() { if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { mNoteBgColorSelector.setVisibility(View.GONE); @@ -472,14 +466,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return false; } - + + // 处理笔记背景颜色变化事件 public void onBackgroundColorChanged() { findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( View.VISIBLE); mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); } - + + // 准备选项菜单,根据笔记状态动态更新菜单项 @Override public boolean onPrepareOptionsMenu(Menu menu) { if (isFinishing()) { @@ -504,7 +500,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return true; } - + + // 处理选项菜单项点击事件 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -552,7 +549,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return true; } - + + // 设置提醒时间 private void setReminder() { DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); d.setOnDateTimeSetListener(new OnDateTimeSetListener() { @@ -562,30 +560,27 @@ public class NoteEditActivity extends Activity implements OnClickListener, }); d.show(); } - - /** - * Share note to apps that support {@link Intent#ACTION_SEND} action - * and {@text/plain} type - */ + + // 分享笔记内容到支持ACTION_SEND的其他应用 private void sendTo(Context context, String info) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, info); intent.setType("text/plain"); context.startActivity(intent); } - + + // 创建新笔记,跳转到新笔记编辑界面 private void createNewNote() { - // Firstly, save current editing notes saveNote(); - - // For safety, start a new NoteEditActivity + finish(); Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); startActivity(intent); } - + + // 删除当前笔记 private void deleteCurrentNote() { if (mWorkingNote.existInDatabase()) { HashSet ids = new HashSet(); @@ -607,16 +602,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, } mWorkingNote.markDeleted(true); } - + + // 判断是否处于同步模式 private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } - + + // 处理时钟提醒变化事件,设置或取消提醒 public void onClockAlertChanged(long date, boolean set) { - /** - * User could set clock to an unsaved note, so before setting the - * alert clock, we should save the note first - */ if (!mWorkingNote.existInDatabase()) { saveNote(); } @@ -632,31 +625,28 @@ public class NoteEditActivity extends Activity implements OnClickListener, alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); } } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ Log.e(TAG, "Clock alert setting error"); showToast(R.string.error_note_empty_for_clock); } } - + + // 处理小部件变化事件,更新小部件显示 public void onWidgetChanged() { updateWidget(); } - + + // 处理EditText删除事件,调整列表项索引 public void onEditTextDelete(int index, String text) { int childCount = mEditTextList.getChildCount(); if (childCount == 1) { return; } - + for (int i = index + 1; i < childCount; i++) { ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) .setIndex(i - 1); } - + mEditTextList.removeViewAt(index); NoteEditText edit = null; if(index == 0) { @@ -671,15 +661,13 @@ public class NoteEditActivity extends Activity implements OnClickListener, edit.requestFocus(); edit.setSelection(length); } - + + // 处理EditText输入事件,添加新列表项 public void onEditTextEnter(int index, String text) { - /** - * Should not happen, check for debug - */ if(index > mEditTextList.getChildCount()) { Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); } - + View view = getListItem(text, index); mEditTextList.addView(view, index); NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); @@ -690,7 +678,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, .setIndex(i); } } - + + // 切换到列表模式,根据笔记内容生成列表项 private void switchToListMode(String text) { mEditTextList.removeAllViews(); String[] items = text.split("\n"); @@ -703,11 +692,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, } mEditTextList.addView(getListItem("", index)); mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); - + mNoteEditor.setVisibility(View.GONE); mEditTextList.setVisibility(View.VISIBLE); } - + + // 获取高亮查询结果,用于显示搜索关键词 private Spannable getHighlightQueryResult(String fullText, String userQuery) { SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); if (!TextUtils.isEmpty(userQuery)) { @@ -724,7 +714,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return spannable; } - + + // 生成一个新的列表项视图 private View getListItem(String item, int index) { View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); @@ -739,7 +730,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } }); - + if (item.startsWith(TAG_CHECKED)) { cb.setChecked(true); edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); @@ -749,13 +740,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); } - + edit.setOnTextViewChangeListener(this); edit.setIndex(index); edit.setText(getHighlightQueryResult(item, mUserQuery)); return view; } - + + // 处理EditText文本变化事件,显示或隐藏复选框 public void onTextChange(int index, boolean hasText) { if (index >= mEditTextList.getChildCount()) { Log.e(TAG, "Wrong index, should not happen"); @@ -767,7 +759,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); } } - + + // 处理笔记模式变化事件,从普通模式切换到列表模式或反之 public void onCheckListModeChanged(int oldMode, int newMode) { if (newMode == TextNote.MODE_CHECK_LIST) { switchToListMode(mNoteEditor.getText().toString()); @@ -781,7 +774,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteEditor.setVisibility(View.VISIBLE); } } - + + // 获取正在编辑的文本内容,根据列表模式添加标签 private boolean getWorkingText() { boolean hasChecked = false; if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { @@ -804,33 +798,23 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return hasChecked; } - + + // 保存笔记内容到数据库 private boolean saveNote() { getWorkingText(); boolean saved = mWorkingNote.saveNote(); if (saved) { - /** - * There are two modes from List view to edit view, open one note, - * create/edit a node. Opening node requires to the original - * position in the list when back from edit view, while creating a - * new node requires to the top of the list. This code - * {@link #RESULT_OK} is used to identify the create/edit state - */ setResult(RESULT_OK); } return saved; } - + + // 将笔记快捷方式添加到桌面 private void sendToDesktop() { - /** - * Before send message to home, we should make sure that current - * editing note is exists in databases. So, for new note, firstly - * save it - */ if (!mWorkingNote.existInDatabase()) { saveNote(); } - + if (mWorkingNote.getNoteId() > 0) { Intent sender = new Intent(); Intent shortcutIntent = new Intent(this, NoteEditActivity.class); @@ -846,28 +830,26 @@ public class NoteEditActivity extends Activity implements OnClickListener, showToast(R.string.info_note_enter_desktop); sendBroadcast(sender); } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ Log.e(TAG, "Send to desktop error"); showToast(R.string.error_note_empty_for_send_to_desktop); } } - + + // 生成桌面快捷方式的标题,截取笔记内容的一部分作为标题 private String makeShortcutIconTitle(String content) { content = content.replace(TAG_CHECKED, ""); content = content.replace(TAG_UNCHECKED, ""); return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, SHORTCUT_ICON_TITLE_MAX_LEN) : content; } - + + // 显示短Toast消息 private void showToast(int resId) { showToast(resId, Toast.LENGTH_SHORT); } - + + // 显示指定持续时间的Toast消息 private void showToast(int resId, int duration) { Toast.makeText(this, resId, duration).show(); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditText.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditText.java index 2afe2a8..d350c57 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditText.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditText.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.Context; import android.graphics.Rect; import android.text.Layout; @@ -31,28 +31,29 @@ import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.MotionEvent; import android.widget.EditText; - + import net.micode.notes.R; - + import java.util.HashMap; import java.util.Map; - + +// 自定义的EditText,用于笔记应用中,支持删除、添加文本事件监听 public class NoteEditText extends EditText { private static final String TAG = "NoteEditText"; private int mIndex; private int mSelectionStartBeforeDelete; - + private static final String SCHEME_TEL = "tel:" ; private static final String SCHEME_HTTP = "http:" ; private static final String SCHEME_EMAIL = "mailto:" ; - + private static final Map sSchemaActionResMap = new HashMap(); static { sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); } - + /** * Call by the {@link NoteEditActivity} to delete or add edit text */ @@ -62,65 +63,69 @@ public class NoteEditText extends EditText { * and the text is null */ void onEditTextDelete(int index, String text); - + /** * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} * happen */ void onEditTextEnter(int index, String text); - + /** * Hide or show item option when text change */ void onTextChange(int index, boolean hasText); } - + private OnTextViewChangeListener mOnTextViewChangeListener; - + public NoteEditText(Context context) { super(context, null); mIndex = 0; } - + + // 设置当前文本框的索引 public void setIndex(int index) { mIndex = index; } - + + // 设置文本变化监听器 public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { mOnTextViewChangeListener = listener; } - + public NoteEditText(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.editTextStyle); } - + public NoteEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } - + + // 处理触摸事件,更新光标位置 @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - + int x = (int) event.getX(); int y = (int) event.getY(); x -= getTotalPaddingLeft(); y -= getTotalPaddingTop(); x += getScrollX(); y += getScrollY(); - + Layout layout = getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); Selection.setSelection(getText(), off); break; } - + return super.onTouchEvent(event); } - + + // 处理按键按下事件,记录删除操作前的光标位置 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { @@ -137,7 +142,8 @@ public class NoteEditText extends EditText { } return super.onKeyDown(keyCode, event); } - + + // 处理按键弹起事件,根据按键类型执行相应操作 @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode) { @@ -166,7 +172,8 @@ public class NoteEditText extends EditText { } return super.onKeyUp(keyCode, event); } - + + // 当EditText焦点发生变化时调用,通知监听器文本是否有内容 @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (mOnTextViewChangeListener != null) { @@ -178,16 +185,17 @@ public class NoteEditText extends EditText { } super.onFocusChanged(focused, direction, previouslyFocusedRect); } - + + // 创建上下文菜单,处理URL点击事件 @Override protected void onCreateContextMenu(ContextMenu menu) { if (getText() instanceof Spanned) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); - + int min = Math.min(selStart, selEnd); int max = Math.max(selStart, selEnd); - + final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); if (urls.length == 1) { int defaultResId = 0; @@ -197,11 +205,11 @@ public class NoteEditText extends EditText { break; } } - + if (defaultResId == 0) { defaultResId = R.string.note_link_other; } - + menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { @@ -214,4 +222,4 @@ public class NoteEditText extends EditText { } super.onCreateContextMenu(menu); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java b/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java index 0f5a878..632fe29 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.Context; import android.database.Cursor; import android.text.TextUtils; - + import net.micode.notes.data.Contact; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; - - + +// 该类用于从数据库游标中提取笔记项数据,并处理与笔记位置相关的逻辑 public class NoteItemData { static final String [] PROJECTION = new String [] { NoteColumns.ID, @@ -41,7 +41,8 @@ public class NoteItemData { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, }; - + + // 定义了游标中各个列的索引位置 private static final int ID_COLUMN = 0; private static final int ALERTED_DATE_COLUMN = 1; private static final int BG_COLOR_ID_COLUMN = 2; @@ -54,7 +55,8 @@ public class NoteItemData { private static final int TYPE_COLUMN = 9; private static final int WIDGET_ID_COLUMN = 10; private static final int WIDGET_TYPE_COLUMN = 11; - + + // 笔记项的各种属性 private long mId; private long mAlertDate; private int mBgColorId; @@ -69,13 +71,15 @@ public class NoteItemData { private int mWidgetType; private String mName; private String mPhoneNumber; - + + // 笔记项在列表中的位置信息 private boolean mIsLastItem; private boolean mIsFirstItem; private boolean mIsOnlyOneItem; private boolean mIsOneNoteFollowingFolder; private boolean mIsMultiNotesFollowingFolder; - + + // 构造函数,从游标中提取笔记项数据 public NoteItemData(Context context, Cursor cursor) { mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); @@ -91,7 +95,7 @@ public class NoteItemData { mType = cursor.getInt(TYPE_COLUMN); mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); - + mPhoneNumber = ""; if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); @@ -102,20 +106,21 @@ public class NoteItemData { } } } - + if (mName == null) { mName = ""; } checkPostion(cursor); } - + + // 检查笔记项在列表中的位置信息 private void checkPostion(Cursor cursor) { mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false; mIsOnlyOneItem = (cursor.getCount() == 1); mIsMultiNotesFollowingFolder = false; mIsOneNoteFollowingFolder = false; - + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { int position = cursor.getPosition(); if (cursor.moveToPrevious()) { @@ -133,92 +138,114 @@ public class NoteItemData { } } } - + + // 判断该笔记项是否是单个笔记跟在一个文件夹后 public boolean isOneFollowingFolder() { return mIsOneNoteFollowingFolder; } - + + // 判断该笔记项是否是多个笔记跟在一个文件夹后 public boolean isMultiFollowingFolder() { return mIsMultiNotesFollowingFolder; } - + + // 判断该笔记项是否是列表中的最后一个项 public boolean isLast() { return mIsLastItem; } - + + // 获取与该笔记项关联的呼叫记录的联系人名称 public String getCallName() { return mName; } - + + // 判断该笔记项是否是列表中的第一个项 public boolean isFirst() { return mIsFirstItem; } - + + // 判断该笔记项是否是列表中唯一的项 public boolean isSingle() { return mIsOnlyOneItem; } - + + // 获取笔记项的ID public long getId() { return mId; } - + + // 获取笔记项的提醒日期 public long getAlertDate() { return mAlertDate; } - + + // 获取笔记项的创建日期 public long getCreatedDate() { return mCreatedDate; } - + + // 判断该笔记项是否有附件 public boolean hasAttachment() { return mHasAttachment; } - + + // 获取笔记项的修改日期 public long getModifiedDate() { return mModifiedDate; } - + + // 获取笔记项的背景颜色ID public int getBgColorId() { return mBgColorId; } - + + // 获取笔记项的父ID public long getParentId() { return mParentId; } - + + // 获取笔记项包含的笔记数量 public int getNotesCount() { return mNotesCount; } - + + // 获取笔记项所在的文件夹ID public long getFolderId () { return mParentId; } - + + // 获取笔记项的类型 public int getType() { return mType; } - + + // 获取笔记项的小部件类型 public int getWidgetType() { return mWidgetType; } - + + // 获取笔记项的小部件ID public int getWidgetId() { return mWidgetId; } - + + // 获取笔记项的摘要 public String getSnippet() { return mSnippet; } - + + // 判断该笔记项是否有提醒 public boolean hasAlert() { return (mAlertDate > 0); } - + + // 判断该笔记项是否是呼叫记录类型 public boolean isCallRecord() { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } - + + // 静态方法,从游标中获取笔记项的类型 public static int getNoteType(Cursor cursor) { return cursor.getInt(TYPE_COLUMN); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java index e843aec..5893567 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java @@ -126,12 +126,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private NoteItemData mFocusNoteDataItem; private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; - + private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; - + private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; @@ -951,4 +951,4 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java b/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..fc77697 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java @@ -13,48 +13,52 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.Context; import android.database.Cursor; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; - + import net.micode.notes.data.Notes; - + import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; - - + +// 自定义的CursorAdapter,用于显示笔记列表 public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; private Context mContext; private HashMap mSelectedIndex; private int mNotesCount; private boolean mChoiceMode; - + + // 用于存储小部件属性的内部类 public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; - + + // 构造函数,初始化上下文和选择索引 public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); mContext = context; mNotesCount = 0; } - + + // 创建新的视图项 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } - + + // 绑定数据到视图项 @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { @@ -63,21 +67,25 @@ public class NotesListAdapter extends CursorAdapter { isSelectedItem(cursor.getPosition())); } } - + + // 设置指定位置的项是否被选中,并通知数据集发生变化 public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } - + + // 检查当前是否处于多选模式 public boolean isInChoiceMode() { return mChoiceMode; } - + + // 设置多选模式,清空选择索引 public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } - + + // 全选或全不选所有笔记 public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { @@ -88,7 +96,8 @@ public class NotesListAdapter extends CursorAdapter { } } } - + + // 获取所有选中的笔记ID集合 public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -101,10 +110,11 @@ public class NotesListAdapter extends CursorAdapter { } } } - + return itemSet; } - + + // 获取所有选中的小部件属性集合 public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -127,7 +137,8 @@ public class NotesListAdapter extends CursorAdapter { } return itemSet; } - + + // 获取选中的笔记数量 public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -142,31 +153,36 @@ public class NotesListAdapter extends CursorAdapter { } return count; } - + + // 检查是否所有笔记都被选中 public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount != 0 && checkedCount == mNotesCount); } - + + // 检查指定位置的项是否被选中 public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; } return mSelectedIndex.get(position); } - + + // 当数据内容发生变化时,更新笔记数量 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } - + + // 更改Cursor时,更新笔记数量 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } - + + // 计算笔记数量 private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { @@ -181,4 +197,4 @@ public class NotesListAdapter extends CursorAdapter { } } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java b/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java index 1221e80..1546bbe 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.content.Context; import android.text.format.DateUtils; import android.view.View; @@ -23,13 +23,13 @@ import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - - + +// NotesListItem 类继承自 LinearLayout,用于表示笔记列表中的一个项 public class NotesListItem extends LinearLayout { private ImageView mAlert; private TextView mTitle; @@ -37,7 +37,8 @@ public class NotesListItem extends LinearLayout { private TextView mCallName; private NoteItemData mItemData; private CheckBox mCheckBox; - + + // 构造函数,初始化 NotesListItem 的视图组件 public NotesListItem(Context context) { super(context); inflate(context, R.layout.note_item, this); @@ -47,7 +48,8 @@ public class NotesListItem extends LinearLayout { mCallName = (TextView) findViewById(R.id.tv_name); mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } - + + // 绑定数据到 NotesListItem 的视图组件,并设置选择模式和选中状态 public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setVisibility(View.VISIBLE); @@ -55,7 +57,7 @@ public class NotesListItem extends LinearLayout { } else { mCheckBox.setVisibility(View.GONE); } - + mItemData = data; if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.GONE); @@ -78,7 +80,7 @@ public class NotesListItem extends LinearLayout { } else { mCallName.setVisibility(View.GONE); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - + if (data.getType() == Notes.TYPE_FOLDER) { mTitle.setText(data.getSnippet() + context.getString(R.string.format_folder_files_count, @@ -95,10 +97,11 @@ public class NotesListItem extends LinearLayout { } } mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); - + setBackground(data); } - + + // 根据数据设置 NotesListItem 的背景资源 private void setBackground(NoteItemData data) { int id = data.getBgColorId(); if (data.getType() == Notes.TYPE_NOTE) { @@ -115,8 +118,9 @@ public class NotesListItem extends LinearLayout { setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } - + + // 获取绑定到此 NotesListItem 的数据 public NoteItemData getItemData() { return mItemData; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..48125be 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.ui; - + import android.accounts.Account; import android.accounts.AccountManager; import android.app.ActionBar; @@ -41,59 +41,60 @@ import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; - - + +// 设置界面活动类,继承自PreferenceActivity public class NotesPreferenceActivity extends PreferenceActivity { public static final String PREFERENCE_NAME = "notes_preferences"; - + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; - + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - + private static final String AUTHORITIES_FILTER_KEY = "authorities"; - + private PreferenceCategory mAccountCategory; - + private GTaskReceiver mReceiver; - + private Account[] mOriAccounts; - + private boolean mHasAddedAccount; - + + // 创建活动时初始化界面 @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - - /* using the app icon for navigation */ + + /* 使用应用图标进行导航 */ getActionBar().setDisplayHomeAsUpEnabled(true); - + addPreferencesFromResource(R.xml.preferences); mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); mReceiver = new GTaskReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); registerReceiver(mReceiver, filter); - + mOriAccounts = null; View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); getListView().addHeaderView(header, null, true); } - + + // 恢复活动时刷新界面 @Override protected void onResume() { super.onResume(); - - // need to set sync account automatically if user has added a new - // account + + // 如果用户添加了新账户,自动设置同步账户 if (mHasAddedAccount) { Account[] accounts = getGoogleAccounts(); if (mOriAccounts != null && accounts.length > mOriAccounts.length) { @@ -112,10 +113,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } } - + refreshUI(); } - + + // 销毁活动时注销广播接收器 @Override protected void onDestroy() { if (mReceiver != null) { @@ -123,10 +125,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { } super.onDestroy(); } - + + // 加载账户偏好设置 private void loadAccountPreference() { mAccountCategory.removeAll(); - + Preference accountPref = new Preference(this); final String defaultAccount = getSyncAccountName(this); accountPref.setTitle(getString(R.string.preferences_account_title)); @@ -135,11 +138,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { public boolean onPreferenceClick(Preference preference) { if (!GTaskSyncService.isSyncing()) { if (TextUtils.isEmpty(defaultAccount)) { - // the first time to set account + // 第一次设置账户 showSelectAccountAlertDialog(); } else { - // if the account has already been set, we need to promp - // user about the risk + // 如果账户已经设置,提示用户切换账户的风险 showChangeAccountConfirmAlertDialog(); } } else { @@ -150,15 +152,16 @@ public class NotesPreferenceActivity extends PreferenceActivity { return true; } }); - + mAccountCategory.addPreference(accountPref); } - + + // 加载同步按钮 private void loadSyncButton() { Button syncButton = (Button) findViewById(R.id.preference_sync_button); TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - - // set button state + + // 设置按钮状态 if (GTaskSyncService.isSyncing()) { syncButton.setText(getString(R.string.preferences_button_sync_cancel)); syncButton.setOnClickListener(new View.OnClickListener() { @@ -175,8 +178,8 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); - - // set last sync time + + // 设置上次同步时间 if (GTaskSyncService.isSyncing()) { lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE); @@ -192,30 +195,32 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } } - + + // 刷新用户界面 private void refreshUI() { loadAccountPreference(); loadSyncButton(); } - + + // 显示选择账户的对话框 private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - + dialogBuilder.setCustomTitle(titleView); dialogBuilder.setPositiveButton(null, null); - + Account[] accounts = getGoogleAccounts(); String defAccount = getSyncAccountName(this); - + mOriAccounts = accounts; mHasAddedAccount = false; - + if (accounts.length > 0) { CharSequence[] items = new CharSequence[accounts.length]; final CharSequence[] itemMapping = items; @@ -236,10 +241,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } }); } - + View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); dialogBuilder.setView(addAccountView); - + final AlertDialog dialog = dialogBuilder.show(); addAccountView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { @@ -253,10 +258,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { } }); } - + + // 显示更改账户确认对话框 private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, @@ -264,7 +270,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); dialogBuilder.setCustomTitle(titleView); - + CharSequence[] menuItemArray = new CharSequence[] { getString(R.string.preferences_menu_change_account), getString(R.string.preferences_menu_remove_account), @@ -282,12 +288,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); dialogBuilder.show(); } - + + // 获取Google账户列表 private Account[] getGoogleAccounts() { AccountManager accountManager = AccountManager.get(this); return accountManager.getAccountsByType("com.google"); } - + + // 设置同步账户 private void setSyncAccount(String account) { if (!getSyncAccountName(this).equals(account)) { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -298,11 +306,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } editor.commit(); - - // clean up last sync time + + // 清除上次同步时间 setLastSyncTime(this, 0); - - // clean up local gtask related info + + // 清除本地Gtask相关信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -311,13 +319,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).start(); - + Toast.makeText(NotesPreferenceActivity.this, getString(R.string.preferences_toast_success_set_accout, account), Toast.LENGTH_SHORT).show(); } } - + + // 移除同步账户 private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); @@ -328,8 +337,8 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.remove(PREFERENCE_LAST_SYNC_TIME); } editor.commit(); - - // clean up local gtask related info + + // 清除本地Gtask相关信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -339,13 +348,15 @@ public class NotesPreferenceActivity extends PreferenceActivity { } }).start(); } - + + // 获取同步账户名称 public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } - + + // 设置上次同步时间 public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -353,15 +364,17 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); editor.commit(); } - + + // 获取上次同步时间 public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } - + + // 广播接收器,用于接收同步状态更新 private class GTaskReceiver extends BroadcastReceiver { - + @Override public void onReceive(Context context, Intent intent) { refreshUI(); @@ -370,10 +383,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); } - + } } - + + // 选项菜单项点击事件处理 public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: @@ -385,4 +399,4 @@ public class NotesPreferenceActivity extends PreferenceActivity { return false; } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider.java b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider.java index ec6f819..0fbd347 100644 --- a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider.java +++ b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.widget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; @@ -24,27 +24,32 @@ import android.content.Intent; import android.database.Cursor; import android.util.Log; import android.widget.RemoteViews; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.ResourceParser; import net.micode.notes.ui.NoteEditActivity; import net.micode.notes.ui.NotesListActivity; - + +// 提供笔记小部件功能的抽象类,继承自AppWidgetProvider public abstract class NoteWidgetProvider extends AppWidgetProvider { + // 查询笔记时使用的投影列 public static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.BG_COLOR_ID, NoteColumns.SNIPPET }; - + + // 投影列对应的索引 public static final int COLUMN_ID = 0; public static final int COLUMN_BG_COLOR_ID = 1; public static final int COLUMN_SNIPPET = 2; - + + // 日志标签 private static final String TAG = "NoteWidgetProvider"; - + + // 当小部件被删除时调用,更新数据库中的小部件ID为无效值 @Override public void onDeleted(Context context, int[] appWidgetIds) { ContentValues values = new ContentValues(); @@ -56,7 +61,8 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { new String[] { String.valueOf(appWidgetIds[i])}); } } - + + // 根据小部件ID获取笔记信息 private Cursor getNoteWidgetInfo(Context context, int widgetId) { return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, @@ -64,11 +70,13 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, null); } - + + // 更新小部件视图 protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { update(context, appWidgetManager, appWidgetIds, false); } - + + // 更新小部件视图,支持隐私模式 private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, boolean privacyMode) { for (int i = 0; i < appWidgetIds.length; i++) { @@ -79,7 +87,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); - + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); if (c != null && c.moveToFirst()) { if (c.getCount() > 1) { @@ -95,11 +103,11 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { snippet = context.getResources().getString(R.string.widget_havenot_content); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); } - + if (c != null) { c.close(); } - + RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); @@ -117,16 +125,19 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, PendingIntent.FLAG_UPDATE_CURRENT); } - + rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } } } - + + // 获取背景资源ID的方法,由子类实现 protected abstract int getBgResourceId(int bgId); - + + // 获取布局ID的方法,由子类实现 protected abstract int getLayoutId(); - + + // 获取小部件类型的ID,由子类实现 protected abstract int getWidgetType(); -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_2x.java b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_2x.java index adcb2f7..ffc1401 100644 --- a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_2x.java +++ b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_2x.java @@ -13,35 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package net.micode.notes.widget; - + import android.appwidget.AppWidgetManager; import android.content.Context; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.tool.ResourceParser; - - + +// 2x2 小部件提供者类,继承自 NoteWidgetProvider public class NoteWidgetProvider_2x extends NoteWidgetProvider { + // 更新小部件时调用的方法 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.update(context, appWidgetManager, appWidgetIds); } - + + // 返回 2x2 小部件的布局 ID @Override protected int getLayoutId() { return R.layout.widget_2x; } - + + // 根据背景 ID 返回对应的 2x2 小部件背景资源 ID @Override protected int getBgResourceId(int bgId) { return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); } - + + // 返回 2x2 小部件的类型 ID @Override protected int getWidgetType() { return Notes.TYPE_WIDGET_2X; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_4x.java b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_4x.java index c12a02e..d1b1240 100644 --- a/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_4x.java +++ b/src/Notes-master/src/net/micode/notes/widget/NoteWidgetProvider_4x.java @@ -1,19 +1,3 @@ -/* - * 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.widget; import android.appwidget.AppWidgetManager; @@ -23,24 +7,28 @@ import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.tool.ResourceParser; - +// 定义一个4x4小部件的提供者类,继承自NoteWidgetProvider public class NoteWidgetProvider_4x extends NoteWidgetProvider { + // 覆盖父类的onUpdate方法,用于更新小部件的视图 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.update(context, appWidgetManager, appWidgetIds); } + // 返回4x4小部件的布局资源ID protected int getLayoutId() { return R.layout.widget_4x; } + // 根据背景ID返回4x4小部件的背景资源ID @Override protected int getBgResourceId(int bgId) { return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); } + // 返回小部件的类型,这里是4x4类型 @Override protected int getWidgetType() { return Notes.TYPE_WIDGET_4X; } -} +} \ No newline at end of file