diff --git a/src/Notesmaster/.gitignore b/src/Notesmaster/.gitignore
index aa724b7..7f909be 100644
--- a/src/Notesmaster/.gitignore
+++ b/src/Notesmaster/.gitignore
@@ -13,3 +13,8 @@
.externalNativeBuild
.cxx
local.properties
+build.gradle.kts
+gradle.properties
+gradlew
+gradlew.bat
+settings.gradle.kts
\ No newline at end of file
diff --git a/src/Notesmaster/app/src/main/AndroidManifest.xml b/src/Notesmaster/app/src/main/AndroidManifest.xml
index 34b32a4..db4380f 100644
--- a/src/Notesmaster/app/src/main/AndroidManifest.xml
+++ b/src/Notesmaster/app/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@
+
+
+
+ android:theme="@style/Theme.Notesmaster" >
@@ -171,14 +181,14 @@
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
- android:theme="@android:style/Theme.Holo.Light" >
+ android:theme="@style/Theme.Notesmaster" >
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java b/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java
new file mode 100644
index 0000000..e6b008a
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/NotesApplication.java
@@ -0,0 +1,18 @@
+package net.micode.notes;
+
+import android.app.Application;
+import net.micode.notes.data.ThemeRepository;
+import com.google.android.material.color.DynamicColors;
+
+public class NotesApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // Apply Dynamic Colors (Material You) if available
+ DynamicColors.applyToActivitiesIfAvailable(this);
+
+ // Apply saved theme preference
+ ThemeRepository repository = new ThemeRepository(this);
+ ThemeRepository.applyTheme(repository.getThemeMode());
+ }
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/FontManager.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/FontManager.java
new file mode 100644
index 0000000..dacf271
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/FontManager.java
@@ -0,0 +1,56 @@
+package net.micode.notes.data;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceManager;
+
+public class FontManager {
+ public static final String PREF_FONT_FAMILY = "pref_font_family";
+
+ private final SharedPreferences mPrefs;
+ private static FontManager sInstance;
+
+ private FontManager(Context context) {
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static synchronized FontManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new FontManager(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ public void applyFont(TextView textView) {
+ String fontValue = mPrefs.getString(PREF_FONT_FAMILY, "default");
+ Typeface typeface = getTypeface(fontValue);
+ if (typeface != null) {
+ textView.setTypeface(typeface);
+ } else {
+ textView.setTypeface(Typeface.DEFAULT);
+ }
+ }
+
+ private Typeface getTypeface(String fontValue) {
+ switch (fontValue) {
+ case "serif":
+ return Typeface.SERIF;
+ case "sans-serif":
+ return Typeface.SANS_SERIF;
+ case "monospace":
+ return Typeface.MONOSPACE;
+ case "cursive":
+ // Android doesn't have a built-in cursive typeface constant,
+ // but we can try to load sans-serif-light or similar as a placeholder,
+ // or load from assets if we had custom fonts.
+ // For now, let's map it to serif-italic style if possible or just serif.
+ return Typeface.create(Typeface.SERIF, Typeface.ITALIC);
+ case "default":
+ default:
+ return Typeface.DEFAULT;
+ }
+ }
+}
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 4a9f86d..b07132d 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
@@ -739,13 +739,15 @@ public class NotesRepository {
return;
}
- String selection = "(" + NoteColumns.TYPE + " = ?) AND (" +
+ String selection = "(" + NoteColumns.TYPE + " <> ?) AND (" +
+ NoteColumns.TITLE + " LIKE ? OR " +
NoteColumns.SNIPPET + " LIKE ? OR " +
NoteColumns.ID + " IN (SELECT " + DataColumns.NOTE_ID +
" FROM data WHERE " + DataColumns.CONTENT + " LIKE ?))";
String[] selectionArgs = new String[]{
- String.valueOf(Notes.TYPE_NOTE),
+ String.valueOf(Notes.TYPE_SYSTEM),
+ "%" + keyword + "%",
"%" + keyword + "%",
"%" + keyword + "%"
};
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/data/ThemeRepository.java b/src/Notesmaster/app/src/main/java/net/micode/notes/data/ThemeRepository.java
new file mode 100644
index 0000000..815f0ce
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/data/ThemeRepository.java
@@ -0,0 +1,43 @@
+package net.micode.notes.data;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.preference.PreferenceManager;
+
+public class ThemeRepository {
+ private static final String PREF_THEME_MODE = "pref_theme_mode";
+ public static final String THEME_MODE_SYSTEM = "system";
+ public static final String THEME_MODE_LIGHT = "light";
+ public static final String THEME_MODE_DARK = "dark";
+
+ private final SharedPreferences mPrefs;
+
+ public ThemeRepository(Context context) {
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public String getThemeMode() {
+ return mPrefs.getString(PREF_THEME_MODE, THEME_MODE_SYSTEM);
+ }
+
+ public void setThemeMode(String mode) {
+ mPrefs.edit().putString(PREF_THEME_MODE, mode).apply();
+ applyTheme(mode);
+ }
+
+ public static void applyTheme(String mode) {
+ switch (mode) {
+ case THEME_MODE_LIGHT:
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
+ break;
+ case THEME_MODE_DARK:
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
+ break;
+ case THEME_MODE_SYSTEM:
+ default:
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
+ break;
+ }
+ }
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/model/Command.java b/src/Notesmaster/app/src/main/java/net/micode/notes/model/Command.java
new file mode 100644
index 0000000..77e4b0e
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/model/Command.java
@@ -0,0 +1,6 @@
+package net.micode.notes.model;
+
+public interface Command {
+ void execute();
+ void undo();
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/model/NoteCommand.java b/src/Notesmaster/app/src/main/java/net/micode/notes/model/NoteCommand.java
new file mode 100644
index 0000000..787bbfb
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/model/NoteCommand.java
@@ -0,0 +1,40 @@
+package net.micode.notes.model;
+
+import android.text.Editable;
+import android.widget.EditText;
+
+public class NoteCommand implements Command {
+ private final EditText mEditor;
+ private final int mStart;
+ private final CharSequence mBefore;
+ private final CharSequence mAfter;
+
+ public NoteCommand(EditText editor, int start, CharSequence before, CharSequence after) {
+ mEditor = editor;
+ mStart = start;
+ mBefore = before.toString();
+ mAfter = after.toString();
+ }
+
+ @Override
+ public void execute() {
+ // Redo: replace 'before' with 'after'
+ Editable text = mEditor.getText();
+ int end = mStart + mBefore.length();
+ if (end <= text.length()) {
+ text.replace(mStart, end, mAfter);
+ mEditor.setSelection(mStart + mAfter.length());
+ }
+ }
+
+ @Override
+ public void undo() {
+ // Undo: replace 'after' with 'before'
+ Editable text = mEditor.getText();
+ int end = mStart + mAfter.length();
+ if (end <= text.length()) {
+ text.replace(mStart, end, mBefore);
+ mEditor.setSelection(mStart + mBefore.length());
+ }
+ }
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/model/UndoRedoManager.java b/src/Notesmaster/app/src/main/java/net/micode/notes/model/UndoRedoManager.java
new file mode 100644
index 0000000..081dab9
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/model/UndoRedoManager.java
@@ -0,0 +1,49 @@
+package net.micode.notes.model;
+
+import java.util.Stack;
+
+public class UndoRedoManager {
+ private static final int MAX_STACK_SIZE = 20;
+ private final Stack mUndoStack = new Stack<>();
+ private final Stack mRedoStack = new Stack<>();
+
+ public void addCommand(Command command) {
+ mUndoStack.push(command);
+ if (mUndoStack.size() > MAX_STACK_SIZE) {
+ mUndoStack.remove(0);
+ }
+ mRedoStack.clear();
+ }
+
+ public void undo() {
+ if (!mUndoStack.isEmpty()) {
+ Command command = mUndoStack.pop();
+ command.undo();
+ mRedoStack.push(command);
+ }
+ }
+
+ public void redo() {
+ if (!mRedoStack.isEmpty()) {
+ Command command = mRedoStack.pop();
+ command.execute();
+ mUndoStack.push(command);
+ if (mUndoStack.size() > MAX_STACK_SIZE) {
+ mUndoStack.remove(0);
+ }
+ }
+ }
+
+ public boolean canUndo() {
+ return !mUndoStack.isEmpty();
+ }
+
+ public boolean canRedo() {
+ return !mRedoStack.isEmpty();
+ }
+
+ public void clear() {
+ mUndoStack.clear();
+ mRedoStack.clear();
+ }
+}
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 f8aadb4..c062c24 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
@@ -427,6 +427,27 @@ public class WorkingNote {
}
}
+ // Wallpaper Support
+ 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
+ }
+ }
+
+ public String getWallpaperPath() {
+ return mWallpaperPath;
+ }
+
/**
* 设置清单模式
*
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 4677289..595d7a5 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
@@ -45,10 +45,40 @@ public class ResourceParser {
/** 红色背景 */
public static final int RED = 4;
+
+ // New Presets
+ public static final int MIDNIGHT_BLACK = 5;
+ public static final int EYE_CARE_GREEN = 6;
+ public static final int WARM = 7;
+ public static final int COOL = 8;
+
+ /** 自定义颜色按钮 ID (用于 UI 显示) */
+ public static final int CUSTOM_COLOR_BUTTON_ID = -100;
+ /** 壁纸按钮 ID (用于 UI 显示) */
+ public static final int WALLPAPER_BUTTON_ID = -101;
/** 默认背景颜色 */
public static final int BG_DEFAULT_COLOR = YELLOW;
+ public static int getNoteBgColor(Context context, int id) {
+ if (id < 0) {
+ return id; // Custom color (ARGB)
+ }
+ switch (id) {
+ case YELLOW: return context.getColor(R.color.bg_yellow);
+ case BLUE: return context.getColor(R.color.bg_blue);
+ case WHITE: return context.getColor(R.color.bg_white);
+ case GREEN: return context.getColor(R.color.bg_green);
+ case RED: return context.getColor(R.color.bg_red);
+ case MIDNIGHT_BLACK: return context.getColor(R.color.bg_midnight_black);
+ case EYE_CARE_GREEN: return context.getColor(R.color.bg_eye_care_green);
+ case WARM: return context.getColor(R.color.bg_warm);
+ case COOL: return context.getColor(R.color.bg_cool);
+ default: return context.getColor(R.color.bg_white);
+ }
+ }
+
+
/** 小号字体 */
public static final int TEXT_SMALL = 0;
@@ -97,6 +127,9 @@ public class ResourceParser {
* @return 背景资源 ID
*/
public static int getNoteBgResource(int id) {
+ if (id >= BG_EDIT_RESOURCES.length || id < 0) {
+ return R.drawable.edit_white;
+ }
return BG_EDIT_RESOURCES[id];
}
@@ -107,6 +140,9 @@ public class ResourceParser {
* @return 标题栏背景资源 ID
*/
public static int getNoteTitleBgResource(int id) {
+ if (id >= BG_EDIT_TITLE_RESOURCES.length || id < 0) {
+ return R.drawable.edit_title_white;
+ }
return BG_EDIT_TITLE_RESOURCES[id];
}
}
@@ -182,6 +218,7 @@ public class ResourceParser {
* @return 首项背景资源 ID
*/
public static int getNoteBgFirstRes(int id) {
+ if (id >= BG_FIRST_RESOURCES.length || id < 0) return R.drawable.list_white_up;
return BG_FIRST_RESOURCES[id];
}
@@ -192,6 +229,7 @@ public class ResourceParser {
* @return 末项背景资源 ID
*/
public static int getNoteBgLastRes(int id) {
+ if (id >= BG_LAST_RESOURCES.length || id < 0) return R.drawable.list_white_down;
return BG_LAST_RESOURCES[id];
}
@@ -202,6 +240,7 @@ public class ResourceParser {
* @return 单项背景资源 ID
*/
public static int getNoteBgSingleRes(int id) {
+ if (id >= BG_SINGLE_RESOURCES.length || id < 0) return R.drawable.list_white_single;
return BG_SINGLE_RESOURCES[id];
}
@@ -212,6 +251,7 @@ public class ResourceParser {
* @return 中间项背景资源 ID
*/
public static int getNoteBgNormalRes(int id) {
+ if (id >= BG_NORMAL_RESOURCES.length || id < 0) return R.drawable.list_white_middle;
return BG_NORMAL_RESOURCES[id];
}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/RichTextHelper.java b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/RichTextHelper.java
new file mode 100644
index 0000000..5ed8da3
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/RichTextHelper.java
@@ -0,0 +1,257 @@
+package net.micode.notes.tool;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.text.Editable;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.BulletSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.QuoteSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+
+public class RichTextHelper {
+
+ public static void applyBold(EditText editText) {
+ applyStyleSpan(editText, Typeface.BOLD);
+ }
+
+ public static void applyItalic(EditText editText) {
+ applyStyleSpan(editText, Typeface.ITALIC);
+ }
+
+ public static void applyUnderline(EditText editText) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start > end) { int temp = start; start = end; end = temp; }
+
+ Editable editable = editText.getText();
+ UnderlineSpan[] spans = editable.getSpans(start, end, UnderlineSpan.class);
+ if (spans != null && spans.length > 0) {
+ for (UnderlineSpan span : spans) {
+ editable.removeSpan(span);
+ }
+ } else {
+ editable.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void applyStrikethrough(EditText editText) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start > end) { int temp = start; start = end; end = temp; }
+
+ Editable editable = editText.getText();
+ StrikethroughSpan[] spans = editable.getSpans(start, end, StrikethroughSpan.class);
+ if (spans != null && spans.length > 0) {
+ for (StrikethroughSpan span : spans) {
+ editable.removeSpan(span);
+ }
+ } else {
+ editable.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ private static void applyStyleSpan(EditText editText, int style) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start > end) { int temp = start; start = end; end = temp; }
+
+ Editable editable = editText.getText();
+ StyleSpan[] spans = editable.getSpans(start, end, StyleSpan.class);
+ boolean exists = false;
+ for (StyleSpan span : spans) {
+ if (span.getStyle() == style) {
+ editable.removeSpan(span);
+ exists = true;
+ }
+ }
+
+ if (!exists) {
+ editable.setSpan(new StyleSpan(style), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void applyHeading(EditText editText, int level) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ // Expand to full line
+ Editable text = editText.getText();
+ String string = text.toString();
+
+ // Find line start and end
+ int lineStart = string.lastIndexOf('\n', start - 1) + 1;
+ if (lineStart < 0) lineStart = 0;
+ int lineEnd = string.indexOf('\n', end);
+ if (lineEnd < 0) lineEnd = string.length();
+
+ // Remove existing heading spans
+ RelativeSizeSpan[] sizeSpans = text.getSpans(lineStart, lineEnd, RelativeSizeSpan.class);
+ for (RelativeSizeSpan span : sizeSpans) {
+ text.removeSpan(span);
+ }
+ StyleSpan[] styleSpans = text.getSpans(lineStart, lineEnd, StyleSpan.class);
+ for (StyleSpan span : styleSpans) {
+ if (span.getStyle() == Typeface.BOLD) {
+ text.removeSpan(span);
+ }
+ }
+
+ if (level > 0) {
+ float scale = 1.0f;
+ switch (level) {
+ case 1: scale = 2.0f; break;
+ case 2: scale = 1.5f; break;
+ case 3: scale = 1.25f; break;
+ case 4: scale = 1.1f; break;
+ case 5: scale = 1.0f; break;
+ case 6: scale = 0.8f; break;
+ }
+ text.setSpan(new RelativeSizeSpan(scale), lineStart, lineEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(new StyleSpan(Typeface.BOLD), lineStart, lineEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void applyBullet(EditText editText) {
+ // Simple bullet implementation for now
+ int start = editText.getSelectionStart();
+ Editable text = editText.getText();
+ String string = text.toString();
+ int lineStart = string.lastIndexOf('\n', start - 1) + 1;
+ if (lineStart < 0) lineStart = 0;
+
+ // Check if already bulleted
+ // Note: BulletSpan covers a paragraph.
+ int lineEnd = string.indexOf('\n', start);
+ if (lineEnd < 0) lineEnd = string.length();
+
+ BulletSpan[] spans = text.getSpans(lineStart, lineEnd, BulletSpan.class);
+ if (spans != null && spans.length > 0) {
+ for (BulletSpan span : spans) {
+ text.removeSpan(span);
+ }
+ } else {
+ text.setSpan(new BulletSpan(20, Color.BLACK), lineStart, lineEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void applyQuote(EditText editText) {
+ int start = editText.getSelectionStart();
+ Editable text = editText.getText();
+ String string = text.toString();
+ int lineStart = string.lastIndexOf('\n', start - 1) + 1;
+ if (lineStart < 0) lineStart = 0;
+ int lineEnd = string.indexOf('\n', start);
+ if (lineEnd < 0) lineEnd = string.length();
+
+ QuoteSpan[] spans = text.getSpans(lineStart, lineEnd, QuoteSpan.class);
+ if (spans != null && spans.length > 0) {
+ for (QuoteSpan span : spans) {
+ text.removeSpan(span);
+ }
+ } else {
+ text.setSpan(new QuoteSpan(Color.GRAY), lineStart, lineEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void applyCode(EditText editText) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start > end) { int temp = start; start = end; end = temp; }
+
+ Editable editable = editText.getText();
+ TypefaceSpan[] spans = editable.getSpans(start, end, TypefaceSpan.class);
+ boolean exists = false;
+ for (TypefaceSpan span : spans) {
+ if ("monospace".equals(span.getFamily())) {
+ editable.removeSpan(span);
+ exists = true;
+ }
+ }
+
+ // Also toggle background color for code block look
+ BackgroundColorSpan[] bgSpans = editable.getSpans(start, end, BackgroundColorSpan.class);
+ for (BackgroundColorSpan span : bgSpans) {
+ if (span.getBackgroundColor() == 0xFFEEEEEE) {
+ editable.removeSpan(span);
+ }
+ }
+
+ if (!exists) {
+ editable.setSpan(new TypefaceSpan("monospace"), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ editable.setSpan(new BackgroundColorSpan(0xFFEEEEEE), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void insertLink(Context context, final EditText editText) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle("Insert Link");
+
+ final EditText input = new EditText(context);
+ input.setHint("http://example.com");
+ builder.setView(input);
+
+ builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String url = input.getText().toString();
+ if (!TextUtils.isEmpty(url)) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start == end) {
+ // Insert url as text
+ editText.getText().insert(start, url);
+ end = start + url.length();
+ }
+ editText.getText().setSpan(new URLSpan(url), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", null);
+ builder.show();
+ }
+
+ public static void applyColor(EditText editText, int color, boolean isBackground) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ if (start > end) { int temp = start; start = end; end = temp; }
+
+ Editable editable = editText.getText();
+ if (isBackground) {
+ BackgroundColorSpan[] spans = editable.getSpans(start, end, BackgroundColorSpan.class);
+ for (BackgroundColorSpan span : spans) editable.removeSpan(span);
+ editable.setSpan(new BackgroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ ForegroundColorSpan[] spans = editable.getSpans(start, end, ForegroundColorSpan.class);
+ for (ForegroundColorSpan span : spans) editable.removeSpan(span);
+ editable.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ public static void insertDivider(EditText editText) {
+ int start = editText.getSelectionStart();
+ editText.getText().insert(start, "\n-------------------\n");
+ }
+
+ public static String toHtml(Spanned text) {
+ return Html.toHtml(text);
+ }
+
+ public static Spanned fromHtml(String html) {
+ return Html.fromHtml(html);
+ }
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/tool/SearchHistoryManager.java b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/SearchHistoryManager.java
new file mode 100644
index 0000000..1f3f9b0
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/tool/SearchHistoryManager.java
@@ -0,0 +1,67 @@
+package net.micode.notes.tool;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SearchHistoryManager {
+ private static final String PREF_NAME = "search_history";
+ private static final String KEY_HISTORY = "history_list";
+ private static final int MAX_HISTORY_SIZE = 10;
+
+ private final SharedPreferences mPrefs;
+
+ public SearchHistoryManager(Context context) {
+ mPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ }
+
+ public List getHistory() {
+ String json = mPrefs.getString(KEY_HISTORY, "");
+ List list = new ArrayList<>();
+ if (TextUtils.isEmpty(json)) {
+ return list;
+ }
+ try {
+ JSONArray array = new JSONArray(json);
+ for (int i = 0; i < array.length(); i++) {
+ list.add(array.getString(i));
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+
+ public void addHistory(String keyword) {
+ if (TextUtils.isEmpty(keyword)) return;
+ List history = getHistory();
+ // Remove existing to move to top
+ history.remove(keyword);
+ history.add(0, keyword);
+ // Limit size
+ if (history.size() > MAX_HISTORY_SIZE) {
+ history = history.subList(0, MAX_HISTORY_SIZE);
+ }
+ saveHistory(history);
+ }
+
+ public void removeHistory(String keyword) {
+ List history = getHistory();
+ if (history.remove(keyword)) {
+ saveHistory(history);
+ }
+ }
+
+ public void clearHistory() {
+ mPrefs.edit().remove(KEY_HISTORY).apply();
+ }
+
+ private void saveHistory(List history) {
+ JSONArray array = new JSONArray(history);
+ mPrefs.edit().putString(KEY_HISTORY, array.toString()).apply();
+ }
+}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteColorAdapter.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteColorAdapter.java
new file mode 100644
index 0000000..d32c674
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteColorAdapter.java
@@ -0,0 +1,111 @@
+package net.micode.notes.ui;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.micode.notes.R;
+import net.micode.notes.tool.ResourceParser;
+
+import java.util.List;
+
+public class NoteColorAdapter extends RecyclerView.Adapter {
+
+ public interface OnColorClickListener {
+ void onColorClick(int colorId);
+ }
+
+ private List mColorIds;
+ private int mSelectedColorId;
+ private OnColorClickListener mListener;
+
+ public NoteColorAdapter(List colorIds, int selectedColorId, OnColorClickListener listener) {
+ mColorIds = colorIds;
+ mSelectedColorId = selectedColorId;
+ mListener = listener;
+ }
+
+ public void setSelectedColor(int colorId) {
+ mSelectedColorId = colorId;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.note_color_item, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ int colorId = mColorIds.get(position);
+
+ if (colorId == ResourceParser.CUSTOM_COLOR_BUTTON_ID) {
+ holder.colorView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ int padding = (int) (12 * holder.itemView.getContext().getResources().getDisplayMetrics().density);
+ holder.colorView.setPadding(padding, padding, padding, padding);
+ holder.colorView.setImageResource(R.drawable.ic_palette);
+ // Apply a dark tint to ensure visibility
+ holder.colorView.setColorFilter(android.graphics.Color.DKGRAY, android.graphics.PorterDuff.Mode.SRC_IN);
+ // Optional: Set a background for the icon
+ holder.colorView.setBackgroundResource(R.drawable.bg_color_btn_mask);
+ } else if (colorId == ResourceParser.WALLPAPER_BUTTON_ID) {
+ holder.colorView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ int padding = (int) (12 * holder.itemView.getContext().getResources().getDisplayMetrics().density);
+ holder.colorView.setPadding(padding, padding, padding, padding);
+ holder.colorView.setImageResource(R.drawable.ic_image);
+ // Apply a dark tint to ensure visibility
+ holder.colorView.setColorFilter(android.graphics.Color.DKGRAY, android.graphics.PorterDuff.Mode.SRC_IN);
+ // Optional: Set a background for the icon
+ holder.colorView.setBackgroundResource(R.drawable.bg_color_btn_mask);
+ } else {
+ holder.colorView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ holder.colorView.setPadding(0, 0, 0, 0);
+ // 使用ResourceParser获取背景资源
+ int bgRes = ResourceParser.NoteBgResources.getNoteBgResource(colorId);
+ holder.colorView.setImageResource(bgRes);
+ holder.colorView.setBackground(null); // Clear background if reused
+
+ if (colorId >= ResourceParser.MIDNIGHT_BLACK || colorId < 0) {
+ int color = ResourceParser.getNoteBgColor(holder.itemView.getContext(), colorId);
+ holder.colorView.setColorFilter(color, android.graphics.PorterDuff.Mode.MULTIPLY);
+ } else {
+ holder.colorView.clearColorFilter();
+ }
+ }
+
+ if (colorId == mSelectedColorId && colorId != ResourceParser.CUSTOM_COLOR_BUTTON_ID) {
+ holder.checkView.setVisibility(View.VISIBLE);
+ } else {
+ holder.checkView.setVisibility(View.GONE);
+ }
+
+ holder.itemView.setOnClickListener(v -> {
+ if (mListener != null) {
+ mListener.onColorClick(colorId);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mColorIds.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ ImageView colorView;
+ ImageView checkView;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ colorView = itemView.findViewById(R.id.color_view);
+ checkView = itemView.findViewById(R.id.check_view);
+ }
+ }
+}
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 ed3df61..d7993be 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
@@ -57,6 +57,8 @@ import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
+import net.micode.notes.model.NoteCommand;
+import net.micode.notes.model.UndoRedoManager;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
@@ -77,8 +79,11 @@ import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.appbar.MaterialToolbar;
import net.micode.notes.databinding.NoteEditBinding;
+import net.micode.notes.tool.RichTextHelper;
+import net.micode.notes.data.FontManager;
+
public class NoteEditActivity extends AppCompatActivity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
/**
@@ -101,24 +106,6 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
public EditText etTitle;
}
- private static final Map sBgSelectorBtnsMap = new HashMap();
- static {
- sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
- sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
- 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);
- }
-
- private static final Map sBgSelectorSelectionMap = new HashMap();
- static {
- sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
- sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
- 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);
- }
-
private static final Map sFontSizeBtnsMap = new HashMap();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
@@ -145,6 +132,8 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
private View mFontSizeSelector;
+ private View mRichTextSelector;
+
private EditText mNoteEditor;
private View mNoteEditorPanel;
@@ -170,25 +159,11 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
private NoteEditBinding binding;
- /**
- * 辅助方法:根据ID获取背景颜色选择视图
- */
- private View getBgSelectorView(int viewId) {
- switch (viewId) {
- case R.id.iv_bg_yellow_select:
- return binding.ivBgYellowSelect;
- case R.id.iv_bg_red_select:
- return binding.ivBgRedSelect;
- case R.id.iv_bg_blue_select:
- return binding.ivBgBlueSelect;
- case R.id.iv_bg_green_select:
- return binding.ivBgGreenSelect;
- case R.id.iv_bg_white_select:
- return binding.ivBgWhiteSelect;
- default:
- throw new IllegalArgumentException("Unknown view ID: " + viewId);
- }
- }
+ private UndoRedoManager mUndoRedoManager;
+ private boolean mInUndoRedo = false;
+
+ private androidx.recyclerview.widget.RecyclerView mColorSelectorRv;
+ private NoteColorAdapter mColorAdapter;
/**
* 辅助方法:根据ID获取字体选择视图
@@ -215,6 +190,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
// 使用ViewBinding设置布局
binding = NoteEditBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
+ mUndoRedoManager = new UndoRedoManager();
// 初始化Toolbar(使用MaterialToolbar,与列表页面一致)
setSupportActionBar(binding.toolbar);
@@ -368,10 +344,16 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
mNoteEditor = binding.noteEditView;
mNoteEditorPanel = binding.svNoteEdit;
mNoteBgColorSelector = binding.noteBgColorSelector;
+ mColorSelectorRv = binding.rvBgColorSelector;
mNoteEditor.addTextChangedListener(new TextWatcher() {
+ private CharSequence mBeforeText;
+
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ if (!mInUndoRedo) {
+ mBeforeText = s.subSequence(start, start + count).toString();
+ }
}
@Override
@@ -379,6 +361,13 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
if (mNoteHeaderHolder != null && mNoteHeaderHolder.tvCharCount != null) {
mNoteHeaderHolder.tvCharCount.setText(String.valueOf(s.length()) + " 字");
}
+ if (!mInUndoRedo) {
+ CharSequence afterText = s.subSequence(start, start + count).toString();
+ if (!TextUtils.equals(mBeforeText, afterText)) {
+ mUndoRedoManager.addCommand(new NoteCommand(mNoteEditor, start, mBeforeText, afterText));
+ invalidateOptionsMenu();
+ }
+ }
}
@Override
@@ -406,30 +395,34 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
}
});
- // 设置背景颜色选择器的点击事件
- for (int id : sBgSelectorBtnsMap.keySet()) {
- ImageView iv;
- switch (id) {
- case R.id.iv_bg_yellow:
- iv = binding.ivBgYellow;
- break;
- case R.id.iv_bg_red:
- iv = binding.ivBgRed;
- break;
- case R.id.iv_bg_blue:
- iv = binding.ivBgBlue;
- break;
- case R.id.iv_bg_green:
- iv = binding.ivBgGreen;
- break;
- case R.id.iv_bg_white:
- iv = binding.ivBgWhite;
- break;
- default:
- throw new IllegalArgumentException("Unknown view ID: " + id);
+ // Initialize Color Adapter
+ 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.CUSTOM_COLOR_BUTTON_ID,
+ ResourceParser.WALLPAPER_BUTTON_ID
+ );
+ mColorAdapter = new NoteColorAdapter(colors, ResourceParser.YELLOW, new NoteColorAdapter.OnColorClickListener() {
+ @Override
+ public void onColorClick(int colorId) {
+ if (colorId == ResourceParser.CUSTOM_COLOR_BUTTON_ID) {
+ showColorPickerDialog();
+ } else if (colorId == ResourceParser.WALLPAPER_BUTTON_ID) {
+ pickWallpaper();
+ } else {
+ mWorkingNote.setBgColorId(colorId);
+ mNoteBgColorSelector.setVisibility(View.GONE);
+ }
}
- iv.setOnClickListener(this);
- }
+ });
+ mColorSelectorRv.setAdapter(mColorAdapter);
mFontSizeSelector = binding.fontSizeSelector;
for (int id : sFontSizeBtnsMap.keySet()) {
@@ -464,6 +457,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = binding.noteEditList;
+ initRichTextToolbar();
}
@Override
@@ -487,21 +481,28 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
private void initNoteScreen() {
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
+ // Apply custom font
+ FontManager.getInstance(this).applyFont(mNoteEditor);
+
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
- mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
+ String content = mWorkingNote.getContent();
+ if (content.contains("<") && content.contains(">")) {
+ mNoteEditor.setText(RichTextHelper.fromHtml(content));
+ } else {
+ mNoteEditor.setText(getHighlightQueryResult(content, mUserQuery));
+ }
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
mNoteHeaderHolder.etTitle.setText(mWorkingNote.getTitle());
- for (Integer id : sBgSelectorSelectionMap.keySet()) {
- View view = getBgSelectorView(sBgSelectorSelectionMap.get(id));
- if (view != null) {
- view.setVisibility(View.GONE);
- }
+
+ // Update Color Adapter selection
+ if (mColorAdapter != null) {
+ mColorAdapter.setSelectedColor(mWorkingNote.getBgColorId());
}
- mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
- mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
+
+ updateNoteBackgrounds();
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
@@ -678,17 +679,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
int id = v.getId();
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
- View bgView = getBgSelectorView(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId()));
- if (bgView != null) {
- bgView.setVisibility(View.VISIBLE);
- }
- } else if (sBgSelectorBtnsMap.containsKey(id)) {
- View bgView = getBgSelectorView(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId()));
- if (bgView != null) {
- bgView.setVisibility(View.GONE);
- }
- mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
- mNoteBgColorSelector.setVisibility(View.GONE);
+ // Note: Adapter selection is already set in onBackgroundColorChanged or init
} else if (sFontSizeBtnsMap.containsKey(id)) {
View fontView = getFontSelectorView(sFontSelectorSelectionMap.get(mFontSizeId));
if (fontView != null) {
@@ -706,6 +697,8 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
} else {
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
+ // Apply custom font again as setTextAppearance might reset it
+ FontManager.getInstance(this).applyFont(mNoteEditor);
}
mFontSizeSelector.setVisibility(View.GONE);
}
@@ -758,12 +751,143 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
*
*/
public void onBackgroundColorChanged() {
- View bgView = getBgSelectorView(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId()));
- if (bgView != null) {
- bgView.setVisibility(View.VISIBLE);
+ if (mColorAdapter != null) {
+ mColorAdapter.setSelectedColor(mWorkingNote.getBgColorId());
+ }
+ updateNoteBackgrounds();
+ }
+
+ private void updateNoteBackgrounds() {
+ int colorId = mWorkingNote.getBgColorId();
+ String wallpaperPath = mWorkingNote.getWallpaperPath();
+
+ if (wallpaperPath != null) {
+ // Load wallpaper
+ 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());
+
+ // Dynamic Coloring with Palette
+ androidx.palette.graphics.Palette.from(bitmap).generate(palette -> {
+ if (palette != null) {
+ applyPaletteColors(palette);
+ }
+ });
+
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to load wallpaper", e);
+ // Fallback to color
+ applyColorBackground(colorId);
+ }
+ } else {
+ applyColorBackground(colorId);
+ // Reset toolbar colors to default/theme
+ resetToolbarColors();
+ }
+ updateTextColor(colorId);
+ }
+
+ 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);
+
+ // Ensure contrast for onPrimaryColor
+ if (androidx.core.graphics.ColorUtils.calculateContrast(onPrimaryColor, primaryColor) < 3.0) {
+ onPrimaryColor = android.graphics.Color.WHITE;
+ }
+
+ binding.toolbar.setBackgroundColor(primaryColor);
+ binding.toolbar.setTitleTextColor(onPrimaryColor);
+ if (binding.toolbar.getNavigationIcon() != null) {
+ binding.toolbar.getNavigationIcon().setTint(onPrimaryColor);
+ }
+
+ getWindow().setStatusBarColor(primaryColor);
+ }
+
+ 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.setTitleTextColor(onPrimaryColor);
+ if (binding.toolbar.getNavigationIcon() != null) {
+ binding.toolbar.getNavigationIcon().setTint(onPrimaryColor);
+ }
+ getWindow().setStatusBarColor(primaryColor);
+ }
+
+ private void updateTextColor(int colorId) {
+ // Default to black for light backgrounds
+ int textColor = android.graphics.Color.BLACK;
+
+ if (colorId == ResourceParser.MIDNIGHT_BLACK) {
+ 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);
}
+ }
+
+ private boolean isColorDark(int color) {
+ double darkness = 1 - (0.299 * android.graphics.Color.red(color) +
+ 0.587 * android.graphics.Color.green(color) +
+ 0.114 * android.graphics.Color.blue(color)) / 255;
+ return darkness >= 0.5;
+ }
+
+ 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);
+ }
+ } else {
+ // Clear tint for legacy resources
+ if (mNoteEditorPanel.getBackground() != null) {
+ mNoteEditorPanel.getBackground().clearColorFilter();
+ }
+ if (mHeadViewPanel.getBackground() != null) {
+ mHeadViewPanel.getBackground().clearColorFilter();
+ }
+ }
}
/**
@@ -790,6 +914,18 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
+ MenuItem undoItem = menu.findItem(R.id.menu_undo);
+ MenuItem redoItem = menu.findItem(R.id.menu_redo);
+ MenuItem clearItem = menu.findItem(R.id.menu_clear_history);
+ if (undoItem != null) {
+ undoItem.setEnabled(mUndoRedoManager.canUndo());
+ }
+ if (redoItem != null) {
+ redoItem.setEnabled(mUndoRedoManager.canRedo());
+ }
+ if (clearItem != null) {
+ clearItem.setEnabled(mUndoRedoManager.canUndo() || mUndoRedoManager.canRedo());
+ }
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
@@ -825,6 +961,33 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case R.id.menu_rich_text:
+ if (mRichTextSelector.getVisibility() == View.VISIBLE) {
+ mRichTextSelector.setVisibility(View.GONE);
+ } else {
+ mRichTextSelector.setVisibility(View.VISIBLE);
+ mFontSizeSelector.setVisibility(View.GONE);
+ }
+ break;
+ case R.id.menu_undo:
+ mInUndoRedo = true;
+ mUndoRedoManager.undo();
+ mInUndoRedo = false;
+ invalidateOptionsMenu();
+ showToast(R.string.undo_success);
+ break;
+ case R.id.menu_redo:
+ mInUndoRedo = true;
+ mUndoRedoManager.redo();
+ mInUndoRedo = false;
+ invalidateOptionsMenu();
+ showToast(R.string.redo_success);
+ break;
+ case R.id.menu_clear_history:
+ mUndoRedoManager.clear();
+ invalidateOptionsMenu();
+ showToast(R.string.menu_clear_history);
+ break;
case R.id.menu_new_note:
createNewNote();
break;
@@ -1143,6 +1306,9 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
+ // Apply custom font
+ FontManager.getInstance(this).applyFont(edit);
+
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@@ -1202,6 +1368,8 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
* @param newMode 新的模式
*/
public void onCheckListModeChanged(int oldMode, int newMode) {
+ mUndoRedoManager.clear();
+ invalidateOptionsMenu();
if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString());
} else {
@@ -1245,7 +1413,7 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
}
mWorkingNote.setWorkingText(sb.toString());
} else {
- mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
+ mWorkingNote.setWorkingText(RichTextHelper.toHtml(mNoteEditor.getText()));
}
return hasChecked;
}
@@ -1336,6 +1504,92 @@ 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);
+
+ int currentColor = android.graphics.Color.WHITE;
+ if (mWorkingNote.getBgColorId() < 0) {
+ currentColor = mWorkingNote.getBgColorId();
+ }
+
+ final int[] rgb = new int[]{
+ android.graphics.Color.red(currentColor),
+ android.graphics.Color.green(currentColor),
+ 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]);
+
+ android.widget.SeekBar.OnSeekBarChangeListener listener = new android.widget.SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(android.widget.SeekBar seekBar, int progress, boolean fromUser) {
+ 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]));
+ }
+
+ @Override
+ public void onStartTrackingTouch(android.widget.SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(android.widget.SeekBar seekBar) {}
+ };
+
+ sbRed.setOnSeekBarChangeListener(listener);
+ sbGreen.setOnSeekBarChangeListener(listener);
+ sbBlue.setOnSeekBarChangeListener(listener);
+
+ new AlertDialog.Builder(this)
+ .setTitle("Custom Color")
+ .setView(dialogView)
+ .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);
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ private static final int REQUEST_CODE_PICK_WALLPAPER = 105;
+
+ private void pickWallpaper() {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ startActivityForResult(intent, REQUEST_CODE_PICK_WALLPAPER);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_PICK_WALLPAPER && resultCode == RESULT_OK && data != null) {
+ android.net.Uri uri = data.getData();
+ if (uri != null) {
+ // Take persistent permissions
+ try {
+ getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to take persistable uri permission", e);
+ }
+
+ mWorkingNote.setWallpaper(uri.toString());
+ mNoteBgColorSelector.setVisibility(View.GONE);
+ }
+ }
+ }
+
/**
* 显示Toast提示
*
@@ -1358,4 +1612,78 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}
+
+ private void initRichTextToolbar() {
+ mRichTextSelector = findViewById(R.id.rich_text_selector);
+ findViewById(R.id.btn_bold).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyBold(mNoteEditor); }
+ });
+ findViewById(R.id.btn_italic).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyItalic(mNoteEditor); }
+ });
+ findViewById(R.id.btn_underline).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyUnderline(mNoteEditor); }
+ });
+ findViewById(R.id.btn_strikethrough).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyStrikethrough(mNoteEditor); }
+ });
+ findViewById(R.id.btn_header).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);
+ }
+ });
+ builder.show();
+ }
+ });
+ findViewById(R.id.btn_list).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyBullet(mNoteEditor); }
+ });
+ findViewById(R.id.btn_quote).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyQuote(mNoteEditor); }
+ });
+ findViewById(R.id.btn_code).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.applyCode(mNoteEditor); }
+ });
+ findViewById(R.id.btn_link).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.insertLink(NoteEditActivity.this, mNoteEditor); }
+ });
+ findViewById(R.id.btn_divider).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) { RichTextHelper.insertDivider(mNoteEditor); }
+ });
+ findViewById(R.id.btn_color_text).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};
+ AlertDialog.Builder builder = new AlertDialog.Builder(NoteEditActivity.this);
+ builder.setTitle("Text Color");
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ RichTextHelper.applyColor(mNoteEditor, colors[item], false);
+ }
+ });
+ builder.show();
+ }
+ });
+ findViewById(R.id.btn_color_fill).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};
+ AlertDialog.Builder builder = new AlertDialog.Builder(NoteEditActivity.this);
+ builder.setTitle("Background Color");
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ RichTextHelper.applyColor(mNoteEditor, colors[item], true);
+ }
+ });
+ builder.show();
+ }
+ });
+ }
}
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
new file mode 100644
index 0000000..523120b
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NoteSearchActivity.java
@@ -0,0 +1,188 @@
+package net.micode.notes.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SearchView;
+import androidx.appcompat.widget.Toolbar;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.micode.notes.R;
+import net.micode.notes.data.Notes;
+import net.micode.notes.data.NotesRepository;
+import net.micode.notes.tool.SearchHistoryManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NoteSearchActivity extends AppCompatActivity implements SearchView.OnQueryTextListener, NoteSearchAdapter.OnItemClickListener {
+
+ private SearchView mSearchView;
+ private RecyclerView mRecyclerView;
+ private TextView mTvNoResult;
+ private NoteSearchAdapter mAdapter;
+ private NotesRepository mRepository;
+ private SearchHistoryManager mHistoryManager;
+
+ private TextView mBtnShowHistory;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_note_search);
+
+ mRepository = new NotesRepository(getContentResolver());
+ mHistoryManager = new SearchHistoryManager(this);
+
+ initViews();
+ // Initial state: search is empty, show history button if there is history, or just show list
+ // Requirement: "history option below search bar"
+ showHistoryOption();
+ }
+
+ private void initViews() {
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ toolbar.setNavigationOnClickListener(v -> finish());
+
+ mSearchView = findViewById(R.id.search_view);
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setFocusable(true);
+ mSearchView.setIconified(false);
+ mSearchView.requestFocusFromTouch();
+
+ mBtnShowHistory = findViewById(R.id.btn_show_history);
+ mBtnShowHistory.setOnClickListener(v -> showHistoryList());
+
+ mRecyclerView = findViewById(R.id.recycler_view);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mAdapter = new NoteSearchAdapter(this, this);
+ mRecyclerView.setAdapter(mAdapter);
+
+ mTvNoResult = findViewById(R.id.tv_no_result);
+ }
+
+ private void showHistoryOption() {
+ // Show the "History" button, hide the list
+ mBtnShowHistory.setVisibility(View.VISIBLE);
+ mRecyclerView.setVisibility(View.GONE);
+ mTvNoResult.setVisibility(View.GONE);
+ }
+
+ private void showHistoryList() {
+ List history = mHistoryManager.getHistory();
+ if (history.isEmpty()) {
+ // If no history, maybe show a toast or empty state?
+ // But for now, let's just show the empty list which is fine
+ }
+ List
- *
- * 该类继承自PreferenceActivity,使用SharedPreferences来持久化设置数据。
- * 通过GTaskReceiver接收同步服务的广播,实时更新同步状态。
- *
- */
-public class NotesPreferenceActivity extends PreferenceActivity {
- /**
- * SharedPreferences文件名
- */
public static final String PREFERENCE_NAME = "notes_preferences";
-
- /**
- * 同步账户名称的SharedPreferences键
- */
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
-
- /**
- * 最后同步时间的SharedPreferences键
- */
- public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
-
- /**
- * 背景颜色随机显示设置的SharedPreferences键
- */
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
- public static final String PREFERENCE_SECURITY_KEY = "pref_key_security";
- public static final int REQUEST_CODE_CHECK_PASSWORD = 104;
-
- /**
- * 同步账户分类的Preference键
- */
- private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
-
- /**
- * 账户授权过滤器键,用于添加账户Intent
- */
- private static final String AUTHORITIES_FILTER_KEY = "authorities";
-
- /**
- * 同步账户分类的PreferenceCategory
- */
- private PreferenceCategory mAccountCategory;
-
- /**
- * 同步服务广播接收器
- */
- private GTaskReceiver mReceiver;
-
- /**
- * 设置头部视图绑定
- */
- private SettingsHeaderBinding mHeaderBinding;
-
- /**
- * 原始账户数组,用于检测新增账户
- */
- private Account[] mOriAccounts;
-
- /**
- * 是否添加了新账户的标志
- */
- private boolean mHasAddedAccount;
-
- /**
- * 创建Activity
- *
- * 初始化设置界面,包括:
- *
- * - 启用ActionBar的返回导航
- * - 加载preferences.xml配置文件
- * - 初始化账户分类和广播接收器
- * - 添加设置界面头部视图
- *
- *
- * @param icicle 保存的实例状态
- */
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- /* using the app icon for navigation */
- getActionBar().setDisplayHomeAsUpEnabled(true);
-
- addPreferencesFromResource(R.xml.preferences);
- mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
- // Google Tasks同步功能已禁用
- // mReceiver = new GTaskReceiver();
- // IntentFilter filter = new IntentFilter();
- // filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
- //registerReceiver(mReceiver, filter);
- // if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
- // // Android 13 (API 33) 及以上版本需要指定导出标志
- // registerReceiver(mReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
- // } else {
- // // Android 12 及以下版本使用旧方法
- // registerReceiver(mReceiver, filter);
- // }
- mOriAccounts = null;
- mHeaderBinding = SettingsHeaderBinding.inflate(getLayoutInflater());
- getListView().addHeaderView(mHeaderBinding.getRoot(), null, true);
-
- loadSecurityPreference();
- }
-
- /**
- * Activity恢复时调用
- *
- * 检查是否有新添加的Google账户,如果有则自动设置为同步账户。
- * 然后刷新UI显示。
- *
- */
@Override
- protected void onResume() {
- super.onResume();
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
- // need to set sync account automatically if user has added a new
- // account
- if (mHasAddedAccount) {
- Account[] accounts = getGoogleAccounts();
- if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
- for (Account accountNew : accounts) {
- boolean found = false;
- for (Account accountOld : mOriAccounts) {
- if (TextUtils.equals(accountOld.name, accountNew.name)) {
- found = true;
- break;
- }
- }
- if (!found) {
- setSyncAccount(accountNew.name);
- break;
- }
- }
- }
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle(R.string.preferences_title);
}
- refreshUI();
- }
-
- /**
- * Activity销毁时调用
- *
- * 注销同步服务广播接收器,防止内存泄漏。
- *
- */
- @Override
- protected void onDestroy() {
- // Google Tasks同步功能已禁用
- // if (mReceiver != null) {
- // unregisterReceiver(mReceiver);
- // }
- mHeaderBinding = null;
- super.onDestroy();
- }
-
- private void loadSecurityPreference() {
- Preference securityPref = findPreference(PREFERENCE_SECURITY_KEY);
- if (securityPref != null) {
- securityPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- if (!SecurityManager.getInstance(NotesPreferenceActivity.this).isPasswordSet()) {
- showSetPasswordDialog();
- } else {
- Intent intent = new Intent(NotesPreferenceActivity.this, PasswordActivity.class);
- intent.setAction(PasswordActivity.ACTION_CHECK_PASSWORD);
- startActivityForResult(intent, REQUEST_CODE_CHECK_PASSWORD);
- }
- return true;
- }
- });
- }
- }
-
- private void showSetPasswordDialog() {
- new AlertDialog.Builder(this)
- .setTitle("设置密码")
- .setItems(new String[]{"数字锁", "手势锁"}, (dialog, which) -> {
- int type = (which == 0) ? SecurityManager.TYPE_PIN : SecurityManager.TYPE_PATTERN;
- Intent intent = new Intent(this, PasswordActivity.class);
- intent.setAction(PasswordActivity.ACTION_SETUP_PASSWORD);
- intent.putExtra(PasswordActivity.EXTRA_PASSWORD_TYPE, type);
- startActivity(intent);
- })
- .show();
- }
-
- private void showManagePasswordDialog() {
- new AlertDialog.Builder(this)
- .setTitle("管理密码")
- .setItems(new String[]{"更改密码", "取消密码"}, (dialog, which) -> {
- if (which == 0) { // Change
- showSetPasswordDialog();
- } else { // Remove
- SecurityManager.getInstance(this).removePassword();
- Toast.makeText(this, "密码已取消", Toast.LENGTH_SHORT).show();
- }
- })
- .show();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_CODE_CHECK_PASSWORD && resultCode == RESULT_OK) {
- showManagePasswordDialog();
+ if (savedInstanceState == null) {
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.settings_container, new SettingsFragment())
+ .commit();
}
- }
-
- /**
- * 加载账户设置选项
- *
- * 创建并添加账户Preference到账户分类中。
- * 点击该Preference时:
- *
- * - 如果未设置账户,显示账户选择对话框
- * - 如果已设置账户,显示确认更改账户对话框
- * - 如果正在同步,显示提示消息
- *
- *
- */
- private void loadAccountPreference() {
- mAccountCategory.removeAll();
- Preference accountPref = new Preference(this);
- final String defaultAccount = getSyncAccountName(this);
- accountPref.setTitle(getString(R.string.preferences_account_title));
- accountPref.setSummary(getString(R.string.preferences_account_summary));
- accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- public boolean onPreferenceClick(Preference preference) {
- // Google Tasks同步功能已禁用
- // if (!GTaskSyncService.isSyncing()) {
- // if (TextUtils.isEmpty(defaultAccount)) {
- // // first time to set account
- // showSelectAccountAlertDialog();
- // } else {
- // // if account has already been set, we need to promp
- // // user about risk
- // showChangeAccountConfirmAlertDialog();
- // }
- // } else {
- // Toast.makeText(NotesPreferenceActivity.this,
- // R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
- // .show();
- // }
- Toast.makeText(NotesPreferenceActivity.this,
- "Google Tasks同步功能已禁用", Toast.LENGTH_SHORT)
- .show();
- return true;
- }
- });
-
- mAccountCategory.addPreference(accountPref);
+ loadSyncButton();
}
- /**
- * 加载同步按钮和同步状态显示
- *
- * 根据当前同步状态设置按钮文本和点击事件:
- *
- * - 正在同步:显示"取消同步"按钮,点击取消同步
- * - 未同步:显示"立即同步"按钮,点击开始同步
- *
- * 同时显示最后同步时间或当前同步进度。
- *
- */
private void loadSyncButton() {
- Button syncButton = mHeaderBinding.preferenceSyncButton;
- TextView lastSyncTimeView = mHeaderBinding.prefenereceSyncStatusTextview;
+ Button syncButton = findViewById(R.id.preference_sync_button);
+ TextView lastSyncTimeView = findViewById(R.id.prefenerece_sync_status_textview);
// Google Tasks同步功能已禁用
- // set button state
- // if (GTaskSyncService.isSyncing()) {
- // syncButton.setText(getString(R.string.preferences_button_sync_cancel));
- // syncButton.setOnClickListener(new View.OnClickListener() {
- // public void onClick(View v) {
- // GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
- // }
- // });
- // } else {
- // syncButton.setText(getString(R.string.preferences_button_sync_immediately));
- // syncButton.setOnClickListener(new View.OnClickListener() {
- // public void onClick(View v) {
- // GTaskSyncService.startSync(NotesPreferenceActivity.this);
- // }
- // });
- // }
- // syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
-
- // 禁用同步按钮
syncButton.setEnabled(false);
syncButton.setText("同步功能已禁用");
- // set last sync time
- // if (GTaskSyncService.isSyncing()) {
- // lastSyncTimeView.setText(GTaskSyncService.getProgressString());
- // lastSyncTimeView.setVisibility(View.VISIBLE);
- // } else {
- // long lastSyncTime = getLastSyncTime(this);
- // if (lastSyncTime != 0) {
- // lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
- // DateFormat.format(getString(R.string.preferences_last_sync_time_format),
- // lastSyncTime)));
- // lastSyncTimeView.setVisibility(View.VISIBLE);
- // } else {
- // lastSyncTimeView.setVisibility(View.GONE);
- // }
- // }
-
lastSyncTimeView.setText("Google Tasks同步功能已禁用");
lastSyncTimeView.setVisibility(View.VISIBLE);
}
- /**
- * 刷新UI显示
- *
- * 重新加载账户设置选项和同步按钮状态。
- *
- */
- private void refreshUI() {
- loadAccountPreference();
- loadSyncButton();
- }
-
- /**
- * 显示选择账户对话框
- *
- * 显示一个对话框,列出所有可用的Google账户供用户选择。
- * 同时提供"添加账户"选项,点击后跳转到系统账户添加界面。
- *
- */
- private void showSelectAccountAlertDialog() {
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
-
- View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
- TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
- titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
- TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
- subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
-
- dialogBuilder.setCustomTitle(titleView);
- dialogBuilder.setPositiveButton(null, null);
-
- Account[] accounts = getGoogleAccounts();
- String defAccount = getSyncAccountName(this);
-
- mOriAccounts = accounts;
- mHasAddedAccount = false;
-
- if (accounts.length > 0) {
- CharSequence[] items = new CharSequence[accounts.length];
- final CharSequence[] itemMapping = items;
- int checkedItem = -1;
- int index = 0;
- for (Account account : accounts) {
- if (TextUtils.equals(account.name, defAccount)) {
- checkedItem = index;
- }
- items[index++] = account.name;
- }
- dialogBuilder.setSingleChoiceItems(items, checkedItem,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- setSyncAccount(itemMapping[which].toString());
- dialog.dismiss();
- refreshUI();
- }
- });
- }
-
- View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
- dialogBuilder.setView(addAccountView);
-
- final AlertDialog dialog = dialogBuilder.show();
- addAccountView.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- mHasAddedAccount = true;
- Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
- intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
- "gmail-ls"
- });
- startActivityForResult(intent, -1);
- dialog.dismiss();
- }
- });
- }
-
- /**
- * 显示更改账户确认对话框
- *
- * 显示一个对话框,提供三个选项:
- *
- * - 更改账户:显示账户选择对话框
- * - 移除账户:删除当前同步账户并清理相关数据
- * - 取消:关闭对话框
- *
- *
- */
- private void showChangeAccountConfirmAlertDialog() {
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
-
- View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
- TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
- titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
- getSyncAccountName(this)));
- TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
- subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
- dialogBuilder.setCustomTitle(titleView);
-
- CharSequence[] menuItemArray = new CharSequence[] {
- getString(R.string.preferences_menu_change_account),
- getString(R.string.preferences_menu_remove_account),
- getString(R.string.preferences_menu_cancel)
- };
- dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (which == 0) {
- showSelectAccountAlertDialog();
- } else if (which == 1) {
- removeSyncAccount();
- refreshUI();
- }
- }
- });
- dialogBuilder.show();
- }
-
- /**
- * 获取所有Google账户
- *
- * 从系统AccountManager中获取所有类型为"com.google"的账户。
- *
- * @return Google账户数组
- */
- private Account[] getGoogleAccounts() {
- AccountManager accountManager = AccountManager.get(this);
- return accountManager.getAccountsByType("com.google");
- }
-
- /**
- * 设置同步账户
- *
- * 保存指定的账户名称到SharedPreferences,并清理相关数据:
- *
- * - 清除最后同步时间
- * - 清除所有笔记的GTASK_ID和SYNC_ID
- *
- *
- * @param account 要设置的账户名称
- */
- private void setSyncAccount(String account) {
- if (!getSyncAccountName(this).equals(account)) {
- SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = settings.edit();
- if (account != null) {
- editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
- } else {
- editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
- }
- editor.commit();
-
- // clean up last sync time
- setLastSyncTime(this, 0);
-
- // clean up local gtask related info
- new Thread(new Runnable() {
- public void run() {
- ContentValues values = new ContentValues();
- values.put(NoteColumns.GTASK_ID, "");
- values.put(NoteColumns.SYNC_ID, 0);
- getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
- }
- }).start();
-
- Toast.makeText(NotesPreferenceActivity.this,
- getString(R.string.preferences_toast_success_set_accout, account),
- Toast.LENGTH_SHORT).show();
- }
- }
-
- /**
- * 移除同步账户
- *
- * 从SharedPreferences中删除同步账户和最后同步时间,
- * 并清理所有笔记的GTASK_ID和SYNC_ID。
- *
- */
- private void removeSyncAccount() {
- SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = settings.edit();
- if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
- editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
- }
- if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
- editor.remove(PREFERENCE_LAST_SYNC_TIME);
- }
- editor.commit();
-
- // clean up local gtask related info
- new Thread(new Runnable() {
- public void run() {
- ContentValues values = new ContentValues();
- values.put(NoteColumns.GTASK_ID, "");
- values.put(NoteColumns.SYNC_ID, 0);
- getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
- }
- }).start();
- }
-
- /**
- * 获取同步账户名称
- *
- * 从SharedPreferences中读取已设置的同步账户名称。
- *
- * @param context 上下文对象
- * @return 同步账户名称,如果未设置则返回空字符串
- */
- public static String getSyncAccountName(Context context) {
- SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
- Context.MODE_PRIVATE);
+ public static String getSyncAccountName(android.content.Context context) {
+ android.content.SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, android.content.Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
- /**
- * 设置最后同步时间
- *
- * 将指定的同步时间保存到SharedPreferences。
- *
- * @param context 上下文对象
- * @param time 同步时间戳
- */
- public static void setLastSyncTime(Context context, long time) {
- SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
- Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = settings.edit();
- editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
- editor.commit();
- }
-
- /**
- * 获取最后同步时间
- *
- * 从SharedPreferences中读取最后同步时间。
- *
- * @param context 上下文对象
- * @return 最后同步时间戳,如果未同步过则返回0
- */
- public static long getLastSyncTime(Context context) {
- SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
- Context.MODE_PRIVATE);
- return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
- }
-
- /**
- * 同步服务广播接收器
- *
- * 接收GTaskSyncService发送的广播,实时更新UI显示同步状态和进度。
- *
- */
- private class GTaskReceiver extends BroadcastReceiver {
-
- /**
- * 接收广播
- *
- * 当收到同步服务广播时,刷新UI并更新同步状态显示。
- *
- * @param context 上下文对象
- * @param intent 广播Intent
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- refreshUI();
- // Google Tasks同步功能已禁用
- // if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
- // TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
- // syncStatus.setText(intent
- // .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
- // }
-
- }
- }
-
- /**
- * 处理菜单项选择
- *
- * 处理ActionBar上的菜单项点击事件。
- * 当点击返回按钮时,返回到笔记列表界面。
- *
- * @param item 被点击的菜单项
- * @return true表示已处理,false表示未处理
- */
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/SettingsFragment.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/SettingsFragment.java
new file mode 100644
index 0000000..de22828
--- /dev/null
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/SettingsFragment.java
@@ -0,0 +1,117 @@
+package net.micode.notes.ui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import net.micode.notes.R;
+import net.micode.notes.tool.SecurityManager;
+
+import net.micode.notes.data.ThemeRepository;
+import androidx.preference.ListPreference;
+
+import static android.app.Activity.RESULT_OK;
+
+public class SettingsFragment extends PreferenceFragmentCompat {
+ public static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
+ public static final String PREFERENCE_SECURITY_KEY = "pref_key_security";
+ public static final String PREFERENCE_THEME_MODE = "pref_theme_mode";
+ public static final int REQUEST_CODE_CHECK_PASSWORD = 104;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.preferences, rootKey);
+ loadThemePreference();
+ loadSecurityPreference();
+ loadAccountPreference();
+ }
+
+ private void loadThemePreference() {
+ ListPreference themePref = findPreference(PREFERENCE_THEME_MODE);
+ if (themePref != null) {
+ themePref.setOnPreferenceChangeListener((preference, newValue) -> {
+ ThemeRepository.applyTheme((String) newValue);
+ return true;
+ });
+ }
+ }
+
+ private void loadSecurityPreference() {
+ Preference securityPref = findPreference(PREFERENCE_SECURITY_KEY);
+ if (securityPref != null) {
+ securityPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (!SecurityManager.getInstance(getActivity()).isPasswordSet()) {
+ showSetPasswordDialog();
+ } else {
+ Intent intent = new Intent(getActivity(), PasswordActivity.class);
+ intent.setAction(PasswordActivity.ACTION_CHECK_PASSWORD);
+ startActivityForResult(intent, REQUEST_CODE_CHECK_PASSWORD);
+ }
+ return true;
+ }
+ });
+ }
+ }
+
+ private void loadAccountPreference() {
+ androidx.preference.PreferenceCategory accountCategory = findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
+ if (accountCategory != null) {
+ accountCategory.removeAll();
+ Preference accountPref = new Preference(getContext());
+ accountPref.setTitle(getString(R.string.preferences_account_title));
+ accountPref.setSummary(getString(R.string.preferences_account_summary));
+ accountPref.setOnPreferenceClickListener(preference -> {
+ Toast.makeText(getActivity(), "Google Tasks同步功能已禁用", Toast.LENGTH_SHORT).show();
+ return true;
+ });
+ accountCategory.addPreference(accountPref);
+ }
+ }
+
+ private void showSetPasswordDialog() {
+ new AlertDialog.Builder(getActivity())
+ .setTitle("设置密码")
+ .setItems(new String[]{"数字锁", "手势锁"}, (dialog, which) -> {
+ int type = (which == 0) ? SecurityManager.TYPE_PIN : SecurityManager.TYPE_PATTERN;
+ Intent intent = new Intent(getActivity(), PasswordActivity.class);
+ intent.setAction(PasswordActivity.ACTION_SETUP_PASSWORD);
+ intent.putExtra(PasswordActivity.EXTRA_PASSWORD_TYPE, type);
+ startActivity(intent);
+ })
+ .show();
+ }
+
+ private void showManagePasswordDialog() {
+ new AlertDialog.Builder(getActivity())
+ .setTitle("管理密码")
+ .setItems(new String[]{"更改密码", "取消密码"}, (dialog, which) -> {
+ if (which == 0) { // Change
+ showSetPasswordDialog();
+ } else { // Remove
+ SecurityManager.getInstance(getActivity()).removePassword();
+ Toast.makeText(getActivity(), "密码已取消", Toast.LENGTH_SHORT).show();
+ }
+ })
+ .show();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_CHECK_PASSWORD && resultCode == RESULT_OK) {
+ showManagePasswordDialog();
+ }
+ }
+}
diff --git a/src/Notesmaster/app/src/main/res/color/primary_text_dark.xml b/src/Notesmaster/app/src/main/res/color/primary_text_dark.xml
deleted file mode 100644
index 7c85459..0000000
--- a/src/Notesmaster/app/src/main/res/color/primary_text_dark.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Notesmaster/app/src/main/res/color/secondary_text_dark.xml b/src/Notesmaster/app/src/main/res/color/secondary_text_dark.xml
deleted file mode 100644
index c1c2384..0000000
--- a/src/Notesmaster/app/src/main/res/color/secondary_text_dark.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_bold.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_bold.xml
new file mode 100644
index 0000000..79236d0
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_bold.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_code.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_code.xml
new file mode 100644
index 0000000..47c222b
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_code.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_color_fill.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_color_fill.xml
new file mode 100644
index 0000000..e2d6c56
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_color_fill.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_color_text.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_color_text.xml
new file mode 100644
index 0000000..db80539
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_color_text.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_header.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_header.xml
new file mode 100644
index 0000000..24f4c33
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_header.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_italic.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_italic.xml
new file mode 100644
index 0000000..1f8c0a1
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_italic.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_list_bulleted.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_list_bulleted.xml
new file mode 100644
index 0000000..9d3de50
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_list_bulleted.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_quote.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_quote.xml
new file mode 100644
index 0000000..304bfdc
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_quote.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_strikethrough.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_strikethrough.xml
new file mode 100644
index 0000000..29f1677
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_strikethrough.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_format_underline.xml b/src/Notesmaster/app/src/main/res/drawable/ic_format_underline.xml
new file mode 100644
index 0000000..b275f43
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_format_underline.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_image.xml b/src/Notesmaster/app/src/main/res/drawable/ic_image.xml
new file mode 100644
index 0000000..0da450d
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_image.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_insert_divider.xml b/src/Notesmaster/app/src/main/res/drawable/ic_insert_divider.xml
new file mode 100644
index 0000000..a64b853
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_insert_divider.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_insert_link.xml b/src/Notesmaster/app/src/main/res/drawable/ic_insert_link.xml
new file mode 100644
index 0000000..b862207
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_insert_link.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_menu_redo.xml b/src/Notesmaster/app/src/main/res/drawable/ic_menu_redo.xml
new file mode 100644
index 0000000..e6ac8a5
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_menu_redo.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_menu_rich_text.xml b/src/Notesmaster/app/src/main/res/drawable/ic_menu_rich_text.xml
new file mode 100644
index 0000000..751b78c
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_menu_rich_text.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_menu_undo.xml b/src/Notesmaster/app/src/main/res/drawable/ic_menu_undo.xml
new file mode 100644
index 0000000..aac484f
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_menu_undo.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/src/Notesmaster/app/src/main/res/drawable/ic_palette.xml b/src/Notesmaster/app/src/main/res/drawable/ic_palette.xml
new file mode 100644
index 0000000..dbcb253
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/drawable/ic_palette.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/layout/activity_note_search.xml b/src/Notesmaster/app/src/main/res/layout/activity_note_search.xml
new file mode 100644
index 0000000..6fc7a8a
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/layout/activity_note_search.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/layout/activity_settings.xml b/src/Notesmaster/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..791a380
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/layout/dialog_color_picker.xml b/src/Notesmaster/app/src/main/res/layout/dialog_color_picker.xml
new file mode 100644
index 0000000..5d93b9b
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/layout/dialog_color_picker.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/layout/note_color_item.xml b/src/Notesmaster/app/src/main/res/layout/note_color_item.xml
new file mode 100644
index 0000000..39a650d
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/layout/note_color_item.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
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 b0b9e28..aba7781 100644
--- a/src/Notesmaster/app/src/main/res/layout/note_edit.xml
+++ b/src/Notesmaster/app/src/main/res/layout/note_edit.xml
@@ -170,112 +170,16 @@
android:layout_marginTop="30dp"
android:layout_marginRight="8dp"
android:layout_gravity="top|right"
- android:visibility="gone">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:visibility="gone"
+ android:orientation="horizontal">
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/layout/note_list.xml b/src/Notesmaster/app/src/main/res/layout/note_list.xml
index 21c7ba8..3d88391 100644
--- a/src/Notesmaster/app/src/main/res/layout/note_list.xml
+++ b/src/Notesmaster/app/src/main/res/layout/note_list.xml
@@ -22,7 +22,7 @@
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/list_background">
+ android:background="@color/background_color">
diff --git a/src/Notesmaster/app/src/main/res/layout/search_history_item.xml b/src/Notesmaster/app/src/main/res/layout/search_history_item.xml
new file mode 100644
index 0000000..b506853
--- /dev/null
+++ b/src/Notesmaster/app/src/main/res/layout/search_history_item.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesmaster/app/src/main/res/menu/note_edit.xml b/src/Notesmaster/app/src/main/res/menu/note_edit.xml
index 35cacd1..bff7ff4 100644
--- a/src/Notesmaster/app/src/main/res/menu/note_edit.xml
+++ b/src/Notesmaster/app/src/main/res/menu/note_edit.xml
@@ -16,12 +16,35 @@
-->