pull/13/head
庞浩 2 years ago
parent add039a4a1
commit b48f8248c6

@ -1,387 +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;
}
/* `TAG` String tag
`sInstance`
`getInstance()` `Context` `BackupUtils`
使线
*/
/**
* 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 方法,用于将数据导出为文本,并返回一个整数值。
public String getExportedTextFileName() {
return mTextExport.mFileName;
}//这是一个 public 方法,用于获取导出的文本文件名。
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}//这是一个 public 方法,用于获取导出的文本文件所在的目录。
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;
// 定义了一个字符串数组 TEXT_FORMAT其中存储了导出笔记时的文件格式
private Context mContext;
private String mFileName;
private String mFileDirectory;
// 定义了 Context mContext、String mFileName、String mFileDirectory 三个变量
// mContext 存储了当前上下文mFileName 存储了文件名mFileDirectory 存储了文件夹路径
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";// 构造函数,初始化了 TEXT_FORMAT、mContext、mFileName 和 mFileDirectory
}
private String getFormat(int id) {
return TEXT_FORMAT[id];
}// getFormat 方法返回 TEXT_FORMAT 数组中指定 id 的字符串
/**
* Export the folder identified by folder id to text
* (exportFolderToText )
*/
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);// exportNoteToText 方法用于将指定笔记的数据导出到文本中
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使JavabyteCharacter.LINE_SEPARATORCharacter.LETTER_NUMBER
Character.LINE_SEPARATORWindows"\r\n"Unix/Linux"\n"
Character.LETTER_NUMBER
bytePrintStream便IOExceptionLogcat*/
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}//这段代码中的 getExportToTextPrintStream() 是一个自定义方法,它返回一个 PrintStream 对象,该对象用于将笔记数据导出到文本文件中
// 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();
}/*
ContentResolver query() Call Record Cursor folderCursor
folderCursor ID ps Call Record 使*/
// 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;
}/*
ContentResolver query() Cursor noteCursor
noteCursor ps exportNoteToText() ps
ps STATE_SUCCESS noteCursor */
/**
* 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;
}
}/* PrintStream
generateFileMountedOnSDcard() File file
file nullLogcat "create file to exported failed" null
file FileOutputStream fos PrintStream PrintStream ps
ps PrintStream FileNotFoundException NullPointerException null*/
/**
* 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;
}
}/*
filePathResId StringBuilder sb
File filedir mkdir()
使 fileNameFormatResId sb File file
file filedir file SecurityException IOException null*/

@ -1,359 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";//定义了一个Java常量变量名为TAG
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}/*ContentResolverLongHashSet
idstrueids0true
ids0idsNotes.ID_ROOT_FOLDERContentProviderOperation使ContentProviderOperation.newDelete()operationList
使ContentResolver.applyBatch()truefalseRemoteExceptionOperationApplicationExceptionfalse*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}/*ContentResolverlongidlongsrcFolderIdlongdesFolderId
ContentValuesNoteColumns.PARENT_IDdesFolderIdNoteColumns.ORIGIN_PARENT_IDsrcFolderIdNoteColumns.LOCAL_MODIFIED1
ContentResolver.update()URIURIContentUris.withAppendedId()Notes.CONTENT_NOTE_URIidContentValuesnull*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}/*ContentResolverLongHashSetidsLongfolderId
idstrue
idsidsContentProviderOperation使ContentProviderOperation.newUpdate()NoteColumns.PARENT_IDfolderIdNoteColumns.LOCAL_MODIFIED1operationList
使ContentResolver.applyBatch()truefalseRemoteExceptionOperationApplicationExceptionfalse*/
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
if(cursor != null) {
if(cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
cursor.close();
}
}
}
return count;
}/*ContentResolver
Cursor使ContentResolver.query()Notes.TYPE_FOLDERCOUNT(*)
CursorCursornull使getInt(0)IndexOutOfBoundsExceptionCursor
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}/*ContentResolverlongnoteIdinttype
Cursor使ContentResolver.query()type
existtrueCursorexist*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}/*ContentResolverlongnoteId
Cursor使ContentResolver.query()IDnoteId
existtrueCursorexist*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}/*ContentResolverlongdataId
Cursor使ContentResolver.query()IDdataId
existtrueCursorexist*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}/*ContentResolverStringname
Cursor使ContentResolver.query()Notes.TYPE_FOLDERNoteColumns.PARENT_ID <> Notes.ID_TRASH_FOLERNoteColumns.SNIPPET
existtrueCursorexist*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet<AppWidgetAttribute> set = null;
if (c != null) {
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
c.close();
}
return set;
}/*ContentResolverlongfolderIdID
Cursor使ContentResolver.query()IDfolderIdID
HashSetHashSet<AppWidgetAttribute>IDHashSetnull
CursorHashSetID*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
cursor.close();
}
}
return "";
}/*ContentResolverlongnoteIdID
Cursor使ContentResolver.query()IDnoteIdMIMECallNote.CONTENT_ITEM_TYPE
Cursor*/
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
cursor.close();
}
return 0;
}/*ContentResolverStringphoneNumberlongcallDateID
Cursor使ContentResolver.query()callDateMIMECallNote.CONTENT_ITEM_TYPEphoneNumberID
ID0
CursorID0*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0);
}
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}/*ContentResolverlongnoteIdIDsnippet
Cursor使ContentResolver.query()IDnoteId
snippetCursorsnippetIllegalArgumentExceptionNote is not found with id: noteId*/
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');
if (index != -1) {
snippet = snippet.substring(0, index);
}
}
return snippet;
}
}/*Stringsnippetsnippet便
snippet使String.trim()snippet使String.indexOf()snippet使String.substring()snippetsnippetsnippet
snippet*/

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

