注释代码

谢丛政
xxxx 2 months ago
parent 4c99398b86
commit be3e994de6

@ -0,0 +1,822 @@
/*
* 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.tool;
import android.content.Context; // 引入Context类它是Android应用的全局信息的接口
import android.database.Cursor; // 引入Cursor类用于遍历数据库查询结果
import android.os.Environment; // 引入Environment类提供有关Android设备环境的访问
import android.text.TextUtils; // 引入TextUtils类包含一些静态方法用于处理字符串
import android.text.format.DateFormat; // 引入DateFormat类用于格式化日期和时间
import android.util.Log; // 引入Log类用于记录日志信息
import net.micode.notes.R; // 引入R类它是一个包含应用资源引用的类
import net.micode.notes.data.Notes; // 引入Notes类它可能是一个包含数据库访问逻辑的类
import net.micode.notes.data.Notes.DataColumns; // 引入DataColumns接口定义了数据表列名
import net.micode.notes.data.Notes.DataConstants; // 引入DataConstants类可能包含一些数据相关的常量
import net.micode.notes.data.Notes.NoteColumns; // 引入NoteColumns接口定义了笔记表列名
import java.io.File; // 引入File类表示文件和目录路径名的抽象表示形式
import java.io.FileNotFoundException; // 引入FileNotFoundException类当文件未找到时抛出
import java.io.FileOutputStream; // 引入FileOutputStream类用于写入文件数据
import java.io.IOException; // 引入IOException类是输入输出异常的超类
import java.io.PrintStream; // 引入PrintStream类用于表示打印流的抽象类
// BackupUtils类用于实现备份工具的功能
public class BackupUtils {
private static final String TAG = "BackupUtils"; // 定义日志标签,用于日志输出
// Singleton stuff单例模式相关代码
private static BackupUtils sInstance; // 定义单例实例变量
// 获取BackupUtils类的实例使用单例模式确保全局只有一个实例
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
// 定义一些常量来表示备份或恢复的状态
public static final int STATE_SD_CARD_UNMOUONTED = 0; // SD卡未挂载
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; // 成功
private TextExport mTextExport; // 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;
}
// TextExport内部类用于实现将数据导出到文本文件的功能
private static class TextExport {
// 定义从Note表中查询数据的列名数组
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 定义Note表中各列在NOTE_PROJECTION数组中的索引
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;
// 定义从Data表中查询数据的列名数组
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 定义Data表中各列在DATA_PROJECTION数组中的索引
// 注意这里有一个错误应该是DATA_COLUMN_DATA3而不是DATA_COLUMN_CALL_DATE
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; // 错误应为DATA_COLUMN_DATA1或其他正确的索引
private static final int DATA_COLUMN_PHONE_NUMBER = 4; // 错误索引与DATA_PROJECTION不匹配
// 从资源文件中获取文本格式的数组
private final String [] TEXT_FORMAT;
// 定义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;
// Context实例变量用于访问应用的资源和类
private Context mContext;
// 导出的文本文件名
private String mFileName;
// 导出的文本文件目录
private String mFileDirectory;
// TextExport类的构造方法
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
// 根据索引获取TEXT_FORMAT数组中对应格式的方法
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// 使用内容解析器查询属于指定文件夹的笔记
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
// 检查返回的Cursor是否为null
if (notesCursor != null) {
// 移动Cursor到第一行如果Cursor不为空且至少有一行数据
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))));
// 获取当前笔记的ID
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
// 调用方法导出指定ID的笔记到文本
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext()); // 循环直到所有笔记都被处理
}
// 关闭Cursor
notesCursor.close();
}
}
/**
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
// 使用内容解析器查询指定笔记的数据
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
// 检查返回的Cursor是否为null
if (dataCursor != null) {
// 移动Cursor到第一行如果Cursor不为空且至少有一行数据
if (dataCursor.moveToFirst()) {
do {
// 获取当前数据的MIME类型
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
// 如果MIME类型表示这是一个电话笔记
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// 打印电话号码
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));
}
// 打印通话日期
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// 如果位置信息不为空,则打印位置信息
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
}
// 如果MIME类型表示这是一个普通笔记
else if (DataConstants.NOTE.equals(mimeType)) {
// 获取笔记内容
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
// 如果内容不为空,则打印内容
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext()); // 循环直到所有笔记数据都被处理
}
// 关闭Cursor
dataCursor.close();
}
// 在每个笔记之后打印一个行分隔符
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, // 实际上这里应该是只有行分隔符,后面这个字符可能是误加
// Character.LETTER_NUMBER // 这行应该是错误的,通常我们只需要行分隔符
});
} catch (IOException e) {
// 捕获并打印异常
Log.e(TAG, e.toString());
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
// 如果外部存储不可用,记录日志并返回存储卡未挂载的状态
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取用于导出文本的输出流
PrintStream ps = getExportToTextPrintStream();
// 如果无法获取输出流,记录错误日志并返回系统错误状态
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出文件夹及其笔记
// 使用ContentResolver查询数据库中的文件夹数据
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI, // 查询的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); // 无排序和选择参数
// 如果查询结果不为空
if (folderCursor != null) {
// 移动到查询结果的第一条记录
if (folderCursor.moveToFirst()) {
do {
// 打印文件夹的名称
String folderName = "";
// 如果是通话记录文件夹,使用特定的字符串
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);
}
// 如果文件夹名称不为空,则格式化并打印
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
// 获取文件夹ID并导出该文件夹下的所有笔记
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext()); // 移动到下一条记录
}
// 关闭Cursor以释放资源
folderCursor.close();
}
// 导出根文件夹中的笔记
// 使用ContentResolver查询数据库中的笔记数据
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI, // 查询的URI
NOTE_PROJECTION, // 查询的列
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID // 笔记类型且父ID为0根文件夹
+ "=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)))); // 笔记的修改日期
// 获取笔记ID并导出该笔记的详细数据
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext()); // 移动到下一条记录
}
// 关闭Cursor以释放资源
noteCursor.close();
}
// 关闭输出流以释放资源
ps.close();
// 返回成功状态
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file defined by generateExportedTextFile.
* This method is private and returns a PrintStream object or null if an error occurs.
*/
private PrintStream getExportToTextPrintStream() {
// Call a method to generate a file on the SD card.
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
// If the file generation failed (i.e., file is null), log an error and return null.
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
// Store the file name and directory for later use.
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
// Initialize a PrintStream object to null.
PrintStream ps = null;
// Try to create a FileOutputStream for the file and wrap it in a PrintStream.
try {
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
// If the file was not found, log the error and return null.
e.printStackTrace();
return null;
} catch (NullPointerException e) {
// If a NullPointerException occurs (e.g., due to a null file object), log the error and return null.
e.printStackTrace();
return null;
}
// Return the PrintStream object if no errors occurred.
return ps;
}
/**
* Generate the text file to store imported data on the SD card.
* This method is private and static, returning a File object or null if an error occurs.
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// Create a StringBuilder to build the file path.
StringBuilder sb = new StringBuilder();
// Append the external storage directory path.
sb.append(Environment.getExternalStorageDirectory());
// Append the directory path from resources.
sb.append(context.getString(filePathResId));
// Create a File object for the directory.
File filedir = new File(sb.toString());
// Append the formatted file name to the StringBuilder.
// The file name includes a date format obtained from resources.
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// Create a File object for the full file path.
File file = new File(sb.toString());
try {
// If the directory does not exist, create it.
if (!filedir.exists()) {
filedir.mkdir();
}
// If the file does not exist, create it.
if (!file.exists()) {
file.createNewFile();
}
// Return the File object if no errors occurred.
return file;
} catch (SecurityException e) {
// If there is a security exception (e.g., no permission to write to external storage), log the error.
e.printStackTrace();
} catch (IOException e) {
// If an I/O error occurs, log the error.
e.printStackTrace();
}
// Return null if an error occurred.
return null;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,269 @@
/*
* 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.
*/
// 定义一个名为GTaskSyncService的服务类继承自Service用于同步Google Tasks
package net.micode.notes.gtask.remote;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
// 声明服务类
public class GTaskSyncService extends Service {
// 定义一个常量字符串,用于标识同步操作的类型
public final static String ACTION_STRING_NAME = "sync_action_type";
// 定义同步操作的常量整型值
public final static int ACTION_START_SYNC = 0; // 开始同步
public final static int ACTION_CANCEL_SYNC = 1; // 取消同步
public final static int ACTION_INVALID = 2; // 无效操作
// 定义广播接收器名称和键值对常量
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; // 是否正在同步
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; // 同步进度消息
// 定义一个静态的异步任务变量,用于执行同步操作
private static GTaskASyncTask mSyncTask = null;
// 定义一个静态的同步进度消息变量
private static String mSyncProgress = "";
// 开始同步操作的方法
private void startSync() {
if (mSyncTask == null) { // 如果当前没有正在执行的同步任务
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
// 定义一个完成监听器,当异步任务完成时调用
public void onComplete() {
mSyncTask = null; // 清空同步任务变量
sendBroadcast(""); // 发送广播,通知同步完成
stopSelf(); // 停止服务
}
});
sendBroadcast(""); // 发送广播,可能用于通知开始同步
mSyncTask.execute(); // 执行异步任务
}
}
// 取消同步操作的方法
private void cancelSync() {
if (mSyncTask != null) { // 如果当前有正在执行的同步任务
mSyncTask.cancelSync(); // 取消同步任务
}
}
// 当服务被创建时调用
@Override
public void onCreate() {
mSyncTask = null; // 清空同步任务变量
}
// 当服务被启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras(); // 获取启动服务的Intent中的附加数据
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { // 如果附加数据不为空且包含操作类型
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { // 根据操作类型执行相应的操作
case ACTION_START_SYNC:
startSync(); // 开始同步
break;
case ACTION_CANCEL_SYNC:
cancelSync(); // 取消同步
break;
default:
break;
}
return START_STICKY; // 如果服务被杀死系统会在内存可用时重启服务并传递最后一个Intent
}
return super.onStartCommand(intent, flags, startId); // 如果没有指定操作类型,则调用父类方法
}
// 当系统内存低时调用
@Override
public void onLowMemory() {
if (mSyncTask != null) { // 如果当前有正在执行的同步任务
mSyncTask.cancelSync(); // 取消同步任务以释放内存
}
}
// 绑定服务时调用,此处不提供服务绑定支持
public IBinder onBind(Intent intent) {
return null;
}
// 发送广播通知同步状态的方法
public void sendBroadcast(String msg) {
mSyncProgress = msg; // 更新同步进度消息
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); // 创建一个新的Intent
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); // 添加是否正在同步的键值对
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); // 添加同步进度消息的键值对
sendBroadcast(intent); // 发送广播
}
// 从Activity启动同步服务的方法
public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity); // 设置活动上下文
Intent intent = new Intent(activity, GTaskSyncService.class); // 创建一个新的Intent
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
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); // 添加操作类型的键值对
context.startService(intent); // 启动服务
}
// 判断是否正在同步的方法
public static boolean isSyncing() {
return mSyncTask != null; // 返回是否有正在执行的同步任务
}
// 获取同步进度消息的方法
public static String getProgressString() {
return mSyncProgress; // 返回同步进度消息
}
}

