Compare commits

..

No commits in common. 'main' and '202502' have entirely different histories.
main ... 202502

@ -1,2 +1 @@
第一步21312213cccc666611111 第一步21312213cccc
dhjebjc

@ -1 +0,0 @@
213wqe1231111

@ -1 +0,0 @@
Subproject commit 87902d310e6c672d3d3a95ead30c1beb0754068f

@ -1,295 +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.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";
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
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 e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
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);
}
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<ContentProviderOperation>();
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, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
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_FOLER)},
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;
}
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
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;
}
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
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;
}
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 "";
}
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;
}
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);
}
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;
}
}

@ -1,467 +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.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
* @param ids ID
* @return true false
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// 检查传入的 ID 集合是否为空
if (ids == null) {
// 如果为空,记录日志并返回 true
Log.d(TAG, "the ids is null");
return true;
}
// 检查 ID 集合的大小是否为 0
if (ids.size() == 0) {
// 如果为 0记录日志并返回 true
Log.d(TAG, "no id is in the hashset");
return true;
}
// 创建一个用于存储内容提供者操作的列表
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历 ID 集合
for (long id : ids) {
// 检查是否为系统根文件夹的 ID如果是则不进行删除操作并记录错误日志
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) {
// 如果无效,记录日志并返回 false
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
// 操作成功,返回 true
return true;
} catch (RemoteException e) {
// 捕获远程异常并记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 捕获操作应用异常并记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
// 发生异常,返回 false
return false;
}
/**
*
*
* @param resolver
* @param id ID
* @param srcFolderId ID
* @param desFolderId ID
*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
// 创建一个 ContentValues 对象,用于存储要更新的字段和值
ContentValues values = new ContentValues();
// 设置笔记的父文件夹 ID 为目标文件夹 ID
values.put(NoteColumns.PARENT_ID, desFolderId);
// 设置笔记的原始父文件夹 ID 为源文件夹 ID
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
// 设置笔记的本地修改标志为 1
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 执行更新操作,将笔记移动到目标文件夹
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
/**
*
*
* @param resolver
* @param ids ID
* @param folderId ID
* @return true false
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 检查传入的 ID 集合是否为空
if (ids == null) {
// 如果为空,记录日志并返回 true
Log.d(TAG, "the ids is null");
return true;
}
// 创建一个用于存储内容提供者操作的列表
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 遍历 ID 集合
for (long id : ids) {
// 创建一个更新操作的构建器
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
// 设置笔记的父文件夹 ID 为目标文件夹 ID
builder.withValue(NoteColumns.PARENT_ID, folderId);
// 设置笔记的本地修改标志为 1
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) {
// 如果无效,记录日志并返回 false
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
// 操作成功,返回 true
return true;
} catch (RemoteException e) {
// 捕获远程异常并记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
// 捕获操作应用异常并记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
// 发生异常,返回 false
return false;
}
/**
*
*
* @param resolver
* @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_FOLER)},
null);
// 初始化文件夹数量为 0
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
* @param noteId ID
* @param type
* @return true false
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
// 查询笔记数据库,检查笔记是否存在且不在回收站中
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
// 初始化存在标志为 false
boolean exist = false;
// 检查游标是否不为空
if (cursor != null) {
// 检查游标中的记录数量是否大于 0
if (cursor.getCount() > 0) {
// 如果大于 0说明笔记存在设置存在标志为 true
exist = true;
}
// 关闭游标
cursor.close();
}
// 返回存在标志
return exist;
}
/**
*
*
* @param resolver
* @param noteId ID
* @return true false
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 查询笔记数据库,检查笔记是否存在
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
// 初始化存在标志为 false
boolean exist = false;
// 检查游标是否不为空
if (cursor != null) {
// 检查游标中的记录数量是否大于 0
if (cursor.getCount() > 0) {
// 如果大于 0说明笔记存在设置存在标志为 true
exist = true;
}
// 关闭游标
cursor.close();
}
// 返回存在标志
return exist;
}
/**
*
*
* @param resolver
* @param dataId ID
* @return true false
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 查询数据数据库,检查数据是否存在
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
// 初始化存在标志为 false
boolean exist = false;
// 检查游标是否不为空
if (cursor != null) {
// 检查游标中的记录数量是否大于 0
if (cursor.getCount() > 0) {
// 如果大于 0说明数据存在设置存在标志为 true
exist = true;
}
// 关闭游标
cursor.close();
}
// 返回存在标志
return exist;
}
/**
*
*
* @param resolver
* @param name
* @return true false
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
// 查询笔记数据库,检查可见文件夹名称是否已存在
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
// 初始化存在标志为 false
boolean exist = false;
// 检查游标是否不为空
if(cursor != null) {
// 检查游标中的记录数量是否大于 0
if(cursor.getCount() > 0) {
// 如果大于 0说明文件夹名称已存在设置存在标志为 true
exist = true;
}
// 关闭游标
cursor.close();
}
// 返回存在标志
return exist;
}
/**
*
*
* @param resolver
* @param folderId ID
* @return null
*/
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);
// 初始化小部件属性集合为 null
HashSet<AppWidgetAttribute> set = null;
// 检查游标是否不为空
if (c != null) {
// 将游标移动到第一行
if (c.moveToFirst()) {
// 创建一个小部件属性集合
set = new HashSet<AppWidgetAttribute>();
do {
try {
// 创建一个小部件属性对象
AppWidgetAttribute widget = new AppWidgetAttribute();
// 从游标中获取小部件 ID
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
* @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
* @param phoneNumber
* @param callDate
* @return ID 0
*/
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;
}
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);
}
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;
}
}