@ -1,215 +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
};/*这是一个静态内部类 NoteBgResources其中包含一个名为 BG_EDIT_RESOURCES 的静态常量数组,该数组包含了 5 个整型元素,这些整型元素对应着项目中的一些 drawable 资源 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
};/*这是一个私有的静态常量数组 BG_EDIT_TITLE_RESOURCES它包含了五个整型元素这些整型元素对应着项目中的一些 drawable 资源 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];
}/*一个静态方法 getNoteBgResource(),它接受一个整型参数 id并返回一个整型值。在这个方法中静态常量数组 BG_EDIT_RESOURCES 被索引到,以返回该数组中索引为 id 的元素的值。*/
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}/*一个静态方法 getNoteTitleBgResource(),它接受一个整型参数 id并返回一个整型值。在这个方法中静态常量数组 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;
}
}/* getDefaultBgId() Context
PreferenceManager.getDefaultSharedPreferences(context) SharedPreferences PREFERENCE_SET_BG_COLOR_KEY true true使 Math.random() NoteBgResources.BG_EDIT_RESOURCES ID PREFERENCE_SET_BG_COLOR_KEY false BG_DEFAULT_COLOR ResourceParser */
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
};/* NoteItemBgResources BG_FIRST_RESOURCES
5 drawable R.drawable.list_yellow_upR.drawable.list_blue_upR.drawable.list_white_upR.drawable.list_green_upR.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
};/* BG_NORMAL_RESOURCES drawable R.drawable.list_yellow_middleR.drawable.list_blue_middleR.drawable.list_white_middleR.drawable.list_green_middleR.drawable.list_red_middle
drawable
R.drawable.list_yellow_middleR.drawable.list_blue_middleR.drawable.list_white_middleR.drawable.list_green_middleR.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,
};/* BG_LAST_RESOURCES drawable R.drawable.list_yellow_downR.drawable.list_blue_downR.drawable.list_white_downR.drawable.list_green_downR.drawable.list_red_down
drawable R.drawable.list_yellow_downR.drawable.list_blue_downR.drawable.list_white_downR.drawable.list_green_downR.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
};/* BG_SINGLE_RESOURCES drawable R.drawable.list_yellow_singleR.drawable.list_blue_singleR.drawable.list_white_singleR.drawable.list_green_singleR.drawable.list_red_single
drawable R.drawable.list_yellow_singleR.drawable.list_blue_singleR.drawable.list_white_singleR.drawable.list_green_singleR.drawable.list_red_single */
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}/*一个公共静态方法 getNoteBgFirstRes(),它接受一个整型参数 id并返回一个整型值。*/
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}/* getNoteBgLastRes() id
BG_LAST_RESOURCES id */
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}/* getNoteBgSingleRes() id
BG_SINGLE_RESOURCES id */
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}/* getNoteBgNormalRes() id
BG_NORMAL_RESOURCES id */
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}/* getFolderBgRes()
list_folder drawable ID*/
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,
};/*一个静态内部类 WidgetBgResources它包含一个私有的静态常量数组 BG_2X_RESOURCES该数组包含五个整型元素这些整型元素对应着项目中的一些 drawable 资源 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
};/* getWidget2xBgResource() id
BG_2X_RESOURCES id
2x2 ID drawable
BG_4X_RESOURCES drawable R.drawable.widget_4x_yellowR.drawable.widget_4x_blueR.drawable.widget_4x_whiteR.drawable.widget_4x_greenR.drawable.widget_4x_red*/
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}/* getWidget4xBgResource() id
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
};/*一个静态内部类 TextAppearanceResources它包含一个私有的静态常量数组 TEXTAPPEARANCE_RESOURCES该数组包含四个整型元素这些整型元素对应着项目中的一些样式资源 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];
}/* getTexAppearanceResource() id
id TEXTAPPEARANCE_RESOURCES BG_DEFAULT_FONT_SIZE
id TEXTAPPEARANCE_RESOURCES TEXTAPPEARANCE_RESOURCES id */
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}/* getTexAppearanceResource() id
id TEXTAPPEARANCE_RESOURCES BG_DEFAULT_FONT_SIZE
id TEXTAPPEARANCE_RESOURCES TEXTAPPEARANCE_RESOURCES id */

