diff --git a/src/model/Note.java b/src/model/Note.java new file mode 100644 index 0000000..a0e070d --- /dev/null +++ b/src/model/Note.java @@ -0,0 +1,268 @@ +/* + * 单个便签项 +* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model;//包名 +import android.content.ContentProviderOperation;//批量的更新、插入、删除数据 +import android.content.ContentProviderResult;//操作结果 +import android.content.ContentUris;//添加或者修改uri后面的ID +import android.content.ContentValues;//存储基本数据类型的数据 +import android.content.Context;// 获取调用内容 +import android.content.OperationApplicationException;//操作数据容错 +import android.net.Uri;//Uri相关操作,标识唯一的资源 +import android.os.RemoteException;//异常处理 +import android.util.Log;//输出日志,比如说出错、警告等 + +import net.micode.notes.data.Notes;//小米便签一些属性的操作 +import net.micode.notes.data.Notes.CallNote;//调用操作 +import net.micode.notes.data.Notes.DataColumns;//数据栏 +import net.micode.notes.data.Notes.NoteColumns;//笔记栏 +import net.micode.notes.data.Notes.TextNote;//操作区 + +import java.util.ArrayList;//导入Java,util,ArrayList + + +public class Note {//定义note类,处理单个便签 + private ContentValues mNoteDiffValues;//声明一个ContentValues私有变量ContentValues,用来存储note与上次修改后的改动 + private NoteData mNoteData;//声明一个私有变量NoteData,用来记录单个便签的基本信息 + private static final String TAG = "Note";//设置软件标签 + /** + * Create a new note id for adding a new note to databases + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + //Create a new note in the database + /* + * 作用:为新便签在数据库里边创建一个新的ID + * 实现:创建一个内容集合,存入创建便签的时间,最后修改的时间,再根据当前便签所处的上下文和文件夹的ID,从内容解析器中得到的路径uri里面 + * 获取新的便签的ID。最后判断ID是否合法,然后返回 + * 参数:@context:便签所处的上下文 + * @folderId:所处的便签文件夹ID + * @return:新创建便签的ID + */ + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() {//定义两个变量用来存储便签的数据,一个是存储便签属性、一个是存储便签内容 + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + + public void setNoteValue(String key, String value) { + /* + * 作用:设置便签的属性 + * 实现:通过传入key,指定修改便签的属性;传入value,指定修改的值,通过属性管理器mNoteDiffValues写入。最后,改变最后修改时间 + * 参数:@key:要修改的属性 + * @value:要修改的值 + */ + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + public void setTextData(String key, String value) {//设置数据库表格的标签文本内容的数据 + mNoteData.setTextData(key, value); + } + + public void setTextDataId(long id) {//设置文本数据的ID + mNoteData.setTextDataId(id); + } + + public long getTextDataId() {//获取文本数据的ID + return mNoteData.mTextDataId; + } + + public void setCallDataId(long id) {//设置电话号码数据的ID + mNoteData.setCallDataId(id); + } + + public void setCallData(String key, String value) {//设置电话号码的数据 + mNoteData.setCallData(key, value); + } + + public boolean isLocalModified() {//判断便签是否进行了本地修改 + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + + public boolean syncNote(Context context, long noteId) {//判断对修改过的便签是否进行同步 + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData {//定义一个基本的便签内容的数据类,主要包含文本数据和电话号码数据 + private long mTextDataId;//文本数据id + + private ContentValues mTextDataValues;//文本数据内容 + + private long mCallDataId;//电话号码数据ID + + private ContentValues mCallDataValues;//电话号码数据内容 + + private static final String TAG = "NoteData";//默认构造函数 + + public NoteData() {//NoteData的构造函数,初始化四个变量 + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() {//判断是否本地修改 + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) {//设置文本数据的ID + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) {//设置电话号码对应的id + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) {//设置电话号码数据内容,并且保存修改时间 + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) {//设置文本数据 + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) {//使用uri将数据添加到数据库 + /** + * Check for safety + */ + if (noteId <= 0) {//判断数据是否合法 + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) {//把文本数据存入DataColumns + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) {//文本数据ID为零,意味着这个id是新建默认的id + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) {//电话号码同步 + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) {//将电话号码的id设定为uri提供的id + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else {//当电话号码不为新建时,更新电话号码ID + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) {//存储过程中如果遇到异常,通过如下进行处理 + try {//抛出远程异常,并写回日志 + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) {//捕捉操作异常并写回日志 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));//异常日志 + return null; + } + } + return null; + } + } +} diff --git a/src/model/WorkingNote.java b/src/model/WorkingNote.java new file mode 100644 index 0000000..f78e8b0 --- /dev/null +++ b/src/model/WorkingNote.java @@ -0,0 +1,370 @@ +/*当前活动便签项 + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model;//在model包中 + +import android.appwidget.AppWidgetManager;//需要调用的其他类 +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + + +public class WorkingNote {/*声明WorkingNote类,创建小米便签的主要类, + 包括创建空便签,保存便签,加载小米便签内容,和设置小米便签的一些小部件之类的操作*/ + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] {/*声明NOTE_PROJECTION字符串数组, + 用于存储数据信息。*/ + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] {//保存便签属性信息的string数组 + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0;//以下定义的是上述两个列表中各元素的索引 + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + private WorkingNote(Context context, long folderId) {//初始化WorkingNote类的内部数据变量 + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis();//获取系统当前时间的方法,返回当前时间 + mFolderId = folderId; + mNote = new Note();//加载一个已存在的便签 + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE;//默认是不可见 + } + + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) {//载入便签 + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + + private void loadNote() {//加载已有的便签 + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + + private void loadNoteData() {//加载便签数据 + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) {//创建一个空便签 + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) {//加载已经创建的便签 + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() {//保存便签,成功保存返回true,否则返回false + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + } + } + + public boolean existInDatabase() {//查看该note是否已经存放到数据库中 + return mNoteId > 0; + } + + private boolean isWorthSaving() {//判断是否需要保存 + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {//设置状态改变的监听器 + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) {//设置提醒日期 + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) {//设置删除标志 + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) {//设定背景颜色 + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) {//设置检查列表模式 + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) {//设置窗口类型 + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) {//设置窗口编号 + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) {//写入文本内容 + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) {//转换成电话提醒的便签 + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() {//检测是否有时钟提醒,mAlertDate > 0返回真,否则返回假 + return (mAlertDate > 0 ? true : false); + } + + public String getContent() {//获取便签内容 + return mContent; + } + + public long getAlertDate() {//获取提醒时间 + return mAlertDate; + } + + public long getModifiedDate() {//获取修改时间 + return mModifiedDate; + } + + public int getBgColorResId() {//获取背景颜色来源id + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() {//获取背景颜色id + return mBgColorId; + } + + public int getTitleBgResId() {//获取标题背景颜色资源的id + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() {//获取清单模式 + return mMode; + } + + public long getNoteId() {//获取便签id + return mNoteId; + } + + public long getFolderId() {//获取文件夹id + return mFolderId; + } + + public int getWidgetId() {//获取窗口id + return mWidgetId; + } + + public int getWidgetType() {//监听检测便签设置变化的接口 + return mWidgetType; + } + + public interface NoteSettingChangedListener {//监听便签设置是否改变 + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged();//背景颜色改变 + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set);//提醒时间按钮,可进行时间的更改和提醒的开关 + + /** + * Call when user create note from widget + */ + void onWidgetChanged();//窗口改变 + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode);//便签检查列表模式改变 + } +} diff --git a/src/tool/BackupUtils.java b/src/tool/BackupUtils.java new file mode 100644 index 0000000..b4fd702 --- /dev/null +++ b/src/tool/BackupUtils.java @@ -0,0 +1,343 @@ +/* + * 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";//瀹炲寲涓涓狟ackupUtils鐨勫璞 + // Singleton stuff + private static BackupUtils sInstance;//鍒濆鍖杝Instance + + public static synchronized BackupUtils getInstance(Context context) {//濡傛灉褰撳墠澶囦唤涓嶅湪锛屽垯鍐嶆柊澹版槑涓涓 + if (sInstance == null) { + sInstance = new BackupUtils(context); + } + return sInstance;//杩斿洖褰撳墠鐨剆Instance鍊 + } + + /** + * 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;//绯荤粺閿欒鐘舵佷负3 + // Backup or restore success + public static final int STATE_SUCCESS = 4;//鍙橀噺琛ㄧず澶囦唤鎴愬姛 + + private TextExport mTextExport;//瀹炰緥鍖栦竴涓负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,//ID + NoteColumns.MODIFIED_DATE,//鏃ユ湡 + NoteColumns.SNIPPET, + NoteColumns.TYPE + }; + + private static final int NOTE_COLUMN_ID = 0;//鍒濆鍖栦究绛綢D + + 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;//琛ㄧず璁惧畾锛氭暟鎹唴瀹硅〃绀轰负0 + + private static final int DATA_COLUMN_MIME_TYPE = 1;//鏁版嵁濯掍綋绫诲瀷鏍囪瘑涓1 + + private static final int DATA_COLUMN_CALL_DATE = 2;//璁块棶鏃ユ湡琛ㄧず涓2 + + private static final int DATA_COLUMN_PHONE_NUMBER = 4;//鐢佃瘽鍙风爜琛ㄧず涓4 + + private final String [] TEXT_FORMAT;//鏂囨。鏍煎紡鏍囪瘑 + private static final int FORMAT_FOLDER_NAME = 0;//鏂囦欢鍛藉悕鏍煎紡琛ㄧず涓0 + private static final int FORMAT_NOTE_DATE = 1;//渚跨鏃ユ湡鏍煎紡琛ㄧず涓1 + private static final int FORMAT_NOTE_CONTENT = 2;//渚跨鐩綍鏍煎紡 + + private Context mContext;//瀹氫箟涓婁笅鏂囩被 + private String mFileName;//瀹氫箟鏂囦欢鍚 + private String mFileDirectory;//鏂囦欢璺緞 + + public TextExport(Context context) {//浠巆ontext绫诲疄渚嬩腑鑺卞幓淇℃伅锛岀粰瀵瑰簲鐨勫睘鎬ц祴鍒濆鍊 + 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) {//閫氳繃鏂囦欢澶圭洰褰旾D灏嗙洰褰曞鍑哄悗鎴愪负鏂囦欢 + // 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)) {//杈撳嚭浣嶇疆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 {//姝e父鎯呭喌鍐峮ote涓嬮潰杈撳嚭涓鏉$嚎 + 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() {//浠EXT褰㈠紡杈撳嚭鍒板閮ㄨ澶 + if (!externalStorageAvailable()) {//濡傛灉澶栭儴璁惧鏈畨瑁呭ソ锛岃繑鍥炲搴旂姸鎬 + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUONTED; + } + + PrintStream ps = getExportToTextPrintStream();//鑾峰彇瀛樺偍淇℃伅 + if (ps == null) {//濡傛灉娌℃湁寰楀埌姝e父鐨勬暟鎹祦锛屽垯杈撳嚭閿欒淇℃伅 + 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)) {//鍒ゆ柇folderName鏄惁瀛樺湪 + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID);//閫氳繃渚跨ID鑾峰緱folderID + 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);//鎵惧埌杩欏潡鏁版嵁鐨処D + 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;//鍒濆鍖杙s + try {//灏唒s杈撳嚭娴佽緭鍑哄埌鐗瑰畾鐨勬枃浠讹紝鐩殑鏄鍑烘枃浠 + 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; + } +} \ No newline at end of file diff --git a/src/tool/DataUtils.java b/src/tool/DataUtils.java new file mode 100644 index 0000000..8633d59 --- /dev/null +++ b/src/tool/DataUtils.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; + +import java.util.ArrayList; +import java.util.HashSet; + + +public class DataUtils {//鏁版嵁鐨勯泦鎴愬伐鍏风被 + public static final String TAG = "DataUtils"; + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + //鏂规硶锛氬疄鐜颁簡鎵归噺鍒犻櫎渚跨 + if (ids == null) {//鍒ゆ柇绗旇id鏄惁涓虹┖ + 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 operationList = new ArrayList();//鎻愪緵涓涓簨浠剁殑鍒楄〃 + 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杩涜鍒犻櫎 + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + operationList.add(builder.build()); + } + try {//杩欐槸涓涓敊璇鐞嗘満鍒讹紝骞跺垎鍒拡瀵逛袱绉嶄笉鍚岀殑閿欒杩涜澶勭悊銆 + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + //鏁版嵁搴撲簨椤癸紝鐢变竴绯诲垪鐨勬暟鎹簱鎿嶄綔搴忓垪鏋勬垚锛岃繖涓簨椤逛綔涓轰竴涓暣浣撴潵澶勭悊 + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) {//瀵归敊璇繘琛屽鐞嗭紝骞跺皢閿欒瀛樺偍鍒版棩蹇楀綋涓 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + //灏嗘爣绛剧Щ鍔ㄥ埌鍙︿竴涓洰褰曚笅 + ContentValues values = new ContentValues();//瀹炰緥鍖栦竴涓猚ontentValues绫 + values.put(NoteColumns.PARENT_ID, desFolderId);//灏哖ARENT_ID鏇存敼涓虹洰鏍囩洰褰旾D + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);//璁剧疆origin涔熷嵆鍘熸湰鐨勭埗鑺傜偣涓哄師鏈殑鏂囦欢澶圭殑id + values.put(NoteColumns.LOCAL_MODIFIED, 1);//璁剧疆淇敼绗﹀彿涓1 + resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); + //瀵归渶瑕佺Щ鍔ㄧ殑渚跨杩涜鏁版嵁鏇存柊锛岀劧鍚庣敤update瀹炵幇 + } + + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, + long folderId) {//鎵归噺鐨勫皢鏍囩绉诲姩鍒板彟涓涓洰褰曚笅 + if (ids == null) {//鍒ゆ柇渚跨ID鏄惁涓虹┖ + Log.d(TAG, "the ids is null"); + return true; + } + + ArrayList operationList = new ArrayList(); + //灏唅ds閲屽寘鍚殑姣忎竴鍒楃殑鏁版嵁閫愭鍔犲叆鍒皁perationList涓紝绛夊緟鏈鍚庣殑鎵归噺澶勭悊 + for (long id : ids) {//閬嶅巻鎵鏈夐変腑鐨勪究绛剧殑id + 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());//灏唅ds閲岀殑鏁版嵁娣诲姞鍒皁perationlist涓互渚挎帴涓嬫潵鎵归噺澶勭悊 + } + + try {//鍒╃敤Log杈撳嚭淇℃伅鏉ヨ繘琛岄敊璇娴 + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + //applyBatch涓娆℃у鐞嗕竴涓搷浣滃垪琛 + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) {//寮傚父澶勭悊 + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + /** + * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + */ + public static int getUserFolderCount(ContentResolver resolver) {//鑾峰彇鐢ㄦ埛鏂囦欢澶规暟 + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + //resolver.query()鏂规硶绗簩涓弬鏁版槸瑕佽繑鍥炵殑鍒楋紝绗笁涓弬鏁版槸Section锛屾煡璇here瀛楀彞锛涚鍥涗釜鏄煡璇㈡潯浠跺睘鎬у硷紝绗簲涓槸绛涢夎鍒 + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, + null);//String.valueof灏嗗舰鍙傝浆鎴愬瓧绗︿覆杩斿洖 + + int count = 0; + if(cursor != null) {//灏濊瘯寰楀埌鐢ㄦ埛鏂囦欢澶圭殑鏁伴噺 + if(cursor.moveToFirst()) { + try {//寮傚父澶勭悊 + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) {//绱㈠紩搴忓彿瓒呭嚭鐣岄檺 + Log.e(TAG, "get folder count failed:" + e.toString()); + } finally { + cursor.close();//鍏抽棴娓告爣 + } + } + } + return count; + } + + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {//鏄惁鍦ㄤ究绛炬暟鎹簱涓彲瑙 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),//閫氳繃withappendedid鐨勬柟娉曚负uri鍔犱笂id + null, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, + new String [] {String.valueOf(type)}, + null);//sql璇彞锛岃〃绀虹瓫閫夊嚭鍒椾腑type绛変簬string鏁扮粍涓璽ype锛屼笖姣忎竴椤圭殑PARENT_ID涓嶇瓑浜嶯otes.ID.TRAXH_FOLDER + + boolean exist = false;//鏌ヨ鏂囦欢 + if (cursor != null) {//鐢╣etcount鍑芥暟鍒ゆ柇cursor鏄惁涓虹┖ + if (cursor.getCount() > 0) {//濡傛灉鏈夋弧瓒虫潯浠剁殑鏉$洰锛岄偅涔堝氨鏄彲瑙侊紝exist涓虹湡锛屽惁鍒欎笉鍙锛宔xist涓哄亣 + exist = true; + } + cursor.close();//鍏抽棴娓告爣 + } + return exist;//鍦ㄦ暟鎹暟鎹簱涓槸鍚﹀瓨鍦 + } + + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {//鍒ゆ柇璇ote鏄惁鍦ㄦ暟鎹簱涓瓨鍦 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, null, null, null); +//鐩告瘮浜庝笂闈紝杩欎釜鍙槸瀛樺湪鎬у垽瀹氾紝鍥犳涓嶉渶瑕佽繃澶氱瓫閫夋潯浠 + boolean exist = false;//鍒濆鍖栧瓨鍦ㄧ姸鎬 + if (cursor != null) {//鏍规嵁getcount姝ゆ椂鐨勫煎彲浠ュ垽鏂璬ataID鐨勫瓨鍦ㄦ + if (cursor.getCount() > 0) {//鏍规嵁绛涢夊嚭鏉ョ殑鏉℃暟鍒ゆ柇瀛樺湪鎬 + exist = true; + } + cursor.close();//鍏抽棴娓告爣 + } + return exist; + } + + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {//妫鏌ユ枃浠跺悕瀛楁槸鍚﹀彲瑙 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), + //閫氳繃URI涓巇ataId鍦ㄦ暟鎹簱涓煡鎵炬暟鎹 + null, null, null, null); + + boolean exist = false;//鏍规嵁鏁版嵁鐨勬湁鏃犺繑鍥炵浉搴旂殑甯冨皵鍊 + if (cursor != null) { + if (cursor.getCount() > 0) {//璋冪敤瀵瑰簲鐨剈ri鐨勬暟鎹艰繘琛屾煡璇 + exist = true; + } + cursor.close();//鍏抽棴娓告爣 + } + return exist;//閫氳繃鍚嶅瓧鏌ヨ鏂囦欢鏄惁瀛樺湪 + } + + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { + //杩欎釜鍒欐槸鍒ゆ柇鏌愪釜鏂囦欢澶瑰悕瀛楁槸鍚﹀搴斾竴涓湭琚垹闄ょ殑鏂囦欢澶 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,//绛涢夌被鍨嬫纭殑銆佹湭琚垹闄ょ殑鍜屽悕瀛楀搴斿緱涓婄殑 + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", + new String[] { name }, null);//閫氳繃鍚嶅瓧鏌ヨ鏂囦欢鏄惁瀛樺湪 + boolean exist = false; + if(cursor != null) {//鍒ゆ柇鎵惧埌鐨勬枃浠跺悕杩斿洖鏂囦欢鏄惁瀛樺湪鐨刡ool鍊 + if(cursor.getCount() > 0) { + exist = true; + } + cursor.close();//鍏抽棴娓告爣 + } + return exist; + } + + public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + //浣跨敤hashset鏉ュ瓨鍌ㄤ笉鍚岀獥鍙g殑id鍜宼ype骞朵笖寤虹珛瀵瑰簲鍏崇郴 + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,//鐖秈d涓轰紶鍏ョ殑鏂囦欢澶筰d + new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(folderId) },//hash闆嗗悎涓虹┖鐨勬儏鍐 + null); + + HashSet set = null;//鏍规嵁绐楀彛鐨勮褰曚竴涓娣诲姞瀵瑰簲鐨勫睘鎬у + if (c != null) {//灏哸pp绐楀彛鐨勫睘鎬у姞鍏ュ埌HashSet涓 + if (c.moveToFirst()) { + set = new HashSet(); + do { + try {//鎶婃瘡涓涓潯鐩搴旂殑绐楀彛id鍜宼ype璁板綍涓嬫潵锛屾斁鍒皊et閲岄潰銆傛瘡涓琛岀殑绗0涓猧nt鍜岀1涓猧nt鍒嗗埆瀵瑰簲widgetId鍜寃idgetType + AppWidgetAttribute widget = new AppWidgetAttribute();//鏂板缓涓涓尯鍧 + widget.widgetId = c.getInt(0);//0瀵瑰簲鐨凬oteColumns.WIDGET_ID + widget.widgetType = c.getInt(1);//1瀵瑰簲鐨凬oteColumns.WIDGET_TYPE + set.add(widget); + } catch (IndexOutOfBoundsException e) {//褰撲笅鏍囪秴杩囪竟鐣岋紝閭d箞杩斿洖閿欒 + Log.e(TAG, e.toString()); + } + } while (c.moveToNext());//璁块棶涓嬩竴鏉 + } + c.close();//鍏抽棴娓告爣 + } + return set; + } + + 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 + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },//鏂板缓瀛楃鍒楄〃 + null); + + if (cursor != null && cursor.moveToFirst()) {// 鑾峰彇鐢佃瘽鍙风爜锛屽苟澶勭悊寮傚父銆 + try {//杩斿洖鐢佃瘽鍙风爜 + return cursor.getString(0); + } catch (IndexOutOfBoundsException e) {//鎹曡幏瀛楃 + Log.e(TAG, "Get call number fails " + e.toString()); + } finally { + cursor.close(); + } + } + return ""; + } + + public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + //鍚屾牱鐨勶紝閫氳繃鏄犲皠鍏崇郴锛岄氳繃鍙风爜鍜屾棩鏈熻幏鍙朓D + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,//閫氳繃鏁版嵁搴撴搷浣滐紝鏌ヨ鏉′欢鏄紙callDate鍜宲honeNumber鍖归厤浼犲叆鍙傛暟鐨勫硷級 + 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);//閫氳繃鏁版嵁搴撴搷浣滐紝鏌ヨ鏉′欢锛坈allDate鍜宲honeNumber鍖归厤浼犲叆鍙傛暟鐨勫硷級锛屾壘鍒板搴旂殑note + + if (cursor != null) {//寰楀埌璇ote鐨勭郴缁熷睘鎬э紝骞朵互Long鍊肩殑褰㈠紡鏉ヤ繚瀛 + if (cursor.moveToFirst()) { + try { + return cursor.getLong(0);//0瀵瑰簲鐨凜allNote.NOTE_ID + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call note id fails " + e.toString()); + } + } + cursor.close();//鍏抽棴娓告爣 + } + return 0; + } + + public static String getSnippetById(ContentResolver resolver, long noteId) {//鎸塈D鑾峰彇鐗囨 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,//閫氳繃ID鏌ヨ + new String [] { NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String [] { String.valueOf(noteId)}, + null);//閫氳繃鏁版嵁搴撴搷浣滐紝鏌ヨ鏉′欢鏄紙callDate鍜宲honeNumber鍖归厤浼犲叆鍙傛暟鐨勫 + + if (cursor != null) {//浠tring鐨勫舰寮忚幏鍙栬瀵硅薄褰撳墠琛屾寚瀹氬垪鐨勫笺 + String snippet = "";//瀵瑰瓧绗︿覆杩涜鏍煎紡澶勭悊锛屽皢瀛楃涓蹭袱澶寸殑绌烘牸鍘绘帀鍚屾椂灏嗘崲琛岀鍘绘帀 + if (cursor.moveToFirst()) { + snippet = cursor.getString(0); + } + cursor.close(); + return snippet; + } + throw new IllegalArgumentException("Note is not found with id: " + noteId); + //IllegalArgumentException鏄潪娉曚紶鍙傚紓甯革紝涔熷氨鏄弬鏁颁紶鐨勭被鍨嬪啿绐侊紝灞炰簬RunTimeException杩愯鏃跺紓甯 + } + + public static String getFormattedSnippet(String snippet) {//瀵瑰瓧绗︿覆杩涜鏍煎紡澶勭悊锛屽皢瀛楃涓蹭袱澶寸殑绌烘牸鍘绘帀锛屽悓鏃跺皢鎹㈣绗﹀幓鎺 + if (snippet != null) { + snippet = snippet.trim();// trim()鍑芥暟: 绉婚櫎瀛楃涓蹭袱渚х殑绌虹櫧瀛楃鎴栧叾浠栭瀹氫箟瀛楃 + int index = snippet.indexOf('\n'); + if (index != -1) { + snippet = snippet.substring(0, index);//鎴彇鍒扮涓涓崲琛岀 + } + } + return snippet; + } +} diff --git a/src/tool/GTaskStringUtils.java b/src/tool/GTaskStringUtils.java new file mode 100644 index 0000000..a33b4ab --- /dev/null +++ b/src/tool/GTaskStringUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool;//瀹氫箟浜嗗緢澶氱殑闈欐佸瓧绗︿覆锛岀洰鐨勫氨鏄负浜嗘彁渚沯sonObject涓浉搴斿瓧绗︿覆鐨"key" + +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";//褰撳墠鍒楄〃浣嶇疆 + + 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";//鎼。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"; + +} diff --git a/src/tool/ResourceParser.java b/src/tool/ResourceParser.java new file mode 100644 index 0000000..4ec7fb2 --- /dev/null +++ b/src/tool/ResourceParser.java @@ -0,0 +1,181 @@ +/* + * 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 {//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 {// Note鐨勮儗鏅鑹 + private final static int [] BG_EDIT_RESOURCES = new int [] {//.璋冪敤drawable涓殑浜旂棰滆壊鐨勮儗鏅浘鐗噋ng鏂囦欢 + R.drawable.edit_yellow,//璋冪敤drawable涓殑浜旂棰滆壊鐨勬爣棰樿儗鏅浘鐗噋ng鏂囦欢 + 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]; + }//鑾峰彇渚跨鑳屾櫙璧勬簮id + + public static int getNoteTitleBgResource(int id) { + return BG_EDIT_TITLE_RESOURCES[id]; + } + }//鑾峰彇鏍囬浣跨敤鐨勮祫婧愮殑ID + + public static int getDefaultBgId(Context context) {//鑾峰彇榛樿鑳屾櫙棰滆壊瀵瑰簲Id + 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 [] {//涓嶅悓drawable鐨勫彉閲忓0鏄 + 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]; + }//閫氳繃ID鑾峰彇鎵闇瑕佺殑璧勬簮 + + public static int getNoteBgLastRes(int id) { + return BG_LAST_RESOURCES[id]; + }//閫氳繃ID瀵绘壘last鐨勯鑹插 + + public static int getNoteBgSingleRes(int id) { + return BG_SINGLE_RESOURCES[id]; + }//閫氳繃ID鑾峰彇鍗曚釜渚跨鑳屾櫙棰滆壊璧勬簮 + + public static int getNoteBgNormalRes(int id) { + return BG_NORMAL_RESOURCES[id]; + }//閫氳繃ID瀵绘壘normal鐨勯鑹插 + + public static int getFolderBgRes() { + return R.drawable.list_folder; + }//璁剧疆绐楀彛鐨勮祫婧 + } + + public static class WidgetBgResources {//灏忕獥鍙f儏鍐典笅鐨勮儗鏅祫婧愮被 + private final static int [] BG_2X_RESOURCES = new int [] {//2x灏忕獥鍙h儗鏅祫婧愬垵濮嬪寲 + 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]; + }//鏍规嵁ID鍔犺浇BG_2X_RESOURCES鏁扮粍閲岀殑棰滆壊璧勬簮搴忓彿銆 + + private final static int [] BG_4X_RESOURCES = new int [] {//鏈潯涓庝笅涓鏉rivate瀹氫箟涓庝笂涓ゆ潯鐩稿悓锛屽彧涓嶈繃鐢2鍊嶆墿澶ф垚浜4鍊 + 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]; + }//鏍规嵁ID鍔犺浇BG_4X_RESOURCES鏁扮粍閲岀殑棰滆壊璧勬簮搴忓彿銆 + } + + public static class TextAppearanceResources {//鏂囨湰澶栬璧勬簮锛屽寘鎷粯璁ゅ瓧浣擄紝浠ュ強鑾峰彇璧勬簮澶у皬 + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {//瀹氫箟澶栬璧勬簮 + R.style.TextAppearanceNormal,//閫氳繃ID鎵惧埌鏍煎紡锛屾病鏈夎姹傜殑璇濆氨璁剧疆涓洪粯璁ゆ牸寮 + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper + }; + + public static int getTexAppearanceResource(int id) {//杩欐槸涓涓閿欑殑鍑芥暟锛岄槻姝㈣緭鍏ョ殑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) {//鑻ヨ緭鍏d澶т簬瀛椾綋缂栧彿鏈澶у硷紝鍒欒繑鍥為粯璁ゅ + return BG_DEFAULT_FONT_SIZE; + } + return TEXTAPPEARANCE_RESOURCES[id]; + } + + public static int getResourcesSize() { + return TEXTAPPEARANCE_RESOURCES.length; + }//鐩存帴杩斿洖涓鸿祫婧愮殑闀垮害 + } +} diff --git a/src/ui/NoteEditText.java b/src/ui/NoteEditText.java new file mode 100644 index 0000000..f18e72c --- /dev/null +++ b/src/ui/NoteEditText.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Selection; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.URLSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.widget.EditText; + +import net.micode.notes.R; + +import java.util.HashMap; +import java.util.Map; + +public class NoteEditText extends EditText {//缁ф壙EditText锛岃缃究绛捐缃枃鏈 + private static final String TAG = "NoteEditText";//绫荤殑鍚嶇О锛屽彲浠ョ敤鏉ヨ緭鍑烘棩蹇楁枃浠 + private int mIndex;//寤虹珛涓涓瓧绗﹀拰鏁存暟鐨勮〃锛屽瓨鏀剧數璇濆彿鐮併佺綉鍧銆侀偖绠 + private int mSelectionStartBeforeDelete;//澹版槑鏁村瀷鍙橀噺锛岃幏鍙栧垹闄ゆ枃鏈墠鐨勪綅缃 + + private static final String SCHEME_TEL = "tel:" ;//澹版槑瀛楃涓插父閲忥紝鏍囧織鐢佃瘽銆佺綉鍧銆侀偖浠 + private static final String SCHEME_HTTP = "http:" ;//鏂囨湰涓綉椤靛唴瀹 + private static final String SCHEME_EMAIL = "mailto:" ;//鏂囨湰涓偖浠跺唴瀹 + + private static final Map sSchemaActionResMap = new HashMap();//璁剧疆鏄犲皠锛屽皢鏂囨湰鍐呭锛堢數璇濄佺綉鍧銆侀偖浠讹級鍋氶摼鎺ュ鐞 + static { + 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); + }//鍦∟oteEditActivity杩涜鏂囨湰鐨勬搷浣滐紝鍒犻櫎锛岃繘鍏ワ紝缂栬緫 + + /** + * Call by the {@link NoteEditActivity} to delete or add edit text + */ + public interface OnTextViewChangeListener {//鎺ュ彛OnTextViewChangeListener锛屼細琚玁oteEditActivity澶ч噺璋冪敤锛屽叾涓畾涔変簡鍦ㄧ紪杈戞枃鏈腑delete銆佺偣鍑籩nter銆乼ext鏀瑰彉鏃剁殑鏂规硶 + /** + * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens + * and the text is null + */ + void onEditTextDelete(int index, String text);//褰揹elete閿寜涓嬫椂鍒犻櫎褰撳墠缂栬緫鐨勬枃瀛楀潡 + + /** + * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} + * happen + */ + void onEditTextEnter(int index, String text);//褰撹Е鍙戣緭鍏ユ枃鏈琄eyEvent鏃跺娣绘枃鏈 + + /** + * Hide or show item option when text change + */ + void onTextChange(int index, boolean hasText);//鏂囧瓧鏇存敼鏃堕殣钘忔垨鏄剧ず椤圭洰閫夐」 + } + + private OnTextViewChangeListener mOnTextViewChangeListener;//鏂板缓绉佹湁鍙橀噺锛氭枃鏈敼鍙樼殑鐩戝惉鍣 + + public NoteEditText(Context context) {//鏍规嵁context璁剧疆鏂囨湰 + super(context, null);//鐢╯uper寮曠敤鐖剁被鍙橀噺 + mIndex = 0;//璁剧疆褰撳墠鍏夋爣 + } + + public void setIndex(int index) { + mIndex = index; + }//鍒濆鍖栨枃鏈慨鏀规爣璁,鏇存柊鍏夋爣鎸囧悜鐨勭储寮曞 + + public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { + mOnTextViewChangeListener = listener; + }//璁剧疆鏂囨湰瑙嗗浘鍙樺寲鐩戝惉鍣 + + public NoteEditText(Context context, AttributeSet attrs) {//鑷畾涔夌┖鎺т欢灞炴э紝鐢ㄤ簬缁存姢渚跨鍔ㄦ佸彉鍖栵紝杩欎釜鍑芥暟鍔熻兘涓哄垵濮嬪寲渚跨 + super(context, attrs, android.R.attr.editTextStyle); + } + +// public NoteEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle);\ + // TODO Auto-generated constructor stub + }//鑷畾涔夋帶浠讹紝鐢ㄤ簬缁存姢渚跨鍔ㄦ佸彉鍖栫殑灞炴с傚垵濮嬪寲渚跨 + + @Override + public boolean onTouchEvent(MotionEvent event) {//view閲岀殑鍑芥暟锛屽鐞嗘墜鏈哄睆骞曠殑鎵鏈変簨浠躲傚弬鏁癳vent涓烘墜鏈哄睆骞曡Е鎽镐簨浠跺皝瑁呯被鐨勫璞★紝鍏朵腑灏佽浜嗚浜嬩欢鐨勬墍鏈変俊鎭紝渚嬪瑙︽懜鐨勪綅缃佽Е鎽哥殑绫诲瀷浠ュ強瑙︽懜鐨勬椂闂寸瓑銆傝瀵硅薄浼氬湪鐢ㄦ埛瑙︽懜鎵嬫満灞忓箷鏃惰鍒涘缓銆 + switch (event.getAction()) {//閲嶅啓灞忓箷瑙﹀彂浜嬩欢 + case MotionEvent.ACTION_DOWN://鏇存柊鍧愭爣 + + int x = (int) event.getX();//鏇存柊x鍊间负褰撳墠瑙︽懜澶勭殑x鍊 + int y = (int) event.getY();//鏇存柊y鍊间负褰撳墠瑙︽懜澶勭殑y鍊 + x -= getTotalPaddingLeft();//鍑忓幓宸﹁竟鎺т欢鐨勮窛绂 + y -= getTotalPaddingTop();//鍑忓幓涓婃柟鎺т欢鐨勮窛绂 + x += getScrollX(); + y += getScrollY();//鍔犱笂婊氳疆婊氳繃鐨勮窛绂 + + Layout layout = getLayout();//鐢ㄥ竷灞鎺т欢layout鏍规嵁x,y鐨勬柊鍊艰缃柊鐨勪綅缃 + int line = layout.getLineForVertical(y);//鑾峰彇绾靛悜琛屾暟 + int off = layout.getOffsetForHorizontal(line, x);//鑾峰彇妯悜鍋忕Щ閲 + Selection.setSelection(getText(), off);//鏇存柊鍏夋爣浣嶇疆 + break; + } + + return super.onTouchEvent(event);//璋冪敤鐖剁被锛屽綋灞忓箷鏈塗ouch浜嬩欢鏃讹紝姝ゆ柟娉曞氨浼氳璋冪敤銆 + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) {//澶勭悊鐢ㄦ埛鎸変笅涓涓敭鐩樻寜閿椂浼氳Е鍙 鐨勪簨浠 + switch (keyCode) {//鏍规嵁鎸夐敭鐨凨eyCode鏉ュ鐞 + case KeyEvent.KEYCODE_ENTER://鎸変笅鍥炶溅鏃,濡傛灉mOnTextViewChangeListener瀛樺湪鍒欒繑鍥瀎alse + if (mOnTextViewChangeListener != null) { + return false; + } + break; + case KeyEvent.KEYCODE_DEL://鍒犻櫎鎸夐敭 + mSelectionStartBeforeDelete = getSelectionStart();//鑾峰彇鍒犻櫎鏂囨湰寮濮嬩綅缃 + break; + default://鍏朵粬鎯呭喌,杩斿洖鐖剁被鐨刼nKeyDown鍊 + break; + } + return super.onKeyDown(keyCode, event);//缁х画鎵ц鐖剁被鐨勫叾浠栫偣鍑讳簨浠 + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) {//澶勭悊鐢ㄦ埛鏉惧紑涓涓敭鐩樻寜閿椂浼氳Е鍙戠殑浜嬩欢 + switch(keyCode) {//鏍规嵁鎸夐敭鐨 Unicode 缂栫爜鍊兼潵澶勭悊锛屾湁鍒犻櫎鍜岃繘鍏2绉嶆搷浣 + case KeyEvent.KEYCODE_DEL://鑻ヨЕ鍙戜慨鏀逛笖鏂囨。涓嶄负绌猴紝鍒欒皟鐢ㄥ墠闈唬鐮佺殑onEditTextDelete鍑芥暟杩涜鏂囨湰鍒犻櫎 + if (mOnTextViewChangeListener != null) { + if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); + return true;//鍒╃敤涓婃枃OnTextViewChangeListener瀵筀EYCODE_DEL鎸夐敭鎯呭喌鐨勫垹闄ゅ嚱鏁拌繘琛屽垹闄 + } + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted");//鍏朵粬鎯呭喌鎶ラ敊锛屾枃妗g殑鏀瑰姩鐩戝惉鍣ㄥ苟娌℃湁寤虹珛 + } + break; + case KeyEvent.KEYCODE_ENTER://鑻ユ枃妗f敼鍔ㄧ洃鍚櫒宸插缓绔嬶紝鍒欒幏鍙栧綋鍓嶄綅缃拰鏂囨湰锛屽苟鏍规嵁鑾峰彇鐨勪俊鎭皟鐢╫nEditTextEnter鍑芥暟杩涜鏂囨湰澧炴坊 + if (mOnTextViewChangeListener != null) { + int selectionStart = getSelectionStart(); + String text = getText().subSequence(selectionStart, length()).toString();//鑾峰彇閫夋嫨鍖哄煙鍚庨潰鐨勬枃鏈俊鎭 + setText(getText().subSequence(0, selectionStart));//瀹炵幇鏂囨湰鎹㈣鐨勫姛鑳 + mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);//灏嗛夋嫨鍖哄煙鍐呯殑鏂囧瓧绉诲埌涓嬩竴琛 + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted");//鍏朵粬鎯呭喌鎶ラ敊锛岀洃鍚櫒OnTextViewChangeListener骞舵病鏈夊缓绔嬨 + } + break; + default: + break; + } + return super.onKeyUp(keyCode, event);//缁х画鎵ц鐖剁被鐨勫叾浠栨寜閿脊璧风殑浜嬩欢 + } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {//杩欎釜鍑芥暟瑙勫畾浜嗙紪杈戞枃鏈劍鐐规敼鍙樻椂鐨勭郴缁熷搷搴 + if (mOnTextViewChangeListener != null) {//鑻ョ洃鍚櫒宸插缓绔 + if (!focused && TextUtils.isEmpty(getText())) {//鑾峰彇鍒扮劍鐐瑰苟涓旀枃鏈笉涓虹┖ + mOnTextViewChangeListener.onTextChange(mIndex, false);//缃甪alse闅愯棌浜嬩欢閫夐」 + } else { + mOnTextViewChangeListener.onTextChange(mIndex, true);//缃畉rue鏄剧ず浜嬩欢閫夐」 + } + } + super.onFocusChanged(focused, direction, previouslyFocusedRect);//鎵ц鐖剁被鐨勫叾浠栫劍鐐瑰彉鍖栫殑浜嬩欢 + } + + @Override + protected void onCreateContextMenu(ContextMenu menu) {//鐢熸垚涓婁笅鏂囪彍鍗 + if (getText() instanceof Spanned) {//鑻ユ湁鏂囨湰瀛樺湪 + int selStart = getSelectionStart(); + int selEnd = getSelectionEnd();////鑾峰彇鏂囨湰寮濮嬬粨灏句綅缃 + + int min = Math.min(selStart, selEnd); +// int max = Math.max(selStart, selEnd);//鑾峰彇寮濮嬪埌缁撳熬鐨勬渶澶с佹渶灏忓 + final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);//璁剧疆url鐨勪俊鎭殑鑼冨洿鍊 + if (urls.length == 1) { + int defaultResId = 0;// 榛樿鐨勮祫婧怚D鍊间负0 + for(String schema: sSchemaActionResMap.keySet()) {//鑾峰彇璁″垝琛ㄤ腑鎵鏈夌殑key鍊 + if(urls[0].getURL().indexOf(schema) >= 0) { + defaultResId = sSchemaActionResMap.get(schema); + break; + }//鑻rl鍙互娣诲姞鍒欏湪娣诲姞鍚庡皢defaultResId缃负key鎵鏄犲皠鐨勫 + } + if (defaultResId == 0) { + defaultResId = R.string.note_link_other; + }//defaultResId == 0鍒欒鏄巙rl骞舵病鏈夋坊鍔犱换浣曚笢瑗匡紝鎵浠ョ疆涓鸿繛鎺ュ叾浠朣chemaActionResMap鐨勫 + 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; + } + }); + } + } + super.onCreateContextMenu(menu);//鍒涘缓鏂囨湰鑿滃崟 + } +} diff --git a/src/ui/NoteItemData.java b/src/ui/NoteItemData.java new file mode 100644 index 0000000..3473ca0 --- /dev/null +++ b/src/ui/NoteItemData.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; + +import net.micode.notes.data.Contact; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.DataUtils; + + +public class NoteItemData {//渚跨鏁版嵁鐨勮褰曪紝鍖呮嫭鍚勭甯搁噺锛屽弬鏁 + static final String [] PROJECTION = new String [] {//灏嗕究绛緄d锛屾彁閱掓椂闂达紝鑳屾櫙棰滆壊id锛屽垱寤烘椂闂达紝鍏宠仈妗岄潰鎸備欢锛屼慨鏀规椂闂达紝渚跨鏁伴噺锛岀埗鏂囦欢澶筰d锛屾枃浠跺すid锛堜究绛剧殑涓娈靛唴瀹癸級锛屼究绛剧殑绉嶇被锛屾闈㈡寕浠剁殑id锛屾闈㈡寕浠剁殑绫诲瀷鐨勫悕绉版斁鍦ㄤ竴涓狿ROJECTION鐨勫瓧绗︿覆鏁扮粍閲 + NoteColumns.ID,//姣忎釜渚跨鐨勫簭鍙稩D + NoteColumns.ALERTED_DATE,//鎻愰啋鏃ユ湡 + NoteColumns.BG_COLOR_ID,//鑳屾櫙棰滆壊鐨勫簭鍙穒d + NoteColumns.CREATED_DATE,//鍒涘缓鏃ユ湡 + NoteColumns.HAS_ATTACHMENT,//鏄惁鍚湁闄勪欢 + NoteColumns.MODIFIED_DATE,//淇敼鏃ユ湡 + NoteColumns.NOTES_COUNT,//渚跨鏁伴噺 + NoteColumns.PARENT_ID, + NoteColumns.SNIPPET,//鏂囦欢澶瑰悕绉版垨鑰呮枃鏈敞閲婂唴瀹 + NoteColumns.TYPE,//note鐨勭绫 + NoteColumns.WIDGET_ID,//鎸備欢id + NoteColumns.WIDGET_TYPE,//鎸備欢鐨勭被鍨 + }; + + private static final int ID_COLUMN = 0; + private static final int ALERTED_DATE_COLUMN = 1; + private static final int BG_COLOR_ID_COLUMN = 2; + private static final int CREATED_DATE_COLUMN = 3; + private static final int HAS_ATTACHMENT_COLUMN = 4; + private static final int MODIFIED_DATE_COLUMN = 5; + private static final int NOTES_COUNT_COLUMN = 6; + private static final int PARENT_ID_COLUMN = 7; + private static final int SNIPPET_COLUMN = 8; + private static final int TYPE_COLUMN = 9; + private static final int WIDGET_ID_COLUMN = 10; + private static final int WIDGET_TYPE_COLUMN = 11; + + private long mId;//PROJECT鐨勫瓧绗︿覆鍚嶇О瀵瑰簲鐨勫 + private long mAlertDate; + private int mBgColorId; + 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;//瀹藉害 + 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) {//鍒濆鍖朜oteItemData,鍒╃敤鍏夋爣鍜宑ontext鑾峰彇鐨勫唴瀹 + mId = cursor.getLong(ID_COLUMN);//鑾峰彇鎸囧畾鏁版嵁锛屽苟浠ype绫诲瀷浼犲洖 + mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); + mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); + mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;//鍒ゆ柇琛屽垪 + mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); + mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); + mParentId = cursor.getLong(PARENT_ID_COLUMN); + mSnippet = cursor.getString(SNIPPET_COLUMN);//鑾峰緱瀛楃涓 + mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(//鎶婃瘡椤瑰墠鐨勬柟妗嗙鍙峰拰鉁旂鍙峰幓鎺 + NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); + mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); + + mPhoneNumber = "";//鍒濆鍖栫數璇濆彿鐮佺殑淇℃伅 + if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {//濡傛灉鐖舵枃浠剁殑id鏄痗all_record_folder + mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);//浣跨敤DataUtils绫讳腑瀹氫箟鐨勫嚱鏁拌幏鍙栫數璇濆彿鐮佷俊鎭 + if (!TextUtils.isEmpty(mPhoneNumber)) {//鑻phonenumber閲屾湁绗﹀悎瀛楃涓诧紝鍒欑敤contart鍔熻兘杩炴帴 + mName = Contact.getContact(context, mPhoneNumber);//閫氳繃phonenumber锛岃皟鐢╣etContact锛屽埄鐢ㄩ敭鍊煎鑾峰彇瀵瑰簲鐨刵ame + if (mName == null) { + mName = mPhoneNumber;//鑻ユ湭淇濆瓨鍚嶅瓧锛屼互鍙风爜鍛藉悕 + } + } + } + + if (mName == null) { + mName = ""; + }//鑻ユ病鏈夊name澶嶅埗鎴愬姛锛屽垯鎶妌ame璁剧疆涓虹┖鍊 + checkPostion(cursor);//妫鏌ュ厜鏍囦綅缃 + } + + private void checkPostion(Cursor cursor) {//鏍规嵁鍏夋爣浣嶇疆璁剧疆鏍囪 + mIsLastItem = cursor.isLast() ? true : false;//鍒嗗埆涓哄悇绉嶆弿杩扮姸鎬佺殑鍙橀噺杩涜璧嬪 + mIsFirstItem = cursor.isFirst() ? true : false; + mIsOnlyOneItem = (cursor.getCount() == 1); + mIsMultiNotesFollowingFolder = false; + mIsOneNoteFollowingFolder = false;//鍒濆鍖栤滃閲嶅瓙鏂囦欢鈥濃滃崟涓瀛愭枃浠垛2涓爣璁 + + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {//濡傛灉瀵硅薄鐨勭被鍨嬩负渚跨涓斾笉涓虹涓涓」 + int position = cursor.getPosition();//鑾峰緱鍏夋爣浣嶇疆 + if (cursor.moveToPrevious()) {//濡傛灉鍏夋爣绉诲姩 + if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER//鑻ュ厜鏍囨弧瓒砈YSTEM鎴朏OLDER鏍煎紡 + || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { + if (cursor.getCount() > (position + 1)) {//鑻ュ厜鏍囦綅缃ぇ浜庡綋鍓嶄綅缃紝缃渚跨瀛愭枃浠跺す鏍囪涓虹湡 + mIsMultiNotesFollowingFolder = true;//璁剧疆涓哄閲嶄究绛 + } else { + mIsOneNoteFollowingFolder = true; + } + } + if (!cursor.moveToNext()) {//鑻ュ厜鏍囦笉鑳介噸鏂板悜涓嬭蛋鍒欐姤閿 + throw new IllegalStateException("cursor move to previous but can't move back"); + } + } + } + } + + public boolean isOneFollowingFolder() { + return mIsOneNoteFollowingFolder; + }//鍒ゆ柇鏄惁鍙湁涓涓究绛 + + public boolean isMultiFollowingFolder() { + return mIsMultiNotesFollowingFolder; + }//鑻ユ暟鎹埗id涓轰繚瀛樿嚦鏂囦欢澶规ā寮忕殑id涓旀弧瓒崇數璇濆彿鐮佸崟鍏冧笉涓虹┖锛屽垯isCallRecord涓簍rue + + public boolean isLast() { + return mIsLastItem; + }//鍒ゆ柇鏄惁涓烘渶鍚庝竴涓」 + + public String getCallName() { + return mName; + }//鑾峰緱渚跨鐨勫鍚 + + public boolean isFirst() { + return mIsFirstItem; + }//鍒ゆ柇鏄惁鏄涓涓」 + + public boolean isSingle() { + return mIsOnlyOneItem; + }//鍒ゆ柇鏄惁鍙湁涓涓」 + + public long getId() { + return mId; + }//鑾峰緱瀵瑰簲鐨処D鍊 + + public long getAlertDate() { + return mAlertDate; + }//鑾峰彇璁剧疆鐨勬彁閱掓椂闂 + + public long getCreatedDate() { + return mCreatedDate; + }//鑾峰彇鍒涘缓鏃堕棿 + + public boolean hasAttachment() { + return mHasAttachment; + }//鍒ゆ柇鏄惁鍏宠仈妗岄潰鎸備欢 + + public long getModifiedDate() { + return mModifiedDate; + }//鑾峰緱淇敼鍚庢椂闂 + + public int getBgColorId() { + return mBgColorId; + }//鑾峰彇鑳屾櫙棰滆壊 + + public long getParentId() { + return mParentId; + }//鑾峰彇鐖惰繘绋媔d + + public int getNotesCount() { + return mNotesCount; + }//鑾峰彇渚跨鏁伴噺 + + public long getFolderId () { + return mParentId; + }//鑾峰彇鏂囦欢澶筰d + + public int getType() { + return mType; + }//鑾峰緱椤圭殑绫诲瀷 + + public int getWidgetType() { + return mWidgetType; + }//鑾峰彇鎸備欢绫诲瀷 + + public int getWidgetId() { + return mWidgetId; + }//鑾峰彇鎸備欢id + + public String getSnippet(){ + return mSnippet; + }//鑾峰彇渚跨鐨勫瑙傜墖娈 + public boolean hasAlert() { + return (mAlertDate > 0); + }//鍒よ姝や究绛炬槸鍚︽湁鎻愰啋鍔熻兘 + + public boolean isCallRecord() { + return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); + }//濡傛灉鐖剁被id淇濆瓨鑷虫枃浠跺す妯″紡骞朵笖鐢佃瘽鍙风爜鍗曞厓涓嶄负绌 + + public static int getNoteType(Cursor cursor) { + return cursor.getInt(TYPE_COLUMN); + }//鑾峰彇渚跨绫诲瀷 +} diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java new file mode 100644 index 0000000..39f7772 --- /dev/null +++ b/src/ui/NotesListActivity.java @@ -0,0 +1,954 @@ +/* + * 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.app.Dialog; +import android.appwidget.AppWidgetManager; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; +import android.view.View.OnTouchListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.PopupMenu; +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; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.tool.BackupUtils; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashSet; + +public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {//鍦ㄧ被鐨勫0鏄庝腑锛岄氳繃鍏抽敭瀛梕xtends鏉ュ垱寤轰竴涓被鐨勫瓙绫汇備竴涓被閫氳繃鍏抽敭瀛梚mplements澹版槑鑷繁浣跨敤涓涓垨鑰呭涓帴鍙c + 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;//淇敼鑿滃崟鏂囦欢鍚嶇О鍔熻兘 + + 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;//渚跨鍒楄〃閰嶉傚櫒 + + private ListView mNotesListView;//涓荤晫闈㈢殑瑙嗗浘 + + private Button mAddNewNote;//鏈涓嬫柟娣诲姞渚跨鐨勬寜閽 + + private boolean mDispatch;//鏄惁璋冨害鐨勫垽鏂彉閲 + + private int mOriginY;//棣栨瑙︽懜鏃跺睆骞曚笂鐨勫瀭鐩磋窛绂伙紙y鍊硷級 + + private int mDispatchY;//閲嶆柊璋冨害鏃剁殑瑙︽懜鐨勫湪灞忓箷涓婄殑鍨傜洿璺濈 + + private TextView mTitleBar;//瀛愭枃浠跺す涓嬫爣澶 + + private long mCurrentFolderId;//褰撳墠鏂囦欢澶圭殑ID + + private ContentResolver mContentResolver;//鎻愪緵鍐呭鍒嗘瀽 + + private ModeCallback mModeCallBack;//杩斿洖璋冪敤鏂规硶 + + 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;//鎵撳紑鏌愪釜渚跨鏃讹紝鍒涘缓娲诲姩鐨勮姹傜爜 + + @Override//鍔熻兘涓篈ctivity鐢熷懡鍛ㄦ湡寮濮嬫椂璋冪敤鐨勫嚱鏁帮紝鐢ㄦ潵淇濆瓨璇诲彇鐘舵侊紝璁剧疆鐣岄潰 + protected void onCreate(Bundle savedInstanceState) {//鍒涘缓绫 + super.onCreate(savedInstanceState);//寮曠敤鐖剁被瀵硅薄 + setContentView(R.layout.note_list);//璁剧疆鍐呭鐨勮鍥 + initResources();//鍒濆鍖栫被鐨勮祫婧 + + /** + * Insert an introduction when user firstly use this application + */ + setAppInfoFromRawRes();//褰撶敤鎴风涓娆¤闂瓵PP鏃舵彁渚涚浉鍏充俊鎭 + } + + @Override// 浠h〃鎵ц杩欎釜鏂规硶鏃讹紝閲嶅啓骞惰皟鐢ㄤ簡鐖剁被鐨勬柟娉 + protected void onActivityResult(int requestCode, int resultCode, Intent data) {//杩斿洖涓浜涘瓙妯″潡瀹屾垚鐨勬暟鎹氦缁欎富Activity澶勭悊 + if (resultCode == RESULT_OK//褰撹閿姣佺殑娲诲姩鏄墦寮渚跨鎴栬呮柊寤轰究绛句笖缁撴灉鐮佸尮閰嶆椂,鎶婁粠缂栬緫鐣岄潰鐨勫厜鏍囨暟鎹垹闄 + && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { + mNotesListAdapter.changeCursor(null); + } else {//濡傛灉鏉′欢涓嶆弧瓒筹紝鍒欏皢鏁版嵁杩斿洖缁欑埗绫伙紝鍗宠皟鐢ㄧ埗绫荤殑onActivityResult()銆 + super.onActivityResult(requestCode, resultCode, data);//璋冪敤鐖剁被Activity鐨刼nActivityResult鏂规硶 灏嗘暟鎹繑鍥炵粰鐖剁被澶勭悊 + } + } + + private void setAppInfoFromRawRes() {//鍒╃敤鍘熷璧勬簮鏂囦欢璁剧疆APP鐨勭浉鍏充俊鎭 + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);//淇濆瓨閰嶇疆鍙傛暟 + if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {//鍒ゆ柇鍋忓ソ锛屽鍔犺鏄 + StringBuilder sb = new StringBuilder();//璇诲彇鍘熺敓璧勬簮淇℃伅 + InputStream in = null;//杈撳叆娴佸垵濮嬭缃负绌 + try {//浣跨敤getResources鑾峰彇璧勬簮鍚,浠penRawResource鏂规硶锛堜笉甯﹀悗缂鐨勮祫婧愭枃浠跺悕锛夋墦寮杩欎釜鏂囦欢銆 + in = getResources().openRawResource(R.raw.introduction);//鍔犺浇Welcome to use MIUI notes + if (in != null) {//濡傛灉淇℃伅涓嶄负绌 + InputStreamReader isr = new InputStreamReader(in);//浣跨敤鎸囧畾鐨勫瓧绗﹂泦璇诲彇瀛楄妭骞跺皢瀹冧滑瑙g爜涓哄瓧绗 + BufferedReader br = new BufferedReader(isr);//鍒嗛厤缂撳啿鍖鸿鍙栫┖闂 + char [] buf = new char[1024]; + int len = 0; + while ((len = br.read(buf)) > 0) {//涓嶆柇鍦╞uf涓鍙栨暟鎹斁鍏b閲 + sb.append(buf, 0, len);//浣跨敤append鍑芥暟鍦ㄦ寚瀹氬厓绱犵殑缁撳熬鎻掑叆鍐呭 + } + } else { + Log.e(TAG, "Read introduction file error");//鎶ラ敊锛岃鍙栨枃浠堕敊璇 + return; + } + } catch (IOException e) {//IO鍑洪敊锛屾墦鍗板紓甯镐俊鎭 + e.printStackTrace();//鍦ㄥ懡浠よ鎵撳嵃寮傚父淇℃伅鍦ㄧ▼搴忎腑鍑洪敊鐨勪綅缃強鍘熷洜 + return; + } finally {//蹇呯劧鎵ц閮ㄥ垎 + if(in != null) { + try { + in.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace();//杈撳嚭鏍堣建杩 + } + } + } + + WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,//鍒涘缓鏂颁究绛 + AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, + ResourceParser.RED); + note.setWorkingText(sb.toString());//璁剧疆鏂囨湰鏁版嵁 + if (note.saveNote()) {//鑻ュ瓨鍌ㄦ潯浠舵垚绔嬶紝淇濆瓨璇ヤ究绛 + sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();//鏇存柊淇濆瓨note鐨勪俊鎭 + } else { + Log.e(TAG, "Save introduction note error"); + return; + }//鑻ヤ究绛句繚瀛樹笉鎴愬姛锛屽垯鎶婇敊璇俊鎭墦鍗板埌鏃ュ織閲 + } + } + + @Override + protected void onStart() {//浠h〃activity鐢熷懡鍛ㄦ湡寮濮 + super.onStart();//璋冪敤鐖剁被锛屽惎鍔ㄦ椿鍔 + startAsyncNotesListQuery();//鍚屾鍒楄〃涓殑渚跨淇℃伅 + } + + private void initResources() {//鍒濆鍖栬祫婧 + mContentResolver = this.getContentResolver();//鑾峰彇搴旂敤绋嬪簭鐨勬暟鎹 + mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());//鍔ㄦ佸垱寤哄悗鍙拌姹傚鐞嗗櫒鐨勫疄渚 + mCurrentFolderId = Notes.ID_ROOT_FOLDER;//璁剧疆褰撳墠鏂囦欢澶笽D鏄牴鐩綍ID + mNotesListView = (ListView) findViewById(R.id.notes_list);//鏍规嵁R鏂囦欢涓殑id鍊兼煡璇㈠埌鐩稿簲鐨刅iew,鐒跺悗杩斿洖 + mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), + null, false); + mNotesListView.setOnItemClickListener(new OnListItemClickListener());//璁剧疆椤圭洰鐐瑰嚮鐩戝惉 + mNotesListView.setOnItemLongClickListener(this);//璁剧疆闀挎寜鐩戝惉鍣 + mNotesListAdapter = new NotesListAdapter(this);//鍒涘缓渚跨瑙嗗浘閰嶇疆鍣 + mNotesListView.setAdapter(mNotesListAdapter); + mAddNewNote = (Button) findViewById(R.id.btn_new_note);//鍦╝ctivity涓鑾峰彇璇ユ寜閽 + mAddNewNote.setOnClickListener(this);//鐐瑰嚮鐩戝惉 + mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());//灞忓箷鐩戝惉 + mDispatch = false;//鐢ㄤ簬鏂板缓渚跨妯″潡 + mDispatchY = 0;//鍒濆y鍊艰缃负0 + mOriginY = 0;//鍔犺浇鏂囦欢澶逛笅鐨勬爣澶磋祫婧 + mTitleBar = (TextView) findViewById(R.id.tv_title_bar);//鍔犺浇鏂囦欢澶逛笅鐨勬爣澶磋祫婧 + mState = ListEditState.NOTE_LIST;//璁剧疆鐘舵佷负涓荤晫闈 + mModeCallBack = new ModeCallback();//瀵逛究绛剧殑鏂规硶璋冪敤锛屽寘鎷垹闄ゅ拰绉诲姩 + } + + private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {//implements澹版槑鑷繁浣跨敤涓涓垨澶氫釜鎺ュ彛 + private DropdownMenu mDropDownMenu;//涓嬫媺鑿滃崟 + private ActionMode mActionMode;//鍔ㄤ綔鏂瑰紡 + private MenuItem mMoveMenu;//绉诲姩鑿滃崟 + + public boolean onCreateActionMode(ActionMode mode, Menu menu) {//鏍规嵁鑿滃崟鍜屽姩浣滃垱寤烘柊鍔ㄤ綔 + getMenuInflater().inflate(R.menu.note_list_options, menu);//閰嶇疆menu鐨勫竷灞 + menu.findItem(R.id.delete).setOnMenuItemClickListener(this);//涓轰究绛惧垹闄ゆā鍧楄瀹氱洃鍚櫒 + mMoveMenu = menu.findItem(R.id.move);//鑾峰彇鑿滃崟椤圭洰 + if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER//鑻ョ埗绫籭d鍦ㄦ枃浠跺す涓繚瀛樻垨鑰呯敤鎴锋枃浠舵暟閲忎负闆讹紝璁剧疆绉诲姩鑿滃崟涓轰笉鍙锛屽惁鑰呰涓哄彲瑙 + || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this);//闀挎寜鏌愪竴鏍囩鏃讹紝鎵ц杩欎釜绉诲姩鍒版枃浠跺す鎸夐敭鍙樺緱鍙 + } + mActionMode = mode; + mNotesListAdapter.setChoiceMode(true);//杩涘叆閫夋嫨妯″紡 + mNotesListView.setLongClickable(false);//鍏抽棴闀挎寜鍒楄〃椤瑰彂鐢熶簨浠跺姛鑳 + mAddNewNote.setVisibility(View.GONE);//闅愯棌浜嗘柊澧炰究绛炬寜閽 + + View customView = LayoutInflater.from(NotesListActivity.this).inflate(//璁剧疆鐢ㄦ埛鍙鍖栫晫闈 + R.layout.note_list_dropdown_menu, null);//鍔犺浇涓嬫媺鑿滃崟鐨勫竷灞 + mode.setCustomView(customView); + mDropDownMenu = new DropdownMenu(NotesListActivity.this,//鍒涘缓涓嬫媺鑿滃崟鐨勫疄渚嬪璞 + (Button) customView.findViewById(R.id.selection_menu), + R.menu.note_list_dropdown);//涓簐iew娣诲姞dropDownMenu + mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ + public boolean onMenuItemClick(MenuItem item) {//鐐瑰嚮鑿滃崟鏃讹紝璁剧疆涓哄叏閫夊苟鏇存柊鑿滃崟 + mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());//瀵瑰簲涓嬫媺鑿滃崟閲岀殑鍏ㄩ夋寜閿 + updateMenu();//鏇存柊鑿滃崟 + return true; + } + + }); + return true; + } + + private void updateMenu() {//鐢ㄤ簬鐢ㄦ埛鍦ㄨ繘琛屼簡鍕鹃変箣鍚庯紝鏇存柊鍒楄〃 + int selectedCount = mNotesListAdapter.getSelectedCount();//鑾峰彇琚嬀閫夌殑鏉$洰鏁伴噺 + // Update dropdown menu + String format = getResources().getString(R.string.menu_select_title, selectedCount);//浠庡師濮嬭祫婧愪腑璇诲彇淇℃伅鏇存敼涓嬫媺鑿滃崟鍐呭 + mDropDownMenu.setTitle(format);//鏇存敼鏍囬 + MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);//鍏ㄩ夋搷浣 + if (item != null) {//褰撳叏閫夋垚鍔燂紝鍒欏皢鈥滃叏閫夆濊彍鍗曢」鏀逛负鈥滃彇娑堝叏閫夆濊彍鍗曪紝鍚﹀垯浠嶄繚鎸佲滃叏閫夆濊彍鍗曢」 + if (mNotesListAdapter.isAllSelected()) {//濡傛灉渚跨鍒楄〃閫傞厤鍣ㄥ浜庡叏閫夌姸鎬侊紝璁剧疆涓哄凡鍕鹃夛紝骞舵彁渚涘彇娑堝叏閫夋搷浣滐紱鍚﹀垯璁剧疆涓烘湭鍕鹃夛紝鎻愪緵鍏ㄩ夋搷浣 + item.setChecked(true);//鍙栨秷鍏ㄩ + item.setTitle(R.string.menu_deselect_all); + } else { + item.setChecked(false); + item.setTitle(R.string.menu_select_all); + } + } + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) {//鍑嗗娲诲姩妯″紡 + // TODO Auto-generated method stub + return false; + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) {//鑿滃崟鍔ㄤ綔瑙﹀彂鏍囪 + // TODO Auto-generated method stub + return false; + } + + public void onDestroyActionMode(ActionMode mode) {//閿姣佸姩浣滄ā寮忥紝璁剧疆渚跨鍙 + mNotesListAdapter.setChoiceMode(false);//璁剧疆绗旇鍒楄〃閫傞厤鍣ㄩ夋嫨鏂瑰紡 + mNotesListView.setLongClickable(true);//闀挎寜鎿嶄綔 + mAddNewNote.setVisibility(View.VISIBLE);//璁剧疆鏂板缓鐨勭瑪璁颁负鍙 + } + + public void finishActionMode() { + mActionMode.finish(); + }//鍔ㄤ綔妯″紡缁撴潫,鑿滃崟鍕鹃夌姸鎬佹敼鍙 + + public void onItemCheckedStateChanged(ActionMode mode, int position, long id,//鍕鹃夌姸鎬佹敼鍙樻椂锛屾洿鏀瑰嬀閫夋爣蹇楋紝鏇存柊鑿滃崟 + boolean checked) {//鐐瑰嚮鑿滃崟閫夐」瑙﹀彂鎿嶄綔 + mNotesListAdapter.setCheckedItem(position, checked);//鏀瑰彉鍕鹃夌殑鐘舵 + updateMenu();//鏇存柊鑿滃崟 + } + + public boolean onMenuItemClick(MenuItem item) {//鍒ゆ柇鑿滃崟鏄惁琚偣鍑 + if (mNotesListAdapter.getSelectedCount() == 0) {// 褰撳嬀閫夋暟涓洪浂鏃讹紙鍗虫湭鐐瑰嚮锛夛紝鍒涘缓鏂囨湰骞舵樉绀 + Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), + Toast.LENGTH_SHORT).show(); + return true; + } + + switch (item.getItemId()) {//鏍规嵁id鍙峰垽鏂槸鍒犻櫎杩樻槸绉诲姩 + case R.id.delete://鍒犻櫎鑿滃崟椤 + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);//璀﹀憡瀵硅瘽妗 + builder.setTitle(getString(R.string.alert_title_delete));//璁剧疆鈥滃垹闄ら変腑鐨勪究绛锯濈殑title + builder.setIcon(android.R.drawable.ic_dialog_alert);//璁剧疆鎻愰啋鍒犻櫎鐨勫浘鐗 + builder.setMessage(getString(R.string.alert_message_delete_notes, + mNotesListAdapter.getSelectedCount()));//璁剧疆璀﹀憡瀵硅瘽妗嗙殑鍥炬爣 + builder.setPositiveButton(android.R.string.ok,//璁剧疆鍚﹀畾鎸夐挳 + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + batchDelete();//鎵ц鎵归噺鍒犻櫎鎿嶄綔 + } + }); + builder.setNegativeButton(android.R.string.cancel, null);//鍙栨秷鎸夐敭鐨勮鍥 + builder.show(); + break; + case R.id.move://绉诲姩渚跨鍒版枃浠跺す + startQueryDestinationFolders();//寮濮嬫煡璇㈢洰鏍囨枃浠 + break; + default: + return false; + } + return true; + } + } + + private class NewNoteOnTouchListener implements OnTouchListener {//瑙︽懜渚跨鐩戝惉鍣 + + public boolean onTouch(View v, MotionEvent event) {//瑙︽懜澶勭悊鍑芥暟 + switch (event.getAction()) {//浜嬩欢璋冪敤鑾峰彇琛屼负鍑芥暟锛屾牴鎹笉鍚屽姩浣滐紝瀵瑰簲涓嶅悓鎿嶄綔 + case MotionEvent.ACTION_DOWN: {//鑻ユ槸鍒涘缓鏂颁究绛撅紝閫氳繃璁$畻璋冩暣鐣岄潰澶у皬 + Display display = getWindowManager().getDefaultDisplay(); + int screenHeight = display.getHeight();//鑾峰彇灞忓箷楂樺害 + int newNoteViewHeight = mAddNewNote.getHeight();//鑾峰彇鏂板渚跨鐨勯珮搴 + int start = screenHeight - newNoteViewHeight; + int eventY = start + (int) event.getY(); + /** + * Minus TitleBar's height + */ + if (mState == ListEditState.SUB_FOLDER) { + eventY -= mTitleBar.getHeight(); + start -= mTitleBar.getHeight(); + } + /** + * HACKME:When click the transparent part of "New Note" button, dispatch + * the event to the list view behind this button. The transparent part of + * "New Note" button could be expressed by formula y=-0.12x+94锛圲nit:pixel锛 + * and the line top of the button. The coordinate based on left of the "New + * Note" button. The 94 represents maximum height of the transparent part. + * Notice that, if the background of the button changes, the formula should + * also change. This is very bad, just for the UI designer's strong requirement. + */ + if (event.getY() < (event.getX() * (-0.12) + 94)) { + View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 + - mNotesListView.getFooterViewsCount()); + if (view != null && view.getBottom() > start + && (view.getTop() < (start + 94))) { + mOriginY = (int) event.getY(); + mDispatchY = eventY; + event.setLocation(event.getX(), mDispatchY); + mDispatch = true; + return mNotesListView.dispatchTouchEvent(event); + } + } + break; + } + case MotionEvent.ACTION_MOVE: { + if (mDispatch) { + mDispatchY += (int) event.getY() - mOriginY; + event.setLocation(event.getX(), mDispatchY); + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + default: { + if (mDispatch) { + event.setLocation(event.getX(), mDispatchY); + mDispatch = false; + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + } + return false; + } + + }; + + private void startAsyncNotesListQuery() { + String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION + : NORMAL_SELECTION; + mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { + String.valueOf(mCurrentFolderId) + }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); + } + + private final class BackgroundQueryHandler extends AsyncQueryHandler { + public BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + switch (token) { + case FOLDER_NOTE_LIST_QUERY_TOKEN: + mNotesListAdapter.changeCursor(cursor); + break; + case FOLDER_LIST_QUERY_TOKEN: + if (cursor != null && cursor.getCount() > 0) { + showFolderListMenu(cursor); + } else { + Log.e(TAG, "Query folder failed"); + } + break; + default: + return; + } + } + } + + private void showFolderListMenu(Cursor cursor) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(R.string.menu_title_select_folder); + final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + DataUtils.batchMoveToFolder(mContentResolver, + mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); + Toast.makeText( + NotesListActivity.this, + getString(R.string.format_move_notes_to_folder, + mNotesListAdapter.getSelectedCount(), + adapter.getFolderName(NotesListActivity.this, which)), + Toast.LENGTH_SHORT).show(); + mModeCallBack.finishActionMode(); + } + }); + builder.show(); + } + + 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>() { + protected HashSet doInBackground(Void... unused) { + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + if (!isSyncMode()) { + // if not synced, delete notes directly + if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter + .getSelectedItemIds())) { + } else { + Log.e(TAG, "Delete notes error, should not happens"); + } + } else { + // in sync mode, we'll move the deleted note into the trash + // folder + if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter + .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + return widgets; + } + + @Override + protected void onPostExecute(HashSet widgets) { + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + mModeCallBack.finishActionMode(); + } + }.execute(); + } + + private void deleteFolder(long folderId) { + if (folderId == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Wrong folder id, should not happen " + folderId); + return; + } + + HashSet ids = new HashSet(); + ids.add(folderId); + HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, + folderId); + if (!isSyncMode()) { + // if not synced, delete folder directly + DataUtils.batchDeleteNotes(mContentResolver, ids); + } else { + // in sync mode, we'll move the deleted folder into the trash folder + DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); + } + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + } + + 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); + } + + private void openFolder(NoteItemData data) { + mCurrentFolderId = data.getId(); + startAsyncNotesListQuery(); + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mState = ListEditState.CALL_RECORD_FOLDER; + mAddNewNote.setVisibility(View.GONE); + } else { + mState = ListEditState.SUB_FOLDER; + } + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mTitleBar.setText(R.string.call_record_folder_name); + } else { + mTitleBar.setText(data.getSnippet()); + } + mTitleBar.setVisibility(View.VISIBLE); + } + + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_new_note: + createNewNote(); + break; + default: + break; + } + } + + private void showSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } + + private void hideSoftInput(View view) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + 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); + final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); + showSoftInput(); + if (!create) { + if (mFocusNoteDataItem != null) { + etName.setText(mFocusNoteDataItem.getSnippet()); + builder.setTitle(getString(R.string.menu_folder_change_name)); + } else { + Log.e(TAG, "The long click data item is null"); + return; + } + } else { + etName.setText(""); + builder.setTitle(this.getString(R.string.menu_create_folder)); + } + + builder.setPositiveButton(android.R.string.ok, null); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + hideSoftInput(etName); + } + }); + + final Dialog dialog = builder.setView(view).show(); + final Button positive = (Button)dialog.findViewById(android.R.id.button1); + positive.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + hideSoftInput(etName); + String name = etName.getText().toString(); + if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), + Toast.LENGTH_LONG).show(); + etName.setSelection(0, etName.length()); + return; + } + if (!create) { + if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + + "=?", new String[] { + String.valueOf(mFocusNoteDataItem.getId()) + }); + } + } else if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + } + dialog.dismiss(); + } + }); + + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } + /** + * When the name edit text is null, disable the positive button + */ + etName.addTextChangedListener(new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // TODO Auto-generated method stub + + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } else { + positive.setEnabled(true); + } + } + + public void afterTextChanged(Editable s) { + // TODO Auto-generated method stub + + } + }); + } + + @Override + public void onBackPressed() { + switch (mState) { + case SUB_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + break; + case CALL_RECORD_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mAddNewNote.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.GONE); + startAsyncNotesListQuery(); + break; + case NOTE_LIST: + super.onBackPressed(); + break; + default: + break; + } + } + + private void updateWidget(int appWidgetId, int appWidgetType) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (appWidgetType == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + appWidgetId + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + + private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + if (mFocusNoteDataItem != null) { + menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); + menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); + menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); + } + } + }; + + @Override + public void onContextMenuClosed(Menu menu) { + if (mNotesListView != null) { + mNotesListView.setOnCreateContextMenuListener(null); + } + super.onContextMenuClosed(menu); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (mFocusNoteDataItem == null) { + Log.e(TAG, "The long click data item is null"); + return false; + } + switch (item.getItemId()) { + case MENU_FOLDER_VIEW: + openFolder(mFocusNoteDataItem); + break; + case MENU_FOLDER_DELETE: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_folder)); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + deleteFolder(mFocusNoteDataItem.getId()); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + break; + case MENU_FOLDER_CHANGE_NAME: + showCreateOrModifyFolderDialog(false); + break; + default: + break; + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + if (mState == ListEditState.NOTE_LIST) { + getMenuInflater().inflate(R.menu.note_list, menu); + // set sync or sync_cancel + menu.findItem(R.id.menu_sync).setTitle( + GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); + } else if (mState == ListEditState.SUB_FOLDER) { + getMenuInflater().inflate(R.menu.sub_folder, menu); + } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_record_folder, menu); + } else { + Log.e(TAG, "Wrong state:" + mState); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_new_folder: { + showCreateOrModifyFolderDialog(true); + break; + } + case R.id.menu_export_text: { + exportNoteToText(); + break; + } + case R.id.menu_sync: { + if (isSyncMode()) { + if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + GTaskSyncService.startSync(this); + } else { + GTaskSyncService.cancelSync(this); + } + } else { + startPreferenceActivity(); + } + break; + } + case R.id.menu_setting: { + startPreferenceActivity(); + break; + } + case R.id.menu_new_note: { + createNewNote(); + break; + } + case R.id.menu_search: + onSearchRequested(); + break; + default: + break; + } + return true; + } + + @Override + public boolean onSearchRequested() { + startSearch(null, false, null /* appData */, false); + return true; + } + + private void exportNoteToText() { + final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + new AsyncTask() { + + @Override + protected Integer doInBackground(Void... unused) { + return backup.exportToText(); + } + + @Override + protected void onPostExecute(Integer result) { + if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_unmounted)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SUCCESS) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.success_sdcard_export)); + builder.setMessage(NotesListActivity.this.getString( + R.string.format_exported_file_location, backup + .getExportedTextFileName(), backup.getExportedTextFileDir())); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_export)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } + } + + }.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); + } + + private class OnListItemClickListener implements OnItemClickListener { + + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + NoteItemData item = ((NotesListItem) view).getItemData(); + if (mNotesListAdapter.isInChoiceMode()) { + if (item.getType() == Notes.TYPE_NOTE) { + position = position - mNotesListView.getHeaderViewsCount(); + mModeCallBack.onItemCheckedStateChanged(null, position, id, + !mNotesListAdapter.isSelectedItem(position)); + } + return; + } + + switch (mState) { + case NOTE_LIST: + if (item.getType() == Notes.TYPE_FOLDER + || item.getType() == Notes.TYPE_SYSTEM) { + openFolder(item); + } else if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in NOTE_LIST"); + } + break; + case SUB_FOLDER: + case CALL_RECORD_FOLDER: + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in SUB_FOLDER"); + } + break; + default: + break; + } + } + } + + } + + private void startQueryDestinationFolders() { + String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + selection = (mState == ListEditState.NOTE_LIST) ? selection: + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + + mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, + null, + Notes.CONTENT_NOTE_URI, + FoldersListAdapter.PROJECTION, + selection, + new String[] { + String.valueOf(Notes.TYPE_FOLDER), + String.valueOf(Notes.ID_TRASH_FOLER), + String.valueOf(mCurrentFolderId) + }, + NoteColumns.MODIFIED_DATE + " DESC"); + } + + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + mFocusNoteDataItem = ((NotesListItem) view).getItemData(); + if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + if (mNotesListView.startActionMode(mModeCallBack) != null) { + mModeCallBack.onItemCheckedStateChanged(null, position, id, true); + mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } else { + Log.e(TAG, "startActionMode fails"); + } + } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); + } + } + return false; + } +} diff --git a/src/ui/NotesListAdapter.java b/src/ui/NotesListAdapter.java new file mode 100644 index 0000000..fea3146 --- /dev/null +++ b/src/ui/NotesListAdapter.java @@ -0,0 +1,184 @@ +/* + * 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;//引入tools包 + +import android.content.Context;//导入各种类 +import android.database.Cursor; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; + +import net.micode.notes.data.Notes; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + + +public class NotesListAdapter extends CursorAdapter {//继承自CursorAdapter,它为cursor和ListView提供了连接的桥梁。因此,该类为cursor和便签编辑提供了沟通渠道 + private static final String TAG = "NotesListAdapter"; + private Context mContext; + private HashMap mSelectedIndex; + private int mNotesCount; + private boolean mChoiceMode; + + public static class AppWidgetAttribute {//表示桌面widget的属性,包括编号和类型 + public int widgetId; + public int widgetType; + }; + + public NotesListAdapter(Context context) {//初始化便签链接 + super(context, null); + mSelectedIndex = new HashMap(); + mContext = context; + mNotesCount = 0; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) {//利用NotesListLtem类创建新布局 + return new NotesListItem(context); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) {//将光标指向的数据与创建的视图捆绑起来 + if (view instanceof NotesListItem) { + NoteItemData itemData = new NoteItemData(context, cursor); + ((NotesListItem) view).bind(context, itemData, mChoiceMode, + isSelectedItem(cursor.getPosition())); + } + } + + public void setCheckedItem(final int position, final boolean checked) {//设置勾选框 + mSelectedIndex.put(position, checked); + notifyDataSetChanged(); + } + + public boolean isInChoiceMode() {//判断是否被勾选 + return mChoiceMode; + } + + public void setChoiceMode(boolean mode) {//重置勾选框 + mSelectedIndex.clear(); + mChoiceMode = mode; + } + + public void selectAll(boolean checked) {//设置全部勾选 + Cursor cursor = getCursor(); + for (int i = 0; i < getCount(); i++) { + if (cursor.moveToPosition(i)) { + if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { + setCheckedItem(i, checked); + } + } + } + } + + public HashSet getSelectedItemIds() {// 获取已经被勾选的项目的id号加入到散列表itemSet中 + HashSet itemSet = new HashSet(); + for (Integer position : mSelectedIndex.keySet()) { + if (mSelectedIndex.get(position) == true) { + Long id = getItemId(position); + if (id == Notes.ID_ROOT_FOLDER) { + Log.d(TAG, "Wrong item id, should not happen"); + } else { + itemSet.add(id); + } + } + } + + return itemSet; + } + + public HashSet getSelectedWidget() {//获取桌面组件选项表 + HashSet itemSet = new HashSet(); + for (Integer position : mSelectedIndex.keySet()) {//遍历被选中的列表 + if (mSelectedIndex.get(position) == true) { + Cursor c = (Cursor) getItem(position); + if (c != null) { + AppWidgetAttribute widget = new AppWidgetAttribute(); + NoteItemData item = new NoteItemData(mContext, c); + widget.widgetId = item.getWidgetId(); + widget.widgetType = item.getWidgetType(); + itemSet.add(widget); + /** + * Don't close cursor here, only the adapter could close it + */ + } else { + Log.e(TAG, "Invalid cursor");//设置标签,非法的cursor + return null; + } + } + } + return itemSet; + } + + public int getSelectedCount() {//获取选项个数 + Collection values = mSelectedIndex.values();//获取选项下标的值 + if (null == values) { + return 0; + } + Iterator iter = values.iterator();//初始化叠加器 + int count = 0; + while (iter.hasNext()) { + if (true == iter.next()) { + count++; + } + } + 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() {//在activity发生变化时,重新计算便签数量 + 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++) {//遍历所有数据 + Cursor c = (Cursor) getItem(i); + if (c != null) {//判断语句,如果光标不是null,那么便得到信息,便签数目加1 + if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {//若选项的数据类型为便签类型,那么计数+1 + mNotesCount++; + } + } else {//设置为无效的光标 + Log.e(TAG, "Invalid cursor"); + return; + } + } + } +} diff --git a/src/ui/NotesListItem.java b/src/ui/NotesListItem.java new file mode 100644 index 0000000..3231942 --- /dev/null +++ b/src/ui/NotesListItem.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.ui; + +import android.content.Context; +import android.text.format.DateUtils; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser.NoteItemBgResources; + + +public class NotesListItem extends LinearLayout {//鏋勫缓渚跨鍒楄〃鐨勫悇涓」鐩殑璇︾粏鍏蜂綋淇℃伅 + private ImageView mAlert;//闂归挓鍥剧墖 + private TextView mTitle;//鏂囨湰鏍囬 + private TextView mTime;//鏃堕棿 + private TextView mCallName;//鍚嶇О + private NoteItemData mItemData;//鏍囩鏁版嵁 + private CheckBox mCheckBox;//鍕鹃夋 + + public NotesListItem(Context context) {//鍒濆鍖栧熀鏈俊鎭 + super(context);//璋冩暣璋冪敤鐖剁被鏋勯犲嚱鏁扮殑椤哄簭 + inflate(context, R.layout.note_item, this);//灏唜ml瀹氫箟鐨勪竴涓竷灞鎵惧嚭鏉 + mAlert = (ImageView) findViewById(R.id.iv_alert_icon);//浠巆ontentView涓煡鎵炬寚瀹欼D鐨刅iew + mTitle = (TextView) findViewById(R.id.tv_title);//鑾峰彇棰樼洰 + mTime = (TextView) findViewById(R.id.tv_time);//鑾峰彇鍒涘缓鎴栦慨鏀规椂闂 + mCallName = (TextView) findViewById(R.id.tv_name);//鑾峰彇鑱旂郴浜哄鍚 + mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);//鑾峰彇澶嶉夋 + } + + public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {//鏍规嵁data鐨勫睘鎬у鍚勪釜鎺т欢鐨勫睘鎬х殑鎺у埗 + if (choiceMode && data.getType() == Notes.TYPE_NOTE) {//濡傛灉褰撳墠澶勪簬閫夋嫨妯″紡涓嬩笖鏁版嵁绫诲瀷涓轰究绛 + mCheckBox.setVisibility(View.VISIBLE);//璁剧疆View鍙 + mCheckBox.setChecked(checked);//璁剧疆鍕鹃 + } else { + mCheckBox.setVisibility(View.GONE);//璁剧疆澶嶉夋涓嶅彲瑙 + } + + mItemData = data;//鎶婃暟鎹紶缁欐爣绛 + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {//璁剧疆鎺т欢灞炴э紝閫氳繃鍒ゆ柇淇濆瓨鍒版枃浠跺す鐨処D銆佸綋鍓岻D浠ュ強鐖禝D涔嬮棿鍏崇郴鍐冲畾 + mCallName.setVisibility(View.GONE);//璁剧疆鑱旂郴浜哄悕瀛椾笉鍙 + mAlert.setVisibility(View.VISIBLE);//璁剧疆闂归挓鍥炬爣鍙 + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);//璁剧疆澶栬椋庢牸 + mTitle.setText(context.getString(R.string.call_record_folder_name)//璁剧疆鏍囬鍐呭涓烘枃浠跺す鍚嶅瓧銆佹枃浠舵暟鍜屼究绛炬暟 + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + mAlert.setImageResource(R.drawable.call_record);//璁剧疆鍥剧墖鏉ユ簮 + } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {//鍏充簬闂归挓鐨勮缃 + mCallName.setVisibility(View.VISIBLE);//璁剧疆鑱旂郴浜哄鍚嶅彲瑙 + mCallName.setText(data.getCallName());//璁剧疆鑱旂郴浜哄鍚嶇殑鏂囨湰鍐呭 + mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);//璁剧疆title鏂囨湰椋庢牸 + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));//璁剧疆title鐨勬枃鏈唴瀹逛负渚跨鍐呭鐨勫墠闈㈢墖娈 + if (data.hasAlert()) {//濡傛灉鏃堕棿鎻愰啋瀛樺湪锛岃缃浘鐗囨潵婧愶紝灏嗘椂闂存彁閱掑浘鏍囧畾涓哄彲瑙 + mAlert.setImageResource(R.drawable.clock);//鍥剧墖鏉ユ簮鐨勮缃 + mAlert.setVisibility(View.VISIBLE);//灏嗘彁閱掑浘鏍囪缃负鍙 + } else {//鍚﹀垯灏嗘彁閱掑浘鏍囪缃负涓嶅彲瑙 + mAlert.setVisibility(View.GONE); + } + } else {//濡傛灉鐖剁被鍜屽綋鍓峣d鍧囦笌淇濆瓨鍦ㄦ枃浠跺す涓殑id涓嶅悓 + mCallName.setVisibility(View.GONE);//璁剧疆鑱旂郴浜哄鍚嶄笉鍙 + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);//璁剧疆title鐨勬枃鏈牸寮 + + if (data.getType() == Notes.TYPE_FOLDER) {//璁剧疆Type鏍煎紡 + mTitle.setText(data.getSnippet()//璁剧疆渚跨鏍囬鍐呭涓轰究绛剧殑鍓嶉潰閮ㄥ垎鐨勫唴瀹+鏂囦欢鏁+渚跨鏁 + + context.getString(R.string.format_folder_files_count, + data.getNotesCount()));//璁剧疆鍐呭锛屼粠data缂栬緫鐨勬棩鏈熶腑鑾峰彇鏃堕棿 + mAlert.setVisibility(View.GONE);//璁剧疆鏃堕棿鎻愰啋鍥炬爣涓轰笉鍙 + } else {//濡傛灉涓嶆槸鏂囦欢澶圭被鍨,璁剧疆渚跨鐨則itle涓轰究绛惧唴瀹圭殑鍓嶉潰鐗囨 + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + if (data.hasAlert()) {//鑻ュ綋鍓嶄究绛惧瓨鍦ㄦ彁閱掗椆閽熸椂闂达紝鍒欐樉绀虹浉搴旂殑鍥剧墖 + mAlert.setImageResource(R.drawable.clock);//灏嗘彁閱掑浘鏍囪缃负闂归挓鏍峰紡 + mAlert.setVisibility(View.VISIBLE);//璁剧疆鎻愰啋闂归挓鍙 + } else {//鍚﹀垯璁剧疆鎻愰啋鍥炬爣涓嶅彲瑙 + mAlert.setVisibility(View.GONE); + } + } + } + mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));//灏嗘椂闂磋缃负缂栬緫渚跨鐨勬椂闂 + + setBackground(data);//浠巇ata閲岀紪杈戠殑鏃ユ湡涓幏鍙栧唴瀹瑰拰鐩稿叧鏃堕棿 + } + + private void setBackground(NoteItemData data) {//鏍规嵁data鐨勬枃浠跺睘鎬ф潵璁剧疆鑳屾櫙 + int id = data.getBgColorId();//鑾峰彇id锛岀敤姝d鐢ㄦ潵鑾峰彇鑳屾櫙棰滆壊 + if (data.getType() == Notes.TYPE_NOTE) {//鏍规嵁data鐨勫睘鎬ф潵鏄惁涓篘ote灞炴э紝鍒嗕负4绉嶆儏鍐 + if (data.isSingle() || data.isOneFollowingFolder()) {//鍗曚釜鏁版嵁鎴栧彧鏈変竴涓瓙鏂囦欢澶 + setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));//璁剧疆鑳屾櫙鏉ユ簮涓篿d鐨勫崟涓暟鎹 + } else if (data.isLast()) {//鑻ュ綋鍓嶄究绛句负鏈鍚庝竴涓,璁剧疆鑳屾櫙鏉ユ簮涓篿d鐨勬渶鍚庝竴涓暟鎹 + setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); + } else if (data.isFirst() || data.isMultiFollowingFolder()) {//鑻ユ槸绗竴涓暟鎹垨鑰呮湁寰堝涓瓙鏂囦欢澶, 璁剧疆鑳屾櫙鏉ユ簮涓篿d鐨勭涓涓暟鎹 + setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); + } else { + setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));//灏嗕究绛捐缃负鏅氱被鍨嬩究绛剧殑鑳屾櫙 + } + } else {//鍒欏皢鑳屾櫙璁剧疆涓烘枃浠跺す鐨勮儗鏅 + setBackgroundResource(NoteItemBgResources.getFolderBgRes()); + } + } + + public NoteItemData getItemData() { + return mItemData; + }//杩斿洖褰撳墠渚跨鐨勬暟鎹俊鎭 +} diff --git a/src/ui/NotesPreferenceActivity.java b/src/ui/NotesPreferenceActivity.java new file mode 100644 index 0000000..b87168e --- /dev/null +++ b/src/ui/NotesPreferenceActivity.java @@ -0,0 +1,389 @@ +/* + * 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 {//瀹炵幇瀵硅儗鏅鑹插拰瀛椾綋澶у皬鐨勬暟鎹偍瀛 + public static final String PREFERENCE_NAME = "notes_preferences";//浼樺厛鍚 + + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";//鍚屾璐﹀彿 + + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";//鍚屾鏃堕棿 + + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";//鑳屾櫙棰滆壊璁剧疆鎸夐敭 + + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";//璐︽埛鍚屾瀵嗙爜 + + private static final String AUTHORITIES_FILTER_KEY = "authorities";//鏈湴瀵嗙爜 + + private PreferenceCategory mAccountCategory;//璐︽埛鍒嗙粍 + + private GTaskReceiver mReceiver;//鍚屾浠诲姟鎺ユ敹鍣 + + private Account[] mOriAccounts;//璐︽埛 + + private boolean mHasAddedAccount;//璐︽埛鐨刪ash鏍囪 + + @Override + protected void onCreate(Bundle icicle) {//鍒涘缓涓涓猘ctivity锛屽湪鍑芥暟閲屽畬鎴愭墍鏈夌殑姝e父闈欐佽缃 + super.onCreate(icicle);//鎵ц鐖剁被鍒涘缓鍑芥暟 + + /* using the app icon for navigation */ + getActionBar().setDisplayHomeAsUpEnabled(true);//缁欏乏涓婅鍥炬爣鐨勫乏杈瑰姞涓婁竴涓繑鍥炵殑鍥炬爣 + + addPreferencesFromResource(R.xml.preferences);//浠庤祫婧愰噷娣诲姞甯冨眬鏂囦欢 + mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);//鏍规嵁鍚屾璐︽埛瀵嗙爜杩涜璐︽埛鍒嗙粍 + mReceiver = new GTaskReceiver();//璁剧疆鍚屾浠诲姟鎺ユ敹鍣 + IntentFilter filter = new IntentFilter();//璁剧疆杩囨护椤 + filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);// + registerReceiver(mReceiver, filter); + + mOriAccounts = null;//鍒濆鍖栧悓姝ョ粍浠 + View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);//浠巟ml鑾峰彇Listview + getListView().addHeaderView(header, null, true);//鍒楀嚭鎵鏈夐夋嫨 + } + + @Override//鍔熻兘鎻忚堪锛氶噸鍚椿鍔 + protected void onResume() { + super.onResume();//activity浜や簰鍔熻兘鐨勫疄鐜帮紝鐢ㄤ簬鎺ュ彈鐢ㄦ埛鐨勮緭鍏 + + // need to set sync account automatically if user has added a new + // account + if (mHasAddedAccount) {//鑻ョ敤鎴锋柊鍔犱簡璐︽埛鍒欒嚜鍔ㄨ缃悓姝ヨ处鎴 + Account[] accounts = getGoogleAccounts();//鑾峰彇google鍚屾璐︽埛 + 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();//鍒锋柊鏍囩鐣岄潰 + } + + @Override//鍔熻兘鎻忚堪锛氶攢姣佹椿鍔 + protected void onDestroy() {//閿姣丄ctivity + if (mReceiver != null) { + unregisterReceiver(mReceiver);//鍙栨秷骞挎挱鍣ㄧ殑鐩戝惉 + }//閿姣佹帴鏀跺櫒 + super.onDestroy();//鎵ц閿姣佸姩浣 + } + + private void loadAccountPreference() {//閲嶆柊璁剧疆璐︽埛淇℃伅 + mAccountCategory.removeAll();//閿姣佹墍鏈夌殑鍒嗙粍 + + Preference accountPref = new Preference(this);//寤虹珛棣栭夐」 + final String defaultAccount = getSyncAccountName(this);//榛樿璐︽埛涓哄綋鍓嶈处鎴 + accountPref.setTitle(getString(R.string.preferences_account_title)); + accountPref.setSummary(getString(R.string.preferences_account_summary)); + accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {//璁剧疆棣栭夐」鐨勫ぇ鏍囬鍜屽皬鏍囬 + public boolean onPreferenceClick(Preference preference) {//寤虹珛鐩戝惉鍣 + if (!GTaskSyncService.isSyncing()) {//涓嶅湪鍚屾鐘舵佷笅锛屽鏋滄病鏈夐粯璁ょ殑璐︽埛锛屾樉绀洪夋嫨璐︽埛鐨勫璇濇锛屽惁鍒欐樉绀洪渶瑕佹敼鍙樿处鎴风殑瀵硅瘽妗 + if (TextUtils.isEmpty(defaultAccount)) {//绗竴娆¤缃处鎴 + // 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 {//鑻ュ湪娌℃湁鍚屾鐨勬儏鍐典笅锛屽垯鍦╰oast涓樉绀轰笉鑳戒慨鏀 + Toast.makeText(NotesPreferenceActivity.this, + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + .show();//姝e湪鍚屾锛屼笉鑳藉垏鎹㈠悓姝ョ殑璐﹀彿 + } + return true; + } + }); + + mAccountCategory.addPreference(accountPref);//鏍规嵁鏂板缓棣栭夐」缂栬緫鏂扮殑璐︽埛鍒嗙粍 + } + + private void loadSyncButton() {//璁剧疆鍚屾鎸夐敭鍜屾渶杩戝悓姝ユ椂闂 + Button syncButton = (Button) findViewById(R.id.preference_sync_button);//閰嶇疆璧勬簮锛岃缃竴涓猙utton + 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()) {//濡傛灉姝e湪鍚屾鍒欒鍙栨鍦ㄥ悓姝ョ殑杩涘害锛屽惁鍒欐樉绀烘渶鍚庡悓姝ョ殑鏃堕棿 + lastSyncTimeView.setText(GTaskSyncService.getProgressString()); + lastSyncTimeView.setVisibility(View.VISIBLE); + } else {//鑻ユ槸闈炲悓姝ユ儏鍐 + long lastSyncTime = getLastSyncTime(this); + if (lastSyncTime != 0) {//鑻ユ渶杩戝悓姝ユ椂闂翠笉涓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); + } + } + }//鏍规嵁褰撳墠鍚屾鏈嶅姟鍣ㄨ缃椂闂存樉绀烘鐨勬枃鏈互鍙婂彲瑙佹 + + private void refreshUI() {//鍒锋柊鏍囩鐣岄潰 + loadAccountPreference(); + loadSyncButton();//鍔犺浇鈥滀繚瀛樷濇寜閽 + } + + private void showSelectAccountAlertDialog() {//鏄剧ず璐︽埛閫夋嫨鐨勫璇濇骞惰繘琛岃处鎴风殑璁剧疆 + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//鍒涘缓涓涓柊鐨勫璇濇 + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);//鏂囨湰瑙嗗浘璁剧疆 + subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); + + dialogBuilder.setCustomTitle(titleView);//璁剧疆鏍囬浠ュ強瀛愭爣棰樼殑鍐呭 + dialogBuilder.setPositiveButton(null, null);//涓嶈缃滅‘瀹氣濈殑鎸夐挳 + + Account[] accounts = getGoogleAccounts();//鑾峰彇褰撳墠璋锋瓕璐︽埛 + String defAccount = getSyncAccountName(this);//鑾峰緱鍚屾璐︽埛 + + mOriAccounts = accounts; + mHasAddedAccount = false;//鑾峰彇鍚屾璐︽埛淇℃伅 + + if (accounts.length > 0) {//濡傛灉鏈夎胺姝岃处鎴凤紝鍒欐樉绀烘墍鏈夎处鎴风殑鍚嶇О锛屽苟璁剧疆涓洪夐」 + CharSequence[] items = new CharSequence[accounts.length]; + final CharSequence[] itemMapping = items; + int checkedItem = -1; + int index = 0; + for (Account account : accounts) {//閫氳繃寰幆妫鏌ヨ处鎴峰垪琛 + if (TextUtils.equals(account.name, defAccount)) { + checkedItem = index; + }//鍦ㄨ处鎴峰垪琛ㄤ腑鏌ヨ鍒版墍闇璐︽埛 + items[index++] = account.name; + } + dialogBuilder.setSingleChoiceItems(items, checkedItem,//鍦ㄥ璇濇寤虹珛涓涓崟閫夌殑澶嶉夋 + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) {//鍝嶅簲瀵硅瘽妗嗙殑鐐瑰嚮 + setSyncAccount(itemMapping[which].toString());//鐐瑰嚮鍒欏紑濮嬭缃悓姝ヨ处鎴 + dialog.dismiss();//鍙栨秷瀵硅瘽妗 + refreshUI();//鍒锋柊鐣岄潰 + } + }); + }//璁剧疆鐐瑰嚮鍚庢墽琛岀殑浜嬩欢锛屽寘鎷褰曟柊鍚屾璐︽埛鍜屽埛鏂版爣绛剧晫闈 + + View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);//娣诲姞鏂扮殑璐︽埛 + dialogBuilder.setView(addAccountView);//缁欐柊鍔犺处鎴峰璇濇璁剧疆鑷畾涔夋牱寮 + + final AlertDialog dialog = dialogBuilder.show();//鏄剧ず瀵硅瘽妗 + addAccountView.setOnClickListener(new View.OnClickListener() {//璁剧疆鐩戝惉鍣 + public void onClick(View v) {//鍝嶅簲鐐瑰嚮娣诲姞璐︽埛鐨勮姹 + mHasAddedAccount = true;//灏嗘柊鍔犺处鎴风殑hash缃负true + Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");//寤虹珛缃戠粶缁勪欢 + intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { + "gmail-ls" + }); + startActivityForResult(intent, -1);//璺冲洖涓婁竴涓夐」 + dialog.dismiss(); + } + }); + } + + private void showChangeAccountConfirmAlertDialog() {//鍒锋柊鏍囩鐣岄潰 + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);//鍒涘缓涓涓柊鐨勫璇濇 + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, + getSyncAccountName(this))); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); + dialogBuilder.setCustomTitle(titleView);//鏍规嵁鍚屾淇敼鐨勮处鎴蜂俊鎭缃爣棰樹互鍙婂瓙鏍囬鐨勫唴瀹 + + CharSequence[] menuItemArray = new CharSequence[] { + getString(R.string.preferences_menu_change_account), + getString(R.string.preferences_menu_remove_account), + getString(R.string.preferences_menu_cancel) + };//璁剧疆瀵硅瘽妗嗙殑鑷畾涔夋爣棰 + dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {//瀹氫箟涓浜涙爣璁板瓧绗︿覆 + public void onClick(DialogInterface dialog, int which) {//鍝嶅簲瀵硅瘽妗嗙殑鐐瑰嚮 + if (which == 0) {//濡傛灉鏄敼鍙樺悓姝ヨ处鎴凤紝鍒欐樉绀哄彲閫夋嫨鐨勮处鎴 + showSelectAccountAlertDialog(); + } else if (which == 1) { + removeSyncAccount(); + refreshUI(); + }//鍒犻櫎鍚屾璐︽埛 + } + }); + dialogBuilder.show();//鏄剧ず瀵硅瘽妗 + } + + private Account[] getGoogleAccounts() {//鑾峰彇璋锋瓕璐︽埛 + AccountManager accountManager = AccountManager.get(this); + return accountManager.getAccountsByType("com.google"); + } + + private void setSyncAccount(String account) {//璁剧疆鍚屾璐︽埛 + if (!getSyncAccountName(this).equals(account)) {//濡傛灉璇ヨ处鍙蜂笉鍦ㄥ悓姝ヨ处鍙峰垪琛ㄤ腑 + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//缂栬緫鍏变韩棣栭夐」 + if (account != null) {//濡傛灉璐︽埛涓嶄负绌 + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); + } else { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + }//灏嗚璐﹀彿鍔犲叆鍒伴閫夐」涓 + editor.commit();//鎻愪氦淇敼鐨勬暟鎹 + + // 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(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + }//娓呴櫎鏈湴鐨刧task鍏宠仈鐨勪俊鎭 + }).start();//閲嶇疆褰撳湴鍚屾浠诲姟鐨勪俊鎭 + + Toast.makeText(NotesPreferenceActivity.this,//璁剧疆涓涓猼oast鎻愮ず淇℃伅锛屾彁绀虹敤鎴锋垚鍔熻缃悓姝 + getString(R.string.preferences_toast_success_set_accout, account), + Toast.LENGTH_SHORT).show();//灏唗oast鐨勬枃鏈俊鎭疆涓衡滆缃处鎴锋垚鍔熲濆苟鏄剧ず鍑烘潵 + } + } + + private void removeSyncAccount() {//鍒犻櫎鍚屾璐︽埛 + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);//瀛樻斁鏁版嵁鍜岃鍙栨暟鎹 + SharedPreferences.Editor editor = settings.edit();//璁剧疆鍏变韩棣栭夐」 + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { + editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); + }//鑻ュ綋鍓嶉閫夐」涓湁璐︽埛灏卞垹闄 + if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { + editor.remove(PREFERENCE_LAST_SYNC_TIME); + }//鍒犻櫎褰撳墠棣栭夐」涓湁璐︽埛鏃堕棿 + editor.commit();//鎻愪氦鏇存柊鍚庣殑鏁版嵁 + + // clean up local gtask related info + new Thread(new Runnable() {//鏂扮嚎绋嬬殑鍒涘缓 + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + }//娓呴櫎鏈湴鐨刧task鍏宠仈鐨勪俊鎭紝灏嗕竴浜涘弬鏁拌缃负0鎴朜ULL + }).start(); + } + + public static String getSyncAccountName(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + }//鑾峰彇鍚屾璐︽埛鍚嶇О + + public static void setLastSyncTime(Context context, long time) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit();//浠庡叡浜閫夐」涓壘鍒扮浉鍏宠处鎴峰苟鑾峰彇鍏剁紪杈戝櫒 + editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + editor.commit();//缂栬緫鏈缁堝悓姝ユ椂闂村苟鎻愪氦鏇存柊 + }//璁剧疆鏈缁堝悓姝ョ殑鏃堕棿 + + public static long getLastSyncTime(Context context) {//鑾峰彇鏈缁堝悓姝ユ椂闂 + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE);//閫氳繃鍏变韩锛岃幏鍙栨椂闂 + return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); + } + + 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));////閫氳繃鑾峰彇鐨勬暟鎹湪璁剧疆绯荤粺鐨勭姸鎬 + }//鑾峰彇闅忓箍鎾屾潵鐨処ntent涓殑鍚屾鏈嶅姟鐨勬暟鎹 + + } + } + + public boolean onOptionsItemSelected(MenuItem item) {//澶勭悊鑿滃崟鐨勯夐」 + switch (item.getItemId()) {//鏍规嵁閫夐」鐨刬d閫夋嫨 + case android.R.id.home://杩斿洖涓荤晫闈 + Intent intent = new Intent(this, NotesListActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent);//鍒涘缓娲诲姩 + return true; + default://鍦ㄤ富椤垫儏鍐典笅鍦ㄥ垱寤鸿繛鎺ョ粍浠秈ntent锛屽彂鍑烘竻绌虹殑淇″彿骞跺紑濮嬩竴涓浉搴旂殑activity + return false; + } + } +}