diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/Notes.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/Notes.java index ea474ab..479e78e 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/data/Notes.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/Notes.java @@ -62,6 +62,11 @@ public class Notes { */ public static final int TYPE_TASK = 3; + /** + * 模板笔记类型 + */ + public static final int TYPE_TEMPLATE = 4; + /** * 以下ID是系统文件夹的标识符 * {@link Notes#ID_ROOT_FOLDER } 是默认文件夹 diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index 92bfc6c..b130c5b 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -982,21 +982,21 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 工作模板 long workFolderId = insertFolder(db, Notes.ID_TEMPLATE_FOLDER, "工作"); if (workFolderId > 0) { - insertNote(db, workFolderId, "会议记录", "会议主题:\n时间:\n地点:\n参会人:\n\n会议内容:\n\n行动项:\n"); - insertNote(db, workFolderId, "周报", "本周工作总结:\n1. \n2. \n\n下周工作计划:\n1. \n2. \n\n需要协调的问题:\n"); + insertNote(db, workFolderId, "会议记录", "会议主题:\n时间:\n地点:\n参会人:\n\n会议内容:\n\n行动项:\n", Notes.TYPE_TEMPLATE); + insertNote(db, workFolderId, "周报", "本周工作总结:\n1. \n2. \n\n下周工作计划:\n1. \n2. \n\n需要协调的问题:\n", Notes.TYPE_TEMPLATE); } // 生活模板 long lifeFolderId = insertFolder(db, Notes.ID_TEMPLATE_FOLDER, "生活"); if (lifeFolderId > 0) { - insertNote(db, lifeFolderId, "日记", "日期:\n天气:\n心情:\n\n正文:\n"); - insertNote(db, lifeFolderId, "购物清单", "1. \n2. \n3. \n"); + insertNote(db, lifeFolderId, "日记", "日期:\n天气:\n心情:\n\n正文:\n", Notes.TYPE_TEMPLATE); + insertNote(db, lifeFolderId, "购物清单", "1. \n2. \n3. \n", Notes.TYPE_TEMPLATE); } // 学习模板 long studyFolderId = insertFolder(db, Notes.ID_TEMPLATE_FOLDER, "学习"); if (studyFolderId > 0) { - insertNote(db, studyFolderId, "读书笔记", "书名:\n作者:\n\n核心观点:\n\n精彩摘录:\n\n读后感:\n"); + insertNote(db, studyFolderId, "读书笔记", "书名:\n作者:\n\n核心观点:\n\n精彩摘录:\n\n读后感:\n", Notes.TYPE_TEMPLATE); } } @@ -1012,10 +1012,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { return db.insert(TABLE.NOTE, null, values); } - private void insertNote(SQLiteDatabase db, long parentId, String title, String content) { + private void insertNote(SQLiteDatabase db, long parentId, String title, String content, int type) { ContentValues values = new ContentValues(); values.put(NoteColumns.PARENT_ID, parentId); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.TYPE, type); values.put(NoteColumns.CREATED_DATE, System.currentTimeMillis()); values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); values.put(NoteColumns.SNIPPET, content); // SNIPPET acts as content preview or full content for simple notes diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesProvider.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesProvider.java index aa2cf34..084d09a 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesProvider.java @@ -168,7 +168,7 @@ public class NotesProvider extends ContentProvider { + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER - + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + " AND (" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " OR " + NoteColumns.TYPE + "=" + Notes.TYPE_TEMPLATE + ")"; /** * 创建Content Provider diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesRepository.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesRepository.java index c4282fc..169d437 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesRepository.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/NotesRepository.java @@ -88,6 +88,10 @@ public class NotesRepository { return parentId; } + public void setParentId(long parentId) { + this.parentId = parentId; + } + public String getNoteDataValue() { return snippet; } @@ -308,11 +312,17 @@ public class NotesRepository { selection = NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; selectionArgs = null; + } else if (folderId == Notes.ID_TEMPLATE_FOLDER) { + // Special case for template folder: show all templates regardless of category + selection = NoteColumns.TYPE + "=" + Notes.TYPE_TEMPLATE; + selectionArgs = null; } else if (folderId == Notes.ID_ROOT_FOLDER) { selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=?)"; selectionArgs = new String[]{String.valueOf(Notes.ID_ROOT_FOLDER)}; } else { - selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + // In a sub-folder, show both normal notes and templates if they exist there + selection = NoteColumns.PARENT_ID + "=? AND (" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + + " OR " + NoteColumns.TYPE + "=" + Notes.TYPE_TEMPLATE + ")"; selectionArgs = new String[]{String.valueOf(folderId)}; } @@ -605,8 +615,19 @@ public class NotesRepository { ContentValues values = new ContentValues(); long currentTime = System.currentTimeMillis(); + int type = Notes.TYPE_NOTE; + if (folderId == Notes.ID_TEMPLATE_FOLDER) { + type = Notes.TYPE_TEMPLATE; + } else if (folderId > 0) { + // Check if folder is under templates + NoteInfo folder = getFolderInfo(folderId); + if (folder != null && folder.parentId == Notes.ID_TEMPLATE_FOLDER) { + type = Notes.TYPE_TEMPLATE; + } + } + values.put(NoteColumns.PARENT_ID, folderId); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.TYPE, type); values.put(NoteColumns.CREATED_DATE, currentTime); values.put(NoteColumns.MODIFIED_DATE, currentTime); values.put(NoteColumns.LOCAL_MODIFIED, 1); @@ -1482,7 +1503,7 @@ public class NotesRepository { long currentTime = System.currentTimeMillis(); values.put(NoteColumns.PARENT_ID, categoryId); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.TYPE, Notes.TYPE_TEMPLATE); values.put(NoteColumns.CREATED_DATE, currentTime); values.put(NoteColumns.MODIFIED_DATE, currentTime); values.put(NoteColumns.LOCAL_MODIFIED, 1); diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/model/WorkingNote.java b/src/Notesmaster/app/src/main/java/net/micode/notes/model/WorkingNote.java index 63f7173..9a8976f 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/model/WorkingNote.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/model/WorkingNote.java @@ -115,6 +115,7 @@ public class WorkingNote { DataColumns.DATA2, DataColumns.DATA3, DataColumns.DATA4, + DataColumns.DATA5, }; /** 数据查询投影 - 笔记元数据 */ @@ -190,7 +191,36 @@ public class WorkingNote { mMode = 0; mWidgetType = Notes.TYPE_WIDGET_INVALIDE; mLocalModified = 1; // 新建笔记需要同步 - mType = Notes.TYPE_NOTE; // 默认为普通笔记类型 + + // Determine type based on folder + if (folderId == Notes.ID_TEMPLATE_FOLDER) { + mType = Notes.TYPE_TEMPLATE; + } else if (folderId > 0) { + // Check if parent is template folder + int parentType = net.micode.notes.tool.DataUtils.getNoteTypeById(context.getContentResolver(), folderId); + if (parentType == Notes.TYPE_FOLDER) { + // We need to check the folder's parent + long parentId = 0; + android.database.Cursor c = context.getContentResolver().query( + android.content.ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, folderId), + new String[] { NoteColumns.PARENT_ID }, null, null, null); + if (c != null) { + if (c.moveToFirst()) { + parentId = c.getLong(0); + } + c.close(); + } + if (parentId == Notes.ID_TEMPLATE_FOLDER) { + mType = Notes.TYPE_TEMPLATE; + } else { + mType = Notes.TYPE_NOTE; + } + } else { + mType = Notes.TYPE_NOTE; + } + } else { + mType = Notes.TYPE_NOTE; + } } /** @@ -287,6 +317,12 @@ public class WorkingNote { mContent = cursor.getString(DATA_CONTENT_COLUMN); mMode = cursor.getInt(DATA_MODE_COLUMN); mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + + // 加载壁纸路径 + int wallpaperIndex = cursor.getColumnIndex(DataColumns.DATA5); + if (wallpaperIndex != -1) { + mWallpaperPath = cursor.getString(wallpaperIndex); + } } else if (DataConstants.CALL_NOTE.equals(type)) { // 加载通话记录数据 mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); @@ -366,9 +402,9 @@ public class WorkingNote { public synchronized boolean saveNote() { if (isWorthSaving()) { if (!existInDatabase()) { - // 创建新笔记 - if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { - Log.e(TAG, "Create new note fail with id:" + mNoteId); + // 创建新笔记 + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId, mType)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); return false; } } @@ -501,16 +537,12 @@ public class WorkingNote { private String mWallpaperPath; public void setWallpaper(String path) { - mWallpaperPath = path; - // Ideally we should save this to DB, but for now we might use shared prefs or a separate table - // Or reuse bg_color_id with a special flag if we want to stick to existing schema strictly? - // Better: store in a new column or reuse a data column if possible. - // Given existing schema, let's use DataColumns.DATA5 if available? No DATA5. - // Let's use a SharedPreference for mapping noteId -> wallpaperPath for now to avoid schema migration complexity in this step. - // Or just use a special negative color ID range for wallpapers? - // Actually, let's use a separate storage for wallpapers map: note_id -> uri string - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onBackgroundColorChanged(); // Reuse this to trigger refresh + if (!TextUtils.equals(mWallpaperPath, path)) { + mWallpaperPath = path; + mNote.setTextData(DataColumns.DATA5, mWallpaperPath); + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); // Reuse this to trigger refresh + } } } diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/DataUtils.java b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/DataUtils.java index d237122..46fd4c6 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/DataUtils.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/DataUtils.java @@ -182,10 +182,22 @@ public class DataUtils { * @return 如果笔记可见返回 true,否则返回 false */ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + String selection; + String[] selectionArgs; + + if (type == Notes.TYPE_NOTE) { + // If checking for a regular note, also allow templates as they are essentially notes + selection = "(" + NoteColumns.TYPE + "=? OR " + NoteColumns.TYPE + "=?) AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; + selectionArgs = new String[] {String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.TYPE_TEMPLATE)}; + } else { + selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; + selectionArgs = new String [] {String.valueOf(type)}; + } + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String [] {String.valueOf(type)}, + selection, + selectionArgs, null); boolean exist = false; diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/ResourceParser.java b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/ResourceParser.java index 595d7a5..3743336 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/ResourceParser.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/ResourceParser.java @@ -51,6 +51,12 @@ public class ResourceParser { public static final int EYE_CARE_GREEN = 6; public static final int WARM = 7; public static final int COOL = 8; + + // Gradient Presets + public static final int SUNSET = 9; + public static final int OCEAN = 10; + public static final int FOREST = 11; + public static final int LAVENDER = 12; /** 自定义颜色按钮 ID (用于 UI 显示) */ public static final int CUSTOM_COLOR_BUTTON_ID = -100; @@ -108,7 +114,15 @@ public class ResourceParser { R.drawable.edit_blue, R.drawable.edit_white, R.drawable.edit_green, - R.drawable.edit_red + R.drawable.edit_red, + R.color.bg_midnight_black, + R.color.bg_eye_care_green, + R.color.bg_warm, + R.color.bg_cool, + R.drawable.preset_sunset, + R.drawable.preset_ocean, + R.drawable.preset_forest, + R.drawable.preset_lavender }; /** 标题栏背景资源数组 */ diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java index efdb3e1..b19c795 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -77,7 +77,10 @@ import java.util.regex.Pattern; import androidx.appcompat.app.AppCompatActivity; import com.google.android.material.appbar.MaterialToolbar; +import com.google.android.material.bottomsheet.BottomSheetDialog; +import net.micode.notes.databinding.DialogBackgroundSelectorBinding; +import net.micode.notes.databinding.DialogColorPickerBinding; import net.micode.notes.databinding.NoteEditBinding; import net.micode.notes.tool.RichTextHelper; @@ -162,7 +165,6 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen private UndoRedoManager mUndoRedoManager; private boolean mInUndoRedo = false; - private androidx.recyclerview.widget.RecyclerView mColorSelectorRv; private NoteColorAdapter mColorAdapter; /** @@ -331,7 +333,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen *
*/ private void initResources() { - mHeadViewPanel = binding.noteTitle; + mHeadViewPanel = binding.cvEditorSurface; mNoteHeaderHolder = new HeadViewHolder(); mNoteHeaderHolder.tvModified = binding.tvModifiedDate; mNoteHeaderHolder.ivAlertIcon = binding.ivAlertIcon; @@ -342,9 +344,8 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen mNoteHeaderHolder.etTitle = binding.etTitle; mNoteEditor = binding.noteEditView; - mNoteEditorPanel = binding.svNoteEdit; + mNoteEditorPanel = binding.cvEditorSurface; mNoteBgColorSelector = binding.noteBgColorSelector; - mColorSelectorRv = binding.rvBgColorSelector; mNoteEditor.addTextChangedListener(new TextWatcher() { private CharSequence mBeforeText; @@ -406,6 +407,10 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen ResourceParser.EYE_CARE_GREEN, ResourceParser.WARM, ResourceParser.COOL, + ResourceParser.SUNSET, + ResourceParser.OCEAN, + ResourceParser.FOREST, + ResourceParser.LAVENDER, ResourceParser.CUSTOM_COLOR_BUTTON_ID, ResourceParser.WALLPAPER_BUTTON_ID ); @@ -418,11 +423,11 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen pickWallpaper(); } else { mWorkingNote.setBgColorId(colorId); + mWorkingNote.setWallpaper(null); mNoteBgColorSelector.setVisibility(View.GONE); } } }); - mColorSelectorRv.setAdapter(mColorAdapter); mFontSizeSelector = binding.fontSizeSelector; for (int id : sFontSizeBtnsMap.keySet()) { @@ -680,8 +685,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen public void onClick(View v) { int id = v.getId(); if (id == R.id.btn_set_bg_color) { - mNoteBgColorSelector.setVisibility(View.VISIBLE); - // Note: Adapter selection is already set in onBackgroundColorChanged or init + showBackgroundSelector(); } else if (sFontSizeBtnsMap.containsKey(id)) { View fontView = getFontSelectorView(sFontSelectorSelectionMap.get(mFontSizeId)); if (fontView != null) { @@ -764,27 +768,14 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen String wallpaperPath = mWorkingNote.getWallpaperPath(); if (wallpaperPath != null) { - // Load wallpaper + binding.ivNoteWallpaper.setVisibility(View.VISIBLE); + binding.viewBgMask.setVisibility(View.VISIBLE); + android.net.Uri uri = android.net.Uri.parse(wallpaperPath); try { java.io.InputStream inputStream = getContentResolver().openInputStream(uri); android.graphics.Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(inputStream); - android.graphics.drawable.BitmapDrawable drawable = new android.graphics.drawable.BitmapDrawable(getResources(), bitmap); - - // Tiling mode (can be configurable later) - drawable.setTileModeXY(android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT); - - // Add Blur Effect for Android 12+ - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - mNoteEditorPanel.setBackground(drawable); - mNoteEditorPanel.setRenderEffect(android.graphics.RenderEffect.createBlurEffect( - 20f, 20f, android.graphics.Shader.TileMode.CLAMP)); - } else { - mNoteEditorPanel.setBackground(drawable); - } - - // Header always uses original wallpaper (or maybe slightly darker?) - mHeadViewPanel.setBackground(drawable.getConstantState().newDrawable()); + binding.ivNoteWallpaper.setImageBitmap(bitmap); // Dynamic Coloring with Palette androidx.palette.graphics.Palette.from(bitmap).generate(palette -> { @@ -795,12 +786,14 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen } catch (Exception e) { Log.e(TAG, "Failed to load wallpaper", e); - // Fallback to color + binding.ivNoteWallpaper.setVisibility(View.GONE); + binding.viewBgMask.setVisibility(View.GONE); applyColorBackground(colorId); } } else { + binding.ivNoteWallpaper.setVisibility(View.GONE); + binding.viewBgMask.setVisibility(View.GONE); applyColorBackground(colorId); - // Reset toolbar colors to default/theme resetToolbarColors(); } updateTextColor(colorId); @@ -809,30 +802,47 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen private void applyPaletteColors(androidx.palette.graphics.Palette palette) { int primaryColor = palette.getDominantColor(getResources().getColor(R.color.primary_color)); int onPrimaryColor = getResources().getColor(R.color.on_primary_color); + int mutedColor = palette.getMutedColor(android.graphics.Color.WHITE); // Ensure contrast for onPrimaryColor if (androidx.core.graphics.ColorUtils.calculateContrast(onPrimaryColor, primaryColor) < 3.0) { - onPrimaryColor = android.graphics.Color.WHITE; + onPrimaryColor = isColorDark(primaryColor) ? android.graphics.Color.WHITE : android.graphics.Color.BLACK; } - binding.toolbar.setBackgroundColor(primaryColor); binding.toolbar.setTitleTextColor(onPrimaryColor); if (binding.toolbar.getNavigationIcon() != null) { binding.toolbar.getNavigationIcon().setTint(onPrimaryColor); } getWindow().setStatusBarColor(primaryColor); + + // Update Card Surface - semi-transparent glass effect + int surfaceColor = androidx.core.graphics.ColorUtils.setAlphaComponent(mutedColor, 230); // 90% opacity + binding.cvEditorSurface.setCardBackgroundColor(surfaceColor); + + // Update input text color based on surface color + int textColor = isColorDark(surfaceColor) ? android.graphics.Color.WHITE : android.graphics.Color.BLACK; + mNoteEditor.setTextColor(textColor); + if (mNoteHeaderHolder != null && mNoteHeaderHolder.etTitle != null) { + mNoteHeaderHolder.etTitle.setTextColor(textColor); + mNoteHeaderHolder.etTitle.setHintTextColor(androidx.core.graphics.ColorUtils.setAlphaComponent(textColor, 128)); + } + binding.tvCharCount.setTextColor(textColor); + binding.tvModifiedDate.setTextColor(textColor); } private void resetToolbarColors() { int primaryColor = getResources().getColor(R.color.primary_color); int onPrimaryColor = getResources().getColor(R.color.on_primary_color); - binding.toolbar.setBackgroundColor(primaryColor); + binding.toolbar.setBackgroundColor(android.graphics.Color.TRANSPARENT); binding.toolbar.setTitleTextColor(onPrimaryColor); if (binding.toolbar.getNavigationIcon() != null) { binding.toolbar.getNavigationIcon().setTint(onPrimaryColor); } getWindow().setStatusBarColor(primaryColor); + + // Reset Card Surface + binding.cvEditorSurface.setCardBackgroundColor(android.graphics.Color.parseColor("#CCFFFFFF")); } private void updateTextColor(int colorId) { @@ -843,20 +853,27 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen textColor = android.graphics.Color.WHITE; } else if (colorId < 0) { // Custom color: Calculate luminance - // colorId is the ARGB value for custom colors if (isColorDark(colorId)) { textColor = android.graphics.Color.WHITE; } } - // For wallpaper, we might want to check palette, but for now default to black or keep current - // If wallpaper is set, this method is called with the underlying colorId. - // We should probably rely on the underlying color or default to white/black. - - mNoteEditor.setTextColor(textColor); - // Also update title color if needed - if (mNoteHeaderHolder != null && mNoteHeaderHolder.etTitle != null) { - mNoteHeaderHolder.etTitle.setTextColor(textColor); + // If wallpaper is set, applyPaletteColors already handled text color. + if (mWorkingNote.getWallpaperPath() == null) { + mNoteEditor.setTextColor(textColor); + if (mNoteHeaderHolder != null && mNoteHeaderHolder.etTitle != null) { + mNoteHeaderHolder.etTitle.setTextColor(textColor); + mNoteHeaderHolder.etTitle.setHintTextColor(androidx.core.graphics.ColorUtils.setAlphaComponent(textColor, 128)); + } + binding.tvCharCount.setTextColor(textColor); + binding.tvModifiedDate.setTextColor(textColor); + + // Adjust card surface opacity for pure colors + if (colorId == ResourceParser.WHITE) { + binding.cvEditorSurface.setCardBackgroundColor(android.graphics.Color.WHITE); + } else { + binding.cvEditorSurface.setCardBackgroundColor(android.graphics.Color.parseColor("#CCFFFFFF")); + } } } @@ -868,27 +885,10 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen } private void applyColorBackground(int colorId) { - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - - if (colorId >= ResourceParser.MIDNIGHT_BLACK || colorId < 0) { - int color = ResourceParser.getNoteBgColor(this, colorId); - if (mNoteEditorPanel.getBackground() != null) { - mNoteEditorPanel.getBackground().setTint(color); - mNoteEditorPanel.getBackground().setTintMode(android.graphics.PorterDuff.Mode.MULTIPLY); - } - if (mHeadViewPanel.getBackground() != null) { - mHeadViewPanel.getBackground().setTint(color); - mHeadViewPanel.getBackground().setTintMode(android.graphics.PorterDuff.Mode.MULTIPLY); - } + if (colorId < 0) { + binding.noteEditRoot.setBackgroundColor(colorId); } else { - // Clear tint for legacy resources - if (mNoteEditorPanel.getBackground() != null) { - mNoteEditorPanel.getBackground().clearColorFilter(); - } - if (mHeadViewPanel.getBackground() != null) { - mHeadViewPanel.getBackground().clearColorFilter(); - } + binding.noteEditRoot.setBackgroundResource(ResourceParser.NoteBgResources.getNoteBgResource(colorId)); } } @@ -1535,13 +1535,50 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen SHORTCUT_ICON_TITLE_MAX_LEN) : content; } - private void showColorPickerDialog() { - final View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_color_picker, null); - final View colorPreview = dialogView.findViewById(R.id.view_color_preview); - android.widget.SeekBar sbRed = dialogView.findViewById(R.id.sb_red); - android.widget.SeekBar sbGreen = dialogView.findViewById(R.id.sb_green); - android.widget.SeekBar sbBlue = dialogView.findViewById(R.id.sb_blue); + private void showBackgroundSelector() { + BottomSheetDialog dialog = new BottomSheetDialog(this); + DialogBackgroundSelectorBinding dialogBinding = DialogBackgroundSelectorBinding.inflate(getLayoutInflater()); + + java.util.List