main
18660474160 2 years ago
commit 130693abec

@ -1,344 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class BackupUtils {
private static final String TAG = "BackupUtils";
// Singleton stuff
private static BackupUtils sInstance;
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success
public static final int STATE_SUCCESS = 4;
private TextExport mTextExport;
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
public int exportToText() {
return mTextExport.exportToText();
}
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
private static class TextExport {
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
private static final int NOTE_COLUMN_ID = 0;
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
private static final int NOTE_COLUMN_SNIPPET = 2;
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
private static final int DATA_COLUMN_CALL_DATE = 2;
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
private final String [] TEXT_FORMAT;
private static final int FORMAT_FOLDER_NAME = 0;
private static final int FORMAT_NOTE_DATE = 1;
private static final int FORMAT_NOTE_CONTENT = 2;
private Context mContext;
private String mFileName;
private String mFileDirectory;
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// Print phone number
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// First export folder and its notes
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// Print folder's name
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
File file = new File(sb.toString());
try {
if (!filedir.exists()) {
filedir.mkdir();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

@ -1,181 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.content.Context;
import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
public class ResourceParser {
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
public static final int BG_DEFAULT_COLOR = YELLOW;
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
public static class NoteBgResources {
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[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
};
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
public static class WidgetBgResources {
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,
};
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
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
};
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
};
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];
}
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}

@ -0,0 +1,348 @@
/*
* 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";
// 单例相关
private static BackupUtils sInstance;
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
*
*/
// 当前SD卡未挂载
public static final int STATE_SD_CARD_UNMOUNTED = 0;
// 备份文件不存在
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// 数据格式不正确,可能被其他程序更改
public static final int STATE_DATA_DESTROYED = 2;
// 运行时异常导致还原或备份失败
public static final int STATE_SYSTEM_ERROR = 3;
// 备份或还原成功
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];
}
/**
*
*/
public int exportToText() {
// 导出文件的逻辑
}
}
private void exportFolderToText(String folderId, PrintStream ps) {
// 查询属于该文件夹的笔记
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// 打印笔记的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于该笔记的数据
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* ID
*/
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)) {
// 打印电话号码
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// 打印通话日期
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// 打印通话附件位置
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// 在每个笔记之间打印一个分隔线
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
*
*/
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUNTED;
}
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出文件夹及其笔记
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// 打印文件夹名称
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// 导出根文件夹中的笔记
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于该笔记的数据
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* {@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 export 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;
}
/**
*
*/
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;
}
}

@ -37,6 +37,13 @@ import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";
/**
*
* @param resolver ContentResolver
* @param ids ID
* @return
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
@ -72,6 +79,13 @@ public class DataUtils {
return false;
}
/**
*
* @param resolver ContentResolver
* @param id ID
* @param srcFolderId ID
* @param desFolderId ID
*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
@ -80,8 +94,14 @@ public class DataUtils {
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
/**
*
* @param resolver ContentResolver
* @param ids ID
* @param folderId ID
* @return
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, long folderId) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
@ -112,10 +132,13 @@ public class DataUtils {
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*
* @param resolver ContentResolver
* @return
*/
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
// 查询文件夹数量
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)},
@ -127,7 +150,7 @@ public class DataUtils {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
Log.e(TAG, "获取文件夹数量失败:" + e.toString());
} finally {
cursor.close();
}
@ -137,6 +160,7 @@ public class DataUtils {
}
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,
@ -154,6 +178,7 @@ public class DataUtils {
}
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
// 检查笔记是否存在于数据库中
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
@ -168,6 +193,7 @@ public class DataUtils {
}
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
// 检查数据是否存在于数据库中
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
@ -182,6 +208,7 @@ public class DataUtils {
}
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 +
@ -198,6 +225,7 @@ public class DataUtils {
}
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 + "=?",
@ -225,6 +253,7 @@ public class DataUtils {
}
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 根据笔记ID获取通话号码
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
@ -235,15 +264,16 @@ public class DataUtils {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
Log.e(TAG, "获取通话号码失败:" + e.toString());
} finally {
cursor.close();
}
}
return "";
return "";
}
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 根据电话号码和通话日期获取笔记ID
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
@ -256,7 +286,7 @@ public class DataUtils {
try {
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
Log.e(TAG, "获取通话笔记ID失败" + e.toString());
}
}
cursor.close();
@ -265,6 +295,7 @@ public class DataUtils {
}
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 根据笔记ID获取摘要
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
@ -279,10 +310,11 @@ public class DataUtils {
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
throw new IllegalArgumentException("找不到ID为 " + noteId + " 的笔记");
}
public static String getFormattedSnippet(String snippet) {
// 获取格式化后的摘要
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');

@ -18,96 +18,51 @@ 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";
public final static String GTASK_JSON_ACTION_ID = "action_id"; // 动作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"; // 创建者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"; // 当前列表ID
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; // 默认列表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"; // 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"; // 列表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"; // 新ID
public final static String GTASK_JSON_NOTES = "notes"; // 笔记
public final static String GTASK_JSON_PARENT_ID = "parent_id"; // 父级ID
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; // 上一个同级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]"; // MIUI文件夹前缀
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"; // 元数据GTask ID
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,209 @@
/*
* 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
};
// 获取笔记背景资源
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
// 获取笔记标题背景资源
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
// 获取默认背景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
};
// 普通背景资源数组
privatefinal 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
};
// 获取笔记项第一个背景资源
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
// 获取笔记项最后一个背景资源
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
// 获取笔记项单独背景资源
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
// 获取笔记项普通背景资源
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[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,
};
// 获取2x小部件背景资源
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
};
// 获取4x小部件背景资源
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
};
// 获取文本外观资源
public static int getTexAppearanceResource(int id) {
/**
* HACKME: ID
* ID BG_DEFAULT_FONT_SIZE
*/
if (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
// 获取资源数组大小
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}

@ -1,158 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@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);
}
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
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 {
finish();
}
}
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
private void playAlarmSound() {
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) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
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);
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
break;
default:
break;
}
}
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
}
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
}

