Compare commits

...

3 Commits

@ -0,0 +1,416 @@
/*
* 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;
/**
* BackupUtils
*/
public class BackupUtils {
/**
* Logcat
*/
private static final String TAG = "BackupUtils";
// 单例模式相关变量
private static BackupUtils sInstance;
/**
* BackupUtils
* @param context ActivityService
* @return BackupUtils
*/
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
*
*/
// SD卡未挂载
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// 备份文件不存在
public static final int STATE_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;
// TextExport对象用于将数据导出到文本文件
private TextExport mTextExport;
/**
*
* @param context
*/
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
/**
*
* @return truefalse
*/
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
*
* @return
*/
public int exportToText() {
return mTextExport.exportToText();
}
/**
*
* @return
*/
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
/**
*
* @return
*/
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
/**
* TextExport
*/
private static class TextExport {
// NOTE_PROJECTION数组定义了查询笔记数据时需要的列
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// NOTE_COLUMN_常量定义了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_PROJECTION数组定义了查询笔记数据时需要的列
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// DATA_COLUMN_常量定义了DATA_PROJECTION数组中各列的索引
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;
// TEXT_FORMAT数组包含了导出文本的格式字符串
private final String[] TEXT_FORMAT;
// 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;
// 成员变量,用于存储上下文对象、导出文件名和目录
private Context mContext;
private String mFileName;
private String mFileDirectory;
/**
* TextExport
* @param context ActivityService
*/
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
/**
* id
* @param id
* @return
*/
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* ID
* @param folderId ID
* @param ps PrintStream
*/
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);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// 打印笔记的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于该笔记的数据
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
}
/**
* IDPrintStream
* @param noteId ID
* @param ps PrintStream
*/
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 {
// 获取数据的MIME类型
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
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));
}
} 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();
}
// 打印笔记之间的分隔线
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
*
* @return
*/
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取PrintStream对象用于写入文本数据
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出文件夹及其笔记
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER + ") 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));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// 导出根文件夹中的笔记
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于该笔记的数据
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* PrintStream
* @return PrintStreamnull
*/
private PrintStream getExportToTextPrintStream() {
// 在SD卡上生成用于存储导出数据的文本文件
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
// 创建文件输出流并将其封装为PrintStream对象
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
/**
* SD
* @param context ActivityService
* @param filePathResId ID
* @param fileNameFormatResId ID
* @return null
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 定义一个StringBuilder对象用于构建文件路径
StringBuilder sb = new StringBuilder();
// 追加SD卡的根目录到路径
sb.append(Environment.getExternalStorageDirectory());
// 追加资源文件中定义的文件夹路径到SD卡根目录
sb.append(context.getString(filePathResId));
// 根据路径创建一个文件夹对象
File filedir = new File(sb.toString());
// 使用资源文件中定义的文件名格式和当前日期时间创建文件名
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// 根据完整的路径和文件名创建一个文件对象
File file = new File(sb.toString());
try {
// 如果文件夹不存在,则创建文件夹
if (!filedir.exists()) {
filedir.mkdir();
}
// 如果文件不存在,则创建文件
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
// 如果没有权限创建文件,则打印异常堆栈跟踪
e.printStackTrace();
} catch (IOException e) {
// 如果发生IO异常则打印异常堆栈跟踪
e.printStackTrace();
}
// 如果文件创建失败则返回null
return null;
}
}

@ -0,0 +1,359 @@
/*
* 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;
public class DataUtils {
public static final String TAG = "DataUtils";
/**
*
* @param resolver ContentResolver
* @param ids ID
* @return truefalse
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 如果传入的ID集合为空或者大小为0则不执行删除操作
if (ids == null || ids.size() == 0) {
Log.d(TAG, "the ids is null or empty");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
// 遍历ID集合为每个ID创建一个删除操作
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try {
// 执行批量操作
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
// 如果操作结果为空或者第一个结果为空,则删除失败
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
*
* @param resolver ContentResolver
* @param id ID
* @param srcFolderId ID
* @param desFolderId ID
*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
/**
*
* @param resolver ContentResolver
* @param ids ID
* @param folderId ID
* @return truefalse
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, long folderId) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "move notes to folder failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
*
* @param resolver ContentResolver
* @return
*/
public static int getUserFolderCount(ContentResolver resolver) {
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_FOLDER) },
null);
int count = 0;
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
cursor.close();
}
}
}
return count;
}
/**
*
* @param resolver ContentResolver
* @param noteId ID
* @param type
* @return truefalse
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
/**
*
* @param resolver ContentResolver
* @param noteId ID
* @return truefalse
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
/**
*
* @param resolver ContentResolver
* @param name
* @return truefalse
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
/**
*
* @param resolver ContentResolver
* @param folderId ID
* @return HashSetnull
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
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;
if (c != null) {
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
c.close();
}
return set;
}
/**
* ID
* @param resolver ContentResolver
* @param noteId ID
* @return
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
cursor.close();
}
}
return "";
}
/**
* ID
* @param resolver ContentResolver
* @param phoneNumber
* @param callDate
* @return ID0
*/
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
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);
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
cursor.close();
}
return 0;
}
/**
* ID
* @param resolver ContentResolver
* @param noteId ID
* @return IllegalArgumentException
*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0);
}
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
/**
*
* @param snippet
* @return
*/
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');
if (index != -1) {
snippet = snippet.substring(0, index);
}
}
return snippet;
}
}

