Yujiaojiao #2

Merged
pqmra35s2 merged 4 commits from YUjiaojiao into main 9 months ago

@ -0,0 +1,324 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
public class Note {
// 用于存储笔记发生变化的相关数据,例如修改日期、是否本地修改等信息
private ContentValues mNoteDiffValues;
// 存储笔记具体的数据像文本数据、通话数据等相关内容通过内部类NoteData来管理
private NoteData mNoteData;
// 用于日志记录时标识当前类的标签
private static final String TAG = "Note";
/**
* Create a new note id for adding a new note to databases
* ID
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个新的ContentValues对象用于存放要插入数据库的键值对数据
ContentValues values = new ContentValues();
// 获取当前系统时间的毫秒数,作为创建时间和初始修改时间
long createdTime = System.currentTimeMillis();
// 将创建时间放入ContentValues中对应数据库表中的CREATED_DATE字段
values.put(NoteColumns.CREATED_DATE, createdTime);
// 将修改时间放入ContentValues中对应数据库表中的MODIFIED_DATE字段初始与创建时间相同
values.put(NoteColumns.MODIFIED_DATE, createdTime);
// 设置笔记的类型这里假定Notes.TYPE_NOTE是定义好的表示笔记类型的常量
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
// 设置本地修改标识为1表示有本地修改可能后续会根据实际情况改变这个值
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 设置父文件夹的ID用于在数据库中关联笔记所属的文件夹
values.put(NoteColumns.PARENT_ID, folderId);
// 通过内容解析器向指定的笔记内容URINotes.CONTENT_NOTE_URI应该是预定义的常量插入数据并获取返回的Uri
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
// 尝试从返回的Uri的路径片段中获取第二个元素通常是新插入笔记的ID并转换为long类型
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 如果在转换过程中出现数字格式异常记录错误日志并将noteId设置为0
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
if (noteId == -1) {
// 如果获取到的笔记ID为 -1抛出非法状态异常提示错误的笔记ID
throw new IllegalStateException("Wrong note id:" + noteId);
}
return noteId;
}
public Note() {
// 初始化用于存储笔记变化数据的ContentValues对象
mNoteDiffValues = new ContentValues();
// 初始化管理笔记具体数据的NoteData对象
mNoteData = new NoteData();
}
public void setNoteValue(String key, String value) {
// 将传入的键值对放入mNoteDiffValues中用于记录笔记的某个属性值变化
mNoteDiffValues.put(key, value);
// 更新本地修改标识为1表示有本地修改
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// 更新修改日期为当前系统时间的毫秒数
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
public void setTextData(String key, String value) {
// 调用内部类NoteData的setTextData方法传递键值对用于设置笔记的文本数据相关内容
mNoteData.setTextData(key, value);
}
public void setTextDataId(long id) {
// 调用内部类NoteData的setTextDataId方法传递ID值用于设置笔记文本数据的ID
mNoteData.setTextDataId(id);
}
public long getTextDataId() {
// 返回内部类NoteData中存储的文本数据的ID
return mNoteData.mTextDataId;
}
public void setCallDataId(long id) {
// 调用内部类NoteData的setCallDataId方法传递ID值用于设置笔记通话数据的ID
mNoteData.setCallDataId(id);
}
public void setCallData(String key, String value) {
// 调用内部类NoteData的setCallData方法传递键值对用于设置笔记的通话数据相关内容
mNoteData.setCallData(key, value);
}
public boolean isLocalModified() {
// 判断笔记是否有本地修改通过检查mNoteDiffValues的大小即是否有属性变化记录以及mNoteData对象是否有本地修改来综合判断
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
// 如果传入的笔记ID小于等于0抛出非法参数异常提示错误的笔记ID
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
// 如果笔记没有本地修改直接返回true表示无需同步操作
return true;
}
/**
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
* NoteColumns.LOCAL_MODIFIEDNoteColumns.MODIFIED_DATE使
*/
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
// 如果通过内容解析器更新笔记数据失败返回0表示影响的行数为0即更新失败记录错误日志但不直接返回继续往下执行
Log.e(TAG, "Update note error, should not happen");
// Do not return, fall through
}
// 清除已经同步过的笔记变化数据(可能是为了下次记录新的变化)
mNoteDiffValues.clear();
if (mNoteData.isLocalModified()
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
return false;
}
return true;
}
private class NoteData {
// 存储笔记文本数据的ID
private long mTextDataId;
// 用于存储笔记文本数据的相关属性值的ContentValues对象
private ContentValues mTextDataValues;
// 存储笔记通话数据的ID
private long mCallDataId;
// 用于存储笔记通话数据的相关属性值的ContentValues对象
private ContentValues mCallDataValues;
// 用于日志记录时标识内部类NoteData的标签
private static final String TAG = "NoteData";
public NoteData() {
// 初始化用于存储笔记文本数据属性值的ContentValues对象
mTextDataValues = new ContentValues();
// 初始化用于存储笔记通话数据属性值的ContentValues对象
mCallDataValues = new ContentValues();
// 初始文本数据的ID设置为0
mTextDataId = 0;
// 初始通话数据的ID设置为0
mCallDataId = 0;
}
boolean isLocalModified() {
// 判断笔记的文本数据或通话数据是否有本地修改通过检查对应的ContentValues对象的大小来判断即是否有属性变化记录
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
void setTextDataId(long id) {
if (id <= 0) {
// 如果传入的文本数据ID小于等于0抛出非法参数异常要求ID应该大于0
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
void setCallDataId(long id) {
if (id <= 0) {
// 如果传入的通话数据ID小于等于0抛出非法参数异常要求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);
// 更新外部类Note中的本地修改标识为1表示有本地修改
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// 更新外部类Note中的修改日期为当前系统时间的毫秒数
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
void setTextData(String key, String value) {
// 将传入的键值对放入mTextDataValues中用于记录笔记文本数据的某个属性值变化
mTextDataValues.put(key, value);
// 更新外部类Note中的本地修改标识为1表示有本地修改
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
// 更新外部类Note中的修改日期为当前系统时间的毫秒数
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
* ID0
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
if (mTextDataValues.size() > 0) {
// 将笔记ID放入文本数据的属性值中用于关联对应的笔记
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
// 如果文本数据的ID为0表示是新插入的数据设置对应的MIME_TYPE类型TextNote.CONTENT_ITEM_TYPE应该是预定义的常量
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
// 通过内容解析器向指定的笔记数据内容URINotes.CONTENT_DATA_URI应该是预定义的常量插入数据并获取返回的Uri
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
// 尝试从返回的Uri的路径片段中获取第二个元素通常是新插入文本数据的ID并转换为long类型设置为文本数据的ID
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 如果在转换过程中出现数字格式异常记录错误日志清除文本数据的属性值并返回null表示插入失败
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
// 如果文本数据的ID不为0表示是更新操作创建一个ContentProviderOperation的更新构建器
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
// 设置要更新的值为当前的文本数据属性值
builder.withValues(mTextDataValues);
// 将构建好的更新操作添加到操作列表中
operationList.add(builder.build());
}
// 清除文本数据的属性值(可能是为了下次重新记录新变化)
mTextDataValues.clear();
}
if (mCallDataValues.size() > 0) {
// 将笔记ID放入通话数据的属性值中用于关联对应的笔记
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
// 如果通话数据的ID为0表示是新插入的数据设置对应的MIME_TYPE类型CallNote.CONTENT_ITEM_TYPE应该是预定义的常量
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
// 通过内容解析器向指定的笔记数据内容URINotes.CONTENT_DATA_URI应该是预定义的常量插入数据并获取返回的Uri
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
try {
// 尝试从返回的Uri的路径片段中获取第二个元素通常是新插入通话数据的ID并转换为long类型设置为通话数据的ID
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 如果在转换过程中出现数字格式异常记录错误日志清除通话数据的属性值并返回null表示插入失败
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
mCallDataValues.clear();
return null;
}
} else {
// 如果通话数据的ID不为0表示是更新操作创建一个ContentProviderOperation的更新构建器
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);
// 如果结果数组为空、长度为0或者第一个元素为null返回null否则返回关联了笔记ID的对应笔记内容的Uri
return (results == null || results.length == 0 || results[0] == null)? null
: ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
} catch (RemoteException e) {
// 如果出现远程异常记录错误日志并返回null
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
} catch (OperationApplicationException e) {
// 如果出现操作应用异常记录错误日志并返回null
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
}
}
return null;
}
}
}

@ -0,0 +1,435 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.model;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
public class WorkingNote {
// Note for the working note
private Note mNote;
// Note Id
private long mNoteId;
// Note content
private String mContent;
// Note mode
private int mMode;
private long mAlertDate;
private long mModifiedDate;
private int mBgColorId;
private int mWidgetId;
private int mWidgetType;
private long mFolderId;
private Context mContext;
private static final String TAG = "WorkingNote";
private boolean mIsDeleted;
private NoteSettingChangedListener mNoteSettingStatusListener;
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
private static final int NOTE_WIDGET_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
// New note construct
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mNote = new Note();
mNoteId = 0;
mIsDeleted = false;
mMode = 0;
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
// Existing note construct
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();
}
private void loadNote() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData();
}
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId);
/**
* Update widget content if there exist any widget of this note
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
public boolean existInDatabase() {
return mNoteId > 0;
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
// 该方法用于设置提醒日期相关操作,根据传入的日期参数与当前保存的日期对比情况,更新日期值并通知相关监听器(如果存在)
public void setAlertDate(long date, boolean set) {
// 比较传入的日期date和当前类中保存的提醒日期mAlertDate是否不相等如果不相等说明提醒日期有变化需要进行相应更新操作
if (date!= mAlertDate) {
// 将当前类中保存的提醒日期更新为传入的新日期
mAlertDate = date;
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setNoteValue方法
// 将NoteColumns.ALERTED_DATE字段可能是表示提醒日期的字段常量的值设置为更新后的提醒日期的字符串表示形式
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
// 判断mNoteSettingStatusListener应该是实现了NoteSettingChangedListener接口的监听器对象用于监听笔记相关设置状态变化是否不为null
// 如果不为null说明有对应的监听器调用其onClockAlertChanged方法通知监听器提醒日期发生了变化传入更新后的日期和设置状态set参数具体含义取决于业务逻辑
if (mNoteSettingStatusListener!= null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
// 该方法用于标记笔记是否被删除的状态,根据传入的标记值更新内部状态,并在满足一定条件下通知相关监听器(如果存在)
public void markDeleted(boolean mark) {
// 将当前类中保存的表示笔记是否被删除的状态变量mIsDeleted更新为传入的标记值mark以此改变笔记的删除状态
mIsDeleted = mark;
// 判断以下多个条件是否同时满足:
// 1. mWidgetId不等于AppWidgetManager.INVALID_APPWIDGET_ID表示当前的小部件ID是有效的不是无效的默认值
// 2. mWidgetType不等于Notes.TYPE_WIDGET_INVALIDE表示小部件类型是有效的不是无效的类型值
// 3. mNoteSettingStatusListener不为null即存在对应的监听器
// 如果这些条件都满足说明符合通知监听器的情况调用监听器的onWidgetChanged方法通知监听器笔记的相关状态可能与小部件相关取决于业务逻辑发生了变化
if (mWidgetId!= AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType!= Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener!= null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
// 该方法用于设置笔记的背景颜色ID根据传入的ID与当前保存的ID对比情况更新ID值并通知相关监听器如果存在同时更新笔记对应的数据字段
public void setBgColorId(int id) {
// 比较传入的背景颜色IDid和当前类中保存的背景颜色IDmBgColorId是否不相等如果不相等说明背景颜色ID有变化需要进行相应更新操作
if (id!= mBgColorId) {
// 将当前类中保存的背景颜色ID更新为传入的新ID
mBgColorId = id;
// 判断mNoteSettingStatusListener应该是实现了NoteSettingChangedListener接口的监听器对象用于监听笔记相关设置状态变化是否不为null
// 如果不为null说明有对应的监听器调用其onBackgroundColorChanged方法通知监听器笔记的背景颜色ID发生了变化
if (mNoteSettingStatusListener!= null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setNoteValue方法
// 将NoteColumns.BG_COLOR_ID字段可能是表示背景颜色ID的字段常量的值设置为更新后的背景颜色ID的字符串表示形式以此在笔记数据中记录新的背景颜色ID
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
// 该方法用于设置笔记的复选框列表模式,根据传入的模式与当前保存的模式对比情况,更新模式值并通知相关监听器(如果存在),同时更新笔记对应的数据字段
public void setCheckListMode(int mode) {
// 比较传入的复选框列表模式mode和当前类中保存的模式mMode是否不相等如果不相等说明模式有变化需要进行相应更新操作
if (mMode!= mode) {
// 判断mNoteSettingStatusListener应该是实现了NoteSettingChangedListener接口的监听器对象用于监听笔记相关设置状态变化是否不为null
// 如果不为null说明有对应的监听器调用其onCheckListModeChanged方法通知监听器笔记的复选框列表模式发生了变化传入旧模式mMode和新模式mode方便监听器根据变化情况做相应处理
if (mNoteSettingStatusListener!= null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
// 将当前类中保存的复选框列表模式更新为传入的新模式
mMode = mode;
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setTextData方法
// 将TextNote.MODE字段可能是表示复选框列表模式的字段常量的值设置为更新后的模式的字符串表示形式以此在笔记数据中记录新的模式
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
// 该方法用于设置笔记关联的小部件类型,根据传入的类型与当前保存的类型对比情况,更新类型值并更新笔记对应的数据字段
public void setWidgetType(int type) {
// 比较传入的小部件类型type和当前类中保存的小部件类型mWidgetType是否不相等如果不相等说明小部件类型有变化需要进行相应更新操作
if (type!= mWidgetType) {
// 将当前类中保存的小部件类型更新为传入的新类型
mWidgetType = type;
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setNoteValue方法
// 将NoteColumns.WIDGET_TYPE字段可能是表示小部件类型的字段常量的值设置为更新后的小部件类型的字符串表示形式以此在笔记数据中记录新的小部件类型
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
// 该方法用于设置笔记关联的小部件ID根据传入的ID与当前保存的ID对比情况更新ID值并更新笔记对应的数据字段
public void setWidgetId(int id) {
// 比较传入的小部件IDid和当前类中保存的小部件IDmWidgetId是否不相等如果不相等说明小部件ID有变化需要进行相应更新操作
if (id!= mWidgetId) {
// 将当前类中保存的小部件ID更新为传入的新ID
mWidgetId = id;
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setNoteValue方法
// 将NoteColumns.WIDGET_ID字段可能是表示小部件ID的字段常量的值设置为更新后的小部件ID的字符串表示形式以此在笔记数据中记录新的小部件ID
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
// 该方法用于设置笔记的工作文本内容(可能是笔记正文等相关文本),根据传入的文本与当前保存的文本对比情况,更新文本内容并更新笔记对应的数据字段
public void setWorkingText(String text) {
// 使用TextUtils.equals方法可能是用于比较字符串是否相等的工具方法比较传入的文本text和当前类中保存的文本内容mContent是否不相等
// 如果不相等,说明文本内容有变化,需要进行相应更新操作
if (!TextUtils.equals(mContent, text)) {
// 将当前类中保存的文本内容更新为传入的新文本
mContent = text;
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setTextData方法
// 将DataColumns.CONTENT字段可能是表示笔记文本内容的字段常量的值设置为更新后的文本内容mContent以此在笔记数据中记录新的文本内容
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
// 该方法用于将笔记转换为通话笔记类型设置通话笔记相关的数据字段比如通话日期、电话号码以及所属文件夹ID可能是将普通笔记转换为通话记录相关的笔记
public void convertToCallNote(String phoneNumber, long callDate) {
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setCallData方法
// 将CallNote.CALL_DATE字段可能是表示通话日期的字段常量的值设置为通话日期的字符串表示形式以此在笔记数据中记录通话日期信息
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setCallData方法
// 将CallNote.PHONE_NUMBER字段可能是表示电话号码的字段常量的值设置为传入的电话号码phoneNumber以此在笔记数据中记录电话号码信息
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
// 通过mNote对象应该是与笔记相关的某个实例可能用于操作笔记数据调用setNoteValue方法
// 将NoteColumns.PARENT_ID字段可能是表示笔记所属文件夹ID的字段常量的值设置为Notes.ID_CALL_RECORD_FOLDER可能是代表通话记录文件夹的常量ID的字符串表示形式
// 以此将笔记的所属文件夹设置为通话记录文件夹,完成将笔记转换为通话笔记并关联到对应文件夹的操作
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
// 该方法用于判断笔记是否设置了时钟提醒根据保存的提醒日期mAlertDate的值来返回相应的布尔值结果
public boolean hasClockAlert() {
// 使用三目运算符判断mAlertDate是否大于0如果大于0表示设置了提醒日期返回true否则返回false以此表明笔记是否有设置时钟提醒
return (mAlertDate > 0? true : false);
}
// 该方法用于获取笔记的文本内容可能是笔记正文等相关文本直接返回当前类中保存的文本内容mContent变量的值
public String getContent() {
return mContent;
}
// 该方法用于获取笔记设置的提醒日期直接返回当前类中保存的提醒日期mAlertDate变量的值
public long getAlertDate() {
return mAlertDate;
}
// 该方法用于获取笔记的最后修改日期直接返回当前类中保存的最后修改日期mModifiedDate变量的值
public long getModifiedDate() {
return mModifiedDate;
}
// 该方法用于获取笔记背景颜色对应的资源ID通过NoteBgResources类可能是用于管理笔记背景资源相关的工具类的getNoteBgResource静态方法
// 传入当前类中保存的背景颜色IDmBgColorId来获取对应的背景颜色资源ID并返回
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
// 该方法用于获取笔记的背景颜色ID直接返回当前类中保存的背景颜色IDmBgColorId变量的值
public int getBgColorId() {
return mBgColorId;
}
// 该方法用于获取笔记标题背景对应的资源ID通过NoteBgResources类可能是用于管理笔记背景资源相关的工具类的getNoteTitleBgResource静态方法
// 传入当前类中保存的背景颜色IDmBgColorId来获取对应的标题背景资源ID并返回
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
// 该方法用于获取笔记的复选框列表模式直接返回当前类中保存的复选框列表模式mMode变量的值
public int getCheckListMode() {
return mMode;
}
// 该方法用于获取笔记的唯一标识ID直接返回当前类中保存的笔记IDmNoteId变量的值
public long getNoteId() {
return mNoteId;
}
// 该方法用于获取笔记所属文件夹的ID直接返回当前类中保存的文件夹IDmFolderId变量的值
public long getFolderId() {
return mFolderId;
}
// 该方法用于获取笔记关联的小部件ID直接返回当前类中保存的小部件IDmWidgetId变量的值
public int getWidgetId() {
return mWidgetId;
}
// 该方法用于获取笔记关联的小部件类型直接返回当前类中保存的小部件类型mWidgetType变量的值
public int getWidgetType() {
return mWidgetType;
}
// 定义一个内部接口NoteSettingChangedListener用于定义当笔记相关设置发生变化时需要调用的回调方法方便外部类实现该接口来监听并处理相应变化
public interface NoteSettingChangedListener {
/**
*
*/
void onBackgroundColorChanged();
/**
*
*
*/
void onClockAlertChanged(long date, boolean set);
/**
*
*/
void onWidgetChanged();
/**
*
*
*/
void onCheckListModeChanged(int oldMode, int newMode);
}