@ -1,485 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import 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 NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl();
onDateTimeChanged();
}
};
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
} else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
onDateTimeChanged();
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
} else {
mIsAm = true;
updateAmPmControl();
}
}
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
}
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
updateAmPmControl();
onDateTimeChanged();
}
};
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
super.setEnabled(enabled);
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
}
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* Set the current date
*
* @param date The current date in millis
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
/**
* Get current year
*
* @return The current year
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
return;
}
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
updateAmPmControl();
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
}
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
}
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
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) {
cal.add(Calendar.DAY_OF_YEAR, 1);
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) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}

@ -0,0 +1,185 @@
/*
* 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的公开类该类继承自Activity类。在Android中Activity是应用组件用于与用户交互。
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
// 定义一个私有长整型变量mNoteId可能用于存储与提醒相关的笔记ID。
private long mNoteId;
// 定义一个私有字符串变量mSnippet可能用于存储与提醒相关的小片段信息如文本、音频等
private String mSnippet;
// 定义一个静态常量SNIPPET_PREW_MAX_LEN其值为60可能用于限制mSnippet的最大长度。
private static final int SNIPPET_PREW_MAX_LEN = 60;
// 定义一个私有MediaPlayer对象mPlayer可能用于播放音频/音乐。
MediaPlayer mPlayer;
@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);
}
Intent intent = getIntent();
try {
// 从Intent中获取与提醒相关的笔记ID并存储到mNoteId变量中。
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 从数据工具类中获取与mNoteId对应的提醒片段并存储到mSnippet变量中。
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
// 如果mSnippet的长度超过SNIPPET_PREW_MAX_LEN则截取前SNIPPET_PREW_MAX_LEN个字符并附加字符串"..."。
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 {
finish();
}
}
// 检查屏幕是否亮着。
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) {
// 如果闹钟铃声受铃声模式影响则设置MediaPlayer的音频流为silentModeStreams。
mPlayer.setAudioStreamType(silentModeStreams);
} else {
// 否则设置MediaPlayer的音频流为闹钟铃声流。
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 设置MediaPlayer的数据源为闹钟铃声的URI。
mPlayer.setDataSource(this, url);
// 准备MediaPlayer的播放器。
mPlayer.prepare();
// 设置MediaPlayer循环播放。
mPlayer.setLooping(true);
// 开始播放音频。
mPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
//TODO Auto-generated catch block
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);
}
// 对话框按钮点击事件的处理。
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
// 点击取消按钮,打开与提醒相关的笔记编辑界面。
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
break;
default:
break;
}
}
// 对话框关闭事件的处理。
public void onDismiss(DialogInterface dialog) {
// 停止播放音频。
stopAlarmSound();
// 结束当前Activity。
finish();
}
// 停止播放音频。
private void stopAlarmSound() {
if (mPlayer != null) {
// 停止MediaPlayer的播放。
mPlayer.stop();
// 释放MediaPlayer的资源。
mPlayer.release();
mPlayer = null;
}
}
}

@ -27,36 +27,44 @@ import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class AlarmInitReceiver extends BroadcastReceiver {
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
// 定义一个字符串数组PROJECTION用于指定查询提醒相关信息时返回的列。
private static final String[] PROJECTION = new String[]{
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
// 定义一些常量用于指定PROJECTION数组中的列索引。
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前时间的毫秒值。
long currentDate = System.currentTimeMillis();
// 使用ContentResolver查询数据库获取所有提醒日期大于当前时间的提醒记录。
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
new String[]{String.valueOf(currentDate)},
null);
if (c != null) {
if (c.moveToFirst()) {
do {
// 获取提醒日期的毫秒值。
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个新的Intent指向AlarmReceiver类。
Intent sender = new Intent(context, AlarmReceiver.class);
// 设置Intent的数据为提醒记录的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);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
// 获取系统的闹钟管理器。
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// 设置闹钟当到达提醒日期时触发PendingIntent发送广播。
alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
c.close();

@ -23,8 +23,11 @@ import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 创建一个新的Intent指向AlarmAlertActivity类。
intent.setClass(context, AlarmAlertActivity.class);
// 添加FLAG_ACTIVITY_NEW_TASK标志以在一个新的任务中启动活动。
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动活动。
context.startActivity(intent);
}
}
}

@ -0,0 +1,635 @@
/*
* 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 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; // 24小时制小时选择器的最小值
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; // 24小时制小时选择器的最大值
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; // 12小时制小时选择器的最小值
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; // 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; // 是否为24小时制
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; // 是否启用日期时间选择器
private boolean mInitialising; // 是否正在初始化
private OnDateTimeChangedListener mOnDateTimeChangedListener; // 日期时间变化监听器
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当日期选择器的值发生变化时调用
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); // 更新日期
updateDateControl(); // 更新日期控件
onDateTimeChanged(); // 通知日期时间发生变化
}
};
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当小时选择器的值发生变化时调用
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) { // 如果是12小时制
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
// 从上午切换到下午
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
// 从下午切换到上午
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
// 切换上午/下午
mIsAm = !mIsAm;
updateAmPmControl(); // 更新上午/下午控件
}
} else { // 如果是24小时制
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
// 从最后一小时切换到第一小时
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
// 从第一小时切换到最后一小时
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour); // 更新小时
onDateTimeChanged(); // 通知日期时间发生变化
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当分钟选择器的值发生变化时调用
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
} else {
mIsAm = true;
updateAmPmControl();
}
}
mDate.set(Calendar.MINUTE, newVal); // 更新分钟
onDateTimeChanged(); // 通知日期时间发生变化
}
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// 当上午/下午选择器的值发生变化时调用
mIsAm = !mIsAm; // 切换上午/下午
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
updateAmPmControl(); // 更新上午/下午控件
onDateTimeChanged(); // 通知日期时间发生变化
}
};
}
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
public DateTimePicker(Context context) {
// 使用当前时间初始化 DateTimePicker
this(context, System.currentTimeMillis());
}
public DateTimePicker(Context context, long date) {
// 使用指定的时间初始化 DateTimePicker并根据上下文的时间格式确定是否为24小时制
this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
// 初始化日期、时间和 AM/PM 的选择器
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 更新控件的初始状态
updateDateControl();
updateHourControl();
updateAmPmControl();
// 设置是否为24小时制
set24HourView(is24HourView);
// 设置为当前时间
setCurrentDate(date);
setEnabled(isEnabled());
// 设置内容描述
mInitialising = false;
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
super.setEnabled(enabled);
// 设置是否可用
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
}
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
*
*
* @return
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
*
*
* @param date
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
*
*
* @param year
* @param month
* @param dayOfMonth
* @param hourOfDay
* @param minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
/**
*
*
* @return
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
*
*
* @param year
*/
public void setCurrentYear(int year) {
mDate.set(Calendar.YEAR, year);
updateDateControl();
}
/**
*
*
* @return
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
*
*
* @param month
*/
public void setCurrentMonth(int month) {
mDate.set(Calendar.MONTH, month);
updateDateControl();
}
/**
*
*
* @return
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
*
*
*@param dayOfMonth
*/
public void setCurrentDay(int dayOfMonth) {
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
}
/**
*
*
* @return
*/
public int getCurrentHour() {
int hourOfDay = mDate.get(Calendar.HOUR_OF_DAY);
if (mIsAm && hourOfDay >= HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
} else if (!mIsAm && hourOfDay < HOURS_IN_HALF_DAY) {
hourOfDay += HOURS_IN_HALF_DAY;
}
return hourOfDay;
}
/**
*
*
* @param hour
*/
public void setCurrentHour(int hour) {
if (hour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
hour -= HOURS_IN_HALF_DAY;
} else {
mIsAm = true;
}
mDate.set(Calendar.HOUR_OF_DAY, hour);
updateHourControl();
updateAmPmControl();
}
/**
*
*
* @return
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
*
*
* @param minute
*/
public void setCurrentMinute(int minute) {
mDate.set(Calendar.MINUTE, minute);
updateMinuteControl();
}
/**
*
*
* @param year
*/
public void setCurrentYear(int year) {
// 如果不是初始化状态并且传入的年份与当前年份相同,则不做任何操作
if (!mInitialising && year == getCurrentYear()) {
return;
}
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* 00
*
* @return
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* 00
*
* @param month
*/
public void setCurrentMonth(int month) {
// 如果不是初始化状态并且传入的月份与当前月份相同,则不做任何操作
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
*
*
* @param dayOfMonth
*/
public void setCurrentDay(int dayOfMonth) {
// 如果不是初始化状态并且传入的日期与当前日期相同,则不做任何操作
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
/**
* 240~23
*
* @return
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
/**
* 12
*
* @return
*/
private int getCurrentHour() {
if (mIs24HourView) {
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
}
/**
* 240~23
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
// 如果不是初始化状态并且传入的小时与当前小时相同,则不做任何操作
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
updateAmPmControl();
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
}
/**
*
*
* @return
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
*
*
* @param minute
*/
public void setCurrentMinute(int minute) {
// 如果不是初始化状态并且传入的分钟与当前分钟相同,则不做任何操作
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
/**
* 24
*
* @return 24truefalse
*/
public boolean is24HourView() {
return mIs24HourView;
}
/**
* 24
*
* @param is24HourView true24falseAM/PM
*/
public void set24HourView(boolean is24HourView) {
//如果当前的显示模式24小时制或AM/PM制与要设置的模式相同则不做任何操作
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
}
/**
*
*
* @param callback
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
/**
*
*/
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
/**
*
*/
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
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) {
cal.add(Calendar.DAY_OF_YEAR, 1);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
}
/**
* AM/PM
*/
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}
/**
*
*/
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}

@ -35,7 +35,7 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker;
// 回调接口,用于在日期时间设置完成后执行一些操作
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
@ -63,15 +63,15 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
set24HourView(DateFormat.is24HourFormat(this.getContext()));
updateTitle(mDate.getTimeInMillis());
}
// 设置是否使用24小时制显示
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
// 设置回调接口
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
// 更新对话框标题,显示当前日期时间
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
@ -80,7 +80,7 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
// 确定按钮点击事件
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());

@ -35,9 +35,13 @@ public class DropdownMenu {
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
// 创建弹出菜单
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
// 设置按钮点击事件,显示弹出菜单
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
@ -45,16 +49,19 @@ public class DropdownMenu {
});
}
// 设置菜单项点击监听器
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
}
}
// 根据ID查找菜单项
public MenuItem findItem(int id) {
return mMenu.findItem(id);
}
// 设置按钮标题
public void setTitle(CharSequence title) {
mButton.setText(title);
}

