新增字符统计功能 #46

Merged
p7fulywfa merged 1 commits from tangbo_branch into master 3 months ago

@ -265,6 +265,13 @@ public class Notes {
*/ */
public static final String LOCK_TYPE = "lock_type"; public static final String LOCK_TYPE = "lock_type";
/**
*
* <P> : TEXT </P>
* <P> </P>
*/
public static final String LOCK_HINT = "lock_hint";
/** /**
* 便 * 便
* <P> : INTEGER (long) </P> * <P> : INTEGER (long) </P>

@ -35,7 +35,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
// 数据库文件名 // 数据库文件名
private static final String DB_NAME = "note.db"; private static final String DB_NAME = "note.db";
// 数据库版本号,用于升级控制 // 数据库版本号,用于升级控制
private static final int DB_VERSION = 7; private static final int DB_VERSION = 8;
/** /**
* *
@ -79,6 +79,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''," + NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCK_HINT + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.DELETED_DATE + " INTEGER NOT NULL DEFAULT 0" + NoteColumns.DELETED_DATE + " INTEGER NOT NULL DEFAULT 0" +
")"; ")";
@ -425,6 +426,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++; oldVersion++;
} }
// 版本7升级到版本8添加密码提示字段
if (oldVersion == 7) {
upgradeToV8(db);
oldVersion++;
}
// 如果需要,重新创建触发器 // 如果需要,重新创建触发器
if (reCreateTriggers) { if (reCreateTriggers) {
reCreateNoteTableTriggers(db); reCreateNoteTableTriggers(db);
@ -530,4 +537,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" +
" END"); " END");
} }
/**
* 8
* @param db
*/
private void upgradeToV8(SQLiteDatabase db) {
// 添加LOCK_HINT字段用于存储密码提示
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_HINT
+ " TEXT NOT NULL DEFAULT ''");
}
} }