@ -1,270 +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.tool;
import android.content.Context;
import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
/**
*
*/
public class ResourceParser {
// 定义笔记背景颜色的常量
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
// 默认的笔记背景颜色
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;
// 默认的笔记字体大小
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
/**
*
*/
public static class NoteBgResources {
// 笔记编辑界面的背景资源数组
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
// 笔记编辑界面标题的背景资源数组
private final static int [] BG_EDIT_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
* @param id ID
* @return ID
*/
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
/**
* ID ID
* @param id ID
* @return ID
*/
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
/**
* ID
* ID
* ID
* @param context
* @return ID
*/
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
return BG_DEFAULT_COLOR;
}
}
/**
*
*/
public static class NoteItemBgResources {
// 笔记列表项中第一项的背景资源数组
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
// 笔记列表项中普通项的背景资源数组
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
// 笔记列表项中最后一项的背景资源数组
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
// 笔记列表项中单独一项的背景资源数组
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
/**
* ID ID
* @param id ID
* @return ID
*/
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
/**
* ID ID
* @param id ID
* @return ID
*/
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
/**
* ID ID
* @param id ID
* @return ID
*/
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
/**
* ID ID
* @param id ID
* @return ID
*/
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
/**
* ID
* @return ID
*/
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
/**
*
*/
public static class WidgetBgResources {
// 2x 大小小部件的背景资源数组
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
/**
* ID 2x ID
* @param id ID
* @return 2x ID
*/
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
// 4x 大小小部件的背景资源数组
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
/**
* ID 4x ID
* @param id ID
* @return 4x ID
*/
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
/**
*
*/
public static class TextAppearanceResources {
// 文本外观资源数组
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
/**
* ID ID
* ID ID
* @param id ID
* @return 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];
}
/**
*
* @return
*/
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}

@ -1,390 +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.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;
// 用于存储笔记的数据,包括文本数据和通话数据
private NoteData mNoteData;
// 日志标签,用于在日志中标识该类的信息
private static final String TAG = "Note";
/**
* ID
*
* @param context 访
* @param folderId ID
* @return ID
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个新的 ContentValues 对象,用于存储要插入数据库的笔记信息
ContentValues values = new ContentValues();
// 获取当前时间作为笔记的创建时间
long createdTime = System.currentTimeMillis();
// 将创建时间添加到 ContentValues 中
values.put(NoteColumns.CREATED_DATE, createdTime);
// 将修改时间设置为创建时间,因为是新创建的笔记
values.put(NoteColumns.MODIFIED_DATE, createdTime);
// 设置笔记的类型为普通笔记
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
// 标记笔记为本地已修改
values.put(NoteColumns.LOCAL_MODIFIED, 1);
// 设置笔记所属文件夹的 ID
values.put(NoteColumns.PARENT_ID, folderId);
// 使用内容解析器插入新笔记,并获取插入后的 URI
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
try {
// 从 URI 中提取笔记的 ID
noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
// 若提取 ID 时出现异常,记录错误日志
Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0;
}
if (noteId == -1) {
// 若笔记 ID 为 -1抛出异常表示笔记 ID 错误
throw new IllegalStateException("Wrong note id:" + noteId);
}
return noteId;
}
/**
*
*/
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
/**
*
*
* @param key
* @param value
*/
public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
*
* @param key
* @param value
*/
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
/**
* ID
*
* @param id ID
*/
public void setTextDataId(long id) {
mNoteData.setTextDataId(id);
}
/**
* ID
*
* @return ID
*/
public long getTextDataId() {
return mNoteData.mTextDataId;
}
/**
* ID
*
* @param id ID
*/
public void setCallDataId(long id) {
mNoteData.setCallDataId(id);
}
/**
*
*
* @param key
* @param value
*/
public void setCallData(String key, String value) {
mNoteData.setCallData(key, value);
}
/**
*
*
* @return true false
*/
public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
/**
*
*
* @param context 访
* @param noteId ID
* @return true false
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
// 若笔记 ID 不合法,抛出异常
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
if (!isLocalModified()) {
// 若笔记未被修改,直接返回 true
return true;
}
/**
* LOCAL_MODIFIED MODIFIED_DATE
* 使
*/
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
// 若更新笔记失败,记录错误日志
Log.e(TAG, "Update note error, should not happen");
// 不返回,继续执行后续操作
}
// 清空笔记的差异值
mNoteDiffValues.clear();
if (mNoteData.isLocalModified()
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
// 若笔记数据被修改且同步失败,返回 false
return false;
}
return true;
}
/**
*
*/
private class NoteData {
// 文本数据的 ID
private long mTextDataId;
// 存储文本数据的 ContentValues 对象
private ContentValues mTextDataValues;
// 通话数据的 ID
private long mCallDataId;
// 存储通话数据的 ContentValues 对象
private ContentValues mCallDataValues;
// 日志标签,用于在日志中标识该内部类的信息
private static final String TAG = "NoteData";
/**
* ContentValues ID 0
*/
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
}
/**
*
*
* @return true false
*/
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
/**
* ID
*
* @param id ID
*/
void setTextDataId(long id) {
if(id <= 0) {
// 若文本数据 ID 不合法,抛出异常
throw new IllegalArgumentException("Text data id should larger than 0");
}
mTextDataId = id;
}
/**
* ID
*
* @param id ID
*/
void setCallDataId(long id) {
if (id <= 0) {
// 若通话数据 ID 不合法,抛出异常
throw new IllegalArgumentException("Call data id should larger than 0");
}
mCallDataId = id;
}
/**
*
*
* @param key
* @param value
*/
void setCallData(String key, String value) {
mCallDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
*
* @param key
* @param value
*/
void setTextData(String key, String value) {
mTextDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
*
* @param context 访
* @param noteId ID
* @return URI null
*/
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* ID
*/
if (noteId <= 0) {
// 若笔记 ID 不合法,抛出异常
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
// 创建一个 ContentProviderOperation 列表,用于批量操作
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// ContentProviderOperation 构建器
ContentProviderOperation.Builder builder = null;
if(mTextDataValues.size() > 0) {
// 若文本数据有修改
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
// 若文本数据 ID 为 0说明是新的文本数据
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
// 插入新的文本数据
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
try {
// 获取插入后的文本数据 ID
setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 若获取 ID 失败,记录错误日志并清空文本数据
Log.e(TAG, "Insert new text data fail with noteId" + noteId);
mTextDataValues.clear();
return null;
}
} else {
// 若文本数据 ID 不为 0说明是更新已有的文本数据
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}
// 清空文本数据的 ContentValues 对象
mTextDataValues.clear();
}
if(mCallDataValues.size() > 0) {
// 若通话数据有修改
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
// 若通话数据 ID 为 0说明是新的通话数据
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
// 插入新的通话数据
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
try {
// 获取插入后的通话数据 ID
setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
} catch (NumberFormatException e) {
// 若获取 ID 失败,记录错误日志并清空通话数据
Log.e(TAG, "Insert new call data fail with noteId" + noteId);
mCallDataValues.clear();
return null;
}
} else {
// 若通话数据 ID 不为 0说明是更新已有的通话数据
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mCallDataId));
builder.withValues(mCallDataValues);
operationList.add(builder.build());
}
// 清空通话数据的 ContentValues 对象
mCallDataValues.clear();
}
if (operationList.size() > 0) {
try {
// 批量执行 ContentProviderOperation 列表中的操作
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
return (results == null || results.length == 0 || results[0] == null) ? null
: ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
} catch (RemoteException e) {
// 若执行批量操作时出现远程异常,记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
} catch (OperationApplicationException e) {
// 若执行批量操作时出现操作应用异常,记录错误日志
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null;
}
}
return null;
}
}
}

