From 5cde97fe734877330afc402b2c48b62185699f7f Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Thu, 23 Jan 2020 22:14:35 +0100 Subject: [PATCH] #439 Allow Rendering of any markdown file Create NoteReadonlyFragment to keep NotePreviewFragment clean --- .../android/activity/EditNoteActivity.java | 12 +- .../android/fragment/BaseNoteFragment.java | 4 +- .../android/fragment/NotePreviewFragment.java | 114 ++++++------ .../fragment/NoteReadonlyFragment.java | 176 ++++++++++++++++++ 4 files changed, 233 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java index e264343e..c7853911 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java @@ -13,7 +13,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import java.io.BufferedReader; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -24,6 +23,7 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.fragment.BaseNoteFragment; import it.niedermann.owncloud.notes.android.fragment.NoteEditFragment; import it.niedermann.owncloud.notes.android.fragment.NotePreviewFragment; +import it.niedermann.owncloud.notes.android.fragment.NoteReadonlyFragment; import it.niedermann.owncloud.notes.model.Category; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; @@ -179,21 +179,17 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm Intent intent = getIntent(); StringBuilder text = new StringBuilder(); try { - InputStream inputStream = getContentResolver().openInputStream(intent.getData()); - BufferedReader r = new BufferedReader(new InputStreamReader(inputStream)); + InputStream inputStream = getContentResolver().openInputStream(Objects.requireNonNull(intent.getData())); + BufferedReader r = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream))); String mLine; while ((mLine = r.readLine()) != null) { text.append(mLine).append('\n'); } - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } - Log.v("YEY", text.toString()); - - fragment = NotePreviewFragment.newReadonlyInstance(text.toString()); + fragment = NoteReadonlyFragment.newInstance(text.toString()); getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit(); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java index 0c75056c..62a496b5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java @@ -45,7 +45,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo private static final String TAG = BaseNoteFragment.class.getSimpleName(); - private static final int MENU_ID_PIN = -1; + protected static final int MENU_ID_PIN = -1; public static final String PARAM_NOTE_ID = "noteId"; public static final String PARAM_ACCOUNT_ID = "accountId"; public static final String PARAM_CONTENT = "content"; @@ -62,7 +62,6 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo private NoteFragmentListener listener; boolean isNew = true; - boolean isReadonly = false; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -93,7 +92,6 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo if (content == null) { throw new IllegalArgumentException(PARAM_NOTE_ID + " is not given, argument " + PARAM_NEWNOTE + " is missing and " + PARAM_CONTENT + " is missing."); } else { - isReadonly = true; note = new DBNote(-1, -1, null, NoteUtil.generateNonEmptyNoteTitle(content, getContext()), content, false, "", null, DBStatus.VOID, -1, ""); } } else { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java index 4963a215..8e45c93a 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java @@ -25,7 +25,6 @@ import androidx.core.view.ViewCompat; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.yydcdut.markdown.MarkdownConfiguration; import com.yydcdut.markdown.MarkdownProcessor; import com.yydcdut.markdown.MarkdownTextView; import com.yydcdut.markdown.syntax.text.TextFactory; @@ -64,14 +63,6 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment { @BindView(R.id.single_note_content) MarkdownTextView noteContent; - public static NotePreviewFragment newReadonlyInstance(String content) { - NotePreviewFragment f = new NotePreviewFragment(); - Bundle b = new Bundle(); - b.putString(PARAM_CONTENT, content); - f.setArguments(b); - return f; - } - public static NotePreviewFragment newInstance(long accountId, long noteId) { NotePreviewFragment f = new NotePreviewFragment(); Bundle b = new Bundle(); @@ -122,62 +113,61 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment { ButterKnife.bind(this, Objects.requireNonNull(getView())); markdownProcessor = new MarkdownProcessor(Objects.requireNonNull(getActivity())); markdownProcessor.factory(TextFactory.create()); - MarkdownConfiguration.Builder config = MarkDownUtil.getMarkDownConfiguration(noteContent.getContext()) - .setOnLinkClickCallback((view, link) -> { - if (NoteLinksUtils.isNoteLink(link)) { - long noteRemoteId = NoteLinksUtils.extractNoteRemoteId(link); - long noteLocalId = db.getLocalIdByRemoteId(this.note.getAccountId(), noteRemoteId); - Intent intent = new Intent(getActivity().getApplicationContext(), EditNoteActivity.class); - intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, noteLocalId); - startActivity(intent); - } else { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); - startActivity(browserIntent); - } - }); - if (!isReadonly) { - config.setOnTodoClickCallback((view, line, lineNumber) -> { - try { - String[] lines = TextUtils.split(note.getContent(), "\\r?\\n"); - /* - * Workaround for RxMarkdown-bug: - * When (un)checking a checkbox in a note which contains code-blocks, the "`"-characters get stripped out in the TextView and therefore the given lineNumber is wrong - * Find number of lines starting with ``` before lineNumber - */ - for (int i = 0; i < lines.length; i++) { - if (lines[i].startsWith("```")) { - lineNumber++; - } - if (i == lineNumber) { - break; - } - } + markdownProcessor.config( + MarkDownUtil.getMarkDownConfiguration(noteContent.getContext()) + .setOnTodoClickCallback((view, line, lineNumber) -> { + try { + String[] lines = TextUtils.split(note.getContent(), "\\r?\\n"); + /* + * Workaround for RxMarkdown-bug: + * When (un)checking a checkbox in a note which contains code-blocks, the "`"-characters get stripped out in the TextView and therefore the given lineNumber is wrong + * Find number of lines starting with ``` before lineNumber + */ + for (int i = 0; i < lines.length; i++) { + if (lines[i].startsWith("```")) { + lineNumber++; + } + if (i == lineNumber) { + break; + } + } - /* - * Workaround for multiple RxMarkdown-bugs: - * When (un)checking a checkbox which is in the last line, every time it gets toggled, the last character of the line gets lost. - * When (un)checking a checkbox, every markdown gets stripped in the given line argument - */ - if (lines[lineNumber].startsWith("- [ ]") || lines[lineNumber].startsWith("* [ ]")) { - lines[lineNumber] = lines[lineNumber].replace("- [ ]", "- [x]"); - lines[lineNumber] = lines[lineNumber].replace("* [ ]", "* [x]"); + /* + * Workaround for multiple RxMarkdown-bugs: + * When (un)checking a checkbox which is in the last line, every time it gets toggled, the last character of the line gets lost. + * When (un)checking a checkbox, every markdown gets stripped in the given line argument + */ + if (lines[lineNumber].startsWith("- [ ]") || lines[lineNumber].startsWith("* [ ]")) { + lines[lineNumber] = lines[lineNumber].replace("- [ ]", "- [x]"); + lines[lineNumber] = lines[lineNumber].replace("* [ ]", "* [x]"); + } else { + lines[lineNumber] = lines[lineNumber].replace("- [x]", "- [ ]"); + lines[lineNumber] = lines[lineNumber].replace("* [x]", "* [ ]"); + } + + changedText = TextUtils.join("\n", lines); + noteContent.setText(markdownProcessor.parse(changedText)); + saveNote(null); + } catch (IndexOutOfBoundsException e) { + Toast.makeText(getActivity(), R.string.checkbox_could_not_be_toggled, Toast.LENGTH_SHORT).show(); + e.printStackTrace(); + } + return line; + } + ) + .setOnLinkClickCallback((view, link) -> { + if (NoteLinksUtils.isNoteLink(link)) { + long noteRemoteId = NoteLinksUtils.extractNoteRemoteId(link); + long noteLocalId = db.getLocalIdByRemoteId(this.note.getAccountId(), noteRemoteId); + Intent intent = new Intent(getActivity().getApplicationContext(), EditNoteActivity.class); + intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, noteLocalId); + startActivity(intent); } else { - lines[lineNumber] = lines[lineNumber].replace("- [x]", "- [ ]"); - lines[lineNumber] = lines[lineNumber].replace("* [x]", "* [ ]"); + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); + startActivity(browserIntent); } - - changedText = TextUtils.join("\n", lines); - noteContent.setText(markdownProcessor.parse(changedText)); - saveNote(null); - } catch (IndexOutOfBoundsException e) { - Toast.makeText(getActivity(), R.string.checkbox_could_not_be_toggled, Toast.LENGTH_SHORT).show(); - e.printStackTrace(); - } - return line; - } - ); - } - markdownProcessor.config(config.build()); + }) + .build()); try { noteContent.setText(markdownProcessor.parse(NoteLinksUtils.replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); onResume(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java new file mode 100644 index 00000000..88477620 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java @@ -0,0 +1,176 @@ +package it.niedermann.owncloud.notes.android.fragment; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Layout; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.yydcdut.markdown.MarkdownProcessor; +import com.yydcdut.markdown.MarkdownTextView; +import com.yydcdut.markdown.syntax.text.TextFactory; + +import java.util.Objects; + +import butterknife.BindView; +import butterknife.ButterKnife; +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; +import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; +import it.niedermann.owncloud.notes.util.DisplayUtils; +import it.niedermann.owncloud.notes.util.ICallback; +import it.niedermann.owncloud.notes.util.MarkDownUtil; +import it.niedermann.owncloud.notes.util.NoteLinksUtils; + +public class NoteReadonlyFragment extends SearchableBaseNoteFragment { + + private MarkdownProcessor markdownProcessor; + + @BindView(R.id.swiperefreshlayout) + SwipeRefreshLayout swipeRefreshLayout; + + @BindView(R.id.scrollView) + ScrollView scrollView; + + @BindView(R.id.searchNext) + FloatingActionButton searchNext; + + @BindView(R.id.searchPrev) + FloatingActionButton searchPrev; + + @BindView(R.id.single_note_content) + MarkdownTextView noteContent; + + public static NoteReadonlyFragment newInstance(String content) { + NoteReadonlyFragment f = new NoteReadonlyFragment(); + Bundle b = new Bundle(); + b.putString(PARAM_CONTENT, content); + f.setArguments(b); + return f; + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.menu_favorite).setVisible(false); + menu.findItem(R.id.menu_edit).setVisible(false); + menu.findItem(R.id.menu_preview).setVisible(false); + menu.findItem(R.id.menu_cancel).setVisible(false); + menu.findItem(R.id.menu_delete).setVisible(false); + menu.findItem(R.id.menu_share).setVisible(false); + menu.findItem(R.id.menu_move).setVisible(false); + menu.findItem(R.id.menu_category).setVisible(false); + menu.findItem(MENU_ID_PIN).setVisible(false); + } + + @Override + public ScrollView getScrollView() { + return scrollView; + } + + @Override + protected FloatingActionButton getSearchNextButton() { + return searchNext; + } + + @Override + protected FloatingActionButton getSearchPrevButton() { + return searchPrev; + } + + @Override + protected Layout getLayout() { + noteContent.onPreDraw(); + return noteContent.getLayout(); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup + container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_note_preview, container, false); + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + ButterKnife.bind(this, Objects.requireNonNull(getView())); + markdownProcessor = new MarkdownProcessor(Objects.requireNonNull(getActivity())); + markdownProcessor.factory(TextFactory.create()); + markdownProcessor.config( + MarkDownUtil.getMarkDownConfiguration(noteContent.getContext()) + .setOnLinkClickCallback((view, link) -> { + if (NoteLinksUtils.isNoteLink(link)) { + long noteRemoteId = NoteLinksUtils.extractNoteRemoteId(link); + long noteLocalId = db.getLocalIdByRemoteId(this.note.getAccountId(), noteRemoteId); + Intent intent = new Intent(getActivity().getApplicationContext(), EditNoteActivity.class); + intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, noteLocalId); + startActivity(intent); + } else { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); + startActivity(browserIntent); + } + }) + .build()); + try { + noteContent.setText(markdownProcessor.parse(note.getContent())); + onResume(); + } catch (StringIndexOutOfBoundsException e) { + // Workaround for RxMarkdown: https://github.com/stefan-niedermann/nextcloud-notes/issues/668 + Toast.makeText(noteContent.getContext(), R.string.could_not_load_preview_two_digit_numbered_list, Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } + noteContent.setMovementMethod(LinkMovementMethod.getInstance()); + + db = NoteSQLiteOpenHelper.getInstance(getActivity()); + swipeRefreshLayout.setEnabled(false); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(Objects.requireNonNull(getActivity()).getApplicationContext()); + noteContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(sp)); + if (sp.getBoolean(getString(R.string.pref_key_font), false)) { + noteContent.setTypeface(Typeface.MONOSPACE); + } + } + + @Override + public void onCloseNote() { + // Do nothing + } + + @Override + protected void saveNote(@Nullable ICallback callback) { + // Do nothing + } + + @Override + protected void colorWithText(String newText) { + if (noteContent != null && ViewCompat.isAttachedToWindow(noteContent)) { + noteContent.setText(markdownProcessor.parse(DisplayUtils.searchAndColor(getContent(), new SpannableString + (getContent()), newText, getResources().getColor(R.color.primary))), + TextView.BufferType.SPANNABLE); + } + } + + @Override + protected String getContent() { + return note.getContent(); + } +}