|
|
|
|
@ -115,6 +115,127 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
*/
|
|
|
|
|
private enum ListEditState {
|
|
|
|
|
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示密码输入对话框
|
|
|
|
|
* <p>
|
|
|
|
|
* 该方法用于显示一个密码输入对话框,让用户输入密码来锁定选中的笔记或文件夹
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
|
|
|
|
private void showPasswordDialog() {
|
|
|
|
|
final EditText passwordEditText = new EditText(this);
|
|
|
|
|
passwordEditText.setHint(R.string.hint_enter_password);
|
|
|
|
|
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
|
|
|
|
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle(R.string.dialog_enter_password)
|
|
|
|
|
.setView(passwordEditText)
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String password = passwordEditText.getText().toString();
|
|
|
|
|
if (!TextUtils.isEmpty(password)) {
|
|
|
|
|
toggleLockedStatus(password);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 切换锁定状态
|
|
|
|
|
* <p>
|
|
|
|
|
* 该方法用于切换选中笔记或文件夹的锁定状态,使用异步任务处理数据库操作
|
|
|
|
|
* </p>
|
|
|
|
|
* @param password 用于锁定的密码
|
|
|
|
|
*/
|
|
|
|
|
private void toggleLockedStatus(final String password) {
|
|
|
|
|
final HashSet<Long> selectedIds = mNotesListAdapter.getSelectedItemIds();
|
|
|
|
|
final int selectedCount = mNotesListAdapter.getSelectedCount();
|
|
|
|
|
|
|
|
|
|
new AsyncTask<Void, Void, Integer>() {
|
|
|
|
|
protected Integer doInBackground(Void... unused) {
|
|
|
|
|
int finalLockedStatus = -1; // 默认为-1,表示未处理
|
|
|
|
|
for (Long noteId : selectedIds) {
|
|
|
|
|
// 查询当前便签的锁定状态
|
|
|
|
|
Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
|
|
|
|
|
new String[]{NoteColumns.IS_LOCKED},
|
|
|
|
|
NoteColumns.ID + "=?",
|
|
|
|
|
new String[]{String.valueOf(noteId)},
|
|
|
|
|
null);
|
|
|
|
|
|
|
|
|
|
if (cursor != null && cursor.moveToFirst()) {
|
|
|
|
|
int currentLocked = cursor.getInt(0);
|
|
|
|
|
// 切换锁定状态
|
|
|
|
|
int newLocked = currentLocked == 1 ? 0 : 1;
|
|
|
|
|
finalLockedStatus = newLocked; // 保存最终锁定状态
|
|
|
|
|
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(NoteColumns.IS_LOCKED, newLocked);
|
|
|
|
|
|
|
|
|
|
if (newLocked == 1) {
|
|
|
|
|
// 如果是锁定操作,设置密码和锁定类型
|
|
|
|
|
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 {
|
|
|
|
|
// 如果是解锁操作,清空密码
|
|
|
|
|
values.put(NoteColumns.LOCK_PASSWORD, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mContentResolver.update(Notes.CONTENT_NOTE_URI,
|
|
|
|
|
values,
|
|
|
|
|
NoteColumns.ID + "=?",
|
|
|
|
|
new String[]{String.valueOf(noteId)});
|
|
|
|
|
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return finalLockedStatus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(Integer finalLockedStatus) {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加密密码
|
|
|
|
|
* <p>
|
|
|
|
|
* 该方法使用Base64和ANDROID_ID对密码进行加密
|
|
|
|
|
* </p>
|
|
|
|
|
* @param password 原始密码
|
|
|
|
|
* @return 加密后的密码
|
|
|
|
|
*/
|
|
|
|
|
private String encryptPassword(String password) {
|
|
|
|
|
try {
|
|
|
|
|
// 获取设备的ANDROID_ID作为加密密钥
|
|
|
|
|
String androidId = android.provider.Settings.Secure.getString(getContentResolver(),
|
|
|
|
|
android.provider.Settings.Secure.ANDROID_ID);
|
|
|
|
|
// 使用简单的加密算法:将密码与ANDROID_ID拼接后进行Base64编码
|
|
|
|
|
String combined = password + androidId;
|
|
|
|
|
return android.util.Base64.encodeToString(combined.getBytes(), android.util.Base64.DEFAULT);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Password encryption failed", e);
|
|
|
|
|
return password; // 加密失败时返回原始密码
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 当前列表编辑状态 */
|
|
|
|
|
@ -201,6 +322,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
* Insert an introduction when user firstly use this application
|
|
|
|
|
*/
|
|
|
|
|
setAppInfoFromRawRes();
|
|
|
|
|
|
|
|
|
|
// 注册Android 13+的返回键回调
|
|
|
|
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
|
|
|
|
|
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
|
|
|
|
android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
|
|
|
|
new android.window.OnBackInvokedCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackInvoked() {
|
|
|
|
|
handleBackPress();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -366,6 +500,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
if (pinMenu != null) {
|
|
|
|
|
pinMenu.setOnMenuItemClickListener(this);
|
|
|
|
|
}
|
|
|
|
|
// 添加锁定菜单初始化
|
|
|
|
|
MenuItem lockMenu = menu.findItem(R.id.lock);
|
|
|
|
|
if (lockMenu != null) {
|
|
|
|
|
lockMenu.setOnMenuItemClickListener(this);
|
|
|
|
|
}
|
|
|
|
|
mActionMode = mode;
|
|
|
|
|
mNotesListAdapter.setChoiceMode(true);
|
|
|
|
|
mNotesListView.setLongClickable(false);
|
|
|
|
|
@ -528,6 +667,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
case R.id.pin:
|
|
|
|
|
togglePinnedStatus();
|
|
|
|
|
break;
|
|
|
|
|
case R.id.lock:
|
|
|
|
|
showPasswordDialog();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@ -862,11 +1004,72 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
* </p>
|
|
|
|
|
* @param data 要打开的笔记数据项
|
|
|
|
|
*/
|
|
|
|
|
private void openNode(NoteItemData data) {
|
|
|
|
|
Intent intent = new Intent(this, NoteEditActivity.class);
|
|
|
|
|
intent.setAction(Intent.ACTION_VIEW);
|
|
|
|
|
intent.putExtra(Intent.EXTRA_UID, data.getId());
|
|
|
|
|
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
|
|
|
|
|
private void openNode(final NoteItemData data) {
|
|
|
|
|
// 检查笔记是否被锁定
|
|
|
|
|
if (data.isLocked()) {
|
|
|
|
|
final EditText passwordEditText = new EditText(this);
|
|
|
|
|
passwordEditText.setHint(R.string.hint_enter_password);
|
|
|
|
|
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
|
|
|
|
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle(R.string.dialog_enter_password)
|
|
|
|
|
.setView(passwordEditText)
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String password = passwordEditText.getText().toString();
|
|
|
|
|
if (!TextUtils.isEmpty(password)) {
|
|
|
|
|
verifyPasswordAndOpenNote(data.getId(), password);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
.show();
|
|
|
|
|
} else {
|
|
|
|
|
// 未锁定,直接打开笔记
|
|
|
|
|
Intent intent = new Intent(this, NoteEditActivity.class);
|
|
|
|
|
intent.setAction(Intent.ACTION_VIEW);
|
|
|
|
|
intent.putExtra(Intent.EXTRA_UID, data.getId());
|
|
|
|
|
startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 验证密码并打开笔记
|
|
|
|
|
* <p>
|
|
|
|
|
* 该方法完成以下工作:
|
|
|
|
|
* 1. 查询笔记的加密密码
|
|
|
|
|
* 2. 验证密码是否正确
|
|
|
|
|
* 3. 如果正确,打开笔记;否则显示错误信息
|
|
|
|
|
* </p>
|
|
|
|
|
* @param noteId 笔记ID
|
|
|
|
|
* @param password 用户输入的密码
|
|
|
|
|
*/
|
|
|
|
|
private void verifyPasswordAndOpenNote(final long noteId, String password) {
|
|
|
|
|
// 查询笔记的加密密码
|
|
|
|
|
String[] projection = {NoteColumns.LOCK_PASSWORD};
|
|
|
|
|
String selection = NoteColumns.ID + "=?";
|
|
|
|
|
String[] selectionArgs = {String.valueOf(noteId)};
|
|
|
|
|
|
|
|
|
|
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(password))) {
|
|
|
|
|
// 密码正确,打开笔记
|
|
|
|
|
Intent intent = new Intent(this, NoteEditActivity.class);
|
|
|
|
|
intent.setAction(Intent.ACTION_VIEW);
|
|
|
|
|
intent.putExtra(Intent.EXTRA_UID, noteId);
|
|
|
|
|
startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
|
|
|
|
|
} else {
|
|
|
|
|
// 密码错误,显示错误信息
|
|
|
|
|
Toast.makeText(this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1052,8 +1255,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
* 3. 如果在根文件夹中,调用默认的返回处理
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
private void handleBackPress() {
|
|
|
|
|
switch (mState) {
|
|
|
|
|
case SUB_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
@ -1076,6 +1278,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
handleBackPress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 更新部件
|
|
|
|
|
* <p>
|
|
|
|
|
|