@ -1,523 +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.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;
/**
* WorkingNote 便
* 便便
*/
public class WorkingNote {
// 便签对象,用于管理便签的各种数据
private Note mNote;
// 便签的 ID
private long mNoteId;
// 便签的内容
private String mContent;
// 便签的模式
private int mMode;
// 便签的提醒日期
private long mAlertDate;
// 便签的修改日期
private long mModifiedDate;
// 便签的背景颜色 ID
private int mBgColorId;
// 便签小部件的 ID
private int mWidgetId;
// 便签小部件的类型
private int mWidgetType;
// 便签所在文件夹的 ID
private long mFolderId;
// 上下文对象,用于访问系统资源和执行操作
private Context mContext;
// 日志标签,用于调试日志输出
private static final String TAG = "WorkingNote";
// 标记便签是否已被删除
private boolean mIsDeleted;
// 便签设置更改监听器,用于监听便签设置的变化
private NoteSettingChangedListener mNoteSettingStatusListener;
// 数据查询的投影列,用于从数据库中查询数据
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 便签查询的投影列,用于从数据库中查询便签信息
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE
};
// 数据 ID 列的索引
private static final int DATA_ID_COLUMN = 0;
// 数据内容列的索引
private static final int DATA_CONTENT_COLUMN = 1;
// 数据 MIME 类型列的索引
private static final int DATA_MIME_TYPE_COLUMN = 2;
// 数据模式列的索引
private static final int DATA_MODE_COLUMN = 3;
// 便签父文件夹 ID 列的索引
private static final int NOTE_PARENT_ID_COLUMN = 0;
// 便签提醒日期列的索引
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
// 便签背景颜色 ID 列的索引
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
// 便签小部件 ID 列的索引
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;
/**
* 便
*
* @param context
* @param folderId 便 ID
*/
private WorkingNote(Context context, long folderId) {
mContext = context;
// 初始化提醒日期为 0表示没有提醒
mAlertDate = 0;
// 初始化修改日期为当前时间
mModifiedDate = System.currentTimeMillis();
mFolderId = folderId;
mNote = new Note();
// 初始化便签 ID 为 0表示新便签还未分配 ID
mNoteId = 0;
// 标记便签未被删除
mIsDeleted = false;
// 初始化便签模式为 0
mMode = 0;
// 初始化小部件类型为无效类型
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
/**
* 便
*
* @param context
* @param noteId 便 ID
* @param folderId 便 ID
*/
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
// 标记便签未被删除
mIsDeleted = false;
mNote = new Note();
// 加载便签信息
loadNote();
}
/**
* 便 ID ID ID
*/
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()) {
// 获取便签所在文件夹的 ID
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
// 获取便签的背景颜色 ID
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
// 获取便签小部件的 ID
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);
// 抛出异常,表示找不到指定 ID 的便签
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
// 加载便签的数据
loadNoteData();
}
/**
* 便便
*/
private void loadNoteData() {
// 查询便签的数据
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
// 获取数据的 MIME 类型
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
// 如果是普通便签类型,获取便签内容和模式
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
// 设置便签的文本数据 ID
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
// 如果是通话便签类型,设置通话数据 ID
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);
// 抛出异常,表示找不到指定 ID 的便签数据
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
/**
* 便
*
* @param context
* @param folderId 便 ID
* @param widgetId 便 ID
* @param widgetType 便
* @param defaultBgColorId 便 ID
* @return 便
*/
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
// 创建一个新的便签对象
WorkingNote note = new WorkingNote(context, folderId);
// 设置便签的背景颜色 ID
note.setBgColorId(defaultBgColorId);
// 设置便签小部件的 ID
note.setWidgetId(widgetId);
// 设置便签小部件的类型
note.setWidgetType(widgetType);
return note;
}
/**
* ID 便
*
* @param context
* @param id 便 ID
* @return 便
*/
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
/**
* 便
*
* @return true false
*/
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
// 如果便签不在数据库中,创建一个新的便签 ID
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
// 记录错误日志
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
// 同步便签数据到数据库
mNote.syncNote(mContext, mNoteId);
/**
* 便
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
// 通知监听器小部件内容发生变化
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
/**
* 便
*
* @return true false
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
/**
* 便
*
* @return true false
*/
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
/**
* 便
*
* @param l
*/
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
/**
* 便
*
* @param date
* @param set
*/
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
// 设置便签的提醒日期
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {
// 通知监听器提醒日期发生变化
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
/**
* 便
*
* @param mark true
*/
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
// 通知监听器小部件内容发生变化
mNoteSettingStatusListener.onWidgetChanged();
}
}
/**
* 便 ID
*
* @param id ID
*/
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
// 通知监听器背景颜色发生变化
mNoteSettingStatusListener.onBackgroundColorChanged();
}
// 设置便签的背景颜色 ID
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
/**
* 便
*
* @param mode
*/
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
// 通知监听器检查列表模式发生变化
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
// 设置便签的文本数据模式
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
/**
* 便
*
* @param type
*/
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
// 设置便签的小部件类型
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
/**
* 便 ID
*
* @param id ID
*/
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
// 设置便签的小部件 ID
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
/**
* 便
*
* @param text
*/
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
// 设置便签的文本数据内容
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
/**
* 便便
*
* @param phoneNumber
* @param callDate
*/
public void convertToCallNote(String phoneNumber, long callDate) {
// 设置通话便签的通话日期
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
// 设置通话便签的电话号码
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
// 设置便签的父文件夹 ID 为通话记录文件夹的 ID
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
}
/**
* 便
*
* @return true false
*/
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
public String getContent() {
return mContent;
}
public long getAlertDate() {
return mAlertDate;
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
public int getBgColorId() {
return mBgColorId;
}
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
public int getCheckListMode() {
return mMode;
}
public long getNoteId() {
return mNoteId;
}
public long getFolderId() {
return mFolderId;
}
public int getWidgetId() {
return mWidgetId;
}
public int getWidgetType() {
return mWidgetType;
}
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
* Called when user set clock
*/
void onClockAlertChanged(long date, boolean set);
/**
* Call when user create note from widget
*/
void onWidgetChanged();
/**
* Call when switch between check list mode and normal mode
* @param oldMode is previous mode before change
* @param newMode is new mode
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
}

@ -1,226 +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.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import android.widget.RemoteViews;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
/**
* NoteWidgetProvider AppWidgetProvider
*
*/
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 查询笔记信息时使用的投影,指定要查询的列
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 笔记 ID 在投影数组中的索引
public static final int COLUMN_ID = 0;
// 笔记背景颜色 ID 在投影数组中的索引
public static final int COLUMN_BG_COLOR_ID = 1;
// 笔记摘要在投影数组中的索引
public static final int COLUMN_SNIPPET = 2;
// 日志标签,用于在日志中标识该类的相关信息
private static final String TAG = "NoteWidgetProvider";
/**
*
* ID
*
* @param context
* @param appWidgetIds ID
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 创建 ContentValues 对象,用于存储要更新的字段和值
ContentValues values = new ContentValues();
// 将笔记的小部件 ID 设置为无效值
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历被删除的小部件 ID 数组
for (int i = 0; i < appWidgetIds.length; i++) {
// 更新笔记数据库中对应小部件 ID 的笔记记录
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
new String[] { String.valueOf(appWidgetIds[i])});
}
}
/**
* ID
*
* @param context
* @param widgetId ID
* @return null
*/
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
// 查询笔记数据库,获取指定小部件 ID 且不在回收站的笔记信息
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
/**
*
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用带隐私模式参数的更新方法,隐私模式为 false
update(context, appWidgetManager, appWidgetIds, false);
}
/**
*
* ID
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
* @param privacyMode
*/
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历要更新的小部件 ID 数组
for (int i = 0; i < appWidgetIds.length; i++) {
// 检查小部件 ID 是否有效
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取默认的背景颜色 ID
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化笔记摘要为空字符串
String snippet = "";
// 创建一个启动笔记编辑活动的意图
Intent intent = new Intent(context, NoteEditActivity.class);
// 设置意图标志,确保活动以单顶模式启动
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 将小部件 ID 作为额外数据添加到意图中
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
// 将小部件类型作为额外数据添加到意图中
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 获取指定小部件 ID 的笔记信息游标
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
// 检查是否存在多条具有相同小部件 ID 的笔记记录
if (c.getCount() > 1) {
// 记录错误日志
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
// 关闭游标
c.close();
return;
}
// 从游标中获取笔记摘要
snippet = c.getString(COLUMN_SNIPPET);
// 从游标中获取笔记背景颜色 ID
bgId = c.getInt(COLUMN_BG_COLOR_ID);
// 将笔记 ID 作为额外数据添加到意图中
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
// 设置意图的动作类型为查看
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果没有找到笔记信息,设置默认的提示信息
snippet = context.getResources().getString(R.string.widget_havenot_content);
// 设置意图的动作类型为插入或编辑
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 关闭游标
if (c != null) {
c.close();
}
// 创建 RemoteViews 对象,用于设置小部件的布局
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
// 设置小部件的背景图片资源
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
// 将背景颜色 ID 作为额外数据添加到意图中
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* 宿
*/
PendingIntent pendingIntent = null;
if (privacyMode) {
// 如果开启隐私模式,设置小部件文本为隐私模式提示信息
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
// 创建启动笔记列表活动的待定意图
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 如果未开启隐私模式,设置小部件文本为笔记摘要
rv.setTextViewText(R.id.widget_text, snippet);
// 创建启动笔记编辑活动的待定意图
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 设置小部件文本的点击事件为待定意图
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 更新指定小部件 ID 的小部件布局
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
/**
* ID ID
*
*
* @param bgId ID
* @return ID
*/
protected abstract int getBgResourceId(int bgId);
/**
* ID
*
*
* @return ID
*/
protected abstract int getLayoutId();
/**
*
*
*
* @return
*/
protected abstract int getWidgetType();
}

@ -1,77 +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.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_2x NoteWidgetProvider 2x
* 2x
*/
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
/**
*
* update
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的 update 方法进行小部件更新
super.update(context, appWidgetManager, appWidgetIds);
}
/**
* 2x ID
*
* @return 2x ID R.layout.widget_2x
*/
@Override
protected int getLayoutId() {
return R.layout.widget_2x;
}
/**
* ID 2x ID
* ResourceParser WidgetBgResources
*
* @param bgId ID
* @return 2x ID
*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
/**
* 2x
*
* @return 2x Notes.TYPE_WIDGET_2X
*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X;
}
}

@ -1,76 +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.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_4x NoteWidgetProvider 4x
* 4x
*/
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
/**
*
* update
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的 update 方法进行小部件更新
super.update(context, appWidgetManager, appWidgetIds);
}
/**
* 4x ID
*
* @return 4x ID R.layout.widget_4x
*/
protected int getLayoutId() {
return R.layout.widget_4x;
}
/**
* ID 4x ID
* ResourceParser WidgetBgResources
*
* @param bgId ID
* @return 4x ID
*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
/**
* 4x
*
* @return 4x Notes.TYPE_WIDGET_4X
*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;
}
}

@ -1 +0,0 @@
undefined

@ -1,344 +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.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());
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// First export folder and its notes
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);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// Print folder's name
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();
}
// Export notes in root's folder
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))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
private PrintStream getExportToTextPrintStream() {
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 {
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;
}
}
/**
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
File file = new File(sb.toString());
try {
if (!filedir.exists()) {
filedir.mkdir();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

@ -1,113 +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.tool;
public class GTaskStringUtils {
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";
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 +0,0 @@
assdjgyuguyhgnb6

@ -1,229 +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.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
/**
* AlarmAlertActivity
*
*/
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 存储笔记的 ID
private long mNoteId;
// 存储笔记的摘要信息
private String mSnippet;
// 摘要信息显示的最大长度
private static final int SNIPPET_PREW_MAX_LEN = 60;
// 用于播放提醒声音的媒体播放器
MediaPlayer mPlayer;
/**
* Activity
* @param savedInstanceState Activity
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 请求不显示标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 获取当前窗口
final Window win = getWindow();
// 添加标志,使窗口在锁屏状态下也能显示
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
// 如果屏幕处于关闭状态
if (!isScreenOn()) {
// 添加标志,保持屏幕常亮、点亮屏幕、允许在屏幕开启时锁定等
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
// 获取启动该 Activity 的 Intent
Intent intent = getIntent();
try {
// 从 Intent 的数据路径中获取笔记的 ID
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 根据笔记 ID 获取笔记的摘要信息
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
// 如果摘要信息长度超过最大显示长度,则进行截取并添加省略号
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
} catch (IllegalArgumentException e) {
// 处理异常,打印堆栈信息
e.printStackTrace();
return;
}
// 创建媒体播放器实例
mPlayer = new MediaPlayer();
// 检查笔记是否在数据库中可见
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
// 显示提醒对话框
showActionDialog();
// 播放提醒声音
playAlarmSound();
} else {
// 如果笔记不可见,则关闭该 Activity
finish();
}
}
/**
*
* @return true false
*/
private boolean isScreenOn() {
// 获取电源管理服务
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 检查屏幕是否开启
return pm.isScreenOn();
}
/**
*
*/
private void playAlarmSound() {
// 获取默认的闹钟铃声 URI
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 获取静音模式下受影响的音频流设置
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
// 根据静音模式设置音频流类型
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置音频数据源
mPlayer.setDataSource(this, url);
// 准备媒体播放器
mPlayer.prepare();
// 设置循环播放
mPlayer.setLooping(true);
// 开始播放
mPlayer.start();
} catch (IllegalArgumentException e) {
// 处理异常,打印堆栈信息
e.printStackTrace();
} catch (SecurityException e) {
// 处理异常,打印堆栈信息
e.printStackTrace();
} catch (IllegalStateException e) {
// 处理异常,打印堆栈信息
e.printStackTrace();
} catch (IOException e) {
// 处理异常,打印堆栈信息
e.printStackTrace();
}
}
/**
*
*/
private void showActionDialog() {
// 创建对话框构建器
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
// 设置对话框标题
dialog.setTitle(R.string.app_name);
// 设置对话框显示的消息,即笔记摘要信息
dialog.setMessage(mSnippet);
// 设置确认按钮及其点击监听器
dialog.setPositiveButton(R.string.notealert_ok, this);
// 如果屏幕处于开启状态,设置进入笔记编辑页面的按钮
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 显示对话框并设置对话框关闭监听器
dialog.show().setOnDismissListener(this);
}
/**
*
* @param dialog
* @param which
*/
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个 Intent 用于启动笔记编辑 Activity
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置 Intent 的操作类型为查看
intent.setAction(Intent.ACTION_VIEW);
// 传递笔记的 ID 到笔记编辑 Activity
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动笔记编辑 Activity
startActivity(intent);
break;
default:
break;
}
}
/**
*
* @param dialog
*/
public void onDismiss(DialogInterface dialog) {
// 停止播放提醒声音
stopAlarmSound();
// 关闭当前 Activity
finish();
}
/**
*
*/
private void stopAlarmSound() {
if (mPlayer != null) {
// 停止播放
mPlayer.stop();
// 释放媒体播放器资源
mPlayer.release();
mPlayer = null;
}
}
}

