Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db517049 authored by Stefan Niedermann's avatar Stefan Niedermann Committed by Niedermann IT-Dienstleistungen
Browse files

#374 Title and category should be centered if no excerpt is given

parent 68498313
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -100,11 +100,11 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
                break;
            }
            case TYPE_NOTE_WITH_EXCERPT: {
                ((NoteViewHolderWithExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery);
                ((NoteViewHolderWithExcerpt) holder).bind((DBNote) itemList.get(position), showCategory, mainColor, textColor, searchQuery);
                break;
            }
            case TYPE_NOTE_WITHOUT_EXCERPT: {
                ((NoteViewHolderWithoutExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery);
                ((NoteViewHolderWithoutExcerpt) holder).bind((DBNote) itemList.get(position), showCategory, mainColor, textColor, searchQuery);
                break;
            }
        }
+105 −1
Original line number Diff line number Diff line
package it.niedermann.owncloud.notes.model;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.recyclerview.widget.RecyclerView;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.util.Notes;

import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient;
import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark;

public abstract class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener {
    private final NoteClickListener noteClickListener;

    public NoteViewHolder(View v, NoteClickListener noteClickListener) {
    public NoteViewHolder(@NonNull View v, @NonNull NoteClickListener noteClickListener) {
        super(v);
        this.noteClickListener = noteClickListener;
        v.setOnClickListener(this);
@@ -29,6 +52,87 @@ public abstract class NoteViewHolder extends RecyclerView.ViewHolder implements
        return noteClickListener.onNoteLongClick(getAdapterPosition(), v);
    }

    protected void bindCategory(Context context, TextView noteCategory, boolean showCategory, String category, int mainColor) {
        final boolean isDarkThemeActive = Notes.isDarkThemeActive(context);
        noteCategory.setVisibility(showCategory && !category.isEmpty() ? View.VISIBLE : View.GONE);
        noteCategory.setText(category);

        @ColorInt int categoryForeground;
        @ColorInt int categoryBackground;

        if (isDarkThemeActive) {
            if (isColorDark(mainColor)) {
                if (contrastRatioIsSufficient(mainColor, Color.BLACK)) {
                    categoryBackground = mainColor;
                    categoryForeground = Color.WHITE;
                } else {
                    categoryBackground = Color.WHITE;
                    categoryForeground = mainColor;
                }
            } else {
                categoryBackground = mainColor;
                categoryForeground = Color.BLACK;
            }
        } else {
            categoryForeground = Color.BLACK;
            if (isColorDark(mainColor) || contrastRatioIsSufficient(mainColor, Color.WHITE)) {
                categoryBackground = mainColor;
            } else {
                categoryBackground = Color.BLACK;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            DrawableCompat.setTint(noteCategory.getBackground(), categoryBackground);
        } else {
            final GradientDrawable drawable = (GradientDrawable) noteCategory.getBackground();
            drawable.setStroke(1, categoryBackground);
            drawable.setColor(isDarkThemeActive ? categoryBackground : Color.TRANSPARENT);
        }
        noteCategory.setTextColor(categoryForeground);
    }

    protected void bindFavorite(ImageView noteFavorite, boolean isFavorite) {
        noteFavorite.setImageResource(isFavorite ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp);
        noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(getAdapterPosition(), view));
    }

    protected void bindTitleAndExcerpt(Context context, TextView noteTitle, @Nullable TextView noteExcerpt, CharSequence searchQuery, DBNote note, int mainColor) {
        if (!TextUtils.isEmpty(searchQuery)) {
            @ColorInt final int searchBackground = context.getResources().getColor(R.color.bg_highlighted);
            @ColorInt final int searchForeground = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor);

            // The Pattern.quote method will add \Q to the very beginning of the string and \E to the end of the string
            // It implies that the string between \Q and \E is a literal string and thus the reserved keyword in such string will be ignored.
            // See https://stackoverflow.com/questions/15409296/what-is-the-use-of-pattern-quote-method
            final Pattern pattern = Pattern.compile("(" + Pattern.quote(searchQuery.toString()) + ")", Pattern.CASE_INSENSITIVE);
            SpannableString spannableString = new SpannableString(note.getTitle());
            Matcher matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

            noteTitle.setText(spannableString);

            spannableString = new SpannableString(note.getExcerpt());
            matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

            if (noteExcerpt != null) {
                noteExcerpt.setText(spannableString);
            }
        } else {
            noteTitle.setText(note.getTitle());
            if (noteExcerpt != null) {
                noteExcerpt.setText(note.getExcerpt());
            }
        }
    }

    public abstract void showSwipe(boolean left);

    public abstract View getNoteSwipeable();
+4 −89
Original line number Diff line number Diff line
package it.niedermann.owncloud.notes.model;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.DrawableCompat;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding;
import it.niedermann.owncloud.notes.util.Notes;

import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient;
import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark;

public class NoteViewHolderWithExcerpt extends NoteViewHolder {
    @NonNull
@@ -43,81 +26,13 @@ public class NoteViewHolderWithExcerpt extends NoteViewHolder {
        binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention);
    }

    public void bind(DBNote note, NoteClickListener noteClickListener, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) {
    public void bind(DBNote note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) {
        @NonNull final Context context = itemView.getContext();
        final boolean isDarkThemeActive = Notes.isDarkThemeActive(context);

        binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f);
        binding.noteCategory.setVisibility(showCategory && !note.getCategory().isEmpty() ? View.VISIBLE : View.GONE);
        binding.noteCategory.setText(note.getCategory());

        @ColorInt int categoryForeground;
        @ColorInt int categoryBackground;

        if (isDarkThemeActive) {
            if (isColorDark(mainColor)) {
                if (contrastRatioIsSufficient(mainColor, Color.BLACK)) {
                    categoryBackground = mainColor;
                    categoryForeground = Color.WHITE;
                } else {
                    categoryBackground = Color.WHITE;
                    categoryForeground = mainColor;
                }
            } else {
                categoryBackground = mainColor;
                categoryForeground = Color.BLACK;
            }
        } else {
            categoryForeground = Color.BLACK;
            if (isColorDark(mainColor) || contrastRatioIsSufficient(mainColor, Color.WHITE)) {
                categoryBackground = mainColor;
            } else {
                categoryBackground = Color.BLACK;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            DrawableCompat.setTint(binding.noteCategory.getBackground(), categoryBackground);
        } else {
            final GradientDrawable drawable = (GradientDrawable) binding.noteCategory.getBackground();
            drawable.setStroke(1, categoryBackground);
            drawable.setColor(isDarkThemeActive ? categoryBackground : Color.TRANSPARENT);
        }
        binding.noteCategory.setTextColor(categoryForeground);

        bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), mainColor);
        binding.noteStatus.setVisibility(DBStatus.VOID.equals(note.getStatus()) ? View.INVISIBLE : View.VISIBLE);
        binding.noteFavorite.setImageResource(note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp);
        binding.noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(getAdapterPosition(), view));

        if (!TextUtils.isEmpty(searchQuery)) {
            @ColorInt final int searchBackground = context.getResources().getColor(R.color.bg_highlighted);
            @ColorInt final int searchForeground = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor);

            // The Pattern.quote method will add \Q to the very beginning of the string and \E to the end of the string
            // It implies that the string between \Q and \E is a literal string and thus the reserved keyword in such string will be ignored.
            // See https://stackoverflow.com/questions/15409296/what-is-the-use-of-pattern-quote-method
            final Pattern pattern = Pattern.compile("(" + Pattern.quote(searchQuery.toString()) + ")", Pattern.CASE_INSENSITIVE);
            SpannableString spannableString = new SpannableString(note.getTitle());
            Matcher matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

            binding.noteTitle.setText(spannableString);

            spannableString = new SpannableString(note.getExcerpt());
            matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

            binding.noteExcerpt.setText(spannableString);
        } else {
            binding.noteTitle.setText(note.getTitle());
            binding.noteExcerpt.setText(note.getExcerpt());
        }
        bindFavorite(binding.noteFavorite, note.isFavorite());
        bindTitleAndExcerpt(context, binding.noteTitle, binding.noteExcerpt, searchQuery, note, mainColor);
    }

    public View getNoteSwipeable() {
+4 −88
Original line number Diff line number Diff line
package it.niedermann.owncloud.notes.model;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.DrawableCompat;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithoutExcerptBinding;
import it.niedermann.owncloud.notes.util.Notes;

import static it.niedermann.owncloud.notes.util.ColorUtil.contrastRatioIsSufficient;
import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark;

public class NoteViewHolderWithoutExcerpt extends NoteViewHolder {
    @NonNull
@@ -44,80 +27,13 @@ public class NoteViewHolderWithoutExcerpt extends NoteViewHolder {
        binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention);
    }

    public void bind(DBNote note, NoteClickListener noteClickListener, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) {
    public void bind(DBNote note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) {
        @NonNull final Context context = itemView.getContext();
        final boolean isDarkThemeActive = Notes.isDarkThemeActive(context);

        binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f);

        binding.noteCategory.setVisibility(showCategory && !note.getCategory().isEmpty() ? View.VISIBLE : View.GONE);
        binding.noteCategory.setText(note.getCategory());

        @ColorInt int categoryForeground;
        @ColorInt int categoryBackground;

        if (isDarkThemeActive) {
            if (isColorDark(mainColor)) {
                if (contrastRatioIsSufficient(mainColor, Color.BLACK)) {
                    categoryBackground = mainColor;
                    categoryForeground = Color.WHITE;
                } else {
                    categoryBackground = Color.WHITE;
                    categoryForeground = mainColor;
                }
            } else {
                categoryBackground = mainColor;
                categoryForeground = Color.BLACK;
            }
        } else {
            categoryForeground = Color.BLACK;
            if (isColorDark(mainColor) || contrastRatioIsSufficient(mainColor, Color.WHITE)) {
                categoryBackground = mainColor;
            } else {
                categoryBackground = Color.BLACK;
            }
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            DrawableCompat.setTint(binding.noteCategory.getBackground(), categoryBackground);
        } else {
            final GradientDrawable drawable = (GradientDrawable) binding.noteCategory.getBackground();
            drawable.setStroke(1, categoryBackground);
            drawable.setColor(isDarkThemeActive ? categoryBackground : Color.TRANSPARENT);
        }
        binding.noteCategory.setTextColor(categoryForeground);

        bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), mainColor);
        binding.noteStatus.setVisibility(DBStatus.VOID.equals(note.getStatus()) ? View.INVISIBLE : View.VISIBLE);
        binding.noteFavorite.setImageResource(note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp);
        binding.noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(getAdapterPosition(), view));

        if (!TextUtils.isEmpty(searchQuery)) {
            @ColorInt final int searchBackground = context.getResources().getColor(R.color.bg_highlighted);
            @ColorInt final int searchForeground = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor);

            // The Pattern.quote method will add \Q to the very beginning of the string and \E to the end of the string
            // It implies that the string between \Q and \E is a literal string and thus the reserved keyword in such string will be ignored.
            // See https://stackoverflow.com/questions/15409296/what-is-the-use-of-pattern-quote-method
            final Pattern pattern = Pattern.compile("(" + Pattern.quote(searchQuery.toString()) + ")", Pattern.CASE_INSENSITIVE);
            SpannableString spannableString = new SpannableString(note.getTitle());
            Matcher matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

            binding.noteTitle.setText(spannableString);

            spannableString = new SpannableString(note.getExcerpt());
            matcher = pattern.matcher(spannableString);
            while (matcher.find()) {
                spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0);
                spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0);
            }

        } else {
            binding.noteTitle.setText(note.getTitle());
        }
        bindFavorite(binding.noteFavorite, note.isFavorite());
        bindTitleAndExcerpt(context, binding.noteTitle, null, searchQuery, note, mainColor);
    }

    public View getNoteSwipeable() {