新增为便签加密功能 #13

Merged
p3f2h9ljk merged 1 commits from taojunyu_branch into master 1 day ago

@ -99,6 +99,16 @@ public class Notes {
* 4x4便
*/
public static final int TYPE_WIDGET_4X = 1;
/**
* 便
*/
public static final int LOCK_TYPE_NOTE = 0;
/**
*
*/
public static final int LOCK_TYPE_FOLDER = 1;
/**
* 便
@ -234,6 +244,26 @@ public class Notes {
* <P> 01 </P>
*/
public static final String PINNED = "pinned";
/**
*
* <P> : INTEGER </P>
* <P> 01 </P>
*/
public static final String IS_LOCKED = "is_locked";
/**
*
* <P> : TEXT </P>
*/
public static final String LOCK_PASSWORD = "lock_password";
/**
*
* <P> : INTEGER </P>
* <P> 0便1 </P>
*/
public static final String LOCK_TYPE = "lock_type";
}
public interface DataColumns {

@ -35,7 +35,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
// 数据库文件名
private static final String DB_NAME = "note.db";
// 数据库版本号,用于升级控制
private static final int DB_VERSION = 5;
private static final int DB_VERSION = 6;
/**
*
@ -75,7 +75,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.PINNED + " INTEGER NOT NULL DEFAULT 0" +
NoteColumns.PINNED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0" +
")";
/**
@ -385,6 +388,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++;
}
// 版本5升级到版本6添加锁相关字段
if (oldVersion == 5) {
upgradeToV6(db);
oldVersion++;
}
// 如果需要,重新创建触发器
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
@ -445,4 +454,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.PINNED
+ " INTEGER NOT NULL DEFAULT 0");
}
/**
* 6便
* @param db
*/
private void upgradeToV6(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.IS_LOCKED
+ " INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_PASSWORD
+ " TEXT NOT NULL DEFAULT ''");
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_TYPE
+ " INTEGER NOT NULL DEFAULT 0");
}
}

@ -504,6 +504,19 @@ public class WorkingNote {
}
}
/**
*
* <p>
* NotesetNoteValue
* </p>
*
* @param key NoteColumns
* @param value
*/
public void setNoteValue(String key, String value) {
mNote.setNoteValue(key, value);
}
/**
*
* <p>

@ -48,6 +48,10 @@ import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.database.Cursor;
import android.content.ContentResolver;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
@ -671,6 +675,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break;
case R.id.menu_lock:
showPasswordDialogForLock();
break;
case R.id.menu_unlock:
showPasswordDialogForUnlock();
break;
default:
break;
}
@ -709,6 +719,139 @@ public class NoteEditActivity extends Activity implements OnClickListener,
context.startActivity(intent);
}
/**
*
* <p>
*
* </p>
*/
private void showPasswordDialogForLock() {
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)) {
lockCurrentNote(password);
}
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
/**
*
* <p>
*
* </p>
*/
private void showPasswordDialogForUnlock() {
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)) {
unlockCurrentNote(password);
}
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
/**
*
* <p>
*
* </p>
* @param password
*/
private void lockCurrentNote(String password) {
// 设置锁定状态为1已锁定
mWorkingNote.setNoteValue(NoteColumns.IS_LOCKED, "1");
// 设置加密后的密码
mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
// 设置锁定类型为笔记类型
mWorkingNote.setNoteValue(NoteColumns.LOCK_TYPE, String.valueOf(Notes.LOCK_TYPE_NOTE));
// 保存笔记
saveNote();
// 显示提示信息
Toast.makeText(this, getString(R.string.message_note_locked), Toast.LENGTH_SHORT).show();
}
/**
*
* <p>
*
* </p>
* @param password
*/
private void unlockCurrentNote(String password) {
// 使用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(password))) {
// 密码正确设置锁定状态为0未锁定
mWorkingNote.setNoteValue(NoteColumns.IS_LOCKED, "0");
// 清空密码
mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, "");
// 保存笔记
saveNote();
// 显示提示信息
Toast.makeText(this, getString(R.string.message_note_unlocked), Toast.LENGTH_SHORT).show();
} else {
// 密码错误,显示错误信息
Toast.makeText(this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
}
}
/**
*
* <p>
* 使Base64ANDROID_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; // 加密失败时返回原始密码
}
}
/**
*
* <p>

@ -54,6 +54,9 @@ public class NoteItemData {
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.PINNED,
NoteColumns.IS_LOCKED,
NoteColumns.LOCK_PASSWORD,
NoteColumns.LOCK_TYPE,
};
/**
@ -120,6 +123,21 @@ public class NoteItemData {
*
*/
private static final int PINNED_COLUMN = 12;
/**
*
*/
private static final int IS_LOCKED_COLUMN = 13;
/**
*
*/
private static final int LOCK_PASSWORD_COLUMN = 14;
/**
*
*/
private static final int LOCK_TYPE_COLUMN = 15;
/**
* ID
@ -131,6 +149,21 @@ public class NoteItemData {
*/
private boolean mPinned;
/**
*
*/
private boolean mIsLocked;
/**
*
*/
private String mLockPassword;
/**
*
*/
private int mLockType;
/**
*
*/
@ -242,6 +275,9 @@ public class NoteItemData {
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPinned = (cursor.getInt(PINNED_COLUMN) > 0) ? true : false;
mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0) ? true : false;
mLockPassword = cursor.getString(LOCK_PASSWORD_COLUMN);
mLockType = cursor.getInt(LOCK_TYPE_COLUMN);
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
@ -475,6 +511,54 @@ public class NoteItemData {
public void setPinned(boolean pinned) {
mPinned = pinned;
}
/**
*
* @return truefalse
*/
public boolean isLocked() {
return mIsLocked;
}
/**
*
* @param locked truefalse
*/
public void setLocked(boolean locked) {
mIsLocked = locked;
}
/**
*
* @return
*/
public String getLockPassword() {
return mLockPassword;
}
/**
*
* @param password
*/
public void setLockPassword(String password) {
mLockPassword = password;
}
/**
*
* @return 01
*/
public int getLockType() {
return mLockType;
}
/**
*
* @param type 01
*/
public void setLockType(int type) {
mLockType = type;
}
/**
*

@ -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>
* 使Base64ANDROID_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>

@ -49,4 +49,12 @@
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
<item
android:id="@+id/menu_lock"
android:title="@string/menu_lock" />
<item
android:id="@+id/menu_unlock"
android:title="@string/menu_unlock" />
</menu>

@ -33,4 +33,9 @@
android:id="@+id/pin"
android:title="@string/menu_pin"
android:showAsAction="always|withText" />
<item
android:id="@+id/lock"
android:title="@string/menu_lock"
android:showAsAction="always|withText" />
</menu>

@ -50,6 +50,13 @@
<string name="menu_move">Move to folder</string>
<string name="menu_pin">Pin</string>
<string name="menu_unpin">Unpin</string>
<string name="menu_lock">Lock</string>
<string name="menu_unlock">Unlock</string>
<string name="dialog_enter_password">Enter Password</string>
<string name="hint_enter_password">Please enter password</string>
<string name="error_wrong_password">Incorrect password</string>
<string name="message_note_locked">Note locked successfully</string>
<string name="message_note_unlocked">Note unlocked successfully</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_all">Select all</string>

Loading…
Cancel
Save