@ -1,91 +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.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* AlarmInitReceiver 广
* 便便
*/
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义查询便签时需要返回的列
private static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 便签的 ID
NoteColumns.ALERTED_DATE // 便签的提醒日期
};
// 定义查询结果集中各列的索引
private static final int COLUMN_ID = 0; // 便签 ID 列的索引
private static final int COLUMN_ALERTED_DATE = 1; // 便签提醒日期列的索引
/**
* 广
* 便便
*
* @param context
* @param intent 广
*/
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前时间的毫秒数
long currentDate = System.currentTimeMillis();
// 查询数据库中所有提醒日期晚于当前时间且类型为便签的记录
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
// 查询条件:提醒日期大于当前时间且便签类型为普通便签
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
// 查询条件的参数,将当前时间作为参数传入
new String[] { String.valueOf(currentDate) },
null);
// 如果查询结果不为空
if (c != null) {
// 移动到结果集的第一行
if (c.moveToFirst()) {
// 遍历结果集中的每一行
do {
// 获取当前便签的提醒日期
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个新的意图,用于触发 AlarmReceiver
Intent sender = new Intent(context, AlarmReceiver.class);
// 设置意图的数据为当前便签的 URI
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个 PendingIntent用于在提醒日期触发广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取系统的闹钟服务
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 设置闹钟,在提醒日期唤醒设备并触发 PendingIntent
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭游标,释放资源
c.close();
}
}
}

@ -1,42 +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.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* AlarmReceiver BroadcastReceiver广
* 广 AlarmAlertActivity
*/
public class AlarmReceiver extends BroadcastReceiver {
/**
* 广
* @param context
* @param intent 广
*/
@Override
public void onReceive(Context context, Intent intent) {
// 设置意图的目标类为 AlarmAlertActivity
intent.setClass(context, AlarmAlertActivity.class);
// 添加新任务标志,确保可以在新任务中启动 Activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动 AlarmAlertActivity
context.startActivity(intent);
}
}