@ -0,0 +1,393 @@
/*
* 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;
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";
// Singleton stuff
private static BackupUtils sInstance;
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success
public static final int STATE_SUCCESS = 4;
private TextExport mTextExport;
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,
DataColumns.DATA1,
DataColumns.DATA2,
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 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 = "";
}
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) {
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
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);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// Print phone number
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));
}
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} 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());
}
dataCursor.close();
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
*
*/
public int exportToText() {
// 调用externalStorageAvailable方法此处未展示其实现推测是用于检查外部存储是否可用的方法如果外部存储不可用执行以下逻辑。
if (!externalStorageAvailable()) {
// 记录调试日志提示“Media was not mounted”表示外部存储介质如SD卡等未挂载然后返回STATE_SD_CARD_UNMOUONTED应该是预定义的表示SD卡未挂载的状态码常量告知导出操作因存储问题失败。
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 调用getExportToTextPrintStream方法下方有其具体实现尝试获取一个用于将内容输出到文本文件的打印流对象如果获取失败返回null执行以下逻辑。
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
// 记录错误日志提示“get print stream error”表示获取打印流出现错误然后返回STATE_SYSTEM_ERROR应该是预定义的表示系统错误的状态码常量告知导出操作因获取输出流问题失败。
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出文件夹及其包含的笔记信息
// 使用mContext应该是上下文对象用于获取内容解析器等操作与安卓系统交互的getContentResolver方法获取内容解析器并调用其query方法发起数据库查询操作查询的URI为Notes.CONTENT_NOTE_URI可能是预定义的表示笔记相关内容的通用URI
// 查询的字段列表为NOTE_PROJECTION应该是预定义的表示要获取的具体字段的数组常量筛选条件通过SQL语句指定筛选出类型为文件夹NoteColumns.TYPE字段等于Notes.TYPE_FOLDER并且父文件夹ID不是回收站文件夹IDNoteColumns.PARENT_ID字段不等于Notes.ID_TRASH_FOLER或者ID等于通话记录文件夹IDNoteColumns.ID字段等于Notes.ID_CALL_RECORD_FOLDER的记录最后两个参数为null表示无排序等额外条件。
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);
// 判断游标对象folderCursor是否获取成功不为null如果获取成功说明查询操作至少在获取游标这一步是正常执行的可以尝试遍历游标获取查询结果数据并进行处理。
if (folderCursor!= null) {
// 将游标移动到第一条数据位置,准备开始遍历游标获取数据。
if (folderCursor.moveToFirst()) {
// 使用do-while循环遍历游标中的所有数据记录每次循环处理一条记录直到游标移动到最后一条记录后无法再移动moveToNext返回false为止。
do {
// 初始化一个空字符串用于存储文件夹名称,后续会根据不同情况赋值。
String folderName = "";
// 判断当前游标指向的记录的ID通过folderCursor.getLong(NOTE_COLUMN_ID)获取NOTE_COLUMN_ID应该是预定义的表示ID所在列索引的常量是否等于通话记录文件夹IDNotes.ID_CALL_RECORD_FOLDER如果相等执行以下逻辑。
if (folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
// 通过mContext.getString方法用于获取字符串资源获取预定义的通话记录文件夹名称字符串资源R.string.call_record_folder_name并赋值给folderName变量作为通话记录文件夹的名称。
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
// 如果不是通话记录文件夹ID通过folderCursor.getString方法用于从游标获取字符串类型数据获取记录中的文件夹名称字段NOTE_COLUMN_SNIPPET应该是预定义的表示文件夹名称所在列索引的常量的值并赋值给folderName变量获取普通文件夹的名称。
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
// 判断folderName是否不为空字符串即通过TextUtils.isEmpty方法判断是否有有效的文件夹名称如果不为空执行以下逻辑。
if (!TextUtils.isEmpty(folderName)) {
// 使用ps.println方法通过前面获取的打印流对象ps将内容输出到文本文件中println表示输出一行文本并换行按照指定的格式通过getFormat方法获取FORMAT_FOLDER_NAME对应的格式字符串此处getFormat方法实现未展示输出文件夹名称将folderName变量的值按照格式要求格式化后输出到文本文件中。
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
// 通过folderCursor.getString方法获取当前记录的文件夹IDNOTE_COLUMN_ID所在列的数据并赋值给folderId变量用于后续导出该文件夹下的笔记信息。
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
// 调用exportFolderToText方法此处未展示其实现推测是用于将指定文件夹及其内容导出为文本的方法传入获取的folderId和打印流对象ps将该文件夹及其包含的笔记信息导出到文本文件中。
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
// 无论是否成功遍历完所有数据记录都要关闭游标对象folderCursor释放相关的系统资源如数据库连接等资源避免资源泄漏。
folderCursor.close();
}
// 导出根文件夹下的笔记信息
// 使用mContext的getContentResolver方法获取内容解析器并调用其query方法发起数据库查询操作查询的URI为Notes.CONTENT_NOTE_URI查询的字段列表为NOTE_PROJECTION筛选条件通过SQL语句指定筛选出类型为笔记NoteColumns.TYPE字段等于Notes.TYPE_NOTE并且父文件夹ID为0即根文件夹NoteColumns.PARENT_ID字段等于0的记录最后两个参数为null表示无排序等额外条件。
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
// 判断游标对象noteCursor是否获取成功不为null如果获取成功说明查询操作至少在获取游标这一步是正常执行的可以尝试遍历游标获取查询结果数据并进行处理。
if (noteCursor!= null) {
// 将游标移动到第一条数据位置,准备开始遍历游标获取数据。
if (noteCursor.moveToFirst()) {
// 使用do-while循环遍历游标中的所有数据记录每次循环处理一条记录直到游标移动到最后一条记录后无法再移动moveToNext返回false为止。
do {
// 使用ps.println方法按照指定的格式通过getFormat方法获取FORMAT_NOTE_DATE对应的格式字符串此处getFormat方法实现未展示输出笔记的修改日期信息
// 先通过DateFormat.format方法用于按照指定格式格式化日期将从游标中获取的笔记修改日期noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)获取NOTE_COLUMN_MODIFIED_DATE应该是预定义的表示修改日期所在列索引的常量按照预定义的日期时间格式mContext.getString(R.string.format_datetime_mdhm)获取的格式字符串进行格式化然后将格式化后的日期字符串按照FORMAT_NOTE_DATE格式要求输出到文本文件中。
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 通过noteCursor.getString方法获取当前记录的笔记IDNOTE_COLUMN_ID所在列的数据并赋值给noteId变量用于后续导出该笔记的详细信息。
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
// 调用exportNoteToText方法此处未展示其实现推测是用于将指定笔记导出为文本的方法传入获取的noteId和打印流对象ps将该笔记的详细信息导出到文本文件中。
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
// 无论是否成功遍历完所有数据记录都要关闭游标对象noteCursor释放相关的系统资源如数据库连接等资源避免资源泄漏。
noteCursor.close();
}
// 关闭打印流对象ps释放相关资源完成整个文本导出操作此时文本文件中应该已经包含了按照格式要求输出的文件夹、笔记等相关信息。
ps.close();
// 如果整个导出过程顺利完成没有出现上述提到的各种错误情况返回STATE_SUCCESS应该是预定义的表示操作成功的状态码常量告知导出操作成功。
return STATE_SUCCESS;
}
/**
* null
*/
private PrintStream getExportToTextPrintStream() {
// 调用generateFileMountedOnSDcard方法下方有其具体实现尝试创建一个挂载在SD卡上的文件用于存储导出的文本内容传入上下文对象mContext以及文件路径和文件名相关的字符串资源IDR.string.file_path和R.string.file_name_txt_format其具体内容应该是在资源文件中定义的路径和文件名格式相关字符串获取对应的文件对象如果获取失败返回null执行以下逻辑。
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
// 记录错误日志提示“create file to exported failed”表示创建用于导出的文件失败然后返回null告知调用者获取打印流失败。
Log.e(TAG, "create file to exported failed");
return null;
}
// 将创建成功的文件的名称赋值给mFileName变量可能用于后续记录等相关用途此处具体作用取决于整体业务逻辑
mFileName = file.getName();
// 通过mContext.getString方法获取文件路径相关的字符串资源R.string.file_path对应的字符串并赋值给mFileDirectory变量可能用于后续记录等相关用途此处具体作用取决于整体业务逻辑
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
// 创建一个基于前面创建的文件对象file的文件输出流对象fos用于后续创建打印流对象并向文件中写入数据。
FileOutputStream fos = new FileOutputStream(file);
// 使用创建的文件输出流对象fos创建一个打印流对象ps通过打印流对象可以方便地按照文本格式将数据输出到文件中。
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
// 如果在创建文件输出流或者打印流过程中出现文件未找到异常比如文件路径不可访问、文件不存在且无法创建等原因导致打印异常的堆栈信息用于调试排查问题然后返回null告知调用者获取打印流失败。
e.printStackTrace();
return null;
} catch (NullPointerException e) {
// 如果在创建文件输出流或者打印流过程中出现空指针异常比如传入的文件对象为null等原因导致打印异常的堆栈信息用于调试排查问题然后返回null告知调用者获取打印流失败。
e.printStackTrace();
return null;
}
// 如果成功创建了打印流对象ps返回该对象供调用者如exportToText方法使用该打印流将数据输出到文件中进行文本导出操作。
return ps;
}
/**
* SDIOnull
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 创建一个可变字符串对象sb用于拼接构建文件的完整路径。
StringBuilder sb = new StringBuilder();
// 将外部存储的根目录通过Environment.getExternalStorageDirectory方法获取通常是SD卡的根目录或者安卓设备上的外部存储根目录添加到sb中作为文件路径的起始部分。
sb.append(Environment.getExternalStorageDirectory());
// 通过context.getString方法获取文件路径相关的字符串资源filePathResId对应的字符串并添加到sb中进一步构建文件的完整路径。
sb.append(context.getString(filePathResId));
// 使用拼接好的路径字符串创建一个File对象filedir用于表示文件所在的目录如果目录不存在后续会尝试创建它。
File filedir = new File(sb.toString());
// 继续向sb中添加内容通过context.getString方法获取文件名格式相关的字符串资源fileNameFormatResId对应的字符串并使用DateFormat.format方法按照预定义的日期格式context.getString(R.string.format_date_ymd)获取的格式字符串对当前系统时间System.currentTimeMillis获取当前时间戳进行格式化将格式化后的日期字符串作为文件名的一部分添加到sb中构建完整的文件名及路径。
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// 使用拼接好的完整路径字符串创建一个File对象file用于表示最终要创建的文件如果文件不存在后续会尝试创建它。
File file = new File(sb.toString());
try {
// 判断文件所在的目录filedir是否不存在如果不存在调用mkdir方法尝试创建该目录创建成功后该目录就可用于存放文件了。
if (!filedir.exists()) {
filedir.mkdir();
}
// 判断要创建的文件file是否不存在如果不存在调用createNewFile方法尝试创建该文件创建成功后就可以用于存储数据了。
if (!file.exists()) {
file.createNewFile();
}
// 如果文件和目录创建成功返回创建好的文件对象file供调用者如getExportToTextPrintStream方法使用该文件进行后续操作如创建输出流、写入数据等
return file;
} catch (SecurityException e) {
// 如果在创建目录或者文件过程中出现安全异常比如没有足够的权限访问、创建文件或目录等原因导致打印异常的堆栈信息用于调试排查问题然后返回null告知调用者文件创建失败。
e.printStackTrace();
} catch (IOException e) {
// 如果在创建目录或者文件过程中出现IO异常比如磁盘空间不足、文件系统错误等原因导致读写文件出现问题打印异常的堆栈信息用于调试排查问题然后返回null告知调用者文件创建失败。
e.printStackTrace();
}
// 如果出现上述异常情况导致文件或目录创建失败返回null告知调用者文件创建失败。
return null;
}

@ -0,0 +1,596 @@
/*
* 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.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
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.NoteColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
// DataUtils类作为一个工具类可能提供了一系列与笔记数据操作相关的实用方法目前展示的是批量删除笔记的方法所在的类定义部分。
public class DataUtils {
// 定义一个公共静态的常量字符串TAG用于在日志记录中标识当前类方便后续在查看日志输出时能够快速准确地分辨出哪些日志信息是由这个类中的操作产生的这对于调试程序以及排查问题很有帮助。
public static final String TAG = "DataUtils";
/**
* batchDeleteNotesContentResolverIDHashSet
* ID
*
* @param resolver
* @param ids HashSetLongnullsize0
* @return truefalse
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 首先判断传入的ids集合是否为null若为null表示没有有效的要删除的笔记ID信息传入这种情况下可能是调用者没有提供具体要删除的笔记或者出现了某种错误导致参数传递异常。
// 此时记录一条调试级别的日志使用Log.d方法d表示debug用于输出一些在调试阶段有助于查看代码执行情况的信息日志内容为“the ids is null”提示当前传入的要删除笔记的ID集合为空方便开发人员排查问题。
// 然后直接返回true将这种情况视为无需执行实际删除操作也算成功的一种约定逻辑这样可以避免后续代码因为接收到空指针null作为参数而导致出现空指针异常等错误情况使程序逻辑更加健壮。
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
// 接着判断ids集合的大小是否为0即集合中没有元素表示虽然传入了集合对象但实际上并没有指定要删除的具体笔记ID也就是没有实际要删除的笔记任务。
// 同样记录一条调试日志内容为“no id is in the hashset”告知当前传入的集合中没有有效的笔记ID然后返回true把这种无实际删除任务的场景当作一种成功情况来处理符合常规的逻辑处理习惯也避免了不必要的错误提示和复杂的错误处理逻辑。
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
// 创建一个ArrayList<ContentProviderOperation>类型的列表对象operationList它用于存储一系列的ContentProviderOperation对象。
// ContentProviderOperation对象可以用来定义针对内容提供器通常对应数据库操作的具体操作在这里就是用于构建要批量执行的删除笔记的操作集合后续会通过内容解析器一次性批量执行这些操作来实现批量删除笔记的功能。
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 通过for循环遍历传入的ids集合对于集合中的每个笔记ID都要构建相应的删除操作并添加到operationList列表中这样就能为每个要删除的笔记都准备好对应的删除操作指令方便后续批量执行。
for (long id : ids) {
// 在构建删除操作之前先判断当前笔记ID是否等于系统根文件夹的ID这里使用Notes.ID_ROOT_FOLDER应该是在Notes类中预先定义好的一个表示系统根文件夹的常量值来进行比较。
// 通常情况下,系统根文件夹是整个笔记体系结构中的重要基础部分,不应该被随意删除,因为它可能关联着许多其他的笔记数据或者系统相关的重要逻辑,如果误删可能会导致程序出现严重问题。
if (id == Notes.ID_ROOT_FOLDER) {
// 如果当前笔记ID等于系统根文件夹的ID那么记录一条错误级别的日志使用Log.e方法e表示error用于输出表示出现错误情况的重要信息方便开发人员及时发现和解决问题日志内容为“Don't delete system folder root”明确提示不能删除系统根文件夹起到警示作用。
// 然后使用continue语句跳过本次循环即不针对这个系统根文件夹ID构建删除操作也不会将对应的操作添加到operationList列表中直接进入下一次循环继续处理下一个笔记ID确保不会误删重要的系统文件夹数据。
Log.e(TAG, "Don't delete system folder root");
continue;
}
// 如果当前笔记ID不是系统根文件夹的ID那么就可以构建对应的删除操作了。
// 首先创建一个ContentProviderOperation的构建器对象builder通过调用ContentProviderOperation类的newDelete方法来创建这个方法用于构建一个删除类型的操作。
// 在调用newDelete方法时需要传入一个表示要删除数据的内容URIUniform Resource Identifier统一资源标识符这里通过ContentUris.withAppendedId方法根据当前笔记IDid来生成对应的内容URI。
// ContentUris.withAppendedId方法会将给定的基础URI此处是Notes.CONTENT_NOTE_URI应该是预定义的指向笔记数据资源的通用URI与具体的笔记ID进行拼接形成一个能够准确指向要删除的那条笔记数据的唯一URI这样后续执行删除操作时就能准确找到对应的笔记记录进行删除了。
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 将构建好的删除操作通过builder.build()方法将构建器对象转换为实际可执行的ContentProviderOperation对象添加到operationList列表中不断重复这个过程就能把所有要删除的笔记对应的删除操作都添加到列表里为批量执行删除操作做好准备。
operationList.add(builder.build());
}
try {
// 当为所有要删除的笔记都构建好对应的删除操作并添加到operationList列表后就可以通过内容解析器的applyBatch方法来批量执行这些操作了。
// applyBatch方法接收两个参数第一个参数Notes.AUTHORITY应该是在Notes类中预先定义好的一个表示操作权限的常量字符串用于指定当前操作能够访问哪些内容提供器资源的权限范围第二个参数就是前面构建好的包含所有删除操作的operationList列表。
// 该方法会尝试批量执行operationList中的所有ContentProviderOperation操作并返回一个ContentProviderResult[]类型的数组,数组中的每个元素对应一个操作的执行结果,通过检查这些结果可以知道每个删除操作是否成功执行了。
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 接下来判断操作结果数组是否满足以下几种可能表示操作失败的情况:
// 1. results为null说明可能在执行批量操作过程中出现了严重问题导致无法获取到任何操作结果比如可能与内容提供器的通信出现了故障等原因。
// 2. results.length为0表示虽然获取到了结果数组但是数组中没有任何元素意味着可能没有实际执行任何有效的操作也可能是出现了异常情况导致没有结果返回。
// 3. results[0]为null说明即使有结果数组但第一个元素通常第一个元素对应第一个操作的结果如果操作是按顺序执行的话就是空的可能第一个操作就出现了问题进而影响了整个批量操作的结果有效性。
// 如果满足以上任意一种情况,就意味着批量删除操作可能没有成功执行或者没有符合条件的笔记被实际删除,需要进行相应的错误处理并向调用者返回操作失败的结果。
if (results == null || results.length == 0 || results[0] == null) {
// 记录一条调试日志内容为“delete notes failed, ids:”加上要删除笔记的ID集合的字符串表示形式通过ids.toString()方法将HashSet集合转换为字符串方便在日志中查看具体是哪些笔记的删除操作出现了问题提示当前删除笔记的操作失败了以便开发人员根据日志信息排查具体是哪些笔记没能成功删除以及可能出现问题的原因。
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
// 如果操作结果数组正常即不满足上述表示失败的情况说明批量删除操作成功执行了所有符合条件的笔记都已被成功删除此时返回true告知调用者批量删除操作已经顺利完成让调用者可以根据返回值进行后续的逻辑处理比如更新界面显示、处理相关业务逻辑等。
return true;
} catch (RemoteException e) {
// 如果在执行批量删除操作过程中出现了RemoteException异常这个异常通常表示在与远程的内容提供器进行通信时出现了问题比如网络连接异常、远程服务不可用等原因导致无法正常执行操作。
// 当捕获到这个异常时记录一条错误日志使用String.format方法将异常的字符串表示e.toString()可以获取异常的详细类名等信息和详细的错误消息e.getMessage(),通常包含更具体的错误描述内容)按照一定格式输出,方便查看和排查问题根源,例如可以根据日志信息定位是通信的哪个环节出现了故障,以便后续修复问题。
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在执行批量删除操作过程中出现了OperationApplicationException异常这个异常一般是因为构建的操作ContentProviderOperation在应用执行过程中不符合相关规则或者内容提供器内部处理操作时出现了错误等情况导致的。
// 同样当捕获到这个异常时记录一条错误日志按照指定格式使用String.format方法输出异常的字符串表示和详细错误消息用于调试和问题追踪帮助开发人员确定是操作构建环节还是内容提供器执行环节出现了问题进而采取相应的解决措施。
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
// 如果在执行批量删除操作过程中出现了上述的RemoteException或OperationApplicationException异常导致操作未能完整成功执行那么最终返回false表示批量删除操作失败让调用者知晓操作的最终结果是不成功的以便进行相应的错误处理或提示用户等操作。
return false;
}
}
// 定义一个静态公共方法,用于将单个笔记移动到指定文件夹。
// 该方法接收四个参数ContentResolver用于与安卓系统的内容提供器交互以操作数据库
// long类型的id表示要移动的笔记的唯一标识符
// long类型的srcFolderId表示笔记当前所在文件夹的标识符
// long类型的desFolderId表示笔记将要移动到的目标文件夹的标识符。
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
// 创建一个ContentValues对象它用于存储要更新到数据库中的键值对数据
// 在这里会存放与笔记相关的字段及其对应要更新的值,以此来实现对笔记相关属性的修改。
ContentValues values = new ContentValues();
// 使用ContentValues对象的put方法将NoteColumns.PARENT_ID字段通常用于表示笔记所属的文件夹在数据库中的标识符的值设置为desFolderId
// 这一步操作的目的是在数据库中更新笔记的所属文件夹信息,将笔记关联到目标文件夹,实现移动笔记到新文件夹的关键改变。
values.put(NoteColumns.PARENT_ID, desFolderId);
// 同样使用put方法将NoteColumns.ORIGIN_PARENT_ID字段一般用于记录笔记原本所属的文件夹标识符可用于追踪笔记的移动历史等用途的值设置为srcFolderId
// 这样做是为了在数据库中保存笔记原来所在文件夹的信息,方便后续查看笔记是从哪个文件夹被移动过来的,有助于数据的溯源和相关业务逻辑处理。
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
// 再次使用put方法把NoteColumns.LOCAL_MODIFIED字段可作为一个标记用于表示笔记是否在本地有过修改方便其他相关逻辑根据此标记来处理后续事宜比如数据同步等的值设置为1
// 由于移动笔记这个操作在本地改变了笔记的相关属性所以将此标记设为1告知系统该笔记发生了本地相关的修改后续可能触发相应的业务逻辑来处理这个变化。
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 通过传入的ContentResolver对象的update方法来执行实际的数据库更新操作。
// 首先使用ContentUris.withAppendedId方法根据笔记的唯一标识符id与预定义的笔记内容的基础URINotes.CONTENT_NOTE_URI它指向存储笔记相关数据的数据库位置拼接生成一个精确指向要操作的特定笔记记录的内容URI。
// 然后将前面构建好的包含要更新字段及对应值的ContentValues对象values传入后面两个参数为null意味着没有额外的筛选条件比如按照其他字段的值进一步筛选要更新的记录范围和更新参数例如更新操作的一些额外配置等
// 就是单纯基于通过id确定的特定笔记记录按照values里设定的字段值进行更新从而完成将笔记移动到目标文件夹相关属性更新的操作实现笔记移动的功能。
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
// 定义一个静态公共方法,用于批量将多个笔记移动到指定的文件夹。
// 该方法接收三个参数ContentResolver用于与安卓系统的内容提供器交互以操作数据库
// HashSet<Long>类型的ids是一个存放要移动笔记的唯一标识符的集合集合中的每个Long类型元素对应一个笔记的ID
// long类型的folderId表示目标文件夹的标识符所有在ids集合中的笔记都将被移动到这个文件夹中。
// 方法根据批量移动操作的执行结果返回一个布尔值若操作成功返回true若出现异常或部分笔记移动失败则返回false。
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 判断传入的ids集合是否为null若为空集合表示没有要移动的笔记的ID信息传入可能是调用该方法时参数传递有误或者没有指定具体要移动的笔记。
// 在这种情况下记录一条调试级别的日志使用Log.d方法用于输出一些有助于调试查看代码执行情况的信息日志内容为“the ids is null”提示当前传入的要移动笔记的ID集合为空。
// 然后直接返回true将这种没有实际要移动笔记任务的情况视为一种特殊的“成功”情况来处理避免后续代码因为接收到空指针null参数而出现异常简化了方法对边界情况的处理逻辑。
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
// 创建一个ArrayList类型的列表对象operationList它用于存储ContentProviderOperation类型的对象
// 这些对象将用于构建针对每个笔记的具体更新操作,通过将多个这样的操作对象添加到列表中,后续可以一次性批量执行这些操作,实现批量移动笔记的功能。
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 通过for循环遍历传入的ids集合针对集合中的每一个笔记ID都要构建相应的更新操作并添加到operationList列表中以此为每个要移动的笔记准备好对应的操作指令。
for (long id : ids) {
// 创建一个ContentProviderOperation的构建器对象builder通过调用ContentProviderOperation类的newUpdate方法来创建
// 传入的参数是使用ContentUris.withAppendedId方法根据当前笔记的IDid生成的内容URI这个内容URI精确指向了要更新的笔记记录在数据库中的位置
// 构建出一个用于更新操作的构建器,后续可以基于这个构建器进一步设置要更新的字段及对应的值,从而完成一个完整的更新操作构建。
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 使用构建器对象builder的withValue方法将NoteColumns.PARENT_ID字段笔记所属文件夹的标识符字段的值设置为folderId
// 这一步操作明确了要将当前笔记的所属文件夹更新为目标文件夹,是实现将笔记移动到指定文件夹的关键设置,对应了批量移动笔记的核心业务逻辑。
builder.withValue(NoteColumns.PARENT_ID, folderId);
// 使用构建器对象builder的withValue方法将NoteColumns.LOCAL_MODIFIED字段用于标记笔记在本地是否有修改的字段的值设置为1
// 因为移动笔记的操作属于对笔记在本地进行了修改,设置此标记可以让系统或其他相关逻辑知晓笔记发生了变化,便于后续进行如数据同步等相关业务逻辑的处理。
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
// 将构建好的完整的ContentProviderOperation对象通过builder.build()方法将构建器转换为可执行的操作对象添加到operationList列表中
// 不断重复这个过程为ids集合中每个笔记都构建并添加对应的更新操作对象形成一个包含所有要执行的批量更新操作的列表为后续批量执行做准备。
operationList.add(builder.build());
}
try {
// 通过ContentResolver对象的applyBatch方法批量执行operationList列表中存储的所有ContentProviderOperation更新操作。
// applyBatch方法的第一个参数Notes.AUTHORITY是一个预定义的字符串常量用于指定操作的权限范围表明这些操作是针对哪个内容提供器资源有权限进行操作的相当于一个操作许可标识。
// 第二个参数就是前面构建好的包含所有要执行更新操作的operationList列表该方法会尝试依次执行列表中的每个操作并返回一个ContentProviderResult[]类型的数组,数组中的每个元素对应一个操作的执行结果,通过检查这些结果可以判断每个操作是否成功执行。
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 判断操作结果数组是否满足以下几种可能表示操作失败的情况:
// 1. 如果results为null说明在执行批量操作过程中可能出现了严重问题例如与内容提供器的通信完全失败导致无法获取到任何操作结果这种情况可能是网络故障、服务不可用等原因引起的。
// 2. 如果results.length为0表示虽然获取到了结果数组但数组中没有任何元素意味着可能没有实际执行任何有效的操作有可能是操作构建出现问题或者执行过程中出现异常导致没有结果返回。
// 3. 如果results[0]为null说明即使有结果数组但第一个元素通常第一个元素对应第一个操作的结果如果操作是按顺序执行的话就是空的这可能意味着第一个操作就出现了问题进而影响了整个批量操作的结果有效性例如第一个操作由于某些条件不满足而无法执行成功等情况。
// 如果满足上述任意一种情况,就意味着批量移动操作可能没有成功执行或者没有符合条件的笔记被实际移动,需要进行相应的错误处理并向调用者返回操作失败的结果。
if (results == null || results.length == 0 || results[0] == null) {
// 记录一条调试日志使用Log.d方法日志内容为“delete notes failed, ids:”加上要移动笔记的ID集合的字符串表示形式通过ids.toString()方法将HashSet集合转换为字符串方便在日志中查看具体是哪些笔记的移动操作出现了问题
// 以此提示当前批量移动笔记的操作失败了,方便开发人员根据日志信息排查具体是哪些笔记没能成功移动以及可能出现问题的原因所在。
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
// 如果操作结果数组正常即不满足上述表示失败的情况说明批量移动操作成功执行了所有符合条件的笔记都已被成功移动到目标文件夹此时返回true告知调用者批量移动操作已经顺利完成以便调用者可以根据这个返回值进行后续的逻辑处理比如更新界面显示、触发相关业务逻辑等操作。
return true;
} catch (RemoteException e) {
// 如果在执行批量移动操作过程中出现了RemoteException异常这个异常通常表示在与远程的内容提供器进行通信时出现了问题
// 例如网络连接异常中断、远程服务端出现故障导致无法响应请求等原因,致使无法正常执行操作。
// 当捕获到这个异常时记录一条错误日志使用Log.e方法用于输出表示出现错误情况的重要信息方便开发人员及时发现和解决问题
// 通过String.format方法将异常的字符串表示e.toString()可以获取异常的详细类名等信息和详细的错误消息e.getMessage(),通常包含更具体的错误描述内容)按照一定格式输出,
// 方便查看和排查问题根源,例如可以根据日志信息定位是通信的哪个环节出现了故障,以便后续采取相应的修复措施来解决问题。
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 如果在执行批量移动操作过程中出现了OperationApplicationException异常这个异常一般是因为构建的操作ContentProviderOperation在应用执行过程中不符合相关规则或者内容提供器内部处理操作时出现了错误等情况导致的
// 例如操作的参数设置不符合要求、内容提供器对操作的处理逻辑中出现了业务错误等原因都可能引发此异常。
// 同样当捕获到这个异常时记录一条错误日志使用Log.e方法按照指定格式使用String.format方法输出异常的字符串表示和详细错误消息
// 用于调试和问题追踪,帮助开发人员确定是操作构建环节还是内容提供器执行环节出现了问题,进而采取针对性的解决办法来修复异常情况。
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
// 如果在执行批量移动操作过程中出现了上述的RemoteException或OperationApplicationException异常导致操作未能完整成功执行那么最终返回false
// 表示批量移动操作失败,让调用者知晓操作的最终结果是不成功的,以便调用者进行相应的错误处理,比如向用户显示操作失败的提示信息、进行数据回滚等操作。
return false;
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
/**
*
*
*
* @param resolver
* @return 0
*/
public static int getUserFolderCount(ContentResolver resolver) {
// 使用内容解析器的 `query` 方法发起数据库查询操作查询的URI为 `Notes.CONTENT_NOTE_URI`这应该是预定义的用于访问笔记相关内容的通用URI数据库中存放笔记及文件夹等各种属性信息的地址入口
// 查询的字段列表设置为 `new String[] { "COUNT(*)" }`,这里使用了数据库的聚合函数 `COUNT(*)`,表示要统计满足条件的记录行数,也就是符合特定条件的文件夹的数量,而不是获取具体的文件夹记录的各个字段值。
// 筛选条件通过SQL语句形式指定要求 `NoteColumns.TYPE` 字段(用于表示笔记或文件夹的类型)等于普通文件夹类型(通过 `Notes.TYPE_FOLDER` 常量表示,这里先将其转换为字符串形式用于条件判断,意味着只统计类型为文件夹的记录),并且 `NoteColumns.PARENT_ID` 字段表示父文件夹的ID用于判断文件夹所在位置等情况不等于回收站文件夹的ID通过 `Notes.ID_TRASH_FOLER` 常量表示,同样转换为字符串用于条件判断,即排除回收站中的文件夹),通过这样的条件筛选出符合要求的用户文件夹记录来进行数量统计。
// 最后一个参数 `null` 表示无排序、分组等其他额外的查询条件设定,就是单纯按照给定的筛选条件统计文件夹数量。
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)},
null);
int count = 0;
// 判断游标对象 `cursor` 是否获取成功(即不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,接下来可以尝试从游标中获取统计的文件夹数量数据(前提是查询结果中有符合条件的数据)。
if (cursor!= null) {
// 将游标移动到第一条数据位置(因为查询结果如果有符合条件的数据,这里只有一条统计结果数据,所以移动到第一条即可获取到数量值),准备尝试获取其中统计的文件夹数量值。
if (cursor.moveToFirst()) {
try {
// 通过 `cursor.getInt(0)` 尝试从游标当前指向的记录中获取第一个也是唯一一个因为前面查询只返回了一个统计数量值索引从0开始计数字段的值也就是满足条件的文件夹数量并将其赋值给 `count` 变量进行存储。
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
// 如果在获取游标中数据时出现索引越界异常比如可能查询结果格式不符合预期导致获取指定列数据时出错等情况记录一条错误日志日志内容为“get folder count failed:”加上异常的字符串表示e.toString()),方便排查数据获取异常的问题,提示获取文件夹数量操作失败了。
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
// 无论是否成功从游标中获取到了文件夹数量值,都要关闭游标对象 `cursor`,释放相关的系统资源(比如数据库连接等资源),避免资源泄漏,这是良好的编程习惯,确保系统资源能合理利用。
cursor.close();
}
}
}
// 将获取到的或者初始化为0的如果查询出现问题等情况文件夹数量 `count` 返回给调用者,完成方法的功能,即提供满足条件的用户文件夹数量信息。
return count;
}
/**
*
*
* @param resolver
* @param noteId ID
* @param type
* @return `true` `false`
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 使用内容解析器的 `query` 方法发起数据库查询操作,通过 `ContentUris.withAppendedId` 方法根据给定的笔记ID`noteId`生成对应的内容URI指向要查询的笔记资源查询的字段列表设置为 `null`,表示查询所有字段(此处实际可能不需要所有字段,只是一种通用的查询方式,具体可根据业务需求优化)。
// 查询的筛选条件通过SQL语句指定要求笔记的类型`NoteColumns.TYPE` 字段)等于传入的类型(`type` 参数,通过 `String.valueOf` 方法将其转换为字符串形式用于SQL语句条件判断并且父文件夹ID`NoteColumns.PARENT_ID` 字段不等于回收站文件夹ID`Notes.ID_TRASH_FOLER`,同样转换为字符串用于条件判断),这样筛选出符合特定类型且不在回收站的笔记记录。
// 最后一个参数为 `null`,表示无排序等额外条件。
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)},
null);
boolean exist = false;
// 判断游标对象 `cursor` 是否获取成功(不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,可以进一步根据游标中的数据情况判断笔记是否存在(满足条件)。
if (cursor!= null) {
// 通过 `cursor.getCount` 方法获取游标中的数据记录数量如果数量大于0表示找到了符合条件的笔记记录说明笔记在数据库中是满足可见性相关条件存在的将 `exist` 变量设置为 `true`。
if (cursor.getCount() > 0) {
exist = true;
}
// 无论是否找到了符合条件的记录,都要关闭游标对象 `cursor`,释放相关的系统资源(如数据库连接等资源),避免资源泄漏,养成良好的资源管理习惯。
cursor.close();
}
// 返回 `exist` 变量的值,该值表示根据查询判断的笔记在数据库中的可见性情况,`true` 表示可见,`false` 表示不可见。
return exist;
}
/**
* ID
*
* @param resolver ID
* @param noteId IDID
* @return IDtruefalseID
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 使用内容解析器resolver的query方法发起数据库查询操作。
// 首先通过ContentUris.withAppendedId方法根据传入的笔记IDnoteId生成对应的内容URIUniform Resource Identifier统一资源标识符这个生成的URI指向了数据库中具体要查询的笔记资源位置就像是给数据库中的笔记记录定了一个精确的地址方便后续准确查找对应的笔记。
// 后面的四个参数依次为:
// - 查询的字段列表设置为null表示查询所有字段。这里之所以设置为null可能只是简单地想确认笔记是否存在而暂时不需要获取笔记记录里具体某个或某些字段的值所以采用这种通用的查询方式不过在实际应用中如果明确知道不需要所有字段信息可以优化此处的查询字段设置以提高查询效率。
// - 筛选条件设置为null表示没有特定的筛选条件就是单纯地按照根据笔记ID生成的URI去查找对应的笔记记录没有其他诸如按照笔记类型、创建时间等额外条件来进一步限制查询范围。
// - 查询参数设置为null同样表示没有额外的参数需求比如在使用通配符查询等场景下可能会传入一些参数此处不需要所以设为null。
// - 排序规则设置为null意味着查询结果不需要按照任何特定的顺序进行排列比如按时间先后、字母顺序等排序这里只关心是否能找到对应笔记记录对结果顺序没有要求。
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
// 判断游标对象cursor是否获取成功即不为null如果游标获取成功说明查询操作至少在获取游标这一步是正常执行的意味着与数据库的交互没有出现连接失败等严重问题此时可以进一步通过游标中的数据情况来判断对应的笔记记录是否存在。
if (cursor!= null) {
// 通过cursor.getCount()方法获取游标中查询到的数据记录数量该方法返回的是一个整数表示符合查询条件此处实际就是根据笔记ID查找对应的记录的记录个数。
// 如果返回的数量大于0说明找到了至少一条符合条件的笔记记录也就意味着数据库中存在对应ID的笔记此时将exist变量设置为true表示笔记存在。
if (cursor.getCount() > 0) {
exist = true;
}
// 无论最终是否找到了对应笔记记录都要关闭游标对象cursor释放相关的系统资源比如数据库连接资源、内存资源等避免资源泄漏这是良好的编程习惯确保系统资源能够合理地被利用防止因未关闭游标而导致的潜在性能问题或资源耗尽问题。
cursor.close();
}
// 返回exist变量的值这个值就是根据前面的查询和判断过程所确定的表示指定的笔记在数据库中是否存在的最终结果true表示存在false表示不存在将这个结果返回给调用该方法的地方以便调用者根据这个结果进行后续的业务逻辑处理比如根据笔记是否存在来决定是否执行更新、删除等其他操作。
return exist;
}
/**
* IDNotes.CONTENT_DATA_URIURI
*
* @param resolver ID
* @param dataId IDID
* @return dataIdtruefalseID便
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 借助内容解析器resolver的query方法来发起针对数据数据库的查询操作。
// 首先利用ContentUris.withAppendedId方法依据传入的数据IDdataId生成相应的内容URI该URI精确指向了数据数据库中要查询的数据资源所在位置为后续准确查找对应的数据记录提供了明确的地址信息。
// 之后的四个参数设定情况与`existInNoteDatabase`方法中的类似:
// - 查询的字段列表设为null表示查询所有字段。这里可能只是单纯想确认数据是否存在暂时无需特定字段的值不过如有需要可以根据实际情况优化为指定具体的查询字段以提升查询性能。
// - 筛选条件为null意味着没有额外的筛选条件限制仅仅是按照根据数据ID生成的URI去查找对应的记录不存在像按照数据类型、关联对象等其他条件来进一步缩小查询范围的情况。
// - 查询参数设为null表明没有需要额外传入的参数比如在某些特定的模糊查询等场景下可能需要传入参数此处不需要所以置为null。
// - 排序规则设为null说明对查询结果的顺序没有要求不需要按照任何特定的方式如大小顺序、时间先后等对结果进行排列重点在于确认是否能找到对应的数据记录。
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
// 检查游标对象cursor是否成功获取不为null若游标获取成功代表查询操作在获取游标这一环节是正常的不存在与数据库连接失败之类的基础问题进而可以根据游标中的数据状况来判断对应的数据记录是否存在。
if (cursor!= null) {
// 通过cursor.getCount()方法获取游标中所包含的数据记录数量该数值反映了符合查询条件也就是根据传入的数据ID查找对应记录的记录个数。
// 当这个数量大于0时意味着找到了至少一条符合要求的数据记录也就表明数据库中确实存在对应ID的数据此时将exist变量设置为true表示数据存在。
if (cursor.getCount() > 0) {
exist = true;
}
// 无论最终有没有找到对应的数据记录都务必关闭游标对象cursor释放与之相关的系统资源像数据库连接、占用的内存等资源以此避免资源泄漏遵循良好的编程规范保障系统资源的合理运用以及系统的稳定运行。
cursor.close();
}
// 返回exist变量的值该值代表了经过前面的查询和判断流程后确定的指定数据在数据库中是否存在的最终结果true表示存在false表示不存在将这个结果反馈给调用该方法的地方便于调用者依据此结果开展后续的业务逻辑操作例如依据数据是否存在决定下一步的数据处理步骤等。
return exist;
}
/**
*
*
* @param resolver SQL
* @param name
* @return `true` `false`
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 使用内容解析器resolver的 `query` 方法发起数据库查询操作查询的URI指定为 `Notes.CONTENT_NOTE_URI`(这应该是预定义的用于访问笔记以及相关文件夹等内容的通用数据库资源位置标识,数据库中存放着这些信息的相关记录)。
// 查询的字段列表设置为 `null`,意味着查询会返回所有字段的数据,但在这里其实我们主要关心的是是否能找到符合条件的文件夹记录,并不一定需要获取所有字段内容,只是采用了一种通用的查询方式,具体可根据业务实际需求优化字段列表,以提高查询效率等。
// 查询的筛选条件通过SQL语句形式拼接指定具体如下
// - `NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER`:表示文件夹的类型(通过 `NoteColumns.TYPE` 字段标识)要等于预定义的普通文件夹类型常量(`Notes.TYPE_FOLDER`),以此筛选出是文件夹类型的记录,排除其他如笔记类型等记录,确保只在文件夹范畴内查找。
// - `AND` 是SQL语句中的逻辑与连接词用于连接多个筛选条件表示需要同时满足多个条件才能符合要求。
// - `NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER`要求文件夹的父文件夹ID通过 `NoteColumns.PARENT_ID` 字段表示不等于回收站文件夹的ID`Notes.ID_TRASH_FOLER` 常量),即排除在回收站中的文件夹,只查找不在回收站的正常文件夹。
// - `AND` 再次连接下一个条件。
// - `NoteColumns.SNIPPET + "=?"`:表示文件夹的摘要信息(这里假设是通过 `NoteColumns.SNIPPET` 字段存储文件夹名称,具体取决于业务逻辑中对该字段的定义,不过从代码逻辑来看它用于名称匹配)要等于传入的名称(`name` 参数),这里的 `?` 是占位符,后续会通过传入的 `new String[] { name }` 将实际的名称值替换进去,用于精确匹配名称相同的文件夹。
// 最后传入的 `new String[] { name }` 就是前面筛选条件中占位符 `?` 对应的实际参数值,将传入的文件夹名称替换到查询条件中进行精确匹配,最后的 `null` 参数表示无排序、分组等额外的查询条件设定,就是单纯按照上述条件查找符合要求的文件夹记录。
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 + "=?",
new String[] { name }, null);
boolean exist = false;
// 判断游标对象 `cursor` 是否获取成功(即不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,接下来可以进一步根据游标中的数据情况判断是否存在符合条件的文件夹记录(也就是判断文件夹是否可见且名称匹配)。
if (cursor!= null) {
// 通过 `cursor.getCount` 方法获取游标中的数据记录数量如果数量大于0表示找到了符合条件的文件夹记录说明在数据库中存在满足可见性相关条件且名称与传入的 `name` 相符的文件夹,此时将 `exist` 变量设置为 `true`。
if (cursor.getCount() > 0) {
exist = true;
}
// 无论最终是否找到了符合条件的文件夹记录,都要关闭游标对象 `cursor`,释放相关的系统资源(如数据库连接等资源),避免资源泄漏,养成良好的资源管理习惯,确保系统资源能合理利用且不会因为未关闭游标而出现潜在问题。
cursor.close();
}
// 返回 `exist` 变量的值,该值表示根据查询判断的具有指定名称的文件夹在数据库中的可见性情况,`true` 表示可见,`false` 表示不可见,将这个结果返回给调用该方法的地方,以便调用者根据此结果进行后续的业务逻辑处理,比如根据文件夹是否可见来决定是否展示相关界面元素等操作。
return exist;
}
/**
* `HashSet<AppWidgetAttribute>` ID `AppWidgetAttribute` `null`
*
* @param resolver
* @param folderId IDID
* @return `HashSet<AppWidgetAttribute>` `AppWidgetAttribute`ID `null`便
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
// 使用内容解析器resolver的 `query` 方法发起数据库查询操作查询的URI设定为 `Notes.CONTENT_NOTE_URI`(这通常是预定义的用于访问笔记相关内容以及与之关联的小部件等信息的通用数据库资源位置标识,存放着这些方面的数据记录)。
// 查询的字段列表指定为 `new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }`表示此次查询只希望获取小部件的ID通过 `NoteColumns.WIDGET_ID` 字段标识)和小部件的类型(通过 `NoteColumns.WIDGET_TYPE` 字段标识)这两个与小部件属性相关的字段信息,而不是获取所有字段数据,这样可以精准获取我们所需的数据,避免获取不必要的数据,提高查询效率并节省资源,同时方便后续对获取到的数据进行解析和封装操作。
// 筛选条件通过SQL语句形式指定为 `NoteColumns.PARENT_ID + "=?"`表示按照笔记的父文件夹ID进行筛选其中 `?` 是占位符,后续通过 `new String[] { String.valueOf(folderId) }` 将实际的文件夹ID`folderId` 参数,先将其转换为字符串形式)传入作为筛选条件的值,以此精确查找属于指定文件夹(`folderId` 对应的文件夹)下笔记对应的小部件属性记录,确保获取的数据是与目标文件夹相关的。
// 最后一个参数 `null` 表示无排序、分组等其他额外的查询条件设定就是单纯按照给定的文件夹ID来获取其下笔记对应的小部件属性信息。
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet<AppWidgetAttribute> set = null;
// 判断游标对象 `c` 是否获取成功(即不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,接下来可以尝试从游标中获取数据并进行相应的处理,若游标获取失败(为 `null`),则直接返回 `null`,表示没有获取到有效数据,符合没有符合条件的小部件属性记录的情况。
if (c!= null) {
// 将游标移动到第一条数据位置(因为通常查询结果中有符合条件的数据时,第一条就是我们要找的对应记录,当然如果没有符合条件的数据,这个操作会返回 `false`,后续就不会执行获取数据的操作了),准备开始尝试从游标中获取小部件属性信息并进行封装处理,如果游标中有数据,说明存在符合条件的小部件属性记录,可以进行后续操作。
if (c.moveToFirst()) {
// 创建一个 `HashSet<AppWidgetAttribute>` 集合对象 `set`,用于存储后续从游标中解析并封装好的小部件属性信息,后续会将从游标中获取到的每条记录对应的小部件属性封装为 `AppWidgetAttribute` 对象添加到这个集合中,通过集合来统一管理和返回所有符合条件的小部件属性信息。
set = new HashSet<AppWidgetAttribute>();
// 使用 `do-while` 循环遍历游标中的所有数据记录,每次循环处理一条记录,直到游标移动到最后一条记录后无法再移动(`moveToNext` 返回 `false`)为止,这样可以确保获取到所有符合条件的小部件属性信息,不会遗漏任何一条记录对应的属性内容。
do {
try {
// 创建一个 `AppWidgetAttribute` 对象 `widget`,它是自定义的用于封装小部件属性信息的类的实例,用于存放从游标中获取到的当前记录对应的小部件的相关属性,后续会将这个对象添加到 `set` 集合中。
AppWidgetAttribute widget = new AppWidgetAttribute();
// 通过 `c.getInt(0)` 方法从游标中获取当前记录的小部件ID字段索引为0因为前面查询指定了只获取两个字段小部件ID字段排在第一位的值并赋值给 `widget` 对象的 `widgetId` 属性这样就获取并封装了小部件的ID信息。
widget.widgetId = c.getInt(0);
// 通过 `c.getInt(1)` 方法从游标中获取当前记录的小部件类型字段索引为1的值并赋值给 `widget` 对象的 `widgetType` 属性,以此获取并封装了小部件的类型信息,完成了从游标数据到 `AppWidgetAttribute` 对象属性的赋值操作,将当前记录对应的小部件属性完整封装到了 `widget` 对象中。
widget.widgetType = c.getInt(1);
// 将封装好的 `widget` 对象添加到 `set` 集合中,这样集合中就会不断积累所有符合条件的小部件属性信息,每循环一次添加一个小部件的属性信息,直到遍历完所有数据记录为止。
set.add(widget);
} catch (IndexOutOfBoundsException e) {
// 如果在获取游标中数据时出现索引越界异常(比如可能查询结果格式不符合预期,导致获取指定列数据时出错等情况),记录一条错误日志,通过 `Log.e` 方法将异常的字符串表示(`e.toString()`)输出到日志中,方便排查数据获取异常的问题,以便后续对代码进行优化或者修正数据查询相关的逻辑错误。
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
// 无论是否成功遍历完所有数据记录以及是否添加了小部件属性信息到集合中,都要关闭游标对象 `c`,释放相关的系统资源(如数据库连接等资源),避免资源泄漏,遵循良好的编程习惯,确保系统资源能合理利用且不会因为未关闭游标而引发潜在的性能问题等情况。
c.close();
}
// 返回包含小部件属性信息的 `HashSet<AppWidgetAttribute>` 集合 `set`,如果没有符合条件的数据(即游标为 `null` 或者游标中没有符合条件的记录等情况),则返回 `null`,调用者可以根据返回值进行相应的后续处理,比如判断是否为空来决定是否展示小部件相关的界面元素等操作。
return set;
}
/**
* ID `Notes.CONTENT_DATA_URI`
*
*
* @param resolver SQL
* @param noteId IDID
* @return 便
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 使用内容解析器resolver的 `query` 方法发起数据库查询操作查询的URI指定为 `Notes.CONTENT_DATA_URI`(这应该是预定义的用于访问笔记相关详细数据,例如此处与通话记录电话号码等相关信息对应的数据库资源位置标识,存放着这些相关的数据记录)。
// 查询的字段列表设置为 `new String [] { CallNote.PHONE_NUMBER }`,意味着此次查询只希望获取电话号码这一个字段的信息(通过 `CallNote.PHONE_NUMBER` 字段标识电话号码,具体该字段的定义取决于业务逻辑中对通话相关数据结构的设计),而不是获取所有字段数据,这样可以精准获取我们所需的数据,避免获取不必要的数据,提高查询效率并节省资源,同时也明确了我们此次查询的核心目标就是获取电话号码。
// 查询的筛选条件通过SQL语句形式拼接指定具体如下
// - `CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?"`表示要查找的记录需同时满足笔记ID通过 `CallNote.NOTE_ID` 字段标识等于传入的笔记ID`noteId` 参数,通过 `String.valueOf` 方法将其转换为字符串形式用于SQL语句条件判断并且数据的MIME类型通过 `CallNote.MIME_TYPE` 字段标识,通常用于区分不同类型的数据内容)等于预定义的特定内容类型常量(`CallNote.CONTENT_ITEM_TYPE`通过这两个条件的组合精确筛选出与指定笔记ID关联且类型符合要求的包含电话号码信息的记录。
// - 后面的 `new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }` 就是前面筛选条件中两个占位符 `?` 对应的实际参数值按照顺序依次将笔记ID和特定内容类型的值传入作为筛选条件的值用于精确匹配符合要求的记录。
// 最后一个参数 `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);
// 首先判断游标对象 `cursor` 是否获取成功(即不为 `null`),并且游标是否能够移动到第一条数据位置(`moveToFirst` 方法返回 `true` 表示游标中有数据且成功移动到第一条记录位置,意味着找到了符合条件的记录,当然如果没有符合条件的数据,这个操作会返回 `false`),只有这两个条件都满足,才说明找到了对应笔记且存在与之关联的电话号码信息,可以尝试获取电话号码数据。
if (cursor!= null && cursor.moveToFirst()) {
try {
// 通过 `cursor.getString(0)` 方法从游标当前指向的记录中获取第一个也是唯一一个因为前面查询只指定了获取电话号码这一个字段索引从0开始计数字段的值也就是获取对应的电话号码信息并将其作为方法的返回值返回给调用者完成获取电话号码并返回的功能操作。
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
// 如果在获取游标中数据时出现索引越界异常(比如可能查询结果格式不符合预期,导致获取指定列数据时出错等情况),记录一条错误日志,通过 `Log.e` 方法输出日志信息,日志内容为 `"Get call number fails "` 加上异常的字符串表示(`e.toString()`),方便排查数据获取异常的问题,以便后续对代码进行优化或者修正数据查询相关的逻辑错误。
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
// 无论是否成功从游标中获取到了电话号码信息,都要关闭游标对象 `cursor`,释放相关的系统资源(如数据库连接等资源),避免资源泄漏,养成良好的资源管理习惯,确保系统资源能合理利用且不会因为未关闭游标而出现潜在问题。
cursor.close();
}
}
// 如果游标为 `null` 或者游标中没有符合条件的数据记录(即无法移动到第一条记录位置),说明没有获取到有效的电话号码信息,此时返回空字符串作为默认返回值,告知调用者没有查询到对应的电话号码,方便调用者根据此结果进行后续的业务逻辑处理,比如显示提示信息等操作。
return "";
}
/**
* `Notes.CONTENT_DATA_URI` ID
* IDIDID0
*
* @param resolver SQLID
* @param phoneNumber IDID线
* @param callDate IDID
* @return IDID0ID便ID
*/
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 使用内容解析器resolver的 `query` 方法发起数据库查询操作查询的URI指定为 `Notes.CONTENT_DATA_URI`(这是预定义的用于访问笔记相关详细数据,例如此处与通话记录等相关信息对应的数据库资源位置标识,存放着这些相关的数据记录)。
// 查询的字段列表设置为 `new String [] { CallNote.NOTE_ID }`表示此次查询只希望获取笔记ID这一个字段的信息通过 `CallNote.NOTE_ID` 字段标识笔记ID具体该字段的定义取决于业务逻辑中对通话相关数据结构的设计而不是获取所有字段数据这样可以精准获取我们所需的数据避免获取不必要的数据提高查询效率并节省资源同时明确了此次查询的核心目标就是获取与给定电话号码和通话日期关联的笔记ID。
// 查询的筛选条件通过SQL语句形式拼接指定具体如下
// - `CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + CallNote.PHONE_NUMBER + ",?)"`:表示要查找的记录需同时满足通话日期(通过 `CallNote.CALL_DATE` 字段标识)等于传入的通话日期(`callDate` 参数,通过 `String.valueOf` 方法将其转换为字符串形式用于SQL语句条件判断不过此处 `long` 类型直接传入也可因为数据库底层在处理数值类型时会进行相应转换并且数据的MIME类型通过 `CallNote.MIME_TYPE` 字段标识,用于区分不同类型的数据内容)等于预定义的特定内容类型常量(`CallNote.CONTENT_ITEM_TYPE`),同时还需要通过 `PHONE_NUMBERS_EQUAL` 这个自定义的函数(从代码逻辑推测应该是用于比较电话号码是否相等的函数,具体实现依赖于业务逻辑中数据库相关的函数定义)来判断通话记录中的电话号码(`CallNote.PHONE_NUMBER` 字段)是否与传入的电话号码(`phoneNumber` 参数相等通过这三个条件的组合精确筛选出与指定电话号码和通话日期匹配且类型符合要求的包含对应笔记ID信息的记录。
// - 后面的 `new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }` 就是前面筛选条件中三个占位符 `?` 对应的实际参数值,按照顺序依次将通话日期、特定内容类型和电话号码的值传入作为筛选条件的值,用于精确匹配符合要求的记录。
// 最后一个参数 `null` 表示无排序、分组等额外的查询条件设定就是单纯按照上述条件查找对应的笔记ID所在的记录。
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
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 },
null);
// 首先判断游标对象 `cursor` 是否获取成功(即不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,接下来再判断游标是否能够移动到第一条数据位置(`moveToFirst` 方法返回 `true` 表示游标中有数据且成功移动到第一条记录位置,意味着找到了符合条件的记录,若返回 `false` 则表示没有符合条件的数据),只有游标不为 `null` 且能移动到第一条记录位置时才说明找到了对应通话记录且存在与之关联的笔记ID可以尝试获取笔记ID数据。
if (cursor!= null) {
if (cursor.moveToFirst()) {
try {
// 通过 `cursor.getLong(0)` 方法从游标当前指向的记录中获取第一个也是唯一一个因为前面查询只指定了获取笔记ID这一个字段索引从0开始计数字段的值也就是获取对应的笔记ID信息并将其作为方法的返回值返回给调用者完成获取笔记ID并返回的功能操作。
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
// 如果在获取游标中数据时出现索引越界异常(比如可能查询结果格式不符合预期,导致获取指定列数据时出错等情况),记录一条错误日志,通过 `Log.e` 方法输出日志信息,日志内容为 `"Get call note id fails "` 加上异常的字符串表示(`e.toString()`),方便排查数据获取异常的问题,以便后续对代码进行优化或者修正数据查询相关的逻辑错误。
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
// 无论是否成功从游标中获取到了笔记ID信息即无论是否能移动到第一条记录位置获取到数据都要关闭游标对象 `cursor`,释放相关的系统资源(如数据库连接等资源),避免资源泄漏,养成良好的资源管理习惯,确保系统资源能合理利用且不会因为未关闭游标而出现潜在问题。
cursor.close();
}
// 如果游标为 `null` 或者游标中没有符合条件的数据记录即无法移动到第一条记录位置获取到笔记ID说明没有获取到有效的笔记ID信息此时返回0作为默认返回值告知调用者没有查询到对应的笔记ID方便调用者根据此结果进行后续的业务逻辑处理比如显示提示信息等操作。
return 0;
}
/**
* ID `Notes.CONTENT_NOTE_URI` `NoteColumns.SNIPPET`
* ID
*
* @param resolver
* @param noteId IDID
* @return `NoteColumns.SNIPPET` `IllegalArgumentException` ID便
*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 使用内容解析器resolver的 `query` 方法发起数据库查询操作。
// 查询的URI指定为 `Notes.CONTENT_NOTE_URI`,这是预定义的用于访问笔记相关内容的通用数据库资源位置标识,数据库中存放着笔记的各种属性信息,包括我们要获取的摘要信息所在的记录。
// 查询的字段列表设置为 `new String [] { NoteColumns.SNIPPET }`,意味着此次查询只希望获取笔记的摘要信息这一个字段的数据(`NoteColumns.SNIPPET` 字段用于存储摘要相关内容,具体含义取决于业务逻辑中对笔记摘要的定义和存储方式),通过这样精准指定字段的方式,可以避免获取不必要的数据,提高查询效率并节省资源,同时也能准确获取到我们所需的摘要信息内容。
// 查询的筛选条件通过SQL语句形式指定为 `NoteColumns.ID + "=?"`表示按照笔记的ID进行筛选其中 `?` 是占位符,后续通过 `new String [] { String.valueOf(noteId)}` 将实际的笔记ID`noteId` 参数先将其转换为字符串形式以便符合SQL语句中参数传递的要求传入作为筛选条件的值以此精确查找对应ID的笔记记录确保获取的是指定笔记的摘要信息。
// 最后一个参数 `null` 表示无排序、分组等其他额外的查询条件设定就是单纯按照给定的笔记ID来获取其摘要信息保持查询的简洁性和针对性。
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
// 判断游标对象 `cursor` 是否获取成功(即不为 `null`),如果游标获取成功,说明查询操作至少在获取游标这一步是正常执行的,接下来可以尝试从游标中获取数据(也就是笔记的摘要信息),前提是游标中存在符合条件的数据记录。
if (cursor!= null) {
// 初始化一个空字符串 `snippet`,用于存储后续从游标中获取到的笔记摘要信息。如果最终没能从游标中获取到有效的摘要信息(比如对应笔记存在但摘要字段为空等情况),这个空字符串也能保证方法按照预期返回一个合适的值,避免返回 `null` 等可能导致调用者出现空指针异常的情况,使程序逻辑更加健壮。
String snippet = "";
// 将游标移动到第一条数据位置(因为通常查询结果中如果有符合条件的数据,第一条就是我们要找的对应笔记记录所在位置,当然,如果没找到符合条件的数据,这个操作会返回 `false`,后续就不会执行获取数据的操作了),准备尝试获取其中的摘要信息字段值。
if (cursor.moveToFirst()) {
// 通过 `cursor.getString(0)` 尝试从游标当前指向的记录中获取第一个也是唯一一个因为前面查询只指定了获取摘要信息这一个字段索引从0开始计数字段的值也就是笔记的摘要信息并将其赋值给 `snippet` 变量进行存储,完成了从数据库查询结果中提取摘要信息到变量的操作。
snippet = cursor.getString(0);
}
// 无论是否成功从游标中获取到了摘要信息,都要关闭游标对象 `cursor`,释放相关的系统资源(比如数据库连接等资源),避免资源泄漏,这是良好的编程习惯,确保系统资源能合理利用,防止因未关闭游标而导致潜在的性能问题或资源耗尽问题。
cursor.close();
// 将获取到的(或者初始化为空字符串的)笔记摘要信息 `snippet` 返回给调用者,完成方法的功能,即提供对应笔记的摘要信息字符串,以便调用者根据此摘要信息进行后续的业务逻辑处理,比如在界面上展示摘要内容等操作。
return snippet;
}
// 如果游标对象 `cursor` 为 `null`说明根据给定的笔记ID在数据库中没有找到对应的笔记记录这种情况下抛出 `IllegalArgumentException` 异常,并在异常信息中明确提示 “Note is not found with id: ” 加上传入的笔记ID告知调用者试图获取摘要信息的笔记不存在方便调用者根据异常情况进行相应的错误处理例如给用户显示相应的提示信息等操作。
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
/**
*
* `null` `null`
*
* @param snippet 使
* @return `null` `null` `null`便使
*/
public static String getFormattedSnippet(String snippet) {
// 首先判断传入的 `snippet` 字符串是否为 `null`,如果是 `null`,说明没有有效的摘要信息传入,这种情况下不需要进行任何格式化操作,直接按照方法的约定返回 `null`,保持与输入的一致性,避免对 `null` 值进行不必要的处理导致出现空指针异常等问题。
if (snippet!= null) {
// 使用 `trim` 方法去除 `snippet` 字符串两端的空白字符(包括空格、制表符、换行符等),使字符串在展示或后续使用时更加整洁,去除不必要的空白部分,这是一种常见的字符串预处理操作,有助于提高数据的规范性和可读性。
snippet = snippet.trim();
// 使用 `indexOf` 方法查找 `snippet` 字符串中第一个换行符(`\n`)出现的位置索引,如果找到了换行符(返回值不为 `-1`),说明字符串中包含换行内容,按照格式化要求,我们只取换行符之前的内容作为最终的摘要信息。
int index = snippet.indexOf('\n');
if (index!= -1) {
// 通过 `substring` 方法截取从字符串开头到换行符位置(不包含换行符本身)的子字符串,将截取后的字符串重新赋值给 `snippet`,实现了去除换行符及之后内容的操作,使摘要信息更加简洁明了,符合格式化的预期效果。
snippet = snippet.substring(0, index);
}
}
// 返回经过上述格式化处理后的 `snippet` 字符串,如果传入的字符串为 `null`,则返回 `null`;如果经过格式化操作有了相应的调整,则返回调整后的字符串内容,以便后续根据这个格式化后的摘要信息进行展示、存储或其他相关业务逻辑处理。
return snippet;
}

@ -0,0 +1,161 @@
/*
* 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;
// GTaskStringUtils类从名字推测它主要用于处理与GTask相关的字符串操作不过目前代码中主要是定义了一系列的公共静态常量字符串这些常量可能在与GTask相关的数据处理、交互等场景中作为固定的键值使用比如用于JSON数据的解析、构建等操作中对应的字段名等情况。
public class GTaskStringUtils {
// 以下定义的一系列公共静态常量字符串很可能是在处理GTask相关的JSON数据时作为JSON对象中各个属性的键key来使用的方便在代码中统一引用这些键名避免出现拼写错误等情况同时也提高了代码的可读性和可维护性。
// 表示GTask JSON数据中"action_id"这个属性的键名可能用于标识某个操作的唯一标识符例如在任务相关操作的记录、同步等场景中通过这个键来获取对应的操作ID值。
public final static String GTASK_JSON_ACTION_ID = "action_id";
// 表示GTask JSON数据中"action_list"这个属性的键名推测其与操作对应的任务列表相关比如记录某个操作涉及的任务列表信息等可通过该键从JSON数据中提取相应的列表相关内容。
public final static String GTASK_JSON_ACTION_LIST = "action_list";
// 表示GTask JSON数据中"action_type"这个属性的键名,用于标识操作的类型,例如创建、获取全部、移动、更新等不同类型的操作,后续可以根据这个键获取对应的值来判断具体执行的是哪种操作。
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// 表示GTask JSON数据中一种具体的操作类型"create"创建操作对应的常量值当解析JSON数据中"action_type"字段的值为该常量时,就可以确定对应的操作是创建相关的任务、列表等对象的操作,用于明确操作的具体含义和逻辑分支判断。
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
// 表示GTask JSON数据中一种具体的操作类型"get_all"(获取全部操作)对应的常量值,用于判断当"action_type"字段取值为此常量时,意味着执行的操作是获取所有相关的任务、列表等信息,方便在代码中根据不同操作类型进行相应的处理逻辑编写。
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
// 表示GTask JSON数据中一种具体的操作类型"move"移动操作对应的常量值可用于识别JSON数据中代表移动任务、列表等对象的操作情况以便在代码中针对移动操作执行如更新位置等相关的业务逻辑。
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
// 表示GTask JSON数据中一种具体的操作类型"update"更新操作对应的常量值当从JSON数据中解析出"action_type"字段为此值时,知晓对应的是对任务、列表等进行更新属性等相关操作,进而执行相应的更新逻辑处理。
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// 表示GTask JSON数据中"creator_id"这个属性的键名,可能用于标识创建某个任务、列表等对象的用户的唯一标识符,通过该键可以获取到具体是谁创建了相关对象,用于权限判断、记录创建者信息等用途。
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
// 表示GTask JSON数据中"child_entity"这个属性的键名推测是用于表示某个实体比如任务列表等的子实体相关信息例如任务列表中的具体任务等可以作为子实体通过这个键可以从JSON数据中获取相应的子实体内容。
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
// 表示GTask JSON数据中"client_version"这个属性的键名,可能用于记录客户端的版本信息,比如在数据同步、兼容性判断等场景中,通过该键获取客户端版本,来决定是否进行某些特定的操作或者进行版本相关的适配处理。
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// 表示GTask JSON数据中"completed"这个属性的键名,通常用于表示某个任务是否已完成的状态标识,通过该键获取对应的布尔值(推测),可以判断任务是否已经完成,便于在任务管理等相关逻辑中进行相应的展示、筛选等操作。
public final static String GTASK_JSON_COMPLETED = "completed";
// 表示GTask JSON数据中"current_list_id"这个属性的键名可能用于标识当前正在操作的任务列表的唯一标识符比如在切换任务列表、获取当前活跃列表相关信息等场景中通过该键获取对应的列表ID。
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
// 表示GTask JSON数据中"default_list_id"这个属性的键名用于表示默认任务列表的唯一标识符在涉及创建新任务、初始化界面显示默认列表等情况时可以通过该键获取对应的默认列表ID来进行相应操作。
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
// 表示GTask JSON数据中"deleted"这个属性的键名,可能用于标识某个对象(如任务、列表等)是否已被删除的状态,通过该键获取对应的值(推测为布尔值)来判断对象是否已被删除,方便进行数据清理、界面更新等相关逻辑处理。
public final static String GTASK_JSON_DELETED = "deleted";
// 表示GTask JSON数据中"dest_list"这个属性的键名,推测在涉及移动、复制等操作时,用于表示目标任务列表相关信息,比如移动任务到另一个列表时,通过该键可以获取目标列表的具体属性等内容,以完成相应的操作逻辑。
public final static String GTASK_JSON_DEST_LIST = "dest_list";
// 表示GTask JSON数据中"dest_parent"这个属性的键名,可能在处理对象(如任务等)的层级关系变化(比如移动、调整父子关系等操作)时,用于标识目标父对象相关信息,通过该键可以获取目标父对象的相关内容,辅助进行层级关系相关的操作处理。
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
// 表示GTask JSON数据中"dest_parent_type"这个属性的键名,在涉及对象的父对象类型判断(例如任务所属父对象是哪种类型的列表等情况)时,通过该键获取对应的父对象类型值,用于进一步确定对象在层级结构中的准确位置和关系类型。
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
// 表示GTask JSON数据中"entity_delta"这个属性的键名,推测是用于表示某个实体(如任务、列表等)的变化量相关信息,比如在更新操作中,记录实体的哪些属性发生了改变以及改变的具体情况等,通过该键可以获取对应的变化量数据进行相应处理。
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
// 表示GTask JSON数据中"entity_type"这个属性的键名,用于标识某个实体(如任务、列表等)的类型,通过该键获取对应的类型值,可以区分不同类型的实体,以便在代码中根据不同实体类型执行不同的业务逻辑,比如不同类型的任务有不同的处理方式等情况。
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
// 表示GTask JSON数据中"get_deleted"这个属性的键名可能用于获取已删除对象相关的某些信息比如获取已删除任务的详细记录等情况通过该键可以从JSON数据中提取相应的已删除对象相关内容用于数据管理、历史记录查询等用途。
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
// 表示GTask JSON数据中"id"这个属性的键名作为通用的唯一标识符的键可用于各种实体任务、列表、用户等的唯一ID表示通过该键可以获取对应的对象的ID值用于准确查找、操作相应的对象这是在数据处理中非常常见且重要的一个键。
public final static String GTASK_JSON_ID = "id";
// 表示GTask JSON数据中"index"这个属性的键名,可能用于表示某个对象(如任务在列表中的顺序等)的索引位置信息,通过该键获取对应的索引值,可以进行排序、定位等相关操作,例如按照索引调整任务在列表中的显示顺序等情况。
public final static String GTASK_JSON_INDEX = "index";
// 表示GTask JSON数据中"last_modified"这个属性的键名,通常用于记录某个对象(如任务、列表等)最后一次被修改的时间戳等相关信息,通过该键获取对应的时间值,可以用于判断数据的新旧程度、进行数据同步等相关逻辑判断,比如只同步最新修改的数据等情况。
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
// 表示GTask JSON数据中"latest_sync_point"这个属性的键名,可能用于标识最近一次数据同步的时间点、版本号等相关信息,通过该键获取对应的同步点数据,可以判断数据是否需要再次同步、与服务器端同步状态等情况,用于数据同步相关的业务逻辑处理。
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
// 表示GTask JSON数据中"list_id"这个属性的键名作为任务列表的唯一标识符的键可用于准确查找、操作特定的任务列表比如获取某个列表下的所有任务等情况通过该键获取对应的列表ID值进行相关操作。
public final static String GTASK_JSON_LIST_ID = "list_id";
// 表示GTask JSON数据中"lists"这个属性的键名推测是用于表示多个任务列表相关的集合信息比如获取所有任务列表的列表信息等情况通过该键可以从JSON数据中获取整个任务列表的集合内容用于列表管理、展示等相关操作。
public final static String GTASK_JSON_LISTS = "lists";
// 表示GTask JSON数据中"name"这个属性的键名,作为通用的名称属性的键,可用于各种实体(任务、列表、用户等)的名称表示,通过该键可以获取对应的对象的名称值,用于展示、查找、筛选等相关操作,比如根据名称查找特定的任务等情况。
public final static String GTASK_JSON_NAME = "name";
// 表示GTask JSON数据中"new_id"这个属性的键名可能用于在某些操作如复制、移动并重新生成ID等情况标识新生成的对象的唯一标识符通过该键获取对应的新ID值用于后续对新对象进行操作和管理等情况。
public final static String GTASK_JSON_NEW_ID = "new_id";
// 表示GTask JSON数据中"notes"这个属性的键名,推测是用于表示与任务、列表等相关的注释、备注等信息,通过该键可以获取对应的笔记内容,用于展示详细的说明信息、用户添加的备注等情况。
public final static String GTASK_JSON_NOTES = "notes";
// 表示GTask JSON数据中"parent_id"这个属性的键名用于标识某个对象如任务等的父对象的唯一标识符通过该键获取对应的父对象ID可以确定对象在层级结构中的父子关系便于进行层级相关的操作和逻辑处理比如查找某个任务所属的列表等情况。
public final static String GTASK_JSON_PARENT_ID = "parent_id";
// 表示GTask JSON数据中"prior_sibling_id"这个属性的键名可能用于表示某个对象如任务等的前一个同级对象的唯一标识符通过该键获取对应的前一个同级对象ID可以用于排序、顺序调整等相关操作比如按照先后顺序展示任务等情况。
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
// 表示GTask JSON数据中"results"这个属性的键名推测是用于表示某个操作如查询、更新等操作的结果信息集合通过该键可以从JSON数据中获取对应的操作结果内容用于判断操作是否成功、获取操作返回的数据等情况。
public final static String GTASK_JSON_RESULTS = "results";
// 表示GTask JSON数据中"source_list"这个属性的键名,在涉及移动、复制等操作时,用于表示源任务列表相关信息,比如移动任务从哪个列表移动出来等情况,通过该键可以获取源列表的具体属性等内容,以完成相应的操作逻辑。
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
// 表示GTask JSON数据中"tasks"这个属性的键名用于表示多个任务相关的集合信息比如获取所有任务的任务信息等情况通过该键可以从JSON数据中获取整个任务集合的内容用于任务管理、展示等相关操作。
public final static String GTASK_JSON_TASKS = "tasks";
// 表示GTask JSON数据中"type"这个属性的键名,作为通用的类型属性的键,可用于区分不同类型的对象(如任务、列表等)或者操作类型等情况,通过该键获取对应的类型值,可以进行类型判断,以便在代码中根据不同类型执行不同的业务逻辑。
public final static String GTASK_JSON_TYPE = "type";
// 表示GTask JSON数据中一种具体的类型"GROUP"对应的常量值,可用于判断某个对象(如任务列表等)的类型为组类型,用于在代码中根据类型进行不同的处理逻辑,比如组类型的列表有不同的展示、操作方式等情况。
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
// 表示GTask JSON数据中一种具体的类型"TASK"对应的常量值,用于判断某个对象的类型为任务类型,以便在代码中针对任务类型的对象执行如创建、更新、删除等相关的业务逻辑。
public final static String GTASK_JSON_TYPE_TASK = "TASK";
// 表示GTask JSON数据中"user"这个属性的键名可能用于表示用户相关信息比如用户的基本资料、权限等情况通过该键可以从JSON数据中获取对应的用户信息内容用于用户管理、权限判断等相关操作。
public final static String GTASK_JSON_USER = "user";
// 定义一个常量字符串表示特定的MIUI文件夹前缀可能在处理与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";
// 定义一个常量字符串表示元数据中GTask的ID相关的头部标识推测可能在处理元数据时通过这个标识来查找、区分与GTask的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";
}

@ -0,0 +1,250 @@
/*
* 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;
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;
// 定义表示蓝色的整型常量与上面的YELLOW类似用于代表蓝色这种颜色分类在涉及颜色选择、界面元素颜色配置等场景中通过该常量来明确表示蓝色相关的操作和逻辑同样具体颜色表现由应用资源决定。
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 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 BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
// 定义一个内部静态类NoteBgResources从名字推测它主要用于管理和提供与笔记背景相关的资源信息例如背景图片资源等将这些资源相关的操作封装在内部类中使得代码结构更加清晰便于对笔记背景资源进行集中管理和操作。
public static class NoteBgResources {
// 定义一个私有静态整型数组BG_EDIT_RESOURCES数组元素类型为int用于存储与笔记编辑页面背景相关的资源ID通过R.drawable中的资源标识符来表示每个元素对应一种颜色背景的资源这里按照顺序依次存储了黄色、蓝色、白色、绿色、红色对应的编辑页面背景资源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
};
// 定义一个私有静态整型数组BG_EDIT_TITLE_RESOURCES同样用于存储与笔记编辑页面标题背景相关的资源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
};
// 定义一个公共静态方法用于根据传入的索引id获取对应的笔记编辑页面背景资源ID通过直接返回BG_EDIT_RESOURCES数组中指定索引位置的元素实现根据不同的索引值通常对应不同的颜色分类等情况获取相应的背景资源ID方便在设置笔记编辑页面背景时使用。
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
// 定义一个公共静态方法用于根据传入的索引id获取对应的笔记编辑页面标题背景资源ID与getNoteBgResource方法类似通过返回BG_EDIT_TITLE_RESOURCES数组中指定索引位置的元素实现按照索引获取相应的标题背景资源ID用于设置笔记编辑页面标题栏的背景显示效果。
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
// 定义一个公共静态方法用于获取默认的背景颜色资源ID该方法接收一个Context对象作为参数通过Context可以获取应用程序的相关配置信息例如用户在设置中对背景颜色的偏好设置等情况进而决定返回的默认背景颜色资源ID。
public static int getDefaultBgId(Context context) {
// 通过PreferenceManager.getDefaultSharedPreferences方法获取应用程序的默认共享偏好设置对象该对象用于读取和管理用户在应用设置中保存的各种偏好设置信息如是否自定义了背景颜色等
// 然后使用该对象的getBoolean方法传入NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY这应该是在NotesPreferenceActivity中定义的一个用于标识是否设置了背景颜色的偏好设置的键常量作为键以及false作为默认值表示如果没有找到对应的设置项时的默认返回值来判断用户是否已经在设置中自定义了背景颜色。
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
// 如果用户已经自定义了背景颜色即上述getBoolean方法返回true通过Math.random()方法生成一个随机数范围是0到1之间的浮点数然后乘以NoteBgResources.BG_EDIT_RESOURCES.length即背景资源数组的长度也就是颜色种类的数量这里是5种颜色并将结果转换为整数向下取整以此随机获取一个背景资源数组的索引值最后返回对应的背景资源ID实现随机选择一种颜色背景资源作为默认背景的效果给用户一种多样化的默认背景展示前提是用户开启了自定义背景颜色但未明确指定具体颜色的情况
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
// 如果用户没有自定义背景颜色即上述getBoolean方法返回false则直接返回BG_DEFAULT_COLOR常量前面定义为YELLOW对应的常量值也就是采用默认的黄色背景资源ID作为默认背景颜色遵循默认的背景颜色设定保持界面显示的一致性和初始状态。
return BG_DEFAULT_COLOR;
}
}
}
// 定义一个公共静态内部类NoteItemBgResources从类名推测它主要用于管理和提供与笔记项背景相关的各种资源信息比如不同状态下笔记项的背景图片资源等通过将这些资源相关的操作封装在内部类中使代码结构更加清晰便于对笔记项背景资源进行集中管理和统一调用。
public static class NoteItemBgResources {
// 定义一个私有静态整型数组BG_FIRST_RESOURCES数组元素类型为int用于存储与笔记项在列表中处于第一个位置时对应的背景图片资源ID通过R.drawable中的资源标识符来表示每个元素对应一种颜色主题下的首个位置背景资源
// 这里按照顺序依次存储了黄色、蓝色、白色、绿色、红色这几种颜色主题对应的笔记项处于列表首个位置的背景图片资源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
};
// 定义一个私有静态整型数组BG_NORMAL_RESOURCES同样用于存储笔记项在列表中处于普通位置既不是第一个也不是最后一个时对应的背景图片资源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
};
// 定义一个私有静态整型数组BG_LAST_RESOURCES用于存储笔记项在列表中处于最后一个位置时对应的背景图片资源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,
};
// 定义一个私有静态整型数组BG_SINGLE_RESOURCES用于存储当列表中只有一个笔记项时对应的背景图片资源ID每个元素对应一种颜色主题下的单个笔记项背景资源按照黄色、蓝色、白色、绿色、红色的顺序排列这些资源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
};
// 定义一个公共静态方法getNoteBgFirstRes用于根据传入的索引id获取笔记项在列表中处于第一个位置时对应的背景图片资源ID。
// 通过直接返回BG_FIRST_RESOURCES数组中指定索引位置的元素实现根据不同的索引值通常对应不同的颜色主题等情况获取相应的首个位置背景资源ID方便在设置笔记项列表首位背景时使用确保界面能正确显示对应颜色和位置的背景图片。
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
// 定义一个公共静态方法getNoteBgLastRes用于根据传入的索引id获取笔记项在列表中处于最后一个位置时对应的背景图片资源ID。
// 与getNoteBgFirstRes方法类似通过返回BG_LAST_RESOURCES数组中指定索引位置的元素实现按照索引获取相应的末尾位置背景资源ID用于设置笔记项列表尾位背景使列表末尾的笔记项能显示正确的背景图片效果。
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
// 定义一个公共静态方法getNoteBgSingleRes用于根据传入的索引id获取当列表中只有一个笔记项时对应的背景图片资源ID。
// 同样是返回BG_SINGLE_RESOURCES数组中指定索引位置的元素以此根据索引获取相应的单个笔记项背景资源ID用于处理列表仅有一个笔记项的特殊情况保证其背景显示符合预期的颜色和样式要求。
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
// 定义一个公共静态方法getNoteBgNormalRes用于根据传入的索引id获取笔记项在列表中处于普通位置非首非尾时对应的背景图片资源ID。
// 通过返回BG_NORMAL_RESOURCES数组中指定索引位置的元素实现按照索引获取相应的普通位置背景资源ID便于在设置列表中间笔记项背景时使用使笔记项在列表中的不同位置能呈现出相应合适的背景效果提升界面的整体美观度和专业性。
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
// 定义一个公共静态方法getFolderBgRes用于获取文件夹对应的背景图片资源ID。
// 直接返回R.drawable.list_folder说明无论在何种情况下文件夹的背景图片资源都是固定的这一个由list_folder这个资源标识符所对应的图片资源决定用于在界面上统一展示文件夹的背景样式方便识别和区分文件夹与笔记项等不同类型的界面元素。
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
// 定义一个公共静态内部类WidgetBgResources从类名可以推测它主要用于管理和提供与小部件Widget背景相关的资源信息通过将小部件背景资源相关的操作封装在这个内部类中使得代码结构更加清晰便于在应用中对小部件背景资源进行集中管理和统一调用。
public static class WidgetBgResources {
// 定义一个私有静态整型数组BG_2X_RESOURCES数组元素类型为int用于存储小部件在某种特定尺寸可能是 2 倍尺寸相关具体取决于应用中的定义和使用场景对应不同颜色主题的背景图片资源ID通过R.drawable中的资源标识符来表示每个元素对应一种颜色主题下的该尺寸背景资源
// 这里按照顺序依次存储了黄色、蓝色、白色、绿色、红色这几种颜色主题对应的小部件 2 倍尺寸背景图片资源ID方便后续根据不同的索引获取相应颜色主题下的 2 倍尺寸背景资源,用于在界面上为特定尺寸的小部件展示相应颜色和样式的背景效果。
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,
};
// 定义一个公共静态方法getWidget2xBgResource用于根据传入的索引id获取小部件在相应尺寸前面提到的 2 倍尺寸相关情况下对应的背景图片资源ID。
// 通过直接返回BG_2X_RESOURCES数组中指定索引位置的元素实现根据不同的索引值通常对应不同的颜色主题等情况获取相应的 2 倍尺寸背景资源ID方便在设置小部件该尺寸背景时使用确保小部件能正确显示对应颜色和尺寸要求的背景图片。
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
// 定义一个私有静态整型数组BG_4X_RESOURCES同样用于存储小部件在另一种特定尺寸可能是 4 倍尺寸相关依据应用具体设定对应不同颜色主题的背景图片资源ID每个元素对应一种颜色主题下的该尺寸背景资源也是按照黄色、蓝色、白色、绿色、红色的顺序排列这些资源ID。
// 这样便于根据索引获取相应颜色主题下的 4 倍尺寸背景资源,用于在界面上为特定尺寸的小部件准确展示相应颜色和样式的背景效果,满足不同尺寸小部件背景显示的需求。
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
};
// 定义一个公共静态方法getWidget4xBgResource用于根据传入的索引id获取小部件在相应尺寸前面提到的 4 倍尺寸相关情况下对应的背景图片资源ID。
// 与getWidget2xBgResource方法类似通过返回BG_4X_RESOURCES数组中指定索引位置的元素实现按照索引获取相应的 4 倍尺寸背景资源ID用于设置小部件该尺寸背景使小部件能呈现出符合要求的背景图片效果。
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
// 定义一个公共静态内部类TextAppearanceResources从类名推测它主要用于管理和提供与文本外观如字体样式、大小等文本显示相关属性相关的资源信息将这些文本外观资源相关操作封装在内部类中有助于使代码结构条理清晰方便在应用里对文本外观资源进行统一管理与调用。
public static class TextAppearanceResources {
// 定义一个私有静态整型数组TEXTAPPEARANCE_RESOURCES数组元素类型为int用于存储不同文本外观样式对应的资源ID通过R.style中的资源标识符来表示每个元素对应一种文本外观风格的资源
// 这里按照顺序依次存储了表示正常、中等、大、超大这几种不同字体外观风格对应的资源ID分别对应TextAppearanceNormal、TextAppearanceMedium、TextAppearanceLarge、TextAppearanceSuper这些样式资源具体样式的定义取决于应用中在styles.xml等相关资源文件里的设置方便后续根据不同的索引获取相应的文本外观资源用于设置文本在界面上的显示样式。
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
// 定义一个公共静态方法getTexAppearanceResource用于根据传入的索引id获取对应的文本外观资源ID。
// 不过这里有一段注释提示存在一个需要修复的问题HACKME注释部分即存储在共享偏好设置中的资源ID可能会出现大于资源数组长度的情况可能是由于数据存储或获取过程中的错误等原因导致在这种情况下为了避免出现数组越界等异常情况并且保证程序的稳定性当传入的索引id大于资源数组长度时方法会返回默认的字体大小资源ID通过调用外部类的BG_DEFAULT_FONT_SIZE常量获取前面定义为中等字体大小对应的常量值否则返回TEXTAPPEARANCE_RESOURCES数组中指定索引位置的元素以此根据索引获取相应的文本外观资源ID用于设置文本的显示样式确保文本外观资源获取的合理性和程序的健壮性。
public static int getTexAppearanceResource(int id) {
/**
* 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 (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
// 定义一个公共静态方法getResourcesSize用于获取文本外观资源数组的长度也就是当前定义的不同文本外观样式的数量通过返回TEXTAPPEARANCE_RESOURCES数组的长度方便在其他地方比如循环遍历资源、判断资源数量是否满足要求等场景了解文本外观资源的数量情况辅助进行相关的逻辑操作。
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
Loading…
Cancel
Save