From cb104dc7f25f9da51438e0b1e395834eb73a18fc Mon Sep 17 00:00:00 2001 From: Surponess Date: Tue, 27 Jan 2026 09:26:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=B0=E4=BF=AE=E6=94=B9=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/net/micode/notes/data/Notes.java | 12 +- .../notes/data/NotesDatabaseHelper.java | 320 ++++++++-------- .../net/micode/notes/model/WorkingNote.java | 31 +- .../micode/notes/tool/BackgroundManager.java | 263 +++++++++++++ .../net/micode/notes/ui/NoteEditActivity.java | 362 +++++++++++------- .../micode/notes/ui/NotesListActivity.java | 2 +- 6 files changed, 692 insertions(+), 298 deletions(-) create mode 100644 src/Notes-master/src/net/micode/notes/tool/BackgroundManager.java diff --git a/src/Notes-master/src/net/micode/notes/data/Notes.java b/src/Notes-master/src/net/micode/notes/data/Notes.java index 52cd670..afb0840 100644 --- a/src/Notes-master/src/net/micode/notes/data/Notes.java +++ b/src/Notes-master/src/net/micode/notes/data/Notes.java @@ -90,7 +90,7 @@ public class Notes { // 查询数据表的URI public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - /** + /** * Uri to query trash notes */ public static final Uri CONTENT_TRASH_URI = Uri.parse("content://" + AUTHORITY + "/trash"); @@ -153,13 +153,19 @@ public class Notes { // 版本号 public static final String VERSION = "version"; - /** + /** * Whether the note is encrypted *

Type : INTEGER (0 = not encrypted, 1 = encrypted)

*/ public static final String IS_ENCRYPTED = "is_encrypted"; + + /** + * Background image URI for the note + *

Type : TEXT

+ */ + public static final String BACKGROUND_IMAGE_URI = "background_image_uri"; } - public interface TrashColumns extends NoteColumns { + public interface TrashColumns extends NoteColumns { /** * Deleted date for trash note *

Type: INTEGER (long)

diff --git a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index 94dc63d..35c17b2 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -30,7 +30,7 @@ import net.micode.notes.data.Notes.NoteColumns; public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME = "note.db"; - private static final int DB_VERSION = 8; + private static final int DB_VERSION = 9; public interface TABLE { public static final String NOTE = "note"; @@ -38,9 +38,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { public static final String DATA = "data"; public static final String TRASH = "trash_note"; - + public static final String TRASH_DATA = "trash_data"; - + public static final String ENCRYPTED_NOTE_PASSWORD = "encrypted_note_password"; } @@ -49,214 +49,216 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static NotesDatabaseHelper mInstance; private static final String CREATE_NOTE_TABLE_SQL = - "CREATE TABLE " + TABLE.NOTE + "(" + - NoteColumns.ID + " INTEGER PRIMARY KEY," + - NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + - NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0" + - ")"; + "CREATE TABLE " + TABLE.NOTE + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BACKGROUND_IMAGE_URI + " TEXT NOT NULL DEFAULT ''" + + ")"; private static final String CREATE_DATA_TABLE_SQL = - "CREATE TABLE " + TABLE.DATA + "(" + - DataColumns.ID + " INTEGER PRIMARY KEY," + - DataColumns.MIME_TYPE + " TEXT NOT NULL," + - DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA1 + " INTEGER," + - DataColumns.DATA2 + " INTEGER," + - DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + - ")"; + "CREATE TABLE " + TABLE.DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA1 + " INTEGER," + + DataColumns.DATA2 + " INTEGER," + + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + + ")"; private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = - "CREATE INDEX IF NOT EXISTS note_id_index ON " + - TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; + "CREATE INDEX IF NOT EXISTS note_id_index ON " + + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; private static final String CREATE_TRASH_TABLE_SQL = - "CREATE TABLE " + TABLE.TRASH + "(" + - NoteColumns.ID + " INTEGER PRIMARY KEY," + - NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + - NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0," + - "deleted_date INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)" + - ")"; + "CREATE TABLE " + TABLE.TRASH + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BACKGROUND_IMAGE_URI + " TEXT NOT NULL DEFAULT ''," + + "deleted_date INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)" + + ")"; private static final String CREATE_TRASH_DATA_TABLE_SQL = - "CREATE TABLE " + TABLE.TRASH_DATA + "(" + - DataColumns.ID + " INTEGER PRIMARY KEY," + - DataColumns.MIME_TYPE + " TEXT NOT NULL," + - DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA1 + " INTEGER," + - DataColumns.DATA2 + " INTEGER," + - DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + - ")"; + "CREATE TABLE " + TABLE.TRASH_DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA1 + " INTEGER," + + DataColumns.DATA2 + " INTEGER," + + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + + ")"; private static final String CREATE_ENCRYPTED_NOTE_PASSWORD_TABLE_SQL = - "CREATE TABLE " + TABLE.ENCRYPTED_NOTE_PASSWORD + "(" + - NoteColumns.ID + " INTEGER PRIMARY KEY," + - "password TEXT NOT NULL," + - "FOREIGN KEY(" + NoteColumns.ID + ") REFERENCES " + TABLE.NOTE + "(" + NoteColumns.ID + ") ON DELETE CASCADE" + - ")"; + "CREATE TABLE " + TABLE.ENCRYPTED_NOTE_PASSWORD + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + "password TEXT NOT NULL," + + "FOREIGN KEY(" + NoteColumns.ID + ") REFERENCES " + TABLE.NOTE + "(" + NoteColumns.ID + ") ON DELETE CASCADE" + + ")"; /** * Increase folder's note count when move note to the folder */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER increase_folder_count_on_update "+ - " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + - " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + - " END"; + "CREATE TRIGGER increase_folder_count_on_update "+ + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; /** * Decrease folder's note count when move note from folder */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER decrease_folder_count_on_update " + - " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + - " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + - " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + - " END"; + "CREATE TRIGGER decrease_folder_count_on_update " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + + " END"; /** * Increase folder's note count when insert new note to the folder */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = - "CREATE TRIGGER increase_folder_count_on_insert " + - " AFTER INSERT ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + - " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + - " END"; + "CREATE TRIGGER increase_folder_count_on_insert " + + " AFTER INSERT ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; /** * Decrease folder's note count when delete note from the folder */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = - "CREATE TRIGGER decrease_folder_count_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + - " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + - " AND " + NoteColumns.NOTES_COUNT + ">0;" + - " END"; + "CREATE TRIGGER decrease_folder_count_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0;" + + " END"; /** * Update note's content when insert data with type {@link DataConstants#NOTE} */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = - "CREATE TRIGGER update_note_content_on_insert " + - " AFTER INSERT ON " + TABLE.DATA + - " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + - " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + - " END"; + "CREATE TRIGGER update_note_content_on_insert " + + " AFTER INSERT ON " + TABLE.DATA + + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END"; /** * Update note's content when data with {@link DataConstants#NOTE} type has changed */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER update_note_content_on_update " + - " AFTER UPDATE ON " + TABLE.DATA + - " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + - " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + - " END"; + "CREATE TRIGGER update_note_content_on_update " + + " AFTER UPDATE ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " END"; /** * Update note's content when data with {@link DataConstants#NOTE} type has deleted */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = - "CREATE TRIGGER update_note_content_on_delete " + - " AFTER delete ON " + TABLE.DATA + - " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=''" + - " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + - " END"; + "CREATE TRIGGER update_note_content_on_delete " + + " AFTER delete ON " + TABLE.DATA + + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.SNIPPET + "=''" + + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + + " END"; /** * Delete datas belong to note which has been deleted */ private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = - "CREATE TRIGGER delete_data_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN" + - " DELETE FROM " + TABLE.DATA + - " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + - " END"; + "CREATE TRIGGER delete_data_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.DATA + + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + + " END"; /** * Delete notes belong to folder which has been deleted */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = - "CREATE TRIGGER folder_delete_notes_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN" + - " DELETE FROM " + TABLE.NOTE + - " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + - " END"; + "CREATE TRIGGER folder_delete_notes_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END"; /** * Move notes belong to folder which has been moved to trash folder */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = - "CREATE TRIGGER folder_move_notes_on_trash " + - " AFTER UPDATE ON " + TABLE.NOTE + - " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + - " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + - " END"; + "CREATE TRIGGER folder_move_notes_on_trash " + + " AFTER UPDATE ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END"; public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); @@ -409,6 +411,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { oldVersion++; } + if (oldVersion == 8) { + upgradeToV9(db); + oldVersion++; + } + if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); @@ -474,13 +481,22 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { createTrashDataTable(db); Log.d(TAG, "Upgraded to version 7: created trash_data table"); } - + private void upgradeToV8(SQLiteDatabase db) { // 升级到版本8,添加标签支持 // 标签数据将存储在现有的data表中,使用新的MIME类型 Log.d(TAG, "Upgraded to version 8: added tag support"); } + private void upgradeToV9(SQLiteDatabase db) { + // 升级到版本9,添加背景图片URI字段 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.BACKGROUND_IMAGE_URI + + " TEXT NOT NULL DEFAULT ''"); + db.execSQL("ALTER TABLE " + TABLE.TRASH + " ADD COLUMN " + NoteColumns.BACKGROUND_IMAGE_URI + + " TEXT NOT NULL DEFAULT ''"); + Log.d(TAG, "Upgraded to version 9: added background_image_uri column"); + } + public void createEncryptedNotePasswordTable(SQLiteDatabase db) { db.execSQL(CREATE_ENCRYPTED_NOTE_PASSWORD_TABLE_SQL); Log.d(TAG, "encrypted_note_password table has been created"); diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java index 471a0a7..7490046 100644 --- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java +++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java @@ -58,6 +58,7 @@ public class WorkingNote { private NoteSettingChangedListener mNoteSettingStatusListener; // 笔记设置变化监听器 private String mPassword; // 密码(仅在创建时使用) private int mNoteType; // 笔记类型(普通笔记/清单) + private String mCustomBackgroundPath; // 自定义背景图片路径 @@ -81,7 +82,8 @@ public class WorkingNote { NoteColumns.BG_COLOR_ID, // 背景颜色ID NoteColumns.WIDGET_ID, // 小部件ID NoteColumns.WIDGET_TYPE, // 小部件类型 - NoteColumns.MODIFIED_DATE // 修改日期 + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.BACKGROUND_IMAGE_URI // 背景图片URI }; // 数据查询结果列索引 @@ -97,6 +99,7 @@ public class WorkingNote { private static final int NOTE_WIDGET_ID_COLUMN = 3; // 小部件ID列索引 private static final int NOTE_WIDGET_TYPE_COLUMN = 4; // 小部件类型列索引 private static final int NOTE_MODIFIED_DATE_COLUMN = 5; // 修改日期列索引 + private static final int NOTE_BACKGROUND_IMAGE_URI_COLUMN = 6; // 背景图片URI列索引 @@ -151,6 +154,9 @@ public class WorkingNote { mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + if (!cursor.isNull(NOTE_BACKGROUND_IMAGE_URI_COLUMN)) { + mCustomBackgroundPath = cursor.getString(NOTE_BACKGROUND_IMAGE_URI_COLUMN); + } } cursor.close(); } else { @@ -533,7 +539,26 @@ public class WorkingNote { } return encrypted; } - + + /** + * 设置自定义背景图片路径 + * @param path 背景图片路径 + */ + public void setCustomBackgroundPath(String path) { + if (!TextUtils.equals(mCustomBackgroundPath, path)) { + mCustomBackgroundPath = path; + mNote.setNoteValue(NoteColumns.BACKGROUND_IMAGE_URI, path); + } + } + + /** + * 获取自定义背景图片路径 + * @return 背景图片路径 + */ + public String getCustomBackgroundPath() { + return mCustomBackgroundPath; + } + /** * 添加标签 * @param tagName 标签名称 @@ -541,7 +566,7 @@ public class WorkingNote { public void addTag(String tagName) { mNote.addTagData(tagName); } - + /** * 清空所有标签 */ diff --git a/src/Notes-master/src/net/micode/notes/tool/BackgroundManager.java b/src/Notes-master/src/net/micode/notes/tool/BackgroundManager.java new file mode 100644 index 0000000..86aeb5e --- /dev/null +++ b/src/Notes-master/src/net/micode/notes/tool/BackgroundManager.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * 背景管理器类 + * 负责背景图片的选择、存储和应用 + */ +public class BackgroundManager { + private static final String TAG = "BackgroundManager"; + private static final String BACKGROUND_DIR = "note_backgrounds"; + private static final String IMAGE_DIR = "note_images"; + private Context mContext; + + /** + * 构造方法 + * @param context 上下文对象 + */ + public BackgroundManager(Context context) { + mContext = context; + // 初始化目录 + initDirectories(); + } + + /** + * 初始化目录 + */ + private void initDirectories() { + // 创建背景图片目录 + File backgroundDir = getBackgroundDir(); + if (!backgroundDir.exists()) { + if (!backgroundDir.mkdirs()) { + Log.e(TAG, "Failed to create background directory"); + } + } + + // 创建插入图片目录 + File imageDir = getImageDir(); + if (!imageDir.exists()) { + if (!imageDir.mkdirs()) { + Log.e(TAG, "Failed to create image directory"); + } + } + } + + /** + * 获取背景图片目录 + * @return 背景图片目录 + */ + public File getBackgroundDir() { + return new File(mContext.getFilesDir(), BACKGROUND_DIR); + } + + /** + * 获取插入图片目录 + * @return 插入图片目录 + */ + public File getImageDir() { + return new File(mContext.getFilesDir(), IMAGE_DIR); + } + + /** + * 保存背景图片并返回路径 + * @param sourceUri 源图片URI + * @return 保存后的图片路径 + */ + public String saveBackgroundImage(Uri sourceUri) { + try { + // 创建目标文件 + String fileName = generateUniqueFileName(); + File destFile = new File(getBackgroundDir(), fileName); + + // 复制文件 + copyFile(sourceUri, destFile); + + // 返回文件路径 + return destFile.getAbsolutePath(); + } catch (IOException e) { + Log.e(TAG, "Error saving background image: " + e.getMessage()); + return null; + } + } + + /** + * 保存插入的图片并返回路径 + * @param sourceUri 源图片URI + * @return 保存后的图片路径 + */ + public String saveInsertedImage(Uri sourceUri) { + try { + // 创建目标文件 + String fileName = generateUniqueFileName(); + File destFile = new File(getImageDir(), fileName); + + // 复制文件 + copyFile(sourceUri, destFile); + + // 返回文件路径 + return destFile.getAbsolutePath(); + } catch (IOException e) { + Log.e(TAG, "Error saving inserted image: " + e.getMessage()); + return null; + } + } + + /** + * 复制文件 + * @param sourceUri 源文件URI + * @param destFile 目标文件 + * @throws IOException IO异常 + */ + private void copyFile(Uri sourceUri, File destFile) throws IOException { + InputStream is = null; + OutputStream os = null; + try { + is = mContext.getContentResolver().openInputStream(sourceUri); + os = new FileOutputStream(destFile); + byte[] buffer = new byte[4096]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } finally { + if (is != null) { + is.close(); + } + if (os != null) { + os.close(); + } + } + } + + /** + * 生成唯一文件名 + * @return 唯一文件名 + */ + private String generateUniqueFileName() { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()); + return "IMG_" + timeStamp + ".jpg"; + } + + /** + * 根据路径加载背景图片 + * @param path 图片路径 + * @return 背景图片的Drawable对象 + */ + public Drawable loadBackground(String path) { + if (path == null || path.isEmpty()) { + return null; + } + + try { + File file = new File(path); + if (!file.exists()) { + Log.e(TAG, "Background image file not found: " + path); + return null; + } + + Bitmap bitmap = BitmapFactory.decodeFile(path); + if (bitmap == null) { + Log.e(TAG, "Failed to decode background image: " + path); + return null; + } + + return new BitmapDrawable(mContext.getResources(), bitmap); + } catch (Exception e) { + Log.e(TAG, "Error loading background: " + e.getMessage()); + return null; + } + } + + /** + * 清理背景图片文件 + * @param path 图片路径 + * @return 是否清理成功 + */ + public boolean cleanupBackgroundImage(String path) { + if (path == null || path.isEmpty()) { + return false; + } + + File file = new File(path); + if (file.exists()) { + if (file.delete()) { + Log.d(TAG, "Background image deleted: " + path); + return true; + } else { + Log.e(TAG, "Failed to delete background image: " + path); + return false; + } + } + return false; + } + + /** + * 清理插入的图片文件 + * @param path 图片路径 + * @return 是否清理成功 + */ + public boolean cleanupInsertedImage(String path) { + if (path == null || path.isEmpty()) { + return false; + } + + File file = new File(path); + if (file.exists()) { + if (file.delete()) { + Log.d(TAG, "Inserted image deleted: " + path); + return true; + } else { + Log.e(TAG, "Failed to delete inserted image: " + path); + return false; + } + } + return false; + } + + /** + * 检查图片文件是否存在 + * @param path 图片路径 + * @return 是否存在 + */ + public boolean isImageExists(String path) { + if (path == null || path.isEmpty()) { + return false; + } + File file = new File(path); + return file.exists() && file.isFile(); + } +} diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java index 4d0e7b6..d342260 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java @@ -105,6 +105,7 @@ 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.tool.BackgroundManager; import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; import net.micode.notes.widget.NoteWidgetProvider_2x; @@ -140,6 +141,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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); + sBgSelectorBtnsMap.put(R.id.iv_bg_custom, -1); // 自定义背景 } /** @@ -153,6 +155,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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); + sBgSelectorSelectionMap.put(-1, R.id.iv_bg_custom_select); // 自定义背景 } /** @@ -180,8 +183,9 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private static final String TAG = "NoteEditActivity"; // 日志标签 - private static final int REQUEST_CODE_PICK_IMAGE = 100; // 图片选择请求码 OMO - private static final int PHOTO_REQUEST = 100; // 请求照片 + private static final int REQUEST_CODE_PICK_IMAGE = 100; // 插入图片请求码 + private static final int REQUEST_CODE_PICK_BACKGROUND = 101; // 背景图片请求码 + private static final int PHOTO_REQUEST = REQUEST_CODE_PICK_IMAGE; // 保持兼容性 private HeadViewHolder mNoteHeaderHolder; // 头部视图持有者 private View mHeadViewPanel; // 头部视图面板 @@ -193,7 +197,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, private WorkingNote mWorkingNote; // 工作笔记对象 private SharedPreferences mSharedPrefs; // 共享偏好设置 private int mFontSizeId; // 字体大小ID - private ImageButton mBtnInsertImage; // 插入图片按钮 OMO + private ImageButton mBtnInsertImage; // 插入图片按钮 + private BackgroundManager mBackgroundManager; // 背景管理器 private String mText; // 用于存储富文本内容 private int mNoteLength; // 文本长度 @@ -207,7 +212,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, private String mUserQuery; // 用户查询字符串 private Pattern mPattern; // 正则表达式模式(用于高亮查询结果) private ChecklistManager mChecklistManager; // 清单管理器(OMO) - + // 标签管理相关控件 private LinearLayout mTagManagementSection; // 标签管理区域 private LinearLayout mExistingTagsContainer; // 现有标签容器 @@ -215,7 +220,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, private EditText mTagInput; // 标签输入框 private Button mAddTagButton; // 添加标签按钮 private ArrayList mTagsList; // 标签列表 - + // 搜索历史相关 private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键 private static final int MAX_SEARCH_HISTORY = 20; // 最大搜索历史记录数 @@ -296,7 +301,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return false; } } - + // 保存搜索历史 if (!TextUtils.isEmpty(mUserQuery)) { saveSearchHistory(mUserQuery); @@ -514,9 +519,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); } - // 设置头部视图和编辑器面板的背景 + // 加载自定义背景图片 + String backgroundPath = mWorkingNote.getCustomBackgroundPath(); + if (backgroundPath != null && !backgroundPath.isEmpty()) { + Drawable background = mBackgroundManager.loadBackground(backgroundPath); + if (background != null) { + mNoteEditorPanel.setBackground(background); + } else { + // 如果背景图片加载失败,使用默认背景颜色 + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + } + } else { + // 如果没有自定义背景,使用默认背景颜色 + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + } + // 设置头部视图的背景 mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); // 设置最后修改时间 mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, @@ -529,7 +547,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, * is not ready */ showAlertHeader(); // 显示提醒头部 - + // 加载现有标签 loadExistingTags(); } @@ -765,7 +783,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.e(TAG, "RichEditor is null! Check layout file."); return; } - + // 初始化浮动富文本工具栏 mFloatingToolbar = findViewById(R.id.floating_editor_toolbar); if (mFloatingToolbar != null) { @@ -838,6 +856,9 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 初始化富文本功能按钮 initRichEditorButtons(); + + // 初始化背景管理器 + mBackgroundManager = new BackgroundManager(this); } /** @@ -892,10 +913,18 @@ public class NoteEditActivity extends Activity implements OnClickListener, View.VISIBLE); } else if (sBgSelectorBtnsMap.containsKey(id)) { // 处理背景颜色选择 - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.GONE); - mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); - mNoteBgColorSelector.setVisibility(View.GONE); + Integer bgColorId = sBgSelectorBtnsMap.get(id); + if (bgColorId == -1) { + // 处理自定义背景图片选择 + mNoteBgColorSelector.setVisibility(View.GONE); + pickBackgroundImageFromGallery(); + } else { + // 处理普通背景颜色选择 + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + View.GONE); + mWorkingNote.setBgColorId(bgColorId); + mNoteBgColorSelector.setVisibility(View.GONE); + } } else if (sFontSizeBtnsMap.containsKey(id)) { // 处理字体大小选择 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); @@ -913,7 +942,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } mFontSizeSelector.setVisibility(View.GONE); } else if (id == R.id.btn_insert_image) { - // 处理插入图片按钮点击事件 OMO + // 处理插入图片按钮点击事件 pickImageFromGallery(); } } @@ -1048,7 +1077,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return true; } - + /** * 切换标签管理界面的显示/隐藏 */ @@ -1458,10 +1487,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, */ private boolean saveNote() { getWorkingText(); // 获取当前工作文本 - + // 保存标签数据 saveTagsToNote(); - + boolean saved = mWorkingNote.saveNote(); // 保存笔记到数据库 if (saved) { /** @@ -1475,20 +1504,20 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return saved; } - + /** * 将标签保存到笔记对象中 */ private void saveTagsToNote() { // 清空现有标签 mWorkingNote.clearTags(); - + // 添加所有标签 for (String tag : mTagsList) { mWorkingNote.addTag(tag); } } - + /** * 保存搜索历史记录 * @param query 搜索关键词 @@ -1497,13 +1526,13 @@ public class NoteEditActivity extends Activity implements OnClickListener, if (TextUtils.isEmpty(query)) { return; } - + // 获取SharedPreferences SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - + // 获取现有的搜索历史 String historyString = sp.getString(PREFERENCE_SEARCH_HISTORY, ""); - + // 将搜索历史拆分为列表 ArrayList historyList = new ArrayList<>(); if (!TextUtils.isEmpty(historyString)) { @@ -1514,18 +1543,18 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } } - + // 如果搜索关键词已存在,先移除 historyList.remove(query); - + // 将新的搜索关键词添加到列表开头 historyList.add(0, query); - + // 限制搜索历史记录数量 if (historyList.size() > MAX_SEARCH_HISTORY) { historyList = new ArrayList<>(historyList.subList(0, MAX_SEARCH_HISTORY)); } - + // 将搜索历史列表转换为字符串,用逗号分隔 StringBuilder sb = new StringBuilder(); for (int i = 0; i < historyList.size(); i++) { @@ -1534,7 +1563,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } sb.append(historyList.get(i)); } - + // 保存到SharedPreferences sp.edit().putString(PREFERENCE_SEARCH_HISTORY, sb.toString()).apply(); } @@ -1627,13 +1656,44 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 意图:打开系统相册选择图片 Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setType("image/*"); // 只显示图片类型 - startActivityForResult(intent, PHOTO_REQUEST); // 启动相册,等待返回结果 + startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE); // 启动相册,等待返回结果 } catch (ActivityNotFoundException e) { // 如果没有相册应用,尝试使用通用选择器 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); try { - startActivityForResult(intent, PHOTO_REQUEST); + startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE); + } catch (ActivityNotFoundException ex) { + showToast(R.string.error_permission_denied); + Log.e(TAG, "No image picker available", ex); + } + } + } + + /** + * 从相册选择背景图片 + */ + private void pickBackgroundImageFromGallery() { + // 检查运行时权限 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + // 请求权限 + requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_PERMISSION_STORAGE); + return; + } + } + + try { + // 意图:打开系统相册选择图片 + Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + intent.setType("image/*"); // 只显示图片类型 + startActivityForResult(intent, REQUEST_CODE_PICK_BACKGROUND); // 启动相册,等待返回结果 + } catch (ActivityNotFoundException e) { + // 如果没有相册应用,尝试使用通用选择器 + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + try { + startActivityForResult(intent, REQUEST_CODE_PICK_BACKGROUND); } catch (ActivityNotFoundException ex) { showToast(R.string.error_permission_denied); Log.e(TAG, "No image picker available", ex); @@ -1662,79 +1722,103 @@ public class NoteEditActivity extends Activity implements OnClickListener, @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == PHOTO_REQUEST && resultCode == RESULT_OK && data != null) { - Uri uri = data.getData(); - String imageBase64 = saveImageToBase64(uri); - if (imageBase64 == null) return; // 转换失败就退出 - - // 1. 拼接RichEditor支持的标签,使用Base64编码的图片数据 - String imgHtmlTag = "
"; - - // 2. 直接保存到笔记内容中(结合原有插入逻辑) - String originalContent = mWorkingNote.getContent() == null ? "" : mWorkingNote.getContent(); - Log.d(TAG, "Original note content: " + originalContent); - - // 3. 插入图片到RichEditor,兼容空内容情况 - String curHtml = mNoteEditor.getHtml(); - if (curHtml == null || curHtml.isEmpty()) { - // 对于空内容,直接使用图片标签 - mNoteEditor.setHtml(imgHtmlTag); - } else { - // 对于已有内容,追加图片标签 - String newHtml = curHtml + imgHtmlTag; - mNoteEditor.setHtml(newHtml); - } - - // 4. 同时更新工作笔记内容,确保新建笔记也能正确保存 - String updatedHtml = mNoteEditor.getHtml(); - mWorkingNote.setWorkingText(updatedHtml); - - // 弹窗依然保留 - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("图片选择成功!"); - - ImageView imageView = new ImageView(this); - imageView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); // 加布局参数,避免图片显示不全 - imageView.setImageURI(uri); // 弹窗显示原始图片 - imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); // 适配图片大小 - builder.setView(imageView); - - builder.setPositiveButton("确认", (dialog, which) -> { - // 保存HTML内容,包含Base64编码的图片 - String currentHtml = mNoteEditor.getHtml(); - Log.d("NoteDebug", "准备保存的HTML内容:" + currentHtml); - - // 执行保存操作 - mWorkingNote.setWorkingText(currentHtml); - boolean isSaved = mWorkingNote.saveNote(); - - // 根据保存结果提示(更友好) - if (isSaved) { - Toast.makeText(this, "图片信息已保存!", Toast.LENGTH_SHORT).show(); + if (resultCode == RESULT_OK && data != null) { + if (requestCode == PHOTO_REQUEST || requestCode == REQUEST_CODE_PICK_IMAGE) { + Uri uri = data.getData(); + String imageBase64 = saveImageToBase64(uri); + if (imageBase64 == null) return; // 转换失败就退出 + + // 1. 拼接RichEditor支持的标签,使用Base64编码的图片数据 + String imgHtmlTag = "
"; + + // 2. 直接保存到笔记内容中(结合原有插入逻辑) + String originalContent = mWorkingNote.getContent() == null ? "" : mWorkingNote.getContent(); + Log.d(TAG, "Original note content: " + originalContent); + + // 3. 插入图片到RichEditor,兼容空内容情况 + String curHtml = mNoteEditor.getHtml(); + if (curHtml == null || curHtml.isEmpty()) { + // 对于空内容,直接使用图片标签 + mNoteEditor.setHtml(imgHtmlTag); } else { - // 如果保存返回false,可能是因为内容未变化,而不是真正的失败 - Toast.makeText(this, "图片信息已保存!", Toast.LENGTH_SHORT).show(); - Log.d(TAG, "Note save returned false, but image was successfully inserted"); + // 对于已有内容,追加图片标签 + String newHtml = curHtml + imgHtmlTag; + mNoteEditor.setHtml(newHtml); } - }); - builder.setNegativeButton("取消", (dialog, which) -> { - // 如果用户取消,移除刚才插入的图片 - String currentHtml = mNoteEditor.getHtml(); - if (currentHtml != null && currentHtml.endsWith(imgHtmlTag)) { - String cancelNewHtml = currentHtml.substring(0, currentHtml.length() - imgHtmlTag.length()); - mNoteEditor.setHtml(cancelNewHtml); - // 同时更新工作笔记 - mWorkingNote.setWorkingText(cancelNewHtml); - } else if (currentHtml != null && currentHtml.equals(imgHtmlTag)) { - // 如果只有这一张图片,取消后清空内容 - mNoteEditor.setHtml(""); - mWorkingNote.setWorkingText(""); + // 4. 同时更新工作笔记内容,确保新建笔记也能正确保存 + String updatedHtml = mNoteEditor.getHtml(); + mWorkingNote.setWorkingText(updatedHtml); + + // 弹窗依然保留 + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("图片选择成功!"); + + ImageView imageView = new ImageView(this); + imageView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); // 加布局参数,避免图片显示不全 + imageView.setImageURI(uri); // 弹窗显示原始图片 + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); // 适配图片大小 + builder.setView(imageView); + + builder.setPositiveButton("确认", (dialog, which) -> { + // 保存HTML内容,包含Base64编码的图片 + String currentHtml = mNoteEditor.getHtml(); + Log.d("NoteDebug", "准备保存的HTML内容:" + currentHtml); + + // 执行保存操作 + mWorkingNote.setWorkingText(currentHtml); + boolean isSaved = mWorkingNote.saveNote(); + + // 根据保存结果提示(更友好) + if (isSaved) { + Toast.makeText(this, "图片信息已保存!", Toast.LENGTH_SHORT).show(); + } else { + // 如果保存返回false,可能是因为内容未变化,而不是真正的失败 + Toast.makeText(this, "图片信息已保存!", Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Note save returned false, but image was successfully inserted"); + } + }); + + builder.setNegativeButton("取消", (dialog, which) -> { + // 如果用户取消,移除刚才插入的图片 + String currentHtml = mNoteEditor.getHtml(); + if (currentHtml != null && currentHtml.endsWith(imgHtmlTag)) { + String cancelNewHtml = currentHtml.substring(0, currentHtml.length() - imgHtmlTag.length()); + mNoteEditor.setHtml(cancelNewHtml); + // 同时更新工作笔记 + mWorkingNote.setWorkingText(cancelNewHtml); + } else if (currentHtml != null && currentHtml.equals(imgHtmlTag)) { + // 如果只有这一张图片,取消后清空内容 + mNoteEditor.setHtml(""); + mWorkingNote.setWorkingText(""); + } + }); + builder.show(); + } + else if (requestCode == REQUEST_CODE_PICK_BACKGROUND) { + // 处理背景图片选择的情况 + Uri uri = data.getData(); + String backgroundPath = mBackgroundManager.saveBackgroundImage(uri); + if (backgroundPath != null) { + // 设置自定义背景路径 + mWorkingNote.setCustomBackgroundPath(backgroundPath); + // 应用背景图片 + Drawable background = mBackgroundManager.loadBackground(backgroundPath); + if (background != null) { + mNoteEditorPanel.setBackground(background); + mHeadViewPanel.setBackground(background); + } + // 隐藏背景选择器 + mNoteBgColorSelector.setVisibility(View.GONE); + // 显示保存成功提示 + Toast.makeText(this, "背景图片设置成功!", Toast.LENGTH_SHORT).show(); + } else { + // 显示保存失败提示 + Toast.makeText(this, "背景图片设置失败!", Toast.LENGTH_SHORT).show(); } - }); - builder.show(); + } } } @@ -1807,7 +1891,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteEditor.setEditorFontSize(18); // 默认值 } } - + /** * 初始化标签管理相关控件和事件监听器 */ @@ -1817,10 +1901,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, mExistingTagsContainer = findViewById(R.id.existing_tags_container); mTagInput = findViewById(R.id.et_tag_input); mAddTagButton = findViewById(R.id.btn_add_tag); - + // 初始化标签列表 mTagsList = new ArrayList<>(); - + // 设置添加标签按钮的点击事件 mAddTagButton.setOnClickListener(new OnClickListener() { @Override @@ -1828,30 +1912,30 @@ public class NoteEditActivity extends Activity implements OnClickListener, addTag(); } }); - + // 加载现有标签 loadExistingTags(); } - + /** * 添加标签 */ private void addTag() { String tagName = mTagInput.getText().toString().trim(); - + // 检查标签是否有效 if (isValidTag(tagName)) { // 添加标签到列表 mTagsList.add(tagName); - + // 更新UI显示 updateTagsDisplay(); - + // 清空输入框 mTagInput.setText(""); } } - + /** * 检查标签是否有效 * @param tagName 标签名称 @@ -1863,41 +1947,41 @@ public class NoteEditActivity extends Activity implements OnClickListener, Toast.makeText(this, "标签不能为空", Toast.LENGTH_SHORT).show(); return false; } - + // 检查标签长度 if (tagName.length() < 1 || tagName.length() > 15) { Toast.makeText(this, "标签长度必须在1-15个字符之间", Toast.LENGTH_SHORT).show(); return false; } - + // 检查标签数量是否超过限制 if (mTagsList.size() >= 5) { Toast.makeText(this, "每个笔记最多只能添加5个标签", Toast.LENGTH_SHORT).show(); return false; } - + // 检查标签是否已存在 if (mTagsList.contains(tagName)) { Toast.makeText(this, "该标签已存在", Toast.LENGTH_SHORT).show(); return false; } - + return true; } - + /** * 更新标签显示 */ private void updateTagsDisplay() { // 清空现有标签容器 mExistingTagsContainer.removeAllViews(); - + // 添加所有标签到容器 for (final String tag : mTagsList) { // 创建标签管理区域的标签视图 LinearLayout tagView = new LinearLayout(this); LinearLayout.LayoutParams tagViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT, 36); tagViewParams.setMargins(0, 0, 8, 0); tagView.setLayoutParams(tagViewParams); @@ -1907,7 +1991,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, tagView.setBackgroundResource(R.drawable.bg_color_btn_mask); tagView.setClickable(true); tagView.setFocusable(true); - + // 创建标签文本 TextView tagText = new TextView(this); tagText.setText(tag); @@ -1916,7 +2000,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, tagText.setPadding(0, 0, 8, 0); tagText.setSingleLine(true); tagText.setTextSize(14); - + // 创建删除按钮 ImageView deleteButton = new ImageView(this); LinearLayout.LayoutParams deleteParams = new LinearLayout.LayoutParams( @@ -1927,7 +2011,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, deleteButton.setClickable(true); deleteButton.setFocusable(true); deleteButton.setTag(tag); - + // 设置删除按钮点击事件 deleteButton.setOnClickListener(new OnClickListener() { @Override @@ -1936,16 +2020,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, removeTag(tagToRemove); } }); - + // 添加控件到标签视图 tagView.addView(tagText); tagView.addView(deleteButton); - + // 添加标签视图到容器 mExistingTagsContainer.addView(tagView); } } - + /** * 移除标签 * @param tagName 要移除的标签名称 @@ -1954,7 +2038,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mTagsList.remove(tagName); updateTagsDisplay(); } - + /** * 设置浮动富文本工具栏的拖拽功能 */ @@ -1963,14 +2047,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, final boolean[] isDragging = {false}; final int[] lastX = {0}; final int[] lastY = {0}; - + // 设置触摸监听器 mFloatingToolbar.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); - + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录初始触摸位置 @@ -1982,44 +2066,44 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 计算移动距离 int deltaX = x - lastX[0]; int deltaY = y - lastY[0]; - + // 判断是否开始拖拽 if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { isDragging[0] = true; } - + if (isDragging[0]) { // 更新工具栏位置 ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); - + // 计算新的位置 int newLeftMargin = params.leftMargin + deltaX; int newTopMargin = params.topMargin + deltaY; - + // 限制位置在屏幕范围内 DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; - + // 计算工具栏宽度和高度 int toolbarWidth = v.getWidth(); int toolbarHeight = v.getHeight(); - + // 限制左边界 newLeftMargin = Math.max(0, Math.min(newLeftMargin, screenWidth - toolbarWidth)); // 限制上边界 newTopMargin = Math.max(0, Math.min(newTopMargin, screenHeight - toolbarHeight)); - + // 更新布局参数 params.leftMargin = newLeftMargin; params.topMargin = newTopMargin; v.setLayoutParams(params); - + // 更新最后触摸位置 lastX[0] = x; lastY[0] = y; - + return true; // 消耗事件 } break; @@ -2031,12 +2115,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, } break; } - + return false; // 不消耗事件,允许其他触摸事件处理 } }); } - + /** * 加载现有标签 */ @@ -2049,7 +2133,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, Notes.DataColumns.NOTE_ID + "=? AND " + Notes.DataColumns.MIME_TYPE + "=?", new String[]{String.valueOf(mWorkingNote.getNoteId()), Notes.DataConstants.TAG}, null); - + if (cursor != null) { while (cursor.moveToNext()) { String tagName = cursor.getString(0); @@ -2059,12 +2143,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, } cursor.close(); } - + // 更新UI显示 updateTagsDisplay(); } } - + // 初始化富文本编辑器配置 private void initRichEditor() { mNoteEditor.setEditorHeight(600); // 设置编辑器高度 diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java index be2f63a..6d5cc26 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java @@ -373,7 +373,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); // 搜索输入框编辑器动作监听器(处理虚拟键盘上的搜索按钮) - mSearchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + mSearchEditText.setOnEditorActionListener(new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { // 检查是否是搜索动作 -- 2.34.1 From 7d9d6e893436a354de09db4e84e81427f0db7c54 Mon Sep 17 00:00:00 2001 From: Surponess Date: Tue, 27 Jan 2026 09:30:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java index d342260..1f821d8 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java @@ -2157,6 +2157,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteEditor.setPadding(10, 10, 10, 10); // 内边距 mNoteEditor.setPlaceholder("请输入笔记内容..."); // 占位提示 mNoteEditor.setInputEnabled(true); // 允许输入 + mNoteEditor.setEditorBackgroundColor(0x00000000); // 设置背景透明 } // 添加富文本功能按钮初始化方法 -- 2.34.1