Loading app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java +43 −20 Original line number Diff line number Diff line Loading @@ -2,9 +2,9 @@ package it.niedermann.owncloud.notes.model; import android.content.Context; import android.graphics.Color; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.ColorInt; Loading @@ -16,19 +16,22 @@ import java.util.List; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.Branded; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding; import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; import static it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithoutExcerptBinding.inflate; public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Branded { private static final String TAG = ItemAdapter.class.getSimpleName(); private static final int section_type = 0; private static final int note_type = 1; private static final int TYPE_SECTION = R.layout.item_notes_list_section_item; private static final int TYPE_NOTE_WITH_EXCERPT = R.layout.item_notes_list_note_item_with_excerpt; private static final int TYPE_NOTE_WITHOUT_EXCERPT = R.layout.item_notes_list_note_item_without_excerpt; private final NoteClickListener noteClickListener; private List<Item> itemList = new ArrayList<>(); private boolean showCategory = true; private CharSequence searchQuery; @NonNull private Context context; private final List<Integer> selected = new ArrayList<>(); @ColorInt private int mainColor; Loading @@ -36,7 +39,6 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i private int textColor; public <T extends Context & NoteClickListener> ItemAdapter(@NonNull T context) { this.context = context; this.noteClickListener = context; this.mainColor = context.getResources().getColor(R.color.defaultBrand); this.textColor = Color.WHITE; Loading Loading @@ -74,24 +76,37 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v; if (viewType == section_type) { v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_notes_list_section_item, parent, false); return new SectionViewHolder(v); } else { v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_notes_list_note_item, parent, false); return new NoteViewHolder(v, noteClickListener); switch (viewType) { case TYPE_SECTION: { return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(LayoutInflater.from(parent.getContext()))); } case TYPE_NOTE_WITH_EXCERPT: { return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(LayoutInflater.from(parent.getContext())), noteClickListener); } case TYPE_NOTE_WITHOUT_EXCERPT: { return new NoteViewHolderWithoutExcerpt(inflate(LayoutInflater.from(parent.getContext())), noteClickListener); } default: { throw new IllegalArgumentException("Not supported viewType: " + viewType); } } } @Override public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) { final Item item = itemList.get(position); if (item.isSection()) { ((SectionViewHolder) holder).bind((SectionItem) item); } else { ((NoteViewHolder) holder).bind((DBNote) item, noteClickListener, showCategory, mainColor, textColor, searchQuery); switch (getItemViewType(position)) { case TYPE_SECTION: { ((SectionViewHolder) holder).bind((SectionItem) itemList.get(position)); break; } case TYPE_NOTE_WITH_EXCERPT: { ((NoteViewHolderWithExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery); break; } case TYPE_NOTE_WITHOUT_EXCERPT: { ((NoteViewHolderWithoutExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery); break; } } } Loading Loading @@ -147,7 +162,15 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i @Override public int getItemViewType(int position) { return getItem(position).isSection() ? section_type : note_type; Item item = getItem(position); if (item == null) { throw new IllegalArgumentException("Item at position " + position + " must not be null"); } return getItem(position).isSection() ? TYPE_SECTION : TextUtils.isEmpty(((DBNote) getItem(position)).getExcerpt()) ? TYPE_NOTE_WITHOUT_EXCERPT : TYPE_NOTE_WITH_EXCERPT; } @Override Loading app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolder.java +3 −111 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 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.databinding.ItemNotesListNoteItemBinding; 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 class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { private final ItemNotesListNoteItemBinding binding; public abstract class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { private final NoteClickListener noteClickListener; public NoteViewHolder(View v, NoteClickListener noteClickListener) { super(v); binding = ItemNotesListNoteItemBinding.bind(v); this.noteClickListener = noteClickListener; v.setOnClickListener(this); v.setOnLongClickListener(this); Loading @@ -53,91 +29,7 @@ public class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLo return noteClickListener.onNoteLongClick(getAdapterPosition(), v); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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; public abstract void showSwipe(boolean left); 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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } public abstract View getNoteSwipeable(); } No newline at end of file app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolderWithExcerpt.java 0 → 100644 +126 −0 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 private final ItemNotesListNoteItemWithExcerptBinding binding; public NoteViewHolderWithExcerpt(@NonNull ItemNotesListNoteItemWithExcerptBinding binding, @NonNull NoteClickListener noteClickListener) { super(binding.getRoot(), noteClickListener); this.binding = binding; itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } } No newline at end of file app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolderWithoutExcerpt.java 0 → 100644 +126 −0 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 private final ItemNotesListNoteItemWithoutExcerptBinding binding; public NoteViewHolderWithoutExcerpt(@NonNull ItemNotesListNoteItemWithoutExcerptBinding binding, @NonNull NoteClickListener noteClickListener) { super(binding.getRoot(), noteClickListener); this.binding = binding; itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } } No newline at end of file app/src/main/java/it/niedermann/owncloud/notes/model/SectionViewHolder.java +3 −3 Original line number Diff line number Diff line Loading @@ -9,9 +9,9 @@ import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; public class SectionViewHolder extends RecyclerView.ViewHolder { private final ItemNotesListSectionItemBinding binding; public SectionViewHolder(View view) { super(view); binding = ItemNotesListSectionItemBinding.bind(view); public SectionViewHolder(ItemNotesListSectionItemBinding binding) { super(binding.getRoot()); this.binding = binding; } public void bind(SectionItem item) { Loading Loading
app/src/main/java/it/niedermann/owncloud/notes/model/ItemAdapter.java +43 −20 Original line number Diff line number Diff line Loading @@ -2,9 +2,9 @@ package it.niedermann.owncloud.notes.model; import android.content.Context; import android.graphics.Color; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.ColorInt; Loading @@ -16,19 +16,22 @@ import java.util.List; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.Branded; import it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithExcerptBinding; import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; import static it.niedermann.owncloud.notes.databinding.ItemNotesListNoteItemWithoutExcerptBinding.inflate; public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Branded { private static final String TAG = ItemAdapter.class.getSimpleName(); private static final int section_type = 0; private static final int note_type = 1; private static final int TYPE_SECTION = R.layout.item_notes_list_section_item; private static final int TYPE_NOTE_WITH_EXCERPT = R.layout.item_notes_list_note_item_with_excerpt; private static final int TYPE_NOTE_WITHOUT_EXCERPT = R.layout.item_notes_list_note_item_without_excerpt; private final NoteClickListener noteClickListener; private List<Item> itemList = new ArrayList<>(); private boolean showCategory = true; private CharSequence searchQuery; @NonNull private Context context; private final List<Integer> selected = new ArrayList<>(); @ColorInt private int mainColor; Loading @@ -36,7 +39,6 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i private int textColor; public <T extends Context & NoteClickListener> ItemAdapter(@NonNull T context) { this.context = context; this.noteClickListener = context; this.mainColor = context.getResources().getColor(R.color.defaultBrand); this.textColor = Color.WHITE; Loading Loading @@ -74,24 +76,37 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v; if (viewType == section_type) { v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_notes_list_section_item, parent, false); return new SectionViewHolder(v); } else { v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_notes_list_note_item, parent, false); return new NoteViewHolder(v, noteClickListener); switch (viewType) { case TYPE_SECTION: { return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(LayoutInflater.from(parent.getContext()))); } case TYPE_NOTE_WITH_EXCERPT: { return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(LayoutInflater.from(parent.getContext())), noteClickListener); } case TYPE_NOTE_WITHOUT_EXCERPT: { return new NoteViewHolderWithoutExcerpt(inflate(LayoutInflater.from(parent.getContext())), noteClickListener); } default: { throw new IllegalArgumentException("Not supported viewType: " + viewType); } } } @Override public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) { final Item item = itemList.get(position); if (item.isSection()) { ((SectionViewHolder) holder).bind((SectionItem) item); } else { ((NoteViewHolder) holder).bind((DBNote) item, noteClickListener, showCategory, mainColor, textColor, searchQuery); switch (getItemViewType(position)) { case TYPE_SECTION: { ((SectionViewHolder) holder).bind((SectionItem) itemList.get(position)); break; } case TYPE_NOTE_WITH_EXCERPT: { ((NoteViewHolderWithExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery); break; } case TYPE_NOTE_WITHOUT_EXCERPT: { ((NoteViewHolderWithoutExcerpt) holder).bind((DBNote) itemList.get(position), noteClickListener, showCategory, mainColor, textColor, searchQuery); break; } } } Loading Loading @@ -147,7 +162,15 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i @Override public int getItemViewType(int position) { return getItem(position).isSection() ? section_type : note_type; Item item = getItem(position); if (item == null) { throw new IllegalArgumentException("Item at position " + position + " must not be null"); } return getItem(position).isSection() ? TYPE_SECTION : TextUtils.isEmpty(((DBNote) getItem(position)).getExcerpt()) ? TYPE_NOTE_WITHOUT_EXCERPT : TYPE_NOTE_WITH_EXCERPT; } @Override Loading
app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolder.java +3 −111 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 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.databinding.ItemNotesListNoteItemBinding; 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 class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { private final ItemNotesListNoteItemBinding binding; public abstract class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { private final NoteClickListener noteClickListener; public NoteViewHolder(View v, NoteClickListener noteClickListener) { super(v); binding = ItemNotesListNoteItemBinding.bind(v); this.noteClickListener = noteClickListener; v.setOnClickListener(this); v.setOnLongClickListener(this); Loading @@ -53,91 +29,7 @@ public class NoteViewHolder extends RecyclerView.ViewHolder implements View.OnLo return noteClickListener.onNoteLongClick(getAdapterPosition(), v); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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; public abstract void showSwipe(boolean left); 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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } public abstract View getNoteSwipeable(); } No newline at end of file
app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolderWithExcerpt.java 0 → 100644 +126 −0 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 private final ItemNotesListNoteItemWithExcerptBinding binding; public NoteViewHolderWithExcerpt(@NonNull ItemNotesListNoteItemWithExcerptBinding binding, @NonNull NoteClickListener noteClickListener) { super(binding.getRoot(), noteClickListener); this.binding = binding; itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } } No newline at end of file
app/src/main/java/it/niedermann/owncloud/notes/model/NoteViewHolderWithoutExcerpt.java 0 → 100644 +126 −0 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 private final ItemNotesListNoteItemWithoutExcerptBinding binding; public NoteViewHolderWithoutExcerpt(@NonNull ItemNotesListNoteItemWithoutExcerptBinding binding, @NonNull NoteClickListener noteClickListener) { super(binding.getRoot(), noteClickListener); this.binding = binding; itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } public void showSwipe(boolean left) { binding.noteFavoriteLeft.setVisibility(left ? View.VISIBLE : View.INVISIBLE); binding.noteDeleteRight.setVisibility(left ? View.INVISIBLE : View.VISIBLE); 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) { @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); 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()); } } public View getNoteSwipeable() { return binding.noteSwipeable; } } No newline at end of file
app/src/main/java/it/niedermann/owncloud/notes/model/SectionViewHolder.java +3 −3 Original line number Diff line number Diff line Loading @@ -9,9 +9,9 @@ import it.niedermann.owncloud.notes.databinding.ItemNotesListSectionItemBinding; public class SectionViewHolder extends RecyclerView.ViewHolder { private final ItemNotesListSectionItemBinding binding; public SectionViewHolder(View view) { super(view); binding = ItemNotesListSectionItemBinding.bind(view); public SectionViewHolder(ItemNotesListSectionItemBinding binding) { super(binding.getRoot()); this.binding = binding; } public void bind(SectionItem item) { Loading