@ -28,14 +28,13 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class FoldersListAdapter extends CursorAdapter {
public static final String [] PROJECTION = {
public static final String[] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};
public static final int ID_COLUMN = 0;
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
public FoldersListAdapter(Context context, Cursor c) {
@ -45,19 +44,23 @@ public class FoldersListAdapter extends CursorAdapter {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// 创建新的视图对象
return new FolderListItem(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
// 获取文件夹名称
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
// 绑定文件夹名称到视图
((FolderListItem) view).bind(folderName);
}
}
public String getFolderName(Context context, int position) {
// 获取指定位置的文件夹名称
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
@ -68,13 +71,14 @@ public class FoldersListAdapter extends CursorAdapter {
public FolderListItem(Context context) {
super(context);
// 填充布局文件
inflate(context, R.layout.folder_list_item, this);
mName = (TextView) findViewById(R.id.tv_folder_name);
}
public void bind(String name) {
// 设置文件夹名称到文本视图
mName.setText(name);
}
}
}

@ -71,7 +71,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//用于编辑和处理笔记的界面。它允许用户创建、编辑和保存笔记,并提供了一些功能,如发送笔记到桌面、保存笔记等。
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
private class HeadViewHolder {
@ -83,7 +83,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public ImageView ibSetBgColor;
}
// 背景颜色选择按钮与对应颜色值的映射关系
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
@ -92,7 +92,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
// 颜色值与背景颜色选择按钮的选中状态对应的映射关系
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
@ -101,7 +101,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
// 字体大小选择按钮与对应字体大小值的映射关系
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
@ -109,7 +109,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
// 字体大小值与字体大小选择按钮的选中状态对应的映射关系
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
@ -118,36 +118,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
}
private static final String TAG = "NoteEditActivity";
private HeadViewHolder mNoteHeaderHolder;
private View mHeadViewPanel;
private View mNoteBgColorSelector;
private View mFontSizeSelector;
private EditText mNoteEditor;
private View mNoteEditorPanel;
private WorkingNote mWorkingNote;
private SharedPreferences mSharedPrefs;
private int mFontSizeId;
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
public static final String TAG_CHECKED = String.valueOf('\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
private LinearLayout mEditTextList;
private String mUserQuery;
private Pattern mPattern;
private HeadViewHolder mNoteHeaderHolder; // 笔记标题视图持有者
private View mHeadViewPanel; // 笔记标题面板
private View mNoteBgColorSelector; // 笔记背景颜色选择器
private View mFontSizeSelector; // 字体大小选择器
private EditText mNoteEditor; // 笔记编辑器
private View mNoteEditorPanel; // 笔记编辑面板
private WorkingNote mWorkingNote; // 当前编辑的笔记
private SharedPreferences mSharedPrefs; // SharedPreferences 对象,用于存储应用程序的设置信息
private int mFontSizeId; // 字体大小ID
private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; // 字体大小设置的键名
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; // 快捷图标标题的最大长度
public static final String TAG_CHECKED = String.valueOf('\u221A'); // 选中标记
public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); // 未选中标记
private LinearLayout mEditTextList; // 笔记文本列表
private String mUserQuery; // 用户的查询字符串
private Pattern mPattern; // 正则表达式模式
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -162,15 +149,16 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
/**
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
// 恢复被销毁的活动状态
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
// 初始化活动状态
if (!initActivityState(intent)) {
finish();
return;
@ -180,30 +168,29 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private boolean initActivityState(Intent intent) {
/**
* If the user specified the {@link Intent#ACTION_VIEW} but not provided with id,
* then jump to the NotesListActivity
*/
// 根据提供的Intent初始化活动状态
// 检查Intent的操作是否为ACTION_VIEW
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* Starting from the searched result
*/
// 检查Intent是否包含EXTRA_DATA_KEY搜索结果
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// 检查笔记是否存在于笔记数据库中
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
// 如果笔记不存在跳转到NotesListActivity并结束当前活动
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
// 使用指定的noteId加载工作笔记
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
@ -211,11 +198,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return false;
}
}
// 设置软键盘模式(隐藏和调整大小)
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// New note
// 新建笔记
// 从Intent的额外数据中获取文件夹ID、小部件ID、小部件类型和背景资源ID
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
@ -224,7 +214,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// Parse call-record note
// 解析电话记录笔记
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
@ -232,8 +222,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
// 检查具有指定的phoneNumber和callDate的笔记是否已经存在
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
// 如果笔记存在,加载工作笔记
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
@ -241,19 +233,22 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return false;
}
} else {
// 如果笔记不存在,创建空白笔记,并转换为电话记录笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
// 创建空白笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
// 设置软键盘模式(调整大小和可见)
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
@ -261,28 +256,41 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
/**
* (Intent)initActivityState()
*
* @param savedInstanceState
*/
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
/**
*
*/
private void initNoteScreen() {
// 设置笔记编辑器的文本外观
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
// 检查工作笔记的检查清单模式,并根据模式切换到相应的编辑模式
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
// 设置笔记编辑器的文本内容,并根据用户查询结果高亮显示文本
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
// 将光标移动到文本末尾
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// 隐藏所有背景选择器中的选中指示器
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// 设置笔记头部视图和笔记编辑器面板的背景
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 更新修改日期文本视图
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
@ -292,21 +300,28 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
* is not ready
*/
// 显示提醒的头部视图
showAlertHeader();
}
private void showAlertHeader() {
// 检查工作笔记是否设置了提醒
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
// 检查当前时间是否超过了提醒时间
if (time > mWorkingNote.getAlertDate()) {
// 如果超过了提醒时间,将提醒日期文本设置为"提醒已过期"
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
// 如果未超过提醒时间,将提醒日期文本设置为相对时间字符串(如"2分钟前"
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
// 显示提醒日期文本和提醒图标
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
// 如果未设置提醒,隐藏提醒日期文本和提醒图标
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
@ -315,6 +330,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 初始化活动状态
initActivityState(intent);
}
@ -322,27 +338,30 @@ public class NoteEditActivity extends Activity implements OnClickListener,
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* For new note without note id, we should firstly save it to
* generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note
* IDID
* ID
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 将工作笔记的ID保存到状态Bundle中
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 检查背景颜色选择器是否可见,并且触摸事件不在选择器范围内
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
// 隐藏背景颜色选择器
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
// 检查字体大小选择器是否可见,并且触摸事件不在选择器范围内
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
// 隐藏字体大小选择器
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
@ -350,10 +369,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private boolean inRangeOfView(View view, MotionEvent ev) {
// 获取视图在屏幕上的坐标
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
// 检查触摸事件的坐标是否在视图的范围内
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
@ -364,6 +385,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private void initResources() {
// 初始化资源对象和视图控件
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
@ -374,12 +396,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
// 设置背景颜色选择器按钮的点击事件监听器
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
mFontSizeSelector = findViewById(R.id.font_size_selector);
// 设置字体大小选择器按钮的点击事件监听器
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
@ -387,9 +411,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* 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}
* HACKME: ID
* ID BG_DEFAULT_FONT_SIZE
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
@ -400,14 +423,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
@Override
protected void onPause() {
super.onPause();
// 保存笔记数据
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
// 清除设置状态
clearSettingState();
}
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 根据工作笔记的小部件类型,设置相应的广播接收器类
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
@ -416,101 +442,141 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Unspported widget type");
return;
}
// 将工作笔记的小部件ID放入意图中
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
// 发送广播更新小部件
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
int id = v.getId();
// 点击设置背景颜色按钮
if (id == R.id.btn_set_bg_color) {
// 显示背景颜色选择器
mNoteBgColorSelector.setVisibility(View.VISIBLE);
// 显示当前工作笔记的背景颜色选择项
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
// 点击背景颜色选择器的选项按钮
} else if (sBgSelectorBtnsMap.containsKey(id)) {
// 隐藏当前工作笔记的背景颜色选择项
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
// 设置工作笔记的背景颜色ID
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
// 隐藏背景颜色选择器
mNoteBgColorSelector.setVisibility(View.GONE);
// 点击字体大小选择器的选项按钮
} else if (sFontSizeBtnsMap.containsKey(id)) {
// 隐藏当前字体大小选择项
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
// 设置字体大小ID
mFontSizeId = sFontSizeBtnsMap.get(id);
// 将字体大小ID存储到共享首选项中
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
// 显示当前字体大小选择项
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
// 如果工作笔记处于检查列表模式,切换到列表模式
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
} // 否则,设置编辑器的文本外观为选定的字体大小
else {
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
// 隐藏字体大小选择器
mFontSizeSelector.setVisibility(View.GONE);
}
}
@Override
public void onBackPressed() {
if(clearSettingState()) {
// 清除设置状态
if (clearSettingState()) {
return;
}
// 保存笔记数据
saveNote();
super.onBackPressed();
}
private boolean clearSettingState() {
// 如果背景颜色选择器可见,隐藏它
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
}
// 如果字体大小选择器可见,隐藏它
else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return false;
}
public void onBackgroundColorChanged() {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
// 显示当前工作笔记的背景颜色选择项
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE);
// 设置笔记编辑区域的背景颜色
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 设置标题栏的背景颜色
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 如果Activity已经被销毁直接返回
if (isFinishing()) {
return true;
}
// 清除设置状态
clearSettingState();
// 清空菜单
menu.clear();
// 根据工作笔记所属文件夹的不同,加载不同的菜单布局文件
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
// 根据当前工作笔记的检查列表模式,设置菜单项的标题
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
// 如果工作笔记设置了闹钟提醒,隐藏提醒菜单项;否则,隐藏删除提醒菜单项
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// 点击新建笔记菜单项
case R.id.menu_new_note:
createNewNote();
break;
// 点击删除笔记菜单项
case R.id.menu_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
@ -526,24 +592,35 @@ public class NoteEditActivity extends Activity implements OnClickListener,
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
// 点击字体大小菜单项
case R.id.menu_font_size:
// 显示字体大小选择器
mFontSizeSelector.setVisibility(View.VISIBLE);
// 显示当前字体大小选择项
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
break;
// 点击切换列表模式菜单项
case R.id.menu_list_mode:
// 切换工作笔记的检查列表模式
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
break;
// 点击分享菜单项
case R.id.menu_share:
// 获取工作笔记的文本内容
getWorkingText();
// 发送到其他应用程序
sendTo(this, mWorkingNote.getContent());
break;
// 点击发送到桌面菜单项
case R.id.menu_send_to_desktop:
sendToDesktop();
break;
// 点击设置提醒菜单项
case R.id.menu_alert:
setReminder();
break;
// 点击删除提醒菜单项
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break;
@ -554,20 +631,22 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private void setReminder() {
// 创建日期时间选择对话框
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true);
// 设置工作笔记的提醒日期
mWorkingNote.setAlertDate(date, true);
}
});
d.show();
}
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
* {@link Intent#ACTION_SEND}{@text/plain}
*/
private void sendTo(Context context, String info) {
// 创建发送文本的意图
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
@ -575,10 +654,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private void createNewNote() {
// Firstly, save current editing notes
// 首先保存当前编辑的笔记
saveNote();
// For safety, start a new NoteEditActivity
// 为了安全起见,启动一个新的NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
@ -611,11 +690,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
public void onClockAlertChanged(long date, boolean set) {
/**
* User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first
*
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
@ -626,23 +703,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if(!set) {
if (!set) {
// 取消闹钟
alarmManager.cancel(pendingIntent);
} else {
// 设置闹钟
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
}
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
* ID
*/
Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}
public void onWidgetChanged() {
// 更新小部件
updateWidget();
}
@ -653,13 +730,15 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
for (int i = index + 1; i < childCount; i++) {
// 更新索引
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
// 移除指定位置的EditText
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
if(index == 0) {
if (index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
@ -674,18 +753,20 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*
*/
if(index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
if (index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundary, should not happen");
}
// 创建新的EditText并插入到指定位置
View view = getListItem(text, index);
mEditTextList.addView(view, index);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.requestFocus();
edit.setSelection(0);
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
// 更新索引
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
@ -696,14 +777,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
String[] items = text.split("\n");
int index = 0;
for (String item : items) {
if(!TextUtils.isEmpty(item)) {
if (!TextUtils.isEmpty(item)) {
// 创建列表项并添加到EditText列表
mEditTextList.addView(getListItem(item, index));
index++;
}
}
// 添加一个空的列表项
mEditTextList.addView(getListItem("", index));
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
// 切换到列表模式隐藏编辑器显示EditText列表
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
}
@ -715,6 +799,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Matcher m = mPattern.matcher(fullText);
int start = 0;
while (m.find(start)) {
// 设置匹配到的文本背景色高亮
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
@ -724,22 +809,29 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return spannable;
}
private View getListItem(String item, int index) {
// 创建列表项视图
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
// 获取列表项中的NoteEditText
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 获取列表项中的CheckBox
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// 设置NoteEditText的文本为删除线效果
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
// 移除NoteEditText的删除线效果
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
// 处理已选中和未选中的列表项
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
@ -750,9 +842,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
// 设置NoteEditText的监听器、索引和文本内容
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
@ -761,7 +855,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Wrong index, should not happen");
return;
}
if(hasText) {
// 根据文本内容的有无显示或隐藏CheckBox
if (hasText) {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
@ -770,18 +865,19 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) {
// 切换到列表模式
switchToListMode(mNoteEditor.getText().toString());
} else {
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));
// 从工作笔记中移除未选中标记
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", ""));
}
// 设置编辑器文本并高亮查询结果
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE);
}
}
private boolean getWorkingText() {
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
@ -791,47 +887,44 @@ public class NoteEditActivity extends Activity implements OnClickListener,
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
// 将选中的列表项添加到StringBuilder中并添加已选中标记
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
// 将未选中的列表项添加到StringBuilder中并添加未选中标记
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
// 将StringBuilder中的文本设置为工作笔记的内容
mWorkingNote.setWorkingText(sb.toString());
} else {
// 将编辑器中的文本设置为工作笔记的内容
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return hasChecked;
}
private boolean saveNote() {
// 获取工作笔记的内容
getWorkingText();
// 保存工作笔记
boolean saved = mWorkingNote.saveNote();
if (saved) {
/**
* There are two modes from List view to edit view, open one note,
* create/edit a node. Opening node requires to the original
* position in the list when back from edit view, while creating a
* new node requires to the top of the list. This code
* {@link #RESULT_OK} is used to identify the create/edit state
*/
// 设置返回结果为RESULT_OK用于标识创建/编辑状态
setResult(RESULT_OK);
}
return saved;
}
private void sendToDesktop() {
/**
* Before send message to home, we should make sure that current
* editing note is exists in databases. So, for new note, firstly
* save it
*/
// 如果当前编辑的笔记不存在于数据库中,则先保存笔记
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
// 创建发送到桌面的Intent
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
@ -843,22 +936,22 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
sender.putExtra("duplicate", true);
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
// 显示发送到桌面成功的提示
showToast(R.string.info_note_enter_desktop);
// 发送广播将快捷方式添加到桌面
sendBroadcast(sender);
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
// 如果笔记内容为空,则提示用户输入内容
Log.e(TAG, "Send to desktop error");
showToast(R.string.error_note_empty_for_send_to_desktop);
}
}
private String makeShortcutIconTitle(String content) {
// 去除内容中的已选中和未选中标记,作为快捷方式的标题
content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, "");
// 如果标题超过最大长度限制,则截断字符串
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}
@ -868,6 +961,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
private void showToast(int resId, int duration) {
// 显示Toast提示
Toast.makeText(this, resId, duration).show();
}
}