@ -1,412 +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 java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout {
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
private static final int DATE_SPINNER_MIN_VAL = 0;
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
private static final int MINUT_SPINNER_MIN_VAL = 0;
private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate;
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm;
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 移除同步账户相关设置
private void removeSyncAccount() {
// 获取指定名称的 SharedPreferences 对象,使用私有模式,确保只有本应用可以访问这些设置
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
// 获取 SharedPreferences 的编辑器,用于修改存储的设置
SharedPreferences.Editor editor = settings.edit();
// 检查 SharedPreferences 中是否包含同步账户名称的键
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
// 如果包含,则从编辑器中移除该键值对
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
// 检查 SharedPreferences 中是否包含最后同步时间的键
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
// 如果包含,则从编辑器中移除该键值对
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
// 提交编辑器中的修改,将更改保存到 SharedPreferences 中
editor.commit();
// 清理本地与 Google 任务GTask相关的信息由于数据库操作可能会阻塞主线程因此在新线程中执行
new Thread(new Runnable() {
public void run() {
// 创建一个 ContentValues 对象,用于存储要更新的数据
ContentValues values = new ContentValues();
// 将 GTask ID 字段设置为空字符串
values.put(NoteColumns.GTASK_ID, "");
// 将同步 ID 字段设置为 0
values.put(NoteColumns.SYNC_ID, 0);
// 使用 ContentResolver 更新 Notes 内容提供者中的笔记数据,将 GTask ID 和同步 ID 字段清空
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
// 获取当前设置的同步账户名称
public static String getSyncAccountName(Context context) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 从 SharedPreferences 中获取同步账户名称,如果不存在则返回空字符串
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
// 设置最后一次同步的时间
public static void setLastSyncTime(Context context, long time) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 获取 SharedPreferences 的编辑器,用于修改存储的设置
SharedPreferences.Editor editor = settings.edit();
// 将最后同步时间存储到编辑器中
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
// 提交编辑器中的修改,将更改保存到 SharedPreferences 中
editor.commit();
}
// 获取最后一次同步的时间
public static long getLastSyncTime(Context context) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 从 SharedPreferences 中获取最后同步时间,如果不存在则返回 0
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
// 自定义广播接收器,用于接收 GTask 同步服务的广播
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 刷新界面,更新账户设置和同步按钮状态等信息
refreshUI();
// 检查广播意图中是否包含正在同步的标志,并且标志为 true
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
// 查找用于显示同步状态的 TextView 控件
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 设置同步状态文本为广播意图中携带的进度消息
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
// 处理选项菜单的点击事件
public boolean onOptionsItemSelected(MenuItem item) {
// 根据点击的菜单项 ID 进行不同的处理
switch (item.getItemId()) {
// 如果点击的是应用图标(即返回按钮)
case android.R.id.home:
// 创建一个意图,用于启动 NotesListActivity
Intent intent = new Intent(this, NotesListActivity.class);
// 添加标志,确保启动的 NotesListActivity 位于任务栈的顶部,并清除之前的所有活动
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 启动 NotesListActivity
startActivity(intent);
// 返回 true 表示事件已处理
return true;
// 如果点击的是其他菜单项
default:
// 返回 false 表示事件未处理
return false;
}
}
// 获取当前月份
public int getCurrentMonth() {
// 通过Calendar对象获取当前月份
return mDate.get(Calendar.MONTH);
}
/**
*
*
* @param month
*/
public void setCurrentMonth(int month) {
// 如果不是初始化阶段且设置的月份与当前月份相同,则直接返回
if (!mInitialising && month == getCurrentMonth()) {
return;
}
// 设置Calendar对象的月份
mDate.set(Calendar.MONTH, month);
// 更新日期控件显示
updateDateControl();
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentDay() {
// 通过Calendar对象获取当前月份中的日期
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
*
*
* @param dayOfMonth
*/
public void setCurrentDay(int dayOfMonth) {
// 如果不是初始化阶段且设置的日期与当前日期相同,则直接返回
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
// 设置Calendar对象的日期
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
// 更新日期控件显示
updateDateControl();
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
* 24(0~23)
* @return 24
*/
public int getCurrentHourOfDay() {
// 通过Calendar对象获取24小时制的当前小时
return mDate.get(Calendar.HOUR_OF_DAY);
}
// 获取当前小时根据是否为24小时制进行不同处理
private int getCurrentHour() {
if (mIs24HourView){
// 如果是24小时制直接返回24小时制的当前小时
return getCurrentHourOfDay();
} else {
// 如果是12小时制
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
// 如果小时数大于12减去12
return hour - HOURS_IN_HALF_DAY;
} else {
// 如果小时数为0则显示为12否则直接显示
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* 24(0~23)
*
* @param hourOfDay 24
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是初始化阶段且设置的小时与当前小时相同,则直接返回
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
// 设置Calendar对象的小时
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
// 如果是12小时制
if (hourOfDay >= HOURS_IN_HALF_DAY) {
// 如果小时数大于等于12设置为下午
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
// 如果小时数大于12减去12
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
// 如果小时数小于12设置为上午
mIsAm = true;
if (hourOfDay == 0) {
// 如果小时数为0则显示为12
hourOfDay = HOURS_IN_HALF_DAY;
}
}
// 更新上午/下午控件显示
updateAmPmControl();
}
// 设置小时选择器的值
mHourSpinner.setValue(hourOfDay);
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentMinute() {
// 通过Calendar对象获取当前分钟
return mDate.get(Calendar.MINUTE);
}
/**
*
*/
public void setCurrentMinute(int minute) {
// 如果不是初始化阶段且设置的分钟与当前分钟相同,则直接返回
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
// 设置分钟选择器的值
mMinuteSpinner.setValue(minute);
// 设置Calendar对象的分钟
mDate.set(Calendar.MINUTE, minute);
// 触发日期时间改变的回调
onDateTimeChanged();
}
/**
* @return 24truefalse
*/
public boolean is24HourView () {
// 返回是否为24小时制视图的标志
return mIs24HourView;
}
/**
* 24/
*
* @param is24HourView true24false/
*/
public void set24HourView(boolean is24HourView) {
// 如果当前模式与要设置的模式相同,则直接返回
if (mIs24HourView == is24HourView) {
return;
}
// 更新是否为24小时制视图的标志
mIs24HourView = is24HourView;
// 根据是否为24小时制视图显示或隐藏上午/下午选择器
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
// 获取当前24小时制的小时
int hour = getCurrentHourOfDay();
// 更新小时选择器的范围
updateHourControl();
// 设置当前小时
setCurrentHour(hour);
// 更新上午/下午控件显示
updateAmPmControl();
}
// 更新日期控件的显示
private void updateDateControl() {
// 创建一个新的Calendar对象
Calendar cal = Calendar.getInstance();
// 设置Calendar对象的时间为当前时间
cal.setTimeInMillis(mDate.getTimeInMillis());
// 将日期向前移动DAYS_IN_ALL_WEEK / 2 + 1天
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
// 清空日期选择器的显示值
mDateSpinner.setDisplayedValues(null);
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
// 日期向后移动1天
cal.add(Calendar.DAY_OF_YEAR, 1);
// 将日期格式化为"MM.dd EEEE"的字符串
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
// 设置日期选择器的显示值
mDateSpinner.setDisplayedValues(mDateDisplayValues);
// 设置日期选择器的当前值为中间值
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
// 使日期选择器重绘
mDateSpinner.invalidate();
}
// 更新上午/下午控件的显示
private void updateAmPmControl() {
if (mIs24HourView) {
// 如果是24小时制视图隐藏上午/下午选择器
mAmPmSpinner.setVisibility(View.GONE);
} else {
// 如果是12小时制视图根据是否为上午设置选择器的值
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
// 显示上午/下午选择器
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
// 更新小时控件的显示
private void updateHourControl() {
if (mIs24HourView) {
// 如果是24小时制视图设置小时选择器的最小值和最大值
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
// 如果是12小时制视图设置小时选择器的最小值和最大值
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
*
* @param callback null
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
// 设置日期时间改变的回调函数
mOnDateTimeChangedListener = callback;
}
// 触发日期时间改变的回调
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
// 调用回调函数,传递当前的年、月、日、小时和分钟
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -1,146 +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 java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
/**
* DateTimePickerDialog AlertDialog
* DateTimePicker
*
*/
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
// 用于存储用户选择的日期和时间
private Calendar mDate = Calendar.getInstance();
// 标记是否使用 24 小时制显示时间
private boolean mIs24HourView;
// 日期时间选择确定的回调接口
private OnDateTimeSetListener mOnDateTimeSetListener;
// 自定义的日期时间选择器组件
private DateTimePicker mDateTimePicker;
/**
*
*/
public interface OnDateTimeSetListener {
/**
*
* @param dialog
* @param date
*/
void OnDateTimeSet(AlertDialog dialog, long date);
}
/**
*
* @param context
* @param date
*/
public DateTimePickerDialog(Context context, long date) {
super(context);
// 创建 DateTimePicker 实例
mDateTimePicker = new DateTimePicker(context);
// 将 DateTimePicker 设置为对话框的视图
setView(mDateTimePicker);
// 为 DateTimePicker 设置日期时间改变的监听器
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
@Override
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
// 更新 Calendar 对象的日期和时间
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
// 更新对话框的标题显示选择的日期时间
updateTitle(mDate.getTimeInMillis());
}
});
// 设置 Calendar 对象的时间为传入的日期时间
mDate.setTimeInMillis(date);
// 将秒数设置为 0
mDate.set(Calendar.SECOND, 0);
// 设置 DateTimePicker 的当前日期时间
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
// 设置对话框的确定按钮及其点击监听器
setButton(context.getString(R.string.datetime_dialog_ok), this);
// 设置对话框的取消按钮
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
// 根据系统设置确定是否使用 24 小时制
set24HourView(DateFormat.is24HourFormat(this.getContext()));
// 更新对话框的标题显示初始日期时间
updateTitle(mDate.getTimeInMillis());
}
/**
* 使 24
* @param is24HourView true 使 24 false 使 12
*/
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
/**
*
* @param callBack OnDateTimeSetListener
*/
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
/**
*
* @param date
*/
private void updateTitle(long date) {
// 设置日期时间显示的标志
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
// 根据是否使用 24 小时制添加相应的标志
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
// 设置对话框的标题为格式化后的日期时间
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
/**
*
* @param arg0
* @param arg1
*/
@Override
public void onClick(DialogInterface arg0, int arg1) {
// 如果设置了回调接口,则调用回调方法
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}

@ -1,101 +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.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
/**
* DropdownMenu
*
*/
public class DropdownMenu {
// 用于触发弹出菜单的按钮
private Button mButton;
// 弹出菜单对象
private PopupMenu mPopupMenu;
// 弹出菜单的菜单对象
private Menu mMenu;
/**
*
*
* @param context
* @param button
* @param menuId ID
*/
public DropdownMenu(Context context, Button button, int menuId) {
// 保存传入的按钮对象
mButton = button;
// 设置按钮的背景资源为下拉图标
mButton.setBackgroundResource(R.drawable.dropdown_icon);
// 创建一个弹出菜单,将其关联到按钮上
mPopupMenu = new PopupMenu(context, mButton);
// 获取弹出菜单的菜单对象
mMenu = mPopupMenu.getMenu();
// 从指定的菜单资源 ID 中加载菜单内容到菜单对象中
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
// 为按钮设置点击监听器,当按钮被点击时显示弹出菜单
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
}
});
}
/**
*
*
* @param listener
*/
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
// 检查弹出菜单是否为空
if (mPopupMenu != null) {
// 设置弹出菜单的菜单项点击监听器
mPopupMenu.setOnMenuItemClickListener(listener);
}
}
/**
* ID
*
* @param id ID
* @return null
*/
public MenuItem findItem(int id) {
// 在菜单对象中查找指定 ID 的菜单项
return mMenu.findItem(id);
}
/**
*
*
* @param title
*/
public void setTitle(CharSequence title) {
// 设置按钮的文本为传入的标题文本
mButton.setText(title);
}
}

@ -1,132 +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.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* FoldersListAdapter CursorAdapter
*
*/
public class FoldersListAdapter extends CursorAdapter {
// 定义查询数据库时需要投影的列,即需要从数据库中获取的列
public static final String [] PROJECTION = {
NoteColumns.ID, // 文件夹的 ID 列
NoteColumns.SNIPPET // 文件夹的名称列
};
// 定义投影列在数组中的索引位置,方便后续使用
public static final int ID_COLUMN = 0; // ID 列的索引
public static final int NAME_COLUMN = 1; // 名称列的索引
/**
* FoldersListAdapter
*
* @param context 访
* @param c
*/
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
/**
*
*
* @param context 访
* @param cursor
* @param parent
* @return FolderListItem
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context);
}
/**
*
*
* @param view
* @param context 访
* @param cursor
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
// 检查视图是否为 FolderListItem 类型
if (view instanceof FolderListItem) {
// 获取文件夹名称,如果文件夹的 ID 是根文件夹的 ID则显示特定的字符串
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
// 调用 FolderListItem 的 bind 方法将文件夹名称绑定到视图上
((FolderListItem) view).bind(folderName);
}
}
/**
*
*
* @param context 访
* @param position
* @return
*/
public String getFolderName(Context context, int position) {
// 获取指定位置的游标
Cursor cursor = (Cursor) getItem(position);
// 获取文件夹名称,如果文件夹的 ID 是根文件夹的 ID则显示特定的字符串
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
/**
* FolderListItem 线
*/
private class FolderListItem extends LinearLayout {
private TextView mName; // 用于显示文件夹名称的文本视图
/**
* FolderListItem
*
* @param context 访
*/
public FolderListItem(Context context) {
super(context);
// 加载文件夹列表项的布局文件
inflate(context, R.layout.folder_list_item, this);
// 获取布局文件中的文本视图
mName = (TextView) findViewById(R.id.tv_folder_name);
}
/**
*
*
* @param name
*/
public void bind(String name) {
mName.setText(name);
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,317 +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.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
/**
* NoteEditText EditText
*
*/
public class NoteEditText extends EditText {
// 日志标签,用于在日志中标识该类的信息
private static final String TAG = "NoteEditText";
// 该编辑文本框的索引,用于在列表中定位
private int mIndex;
// 删除操作前的选择起始位置
private int mSelectionStartBeforeDelete;
// 电话链接的协议前缀
private static final String SCHEME_TEL = "tel:" ;
// HTTP 链接的协议前缀
private static final String SCHEME_HTTP = "http:" ;
// 邮件链接的协议前缀
private static final String SCHEME_EMAIL = "mailto:" ;
// 存储协议前缀和对应的菜单项资源 ID 的映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
// 初始化映射,将电话协议前缀映射到对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
// 初始化映射,将 HTTP 协议前缀映射到对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
// 初始化映射,将邮件协议前缀映射到对应的菜单项资源 ID
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* OnTextViewChangeListener
*
*/
public interface OnTextViewChangeListener {
/**
*
* @param index
* @param text
*/
void onEditTextDelete(int index, String text);
/**
*
* @param index
* @param text
*/
void onEditTextEnter(int index, String text);
/**
*
* @param index
* @param hasText
*/
void onTextChange(int index, boolean hasText);
}
// 文本变化监听器实例
private OnTextViewChangeListener mOnTextViewChangeListener;
/**
* 使 NoteEditText
* @param context
*/
public NoteEditText(Context context) {
super(context, null);
// 初始化索引为 0
mIndex = 0;
}
/**
*
* @param index
*/
public void setIndex(int index) {
mIndex = index;
}
/**
*
* @param listener OnTextViewChangeListener
*/
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
/**
* 使 NoteEditText
* @param context
* @param attrs
*/
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
/**
* 使 NoteEditText
* @param context
* @param attrs
* @param defStyle
*/
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
/**
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取触摸点的 x 坐标
int x = (int) event.getX();
// 获取触摸点的 y 坐标
int y = (int) event.getY();
// 减去左内边距
x -= getTotalPaddingLeft();
// 减去上内边距
y -= getTotalPaddingTop();
// 加上水平滚动距离
x += getScrollX();
// 加上垂直滚动距离
y += getScrollY();
// 获取文本布局对象
Layout layout = getLayout();
// 根据垂直位置获取所在行
int line = layout.getLineForVertical(y);
// 根据行和水平位置获取偏移量
int off = layout.getOffsetForHorizontal(line, x);
// 设置光标位置
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event);
}
/**
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
// 如果有监听器,不处理回车键事件,交由监听器处理
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
// 记录删除操作前的选择起始位置
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
return super.onKeyDown(keyCode, event);
}
/**
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
// 当删除操作从文本开头开始且索引不为 0 时,调用删除回调方法
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
// 记录日志,提示监听器未设置
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
// 获取当前选择的起始位置
int selectionStart = getSelectionStart();
// 获取光标之后的文本内容
String text = getText().subSequence(selectionStart, length()).toString();
// 设置文本为光标之前的内容
setText(getText().subSequence(0, selectionStart));
// 调用回车回调方法
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
// 记录日志,提示监听器未设置
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event);
}
/**
*
* @param focused
* @param direction
* @param previouslyFocusedRect
*/
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 失去焦点且文本为空时,调用文本变化回调方法,标记为无文本
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 其他情况,调用文本变化回调方法,标记为有文本
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
/**
*
* @param menu
*/
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
// 获取选择的起始位置
int selStart = getSelectionStart();
// 获取选择的结束位置
int selEnd = getSelectionEnd();
// 获取选择范围的最小值
int min = Math.min(selStart, selEnd);
// 获取选择范围的最大值
int max = Math.max(selStart, selEnd);
// 获取选择范围内的所有 URLSpan 对象
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
// 如果只有一个 URLSpan 对象
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
// 根据链接协议前缀获取对应的菜单项资源 ID
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
// 如果没有匹配的协议前缀,使用默认的菜单项资源 ID
defaultResId = R.string.note_link_other;
}
// 添加菜单项,并设置点击监听器
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// 点击菜单项时,触发链接的点击事件
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}

@ -1,360 +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.database.Cursor;
import android.text.TextUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
/**
* NoteItemData
* Cursor
*/
public class NoteItemData {
// 定义从数据库查询笔记数据时使用的列名数组
static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 笔记的唯一标识符
NoteColumns.ALERTED_DATE, // 提醒日期
NoteColumns.BG_COLOR_ID, // 背景颜色 ID
NoteColumns.CREATED_DATE, // 创建日期
NoteColumns.HAS_ATTACHMENT, // 是否有附件
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.NOTES_COUNT, // 笔记数量
NoteColumns.PARENT_ID, // 父文件夹 ID
NoteColumns.SNIPPET, // 笔记摘要
NoteColumns.TYPE, // 笔记类型
NoteColumns.WIDGET_ID, // 小部件 ID
NoteColumns.WIDGET_TYPE, // 小部件类型
};
// 定义每个列在 PROJECTION 数组中的索引,方便从 Cursor 中获取数据
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
// 笔记的各种属性
private long mId; // 笔记的唯一标识符
private long mAlertDate; // 提醒日期
private int mBgColorId; // 背景颜色 ID
private long mCreatedDate; // 创建日期
private boolean mHasAttachment; // 是否有附件
private long mModifiedDate; // 修改日期
private int mNotesCount; // 笔记数量
private long mParentId; // 父文件夹 ID
private String mSnippet; // 笔记摘要
private int mType; // 笔记类型
private int mWidgetId; // 小部件 ID
private int mWidgetType; // 小部件类型
private String mName; // 联系人姓名
private String mPhoneNumber; // 联系人电话号码
// 笔记在列表中的位置相关标志
private boolean mIsLastItem; // 是否是列表中的最后一项
private boolean mIsFirstItem; // 是否是列表中的第一项
private boolean mIsOnlyOneItem; // 是否是列表中唯一的一项
private boolean mIsOneNoteFollowingFolder; // 是否是文件夹后面的单条笔记
private boolean mIsMultiNotesFollowingFolder; // 是否是文件夹后面的多条笔记
/**
* Cursor
*
* @param context
* @param cursor Cursor
*/
public NoteItemData(Context context, Cursor cursor) {
// 从 Cursor 中获取笔记的各种属性
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN);
// 移除笔记摘要中的特定标签
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPhoneNumber = "";
// 如果笔记的父文件夹是通话记录文件夹,则获取通话号码和联系人姓名
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = "";
}
// 检查笔记在列表中的位置
checkPostion(cursor);
}
/**
*
*
* @param cursor Cursor
*/
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
// 如果笔记是普通笔记且不是列表中的第一项
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
// 如果前一项是文件夹或系统项
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true;
}
}
// 将 Cursor 移动回原来的位置
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
/**
*
*
* @return true false
*/
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
/**
*
*
* @return true false
*/
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
/**
*
*
* @return true false
*/
public boolean isLast() {
return mIsLastItem;
}
/**
*
*
* @return
*/
public String getCallName() {
return mName;
}
/**
*
*
* @return true false
*/
public boolean isFirst() {
return mIsFirstItem;
}
/**
*
*
* @return true false
*/
public boolean isSingle() {
return mIsOnlyOneItem;
}
/**
*
*
* @return
*/
public long getId() {
return mId;
}
/**
*
*
* @return
*/
public long getAlertDate() {
return mAlertDate;
}
/**
*
*
* @return
*/
public long getCreatedDate() {
return mCreatedDate;
}
/**
*
*
* @return true false
*/
public boolean hasAttachment() {
return mHasAttachment;
}
/**
*
*
* @return
*/
public long getModifiedDate() {
return mModifiedDate;
}
/**
* ID
*
* @return ID
*/
public int getBgColorId() {
return mBgColorId;
}
/**
* ID
*
* @return ID
*/
public long getParentId() {
return mParentId;
}
/**
*
*
* @return
*/
public int getNotesCount() {
return mNotesCount;
}
/**
* ID
*
* @return ID
*/
public long getFolderId () {
return mParentId;
}
/**
*
*
* @return
*/
public int getType() {
return mType;
}
/**
*
*
* @return
*/
public int getWidgetType() {
return mWidgetType;
}
/**
* ID
*
* @return ID
*/
public int getWidgetId() {
return mWidgetId;
}
/**
*
*
* @return
*/
public String getSnippet() {
return mSnippet;
}
/**
*
*
* @return true false
*/
public boolean hasAlert() {
return (mAlertDate > 0);
}
/**
*
*
* @return true false
*/
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
/**
* Cursor
*
* @param cursor Cursor
* @return
*/
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
}

File diff suppressed because it is too large Load Diff

@ -1,312 +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.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";
// 上下文对象,用于获取资源和执行与 UI 相关的操作
private Context mContext;
// 用于存储每个笔记项的选择状态,键为笔记项的位置,值为是否选中的布尔值
private HashMap<Integer, Boolean> mSelectedIndex;
// 笔记的总数
private int mNotesCount;
// 是否处于选择模式的标志
private boolean mChoiceMode;
/**
* ID
*/
public static class AppWidgetAttribute {
// 小部件的 ID
public int widgetId;
// 小部件的类型
public int widgetType;
};
/**
*
* @param context
*/
public NotesListAdapter(Context context) {
// 调用父类的构造函数,传入上下文和初始的 Cursor 为 null
super(context, null);
// 初始化选择状态的 HashMap
mSelectedIndex = new HashMap<Integer, Boolean>();
// 保存上下文对象
mContext = context;
// 初始化笔记总数为 0
mNotesCount = 0;
}
/**
*
* @param context
* @param cursor
* @param parent
* @return NotesListItem
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context);
}
/**
*
* @param view
* @param context
* @param cursor
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
// 检查视图是否为 NotesListItem 类型
if (view instanceof NotesListItem) {
// 创建一个 NoteItemData 对象,用于封装笔记数据
NoteItemData itemData = new NoteItemData(context, cursor);
// 调用 NotesListItem 的 bind 方法,将数据绑定到视图上,并传入选择模式和当前项的选择状态
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
}
}
/**
*
* @param position
* @param checked
*/
public void setCheckedItem(final int position, final boolean checked) {
// 将指定位置的选择状态保存到 HashMap 中
mSelectedIndex.put(position, checked);
// 通知适配器数据发生变化,刷新视图
notifyDataSetChanged();
}
/**
*
* @return true false
*/
public boolean isInChoiceMode() {
return mChoiceMode;
}
/**
*
* @param mode
*/
public void setChoiceMode(boolean mode) {
// 清空选择状态的 HashMap
mSelectedIndex.clear();
// 更新选择模式标志
mChoiceMode = mode;
}
/**
*
* @param checked
*/
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
* @return ID HashSet
*/
public HashSet<Long> getSelectedItemIds() {
// 创建一个 HashSet 用于存储选中笔记项的 ID
HashSet<Long> itemSet = new HashSet<Long>();
// 遍历选择状态的 HashMap
for (Integer position : mSelectedIndex.keySet()) {
// 检查当前位置的笔记项是否被选中
if (mSelectedIndex.get(position) == true) {
// 获取当前位置的笔记项的 ID
Long id = getItemId(position);
// 检查 ID 是否为根文件夹的 ID如果是则记录日志否则将 ID 添加到 HashSet 中
if (id == Notes.ID_ROOT_FOLDER) {
Log.d(TAG, "Wrong item id, should not happen");
} else {
itemSet.add(id);
}
}
}
return itemSet;
}
/**
*
* @return HashSet
*/
public HashSet<AppWidgetAttribute> getSelectedWidget() {
// 创建一个 HashSet 用于存储选中小部件的属性
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
// 遍历选择状态的 HashMap
for (Integer position : mSelectedIndex.keySet()) {
// 检查当前位置的笔记项是否被选中
if (mSelectedIndex.get(position) == true) {
// 获取当前位置的数据库游标
Cursor c = (Cursor) getItem(position);
// 检查游标是否有效
if (c != null) {
// 创建一个 AppWidgetAttribute 对象,用于存储小部件的属性
AppWidgetAttribute widget = new AppWidgetAttribute();
// 创建一个 NoteItemData 对象,用于封装笔记数据
NoteItemData item = new NoteItemData(mContext, c);
// 获取小部件的 ID 并保存到 AppWidgetAttribute 对象中
widget.widgetId = item.getWidgetId();
// 获取小部件的类型并保存到 AppWidgetAttribute 对象中
widget.widgetType = item.getWidgetType();
// 将 AppWidgetAttribute 对象添加到 HashSet 中
itemSet.add(widget);
/**
*
*/
} else {
// 记录游标无效的错误日志
Log.e(TAG, "Invalid cursor");
return null;
}
}
}
return itemSet;
}
/**
*
* @return
*/
public int getSelectedCount() {
// 获取选择状态的 HashMap 中的所有值
Collection<Boolean> values = mSelectedIndex.values();
// 检查值集合是否为空
if (null == values) {
return 0;
}
// 创建一个迭代器,用于遍历值集合
Iterator<Boolean> iter = values.iterator();
// 初始化选中笔记项的数量为 0
int count = 0;
// 遍历值集合
while (iter.hasNext()) {
// 检查当前值是否为 true如果是则增加选中笔记项的数量
if (true == iter.next()) {
count++;
}
}
return count;
}
/**
*
* @return true false
*/
public boolean isAllSelected() {
// 获取选中的笔记项的数量
int checkedCount = getSelectedCount();
// 检查选中的笔记项数量是否不为 0 且等于笔记总数
return (checkedCount != 0 && checkedCount == mNotesCount);
}
/**
*
* @param position
* @return true false
*/
public boolean isSelectedItem(final int position) {
// 检查指定位置的选择状态是否为 null如果是则返回 false否则返回该位置的选择状态
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position);
}
/**
*
*/
@Override
protected void onContentChanged() {
// 调用父类的方法
super.onContentChanged();
// 重新计算笔记总数
calcNotesCount();
}
/**
* 使
* @param cursor
*/
@Override
public void changeCursor(Cursor cursor) {
// 调用父类的方法
super.changeCursor(cursor);
// 重新计算笔记总数
calcNotesCount();
}
/**
*
*/
private void calcNotesCount() {
// 初始化笔记总数为 0
mNotesCount = 0;
// 遍历所有笔记项
for (int i = 0; i < getCount(); i++) {
// 获取当前位置的数据库游标
Cursor c = (Cursor) getItem(i);
// 检查游标是否有效
if (c != null) {
// 检查当前笔记项的类型是否为普通笔记,如果是则增加笔记总数
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
}
} else {
// 记录游标无效的错误日志
Log.e(TAG, "Invalid cursor");
return;
}
}
}
}