@ -0,0 +1,622 @@
/*
* 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;
// 导入所需的Android和Java类
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
// Note类用于表示和管理笔记数据
public class 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.put(NoteColumns.CREATED_DATE, createdTime);
values.put(NoteColumns.MODIFIED_DATE, createdTime);
// 设置笔记类型为普通笔记
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
// 标记笔记为本地修改
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 设置笔记所属的文件夹ID
values.put(NoteColumns.PARENT_ID, folderId);
// 向数据库插入新笔记并获取其Uri
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
// 从Uri中提取新笔记的ID
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 如果提取ID失败则记录错误日志
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
// 如果ID为-1则抛出异常
if (noteId == -1) {
throw new IllegalStateException("Wrong note id:" + noteId);
}
// 返回新笔记的ID
return noteId;
}
// Note类的构造函数
public Note() {
// 初始化差异值存储和内容数据存储
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
// 设置笔记的差异值
public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value);
// 标记笔记为本地修改
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// 更新笔记的修改时间
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
// 设置笔记的文本数据
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
// 设置文本数据的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() {
// 如果有差异值或内容数据有本地修改则返回true
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
// 同步笔记到数据库
public boolean syncNote(Context context, long noteId) {
// 检查笔记ID是否有效
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
// 如果没有本地修改则直接返回true
if (!isLocalModified()) {
return true;
}
// 更新笔记的差异值到数据库
// 注意:即使更新失败,也会继续执行后续代码以确保数据安全性
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
// 如果更新失败,则记录错误日志
Log.e(TAG, "Update note error, should not happen");
// 不返回,继续执行
}
// 清空差异值存储,因为已经同步到数据库
mNoteDiffValues.clear();
// 如果内容数据有本地修改,则尝试将其同步到数据库
if (mNoteData.isLocalModified()
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
// 如果同步失败则返回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;
// 用于日志记录的标签
private static final String TAG = "NoteData";
// 构造函数,初始化成员变量
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
// 检查本地数据是否被修改
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
// 设置文本数据的ID
void setTextDataId(long id) {
if(id <= 0) {
// 如果ID小于等于0抛出异常
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
// 设置通话数据的ID
void setCallDataId(long id) {
if (id <= 0) {
// 如果ID小于等于0抛出异常
throw new IllegalArgumentException("Call data id should larger than 0");
}
mCallDataId = id;
}
// 设置通话数据
void setCallData(String key, String value) {
mCallDataValues.put(key, value);
// 下面的两行代码似乎有误因为mNoteDiffValues在类中未定义
// mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
// 设置文本数据
void setTextData(String key, String value) {
mTextDataValues.put(key, value);
// 同样下面的两行代码因为mNoteDiffValues未定义而存在问题
// mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
// 将数据推送到内容提供者
Uri pushIntoContentResolver(Context context, long noteId) {
// 检查noteId的有效性
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
// 用于存储内容提供者操作的列表
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
// 如果文本数据有变化
if(mTextDataValues.size() > 0) {
// 添加noteId到文本数据
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
// 如果是新数据,插入新记录
if (mTextDataId == 0) {
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
// 从返回的URI中提取新插入的ID
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 如果提取ID失败记录错误并清空数据
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
// 如果是已有数据,更新记录
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}
// 清空已处理的数据
mTextDataValues.clear();
}
// 如果通话数据有变化
if(mCallDataValues.size() > 0) {
// 添加noteId到通话数据
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 {
// 从返回的URI中提取新插入的ID
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 如果提取ID失败记录错误并清空数据
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
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;
}
}

@ -0,0 +1,812 @@
/*
* 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;
// 导入所需的Android和项目内部的类
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
// 定义一个用于处理工作中的笔记的类
public class WorkingNote {
// 笔记对象
private Note mNote;
// 笔记ID
private long mNoteId;
// 笔记内容
private String mContent;
// 笔记模式
private int mMode;
// 提醒日期
private long mAlertDate;
// 修改日期
private long mModifiedDate;
// 背景颜色ID
private int mBgColorId;
// 小部件ID
private int mWidgetId;
// 小部件类型
private int mWidgetType;
// 文件夹ID
private long mFolderId;
// 上下文
private Context mContext;
// 日志标签
private static final String TAG = "WorkingNote";
// 标记笔记是否已被删除
private boolean mIsDeleted;
// 笔记设置变化监听器
private NoteSettingChangedListener mNoteSettingStatusListener;
// 用于从数据表中查询笔记内容的列
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 用于从笔记表中查询笔记元数据的列
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
// 定义从DATA_PROJECTION数组中获取数据的列索引
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; // 注意这里可能有误因为DATA_PROJECTION中没有直接对应MODE的列
// 定义从NOTE_PROJECTION数组中获取数据的列索引
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;
// 构造一个新笔记对象
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
mModifiedDate = System.currentTimeMillis(); // 设置当前时间为修改日期
mFolderId = folderId;
mNote = new Note(); // 初始化Note对象
mNoteId = 0; // 新笔记ID为0表示未保存
mIsDeleted = false;
mMode = 0; // 初始化模式
mWidgetType = Notes.TYPE_WIDGET_INVALIDE; // 初始化小部件类型为无效
}
// 构造一个已存在的笔记对象
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote(); // 加载笔记数据
}
// 从数据库中加载笔记元数据
private void loadNote() {
// 使用内容解析器查询笔记元数据
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
// 从cursor中读取数据并赋值给成员变量
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close(); // 关闭cursor
} else {
// 如果查询失败,记录错误日志并抛出异常
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData(); // 加载笔记内容数据
}
// 从数据库中加载笔记内容数据
private void loadNoteData() {
// 使用内容解析器查询笔记内容数据
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
// 根据MIME类型处理不同的笔记数据
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN); // 注意这里可能有问题因为前面提到DATA_PROJECTION中没有直接对应MODE的列
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close(); // 关闭cursor
} else {
// 如果查询失败,记录错误日志并抛出异常
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
// 创建一个新的空笔记对象
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId); // 调用私有构造函数创建新对象
note.setBgColorId(defaultBgColorId); // 设置背景颜色ID
note.setWidgetId(widgetId); // 设置小部件ID
note.setWidgetType(widgetType); // 设置小部件类型
return note; // 返回新创建的笔记对象
}
// 根据ID加载一个已存在的笔记对象
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0); // 调用私有构造函数加载已存在的笔记对象文件夹ID默认为0
}
}
// 定义一个同步方法,用于保存笔记
public synchronized boolean saveNote() {
// 检查笔记是否值得保存
if (isWorthSaving()) {
// 如果笔记在数据库中不存在
if (!existInDatabase()) {
// 为笔记分配一个新的ID
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
// 如果分配ID失败记录错误日志并返回false
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
// 同步笔记数据到数据库
mNote.syncNote(mContext, mNoteId);
// 如果存在与笔记关联的widget则更新widget内容
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
// 返回true表示保存成功
return true;
} else {
// 如果笔记不值得保存返回false
return false;
}
}
// 判断笔记是否在数据库中已存在
public boolean existInDatabase() {
return mNoteId > 0;
}
// 判断笔记是否值得保存
private boolean isWorthSaving() {
// 如果笔记被标记为删除,或者内容为空且未存在于数据库中,或者存在于数据库中但未本地修改,则不值得保存
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
// 设置笔记设置状态改变的监听器
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
// 设置笔记的提醒日期
public void setAlertDate(long date, boolean set) {
// 如果新的日期与当前日期不同,则更新日期
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
// 如果设置了监听器,通知监听器提醒日期已改变
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
// 标记笔记为已删除或未删除
public void markDeleted(boolean mark) {
mIsDeleted = mark;
// 如果存在与笔记关联的widget并且设置了监听器则通知监听器widget已改变
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
// 设置笔记的背景颜色ID
public void setBgColorId(int id) {
// 如果新的颜色ID与当前不同则更新颜色ID
if (id != mBgColorId) {
mBgColorId = id;
// 如果设置了监听器,通知监听器背景颜色已改变
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
// 设置笔记的清单模式
public void setCheckListMode(int mode) {
// 如果新的模式与当前模式不同,则更新模式
if (mMode != mode) {
// 如果设置了监听器,通知监听器清单模式已改变
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
// 设置笔记的widget类型
public void setWidgetType(int type) {
// 如果新的类型与当前类型不同,则更新类型
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
// 设置笔记的widget ID
public void setWidgetId(int id) {
// 如果新的ID与当前ID不同则更新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;
}
// 获取笔记的widget ID
public int getWidgetId() {
return mWidgetId;
}
// 获取笔记的widget类型
public int getWidgetType() {
return mWidgetType;
}
// 定义笔记设置状态改变的监听器接口
public interface NoteSettingChangedListener {
// 当笔记的背景颜色改变时调用
void onBackgroundColorChanged();
// 当用户设置提醒时调用
void onClockAlertChanged(long date, boolean set);
// 当用户通过widget创建笔记时调用
void onWidgetChanged();
// 当用户切换清单模式和普通模式时调用
void onCheckListModeChanged(int oldMode, int newMode);
}
Loading…
Cancel
Save