From 574573cad8432ad831e070000bd5f20c7d46dd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E5=B0=94=E4=BF=8A?= Date: Sun, 1 Feb 2026 11:20:34 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=94=E8=AE=B0=E7=BC=96=E8=BE=91=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/micode/notes/data/Notes.java | 5 + .../notes/data/NotesDatabaseHelper.java | 14 +- .../net/micode/notes/data/NotesProvider.java | 2 +- .../micode/notes/data/NotesRepository.java | 27 +- .../net/micode/notes/model/WorkingNote.java | 60 +- .../java/net/micode/notes/tool/DataUtils.java | 16 +- .../net/micode/notes/tool/ResourceParser.java | 16 +- .../net/micode/notes/ui/NoteEditActivity.java | 215 +++--- .../net/micode/notes/ui/NoteItemData.java | 4 +- .../micode/notes/ui/NoteSearchActivity.java | 38 +- .../micode/notes/ui/NotesListFragment.java | 24 + .../net/micode/notes/ui/NotesListItem.java | 6 +- .../micode/notes/ui/NotesRecyclerAdapter.java | 2 +- .../notes/viewmodel/NotesListViewModel.java | 80 ++- .../src/main/res/drawable/bg_bottom_sheet.xml | 7 + .../src/main/res/drawable/preset_forest.xml | 5 + .../src/main/res/drawable/preset_lavender.xml | 5 + .../src/main/res/drawable/preset_ocean.xml | 5 + .../src/main/res/drawable/preset_sunset.xml | 5 + .../res/layout/dialog_background_selector.xml | 69 ++ .../app/src/main/res/layout/note_edit.xml | 658 ++++++------------ .../app/src/main/res/values/colors.xml | 10 + 22 files changed, 667 insertions(+), 606 deletions(-) create mode 100644 src/Notesmaster/app/src/main/res/drawable/bg_bottom_sheet.xml create mode 100644 src/Notesmaster/app/src/main/res/drawable/preset_forest.xml create mode 100644 src/Notesmaster/app/src/main/res/drawable/preset_lavender.xml create mode 100644 src/Notesmaster/app/src/main/res/drawable/preset_ocean.xml create mode 100644 src/Notesmaster/app/src/main/res/drawable/preset_sunset.xml create mode 100644 src/Notesmaster/app/src/main/res/layout/dialog_background_selector.xml 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 colors = java.util.Arrays.asList( + ResourceParser.YELLOW, + ResourceParser.BLUE, + ResourceParser.WHITE, + ResourceParser.GREEN, + ResourceParser.RED, + ResourceParser.MIDNIGHT_BLACK, + ResourceParser.EYE_CARE_GREEN, + ResourceParser.WARM, + ResourceParser.COOL, + ResourceParser.SUNSET, + ResourceParser.OCEAN, + ResourceParser.FOREST, + ResourceParser.LAVENDER + ); + + NoteColorAdapter adapter = new NoteColorAdapter(colors, mWorkingNote.getBgColorId(), colorId -> { + mWorkingNote.setBgColorId(colorId); + mWorkingNote.setWallpaper(null); // Clear wallpaper when color selected + dialog.dismiss(); + }); + dialogBinding.rvBackgroundOptions.setAdapter(adapter); + + dialogBinding.btnPickWallpaper.setOnClickListener(v -> { + pickWallpaper(); + dialog.dismiss(); + }); + + dialogBinding.btnCustomColor.setOnClickListener(v -> { + showColorPickerDialog(); + dialog.dismiss(); + }); + + dialog.setContentView(dialogBinding.getRoot()); + dialog.show(); + } + private void showColorPickerDialog() { + DialogColorPickerBinding dialogBinding = DialogColorPickerBinding.inflate(getLayoutInflater()); + int currentColor = android.graphics.Color.WHITE; if (mWorkingNote.getBgColorId() < 0) { currentColor = mWorkingNote.getBgColorId(); @@ -1553,10 +1590,10 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen android.graphics.Color.blue(currentColor) }; - colorPreview.setBackgroundColor(android.graphics.Color.rgb(rgb[0], rgb[1], rgb[2])); - sbRed.setProgress(rgb[0]); - sbGreen.setProgress(rgb[1]); - sbBlue.setProgress(rgb[2]); + dialogBinding.viewColorPreview.setBackgroundColor(android.graphics.Color.rgb(rgb[0], rgb[1], rgb[2])); + dialogBinding.sbRed.setProgress(rgb[0]); + dialogBinding.sbGreen.setProgress(rgb[1]); + dialogBinding.sbBlue.setProgress(rgb[2]); android.widget.SeekBar.OnSeekBarChangeListener listener = new android.widget.SeekBar.OnSeekBarChangeListener() { @Override @@ -1564,7 +1601,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen if (seekBar.getId() == R.id.sb_red) rgb[0] = progress; else if (seekBar.getId() == R.id.sb_green) rgb[1] = progress; else if (seekBar.getId() == R.id.sb_blue) rgb[2] = progress; - colorPreview.setBackgroundColor(android.graphics.Color.rgb(rgb[0], rgb[1], rgb[2])); + dialogBinding.viewColorPreview.setBackgroundColor(android.graphics.Color.rgb(rgb[0], rgb[1], rgb[2])); } @Override @@ -1574,21 +1611,18 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen public void onStopTrackingTouch(android.widget.SeekBar seekBar) {} }; - sbRed.setOnSeekBarChangeListener(listener); - sbGreen.setOnSeekBarChangeListener(listener); - sbBlue.setOnSeekBarChangeListener(listener); + dialogBinding.sbRed.setOnSeekBarChangeListener(listener); + dialogBinding.sbGreen.setOnSeekBarChangeListener(listener); + dialogBinding.sbBlue.setOnSeekBarChangeListener(listener); new AlertDialog.Builder(this) .setTitle("Custom Color") - .setView(dialogView) + .setView(dialogBinding.getRoot()) .setPositiveButton(android.R.string.ok, (dialog, which) -> { int newColor = android.graphics.Color.rgb(rgb[0], rgb[1], rgb[2]); - // Use negative integer for custom color. Ensure it's negative. - // ARGB color with alpha 255 is negative in Java int. - // If alpha is 0, it might be positive. We assume full opacity. newColor |= 0xFF000000; mWorkingNote.setBgColorId(newColor); - mNoteBgColorSelector.setVisibility(View.GONE); + mWorkingNote.setWallpaper(null); }) .setNegativeButton(android.R.string.cancel, null) .show(); @@ -1695,27 +1729,26 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen } private void initRichTextToolbar() { - mRichTextSelector = findViewById(R.id.rich_text_selector); - findViewById(R.id.btn_bold).setOnClickListener(new OnClickListener() { + mRichTextSelector = binding.richTextSelector; + binding.btnBold.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyBold(mNoteEditor); } }); - findViewById(R.id.btn_italic).setOnClickListener(new OnClickListener() { + binding.btnItalic.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyItalic(mNoteEditor); } }); - findViewById(R.id.btn_underline).setOnClickListener(new OnClickListener() { + binding.btnUnderline.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyUnderline(mNoteEditor); } }); - findViewById(R.id.btn_strikethrough).setOnClickListener(new OnClickListener() { + binding.btnStrikethrough.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyStrikethrough(mNoteEditor); } }); - findViewById(R.id.btn_header).setOnClickListener(new OnClickListener() { + binding.btnHeader.setOnClickListener(new OnClickListener() { public void onClick(View v) { final CharSequence[] items = {"H1 (Largest)", "H2", "H3", "H4", "H5", "H6 (Smallest)", "Normal"}; AlertDialog.Builder builder = new AlertDialog.Builder(NoteEditActivity.this); builder.setTitle("Header Level"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { - // item index maps to level: 0->1, 1->2, ..., 5->6, 6->0 (Normal) int level = (item == 6) ? 0 : (item + 1); RichTextHelper.applyHeading(mNoteEditor, level); } @@ -1723,22 +1756,22 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen builder.show(); } }); - findViewById(R.id.btn_list).setOnClickListener(new OnClickListener() { + binding.btnList.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyBullet(mNoteEditor); } }); - findViewById(R.id.btn_quote).setOnClickListener(new OnClickListener() { + binding.btnQuote.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyQuote(mNoteEditor); } }); - findViewById(R.id.btn_code).setOnClickListener(new OnClickListener() { + binding.btnCode.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.applyCode(mNoteEditor); } }); - findViewById(R.id.btn_link).setOnClickListener(new OnClickListener() { + binding.btnLink.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.insertLink(NoteEditActivity.this, mNoteEditor); } }); - findViewById(R.id.btn_divider).setOnClickListener(new OnClickListener() { + binding.btnDivider.setOnClickListener(new OnClickListener() { public void onClick(View v) { RichTextHelper.insertDivider(mNoteEditor); } }); - findViewById(R.id.btn_color_text).setOnClickListener(new OnClickListener() { + binding.btnColorText.setOnClickListener(new OnClickListener() { public void onClick(View v) { final CharSequence[] items = {"Black", "Red", "Blue"}; final int[] colors = {android.graphics.Color.BLACK, android.graphics.Color.RED, android.graphics.Color.BLUE}; @@ -1752,7 +1785,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen builder.show(); } }); - findViewById(R.id.btn_color_fill).setOnClickListener(new OnClickListener() { + binding.btnColorFill.setOnClickListener(new OnClickListener() { public void onClick(View v) { final CharSequence[] items = {"None", "Yellow", "Green", "Cyan"}; final int[] colors = {android.graphics.Color.TRANSPARENT, android.graphics.Color.YELLOW, android.graphics.Color.GREEN, android.graphics.Color.CYAN}; diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteItemData.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteItemData.java index 5e60636..5ca30f1 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteItemData.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteItemData.java @@ -196,8 +196,8 @@ public class NoteItemData { mIsMultiNotesFollowingFolder = false; mIsOneNoteFollowingFolder = false; - // 如果是普通笔记且不是第一项,检查前一项是否为文件夹 - if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { + // 如果是普通笔记或模板且不是第一项,检查前一项是否为文件夹 + if ((mType == Notes.TYPE_NOTE || mType == Notes.TYPE_TEMPLATE) && !mIsFirstItem) { int position = cursor.getPosition(); if (cursor.moveToPrevious()) { // 前一项是文件夹或系统文件夹 diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteSearchActivity.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteSearchActivity.java index 523120b..9fc2d88 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteSearchActivity.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteSearchActivity.java @@ -154,14 +154,36 @@ public class NoteSearchActivity extends AppCompatActivity implements SearchView. mHistoryManager.addHistory(query); } - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, note.getId()); - // Pass search keyword for highlighting in editor - // NoteEditActivity uses SearchManager.EXTRA_DATA_KEY for ID and USER_QUERY for keyword - intent.putExtra(android.app.SearchManager.EXTRA_DATA_KEY, String.valueOf(note.getId())); - intent.putExtra(android.app.SearchManager.USER_QUERY, mSearchView.getQuery().toString()); - startActivity(intent); + if (note.type == Notes.TYPE_TEMPLATE) { + // Apply template: create a new note based on this template + mRepository.applyTemplate(note.getId(), Notes.ID_ROOT_FOLDER, new NotesRepository.Callback() { + @Override + public void onSuccess(Long newNoteId) { + runOnUiThread(() -> { + Intent intent = new Intent(NoteSearchActivity.this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, newNoteId); + startActivity(intent); + Toast.makeText(NoteSearchActivity.this, "已根据模板创建新笔记", Toast.LENGTH_SHORT).show(); + }); + } + + @Override + public void onError(Exception e) { + runOnUiThread(() -> { + Toast.makeText(NoteSearchActivity.this, "应用模板失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + }); + } + }); + } else { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, note.getId()); + // Pass search keyword for highlighting in editor + intent.putExtra(android.app.SearchManager.EXTRA_DATA_KEY, String.valueOf(note.getId())); + intent.putExtra(android.app.SearchManager.USER_QUERY, query); + startActivity(intent); + } } @Override diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListFragment.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListFragment.java index 260f015..2fca2c1 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListFragment.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListFragment.java @@ -148,6 +148,30 @@ public class NotesListFragment extends Fragment implements NotesRepository.NoteInfo note = viewModel.getNotesLiveData().getValue().get(position); if (note.type == Notes.TYPE_FOLDER) { viewModel.enterFolder(note.getId()); + } else if (note.type == Notes.TYPE_TEMPLATE) { + // Apply template: create a new note based on this template + viewModel.applyTemplate(note.getId(), new net.micode.notes.data.NotesRepository.Callback() { + @Override + public void onSuccess(Long newNoteId) { + // Create a temporary NoteInfo to open the editor + net.micode.notes.data.NotesRepository.NoteInfo newNote = new net.micode.notes.data.NotesRepository.NoteInfo(); + newNote.setId(newNoteId); + newNote.setParentId(Notes.ID_ROOT_FOLDER); + newNote.type = Notes.TYPE_NOTE; + + requireActivity().runOnUiThread(() -> { + openNoteEditor(newNote); + Toast.makeText(requireContext(), "已根据模板创建新笔记", Toast.LENGTH_SHORT).show(); + }); + } + + @Override + public void onError(Exception e) { + requireActivity().runOnUiThread(() -> { + Toast.makeText(requireContext(), "应用模板失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + }); + } + }); } else { if (note.isLocked) { pendingNote = note; diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListItem.java index 9a6d238..38ee421 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -69,7 +69,7 @@ public class NotesListItem extends LinearLayout { * @param checked 该项是否被选中(仅在多选模式下有意义) */ public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { - if (choiceMode && data.getType() == Notes.TYPE_NOTE) { + if (choiceMode && (data.getType() == Notes.TYPE_NOTE || data.getType() == Notes.TYPE_TEMPLATE)) { mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(checked); } else { @@ -136,7 +136,7 @@ public class NotesListItem extends LinearLayout { private void setBackground(NoteItemData data) { int id = data.getBgColorId(); int resId; - if (data.getType() == Notes.TYPE_NOTE) { + if (data.getType() == Notes.TYPE_NOTE || data.getType() == Notes.TYPE_TEMPLATE) { if (data.isSingle() || data.isOneFollowingFolder()) { resId = NoteItemBgResources.getNoteBgSingleRes(id); } else if (data.isLast()) { @@ -153,7 +153,7 @@ public class NotesListItem extends LinearLayout { setBackgroundResource(resId); // Apply tint for new colors - if (data.getType() == Notes.TYPE_NOTE && (id >= net.micode.notes.tool.ResourceParser.MIDNIGHT_BLACK || id < 0)) { + if ((data.getType() == Notes.TYPE_NOTE || data.getType() == Notes.TYPE_TEMPLATE) && (id >= net.micode.notes.tool.ResourceParser.MIDNIGHT_BLACK || id < 0)) { int color = net.micode.notes.tool.ResourceParser.getNoteBgColor(getContext(), id); if (getBackground() != null) { getBackground().setTint(color); diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesRecyclerAdapter.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesRecyclerAdapter.java index 5dadec7..3a5c484 100644 --- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesRecyclerAdapter.java +++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesRecyclerAdapter.java @@ -105,7 +105,7 @@ public class NotesRecyclerAdapter extends RecyclerView.Adapter path) { folderPathLiveData.postValue(path); - } - - @Override - public void onError(Exception error) { - Log.e(TAG, "Failed to load folder path", error); - } - }); - - // 加载子文件夹 (Category Tabs) - Always load root folders to keep tabs visible - repository.getSubFolders(Notes.ID_ROOT_FOLDER, new NotesRepository.Callback>() { - @Override - public void onSuccess(List folders) { - // Construct the display list with "All" and "Uncategorized" - List displayFolders = new ArrayList<>(); - - // 1. "All" Folder (Virtual) - NotesRepository.NoteInfo allFolder = new NotesRepository.NoteInfo(); - allFolder.setId(Notes.ID_ALL_NOTES_FOLDER); - allFolder.snippet = "所有"; // Name - displayFolders.add(allFolder); - // 2. Real Folders (from DB) - if (folders != null) { - displayFolders.addAll(folders); + // Determine if we are in template mode + boolean isTemplate = (folderId == Notes.ID_TEMPLATE_FOLDER); + if (!isTemplate && path != null) { + for (NotesRepository.NoteInfo info : path) { + if (info.getId() == Notes.ID_TEMPLATE_FOLDER) { + isTemplate = true; + break; + } + } } - // 3. "Uncategorized" Folder (Actually Root Folder) - NotesRepository.NoteInfo uncategorizedFolder = new NotesRepository.NoteInfo(); - uncategorizedFolder.setId(Notes.ID_ROOT_FOLDER); - uncategorizedFolder.snippet = "未分类"; // Custom Name for Root - displayFolders.add(uncategorizedFolder); - - foldersLiveData.postValue(displayFolders); + final boolean templateMode = isTemplate; + long tabParentId = templateMode ? Notes.ID_TEMPLATE_FOLDER : Notes.ID_ROOT_FOLDER; + + // 加载子文件夹 (Category Tabs) + repository.getSubFolders(tabParentId, new NotesRepository.Callback>() { + @Override + public void onSuccess(List folders) { + // Construct the display list with "All" and "Uncategorized" + List displayFolders = new ArrayList<>(); + + // 1. "All" / "All Templates" Folder (Virtual) + NotesRepository.NoteInfo allFolder = new NotesRepository.NoteInfo(); + allFolder.setId(templateMode ? Notes.ID_TEMPLATE_FOLDER : Notes.ID_ALL_NOTES_FOLDER); + allFolder.snippet = templateMode ? "所有模板" : "所有"; // Name + displayFolders.add(allFolder); + + // 2. Real Folders (from DB) + if (folders != null) { + displayFolders.addAll(folders); + } + + // 3. "Uncategorized" Folder (only for normal notes) + if (!templateMode) { + NotesRepository.NoteInfo uncategorizedFolder = new NotesRepository.NoteInfo(); + uncategorizedFolder.setId(Notes.ID_ROOT_FOLDER); + uncategorizedFolder.snippet = "未分类"; // Custom Name for Root + displayFolders.add(uncategorizedFolder); + } + + foldersLiveData.postValue(displayFolders); + } + + @Override + public void onError(Exception error) { + Log.e(TAG, "Failed to load sub-folders", error); + } + }); } - + @Override public void onError(Exception error) { - Log.e(TAG, "Failed to load sub-folders", error); + Log.e(TAG, "Failed to load folder path", error); } }); diff --git a/src/Notesmaster/app/src/main/res/drawable/bg_bottom_sheet.xml b/src/Notesmaster/app/src/main/res/drawable/bg_bottom_sheet.xml new file mode 100644 index 0000000..a91aca0 --- /dev/null +++ b/src/Notesmaster/app/src/main/res/drawable/bg_bottom_sheet.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/drawable/preset_forest.xml b/src/Notesmaster/app/src/main/res/drawable/preset_forest.xml new file mode 100644 index 0000000..aced12d --- /dev/null +++ b/src/Notesmaster/app/src/main/res/drawable/preset_forest.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/drawable/preset_lavender.xml b/src/Notesmaster/app/src/main/res/drawable/preset_lavender.xml new file mode 100644 index 0000000..b9803bb --- /dev/null +++ b/src/Notesmaster/app/src/main/res/drawable/preset_lavender.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/drawable/preset_ocean.xml b/src/Notesmaster/app/src/main/res/drawable/preset_ocean.xml new file mode 100644 index 0000000..dd2e1ee --- /dev/null +++ b/src/Notesmaster/app/src/main/res/drawable/preset_ocean.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/drawable/preset_sunset.xml b/src/Notesmaster/app/src/main/res/drawable/preset_sunset.xml new file mode 100644 index 0000000..802f1ca --- /dev/null +++ b/src/Notesmaster/app/src/main/res/drawable/preset_sunset.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/layout/dialog_background_selector.xml b/src/Notesmaster/app/src/main/res/layout/dialog_background_selector.xml new file mode 100644 index 0000000..3f50cf6 --- /dev/null +++ b/src/Notesmaster/app/src/main/res/layout/dialog_background_selector.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Notesmaster/app/src/main/res/layout/note_edit.xml b/src/Notesmaster/app/src/main/res/layout/note_edit.xml index aba7781..5bacd39 100644 --- a/src/Notesmaster/app/src/main/res/layout/note_edit.xml +++ b/src/Notesmaster/app/src/main/res/layout/note_edit.xml @@ -15,482 +15,258 @@ limitations under the License. --> - - + android:background="@color/background_color"> - - + + android:layout_height="match_parent" + android:scaleType="centerCrop" + android:visibility="gone" /> - - - - + android:layout_height="match_parent" + android:background="#1A000000" + android:visibility="gone" /> - - - - - - - - - - + + - - + android:layout_height="?attr/actionBarSize" + android:background="@android:color/transparent" + app:navigationIcon="@android:drawable/ic_menu_close_clear_cancel" + app:title="@string/menu_edit_note" + app:titleTextAppearance="@style/TextAppearance.Material3.TitleMedium" /> + + + + - - - - + + + + android:layout_height="wrap_content" + app:cardCornerRadius="24dp" + app:cardElevation="0dp" + app:cardBackgroundColor="#CCFFFFFF" + app:strokeWidth="0dp"> + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="24dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + android:textSize="18sp" + android:textColor="@color/text_color_primary" + android:lineSpacingMultiplier="1.5" + android:fontFamily="sans-serif" /> - - - + + - - + + + + + + android:layout_height="56dp" + android:scrollbars="none"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:gravity="center_vertical" + android:paddingHorizontal="12dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Notesmaster/app/src/main/res/values/colors.xml b/src/Notesmaster/app/src/main/res/values/colors.xml index 8c4a5a3..6405010 100644 --- a/src/Notesmaster/app/src/main/res/values/colors.xml +++ b/src/Notesmaster/app/src/main/res/values/colors.xml @@ -43,6 +43,16 @@ #FFE0B2 #E1BEE7 + + #FF512F + #DD2476 + #2193B0 + #6DD5ED + #11998E + #38EF7D + #834D9B + #D04ED6 + #1976D2 #2196F3