From 97845923e45926ee71bfde68181325a9b233166e Mon Sep 17 00:00:00 2001 From: heshiguang <1047029621@qq.com> Date: Wed, 26 Apr 2023 11:19:48 +0800 Subject: [PATCH] =?UTF-8?q?model=E5=8C=85=E5=92=8Cui=E5=8C=85=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Note.java | 255 ++++++++++ doc/NoteEditActivity.java | 1004 +++++++++++++++++++++++++++++++++++++ doc/NoteEditText.java | 262 ++++++++++ doc/NoteItemData.java | 233 +++++++++ doc/WorkingNote.java | 415 +++++++++++++++ 5 files changed, 2169 insertions(+) create mode 100644 doc/Note.java create mode 100644 doc/NoteEditActivity.java create mode 100644 doc/NoteEditText.java create mode 100644 doc/NoteItemData.java create mode 100644 doc/WorkingNote.java diff --git a/doc/Note.java b/doc/Note.java new file mode 100644 index 0000000..20b0410 --- /dev/null +++ b/doc/Note.java @@ -0,0 +1,255 @@ +/* + * 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;//表示待操作的数据 +import android.os.RemoteException;//远程容错 +import android.util.Log;//输出日志,比如说出错、警告等 + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + + +public class Note { + // private ContentValues mNoteDiffValues; + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + 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 + 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); + //ContentResolver()主要是实现外部应用对ContentProvider中的数据 + //进行添加、删除、修改和查询操作 + 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; + }//try-catch异常处理 + 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) { + 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) { + mNoteData.setTextDataId(id); + }//设置文本数据的ID + + public long getTextDataId() { + return mNoteData.mTextDataId; + }//得到文本数据的ID + + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + }//设置电话号码数据的ID + + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + }//得到电话号码数据的ID + + 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;//文本数据 + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues;//电话号码数据 + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + //下面是上述几个函数的具体实现 + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long 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的操作存储到数据库 + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * 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) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + 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(); + }//把文本数据存入DataColumns + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + 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 { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + }//把电话号码数据存入DataColumns + + 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/doc/NoteEditActivity.java b/doc/NoteEditActivity.java new file mode 100644 index 0000000..e1251dc --- /dev/null +++ b/doc/NoteEditActivity.java @@ -0,0 +1,1004 @@ +/* + * 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.AlarmManager; +import android.app.AlertDialog; +import android.app.PendingIntent; +import android.app.SearchManager; +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Paint; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.style.BackgroundColorSpan; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +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.TextNote; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.model.WorkingNote.NoteSettingChangedListener; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.tool.ResourceParser.TextAppearanceResources; +import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; +import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class NoteEditActivity extends Activity implements OnClickListener, + NoteSettingChangedListener, OnTextViewChangeListener { + //璇ョ被涓昏鏄拡瀵规爣绛剧殑缂栬緫 + //缁ф壙浜嗙郴缁熷唴閮ㄨ澶氬拰鐩戝惉鏈夊叧鐨勭被 + private class HeadViewHolder { + public TextView tvModified; + + public ImageView ivAlertIcon; + + public TextView tvAlertDate; + + public ImageView ibSetBgColor; + } + //浣跨敤Map瀹炵幇鏁版嵁瀛樺偍 + private static final Map sBgSelectorBtnsMap = new HashMap(); + static { + sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); + sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); + sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); + sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); + sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); + //put鍑芥暟鏄皢鎸囧畾鍊煎拰鎸囧畾閿浉杩 + } + + private static final Map sBgSelectorSelectionMap = new HashMap(); + static { + sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); + sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); + sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); + sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); + sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); + //put鍑芥暟鏄皢鎸囧畾鍊煎拰鎸囧畾閿浉杩 + } + + private static final Map sFontSizeBtnsMap = new HashMap(); + static { + sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); + sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); + sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); + sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); + //put鍑芥暟鏄皢鎸囧畾鍊煎拰鎸囧畾閿浉杩 + } + + private static final Map sFontSelectorSelectionMap = new HashMap(); + static { + sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); + //put鍑芥暟鏄皢鎸囧畾鍊煎拰鎸囧畾閿浉杩 + } + + private static final String TAG = "NoteEditActivity"; + + private HeadViewHolder mNoteHeaderHolder; + + private View mHeadViewPanel; + //绉佹湁鍖栦竴涓晫闈㈡搷浣渕HeadViewPanel锛屽琛ㄥご鐨勬搷浣 + private View mNoteBgColorSelector; + //绉佹湁鍖栦竴涓晫闈㈡搷浣渕NoteBgColorSelector锛屽鑳屾櫙棰滆壊鐨勬搷浣 + private View mFontSizeSelector; + //绉佹湁鍖栦竴涓晫闈㈡搷浣渕FontSizeSelector锛屽鏍囩瀛椾綋鐨勬搷浣 + private EditText mNoteEditor; + //澹版槑缂栬緫鎺т欢锛屽鏂囨湰鎿嶄綔 + private View mNoteEditorPanel; + //绉佹湁鍖栦竴涓晫闈㈡搷浣渕NoteEditorPanel锛屾枃鏈紪杈戠殑鎺у埗鏉 + //private WorkingNote mWorkingNote; + private WorkingNote mWorkingNote; + //瀵规ā鏉縒orkingNote鐨勫垵濮嬪寲 + private SharedPreferences mSharedPrefs; + //绉佹湁鍖朣haredPreferences鐨勬暟鎹瓨鍌ㄦ柟寮 + //瀹冪殑鏈川鏄熀浜嶺ML鏂囦欢瀛樺偍key-value閿煎鏁版嵁 + private int mFontSizeId; + //鐢ㄤ簬鎿嶄綔瀛椾綋鐨勫ぇ灏 + private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; + + private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; + + public static final String TAG_CHECKED = String.valueOf('\u221A'); + public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); + + private LinearLayout mEditTextList; + //绾挎у竷灞 + private String mUserQuery; + private Pattern mPattern; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.setContentView(R.layout.note_edit); + //瀵规暟鎹簱鐨勮闂搷浣 + if (savedInstanceState == null && !initActivityState(getIntent())) { + finish(); + return; + } + initResources(); + } + + /** + * Current activity may be killed when the memory is low. Once it is killed, for another time + * user load this activity, we should restore the former state + */ + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); + if (!initActivityState(intent)) { + finish(); + return; + } + Log.d(TAG, "Restoring from killed activity"); + }//涓洪槻姝㈠唴瀛樹笉瓒虫椂绋嬪簭鐨勭粓姝紝鍦ㄨ繖閲屾湁涓涓繚瀛樼幇鍦虹殑鍑芥暟 + } + + private boolean initActivityState(Intent intent) { + /** + * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, + * then jump to the NotesListActivity + */ + mWorkingNote = null; + if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { + long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); + mUserQuery = ""; + //濡傛灉鐢ㄦ埛瀹炰緥鍖栨爣绛炬椂锛岀郴缁熷苟鏈粰鍑烘爣绛綢D + /** + * Starting from the searched result + */ + //鏍规嵁閿兼煡鎵綢D + if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { + noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); + mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); + } + //濡傛灉ID鍦ㄦ暟鎹簱涓湭鎵惧埌 + if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { + Intent jump = new Intent(this, NotesListActivity.class); + startActivity(jump); + //绋嬪簭灏嗚烦杞埌涓婇潰澹版槑鐨刬ntent鈥斺攋ump + showToast(R.string.error_note_not_exist); + finish(); + return false; + } + //ID鍦ㄦ暟鎹簱涓壘鍒 + else { + mWorkingNote = WorkingNote.load(this, noteId); + if (mWorkingNote == null) { + Log.e(TAG, "load note failed with note id" + noteId); + //鎵撳嵃鍑虹孩鑹茬殑閿欒淇℃伅 + finish(); + return false; + } + } + //setSoftInputMode鈥斺旇蒋閿洏杈撳叆妯″紡 + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN + | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { + // intent.getAction() + // 澶у鐢ㄤ簬broadcast鍙戦佸箍鎾椂缁欐満鍒讹紙intent锛夎缃竴涓猘ction锛屽氨鏄竴涓瓧绗︿覆 + // 鐢ㄦ埛鍙互閫氳繃receive锛堟帴鍙楋級intent锛岄氳繃 getAction寰楀埌鐨勫瓧绗︿覆锛屾潵鍐冲畾鍋氫粈涔 + // New note + long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); + int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, + Notes.TYPE_WIDGET_INVALIDE); + int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, + ResourceParser.getDefaultBgId(this)); + // intent.getInt锛圠ong銆丼tring锛塃xtra鏄鍚勫彉閲忕殑璇硶鍒嗘瀽 + // Parse call-record note + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); + if (callDate != 0 && phoneNumber != null) { + if (TextUtils.isEmpty(phoneNumber)) { + Log.w(TAG, "The call record number is null"); + } + long noteId = 0; + if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), + phoneNumber, callDate)) > 0) { + mWorkingNote = WorkingNote.load(this, noteId); + if (mWorkingNote == null) { + Log.e(TAG, "load call note failed with note id" + noteId); + finish(); + return false; + //灏嗙數璇濆彿鐮佷笌鎵嬫満鐨勫彿鐮佺翱鐩稿叧 + } + } else { + mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, + widgetType, bgResId); + mWorkingNote.convertToCallNote(phoneNumber, callDate); + } + } else { + mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, + bgResId); + }//鍒涘缓涓涓柊鐨刉orkingNote + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } else { + Log.e(TAG, "Intent not specified action, should not support"); + finish(); + return false; + } + mWorkingNote.setOnSettingStatusChangedListener(this); + return true; + } + + @Override + protected void onResume() { + super.onResume(); + initNoteScreen(); + } + + private void initNoteScreen() { + //瀵圭晫闈㈢殑鍒濆鍖栨搷浣 + mNoteEditor.setTextAppearance(this, TextAppearanceResources + .getTexAppearanceResource(mFontSizeId)); + //璁剧疆澶栬 + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + switchToListMode(mWorkingNote.getContent()); + } else { + mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); + mNoteEditor.setSelection(mNoteEditor.getText().length()); + } + for (Integer id : sBgSelectorSelectionMap.keySet()) { + findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); + } + mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + + mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, + mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR)); + + /** + * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker + * is not ready + */ + showAlertHeader(); + } + + //璁剧疆闂归挓鐨勬樉绀 + private void showAlertHeader() { + if (mWorkingNote.hasClockAlert()) { + long time = System.currentTimeMillis(); + if (time > mWorkingNote.getAlertDate()) { + mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); + } + //濡傛灉绯荤粺鏃堕棿澶т簬浜嗛椆閽熻缃殑鏃堕棿锛岄偅涔堥椆閽熷け鏁 + else { + mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( + mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); + } + mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); + mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); + //濡傛灉绯荤粺鏃堕棿澶т簬浜嗛椆閽熻缃殑鏃堕棿锛岄偅涔堥椆閽熷け鏁 + } else { + mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); + mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); + }; + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + initActivityState(intent); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + /** + * For new note without note id, we should firstly save it to + * generate a id. If the editing note is not worth saving, there + * is no id which is equivalent to create new note + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + } + //鍦ㄥ垱寤轰竴涓柊鐨勬爣绛炬椂锛屽厛鍦ㄦ暟鎹簱涓尮閰 + //濡傛灉涓嶅瓨鍦紝閭d箞鍏堝湪鏁版嵁搴撲腑瀛樺偍 + outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); + Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); + } + + @Override + //MotionEvent鏄灞忓箷瑙︽帶鐨勪紶閫掓満鍒 + public boolean dispatchTouchEvent(MotionEvent ev) { + if (mNoteBgColorSelector.getVisibility() == View.VISIBLE + && !inRangeOfView(mNoteBgColorSelector, ev)) { + mNoteBgColorSelector.setVisibility(View.GONE); + return true; + }//棰滆壊閫夋嫨鍣ㄥ湪灞忓箷涓婂彲瑙 + + if (mFontSizeSelector.getVisibility() == View.VISIBLE + && !inRangeOfView(mFontSizeSelector, ev)) { + mFontSizeSelector.setVisibility(View.GONE); + return true; + }//瀛椾綋澶у皬閫夋嫨鍣ㄥ湪灞忓箷涓婂彲瑙 + return super.dispatchTouchEvent(ev); + } + //瀵瑰睆骞曡Е鎺х殑鍧愭爣杩涜鎿嶄綔 + private boolean inRangeOfView(View view, MotionEvent ev) { + int []location = new int[2]; + view.getLocationOnScreen(location); + int x = location[0]; + int y = location[1]; + if (ev.getX() < x + || ev.getX() > (x + view.getWidth()) + || ev.getY() < y + || ev.getY() > (y + view.getHeight())) + //濡傛灉瑙︽帶鐨勪綅缃秴鍑轰簡缁欏畾鐨勮寖鍥达紝杩斿洖false + { + return false; + } + return true; + } + + private void initResources() { + mHeadViewPanel = findViewById(R.id.note_title); + mNoteHeaderHolder = new HeadViewHolder(); + mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); + mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon); + mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); + mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); + mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); + mNoteEditor = (EditText) findViewById(R.id.note_edit_view); + mNoteEditorPanel = findViewById(R.id.sv_note_edit); + mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); + for (int id : sBgSelectorBtnsMap.keySet()) { + ImageView iv = (ImageView) findViewById(id); + iv.setOnClickListener(this); + }//瀵规爣绛惧悇椤瑰睘鎬у唴瀹圭殑鍒濆鍖 + + mFontSizeSelector = findViewById(R.id.font_size_selector); + for (int id : sFontSizeBtnsMap.keySet()) { + View view = findViewById(id); + view.setOnClickListener(this); + };//瀵瑰瓧浣撳ぇ灏忕殑閫夋嫨 + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { + mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; + } + mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); + } + + @Override + protected void onPause() { + super.onPause(); + if(saveNote()) { + Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); + } + clearSettingState(); + } + + //鍜屾闈㈠皬宸ュ叿鐨勫悓姝 + private void updateWidget() { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + mWorkingNote.getWidgetId() + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_set_bg_color) { + mNoteBgColorSelector.setVisibility(View.VISIBLE); + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + - View.VISIBLE); + } else if (sBgSelectorBtnsMap.containsKey(id)) { + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + View.GONE); + mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); + mNoteBgColorSelector.setVisibility(View.GONE); + } else if (sFontSizeBtnsMap.containsKey(id)) { + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); + mFontSizeId = sFontSizeBtnsMap.get(id); + mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + getWorkingText(); + switchToListMode(mWorkingNote.getContent()); + } else { + mNoteEditor.setTextAppearance(this, + TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + } + mFontSizeSelector.setVisibility(View.GONE); + } + } + + @Override + public void onBackPressed() { + if(clearSettingState()) { + return; + } + + saveNote(); + super.onBackPressed(); + } + + private boolean clearSettingState() { + if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { + mNoteBgColorSelector.setVisibility(View.GONE); + return true; + } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { + mFontSizeSelector.setVisibility(View.GONE); + return true; + } + return false; + } + + public void onBackgroundColorChanged() { + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + View.VISIBLE); + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); + } + + @Override + //瀵归夋嫨鑿滃崟鐨勫噯澶 + public boolean onPrepareOptionsMenu(Menu menu) { + if (isFinishing()) { + return true; + } + clearSettingState(); + menu.clear(); + if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_note_edit, menu); + } + // MenuInflater鏄敤鏉ュ疄渚嬪寲Menu鐩綍涓嬬殑Menu甯冨眬鏂囦欢鐨 + else { + getMenuInflater().inflate(R.menu.note_edit, menu); + } + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); + } else { + menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); + } + if (mWorkingNote.hasClockAlert()) { + menu.findItem(R.id.menu_alert).setVisible(false); + } else { + menu.findItem(R.id.menu_delete_remind).setVisible(false); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + //鏍规嵁鑿滃崟鐨刬d鏉ョ紪鍓х浉鍏抽」鐩 + case R.id.menu_new_note: + //鍒涘缓涓涓柊鐨勪究绛 + createNewNote(); + break; + case R.id.menu_delete: + //鍒犻櫎渚跨 + AlertDialog.Builder builder = new AlertDialog.Builder(this); + //鍒涘缓鍏充簬鍒犻櫎鎿嶄綔鐨勫璇濇 + builder.setTitle(getString(R.string.alert_title_delete)); + // 璁剧疆鏍囩鐨勬爣棰樹负alert_title_delete + builder.setIcon(android.R.drawable.ic_dialog_alert); + //璁剧疆瀵硅瘽妗嗗浘鏍 + builder.setMessage(getString(R.string.alert_message_delete_note)); + //璁剧疆瀵硅瘽妗嗗唴瀹 + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + //寤虹珛鎸夐敭鐩戝惉鍣 + public void onClick(DialogInterface dialog, int which) { + //鐐瑰嚮鎵瑙﹀彂浜嬩欢 + deleteCurrentNote(); + // 鍒犻櫎鍗曠渚跨 + finish(); + } + }); + //娣诲姞鈥淵ES鈥濇寜閽 + builder.setNegativeButton(android.R.string.cancel, null); + //娣诲姞鈥淣O鈥濈殑鎸夐挳 + builder.show(); + //鏄剧ず瀵硅瘽妗 + break; + case R.id.menu_font_size: + //瀛椾綋澶у皬鐨勭紪杈 + mFontSizeSelector.setVisibility(View.VISIBLE); + // 灏嗗瓧浣撻夋嫨鍣ㄧ疆涓哄彲瑙 + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); + // 閫氳繃id鎵惧埌鐩稿簲鐨勫ぇ灏 + break; + case R.id.menu_list_mode: + //閫夋嫨鍒楄〃妯″紡 + mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? + TextNote.MODE_CHECK_LIST : 0); + break; + case R.id.menu_share: + //鑿滃崟鍏变韩 + getWorkingText(); + sendTo(this, mWorkingNote.getContent()); + // 鐢╯endto鍑芥暟灏嗚繍琛屾枃鏈彂閫佸埌閬嶅巻鐨勬湰鏂囧唴 + break; + case R.id.menu_send_to_desktop: + //鍙戦佸埌妗岄潰 + sendToDesktop(); + break; + case R.id.menu_alert: + //鍒涘缓鎻愰啋鍣 + setReminder(); + break; + case R.id.menu_delete_remind: + //鍒犻櫎鏃ユ湡鎻愰啋 + mWorkingNote.setAlertDate(0, false); + break; + default: + break; + } + return true; + } + + private void setReminder() { + DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); + // 寤虹珛淇敼鏃堕棿鏃ユ湡鐨勫璇濇 + d.setOnDateTimeSetListener(new OnDateTimeSetListener() { + public void OnDateTimeSet(AlertDialog dialog, long date) { + mWorkingNote.setAlertDate(date , true); + //閫夋嫨鎻愰啋鐨勬棩鏈 + } + }); + //寤虹珛鏃堕棿鏃ユ湡鐨勭洃鍚櫒 + d.show(); + //鏄剧ず瀵硅瘽妗 + } + + /** + * Share note to apps that support {@link Intent#ACTION_SEND} action + * and {@text/plain} type + */ + private void sendTo(Context context, String info) { + Intent intent = new Intent(Intent.ACTION_SEND); + //寤虹珛intent閾炬帴閫夐」 + intent.putExtra(Intent.EXTRA_TEXT, info); + //灏嗛渶瑕佷紶閫掔殑渚跨淇℃伅鏀惧叆text鏂囦欢涓 + intent.setType("text/plain"); + //缂栬緫杩炴帴鍣ㄧ殑绫诲瀷 + context.startActivity(intent); + //鍦╝cti涓繘琛岄摼鎺 + } + + private void createNewNote() { + // Firstly, save current editing notes + //淇濆瓨褰撳墠渚跨 + saveNote(); + + // For safety, start a new NoteEditActivity + finish(); + Intent intent = new Intent(this, NoteEditActivity.class); + //璁剧疆閾炬帴鍣 + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + //璇ユ椿鍔ㄥ畾涔変负鍒涘缓鎴栫紪杈 + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); + //灏嗚繍琛屼究绛剧殑id娣诲姞鍒癐NTENT_EXTRA_FOLDER_ID鏍囪涓 + startActivity(intent); + //灏嗚繍琛屼究绛剧殑id娣诲姞鍒癐NTENT_EXTRA_FOLDER_ID鏍囪涓 + } + + private void deleteCurrentNote() { + if (mWorkingNote.existInDatabase()) { + //鍋囧褰撳墠杩愯鐨勪究绛惧唴瀛樻湁鏁版嵁 + HashSet ids = new HashSet(); + long id = mWorkingNote.getNoteId(); + if (id != Notes.ID_ROOT_FOLDER) { + ids.add(id); + //濡傛灉涓嶆槸澶存枃浠跺す寤虹珛涓涓猦ash琛ㄦ妸渚跨id瀛樿捣鏉 + } else { + Log.d(TAG, "Wrong note id, should not happen"); + //鍚﹀垯鎶ラ敊 + } + if (!isSyncMode()) { + //鍦ㄩ潪鍚屾妯″紡鎯呭喌涓 + //鍒犻櫎鎿嶄綔 + if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { + Log.e(TAG, "Delete Note error"); + } + } else { + //鍚屾妯″紡 + //绉诲姩鑷冲瀮鍦炬枃浠跺す鐨勬搷浣 + if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + } + mWorkingNote.markDeleted(true); + //灏嗚繖浜涙爣绛剧殑鍒犻櫎鏍囪缃负true + + } + + private boolean isSyncMode() { + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + } + + public void onClockAlertChanged(long date, boolean set) { + /** + * User could set clock to an unsaved note, so before setting the + * alert clock, we should save the note first + */ + if (!mWorkingNote.existInDatabase()) { + //棣栧厛淇濆瓨宸叉湁鐨勪究绛 + saveNote(); + } + if (mWorkingNote.getNoteId() > 0) { + Intent intent = new Intent(this, AlarmReceiver.class); + intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); + //鑻ユ湁鏈夎繍琛岀殑渚跨灏辨槸寤虹珛涓涓摼鎺ュ櫒灏嗘爣绛緄d閮藉瓨鍦╱ri涓 + PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); + AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); + //璁剧疆鎻愰啋绠$悊鍣 + showAlertHeader(); + if(!set) { + alarmManager.cancel(pendingIntent); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); + //濡傛灉鐢ㄦ埛璁剧疆浜嗘椂闂达紝灏遍氳繃鎻愰啋绠$悊鍣ㄨ缃竴涓洃鍚簨椤 + } + } else { + /** + * There is the condition that user has input nothing (the note is + * not worthy saving), we have no note id, remind the user that he + * should input something + */ + //娌℃湁杩愯鐨勪究绛惧氨鎶ラ敊 + Log.e(TAG, "Clock alert setting error"); + showToast(R.string.error_note_empty_for_clock); + } + } + + public void onWidgetChanged() { + updateWidget(); + } + + public void onEditTextDelete(int index, String text) { + int childCount = mEditTextList.getChildCount(); + if (childCount == 1) { + return; + //娌℃湁缂栬緫妗嗙殑璇濈洿鎺ヨ繑鍥 + } + + for (int i = index + 1; i < childCount; i++) { + ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) + .setIndex(i - 1); + //閫氳繃id鎶婄紪杈戞瀛樺湪渚跨缂栬緫妗嗕腑 + } + + mEditTextList.removeViewAt(index); + //鍒犻櫎鐗瑰畾浣嶇疆鐨勮鍥 + NoteEditText edit = null; + if(index == 0) { + edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( + R.id.et_edit_text); + //閫氳繃id鎶婄紪杈戞瀛樺湪绌虹殑NoteEditText涓 + } else { + edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( + R.id.et_edit_text); + } + int length = edit.length(); + edit.append(text); + edit.requestFocus();//璇锋眰浼樺厛瀹屾垚璇ユ 缂栬緫 + edit.setSelection(length);//瀹氫綅鍒發ength浣嶇疆澶勭殑鏉$洰 + } + + public void onEditTextEnter(int index, String text) { + /** + * Should not happen, check for debug + */ + if(index > mEditTextList.getChildCount()) { + Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); + //瓒婄晫鎶婂伓鍋 + } + + View view = getListItem(text, index); + mEditTextList.addView(view, index); + //寤虹珛涓涓柊鐨勮鍥惧苟娣诲姞鍒扮紪杈戞枃鏈鍐 + NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + edit.requestFocus();//璇锋眰浼樺厛鎿嶄綔 + edit.setSelection(0);//瀹氫綅鍒拌捣濮嬩綅缃 + for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { + ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) + .setIndex(i); + //閬嶅巻瀛愭枃鏈骞惰缃搴斿涓嬫爣 + } + } + + private void switchToListMode(String text) { + mEditTextList.removeAllViews(); + String[] items = text.split("\n"); + int index = 0; + //娓呯┖鎵鏈夎鍥撅紝鍒濆鍖栦笅鏍 + for (String item : items) { + if(!TextUtils.isEmpty(item)) { + mEditTextList.addView(getListItem(item, index)); + index++; + //閬嶅巻鎵鏈夋枃鏈崟鍏冨苟娣诲姞鍒版枃鏈涓 + } + } + mEditTextList.addView(getListItem("", index)); + mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); + //浼樺厛璇锋眰姝ゆ搷浣 + + mNoteEditor.setVisibility(View.GONE); + //渚跨缂栬緫鍣ㄤ笉鍙 + mEditTextList.setVisibility(View.VISIBLE); + //灏嗘枃鏈紪杈戞缃负鍙 + } + + private Spannable getHighlightQueryResult(String fullText, String userQuery) { + SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); + //鏂板缓涓涓晥鏋滈夐」 + if (!TextUtils.isEmpty(userQuery)) { + mPattern = Pattern.compile(userQuery); + //灏嗙敤鎴风殑璇㈤棶杩涜瑙f瀽 + Matcher m = mPattern.matcher(fullText); + //寤虹珛涓涓姸鎬佹満妫鏌attern骞惰繘琛屽尮閰 + int start = 0; + while (m.find(start)) { + spannable.setSpan( + new BackgroundColorSpan(this.getResources().getColor( + R.color.user_query_highlight)), m.start(), m.end(), + Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + //璁剧疆鑳屾櫙棰滆壊 + start = m.end(); + //璺熸柊璧峰浣嶇疆 + } + } + return spannable; + } + + private View getListItem(String item, int index) { + View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); + //鍒涘缓涓涓鍥 + final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + //鍒涘缓涓涓枃鏈紪杈戞骞惰缃彲瑙佹 + CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); + cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } else { + edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); + } + } + }); + //寤虹珛涓涓墦閽╂骞惰缃洃鍚櫒 + + if (item.startsWith(TAG_CHECKED)) { + //閫夋嫨鍕鹃 + cb.setChecked(true); + edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + item = item.substring(TAG_CHECKED.length(), item.length()).trim(); + } else if (item.startsWith(TAG_UNCHECKED)) { + //閫夋嫨涓嶅嬀閫 + cb.setChecked(false); + edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); + item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); + } + + edit.setOnTextViewChangeListener(this); + edit.setIndex(index); + edit.setText(getHighlightQueryResult(item, mUserQuery)); + //杩愯缂栬緫妗嗙殑鐩戝惉鍣ㄥ璇ヨ涓轰綔鍑哄弽搴旓紝骞惰缃笅鏍囧強鏂囨湰鍐呭 + return view; + } + + public void onTextChange(int index, boolean hasText) { + if (index >= mEditTextList.getChildCount()) { + Log.e(TAG, "Wrong index, should not happen"); + return; + //瓒婄晫鎶ラ敊 + } + if(hasText) { + mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); + } else { + mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); + } + //濡傛灉鍐呭涓嶄负绌哄垯灏嗗叾瀛愮紪杈戞鍙鎬х疆涓哄彲瑙侊紝鍚﹀垯涓嶅彲瑙 + } + + public void onCheckListModeChanged(int oldMode, int newMode) { + if (newMode == TextNote.MODE_CHECK_LIST) { + switchToListMode(mNoteEditor.getText().toString()); + //妫鏌ユā寮忓垏鎹㈠埌鍒楄〃妯″紡 + } else { + if (!getWorkingText()) { + mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", + "")); + } + //鑻ユ槸鑾峰彇鍒版枃鏈氨鏀瑰彉鍏舵鏌ユ爣璁 + mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); + mEditTextList.setVisibility(View.GONE); + mNoteEditor.setVisibility(View.VISIBLE); + //淇敼鏂囨湰缂栬緫鍣ㄧ殑鍐呭鍜屽彲瑙佹 + } + } + + private boolean getWorkingText() { + boolean hasChecked = false; + //鍒濆鍖朿heck鏍囪 + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + // 鑻ユā寮忎负CHECK_LIST + StringBuilder sb = new StringBuilder(); + //鍒涘缓鍙彉瀛楃涓 + for (int i = 0; i < mEditTextList.getChildCount(); i++) { + View view = mEditTextList.getChildAt(i); + //閬嶅巻鎵鏈夊瓙缂栬緫妗嗙殑瑙嗗浘 + NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + if (!TextUtils.isEmpty(edit.getText())) { + //鑻ユ枃鏈笉涓虹┖ + if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { + //璇ラ夐」妗嗗凡鎵撻挬 + sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); + hasChecked = true; + //鎵╁睍瀛楃涓蹭负宸叉墦閽╁苟鎶婃爣璁扮疆true + } else { + sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); + //鎵╁睍瀛楃涓叉坊鍔犳湭鎵撻挬 + } + } + } + mWorkingNote.setWorkingText(sb.toString()); + //鍒╃敤缂栬緫濂界殑瀛楃涓茶缃繍琛屼究绛剧殑鍐呭 + } else { + mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); + // 鑻ヤ笉鏄妯″紡鐩存帴鐢ㄧ紪杈戝櫒涓殑鍐呭璁剧疆杩愯涓爣绛剧殑鍐呭 + } + return hasChecked; + } + + private boolean saveNote() { + getWorkingText(); + boolean saved = mWorkingNote.saveNote(); + //杩愯 getWorkingText()涔嬪悗淇濆瓨 + if (saved) { + /** + * There are two modes from List view to edit view, open one note, + * create/edit a node. Opening node requires to the original + * position in the list when back from edit view, while creating a + * new node requires to the top of the list. This code + * {@link #RESULT_OK} is used to identify the create/edit state + */ + //濡傝嫳鏂囨敞閲婃墍璇撮摼鎺ESULT_OK鏄负浜嗚瘑鍒繚瀛樼殑2绉嶆儏鍐碉紝涓鏄垱寤哄悗淇濆瓨锛屼簩鏄慨鏀瑰悗淇濆瓨 + setResult(RESULT_OK); + } + return saved; + } + + private void sendToDesktop() { + /** + * Before send message to home, we should make sure that current + * editing note is exists in databases. So, for new note, firstly + * save it + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + //鑻ヤ笉瀛樺湪鏁版嵁涔熷氨鏄柊鐨勬爣绛惧氨淇濆瓨璧锋潵鍏 + } + + if (mWorkingNote.getNoteId() > 0) { + //鑻ユ槸鏈夊唴瀹 + Intent sender = new Intent(); + Intent shortcutIntent = new Intent(this, NoteEditActivity.class); + //寤虹珛鍙戦佸埌妗岄潰鐨勮繛鎺ュ櫒 + shortcutIntent.setAction(Intent.ACTION_VIEW); + //閾炬帴涓轰竴涓鍥 + shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); + sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); + sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, + makeShortcutIconTitle(mWorkingNote.getContent())); + sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, + Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); + sender.putExtra("duplicate", true); + //灏嗕究绛剧殑鐩稿叧淇℃伅閮芥坊鍔犲埌瑕佸彂閫佺殑鏂囦欢閲 + sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); + //璁剧疆sneder鐨勮涓烘槸鍙戦 + showToast(R.string.info_note_enter_desktop); + sendBroadcast(sender); + //鏄剧ず鍒版闈 + } else { + /** + * There is the condition that user has input nothing (the note is + * not worthy saving), we have no note id, remind the user that he + * should input something + */ + Log.e(TAG, "Send to desktop error"); + showToast(R.string.error_note_empty_for_send_to_desktop); + //绌轰究绛剧洿鎺ユ姤閿 + } + } + + private String makeShortcutIconTitle(String content) { + content = content.replace(TAG_CHECKED, ""); + content = content.replace(TAG_UNCHECKED, ""); + return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, + SHORTCUT_ICON_TITLE_MAX_LEN) : content; + //鐩存帴璁剧疆涓篶ontent涓殑鍐呭骞惰繑鍥烇紝鏈夊嬀閫夊拰鏈嬀閫2绉 + } + + private void showToast(int resId) { + showToast(resId, Toast.LENGTH_SHORT); + } + + private void showToast(int resId, int duration) { + Toast.makeText(this, resId, duration).show(); + } +} diff --git a/doc/NoteEditText.java b/doc/NoteEditText.java new file mode 100644 index 0000000..9010fdb --- /dev/null +++ b/doc/NoteEditText.java @@ -0,0 +1,262 @@ +/* + * 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; + + +//缁ф壙edittext锛岃缃究绛捐缃枃鏈 +public class NoteEditText extends EditText { + private static final String TAG = "NoteEditText"; + private int mIndex; + private int mSelectionStartBeforeDelete; + + private static final String SCHEME_TEL = "tel:" ; + private static final String SCHEME_HTTP = "http:" ; + private static final String SCHEME_EMAIL = "mailto:" ; + //寤虹珛涓涓瓧绗﹀拰鏁存暟鐨刪ash琛紝鐢ㄤ簬閾炬帴鐢佃瘽锛岀綉绔欙紝杩樻湁閭 + 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); + } + + /** + * Call by the {@link NoteEditActivity} to delete or add edit text + */ + //鍦∟oteEditActivity涓垹闄ゆ垨娣诲姞鏂囨湰鐨勬搷浣滐紝鍙互鐪嬪仛鏄竴涓枃鏈槸鍚﹁鍙樼殑鏍囪锛岃嫳鏂囨敞閲婂凡璇存槑鐨勫緢娓呮 + public interface OnTextViewChangeListener { + /** + * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens + * and the text is null + */ + //澶勭悊鍒犻櫎鎸夐敭鏃剁殑鎿嶄綔 + void onEditTextDelete(int index, String text); + + /** + * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} + * happen + */ + //澶勭悊杩涘叆鎸夐敭鏃剁殑鎿嶄綔 + void onEditTextEnter(int index, String text); + + /** + * Hide or show item option when text change + */ + void onTextChange(int index, boolean hasText); + } + + private OnTextViewChangeListener mOnTextViewChangeListener; + + //鏍规嵁context璁剧疆鏂囨湰 + public NoteEditText(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); + } + // 鏍规嵁defstyle鑷姩鍒濆鍖 + public NoteEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO Auto-generated constructor stub + } + + @Override + //view閲岀殑鍑芥暟锛屽鐞嗘墜鏈哄睆骞曠殑鎵鏈変簨浠 + /*鍙傛暟event涓烘墜鏈哄睆骞曡Е鎽镐簨浠跺皝瑁呯被鐨勫璞★紝鍏朵腑灏佽浜嗚浜嬩欢鐨勬墍鏈変俊鎭紝 + 渚嬪瑙︽懜鐨勪綅缃佽Е鎽哥殑绫诲瀷浠ュ強瑙︽懜鐨勬椂闂寸瓑銆傝瀵硅薄浼氬湪鐢ㄦ埛瑙︽懜鎵嬫満灞忓箷鏃惰鍒涘缓銆*/ + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + //閲嶅啓浜嗛渶瑕佸鐞嗗睆骞曡鎸変笅鐨勪簨浠 + case MotionEvent.ACTION_DOWN: + //璺熸柊褰撳墠鍧愭爣鍊 + int x = (int) event.getX(); + int y = (int) event.getY(); + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); + x += getScrollX(); + y += getScrollY(); + + //鐢ㄥ竷灞鎺т欢layout鏍规嵁x,y鐨勬柊鍊艰缃柊鐨勪綅缃 + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + //鏇存柊鍏夋爣鏂扮殑浣嶇疆 + Selection.setSelection(getText(), off); + break; + } + + return super.onTouchEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + //鏍规嵁鎸夐敭鐨 Unicode 缂栫爜鍊兼潵澶勭悊 + case KeyEvent.KEYCODE_ENTER: + //鈥滆繘鍏モ濇寜閿 + if (mOnTextViewChangeListener != null) { + return false; + } + break; + case KeyEvent.KEYCODE_DEL: + //鈥滃垹闄も濇寜閿 + mSelectionStartBeforeDelete = getSelectionStart(); + break; + default: + break; + } + //缁х画鎵ц鐖剁被鐨勫叾浠栫偣鍑讳簨浠 + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch(keyCode) { + //鏍规嵁鎸夐敭鐨 Unicode 缂栫爜鍊兼潵澶勭悊锛屾湁鍒犻櫎鍜岃繘鍏2绉嶆搷浣 + case KeyEvent.KEYCODE_DEL: + if (mOnTextViewChangeListener != null) { + //鑻ユ槸琚慨鏀硅繃 + if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + //鑻ヤ箣鍓嶆湁琚慨鏀瑰苟涓旀枃妗d笉涓虹┖ + mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); + //鍒╃敤涓婃枃OnTextViewChangeListener瀵筀EYCODE_DEL鎸夐敭鎯呭喌鐨勫垹闄ゅ嚱鏁拌繘琛屽垹闄 + return true; + } + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + //鍏朵粬鎯呭喌鎶ラ敊锛屾枃妗g殑鏀瑰姩鐩戝惉鍣ㄥ苟娌℃湁寤虹珛 + } + break; + case KeyEvent.KEYCODE_ENTER: + //鍚屼笂涔熸槸鍒嗕负鐩戝惉鍣ㄦ槸鍚﹀缓绔2绉嶆儏鍐 + if (mOnTextViewChangeListener != null) { + int selectionStart = getSelectionStart(); + //鑾峰彇褰撳墠浣嶇疆 + String text = getText().subSequence(selectionStart, length()).toString(); + //鑾峰彇褰撳墠鏂囨湰 + setText(getText().subSequence(0, selectionStart)); + //鏍规嵁鑾峰彇鐨勬枃鏈缃綋鍓嶆枃鏈 + mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); + //褰搟@link KeyEvent#KEYCODE_ENTER}娣诲姞鏂版枃鏈 + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + //鍏朵粬鎯呭喌鎶ラ敊锛屾枃妗g殑鏀瑰姩鐩戝惉鍣ㄥ苟娌℃湁寤虹珛 + } + 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); + //mOnTextViewChangeListener瀛愬嚱鏁帮紝缃甪alse闅愯棌浜嬩欢閫夐」 + } else { + mOnTextViewChangeListener.onTextChange(mIndex, true); + //mOnTextViewChangeListener瀛愬嚱鏁帮紝缃畉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; + for(String schema: sSchemaActionResMap.keySet()) { + //鑾峰彇璁″垝琛ㄤ腑鎵鏈夌殑key鍊 + if(urls[0].getURL().indexOf(schema) >= 0) { + //鑻rl鍙互娣诲姞鍒欏湪娣诲姞鍚庡皢defaultResId缃负key鎵鏄犲皠鐨勫 + defaultResId = sSchemaActionResMap.get(schema); + break; + } + } + + if (defaultResId == 0) { + //defaultResId == 0鍒欒鏄巙rl骞舵病鏈夋坊鍔犱换浣曚笢瑗匡紝鎵浠ョ疆涓鸿繛鎺ュ叾浠朣chemaActionResMap鐨勫 + defaultResId = R.string.note_link_other; + } + + //寤虹珛鑿滃崟 + menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( + new OnMenuItemClickListener() { + //鏂板缓鎸夐敭鐩戝惉鍣 + public boolean onMenuItemClick(MenuItem item) { + // goto a new intent + urls[0].onClick(NoteEditText.this); + //鏍规嵁鐩稿簲鐨勬枃鏈缃彍鍗曠殑鎸夐敭 + return true; + } + }); + } + } + //缁х画鎵ц鐖剁被鐨勫叾浠栬彍鍗曞垱寤虹殑浜嬩欢 + super.onCreateContextMenu(menu); + } +} diff --git a/doc/NoteItemData.java b/doc/NoteItemData.java new file mode 100644 index 0000000..3f8b240 --- /dev/null +++ b/doc/NoteItemData.java @@ -0,0 +1,233 @@ +/* + * 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 [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, + NoteColumns.HAS_ATTACHMENT, + NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, + NoteColumns.PARENT_ID, + NoteColumns.SNIPPET, + NoteColumns.TYPE, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + }; + + //甯搁噺鏍囪鍜屾暟鎹氨涓嶄竴涓鏍囪浜嗭紝鎰忎箟缈昏瘧鍩烘湰灏辩煡閬 + private static final int ID_COLUMN = 0; + private static final int ALERTED_DATE_COLUMN = 1; + private static final int BG_COLOR_ID_COLUMN = 2; + private static final int CREATED_DATE_COLUMN = 3; + private static final int HAS_ATTACHMENT_COLUMN = 4; + private static final int MODIFIED_DATE_COLUMN = 5; + private static final int NOTES_COUNT_COLUMN = 6; + private static final int PARENT_ID_COLUMN = 7; + private static final int SNIPPET_COLUMN = 8; + private static final int TYPE_COLUMN = 9; + private static final int WIDGET_ID_COLUMN = 10; + private static final int WIDGET_TYPE_COLUMN = 11; + + private long mId; + private long mAlertDate; + private int mBgColorId; + private long mCreatedDate; + private boolean mHasAttachment; + private long mModifiedDate; + private int mNotesCount; + private long mParentId; + private String mSnippet; + private int mType; + private int mWidgetId; + private int mWidgetType; + private String mName; + private String mPhoneNumber; + + private boolean mIsLastItem; + private boolean mIsFirstItem; + private boolean mIsOnlyOneItem; + private boolean mIsOneNoteFollowingFolder; + private boolean mIsMultiNotesFollowingFolder; + + //鍒濆鍖朜oteItemData锛屼富瑕佸埄鐢ㄥ厜鏍嘽ursor鑾峰彇鐨勪笢瑗 + public NoteItemData(Context context, Cursor cursor) { + //getxxx涓鸿浆鎹㈡牸寮 + mId = cursor.getLong(ID_COLUMN); + mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); + mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); + mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; + mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); + mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); + mParentId = cursor.getLong(PARENT_ID_COLUMN); + mSnippet = cursor.getString(SNIPPET_COLUMN); + mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( + NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); + mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); + + //鍒濆鍖栫數璇濆彿鐮佺殑淇℃伅 + mPhoneNumber = ""; + if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); + if (!TextUtils.isEmpty(mPhoneNumber)) { + mName = Contact.getContact(context, mPhoneNumber); + if (mName == null) { + mName = mPhoneNumber; + } + } + } + + if (mName == null) { + mName = ""; + } + checkPostion(cursor); + } + + //鏍规嵁榧犳爣鐨勪綅缃缃爣璁帮紝鍜屼綅缃 + private void checkPostion(Cursor cursor) { + //鍒濆鍖栧嚑涓爣璁帮紝cursor鍏蜂綋鍔熻兘绗旇涓凡鎻愬埌锛屼笉涓涓鍙欒堪 + mIsLastItem = cursor.isLast() ? true : false; + mIsFirstItem = cursor.isFirst() ? true : false; + mIsOnlyOneItem = (cursor.getCount() == 1); + //鍒濆鍖栤滃閲嶅瓙鏂囦欢鈥濃滃崟涓瀛愭枃浠垛2涓爣璁 + mIsMultiNotesFollowingFolder = false; + mIsOneNoteFollowingFolder = false; + + //涓昏鏄缃笂璇2鏍囪 + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {//鑻ユ槸note鏍煎紡骞朵笖涓嶆槸绗竴涓厓绱 + int position = cursor.getPosition(); + if (cursor.moveToPrevious()) {//鑾峰彇鍏夋爣浣嶇疆鍚庣湅涓婁竴琛 + if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER + || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {//鑻ュ厜鏍囨弧瓒崇郴缁熸垨note鏍煎紡 + if (cursor.getCount() > (position + 1)) { + mIsMultiNotesFollowingFolder = true;//鑻ユ槸鏁版嵁琛屾暟澶т簬浣嗗墠浣嶇疆+1鍒欒缃垚姝g‘ + } else { + mIsOneNoteFollowingFolder = true;//鍚﹀垯鍗曚竴鏂囦欢澶规爣璁颁负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; + } + + 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; + } + + 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; + } + + public int getNotesCount() { + return mNotesCount; + } + + public long getFolderId () { + return mParentId; + } + + public int getType() { + return mType; + } + + public int getWidgetType() { + return mWidgetType; + } + + public int getWidgetId() { + return mWidgetId; + } + + public String getSnippet() { + return mSnippet; + } + + public boolean hasAlert() { + return (mAlertDate > 0); + } + + //鑻ユ暟鎹埗id涓轰繚瀛樿嚦鏂囦欢澶规ā寮忕殑id涓旀弧瓒崇數璇濆彿鐮佸崟鍏冧笉涓虹┖锛屽垯isCallRecord涓簍rue + public boolean isCallRecord() { + return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); + } + + public static int getNoteType(Cursor cursor) { + return cursor.getInt(TYPE_COLUMN); + } +} diff --git a/doc/WorkingNote.java b/doc/WorkingNote.java new file mode 100644 index 0000000..cb9c4cc --- /dev/null +++ b/doc/WorkingNote.java @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + + +public class 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; + // 澹版槑 DATA_PROJECTION瀛楃涓叉暟缁 + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + // 澹版槑 NOTE_PROJECTION瀛楃涓叉暟缁 + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + 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) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + + // WorkingNote鐨勬瀯閫犲嚱鏁 + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + + // 鍔犺浇Note + // 閫氳繃鏁版嵁搴撹皟鐢╭uery鍑芥暟鎵惧埌绗竴涓潯鐩 + 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(); + } + + // 鍔犺浇NoteData + 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); + } + } + + // 鍒涘缓绌虹殑Note + // 浼犲弬锛歝ontext锛屾枃浠跺すid锛寃idget锛岃儗鏅鑹 + 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); + } + + // 淇濆瓨Note + public synchronized boolean saveNote() { + 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() { + return mNoteId > 0; + } + + // 鏄惁鍊煎緱淇濆瓨 + private boolean isWorthSaving() { + // 琚垹闄わ紝鎴栵紙涓嶅湪鏁版嵁搴撲腑 鍐呭涓虹┖锛夛紝鎴 鏈湴宸蹭繚瀛樿繃 + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + // 璁剧疆mNoteSettingStatusListener + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + // 璁剧疆AlertDate + // 鑻 mAlertDate涓巇ata涓嶅悓锛屽垯鏇存敼mAlertDate骞惰瀹歂oteValue + 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(); + // 璋冪敤mNoteSettingStatusListener鐨 onWidgetChanged鏂规硶 + } + } + + // 璁惧畾鑳屾櫙棰滆壊 + public void setBgColorId(int id) { + if (id != mBgColorId) {//璁惧畾鏉′欢 id != mBgColorId + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + // 璁惧畾妫鏌ュ垪琛ㄦā寮 + // 鍙傛暟锛歮ode + public void setCheckListMode(int mode) { + if (mMode != mode) {//璁惧畾鏉′欢 mMode != mode + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + // 璁惧畾WidgetType + // 鍙傛暟锛歵ype + public void setWidgetType(int type) { + if (type != mWidgetType) {//璁惧畾鏉′欢 type != mWidgetType + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + // 璋冪敤Note鐨剆etNoteValue鏂规硶鏇存敼WidgetType + } + } + + // 璁惧畾WidgetId + // 鍙傛暟锛歩d + public void setWidgetId(int id) { + if (id != mWidgetId) {//璁惧畾鏉′欢 id != mWidgetId + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + // 璋冪敤Note鐨剆etNoteValue鏂规硶鏇存敼WidgetId + } + } + + // 璁惧畾WorkingTex + // 鍙傛暟锛氭洿鏀圭殑text + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) {//璁惧畾鏉′欢 mContent, text鍐呭涓嶅悓 + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + // 璋冪敤Note鐨剆etTextData鏂规硶鏇存敼WorkingText + } + } + + // 杞彉mNote鐨凜allData鍙奀allNote淇℃伅 + // 鍙傛暟锛歋tring phoneNumber, long callDate + 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() { + return (mAlertDate > 0 ? true : false); + } + + // 鑾峰彇Content + public String getContent() { + return mContent; + } + + // 鑾峰彇AlertDate + public long getAlertDate() { + return mAlertDate; + } + + // 鑾峰彇ModifiedDate + public long getModifiedDate() { + return mModifiedDate; + } + + // 鑾峰彇鑳屾櫙棰滆壊鏉ユ簮id + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + // 鑾峰彇鑳屾櫙棰滆壊id + public int getBgColorId() { + return mBgColorId; + } + + // 鑾峰彇鏍囬鑳屾櫙棰滆壊id + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + // 鑾峰彇CheckListMode + public int getCheckListMode() { + return mMode; + } + + // 鑾峰彇渚跨id + public long getNoteId() { + return mNoteId; + } + + // 鑾峰彇鏂囦欢澶筰d + public long getFolderId() { + return mFolderId; + } + + // 鑾峰彇WidgetId + public int getWidgetId() { + return mWidgetId; + } + + // 鑾峰彇WidgetType + public int getWidgetType() { + return mWidgetType; + } + + // 鍒涘缓鎺ュ彛 NoteSettingChangedListener,渚跨鏇存柊鐩戣 + // 涓篘oteEditActivity鎻愪緵鎺ュ彛 + // 鎻愪緵鍑芥暟鏈 + 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); + } +}