diff --git a/GTaskASyncTask.java b/GTaskASyncTask.java new file mode 100644 index 0000000..70ee0a9 --- /dev/null +++ b/GTaskASyncTask.java @@ -0,0 +1,145 @@ + +/* + * 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. + */ + +// 导入所需的Android和Java类 +package net.micode.notes.gtask.remote; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesListActivity; +import net.micode.notes.ui.NotesPreferenceActivity; + +// 定义一个继承自AsyncTask的类,用于异步执行Google Tasks同步任务 +public class GTaskASyncTask extends AsyncTask { + + // 定义一个常量,用于表示同步通知的ID + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + + // 定义一个接口,用于同步完成后的回调 + public interface OnCompleteListener { + void onComplete(); + } + + // 定义类成员变量 + private Context mContext; + private NotificationManager mNotifiManager; + private GTaskManager mTaskManager; + private OnCompleteListener mOnCompleteListener; + + // 构造函数,初始化成员变量 + public GTaskASyncTask(Context context, OnCompleteListener listener) { + mContext = context; + mOnCompleteListener = listener; + mNotifiManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + mTaskManager = GTaskManager.getInstance(); + } + + // 提供一个方法用于取消同步任务 + public void cancelSync() { + mTaskManager.cancelSync(); + } + + // 提供一个方法用于发布进度更新,封装了AsyncTask的publishProgress方法 + public void publishProgess(String message) { + publishProgress(new String[] { + message + }); + } + + // 定义一个方法用于显示通知 + private void showNotification(int tickerId, String content) { + // 创建一个Notification对象,设置图标、标题和时间戳 + Notification notification = new Notification(R.drawable.notification, mContext + .getString(tickerId), System.currentTimeMillis()); + // 设置通知的默认属性,如灯光闪烁 + notification.defaults = Notification.DEFAULT_LIGHTS; + // 设置通知的标志,使其点击后自动取消 + notification.flags = Notification.FLAG_AUTO_CANCEL; + // 根据通知类型创建PendingIntent + PendingIntent pendingIntent; + if (tickerId != R.string.ticker_success) { + // 如果不是成功通知,则跳转到设置活动 + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), 0); + + } else { + // 如果是成功通知,则跳转到笔记列表活动 + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), 0); + } + // 设置通知的详细信息 + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, + pendingIntent); + // 发送通知 + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); + } + + // 在后台线程中执行同步任务 + @Override + protected Integer doInBackground(Void... unused) { + // 发布登录进度通知 + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity + .getSyncAccountName(mContext))); + // 执行同步任务,并返回结果 + return mTaskManager.sync(mContext, this); + } + + // 当进度更新时,在UI线程中调用此方法 + @Override + protected void onProgressUpdate(String... progress) { + // 显示同步中的通知 + showNotification(R.string.ticker_syncing, progress[0]); + // 如果上下文是GTaskSyncService的实例,则发送广播通知进度 + if (mContext instanceof GTaskSyncService) { + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); + } + } + + // 在后台任务完成后,在UI线程中调用此方法 + @Override + protected void onPostExecute(Integer result) { + // 根据同步结果显示相应的通知 + if (result == GTaskManager.STATE_SUCCESS) { + showNotification(R.string.ticker_success, mContext.getString( + R.string.success_sync_account, mTaskManager.getSyncAccount())); + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); + } else if (result == GTaskManager.STATE_NETWORK_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { + showNotification(R.string.ticker_cancel, mContext + .getString(R.string.error_sync_cancelled)); + } + // 如果设置了回调监听器,则在新线程中调用其onComplete方法 + if (mOnCompleteListener != null) { + new Thread(new Runnable() { + + public void run() { + mOnCompleteListener.onComplete(); + } + }).start(); + } + } +} \ No newline at end of file diff --git a/Note.java b/Note.java new file mode 100644 index 0000000..7c16744 --- /dev/null +++ b/Note.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; // 声明当前类所在的包 + +import android.content.ContentProviderOperation; // 导入用于构建批量数据库操作的类 +import android.content.ContentProviderResult; // 导入用于接收批量操作结果的类 +import android.content.ContentUris; // 导入用于生成URI的类,这些URI指向数据库中的特定行 +import android.content.ContentValues; // 导入用于存储一组键值对的类,这些键值对将被插入到数据库表中 +import android.content.Context; // 导入Android应用上下文类 +import android.content.OperationApplicationException; // 导入表示操作应用异常的类 +import android.net.Uri; // 导入表示统一资源标识符的类 +import android.os.RemoteException; // 导入表示远程过程调用异常的类 +import android.util.Log; // 导入用于打印日志的类 + +import net.micode.notes.data.Notes; // 导入应用自定义的Notes类,可能是一个包含多个内部类的数据库模型 +import net.micode.notes.data.Notes.CallNote; // 导入Notes类中的CallNote内部类,可能代表电话笔记 +import net.micode.notes.data.Notes.DataColumns; // 导入Notes类中的DataColumns内部类,可能包含数据表的列定义 +import net.micode.notes.data.Notes.NoteColumns; // 导入Notes类中的NoteColumns内部类,可能包含笔记表的列定义 +import net.micode.notes.data.Notes.TextNote; // 导入Notes类中的TextNote内部类,可能代表文本笔记 + *//1 + + +import java.util.ArrayList; // 导入ArrayList类 + +public class Note { // 声明一个名为Note的公共类 + // 类的成员变量定义(在此代码段中未展示) + private ContentValues mNoteDiffValues; // 用于存储笔记的差异数据,以便后续更新数据库 + private NoteData mNoteData; // 可能用于存储笔记的详细数据 + private static final String TAG = "Note"; // 用于日志记录的标签 + + /** + * 创建一个新的笔记ID,用于向数据库添加新笔记 + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // 声明一个ContentValues对象,用于存储要插入数据库的新笔记的数据 + ContentValues values = new ContentValues(); + // 获取当前系统时间(毫秒为单位),作为笔记的创建和修改时间 + long createdTime = System.currentTimeMillis(); + // 向values中添加笔记的创建日期 + values.put(NoteColumns.CREATED_DATE, createdTime); + // 向values中添加笔记的修改日期(初始时与创建日期相同) + values.put(NoteColumns.MODIFIED_DATE, createdTime); + // 向values中添加笔记的类型(此处假设Notes.TYPE_NOTE是一个预定义的常量,表示笔记类型) + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + // 向values中添加一个标记,表示笔记是否在本地被修改过(初始化为1,可能表示已修改) + values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 向values中添加笔记的父ID,即它所属的文件夹ID + values.put(NoteColumns.PARENT_ID, folderId); + // 使用ContentResolver向数据库的Notes.CONTENT_NOTE_URI插入新笔记的数据,并返回新插入笔记的URI + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + // 初始化noteId为0,用于存储从URI解析出的新笔记ID + long noteId = 0; + try { + // 从返回的URI的路径段中获取第二个元素(索引为1,因为索引从0开始),并将其转换为long类型,作为新笔记的ID + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + // 如果转换过程中发生数字格式异常,则记录错误日志,并将noteId保持为0 + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + // 检查noteId是否为-1(虽然在此处不太可能,因为Long.valueOf不会返回-1) + // 但为了代码的健壮性,还是进行了检查 + if (noteId == -1) { + // 如果noteId为-1,则抛出IllegalStateException异常,表示获得了错误的笔记ID + throw new IllegalStateException("Wrong note id:" + noteId); + } + // 返回解析出的新笔记ID + return noteId; + } +}*//2 + + + // Note类的构造方法 +public Note() { + // 初始化mNoteDiffValues,用于存储笔记的差异数据,这些数据通常用于更新数据库中的笔记 + mNoteDiffValues = new ContentValues(); + // 初始化mNoteData,可能用于存储笔记的详细数据,如文本、语音等 + mNoteData = new NoteData(); +} + +// 设置笔记的某个键值对数据 +public void setNoteValue(String key, String value) { + // 将键值对存储到mNoteDiffValues中,准备用于更新数据库 + 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方法,将文本数据存储到mNoteData中 + mNoteData.setTextData(key, value); +} + +// 设置文本数据的ID +public void setTextDataId(long id) { + // 调用mNoteData的setTextDataId方法,设置文本数据的ID + mNoteData.setTextDataId(id); +} + +// 获取文本数据的ID +public long getTextDataId() { + // 返回mNoteData中存储的文本数据ID + return mNoteData.mTextDataId; +} + +// 设置通话数据的ID +public void setCallDataId(long id) { + // 调用mNoteData的setCallDataId方法,设置通话数据的ID + mNoteData.setCallDataId(id); +} + +// 设置通话数据的某个键值对数据 +public void setCallData(String key, String value) { + // 调用mNoteData的setCallData方法,将通话数据存储到mNoteData中 + mNoteData.setCallData(key, value); +} + +// 检查笔记是否已本地修改 +public boolean isLocalModified() { + // 如果mNoteDiffValues中有数据,或者mNoteData标记为已修改,则返回true + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); +} + +// 同步笔记到服务器或数据库 +public boolean syncNote(Context context, long noteId) { + // 检查传入的noteId是否有效,如果小于等于0,则抛出异常 + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + // 如果笔记没有本地修改,则不需要同步,直接返回true + if (!isLocalModified()) { + return true; + } +*//3 + + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + // 如果更新返回0,表示没有更新任何行,这通常不应该发生 + Log.e(TAG, "Update note error, should not happen"); + // 不要返回,继续执行后续代码 +} +// 清除mNoteDiffValues中的数据,因为已经尝试更新到数据库 +mNoteDiffValues.clear(); + +// 检查mNoteData是否标记为已修改,并尝试将其推送到ContentResolver +if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + // 如果mNoteData标记为已修改但推送失败,则返回false + return false; +} + +// 如果所有操作都成功,则返回true +return true; +} + +// NoteData内部类定义开始 +private class NoteData { + // 存储文本数据的ID + private long mTextDataId; + // 存储文本数据的ContentValues + private ContentValues mTextDataValues; + // 存储通话数据的ID + private long mCallDataId; + // 存储通话数据的ContentValues + private ContentValues mCallDataValues; + // 用于日志记录的TAG + private static final String TAG = "NoteData"; + + // NoteData的构造方法 + public NoteData() { + // 初始化ContentValues对象 + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + // 初始化数据ID为0,表示还没有关联的数据 + mTextDataId = 0; + mCallDataId = 0; + } + + // 检查NoteData是否已修改 + boolean isLocalModified() { + // 如果mTextDataValues或mCallDataValues中有数据,则返回true + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + // 设置文本数据的ID + void setTextDataId(long id) { + // 检查ID是否有效 + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + // 设置通话数据的ID + void setCallDataId(long id) { + // 检查ID是否有效 + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + // 设置通话数据 + void setCallData(String key, String value) { + // 将键值对存储到mCallDataValues中 + mCallDataValues.put(key, value); + // 标记笔记为已本地修改,并更新修改日期 + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + // 设置文本数据 + void setTextData(String key, String value) { + // 将键值对存储到mTextDataValues中 + mTextDataValues.put(key, value); + // 标记笔记为已本地修改,并更新修改日期 + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + // 将NoteData推送到ContentResolver + Uri pushIntoContentResolver(Context context, long noteId) { + // 检查noteId是否有效 + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + // 创建用于批量操作的内容提供者操作列表 + ArrayList operationList = new ArrayList<>(); + ContentProviderOperation.Builder builder = null; + + // 如果mTextDataValues中有数据 + if(mTextDataValues.size() > 0) { + // 添加noteId到mTextDataValues中 + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + // 如果mTextDataId为0,表示是新的文本数据 + if (mTextDataId == 0) { + // 插入新的文本数据到ContentResolver + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + // 从返回的Uri中提取新的文本数据ID + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + // 如果提取ID失败,则记录错误并清除mTextDataValues + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + // 如果mTextDataId不为0,表示是更新现有的文本数据 + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + // 清除mTextDataValues中的数据,因为已经尝试更新或插入到数据库 + mTextDataValues.clear(); + } + + // 如果mCallDataValues中有数据,处理逻辑与mTextDataValues相同 + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + // 如果操作列表不为空,则执行批量操作 + if (operationList.size() > 0) { + try { + // 执行批量操作,并返回结果 + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + // 检查结果是否有效,并返回相应的Uri或null + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + // 记录远程异常 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + // 记录操作应用异常 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + // 如果没有任何操作要执行,则返回null + return null; + } +} +// Note类定义结束 +}