@ -46,6 +46,9 @@ import android.view.View.OnClickListener;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Button; import android.widget.Button;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.util.TypedValue;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText; import android.widget.EditText;
@ -172,6 +175,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/** 笔记编辑面板 */ /** 笔记编辑面板 */
private View mNoteEditorPanel; private View mNoteEditorPanel;
/** 字符统计显示文本框 */
private TextView mTvCharCount;
/** 工作笔记对象,用于处理笔记数据 */ /** 工作笔记对象,用于处理笔记数据 */
private WorkingNote mWorkingNote; private WorkingNote mWorkingNote;
@ -350,6 +356,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor.setText(getHighlightQueryResult(htmlContent, mUserQuery)); mNoteEditor.setText(getHighlightQueryResult(htmlContent, mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length()); mNoteEditor.setSelection(mNoteEditor.getText().length());
} }
// 初始化字符统计显示
updateCharCount();
for (Integer id : sBgSelectorSelectionMap.keySet()) { for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
} }
@ -480,7 +488,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteHeaderHolder.ibSetBgColor = (ImageButton) findViewById(R.id.btn_set_bg_color); mNoteHeaderHolder.ibSetBgColor = (ImageButton) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteEditor = (NoteEditText) findViewById(R.id.note_edit_view); mNoteEditor = (NoteEditText) findViewById(R.id.note_edit_view);
mNoteEditor.setOnTextViewChangeListener(this);
mNoteEditorPanel = findViewById(R.id.sv_note_edit); mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mTvCharCount = (TextView) findViewById(R.id.tv_char_count);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) { for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id); ImageView iv = (ImageView) findViewById(id);
@ -768,6 +778,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R.id.menu_unlock: case R.id.menu_unlock:
showPasswordDialogForUnlock(); showPasswordDialogForUnlock();
break; break;
case R.id.menu_change_password:
showChangePasswordDialog();
break;
case R.id.menu_note_template: case R.id.menu_note_template:
openTemplateSelector(); openTemplateSelector();
break; break;
@ -812,23 +825,41 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/** /**
* *
* <p> * <p>
* *
* </p> * </p>
*/ */
private void showPasswordDialogForLock() { private void showPasswordDialogForLock() {
// 创建布局并添加密码输入框
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(60, 40, 60, 40);
layout.setPadding(60, 40, 60, 40);
// 设置子视图间距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
final EditText passwordEditText = new EditText(this); final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password); passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(passwordEditText);
// 添加密码提示输入框
final EditText hintEditText = new EditText(this);
hintEditText.setHint(R.string.hint_enter_password_hint);
layout.addView(hintEditText);
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password) .setTitle(R.string.dialog_enter_password)
.setView(passwordEditText) .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString(); String password = passwordEditText.getText().toString();
String hint = hintEditText.getText().toString();
if (!TextUtils.isEmpty(password)) { if (!TextUtils.isEmpty(password)) {
lockCurrentNote(password); lockCurrentNote(password, hint);
} }
} }
}) })
@ -843,13 +874,50 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* </p> * </p>
*/ */
private void showPasswordDialogForUnlock() { private void showPasswordDialogForUnlock() {
// 查询当前笔记的密码提示
String[] projection = {NoteColumns.LOCK_HINT};
String selection = NoteColumns.ID + "=?";
String[] selectionArgs = {String.valueOf(mWorkingNote.getNoteId())};
Cursor cursor = getContentResolver().query(Notes.CONTENT_NOTE_URI, projection, selection, selectionArgs, null);
String lockHint = null;
if (cursor != null && cursor.moveToFirst()) {
lockHint = cursor.getString(0);
cursor.close();
}
// 创建布局并添加密码输入框
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(60, 40, 60, 40);
// 创建布局参数,用于设置子视图间距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
final EditText passwordEditText = new EditText(this); final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password); passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(passwordEditText, params);
// 如果有密码提示,显示提示信息
if (!TextUtils.isEmpty(lockHint)) {
TextView hintTextView = new TextView(this);
hintTextView.setText(getString(R.string.password_hint_prefix) + lockHint);
hintTextView.setTextColor(getResources().getColor(R.color.secondary_text_dark));
hintTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
// 提示文本不需要底部间距
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(hintTextView, hintParams);
}
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password) .setTitle(R.string.dialog_enter_password)
.setView(passwordEditText) .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
@ -866,17 +934,20 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/** /**
* *
* <p> * <p>
* *
* </p> * </p>
* @param password * @param password
* @param hint
*/ */
private void lockCurrentNote(String password) { private void lockCurrentNote(String password, String hint) {
// 设置锁定状态为1已锁定 // 设置锁定状态为1已锁定
mWorkingNote.setNoteValue(NoteColumns.IS_LOCKED, "1"); mWorkingNote.setNoteValue(NoteColumns.IS_LOCKED, "1");
// 设置加密后的密码 // 设置加密后的密码
mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(password)); mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
// 设置锁定类型为笔记类型 // 设置锁定类型为笔记类型
mWorkingNote.setNoteValue(NoteColumns.LOCK_TYPE, String.valueOf(Notes.LOCK_TYPE_NOTE)); mWorkingNote.setNoteValue(NoteColumns.LOCK_TYPE, String.valueOf(Notes.LOCK_TYPE_NOTE));
// 设置密码提示
mWorkingNote.setNoteValue(NoteColumns.LOCK_HINT, hint);
// 保存笔记 // 保存笔记
saveNote(); saveNote();
// 显示提示信息 // 显示提示信息
@ -941,6 +1012,97 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return password; // 加密失败时返回原始密码 return password; // 加密失败时返回原始密码
} }
} }
/**
*
* <p>
*
* </p>
*/
private void showChangePasswordDialog() {
// 创建布局并添加输入框
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(60, 40, 60, 40);
// 创建布局参数,用于设置子视图间距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
// 旧密码输入框
final EditText oldPasswordEditText = new EditText(this);
oldPasswordEditText.setHint(R.string.hint_enter_password);
oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(oldPasswordEditText, params);
// 新密码输入框
final EditText newPasswordEditText = new EditText(this);
newPasswordEditText.setHint(R.string.hint_enter_password);
newPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(newPasswordEditText, params);
// 新密码提示输入框
final EditText newHintEditText = new EditText(this);
newHintEditText.setHint(R.string.hint_enter_password_hint);
layout.addView(newHintEditText);
new AlertDialog.Builder(this)
.setTitle(R.string.menu_change_password)
.setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String oldPassword = oldPasswordEditText.getText().toString();
String newPassword = newPasswordEditText.getText().toString();
String newHint = newHintEditText.getText().toString();
if (!TextUtils.isEmpty(oldPassword) && !TextUtils.isEmpty(newPassword)) {
changePassword(oldPassword, newPassword, newHint);
}
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
/**
*
* <p>
*
* </p>
* @param oldPassword
* @param newPassword
* @param newHint
*/
private void changePassword(String oldPassword, String newPassword, String newHint) {
// 使用ContentResolver直接查询数据库获取加密密码
String[] projection = {NoteColumns.LOCK_PASSWORD};
String selection = NoteColumns.ID + "=?";
String[] selectionArgs = {String.valueOf(mWorkingNote.getNoteId())};
Cursor cursor = getContentResolver().query(Notes.CONTENT_NOTE_URI, projection, selection, selectionArgs, null);
String encryptedPassword = null;
if (cursor != null && cursor.moveToFirst()) {
encryptedPassword = cursor.getString(0);
cursor.close();
}
// 验证旧密码是否正确
if (encryptedPassword != null && encryptedPassword.equals(encryptPassword(oldPassword))) {
// 旧密码正确,更新新密码和密码提示
mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(newPassword));
mWorkingNote.setNoteValue(NoteColumns.LOCK_HINT, newHint);
// 保存笔记
saveNote();
// 显示提示信息
Toast.makeText(this, getString(R.string.message_password_changed), Toast.LENGTH_SHORT).show();
} else {
// 旧密码错误,显示错误信息
Toast.makeText(this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
}
}
/** /**
* *
@ -1229,6 +1391,15 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else { } else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
} }
// 更新字符统计
updateCharCount();
}
/**
*
*/
public void onContentChange() {
updateCharCount();
} }
/** /**
@ -1382,6 +1553,45 @@ public class NoteEditActivity extends Activity implements OnClickListener,
showToast(resId, Toast.LENGTH_SHORT); showToast(resId, Toast.LENGTH_SHORT);
} }
/**
*
* @param text
* @return
*/
private int countValidCharacters(String text) {
if (TextUtils.isEmpty(text)) {
return 0;
}
// 去除所有空白字符后计算长度
return text.replaceAll("\\s", "").length();
}
/**
*
*/
private void updateCharCount() {
int charCount = 0;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 列表模式:遍历所有列表项
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())) {
charCount += countValidCharacters(edit.getText().toString());
}
}
} else {
// 普通文本模式:直接计算编辑框内容
if (!TextUtils.isEmpty(mNoteEditor.getText())) {
charCount = countValidCharacters(mNoteEditor.getText().toString());
}
}
// 更新显示
if (mTvCharCount != null) {
mTvCharCount.setText(charCount + " 字符");
}
}
/** /**
* *
* <p> * <p>

@ -37,6 +37,7 @@ import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener; import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.EditText; import android.widget.EditText;
import android.text.TextWatcher;
import net.micode.notes.R; import net.micode.notes.R;
@ -112,6 +113,11 @@ public class NoteEditText extends EditText {
* *
*/ */
void onTextChange(int index, boolean hasText); void onTextChange(int index, boolean hasText);
/**
*
*/
void onContentChange();
} }
/** /**
@ -151,6 +157,8 @@ public class NoteEditText extends EditText {
*/ */
public NoteEditText(Context context, AttributeSet attrs) { public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle); super(context, attrs, android.R.attr.editTextStyle);
mIndex = 0;
initTextWatcher();
} }
/** /**
@ -161,7 +169,30 @@ public class NoteEditText extends EditText {
*/ */
public NoteEditText(Context context, AttributeSet attrs, int defStyle) { public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
// TODO Auto-generated constructor stub mIndex = 0;
initTextWatcher();
}
/**
*
*/
private void initTextWatcher() {
addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(android.text.Editable s) {
if (mOnTextViewChangeListener != null) {
mOnTextViewChangeListener.onContentChange();
}
}
});
} }
/** /**
@ -207,7 +238,9 @@ public class NoteEditText extends EditText {
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_ENTER: case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) { // 只有在列表模式下mIndex > 0才阻止默认行为
// 普通文本模式下让系统默认处理回车键
if (mOnTextViewChangeListener != null && mIndex > 0) {
return false; return false;
} }
break; break;
@ -244,10 +277,14 @@ public class NoteEditText extends EditText {
break; break;
case KeyEvent.KEYCODE_ENTER: case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) { if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart(); // 只有在列表模式下mIndex > 0才执行特殊处理
String text = getText().subSequence(selectionStart, length()).toString(); // 普通文本模式下,让系统默认处理回车键
setText(getText().subSequence(0, selectionStart)); if (mIndex > 0) {
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
}
} else { } else {
Log.d(TAG, "OnTextViewChangeListener was not seted"); Log.d(TAG, "OnTextViewChangeListener was not seted");
} }

@ -57,6 +57,7 @@ public class NoteItemData {
NoteColumns.IS_LOCKED, NoteColumns.IS_LOCKED,
NoteColumns.LOCK_PASSWORD, NoteColumns.LOCK_PASSWORD,
NoteColumns.LOCK_TYPE, NoteColumns.LOCK_TYPE,
NoteColumns.LOCK_HINT,
NoteColumns.DELETED_DATE, NoteColumns.DELETED_DATE,
}; };
@ -140,10 +141,15 @@ public class NoteItemData {
*/ */
private static final int LOCK_TYPE_COLUMN = 15; private static final int LOCK_TYPE_COLUMN = 15;
/**
*
*/
private static final int LOCK_HINT_COLUMN = 16;
/** /**
* *
*/ */
private static final int DELETED_DATE_COLUMN = 16; private static final int DELETED_DATE_COLUMN = 17;
/** /**
* ID * ID
@ -170,6 +176,11 @@ public class NoteItemData {
*/ */
private int mLockType; private int mLockType;
/**
*
*/
private String mLockHint;
/** /**
* *
*/ */
@ -290,6 +301,7 @@ public class NoteItemData {
mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0) ? true : false; mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0) ? true : false;
mLockPassword = cursor.getString(LOCK_PASSWORD_COLUMN); mLockPassword = cursor.getString(LOCK_PASSWORD_COLUMN);
mLockType = cursor.getInt(LOCK_TYPE_COLUMN); mLockType = cursor.getInt(LOCK_TYPE_COLUMN);
mLockHint = cursor.getString(LOCK_HINT_COLUMN);
mDeletedDate = cursor.getLong(DELETED_DATE_COLUMN); mDeletedDate = cursor.getLong(DELETED_DATE_COLUMN);
mPhoneNumber = ""; mPhoneNumber = "";
@ -565,6 +577,22 @@ public class NoteItemData {
return mLockType; return mLockType;
} }
/**
*
* @return
*/
public String getLockHint() {
return mLockHint;
}
/**
*
* @param hint
*/
public void setLockHint(String hint) {
mLockHint = hint;
}
/** /**
* *
* @param type 01 * @param type 01

@ -57,8 +57,10 @@ import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.util.TypedValue;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
@ -124,19 +126,37 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* </p> * </p>
*/ */
private void showPasswordDialog() { private void showPasswordDialog() {
// 创建布局并添加输入框
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(60, 40, 60, 40);
// 创建布局参数,用于设置子视图间距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
final EditText passwordEditText = new EditText(this); final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password); passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(passwordEditText, params);
// 添加密码提示输入框
final EditText hintEditText = new EditText(this);
hintEditText.setHint(R.string.hint_enter_password_hint);
layout.addView(hintEditText);
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password) .setTitle(R.string.dialog_enter_password)
.setView(passwordEditText) .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString(); String password = passwordEditText.getText().toString();
String hint = hintEditText.getText().toString();
if (!TextUtils.isEmpty(password)) { if (!TextUtils.isEmpty(password)) {
toggleLockedStatus(password); toggleLockedStatus(password, hint);
} }
} }
}) })
@ -150,8 +170,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* 使 * 使
* </p> * </p>
* @param password * @param password
* @param hint
*/ */
private void toggleLockedStatus(final String password) { private void toggleLockedStatus(final String password, final String hint) {
final HashSet<Long> selectedIds = mNotesListAdapter.getSelectedItemIds(); final HashSet<Long> selectedIds = mNotesListAdapter.getSelectedItemIds();
final int selectedCount = mNotesListAdapter.getSelectedCount(); final int selectedCount = mNotesListAdapter.getSelectedCount();
@ -159,45 +180,61 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
protected Integer doInBackground(Void... unused) { protected Integer doInBackground(Void... unused) {
int finalLockedStatus = -1; // 默认为-1表示未处理 int finalLockedStatus = -1; // 默认为-1表示未处理
for (Long noteId : selectedIds) { for (Long noteId : selectedIds) {
// 查询当前便签的锁定状态 // 查询当前便签的锁定状态和密码
Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI, Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
new String[]{NoteColumns.IS_LOCKED}, new String[]{NoteColumns.IS_LOCKED, NoteColumns.LOCK_PASSWORD},
NoteColumns.ID + "=?", NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)}, new String[]{String.valueOf(noteId)},
null); null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
int currentLocked = cursor.getInt(0); int currentLocked = cursor.getInt(0);
// 切换锁定状态 String encryptedPassword = cursor.getString(1);
int newLocked = currentLocked == 1 ? 0 : 1; int newLocked = currentLocked;
finalLockedStatus = newLocked; // 保存最终锁定状态
if (currentLocked == 1) {
ContentValues values = new ContentValues(); // 如果当前是锁定状态,需要验证密码才能解锁
values.put(NoteColumns.IS_LOCKED, newLocked); if (encryptedPassword != null && encryptedPassword.equals(encryptPassword(password))) {
// 密码正确,解锁
if (newLocked == 1) { newLocked = 0;
// 如果是锁定操作,设置密码和锁定类型 finalLockedStatus = newLocked;
values.put(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
// 判断是笔记还是文件夹
Cursor typeCursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
new String[]{NoteColumns.TYPE},
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)},
null);
if (typeCursor != null && typeCursor.moveToFirst()) {
int type = typeCursor.getInt(0);
values.put(NoteColumns.LOCK_TYPE, type == Notes.TYPE_FOLDER ? Notes.LOCK_TYPE_FOLDER : Notes.LOCK_TYPE_NOTE);
typeCursor.close();
} }
} else { } else {
// 如果是解锁操作,清空密码 // 如果当前是未锁定状态,直接锁定
values.put(NoteColumns.LOCK_PASSWORD, ""); newLocked = 1;
finalLockedStatus = newLocked;
} }
mContentResolver.update(Notes.CONTENT_NOTE_URI, if (newLocked != currentLocked) {
values, ContentValues values = new ContentValues();
NoteColumns.ID + "=?", values.put(NoteColumns.IS_LOCKED, newLocked);
new String[]{String.valueOf(noteId)});
if (newLocked == 1) {
// 如果是锁定操作,设置密码、密码提示和锁定类型
values.put(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
values.put(NoteColumns.LOCK_HINT, hint);
// 判断是笔记还是文件夹
Cursor typeCursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
new String[]{NoteColumns.TYPE},
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)},
null);
if (typeCursor != null && typeCursor.moveToFirst()) {
int type = typeCursor.getInt(0);
values.put(NoteColumns.LOCK_TYPE, type == Notes.TYPE_FOLDER ? Notes.LOCK_TYPE_FOLDER : Notes.LOCK_TYPE_NOTE);
typeCursor.close();
}
} else {
// 如果是解锁操作,清空密码和密码提示
values.put(NoteColumns.LOCK_PASSWORD, "");
values.put(NoteColumns.LOCK_HINT, "");
}
mContentResolver.update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)});
}
cursor.close(); cursor.close();
} }
@ -207,11 +244,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override @Override
protected void onPostExecute(Integer finalLockedStatus) { protected void onPostExecute(Integer finalLockedStatus) {
String message = finalLockedStatus == 1 ? getString(R.string.message_note_locked) : getString(R.string.message_note_unlocked); if (finalLockedStatus == -1) {
Toast.makeText(NotesListActivity.this, message, Toast.LENGTH_SHORT).show(); // 密码错误,显示错误信息
// 重新查询数据,更新列表 Toast.makeText(NotesListActivity.this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
startAsyncNotesListQuery(); } else {
mModeCallBack.finishActionMode(); // 操作成功,显示结果
String message = finalLockedStatus == 1 ? getString(R.string.message_note_locked) : getString(R.string.message_note_unlocked);
Toast.makeText(NotesListActivity.this, message, Toast.LENGTH_SHORT).show();
// 重新查询数据,更新列表
startAsyncNotesListQuery();
mModeCallBack.finishActionMode();
}
} }
}.execute(); }.execute();
} }
@ -1193,13 +1236,35 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private void openNode(final NoteItemData data) { private void openNode(final NoteItemData data) {
// 检查笔记是否被锁定 // 检查笔记是否被锁定
if (data.isLocked()) { if (data.isLocked()) {
// 创建布局并添加密码输入框
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(60, 40, 60, 40);
// 创建布局参数,用于设置子视图间距
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
final EditText passwordEditText = new EditText(this); final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password); passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
layout.addView(passwordEditText, params);
// 获取密码提示并显示
String lockHint = data.getLockHint();
if (!TextUtils.isEmpty(lockHint)) {
TextView hintTextView = new TextView(this);
hintTextView.setText(getString(R.string.password_hint_prefix) + lockHint);
hintTextView.setTextColor(getResources().getColor(R.color.secondary_text_dark));
hintTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
layout.addView(hintTextView);
}
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password) .setTitle(R.string.dialog_enter_password)
.setView(passwordEditText) .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {

@ -99,20 +99,34 @@ public class NotesListItem extends LinearLayout {
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name) mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount())); + context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record); // 检查是否被锁定
if (data.isLocked()) {
mAlert.setImageResource(android.R.drawable.ic_lock_lock);
} else {
mAlert.setImageResource(R.drawable.call_record);
}
} else if (data.getId() == Notes.ID_TRASH_FOLER) { } else if (data.getId() == Notes.ID_TRASH_FOLER) {
mCallName.setVisibility(View.GONE); mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE); mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText("回收站" mTitle.setText("回收站"
+ context.getString(R.string.format_folder_files_count, data.getNotesCount())); + context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.delete); // 检查是否被锁定
if (data.isLocked()) {
mAlert.setImageResource(android.R.drawable.ic_lock_lock);
} else {
mAlert.setImageResource(R.drawable.delete);
}
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE); mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName()); mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet()))); mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet())));
if (data.hasAlert()) { // 检查是否被锁定
if (data.isLocked()) {
mAlert.setImageResource(android.R.drawable.ic_lock_lock);
mAlert.setVisibility(View.VISIBLE);
} else if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock); mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE); mAlert.setVisibility(View.VISIBLE);
} else { } else {
@ -126,10 +140,20 @@ public class NotesListItem extends LinearLayout {
mTitle.setText(data.getSnippet() mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count, + context.getString(R.string.format_folder_files_count,
data.getNotesCount())); data.getNotesCount()));
mAlert.setVisibility(View.GONE); // 检查文件夹是否被锁定
if (data.isLocked()) {
mAlert.setImageResource(android.R.drawable.ic_lock_lock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else { } else {
mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet()))); mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet())));
if (data.hasAlert()) { // 检查笔记是否被锁定
if (data.isLocked()) {
mAlert.setImageResource(android.R.drawable.ic_lock_lock);
mAlert.setVisibility(View.VISIBLE);
} else if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock); mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE); mAlert.setVisibility(View.VISIBLE);
} else { } else {

@ -181,6 +181,17 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<TextView
android:id="@+id/tv_char_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|bottom"
android:layout_marginLeft="16dp"
android:layout_marginBottom="8dp"
android:textSize="12sp"
android:textColor="@color/secondary_text_dark"
android:text="0 字符" />
<ImageView <ImageView
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="7dip" android:layout_height="7dip"

@ -57,6 +57,10 @@
<item <item
android:id="@+id/menu_unlock" android:id="@+id/menu_unlock"
android:title="@string/menu_unlock" /> android:title="@string/menu_unlock" />
<item
android:id="@+id/menu_change_password"
android:title="@string/menu_change_password" />
<item <item
android:id="@+id/menu_note_template" android:id="@+id/menu_note_template"

@ -52,11 +52,17 @@
<string name="menu_unpin">Unpin</string> <string name="menu_unpin">Unpin</string>
<string name="menu_lock">Lock</string> <string name="menu_lock">Lock</string>
<string name="menu_unlock">Unlock</string> <string name="menu_unlock">Unlock</string>
<string name="menu_change_password">Change Password</string>
<string name="dialog_enter_password">Enter Password</string> <string name="dialog_enter_password">Enter Password</string>
<string name="dialog_enter_old_password">Enter Old Password</string>
<string name="dialog_enter_new_password">Enter New Password</string>
<string name="hint_enter_password">Please enter password</string> <string name="hint_enter_password">Please enter password</string>
<string name="hint_enter_password_hint">Please enter password hint</string>
<string name="password_hint_prefix">Hint: </string>
<string name="error_wrong_password">Incorrect password</string> <string name="error_wrong_password">Incorrect password</string>
<string name="message_note_locked">Note locked successfully</string> <string name="message_note_locked">Note locked successfully</string>
<string name="message_note_unlocked">Note unlocked successfully</string> <string name="message_note_unlocked">Note unlocked successfully</string>
<string name="message_password_changed">Password changed successfully</string>
<string name="menu_select_title">%d selected</string> <string name="menu_select_title">%d selected</string>
<string name="menu_select_none">Nothing selected, the operation is invalid</string> <string name="menu_select_none">Nothing selected, the operation is invalid</string>
<string name="menu_select_all">Select all</string> <string name="menu_select_all">Select all</string>

Loading…
Cancel
Save