Compare commits

...

36 Commits
main ... main

Author SHA1 Message Date
pecvhqap2 b734e09a57 小米便签model文件和tool文件的java代码注释
9 months ago
JUNNE_TOPIC1 4c7a83ac56 1
9 months ago
pfmv8qroc a7514fd31a ADD file via upload
9 months ago
pfmv8qroc 545fd51e61 ADD file via upload
9 months ago
pfmv8qroc 5b251112e1 ADD file via upload
9 months ago
pfmv8qroc 69a4688a13 ADD file via upload
9 months ago
pfmv8qroc 949e60f4fe Add Widgit
9 months ago
pfmv8qroc 329b70a032 ADD file via upload
9 months ago
pfmv8qroc 54b536ae42 ADD file via upload
9 months ago
pfmv8qroc eb347661be ADD file via upload
9 months ago
pfmv8qroc 1b340dcced ADD file via upload
9 months ago
pfmv8qroc dc6946302a ADD file via upload
9 months ago
pfmv8qroc c91ff7f95b ADD file via upload
9 months ago
pecvhqap2 13123e9736 UI代码注释
9 months ago
hyx cde1784e9e ui文件
9 months ago
hyx c479f855de jave/ui索引下文件的代码注释
9 months ago
JUNNE_TOPIC1 87902d310e Merge branch 'main' of https://bdgit.educoder.net/pecvhqap2/pyx_gitpractice
10 months ago
JUNNE_TOPIC1 655c4ed246 TEST.txt
10 months ago
JUNNE_TOPIC1 38ee4714e8 TEST.txt
10 months ago
19972076849 691f5fc4db 123
10 months ago
19972076849 33b675a676 123
10 months ago
19972076849 2b2e019433 123
10 months ago
19972076849 8fb02d8075 123
10 months ago
19972076849 c4f7a0dca3 Merge branch 'main' of https://bdgit.educoder.net/pecvhqap2/pyx_gitpractice
10 months ago
19972076849 d41c439ac7 123
10 months ago
19972076849 7647e17a3a 12
10 months ago
19972076849 230320cefd 12
10 months ago
19972076849 400b27e01f 12
10 months ago
19972076849 df3cd719df 12
10 months ago
19972076849 f39af77a8e 第二次
10 months ago
JUNNE 2cc49f8b4f TEST.txt
10 months ago
JUNNE 11951d0a14 Merge branch 'main' of https://bdgit.educoder.net/pecvhqap2/pyx_gitpractice
10 months ago
19972076849 0d6e37c571 7
10 months ago
19972076849 28e5faf126 as
10 months ago
19972076849 15b5fa188d 1
10 months ago
JUNNE 91b50b920a TEST.txt修改
10 months ago

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

@ -0,0 +1 @@
213wqe1231111

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

@ -0,0 +1,295 @@
/*
* 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;
}
}

@ -0,0 +1,467 @@
//*
* 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;
}
}

@ -0,0 +1,270 @@
/*
* 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;
}
}
}

@ -0,0 +1,390 @@
/*
* 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;
}
}
}

@ -0,0 +1,523 @@
/*
* 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);
}
}

@ -0,0 +1,226 @@
/*
* 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();
}

@ -0,0 +1,77 @@
/*
* 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;
}
}

@ -0,0 +1,76 @@
/*
* 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;
}
}

@ -0,0 +1 @@
undefined

@ -0,0 +1,344 @@
/*
* 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;
}
}

@ -0,0 +1,113 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
public class GTaskStringUtils {
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";
}

@ -0,0 +1 @@
assdjgyuguyhgnb6

@ -0,0 +1,229 @@
/*
* 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;
}
}
}

@ -0,0 +1,91 @@
/*
* 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();
}
}
}

@ -0,0 +1,42 @@
/*
* 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);
}
}

@ -0,0 +1,412 @@
/*
* 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());
}
}
}

@ -0,0 +1,146 @@
/*
* 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());
}
}
}

@ -0,0 +1,101 @@
/*
* 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);
}
}

@ -0,0 +1,132 @@
/*
* 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

@ -0,0 +1,317 @@
/*
* 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);
}
}

@ -0,0 +1,360 @@
/*
* 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

@ -0,0 +1,312 @@
/*
* 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;
}
}
}
}

@ -0,0 +1,207 @@
/*
* 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;
}
}

@ -0,0 +1,530 @@
/*
* 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