diff --git a/app/src/main/java/com/git/amarradi/leafpad/NoteEditActivity.java b/app/src/main/java/com/git/amarradi/leafpad/NoteEditActivity.java index ef4a8e019..6b34db297 100755 --- a/app/src/main/java/com/git/amarradi/leafpad/NoteEditActivity.java +++ b/app/src/main/java/com/git/amarradi/leafpad/NoteEditActivity.java @@ -31,6 +31,9 @@ import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.textfield.TextInputLayout; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + import java.util.List; import java.util.Objects; @@ -38,15 +41,19 @@ public class NoteEditActivity extends AppCompatActivity { private EditText titleEdit; private EditText bodyEdit; + private TextView previewBody; + private Note note; private NoteViewModel noteViewModel; private MaterialToolbar toolbar; private Resources res; private boolean shouldPersistOnPause = true; private boolean isNoteDeleted = false; private NestedScrollView bodyScroll; + private boolean isNewNote = false; private boolean fromSearch = false; private boolean isUIConfigured = false; + private MenuItem saveMenuItem; @SuppressLint("MissingInflatedId") @@ -88,7 +95,7 @@ protected void onCreate(Bundle savedInstanceState) { } handleIntent(getIntent()); fromSearch = getIntent().getBooleanExtra("fromSearch", false); - observeNote(); + observeViewModel(); View rootEdit = findViewById(R.id.all); View toolbar = findViewById(R.id.toolbar); @@ -170,35 +177,50 @@ private void configureUIFromNote(Note note) { } private void initViews() { - TextInputLayout titleLayout = findViewById(R.id.default_text_input_layout); - TextInputLayout bodyLayout = findViewById(R.id.body_text_input_layout); titleEdit = findViewById(R.id.title_edit); bodyEdit = findViewById(R.id.body_edit); + + previewBody = findViewById(R.id.preview_body); // Initialize TextView bodyScroll = findViewById(R.id.body_scroll); - bodyEdit.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + // clickable links on preview + previewBody.setMovementMethod(LinkMovementMethod.getInstance()); + + // one TextWatcher to update ViewModel on real time + TextWatcher textWatcher = new TextWatcher() { @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { } + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override - public void afterTextChanged(Editable s) { - bodyEdit.post(() -> scrollToCursor()); + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (noteViewModel.getSelectedNote().getValue() != null) { + if (getCurrentFocus() == titleEdit) { + noteViewModel.updateNoteTitle(s.toString()); + } else if (getCurrentFocus() == bodyEdit) { + noteViewModel.updateNoteBody(s.toString()); + } + } } - }); - bodyEdit.setOnFocusChangeListener((v, hasFocus) -> { - if (hasFocus) { - bodyEdit.postDelayed(this::scrollToCursor, 250); + @Override + public void afterTextChanged(Editable s) { + if (getCurrentFocus() == bodyEdit) { + // Stelle sicher, dass Cursor immer sichtbar ist + bodyEdit.post(() -> { + int selection = bodyEdit.getSelectionStart(); + Layout layout = bodyEdit.getLayout(); + if (layout != null && selection > 0) { + int line = layout.getLineForOffset(selection); + int y = layout.getLineBottom(line); + bodyScroll.smoothScrollTo(0, y); + } + }); + } } - }); - - bodyEdit.setOnClickListener(v -> { - bodyEdit.postDelayed(this::scrollToCursor, 250); - }); + }; - titleLayout.setHintEnabled(false); - bodyLayout.setHintEnabled(false); + titleEdit.addTextChangedListener(textWatcher); + bodyEdit.addTextChangedListener(textWatcher); } private void scrollToCursor() { @@ -216,15 +238,50 @@ private boolean isNewEntry(Note note) { note.getBody() == null || note.getBody().isEmpty()); } + private void observeViewModel() { + // Observer for selected note (initial launch) + noteViewModel.getSelectedNote().observe(this, currentNote -> { + if (currentNote != null && !isUIConfigured) { + this.note = currentNote; + titleEdit.setText(currentNote.getTitle()); + bodyEdit.setText(currentNote.getBody()); + isUIConfigured = true; // Prevents reload when rotating screen + invalidateOptionsMenu(); + } + }); + + // observer for state of the preview + noteViewModel.isPreviewActive().observe(this, isActive -> { + bodyEdit.setVisibility(isActive ? View.GONE : View.VISIBLE); + previewBody.setVisibility(isActive ? View.VISIBLE : View.GONE); + + // if preview is active, force refresh of Spanned + if (isActive) { + noteViewModel.getSelectedNote().setValue(noteViewModel.getSelectedNote().getValue()); + } + + invalidateOptionsMenu(); // refresh menu icon + }); + + // Observer for parsed body (updates TextView of the preview) + noteViewModel.parsedBodyAsSpanned.observe(this, spanned -> { + previewBody.setText(spanned); + }); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_note_edit, menu); - saveMenuItem = menu.findItem(R.id.action_save); - if (saveMenuItem != null) { - saveMenuItem.setEnabled(false); // Initial disabled + + // Updates preview icon depending on state + MenuItem previewItem = menu.findItem(R.id.action_preview); + if (noteViewModel.isPreviewActive().getValue() != null && noteViewModel.isPreviewActive().getValue()) { + previewItem.setIcon(R.drawable.ic_edit); // Changes to icon "edit" + } else { + previewItem.setIcon(R.drawable.ic_preview); // icon "preview" } - Note current = noteViewModel.getSelectedNote().getValue(); - if (current != null) { + + if (note != null) { MenuItem recipeItem = menu.findItem(R.id.action_recipe); boolean isRecipe = current.getCategory() != null && current.getCategory().equals(res.getStringArray(R.array.category)[0]); @@ -242,6 +299,12 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); + + if (id == R.id.action_preview) { + noteViewModel.togglePreview(); + return true; + } + switch (id) { case R.id.action_recipe: { Note current = noteViewModel.getSelectedNote().getValue(); diff --git a/app/src/main/java/com/git/amarradi/leafpad/helper/MarkdownParser.java b/app/src/main/java/com/git/amarradi/leafpad/helper/MarkdownParser.java new file mode 100644 index 000000000..293744706 --- /dev/null +++ b/app/src/main/java/com/git/amarradi/leafpad/helper/MarkdownParser.java @@ -0,0 +1,33 @@ +package com.git.amarradi.leafpad.helper; + +public class MarkdownParser { + + public static String parse(String rawText) { + StringBuilder result = new StringBuilder(); + String[] lines = rawText.split("\n"); + + for (String line : lines) { + String parsedLine = line; + + // Heading: "# Heading" + if (parsedLine.startsWith("# ")) { + parsedLine = "