Loading app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java +7 −7 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; Loading @@ -35,12 +34,14 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.FragmentNoteEditBinding; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.MarkDownUtil; import it.niedermann.owncloud.notes.util.NotesTextWatcher; import it.niedermann.owncloud.notes.util.format.ContextBasedFormattingCallback; import it.niedermann.owncloud.notes.util.format.ContextBasedRangeFormattingCallback; import static androidx.core.view.ViewCompat.isAttachedToWindow; import static it.niedermann.owncloud.notes.util.DisplayUtils.searchAndColor; public class NoteEditFragment extends SearchableBaseNoteFragment { private static final String LOG_TAG_AUTOSAVE = "AutoSave"; Loading Loading @@ -238,11 +239,10 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { } @Override protected void colorWithText(String newText) { if (binding != null && ViewCompat.isAttachedToWindow(binding.editContent)) { binding.editContent.setText(DisplayUtils.searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary)), TextView.BufferType.SPANNABLE); protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if (binding != null && isAttachedToWindow(binding.editContent)) { binding.editContent.clearFocus(); binding.editContent.setText(searchAndColor(new SpannableString(getContent()), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } } app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +3 −3 Original line number Diff line number Diff line Loading @@ -183,10 +183,10 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } @Override protected void colorWithText(String newText) { protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if (binding != null && ViewCompat.isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(parseCompat(markdownProcessor, searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary))), binding.singleNoteContent.setText( searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } Loading app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java +5 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; Loading @@ -31,10 +30,11 @@ import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.MarkDownUtil; import it.niedermann.owncloud.notes.util.NoteLinksUtils; import static androidx.core.view.ViewCompat.isAttachedToWindow; import static it.niedermann.owncloud.notes.util.DisplayUtils.searchAndColor; import static it.niedermann.owncloud.notes.util.MarkDownUtil.parseCompat; public class NoteReadonlyFragment extends SearchableBaseNoteFragment { Loading Loading @@ -147,11 +147,9 @@ public class NoteReadonlyFragment extends SearchableBaseNoteFragment { } @Override protected void colorWithText(String newText) { if ((binding != null) && ViewCompat.isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(parseCompat(markdownProcessor, DisplayUtils.searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary))), TextView.BufferType.SPANNABLE); protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if ((binding != null) && isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } Loading app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java +7 −5 Original line number Diff line number Diff line Loading @@ -68,12 +68,12 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (currentVisibility != oldVisibility) { if (currentVisibility != View.VISIBLE) { colorWithText(""); colorWithText("", null); searchQuery = ""; hideSearchFabs(); } else { jumpToOccurrence(); colorWithText(searchQuery); colorWithText(searchQuery, null); occurrenceCount = countOccurrences(getContent(), searchQuery); showSearchFabs(); } Loading @@ -90,6 +90,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (next != null) { next.setOnClickListener(v -> { currentOccurrence++; colorWithText(searchView.getQuery().toString(), currentOccurrence); jumpToOccurrence(); }); } Loading @@ -97,6 +98,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (prev != null) { prev.setOnClickListener(v -> { currentOccurrence--; colorWithText(searchView.getQuery().toString(), currentOccurrence); jumpToOccurrence(); }); } Loading @@ -120,7 +122,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } currentOccurrence = 1; jumpToOccurrence(); colorWithText(searchQuery); colorWithText(searchQuery, currentOccurrence); return true; } }); Loading @@ -136,7 +138,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } } protected abstract void colorWithText(String newText); protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current); protected abstract ScrollView getScrollView(); Loading Loading @@ -194,7 +196,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { int numberLine = layout.getLineForOffset(textUntilFirstOccurrence.length()); if (numberLine >= 0) { getScrollView().smoothScrollTo(0, layout.getLineTop(numberLine)); getScrollView().post(() -> getScrollView().smoothScrollTo(0, layout.getLineTop(numberLine))); } } } Loading app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java +50 −11 Original line number Diff line number Diff line Loading @@ -19,29 +19,34 @@ */ package it.niedermann.owncloud.notes.util; import android.graphics.Typeface; import android.content.Context; import android.graphics.Color; import android.text.Spannable; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.MetricAffectingSpan; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; public class DisplayUtils { private DisplayUtils() { } public static Spannable searchAndColor(String text, Spannable spannable, String searchText, @ColorInt int color) { public static Spannable searchAndColor(Spannable spannable, CharSequence searchText, Context context, @Nullable Integer current) { CharSequence text = spannable.toString(); Object[] spansToRemove = spannable.getSpans(0, text.length(), Object.class); for (Object span : spansToRemove) { if(span instanceof CharacterStyle) if (span instanceof SearchSpan) spannable.removeSpan(span); } Loading @@ -49,18 +54,52 @@ public class DisplayUtils { return spannable; } Matcher m = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) Matcher m = Pattern.compile(searchText.toString(), Pattern.CASE_INSENSITIVE | Pattern.LITERAL) .matcher(text); int i = 1; while (m.find()) { int start = m.start(); int end = m.end(); spannable.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new SearchSpan(context, (current != null && i == current)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); i++; } return spannable; } static class SearchSpan extends MetricAffectingSpan { private final boolean current; private final int bgColorPrimary; private final int bgColorSecondary; SearchSpan(Context context, boolean current) { this.current = current; this.bgColorPrimary = context.getResources().getColor(R.color.bg_search_primary); this.bgColorSecondary = context.getResources().getColor(R.color.bg_search_secondary); } @Override public void updateDrawState(TextPaint tp) { tp.bgColor = current ? bgColorPrimary : bgColorSecondary; tp.setColor(current ? getForeground(Integer.toHexString(tp.bgColor)) : bgColorPrimary); tp.setFakeBoldText(true); } @Override public void updateMeasureState(@NonNull TextPaint tp) { tp.setFakeBoldText(true); } private static @ColorInt int getForeground(String backgroundColorHex) { return ((float) ( 0.2126 * Integer.valueOf(backgroundColorHex.substring(1, 3), 16) + 0.7152 * Integer.valueOf(backgroundColorHex.substring(3, 5), 16) + 0.0722 * Integer.valueOf(backgroundColorHex.substring(5, 7), 16) ) < 140) ? Color.WHITE : Color.BLACK; } } } Loading
app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java +7 −7 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; Loading @@ -35,12 +34,14 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.databinding.FragmentNoteEditBinding; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.MarkDownUtil; import it.niedermann.owncloud.notes.util.NotesTextWatcher; import it.niedermann.owncloud.notes.util.format.ContextBasedFormattingCallback; import it.niedermann.owncloud.notes.util.format.ContextBasedRangeFormattingCallback; import static androidx.core.view.ViewCompat.isAttachedToWindow; import static it.niedermann.owncloud.notes.util.DisplayUtils.searchAndColor; public class NoteEditFragment extends SearchableBaseNoteFragment { private static final String LOG_TAG_AUTOSAVE = "AutoSave"; Loading Loading @@ -238,11 +239,10 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { } @Override protected void colorWithText(String newText) { if (binding != null && ViewCompat.isAttachedToWindow(binding.editContent)) { binding.editContent.setText(DisplayUtils.searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary)), TextView.BufferType.SPANNABLE); protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if (binding != null && isAttachedToWindow(binding.editContent)) { binding.editContent.clearFocus(); binding.editContent.setText(searchAndColor(new SpannableString(getContent()), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } }
app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +3 −3 Original line number Diff line number Diff line Loading @@ -183,10 +183,10 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } @Override protected void colorWithText(String newText) { protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if (binding != null && ViewCompat.isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(parseCompat(markdownProcessor, searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary))), binding.singleNoteContent.setText( searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } Loading
app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteReadonlyFragment.java +5 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; Loading @@ -31,10 +30,11 @@ import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.MarkDownUtil; import it.niedermann.owncloud.notes.util.NoteLinksUtils; import static androidx.core.view.ViewCompat.isAttachedToWindow; import static it.niedermann.owncloud.notes.util.DisplayUtils.searchAndColor; import static it.niedermann.owncloud.notes.util.MarkDownUtil.parseCompat; public class NoteReadonlyFragment extends SearchableBaseNoteFragment { Loading Loading @@ -147,11 +147,9 @@ public class NoteReadonlyFragment extends SearchableBaseNoteFragment { } @Override protected void colorWithText(String newText) { if ((binding != null) && ViewCompat.isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(parseCompat(markdownProcessor, DisplayUtils.searchAndColor(getContent(), new SpannableString (getContent()), newText, getResources().getColor(R.color.primary))), TextView.BufferType.SPANNABLE); protected void colorWithText(@NonNull String newText, @Nullable Integer current) { if ((binding != null) && isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.setText(searchAndColor(new SpannableString(parseCompat(markdownProcessor, getContent())), newText, requireContext(), current), TextView.BufferType.SPANNABLE); } } Loading
app/src/main/java/it/niedermann/owncloud/notes/android/fragment/SearchableBaseNoteFragment.java +7 −5 Original line number Diff line number Diff line Loading @@ -68,12 +68,12 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (currentVisibility != oldVisibility) { if (currentVisibility != View.VISIBLE) { colorWithText(""); colorWithText("", null); searchQuery = ""; hideSearchFabs(); } else { jumpToOccurrence(); colorWithText(searchQuery); colorWithText(searchQuery, null); occurrenceCount = countOccurrences(getContent(), searchQuery); showSearchFabs(); } Loading @@ -90,6 +90,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (next != null) { next.setOnClickListener(v -> { currentOccurrence++; colorWithText(searchView.getQuery().toString(), currentOccurrence); jumpToOccurrence(); }); } Loading @@ -97,6 +98,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (prev != null) { prev.setOnClickListener(v -> { currentOccurrence--; colorWithText(searchView.getQuery().toString(), currentOccurrence); jumpToOccurrence(); }); } Loading @@ -120,7 +122,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } currentOccurrence = 1; jumpToOccurrence(); colorWithText(searchQuery); colorWithText(searchQuery, currentOccurrence); return true; } }); Loading @@ -136,7 +138,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } } protected abstract void colorWithText(String newText); protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current); protected abstract ScrollView getScrollView(); Loading Loading @@ -194,7 +196,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { int numberLine = layout.getLineForOffset(textUntilFirstOccurrence.length()); if (numberLine >= 0) { getScrollView().smoothScrollTo(0, layout.getLineTop(numberLine)); getScrollView().post(() -> getScrollView().smoothScrollTo(0, layout.getLineTop(numberLine))); } } } Loading
app/src/main/java/it/niedermann/owncloud/notes/util/DisplayUtils.java +50 −11 Original line number Diff line number Diff line Loading @@ -19,29 +19,34 @@ */ package it.niedermann.owncloud.notes.util; import android.graphics.Typeface; import android.content.Context; import android.graphics.Color; import android.text.Spannable; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.CharacterStyle; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.MetricAffectingSpan; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; public class DisplayUtils { private DisplayUtils() { } public static Spannable searchAndColor(String text, Spannable spannable, String searchText, @ColorInt int color) { public static Spannable searchAndColor(Spannable spannable, CharSequence searchText, Context context, @Nullable Integer current) { CharSequence text = spannable.toString(); Object[] spansToRemove = spannable.getSpans(0, text.length(), Object.class); for (Object span : spansToRemove) { if(span instanceof CharacterStyle) if (span instanceof SearchSpan) spannable.removeSpan(span); } Loading @@ -49,18 +54,52 @@ public class DisplayUtils { return spannable; } Matcher m = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE | Pattern.LITERAL) Matcher m = Pattern.compile(searchText.toString(), Pattern.CASE_INSENSITIVE | Pattern.LITERAL) .matcher(text); int i = 1; while (m.find()) { int start = m.start(); int end = m.end(); spannable.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new SearchSpan(context, (current != null && i == current)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); i++; } return spannable; } static class SearchSpan extends MetricAffectingSpan { private final boolean current; private final int bgColorPrimary; private final int bgColorSecondary; SearchSpan(Context context, boolean current) { this.current = current; this.bgColorPrimary = context.getResources().getColor(R.color.bg_search_primary); this.bgColorSecondary = context.getResources().getColor(R.color.bg_search_secondary); } @Override public void updateDrawState(TextPaint tp) { tp.bgColor = current ? bgColorPrimary : bgColorSecondary; tp.setColor(current ? getForeground(Integer.toHexString(tp.bgColor)) : bgColorPrimary); tp.setFakeBoldText(true); } @Override public void updateMeasureState(@NonNull TextPaint tp) { tp.setFakeBoldText(true); } private static @ColorInt int getForeground(String backgroundColorHex) { return ((float) ( 0.2126 * Integer.valueOf(backgroundColorHex.substring(1, 3), 16) + 0.7152 * Integer.valueOf(backgroundColorHex.substring(3, 5), 16) + 0.0722 * Integer.valueOf(backgroundColorHex.substring(5, 7), 16) ) < 140) ? Color.WHITE : Color.BLACK; } } }