@ -47,30 +47,30 @@ public class NoteEditText extends EditText {
private static final String SCHEME_EMAIL = "mailto:" ;
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
// 将不同的链接协议和对应的操作资源ID存储在映射表中
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*
*/
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
* {@link KeyEvent#KEYCODE_DEL}
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
* {@link KeyEvent#KEYCODE_ENTER}
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*
*/
void onTextChange(int index, boolean hasText);
}
@ -96,24 +96,28 @@ public class NoteEditText extends EditText {
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
```java
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;
}
@ -126,6 +130,7 @@ public class NoteEditText extends EditText {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
// 如果监听器存在则回调onEditTextEnter方法
return false;
}
break;
@ -144,21 +149,23 @@ public class NoteEditText extends EditText {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
// 当文本为空且按下删除键时回调onEditTextDelete方法
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
Log.d(TAG, "OnTextViewChangeListener未设置");
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
// 当按下回车键时获取光标位置之后的文本并回调onEditTextEnter方法
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");
Log.d(TAG, "OnTextViewChangeListener未设置");
}
break;
default:
@ -171,8 +178,10 @@ public class NoteEditText extends EditText {
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 当失去焦点且文本为空时回调onTextChange方法参数为false
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 当获得焦点或文本不为空时回调onTextChange方法参数为true
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
@ -193,6 +202,7 @@ public class NoteEditText extends EditText {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
// 根据链接协议获取对应的操作资源ID
defaultResId = sSchemaActionResMap.get(schema);
break;
}
@ -202,10 +212,11 @@ public class NoteEditText extends EditText {
defaultResId = R.string.note_link_other;
}
// 添加操作菜单项,并设置点击监听器
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
// 执行跳转到新的意图
urls[0].onClick(NoteEditText.this);
return true;
}

@ -28,18 +28,18 @@ import net.micode.notes.tool.DataUtils;
public class NoteItemData {
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE,
NoteColumns.HAS_ATTACHMENT,
NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT,
NoteColumns.PARENT_ID,
NoteColumns.SNIPPET,
NoteColumns.TYPE,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.ID, // 笔记的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, // 小组件类型
};
private static final int ID_COLUMN = 0;
@ -55,45 +55,47 @@ public class NoteItemData {
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;
private long mCreatedDate;
private boolean mHasAttachment;
private long mModifiedDate;
private int mNotesCount;
private long mParentId;
private String mSnippet;
private int mType;
private int mWidgetId;
private int mWidgetType;
private String mName;
private String mPhoneNumber;
private boolean mIsLastItem;
private boolean mIsFirstItem;
private boolean mIsOnlyOneItem;
private boolean mIsOneNoteFollowingFolder;
private boolean mIsMultiNotesFollowingFolder;
private long mId; // 笔记ID
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; // 是否有多个笔记项跟随在文件夹后面
public NoteItemData(Context context, 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);
mId = cursor.getLong(ID_COLUMN); // 获取笔记ID
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); // 获取提醒日期
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); // 获取背景颜色ID
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); // 获取父笔记ID
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);
NoteEditActivity.TAG_UNCHECKED, ""); // 处理摘要中的特殊标记
mType = cursor.getInt(TYPE_COLUMN); // 获取笔记类型
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); // 获取小组件ID
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); // 获取小组件类型
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
//根据父笔记ID判断是否为通话记录类型的笔记并获取关联的电话号码。
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
@ -109,12 +111,17 @@ public class NoteItemData {
checkPostion(cursor);
}
/**
*
*
* @param cursor
*/
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
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();
@ -122,9 +129,9 @@ public class NoteItemData {
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
mIsMultiNotesFollowingFolder = true; // 有多个笔记项跟随在文件夹后面
} else {
mIsOneNoteFollowingFolder = true;
mIsOneNoteFollowingFolder = true; // 有一个笔记项跟随在文件夹后面
}
}
if (!cursor.moveToNext()) {
@ -218,7 +225,13 @@ public class NoteItemData {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
/**
*
*
* @param cursor
* @return
*/
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
}
}

@ -1,21 +1,10 @@
/*
* 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.
Edit by yanjingyu
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -79,62 +68,63 @@ import java.io.InputStreamReader;
import java.util.HashSet;
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
//常量;用于标识查询的令牌
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
private static final int FOLDER_LIST_QUERY_TOKEN = 1;
// 常量;用于标识上下文菜单中的不同选项
private static final int MENU_FOLDER_DELETE = 0;
private static final int MENU_FOLDER_VIEW = 1;
private static final int MENU_FOLDER_CHANGE_NAME = 2;
//SharedPreferences 中的键,用于标识是否添加了介绍
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
//枚举类型,表示列表的编辑状态
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
//当前的列表编辑状态。
private ListEditState mState;
//后台查询处理程序,用于在后台执行数据库查询
private BackgroundQueryHandler mBackgroundQueryHandler;
//笔记列表的适配器
private NotesListAdapter mNotesListAdapter;
//显示笔记列表的 ListView
private ListView mNotesListView;
//添加新笔记的按钮
private Button mAddNewNote;
//标志位,用于判断是否进行滑动的分发
private boolean mDispatch;
//Y 轴的坐标,用于计算滑动距离
private int mOriginY;
private int mDispatchY;
//显示标题的 TextView。
private TextView mTitleBar;
//当前文件夹的 ID
private long mCurrentFolderId;
//用于访问应用的数据
private ContentResolver mContentResolver;
//上下文操作栏的回调接口
private ModeCallback mModeCallBack;
//用于在日志中标识该 Activity 的字符串
private static final String TAG = "NotesListActivity";
//笔记列表滑动速率
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
//当前焦点笔记数据项
private NoteItemData mFocusNoteDataItem;
//用于构建数据库查询的选择条件
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
//用于标识打开或新建节点的请求码
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
//Activity 创建时调用的方法,用于初始化界面和资源
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -146,7 +136,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*/
setAppInfoFromRawRes();
}
//处理从其他 Activity 返回的结果,主要处理打开或新建节点后更新笔记列表
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK
@ -156,7 +146,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
super.onActivityResult(requestCode, resultCode, data);
}
}
//从应用的 res/raw 目录下的 R.raw.introduction 文件中读取介绍信息,并将其保存为一个笔记
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
@ -202,13 +192,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
//在 Activity 可见时调用,启动异步查询笔记列表的任务
@Override
protected void onStart() {
super.onStart();
startAsyncNotesListQuery();
}
//初始化资源,包括获取 ContentResolver、初始化笔记列表视图等
private void initResources() {
mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
@ -230,12 +220,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mState = ListEditState.NOTE_LIST;
mModeCallBack = new ModeCallback();
}
//实现了 ListView.MultiChoiceModeListener 和 OnMenuItemClickListener 接口,用于处理上下文操作栏的回调以及菜单项的点击事件
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu;
private ActionMode mActionMode;
private MenuItem mMoveMenu;
//在创建上下文操作栏时调用,通过 getMenuInflater().inflate(R.menu.note_list_options, menu) 加载上下文操作栏的菜单布局;
//设置菜单项 R.id.delete 的点击监听器为 this即当前类的实例;根据当前焦点笔记项的父文件夹 ID 和用户创建的文件夹数量,决定是否显示移动菜单项,
//并设置移动菜单项的点击监听器为 this;
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
@ -247,6 +239,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
//初始化 mActionMode 为当前的上下文操作栏,设置笔记列表适配器的选择模式为 true禁用笔记列表的长按事件隐藏新建笔记按钮
mActionMode = mode;
mNotesListAdapter.setChoiceMode(true);
mNotesListView.setLongClickable(false);
@ -255,6 +248,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
//创建一个自定义视图 mDropDownMenu用于显示下拉菜单并设置下拉菜单的点击监听器
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
@ -268,7 +262,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
});
return true;
}
//更新下拉菜单的显示文本和选择状态
//获取已选择的笔记数量,并根据数量更新下拉菜单的标题;根据是否全部选择来设置下拉菜单中全选项的状态和文本
private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu
@ -285,23 +280,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
//在上下文操作栏准备就绪时调用。这个方法在创建上下文操作栏后被调用,通常用于设置一些操作前的准备工作。在这里,返回 false 表示没有额外的准备工作需要执行。
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
//在点击上下文操作栏的菜单项时调用。这个方法用于处理上下文操作栏菜单项的点击事件。在这里,返回 false 表示未处理该点击事件
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
//在销毁上下文操作栏时调用。这个方法用于执行一些清理工作。在这里,它将笔记列表适配器的选择模式设置为 false启用笔记列表的长按事件显示新建笔记按钮。
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE);
}
//结束上下文操作栏的显示。这个方法用于手动结束上下文操作栏,通常在完成一系列操作后调用
public void finishActionMode() {
mActionMode.finish();
}
@ -311,7 +306,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mNotesListAdapter.setCheckedItem(position, checked);
updateMenu();
}
//在点击下拉菜单项时调用。这个方法用于处理下拉菜单项的点击事件。具体操作包括删除选定的笔记或移动选定的笔记到其他文件夹。如果未选择任何笔记,则显示相应的提示信息。
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
@ -347,7 +342,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
private class NewNoteOnTouchListener implements OnTouchListener {
//该方法处理新建笔记按钮的触摸事件。当按下ACTION_DOWN通过计算坐标位置判断是否点击了按钮的透明部分如果是则将事件传递给列表视图。
//在移动ACTION_MOVE如果已经开始分派事件则将事件传递给列表视图。在其他情况下返回 false
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
@ -407,7 +403,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
};
//该方法启动异步查询以获取笔记列表。根据当前文件夹的不同,使用不同的查询条件。查询结果将通过 BackgroundQueryHandler 处理
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
@ -416,7 +412,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
String.valueOf(mCurrentFolderId)
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
//该类是 AsyncQueryHandler 的子类,用于在后台执行异步查询。在查询完成后,根据查询的目的(通过 token 区分),更新相应的 UI
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
@ -440,7 +436,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
//该方法显示一个对话框允许用户选择目标文件夹以进行移动操作。从数据库查询的文件夹列表通过适配器FoldersListAdapter传递给对话框。
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder);
@ -461,14 +457,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
});
builder.show();
}
//启动新建笔记的编辑界面。通过 Intent 设置动作为插入或编辑,并传递当前文件夹的 ID。此方法用于处理新建笔记按钮的点击事件
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
//异步执行批量删除选定的笔记。根据同步模式的不同,执行直接删除或将笔记移动到垃圾文件夹。在删除完成后,通过异步任务的结果更新相应的小部件
private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
@ -505,7 +501,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}.execute();
}
//删除指定文件夹及其内容。如果不是同步模式,直接删除文件夹及其包含的笔记。在同步模式下,将文件夹及其笔记移动到垃圾文件夹。删除完成后,同样会更新相关的小部件。
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
@ -532,14 +528,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
}
//打开指定笔记的编辑界面。创建一个 Intent 并传递笔记的 ID启动笔记编辑界面。
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
//打开指定文件夹。根据传入的 NoteItemData 对象,设置当前文件夹 ID然后异步查询该文件夹下的笔记列表。根据文件夹类型是否为通话记录文件夹
//更新界面状态,包括标题栏的显示和新建笔记按钮的可见性。
private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId();
startAsyncNotesListQuery();
@ -556,7 +553,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
mTitleBar.setVisibility(View.VISIBLE);
}
//处理按钮点击事件的回调方法。目前仅处理新建笔记按钮的点击事件,调用 createNewNote() 方法。
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_new_note:
@ -566,19 +563,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
break;
}
}
//显示软键盘。通过获取 InputMethodManager 的实例,强制显示软键盘
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
//隐藏软键盘。通过获取 InputMethodManager 的实例,隐藏软键盘,传入的 view 参数用于确定当前焦点。
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
//显示创建或修改文件夹的对话框。根据参数 create 决定是创建还是修改文件夹。对话框中包含一个编辑文本框,用户可以输入文件夹名称。
//点击确定按钮后,根据输入执行创建或修改文件夹的操作。在对话框中实现了文本输入变化的监听,以实时更新确定按钮的状态
private void showCreateOrModifyFolderDialog(final boolean create) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
@ -648,7 +646,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
// TODO Auto-generated method stub
}
//当文本内容发生变化时调用。在这里如果输入框中的文本内容为空就禁用确定按钮positive否则启用确定按钮。这是为了在用户输入内容时实时控制确定按钮的可点击状态
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
@ -656,14 +654,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
positive.setEnabled(true);
}
}
//在文本内容发生变化之后调用,这个方法在 onTextChanged 之后被调用。
afterTextChanged(Editable s):
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
//处理返回按钮的点击事件。根据当前的状态mState执行不同的操作。如果当前在子文件夹SUB_FOLDER或通话记录文件夹CALL_RECORD_FOLDER
//则返回到笔记列表。如果当前在笔记列表NOTE_LIST则执行默认的返回操作
@Override
public void onBackPressed() {
switch (mState) {
@ -687,7 +687,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
break;
}
}
//更新小部件,根据小部件的类型选择不同的 NoteWidgetProvider。
private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
@ -706,7 +706,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
//长按文件夹时的上下文菜单创建监听器,用于在上下文菜单中显示与文件夹相关的选项,如查看、删除和修改名称。
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) {
@ -717,7 +717,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
};
//上下文菜单关闭时调用,用于清除上下文菜单的创建监听器。
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
@ -725,7 +725,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
super.onContextMenuClosed(menu);
}
//处理上下文菜单项的选择事件,根据选择的菜单项执行相应的操作,如查看文件夹、删除文件夹或修改文件夹名称。
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
@ -759,7 +759,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true;
}
//在菜单显示之前调用用于动态设置菜单项。根据当前的状态mState显示相应的菜单项。
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
@ -777,7 +777,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
return true;
}
// 处理菜单项的选择事件,根据选择的菜单项执行相应的操作,如创建新文件夹、导出笔记到文本、同步或取消同步、打开设置、创建新笔记等
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -817,7 +817,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
return true;
}
//处理搜索请求,启动搜索活动
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
@ -832,7 +832,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
//导出笔记到文本文件。通过后台任务异步执行导出操作,根据导出的结果显示相应的提示对话框
@Override
protected void onPostExecute(Integer result) {
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
@ -865,19 +865,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute();
}
//检查是否处于同步模式。通过检查偏好设置中同步账户的名称来判断是否启用了同步
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
//启动偏好设置活动。如果有父活动,则使用父活动,否则使用当前活动启动偏好设置活动
private void startPreferenceActivity() {
Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1);
}
//列表项点击监听器处理列表项的点击事件。根据当前的状态mState和点击的项的类型执行不同的操作如打开文件夹或笔记
private class OnListItemClickListener implements OnItemClickListener {
//处理列表项的点击事件。根据当前的状态mState和点击的项的类型执行不同的操作如打开文件夹或笔记。
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
NoteItemData item = ((NotesListItem) view).getItemData();
@ -916,7 +916,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
//启动查询目标文件夹的异步查询。根据当前的状态mState构建查询查询除了垃圾箱和当前文件夹之外的所有文件夹并按修改日期降序排列
private void startQueryDestinationFolders() {
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection:
@ -934,7 +934,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
},
NoteColumns.MODIFIED_DATE + " DESC");
}
//处理列表项的长按事件。如果长按的是笔记项且不在选择模式下则启动上下文操作栏ActionMode并通过 mModeCallBack 处理项的选择状态。
//如果长按的是文件夹项则设置上下文菜单监听器mFolderOnCreateContextMenuListener
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData();

@ -1,17 +1,6 @@
/*
* 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.
Edit by yanjingyu
UI 使
*/
package net.micode.notes.ui;
@ -30,7 +19,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
//构造方法初始化适配器初始化上下文mContext选择模式标志mChoiceMode以及用于存储选中项索引的哈希映射mSelectedIndex
public class NotesListAdapter extends CursorAdapter {
private static final String TAG = "NotesListAdapter";
private Context mContext;
@ -49,12 +38,12 @@ public class NotesListAdapter extends CursorAdapter {
mContext = context;
mNotesCount = 0;
}
//用于创建新的笔记列表项视图。
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context);
}
//用于将数据绑定到笔记列表项视图。
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
@ -63,21 +52,21 @@ public class NotesListAdapter extends CursorAdapter {
isSelectedItem(cursor.getPosition()));
}
}
//设置指定位置的笔记项的选中状态,并通知适配器数据集发生变化。
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
//检查是否处于选择模式。
public boolean isInChoiceMode() {
return mChoiceMode;
}
//设置选择模式,清空选中项索引。
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear();
mChoiceMode = mode;
}
//选择或取消选择所有笔记项。
public void selectAll(boolean checked) {
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
@ -88,7 +77,7 @@ public class NotesListAdapter extends CursorAdapter {
}
}
}
//获取选中的笔记项的ID集合。
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
@ -104,7 +93,8 @@ public class NotesListAdapter extends CursorAdapter {
return itemSet;
}
//获取选中的笔记项中的小部件属性集合。对于每个选中的笔记项,它创建一个 AppWidgetAttribute 对象其中包含小部件的ID和类型。
//返回一个包含所有选中笔记项的小部件属性的 HashSet。
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
@ -127,7 +117,7 @@ public class NotesListAdapter extends CursorAdapter {
}
return itemSet;
}
//获取选中的笔记项的数量。
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
@ -142,31 +132,31 @@ public class NotesListAdapter extends CursorAdapter {
}
return count;
}
//检查是否所有笔记项都被选中。
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
}
//检查特定位置的笔记项是否被选中。
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position);
}
//当数据集发生更改时调用,用于重新计算笔记项的总数。
@Override
protected void onContentChanged() {
super.onContentChanged();
calcNotesCount();
}
//在更改光标时调用,用于重新计算笔记项的总数。
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
calcNotesCount();
}
//计算笔记项的总数。
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {

@ -1,19 +1,7 @@
/*
* 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.
Edit by yanjingyu
*/
package net.micode.notes.ui;
import android.content.Context;
@ -37,7 +25,7 @@ public class NotesListItem extends LinearLayout {
private TextView mCallName;
private NoteItemData mItemData;
private CheckBox mCheckBox;
//构造函数初始化视图,并通过 inflate 方法将布局文件 note_item.xml 载入该自定义视图。
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
@ -47,7 +35,7 @@ public class NotesListItem extends LinearLayout {
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
//该方法用于将数据绑定到视图上。它根据传入的数据项以及选择模式和选中状态来更新视图元素,例如标题、时间、提醒图标等。
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
@ -98,7 +86,7 @@ public class NotesListItem extends LinearLayout {
setBackground(data);
}
//该方法根据笔记项的类型和位置来设置背景。根据笔记项是文件夹还是笔记,以及其在列表中的位置,选择相应的背景资源。
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
@ -115,7 +103,7 @@ public class NotesListItem extends LinearLayout {
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
//获取当前 NotesListItem 对象的关联 NoteItemData。
public NoteItemData getItemData() {
return mItemData;
}

@ -1,17 +1,7 @@
/*
* 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.
Edit by yanjingyu
*/
package net.micode.notes.ui;
@ -68,7 +58,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
private Account[] mOriAccounts;
private boolean mHasAddedAccount;
//该类从资源文件 R.xml.preferences 中加载首选项,并设置了一些必要的界面元素,如显示返回按钮、注册广播接收器等。
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@ -87,7 +77,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
}
//通过调用 refreshUI 方法刷新用户界面,根据同步状态、同步按钮等动态更新设置界面。
@Override
protected void onResume() {
super.onResume();
@ -115,7 +105,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
refreshUI();
}
//注销了之前注册的广播接收器,确保在销毁活动时不再接收广播。
@Override
protected void onDestroy() {
if (mReceiver != null) {
@ -123,7 +113,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
super.onDestroy();
}
//负责加载并显示与帐户相关的首选项。它创建一个 Preference 对象,用于显示帐户的标题和摘要,并设置点击监听器以便在用户点击时触发相应的操作。
private void loadAccountPreference() {
mAccountCategory.removeAll();
@ -153,7 +143,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
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);
@ -192,12 +182,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
}
//用于刷新用户界面,它调用 loadAccountPreference 和 loadSyncButton 方法,以便动态更新设置界面的内容。
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
//显示选择账户的对话框。创建一个 AlertDialog 对象,包含了用户当前的账户列表以供选择,以及一个用于添加新账户的选项。
//用户可以从列表中选择现有账户,或点击添加账户以添加新账户。
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
@ -253,7 +244,8 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
});
}
//显示更改账户的确认对话框。该方法创建一个 AlertDialog 对象,显示当前账户信息,并提供更改账户、移除账户和取消操作的选项。
//用户可以选择更改账户,移除账户,或取消操作。
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
@ -282,12 +274,12 @@ public class NotesPreferenceActivity extends PreferenceActivity {
});
dialogBuilder.show();
}
//获取设备上的 Google 账户列表。通过调用 AccountManager.getAccountsByType("com.google") 获取。
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
//设置同步账户,接受一个账户名作为参数,将其保存到 SharedPreferences 中,并清除与同步相关的本地数据
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
@ -317,7 +309,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
Toast.LENGTH_SHORT).show();
}
}
//移除同步账户,从 SharedPreferences 中移除同步账户信息,并清除与同步相关的本地数据。
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
@ -339,13 +331,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}).start();
}
//获取当前同步账户的名称,从 SharedPreferences 中读取保存的同步账户信息。
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
//设置上次同步时间,将上次同步时间保存到 SharedPreferences 中。
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
@ -353,15 +345,15 @@ public class NotesPreferenceActivity extends PreferenceActivity {
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
//获取上次同步时间,从 SharedPreferences 中读取保存的上次同步时间信息。
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
//用于接收与 GTask 同步服务相关的广播。
private class GTaskReceiver extends BroadcastReceiver {
//它调用了 refreshUI 方法以刷新用户界面,同时根据广播中的同步状态更新同步状态的文本信息。
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
@ -373,7 +365,8 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
//一个处理选项菜单项选择事件的方法。当选择的菜单项的 ID 为 android.R.id.home即返回按钮它创建一个 Intent 对象,用于导航到 NotesListActivity 类,
//并清除了任务栈上的所有活动,确保返回到 NotesListActivity 时,它是任务栈的根活动。最后,通过调用 startActivity(intent) 启动该活动。
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:

@ -1,132 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.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;
public abstract class NoteWidgetProvider extends AppWidgetProvider {
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider";
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
ContentValues values = new ContentValues();
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
for (int i = 0; i < appWidgetIds.length; i++) {
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
new String[] { String.valueOf(appWidgetIds[i])});
}
}
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
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);
}
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
int bgId = ResourceParser.getDefaultBgId(context);
String snippet = "";
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_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 rv = new RemoteViews(context.getPackageName(), getLayoutId());
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* Generate the pending intent to start host for the widget
*/
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);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
protected abstract int getBgResourceId(int bgId);
protected abstract int getLayoutId();
protected abstract int getWidgetType();
}

@ -1,47 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
@Override
protected int getLayoutId() {
return R.layout.widget_2x;
}
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X;
}
}

@ -1,46 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
protected int getLayoutId() {
return R.layout.widget_4x;
}
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;
}
}

@ -0,0 +1,171 @@
/*
* 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;
//导入需要的Android库
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; // 导入Notes数据类该类包含所有关于笔记的数据结构和操作
import net.micode.notes.data.NoteColumns; // 导入NoteColumns数据类该类包含Note表的所有列名和类型
import net.micode.notes.tool.ResourceParser; // 导入ResourceParser工具类该类用于解析资源文件中的数据
import net.micode.notes.ui.NoteEditActivity; // 导入NoteEditActivity类该类是用于编辑笔记的Activity
import net.micode.notes.ui.NotesListActivity; // 导入NotesListActivity类该类是用于显示所有笔记的Activity
//声明一个抽象类NoteWidgetProvider继承自AppWidgetProvider
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义一个字符串数组PROJECTION包含NoteColumns的三个字段ID背景颜色ID和片段
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义三个对应的整型常量,用于后续方便使用
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
// 定义一个私有的静态常量TAG用于记录日志时标识来源
private static final String TAG = "NoteWidgetProvider";
// 重写AppWidgetProvider的onDeleted方法当widget被删除时调用
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
ContentValues values = new ContentValues(); // 创建一个ContentValues对象用于存储数据变更的键值对
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); // 将数据中的WIDGET_ID字段设置为无效值
for (int i = 0; i < appWidgetIds.length; i++) { // 遍历所有被删除的widget的id
context.getContentResolver().update(Notes.CONTENT_NOTE_URI, // 更新Note表中的数据将对应widget的数据的WIDGET_ID字段设置为无效值
values,
NoteColumns.WIDGET_ID + "=?", // 构建更新条件的SQL语句条件是WIDGET_ID等于当前遍历到的widget id
new String[] { String.valueOf(appWidgetIds[i])}); // 将当前遍历到的widget id转换为字符串作为SQL语句的参数
}
}
/// 定义一个私有的名为getNoteWidgetInfo的方法该方法接收两个参数一个上下文对象context和一个widgetId。这个方法可能用于从数据存储中获取特定widgetId的相关信息。
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
// 从上下文对象获取内容解析器它是一个用于访问底层数据存储如SQLite数据库的对象。
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
// 定义一个名为PROJECTION的变量这可能是一个用于过滤查询结果的字符串数组。
PROJECTION,
// 在查询中设置过滤条件。这里我们查找widgetId等于给定值并且parentId不等于特定值Notes.ID_TRASH_FOLER的记录。
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
// 创建一个字符串数组其中包含两个通过String.valueOf方法转换的参数值。第一个是widgetId第二个是Notes.ID_TRASH_FOLER。
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
// 在查询中指定排序方式。这里没有给出排序方式所以为null。
null);
}
// 定义一个受保护的名为update的方法该方法接收三个参数一个上下文对象context一个AppWidgetManager对象appWidgetManager和一个int数组appWidgetIds。这个方法可能用于更新应用小部件的信息。
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用update方法传入相同的参数但增加一个额外的参数即布尔值false。此方法可能用于执行特定条件下的更新操作。
update(context, appWidgetManager, appWidgetIds, false);
}
// 定义一个私有方法update该方法接收四个参数上下文对象contextAppWidgetManager对象appWidgetManagerint数组appWidgetIds以及一个布尔值privacyMode。此方法可能用于更新应用小部件的信息。
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历所有的appWidgetIds
for (int i = 0; i < appWidgetIds.length; i++) {
// 检查appWidgetIds[i]是否不为AppWidgetManager.INVALID_APPWIDGET_ID如果不是则跳过后续操作。AppWidgetManager.INVALID_APPWIDGET_ID表示无效的app部件ID。
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 调用ResourceParser的getDefaultBgId方法传入上下文对象context以获取默认的背景ID并将其赋值给变量bgId。
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化一个空字符串变量snippet。
String snippet = "";
// 创建一个新的意图Intent对象该意图将启动NoteEditActivity。设置FLAG_ACTIVITY_SINGLE_TOP标志位表示如果NoteEditActivity已经存在在堆栈顶则不会重新创建它。
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 将appWidgetIds[i]作为extra数据传递给意图键为Notes.INTENT_EXTRA_WIDGET_ID。
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
// 调用getWidgetType方法获取widget类型并将其作为extra数据传递给意图键为Notes.INTENT_EXTRA_WIDGET_TYPE。
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 调用getNoteWidgetInfo方法传入上下文对象context和appWidgetIds[i]以获取与指定widget ID关联的数据。查询结果存储在Cursor对象c中。
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
// 检查c是否不为null并且是否可以移动到第一条记录。如果满足条件则继续执行下面的代码块。
if (c != null && c.moveToFirst()) {
if (c != null && c.moveToFirst()) {
// 如果c中包含多于一条记录则打印一条错误日志信息并关闭cursor对象c然后返回。
if (c.getCount() > 1) {
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
// 从c中获取指定列的字符串值并赋值给snippet。这里使用的是COLUMN_SNIPPET常量。
snippet = c.getString(COLUMN_SNIPPET);
// 从c中获取指定列的整数值并赋值给bgId。这里使用的是COLUMN_BG_COLOR_ID常量。
bgId = c.getInt(COLUMN_BG_COLOR_ID);
// 将id作为extra数据传递给意图键为Intent.EXTRA_UID。
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
// 设置意图的动作为Intent.ACTION_VIEW。这表示当这个意图被触发时系统会查看或提供用户界面来查看或编辑一条信息。
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果c为null或者不能移动到第一条记录则设置snippet为指定字符串资源ID对应的字符串值。这里使用的是R.string.widget_havenot_content表示widget还没有内容。
snippet = context.getResources().getString(R.string.widget_havenot_content);
// 设置意图的动作为Intent.ACTION_INSERT_OR_EDIT。这表示当这个意图被触发时系统会插入或编辑一条信息
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
// 如果变量c不为空即c存在则关闭c。这是一种良好的资源管理实践可以确保不再需要的资源被及时释放。
if (c != null) {
if (c != null) {
c.close();
}
// 创建一个新的RemoteViews对象它用于定义在appWidgetIds[i]位置的小部件的布局和行为。
// RemoteViews构造函数接收两个参数第一个是包名第二个是布局ID。
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
// 使用getImageViewResource方法设置widget_bg_image的图像资源ID。这是从getBgResourceId方法获取的该方法是一个抽象方法需要由子类实现以返回背景图像的资源ID。
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
// 将背景ID作为额外数据添加到intent中这样它就可以在启动的Activity或Service中使用
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
// 这是一段注释描述了下面的代码将生成一个用于启动宿主Activity的PendingIntent。
/**
* Generate the pending intent to start host for the widget
*/
PendingIntent pendingIntent = null;
// 如果隐私模式为真则设置widget_text的文本为"widget_under_visit_mode"这是一个字符串资源ID它可能表示"正在访问模式"。同时创建一个PendingIntent这个PendingIntent将在稍后被设置到RemoteViews上以便在用户点击widget时启动NotesListActivity。
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 {
// 如果隐私模式为假则设置widget_text的文本为"snippet"这是一个变量可能表示一段可读的内容。同时创建一个PendingIntent这个PendingIntent将在稍后被设置到RemoteViews上以便在用户点击widget时启动由intent定义的Activity。
rv.setTextViewText(R.id.widget_text, snippet);
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
// 使用setOnClickPendingIntent方法设置当用户点击widget_text时应该启动的PendingIntent。这个PendingIntent之前已经被创建并赋值给了pendingIntent变量。
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
//使用appWidgetManager对象的updateAppWidget方法更新指定位置的widget。这会导致小部件的布局被更新以反映在RemoteViews对象中做的更改。
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);

@ -0,0 +1,58 @@
/*
* 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.
*/
// 定义一个位于net.micode.notes.widget包下的NoteWidgetProvider_2x类这个类继承自NoteWidgetProvider类
package net.micode.notes.widget;
// 导入必要的Android库
import android.appwidget.AppWidgetManager; // 管理App小部件的类
import android.content.Context; // 提供应用环境信息的接口
// 导入项目内部的库
import net.micode.notes.R; // 项目的资源文件
import net.micode.notes.data.Notes; // 项目的数据模型类,可能包含笔记的数据结构和方法
import net.micode.notes.tool.ResourceParser; // 项目的工具类,用于解析资源
// NoteWidgetProvider_2x类的定义开始
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 重写父类的onUpdate方法这是在更新小部件时被调用的方法
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法传递相同的参数进行实际的更新操作
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类的getLayoutId方法这个方法用于获取小部件的布局资源ID
@Override
protected int getLayoutId() {
// 返回预定义的2x小部件布局资源ID
return R.layout.widget_2x;
}
// 重写父类的getBgResourceId方法这个方法用于获取小部件的背景资源ID
@Override
protected int getBgResourceId(int bgId) {
// 使用ResourceParser工具的getWidget2xBgResource方法根据传入的bgId参数获取对应的2x小部件背景资源ID并返回
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
// 重写父类的getWidgetType方法这个方法用于获取小部件的类型
@Override
protected int getWidgetType() {
// 返回预定义的2x小部件类型ID这个ID可能在Notes类中有定义
return Notes.TYPE_WIDGET_2X;
}
} // NoteWidgetProvider_2x类的定义结束

@ -0,0 +1,57 @@
/*
* 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.
*/
// 声明一个名为NoteWidgetProvider_4x的公开类该类继承自NoteWidgetProvider类。这个类看起来是用于提供Android小部件Widget的功能。
package net.micode.notes.widget;
// 导入必要的Android库用于处理小部件和应用程序上下文。
import android.appwidget.AppWidgetManager;
import android.content.Context;
// 导入项目内部的库,其中包含小部件相关的类和方法。
import net.micode.notes.R; // 导入项目资源类该类包含与应用相关的资源如ID颜色尺寸等
import net.micode.notes.data.Notes; // 导入数据模型类,可能包含笔记的数据结构和相关方法。
import net.micode.notes.tool.ResourceParser; // 导入项目工具类,可能用于解析和应用资源。
// NoteWidgetProvider_4x类的定义开始。
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 重写父类的onUpdate方法。这个方法通常在小部件需要更新时被系统调用。
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的update方法使用相同的参数进行更新操作。
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类的getLayoutId方法。这个方法用于获取小部件的布局资源ID。
protected int getLayoutId() {
// 返回预定义的4x小部件布局资源ID。
return R.layout.widget_4x;
}
// 重写父类的getBgResourceId方法。这个方法用于获取小部件的背景资源ID。
@Override
protected int getBgResourceId(int bgId) {
// 使用ResourceParser工具类的getWidget4xBgResource方法根据传入的bgId参数获取对应的4x小部件背景资源ID并返回。
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
// 重写父类的getWidgetType方法。这个方法用于获取小部件的类型。
@Override
protected int getWidgetType() {
// 返回预定义的4x小部件类型ID。这个ID可能在Notes类中有定义。
return Notes.TYPE_WIDGET_4X;
}
} // NoteWidgetProvider_4x类的定义结束
Loading…
Cancel
Save