@ -0,0 +1,113 @@
/*
* 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;
public class GTaskStringUtils {
// GTask JSON常量
public final static String GTASK_JSON_ACTION_ID = "action_id";
public final static String GTASK_JSON_ACTION_LIST = "action_list";
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
public final static String GTASK_JSON_COMPLETED = "completed";
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
public final static String GTASK_JSON_DELETED = "deleted";
public final static String GTASK_JSON_DEST_LIST = "dest_list";
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
public final static String GTASK_JSON_ID = "id";
public final static String GTASK_JSON_INDEX = "index";
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
public final static String GTASK_JSON_LIST_ID = "list_id";
public final static String GTASK_JSON_LISTS = "lists";
public final static String GTASK_JSON_NAME = "name";
public final static String GTASK_JSON_NEW_ID = "new_id";
public final static String GTASK_JSON_NOTES = "notes";
public final static String GTASK_JSON_PARENT_ID = "parent_id";
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
public final static String GTASK_JSON_RESULTS = "results";
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
public final static String GTASK_JSON_TASKS = "tasks";
public final static String GTASK_JSON_TYPE = "type";
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
public final static String GTASK_JSON_TYPE_TASK = "TASK";
public final static String GTASK_JSON_USER = "user";
// 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";
// 元数据头部常量
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";
}

@ -1,207 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*:Android,ID
CursorAdapter*/
// 导入必要的包和类
package net.micode.notes.ui; // 包名
import android.content.Context; // 上下文用于与Android系统交互
import android.database.Cursor; // 数据库游标,用于遍历数据库查询结果
import android.util.Log; // 日志工具类,用于输出日志信息
import android.view.View; // 视图基类
import android.view.ViewGroup; // 视图组基类
import android.widget.CursorAdapter; // 适配器基类,用于将数据库查询结果绑定到视图上
import net.micode.notes.data.Notes; // 假设这是包含笔记相关常量和方法的类
import java.util.Collection; // 集合接口
import java.util.HashMap; // 哈希映射接口,用于存储键值对
import java.util.HashSet; // 哈希集合类,不允许重复元素
import java.util.Iterator; // 迭代器接口,用于遍历集合
// NotesListAdapter类继承自CursorAdapter用于将笔记数据绑定到视图
public class NotesListAdapter extends CursorAdapter {
// 定义日志标签
private static final String TAG = "NotesListAdapter";
// 上下文对象用于与Android系统交互
private Context mContext;
// 存储选中项的哈希映射键为位置索引值为选中状态true表示选中
private HashMap<Integer, Boolean> mSelectedIndex;
// 笔记总数
private int mNotesCount;
// 标记是否处于选择模式
private boolean mChoiceMode;
// 定义一个静态内部类,用于存储小部件的属性
public static class AppWidgetAttribute {
public int widgetId; // 小部件ID
public int widgetType; // 小部件类型
}
// 构造函数,初始化成员变量
public NotesListAdapter(Context context) {
super(context, null); // 调用父类构造函数
mSelectedIndex = new HashMap<>(); // 初始化选中项的哈希映射
mContext = context; // 初始化上下文对象
mNotesCount = 0; // 初始化笔记总数为0
}
// 重写newView方法用于创建新的视图对象
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context); // 创建一个NotesListItem视图对象
}
// 重写bindView方法用于绑定视图数据
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) { // 如果视图是NotesListItem的实例
NoteItemData itemData = new NoteItemData(context, cursor); // 创建一个NoteItemData对象用于存储笔记数据
((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); // 绑定数据到视图
}
}
// 设置指定位置的项为选中或未选中状态
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked); // 更新选中项的哈希映射
notifyDataSetChanged(); // 通知数据集已更改,刷新视图
}
// 获取是否处于选择模式
public boolean isInChoiceMode() {
return mChoiceMode;
}
// 设置是否处于选择模式
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear(); // 清空选中项的哈希映射
mChoiceMode = mode; // 更新选择模式状态
}
// 全选或全不选
public void selectAll(boolean checked) {
Cursor cursor = getCursor(); // 获取游标
for (int i = 0; i < getCount(); i++) { // 遍历所有项
if (cursor.moveToPosition(i)) { // 移动到指定位置
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { // 如果是笔记类型
setCheckedItem(i, checked); // 设置选中状态
}
}
}
}
// 获取选中项的ID集合
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<>(); // 创建一个空的哈希集合
for (Integer position : mSelectedIndex.keySet()) { // 遍历选中项的哈希映射的键集
if (mSelectedIndex.get(position) == true) { // 如果选中状态为true
Long id = getItemId(position); // 获取项的ID
if (id == Notes.ID_ROOT_FOLDER) { // 如果是根文件夹ID则忽略
Log.d(TAG, "Wrong item id, should not happen"); // 输出日志信息
} else {
itemSet.add(id); // 添加ID到集合
}
}
}
return itemSet; // 返回选中项的ID集合
}
// 获取选中项的小部件属性集合
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<>(); // 创建一个空的哈希集合
for (Integer position : mSelectedIndex.keySet()) { // 遍历选中项的哈希映射的键集
if (mSelectedIndex.get(position) == true) { // 如果选中状态为true
Cursor c = (Cursor) getItem(position); // 获取项的游标
if (c != null) { // 如果游标不为空
AppWidgetAttribute widget = new AppWidgetAttribute(); // 创建一个AppWidgetAttribute对象
NoteItemData item = new NoteItemData(mContext, c); // 创建一个NoteItemData对象用于存储笔记数据
widget.widgetId = item.getWidgetId(); // 获取小部件ID
widget.widgetType = item.getWidgetType(); // 获取小部件类型
itemSet.add(widget); // 添加小部件属性到集合
// 注意:这里不应该关闭游标,因为适配器会管理游标的生命周期
} else {
Log.e(TAG, "Invalid cursor"); // 输出错误日志信息
return null; // 返回null表示出错
}
}
}
return itemSet; // 返回选中项的小部件属性集合
}
// 获取选中项的数量
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values(); // 获取选中项的哈希映射的值集
if (null == values) {
return 0; // 如果值集为空则返回0
}
Iterator<Boolean> iter = values.iterator(); // 创建一个迭代器,用于遍历值集
int count = 0; // 初始化计数器为0
while (iter.hasNext()) { // 遍历值集
if (true == iter.next()) { // 如果值为true表示选中
count++; // 计数器加1
}
}
return count; // 返回选中项的数量
}
// 判断是否全部选中
public boolean isAllSelected() {
int checkedCount = getSelectedCount(); // 获取选中项的数量
return (checkedCount != 0 && checkedCount == mNotesCount); // 如果选中项的数量不为0且等于笔记总数则返回true
}
// 判断指定位置的项是否被选中
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) { // 如果选中项的哈希映射中不存在指定位置的键
return false; // 返回false表示未选中
}
return mSelectedIndex.get(position); // 返回选中项的哈希映射中指定位置的值(选中状态)
}
// 当数据集内容发生变化时调用此方法
@Override
protected void onContentChanged() {
super.onContentChanged(); // 调用父类方法
calcNotesCount(); // 计算笔记总数
}
// 当游标发生变化时调用此方法
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor); // 调用父类方法
calcNotesCount(); // 计算笔记总数
}
// 计算笔记总数的方法
private void calcNotesCount() {
mNotesCount = 0; // 初始化笔记总数为0
for (int i = 0; i < getCount(); i++) { // 遍历所有项
Cursor c = (Cursor) getItem(i); // 获取项的游标
if (c != null) { // 如果游标不为空
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { // 如果是笔记类型
mNotesCount++; // 笔记总数加1
}
} else {
Log.e(TAG, "Invalid cursor"); // 输出错误日志信息
return; // 提前返回,结束方法执行
}
}
}
}