@ -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.
*/
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;
/**
* NotesListItem LinearLayout
*
*/
public class NotesListItem extends LinearLayout {
// 用于显示提醒图标的 ImageView
private ImageView mAlert;
// 用于显示标题的 TextView
private TextView mTitle;
// 用于显示修改时间的 TextView
private TextView mTime;
// 用于显示通话记录中联系人姓名的 TextView
private TextView mCallName;
// 存储当前笔记项数据的对象
private NoteItemData mItemData;
// 用于选择笔记项的 CheckBox
private CheckBox mCheckBox;
/**
* NotesListItem
*
* @param context
*/
public NotesListItem(Context context) {
super(context);
// 将 note_item 布局文件填充到当前视图中
inflate(context, R.layout.note_item, this);
// 通过 findViewById 方法获取布局中的各个视图组件
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
/**
*
*
*
* @param context
* @param data
* @param choiceMode
* @param checked
*/
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 如果处于选择模式且笔记类型为普通笔记
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
// 显示 CheckBox 并设置其选中状态
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
} else {
// 否则隐藏 CheckBox
mCheckBox.setVisibility(View.GONE);
}
// 保存当前笔记项数据
mItemData = data;
// 如果笔记项的 ID 为通话记录文件夹的 ID
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
// 隐藏通话记录联系人姓名的 TextView
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);
}
// 如果笔记项的父 ID 为通话记录文件夹的 ID
else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
// 显示通话记录联系人姓名的 TextView 并设置其文本
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 {
// 隐藏通话记录联系人姓名的 TextView
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);
}
/**
*
*
*
* @param data
*/
private void setBackground(NoteItemData data) {
// 获取笔记项的背景颜色 ID
int id = data.getBgColorId();
// 如果笔记类型为普通笔记
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());
}
}
/**
*
*
* @return
*/
public NoteItemData getItemData() {
return mItemData;
}
}