@ -1,410 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
public class NotesPreferenceActivity extends PreferenceActivity {// 定义了一个常量字符串 PREFERENCE_NAME表示 SharedPreferences 的名称
public static final String PREFERENCE_NAME = "notes_preferences";// 定义了一个常量字符串 PREFERENCE_SYNC_ACCOUNT_NAME表示同步账户名称的键名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";// 定义了一个常量字符串 PREFERENCE_LAST_SYNC_TIME表示上一次同步时间的键名
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";// 定义了一个常量字符串 PREFERENCE_SET_BG_COLOR_KEY表示是否随机设置背景颜色的键名
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";// 定义了一个常量字符串 PREFERENCE_SYNC_ACCOUNT_KEY表示同步账户的键名
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";// 定义了一个常量字符串 PREFERENCE_SYNC_ACCOUNT_KEY表示同步账户的键名
private static final String AUTHORITIES_FILTER_KEY = "authorities";// 定义了一个常量字符串 AUTHORITIES_FILTER_KEY表示过滤器的 authorities 键名
private PreferenceCategory mAccountCategory;// 声明了一个 PreferenceCategory 类型的成员变量 mAccountCategory表示账户分类
private GTaskReceiver mReceiver;// 声明了一个 GTaskReceiver 类型的成员变量 mReceiver表示 GTask 接收器
private Account[] mOriAccounts;// 声明了一个 Account 数组类型的成员变量 mOriAccounts表示原始账户
private boolean mHasAddedAccount;// 声明了一个 boolean 类型的成员变量 mHasAddedAccount表示是否已添加了账户
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
/* using the app icon for navigation */
getActionBar().setDisplayHomeAsUpEnabled(true);
addPreferencesFromResource(R.xml.preferences);// 加载 preferences.xml 文件中的 Preference
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);// 从 preferences.xml 中找到同步账户分类,赋值给 mAccountCategory
mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);// 注册 GTaskReceiver 广播接收器,监听 Gtask 同步服务的消息
mOriAccounts = null;
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);//将原始账户数组 mOriAccounts 设置为 null。
}
@Override
protected void onResume() {
super.onResume();// onStart 方法,表示 Activity 启动时调用的方
// need to set sync account automatically if user has added a new
// account
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
refreshUI();
}/*1. onCreate() Activity
2. 便Activity
3. preferences.xml Preference
4. preferences.xml mAccountCategory
5. GTaskReceiver 广 GTask
6. mOriAccounts null
7. LayoutInflater R.layout.settings_header
8. ListView
广*/
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}/* Android Activity onDestroy()
1. mReceiver null null广
2. onDestroy() Activity
Activity 广 Android 广广 Activity Activity广*/
private void loadAccountPreference() {
mAccountCategory.removeAll();// 清空同步账户分类中的所有设置项
Preference accountPref = new Preference(this);// 创建一个新的 Preference 对象 accountPref
final String defaultAccount = getSyncAccountName(this);// 获取当前默认的同步账户名称
accountPref.setTitle(getString(R.string.preferences_account_title));// 设置 accountPref 的标题和摘要
accountPref.setSummary(getString(R.string.preferences_account_summary)); // 设置 accountPref 的点击事件监听器
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {// 判断是否正在进行同步任务
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
showSelectAccountAlertDialog();//如果当前同步账户为空,则弹出选择账户对话框
} else {
// if the account has already been set, we need to promp
// user about the risk
showChangeAccountConfirmAlertDialog();
}// 如果已经设置了同步账户,则需要提示用户风险
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();// 如果正在进行同步任务,则提示用户无法更改账户
}
return true;// 返回 true表示点击事件已被处理
}
});
mAccountCategory.addPreference(accountPref);// 将 accountPref 添加到同步账户分类中
}
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);// 获取同步按钮和上次同步时间的视图对
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);// 设置同步按钮的状态和点击事件监听器
// set button state
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel)); // 如果正在同步任务中,则将按钮文本设置为“取消同步”,并添加取消同步的点击事件监听器
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));// 如果没有正在同步任务,则将按钮文本设置为“立即同步”,并添加开始同步的点击事件监听器
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}// 根据当前是否设置了同步账户来设置同步按钮的可用状态
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));// 设置上次同步时间的显示状态和文本内容
// set last sync time
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE); // 如果正在同步任务中,则显示当前同步进度,并将上次同步时间视图设置为可见状态
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}// 如果没有正在同步任务则获取上次同步时间并根据上次同步时间是否为0来设置上次同步时间视图的可见状态和文本内容
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}/*loadAccountPreference() 方法用于加载和显示当前同步账户的信息,而 loadSyncButton() 方法则用于设置同步按钮的状态和点击事件监听器,并显示上次同步时间的信息。*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);// 创建 AlertDialog.Builder 对象
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);/// 创建自定义标题视图
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));// 设置标题文本
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));// 设置副标题文本
dialogBuilder.setCustomTitle(titleView);// 将自定义标题视图设置到对话框中
dialogBuilder.setPositiveButton(null, null); // 不设置确定按钮和点击事件监听器
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);// 获取所有谷歌账户的列表和当前同步账户的名称
mOriAccounts = accounts;
mHasAddedAccount = false;// 保存原始账户列表并将“已添加账户”标志设为 false
if (accounts.length > 0) {// 如果找到了至少一个谷歌账户,则创建单选项列表
CharSequence[] items = new CharSequence[accounts.length];// 创建单选项列表的选项文本数组
final CharSequence[] itemMapping = items;// 创建选项文本数组的映射数组
int checkedItem = -1; // 初始化默认选中项的索引为 -1
int index = 0;
for (Account account : accounts) {// 遍历所有谷歌账户,为每个账户设置一个选项
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}// 如果该账户的名称与当前同步账户的名称相同,则将其作为默认选中项
items[index++] = account.name;// 将该账户的名称添加到选项文本数组中
}
dialogBuilder.setSingleChoiceItems(items, checkedItem, // 将单选项列表设置到对话框中,并为每个选项设置点击事件监听器
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString()); // 将用户选择的账户名称设置为同步账户
dialog.dismiss();// 关闭对话框
refreshUI();// 刷新 UI 界面上的账户和同步按钮状态
}
});
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);//从布局文件中加载一个视图对象并将其赋值给 addAccountView 变量
dialogBuilder.setView(addAccountView);//将 addAccountView 设置为对话框的自定义视图
final AlertDialog dialog = dialogBuilder.show();//创建一个 AlertDialog 对象,并将其显示出来
addAccountView.setOnClickListener(new View.OnClickListener() {//添加一个点击事件监听器
public void onClick(View v) {
mHasAddedAccount = true;//用户是否已经添加了一个账户
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});//创建了一个 Intent 对象,用于启动一个系统设置界面,以便让用户添加新的帐户
startActivityForResult(intent, -1);//启动一个新的界面,等待用户添加新的账户
dialog.dismiss();//关闭对话框
}
});
}
private void showChangeAccountConfirmAlertDialog() {
// 创建一个 AlertDialog.Builder 对象
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
// 从布局文件 R.layout.account_dialog_title 中加载一个视图 titleView
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
// 从 titleView 中找到一个 TextView 对象 titleTextView并设置其文本
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
// 从 titleView 中找到一个 TextView 对象 subtitleTextView并设置其文本
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
// 将 titleView 设置为对话框的自定义标题
dialogBuilder.setCustomTitle(titleView);
// 创建一个 CharSequence 类型的数组 menuItemArray包含三个字符串作为选项菜单的标签文字
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { // 创建一个 DialogInterface.OnClickListener 对象,用于处理选项菜单的点击事件
public void onClick(DialogInterface dialog, int which) {// 判断点击了哪个菜单项
if (which == 0) {
showSelectAccountAlertDialog();// 如果点击了第一个菜单项,调用 showSelectAccountAlertDialog() 方法显示“选择帐户”对话框
} else if (which == 1) {
removeSyncAccount();
refreshUI();// 如果点击了第二个菜单项,先调用 removeSyncAccount() 方法删除同步帐户,再调用 refreshUI() 方法刷新界面
}
}
});
dialogBuilder.show();// 显示对话框
}
private Account[] getGoogleAccounts() {
// 获取 AccountManager 对象
AccountManager accountManager = AccountManager.get(this);
// 调用 getAccountsByType() 方法,传入参数 "com.google",以获取所有类型为 "com.google" 的帐户
return accountManager.getAccountsByType("com.google");
}
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {// 判断传入的 account 是否与当前同步帐户相同
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();// 获取 SharedPreferences 对象
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); // 如果传入的 account 不为 null则将其存储到 SharedPreferences 中
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");// 如果传入的 account 为 null则将空字符串存储到 SharedPreferences 中
}
editor.commit();// 提交修改
// clean up last sync time
setLastSyncTime(this, 0);// 清除上一次同步的时间
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues(); // 创建一个 ContentValues 对象,用于更新所有的 note
values.put(NoteColumns.GTASK_ID, "");// 清空 GTasks ID 和 sync ID
values.put(NoteColumns.SYNC_ID, 0);// 清空 GTasks ID 和 sync ID
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); // 更新所有的 note
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();// 显示一个 Toast提示同步帐户设置成功
}
}
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); // 获取 SharedPreferences 对象
SharedPreferences.Editor editor = settings.edit();// 获取 SharedPreferences 对象
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}// 如果 SharedPreferences 包含同步帐户名称,则从 SharedPreferences 中删除该名称
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME); // 如果 SharedPreferences 包含上一次同步的时间,则从 SharedPreferences 中删除该时间
}
editor.commit(); // 提交修改
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();// 创建一个 ContentValues 对象,用于更新所有的 note
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0); // 清空 GTasks ID 和 sync ID
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();// 更新所有的 note
}
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,// 获取 SharedPreferences 对象
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);
SharedPreferences.Editor editor = settings.edit(); // 获取 SharedPreferences 对象
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);// 将最后一次同步的时间保存到 SharedPreferences 中
editor.commit();// 提交修改
}
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);// 获取 SharedPreferences 对象
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);// 从 SharedPreferences 中获取最后一次同步的时间
}
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();// 刷新用户界面
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));// 如果正在同步,则更新同步状态
}
}
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true; // 创建一个 Intent 对象,跳转到 NotesListActivity并清除之前的所有 Activity
default:
return false;// 如果选择的菜单项不是返回主页,则返回 false
}
}
}

@ -1,153 +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
};/* NoteWidgetProvider AppWidgetProvider
PROJECTION ID ID */
public static final int COLUMN_ID = 0;// 笔记 ID 列在 PROJECTION 数组中的索引
public static final int COLUMN_BG_COLOR_ID = 1;// 笔记背景颜色 ID 列在 PROJECTION 数组中的索引
public static final int COLUMN_SNIPPET = 2;// 笔记摘录内容列在 PROJECTION 数组中的索引
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])});
}
}/* NoteWidgetProvider onDeleted() AppWidgetProvider
ContentValues values NoteColumns.WIDGET_ID AppWidgetManager.INVALID_APPWIDGET_ID
appWidgetIds NoteColumns.WIDGET_ID AppWidgetManager.INVALID_APPWIDGET_ID便
使 getContentResolver() ContentResolver update() NoteColumns.WIDGET_ID + "=?" NoteColumns.WIDGET_ID ID values NoteColumns.WIDGET_ID AppWidgetManager.INVALID_APPWIDGET_ID*/
private Cursor getNoteWidgetInfo(Context context, int widgetId) {// 使用 getContentResolver() 方法获取 ContentResolver 对象,通过该对象进行对笔记数据库的查询操作
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, // 使用 query() 方法查询笔记数据库,返回一个 Cursor 对象
return context.getContentResolver().query(// 查询的 URI笔记数据库中笔记的内容保存在该 URI 下
PROJECTION,
Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",// 查询的列,即笔记 ID、笔记背景颜色 ID 和笔记摘录内容
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },//查询的条件,即笔记关联的小部件 ID 以及笔记的父 ID 不为回收站的笔记
null);// 排序方式,这里为 null 表示不排序
}
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}/*这是 NoteWidgetProvider 抽象类中的一个受保护的方法 update(),它接受一个 Context 对象、一个 AppWidgetManager 对象和一个整型数组 appWidgetIds 作为参数,并在方法中调用了另一个同名方法。*/
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);
}
}
}/* `NoteWidgetProvider` `update()` `Context` `AppWidgetManager` `appWidgetIds` `privacyMode`
`appWidgetIds` ID `AppWidgetManager.INVALID_APPWIDGET_ID`
- ID
- `NoteEditActivity` ID
- `getNoteWidgetInfo()` ID
- ID `RemoteViews` ID `PendingIntent`
- `AppWidgetManager` `updateAppWidget()`
`privacyMode` `true` "隐私模式" `NotesListActivity` `NoteEditActivity`*/
protected abstract int getBgResourceId(int bgId);//用于获取小部件的背景资源 ID。
protected abstract int getLayoutId();//用于获取小部件的布局资源 ID。
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 {//创建一个继承自 NoteWidgetProvider 的子类 NoteWidgetProvider_2x
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}/*重写父类的 onUpdate() 方法,并在其中调用父类的 update() 方法,以更新小部件的显示内容和点击事件。*/
@Override
protected int getLayoutId() {
return R.layout.widget_2x;
}/*重写父类的 getLayoutId() 方法,返回用于小部件的布局资源 ID。这里返回 R.layout.widget_2x表示使用 widget_2x.xml 文件作为布局资源。*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}/*重写父类的 getBgResourceId() 方法,返回用于小部件的背景资源 ID。这里调用了 ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId) 方法,该方法根据传入的背景 ID 返回相应的背景资源 ID。*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X;
}/*重写父类的 getWidgetType() 方法,返回小部件的类型。这里返回 Notes.TYPE_WIDGET_2X表示这是一个 2x 大小的小部件。*/
}

@ -1,45 +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 {//创建一个继承自 NoteWidgetProvider 的子类 NoteWidgetProvider_4x。
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}/*重写父类的 onUpdate() 方法,并在其中调用父类的 update() 方法,以更新小部件的显示内容和点击事件。*/
protected int getLayoutId() {
return R.layout.widget_4x;
}/*重写父类的 getLayoutId() 方法,返回用于小部件的布局资源 ID。这里返回 R.layout.widget_4x表示使用 widget_4x.xml 文件作为布局资源。*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}/*重写父类的 getBgResourceId() 方法,返回用于小部件的背景资源 ID。这里调用了 ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId) 方法,该方法根据传入的背景 ID 返回相应的背景资源 ID。*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;
}
}/*重写父类的 getWidgetType() 方法,返回小部件的类型。这里返回 Notes.TYPE_WIDGET_4X表示这是一个 4x 大小的小部件。*/
Loading…
Cancel
Save