@ -1,142 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//实现功能:显示笔记列表项,包含了标题、时间、警告图标、呼叫名称(针对通话记录)和一个选择框。
//根据传入的数据类型(笔记或文件夹),它会动态地调整视图的显示,包括背景、警告图标、标题文本等。
package net.micode.notes.ui;
// 导入必要的包和类
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
// 自定义的LinearLayout用于显示笔记列表项
public class NotesListItem extends LinearLayout {
// 视图组件
private ImageView mAlert; // 警告图标
private TextView mTitle; // 标题
private TextView mTime; // 时间
private TextView mCallName; // 呼叫名称(针对通话记录)
private NoteItemData mItemData; // 当前项的数据
private CheckBox mCheckBox; // 选择框
// 构造函数,初始化视图
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this); // 加载布局文件
// 初始化视图组件
mAlert = findViewById(R.id.iv_alert_icon);
mTitle = findViewById(R.id.tv_title);
mTime = findViewById(R.id.tv_time);
mCallName = findViewById(R.id.tv_name);
mCheckBox = findViewById(android.R.id.checkbox); // 注意这里使用了Android内置的ID
}
// 绑定数据到视图
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 根据choiceMode和数据类型设置CheckBox的可见性
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
} else {
mCheckBox.setVisibility(View.GONE);
}
mItemData = data; // 保存数据
// 根据数据的不同情况设置不同的视图显示
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
// 如果是通话记录文件夹
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
// 如果是通话记录文件夹下的项
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context, R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else {
// 其他情况
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
if (data.getType() == Notes.TYPE_FOLDER) {
// 如果是文件夹
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
// 如果是笔记
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
}
}
// 设置时间
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 设置背景
setBackground(data);
}
// 根据数据设置背景
private void setBackground(NoteItemData data) {
int id = data.getBgColorId(); // 获取背景颜色ID
if (data.getType() == Notes.TYPE_NOTE) {
// 如果是笔记,根据笔记的位置设置不同的背景
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
// 如果是文件夹,设置统一的背景
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
// 获取当前项的数据
public NoteItemData getItemData() {
return mItemData;
}
}

@ -1,2 +0,0 @@
# xiaomi
Loading…
Cancel
Save