From fcb9a6b8e5d2c8948e8955310be0354ff2458524 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=92=8B=E5=A4=A9=E7=BF=94?=
Date: Tue, 27 Jan 2026 13:15:21 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=EF=BC=9A=201.=E6=90=9C=E7=B4=A2=202.=E6=92=A4=E5=9B=9E=203.the?=
=?UTF-8?q?me=E8=87=AA=E5=AE=9A=E4=B9=89=204.=E5=AF=8C=E6=96=87=E6=9C=AC?=
=?UTF-8?q?=E7=BC=96=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../app/src/main/AndroidManifest.xml | 7 +-
.../net/micode/notes/model/WorkingNote.java | 21 +
.../net/micode/notes/tool/ResourceParser.java | 40 ++
.../net/micode/notes/ui/NoteEditActivity.java | 494 +++++++++++---
.../net/micode/notes/ui/NotesListItem.java | 31 +-
.../notes/ui/NotesPreferenceActivity.java | 639 +-----------------
.../app/src/main/res/layout/note_edit.xml | 262 ++++---
.../app/src/main/res/menu/note_edit.xml | 25 +-
.../src/main/res/values-zh-rCN/strings.xml | 7 +
.../app/src/main/res/values/arrays.xml | 12 +
.../app/src/main/res/values/colors.xml | 17 +
.../app/src/main/res/values/strings.xml | 10 +
.../app/src/main/res/values/styles.xml | 10 +-
.../app/src/main/res/values/themes.xml | 7 +-
.../app/src/main/res/xml/preferences.xml | 18 +
15 files changed, 779 insertions(+), 821 deletions(-)
diff --git a/src/Notesmaster/app/src/main/AndroidManifest.xml b/src/Notesmaster/app/src/main/AndroidManifest.xml
index 68fb7ab..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" >
@@ -180,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/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/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/NotesListItem.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesListItem.java
index ddfe1ce..9a6d238 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
@@ -28,6 +28,7 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
+import net.micode.notes.data.FontManager;
/**
@@ -80,6 +81,7 @@ public class NotesListItem extends LinearLayout {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
+ FontManager.getInstance(context).applyFont(mTitle);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
@@ -87,6 +89,7 @@ public class NotesListItem extends LinearLayout {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
+ FontManager.getInstance(context).applyFont(mTitle);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
@@ -97,6 +100,7 @@ public class NotesListItem extends LinearLayout {
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
+ FontManager.getInstance(context).applyFont(mTitle);
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
@@ -131,18 +135,35 @@ public class NotesListItem extends LinearLayout {
*/
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
+ int resId;
if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
- setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
+ resId = NoteItemBgResources.getNoteBgSingleRes(id);
} else if (data.isLast()) {
- setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
+ resId = NoteItemBgResources.getNoteBgLastRes(id);
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
- setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
+ resId = NoteItemBgResources.getNoteBgFirstRes(id);
} else {
- setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
+ resId = NoteItemBgResources.getNoteBgNormalRes(id);
}
} else {
- setBackgroundResource(NoteItemBgResources.getFolderBgRes());
+ resId = NoteItemBgResources.getFolderBgRes();
+ }
+
+ setBackgroundResource(resId);
+
+ // Apply tint for new colors
+ if (data.getType() == Notes.TYPE_NOTE && (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);
+ getBackground().setTintMode(android.graphics.PorterDuff.Mode.MULTIPLY);
+ }
+ } else {
+ // Ensure no tint for legacy colors (if view is recycled)
+ if (getBackground() != null) {
+ getBackground().clearColorFilter();
+ }
}
}
diff --git a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java
index 368f0d0..de53616 100644
--- a/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java
+++ b/src/Notesmaster/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java
@@ -1,659 +1,60 @@
-/*
- * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package net.micode.notes.ui;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.ActionBar;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceCategory;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
import net.micode.notes.R;
-import net.micode.notes.data.Notes;
-import net.micode.notes.data.Notes.NoteColumns;
-import net.micode.notes.databinding.SettingsHeaderBinding;
-// Google Tasks同步功能已禁用
-// import net.micode.notes.gtask.remote.GTaskSyncService;
-import net.micode.notes.tool.SecurityManager;
-import net.micode.notes.ui.PasswordActivity;
+public class NotesPreferenceActivity extends AppCompatActivity {
-/**
- * 设置界面Activity
- *
- * 该Activity用于管理应用的各种设置,主要包括:
- *
- * - Google Tasks同步账户的设置和管理
- * - 同步状态显示和手动同步控制
- * - 背景颜色随机显示设置
- *
- *
- *
- * 该类继承自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/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/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 @@
-->