@ -1,530 +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.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
* NotesPreferenceActivity
*
*/
public class NotesPreferenceActivity extends PreferenceActivity {
// 存储笔记设置的共享偏好名称
public static final String PREFERENCE_NAME = "notes_preferences";
// 存储同步账户名称的偏好键
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
// 存储最后一次同步时间的偏好键
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
// 存储设置背景颜色随机显示的偏好键
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
// 同步账户偏好类别键
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
// 账户权限过滤器键
private static final String AUTHORITIES_FILTER_KEY = "authorities";
// 同步账户偏好类别
private PreferenceCategory mAccountCategory;
// 用于接收 GTaskSyncService 广播的接收器
private GTaskReceiver mReceiver;
// 原始的 Google 账户数组
private Account[] mOriAccounts;
// 标记是否添加了新账户
private boolean mHasAddedAccount;
/**
*
* @param icicle
*/
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// 设置应用图标可用于导航返回
getActionBar().setDisplayHomeAsUpEnabled(true);
// 从 XML 文件中加载偏好设置
addPreferencesFromResource(R.xml.preferences);
// 查找同步账户偏好类别
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
// 创建 GTask 广播接收器
mReceiver = new GTaskReceiver();
// 创建意图过滤器,监听 GTaskSyncService 的广播
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
// 注册广播接收器
registerReceiver(mReceiver, filter);
// 初始化原始账户数组
mOriAccounts = null;
// 加载设置头部视图
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
// 将头部视图添加到列表视图中
getListView().addHeaderView(header, null, true);
}
/**
* UI
*/
@Override
protected void onResume() {
super.onResume();
// 如果添加了新账户,自动设置同步账户
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
// 刷新 UI
refreshUI();
}
/**
* 广
*/
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
/**
*
*/
private void loadAccountPreference() {
// 清空同步账户偏好类别中的所有偏好
mAccountCategory.removeAll();
// 创建一个新的偏好项
Preference accountPref = new Preference(this);
// 获取当前的默认同步账户名称
final String defaultAccount = getSyncAccountName(this);
// 设置偏好项的标题
accountPref.setTitle(getString(R.string.preferences_account_title));
// 设置偏好项的摘要
accountPref.setSummary(getString(R.string.preferences_account_summary));
// 设置偏好项的点击监听器
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// 如果没有设置同步账户,显示选择账户的对话框
showSelectAccountAlertDialog();
} else {
// 如果已经设置了同步账户,显示更改账户的确认对话框
showChangeAccountConfirmAlertDialog();
}
} else {
// 如果正在同步,显示提示信息
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
// 将偏好项添加到同步账户偏好类别中
mAccountCategory.addPreference(accountPref);
}
/**
*
*/
private void loadSyncButton() {
// 查找同步按钮
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
// 查找最后一次同步时间的文本视图
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 根据同步状态设置按钮状态和文本
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 取消同步操作
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 立即开始同步操作
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
// 只有在设置了同步账户时,同步按钮才可用
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// 根据同步状态设置最后一次同步时间的显示
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/**
* UI
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/**
*
*/
private void showSelectAccountAlertDialog() {
// 创建一个 AlertDialog 构建器
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 加载自定义标题视图
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
// 查找标题文本视图
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
// 设置标题文本
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
// 查找副标题文本视图
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
// 设置副标题文本
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
// 设置自定义标题
dialogBuilder.setCustomTitle(titleView);
// 设置确定按钮为空
dialogBuilder.setPositiveButton(null, null);
// 获取所有 Google 账户
Account[] accounts = getGoogleAccounts();
// 获取当前的默认同步账户名称
String defAccount = getSyncAccountName(this);
// 记录原始账户数组
mOriAccounts = accounts;
// 标记未添加新账户
mHasAddedAccount = false;
if (accounts.length > 0) {
// 创建账户名称数组
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
// 设置单选列表项
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 设置选择的账户为同步账户
setSyncAccount(itemMapping[which].toString());
// 关闭对话框
dialog.dismiss();
// 刷新 UI
refreshUI();
}
});
}
// 加载添加账户的视图
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
// 设置对话框的视图
dialogBuilder.setView(addAccountView);
// 显示对话框
final AlertDialog dialog = dialogBuilder.show();
// 设置添加账户视图的点击监听器
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 标记添加了新账户
mHasAddedAccount = true;
// 创建添加账户的意图
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
// 设置账户权限过滤器
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
// 启动添加账户的活动
startActivityForResult(intent, -1);
// 关闭对话框
dialog.dismiss();
}
});
}
/**
*
*/
private void showChangeAccountConfirmAlertDialog() {
// 创建一个 AlertDialog 构建器
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 加载自定义标题视图
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
// 查找标题文本视图
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
// 设置标题文本,显示当前的同步账户名称
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
// 查找副标题文本视图
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
// 设置副标题文本,显示更改账户的警告信息
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
// 设置自定义标题
dialogBuilder.setCustomTitle(titleView);
// 创建菜单项数组
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
// 设置菜单项的点击监听器
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
// 选择更改账户,显示选择账户的对话框
showSelectAccountAlertDialog();
} else if (which == 1) {
// 选择移除账户,移除同步账户并刷新 UI
removeSyncAccount();
refreshUI();
}
}
});
// 显示对话框
dialogBuilder.show();
}
/**
* Google
* @return Google
*/
private Account[] getGoogleAccounts() {
// 获取账户管理器
AccountManager accountManager = AccountManager.get(this);
// 获取所有 Google 账户
return accountManager.getAccountsByType("com.google");
}
/**
*
* @param account
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
// 获取共享偏好设置
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
// 获取偏好编辑器
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
// 保存同步账户名称
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
// 清空同步账户名称
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
// 提交更改
editor.commit();
// 清空最后一次同步时间
setLastSyncTime(this, 0);
// 清理本地与 GTask 相关的信息
new Thread(new Runnable() {
public void run() {
// 创建内容值对象
ContentValues values = new ContentValues();
// 清空 GTask ID
values.put(NoteColumns.GTASK_ID, "");
// 清空同步 ID
values.put(NoteColumns.SYNC_ID, 0);
// 更新笔记内容提供者中的数据
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
// 显示设置成功的提示信息
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
}
}
// 移除同步账户相关设置
private void removeSyncAccount() {
// 获取指定名称的 SharedPreferences 对象,使用私有模式,确保只有本应用可以访问这些设置
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
// 获取 SharedPreferences 的编辑器,用于修改存储的设置
SharedPreferences.Editor editor = settings.edit();
// 检查 SharedPreferences 中是否包含同步账户名称的键
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
// 如果包含,则从编辑器中移除该键值对
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
// 检查 SharedPreferences 中是否包含最后同步时间的键
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
// 如果包含,则从编辑器中移除该键值对
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
// 提交编辑器中的修改,将更改保存到 SharedPreferences 中
editor.commit();
// 清理本地与 Google 任务GTask相关的信息由于数据库操作可能会阻塞主线程因此在新线程中执行
new Thread(new Runnable() {
public void run() {
// 创建一个 ContentValues 对象,用于存储要更新的数据
ContentValues values = new ContentValues();
// 将 GTask ID 字段设置为空字符串
values.put(NoteColumns.GTASK_ID, "");
// 将同步 ID 字段设置为 0
values.put(NoteColumns.SYNC_ID, 0);
// 使用 ContentResolver 更新 Notes 内容提供者中的笔记数据,将 GTask ID 和同步 ID 字段清空
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
// 获取当前设置的同步账户名称
public static String getSyncAccountName(Context context) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 从 SharedPreferences 中获取同步账户名称,如果不存在则返回空字符串
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
// 设置最后一次同步的时间
public static void setLastSyncTime(Context context, long time) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 获取 SharedPreferences 的编辑器,用于修改存储的设置
SharedPreferences.Editor editor = settings.edit();
// 将最后同步时间存储到编辑器中
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
// 提交编辑器中的修改,将更改保存到 SharedPreferences 中
editor.commit();
}
// 获取最后一次同步的时间
public static long getLastSyncTime(Context context) {
// 获取指定名称的 SharedPreferences 对象,使用私有模式
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
// 从 SharedPreferences 中获取最后同步时间,如果不存在则返回 0
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
// 自定义广播接收器,用于接收 GTask 同步服务的广播
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 刷新界面,更新账户设置和同步按钮状态等信息
refreshUI();
// 检查广播意图中是否包含正在同步的标志,并且标志为 true
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
// 查找用于显示同步状态的 TextView 控件
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// 设置同步状态文本为广播意图中携带的进度消息
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
// 处理选项菜单的点击事件
public boolean onOptionsItemSelected(MenuItem item) {
// 根据点击的菜单项 ID 进行不同的处理
switch (item.getItemId()) {
// 如果点击的是应用图标(即返回按钮)
case android.R.id.home:
// 创建一个意图,用于启动 NotesListActivity
Intent intent = new Intent(this, NotesListActivity.class);
// 添加标志,确保启动的 NotesListActivity 位于任务栈的顶部,并清除之前的所有活动
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 启动 NotesListActivity
startActivity(intent);
// 返回 true 表示事件已处理
return true;
// 如果点击的是其他菜单项
default:
// 返回 false 表示事件未处理
return false;
}
}
Loading…
Cancel
Save