Skip to content

Commit 45ced0d

Browse files
committed
Fix various minor inconsistencies in entry list item offsets
This patch addresses the following: - More consistent offsets between entries in the list, especially in relation to the action bar and the error card. - Consistent correct application of card shapes when switching between favoriting and unfavoriting entries. - Removal of CompactDividerDecoration. We no longer uses dividers, so this is no longer needed.
1 parent 9737c85 commit 45ced0d

File tree

2 files changed

+90
-116
lines changed

2 files changed

+90
-116
lines changed

app/src/main/java/com/beemdevelopment/aegis/ViewMode.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,26 @@ public int getLayoutId() {
3535
}
3636

3737
/**
38-
* Retrieves the height (in dp) that the divider between entries should have in this view mode.
38+
* Retrieves the offset (in dp) that should exist between entries in this view mode.
3939
*/
40-
public float getDividerHeight() {
40+
public float getItemOffset() {
4141
if (this == ViewMode.COMPACT) {
42-
return 0;
42+
return 1;
4343
} else if (this == ViewMode.TILES) {
4444
return 4;
4545
}
4646

4747
return 8;
4848
}
4949

50-
public int getColumnSpan() {
50+
public int getSpanCount() {
5151
if (this == ViewMode.TILES) {
5252
return 2;
5353
}
5454

5555
return 1;
5656
}
5757

58-
public float getDividerWidth() {
59-
if (this == ViewMode.TILES) {
60-
return 4;
61-
}
62-
63-
return 0;
64-
}
65-
6658
public String getFormattedAccountName(String accountName) {
6759
if (this == ViewMode.TILES) {
6860
return accountName;

app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java

Lines changed: 86 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
44

55
import android.annotation.SuppressLint;
6+
import android.content.res.TypedArray;
67
import android.graphics.Rect;
78
import android.graphics.drawable.Drawable;
89
import android.os.Bundle;
10+
import android.util.TypedValue;
911
import android.view.LayoutInflater;
1012
import android.view.MotionEvent;
1113
import android.view.View;
@@ -14,10 +16,11 @@
1416
import android.widget.Button;
1517
import android.widget.LinearLayout;
1618

19+
import androidx.annotation.AttrRes;
1720
import androidx.annotation.NonNull;
1821
import androidx.annotation.Nullable;
22+
import androidx.annotation.StyleRes;
1923
import androidx.fragment.app.Fragment;
20-
import androidx.recyclerview.widget.DividerItemDecoration;
2124
import androidx.recyclerview.widget.GridLayoutManager;
2225
import androidx.recyclerview.widget.ItemTouchHelper;
2326
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -50,7 +53,6 @@
5053
import com.google.android.material.card.MaterialCardView;
5154
import com.google.android.material.chip.Chip;
5255
import com.google.android.material.chip.ChipGroup;
53-
import com.google.android.material.divider.MaterialDividerItemDecoration;
5456
import com.google.android.material.shape.CornerFamily;
5557
import com.google.android.material.shape.ShapeAppearanceModel;
5658
import com.google.common.base.Strings;
@@ -70,8 +72,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener {
7072
private ItemTouchHelper _touchHelper;
7173

7274
private RecyclerView _recyclerView;
73-
private RecyclerView.ItemDecoration _verticalDividerDecoration;
74-
private RecyclerView.ItemDecoration _horizontalDividerDecoration;
75+
private RecyclerView.ItemDecoration _itemDecoration;
7576
private ViewPreloadSizeProvider<VaultEntry> _preloadSizeProvider;
7677
private TotpProgressBar _progressBar;
7778
private boolean _showProgress;
@@ -255,7 +256,7 @@ public void setViewMode(ViewMode mode) {
255256
_touchCallback.setDragFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN);
256257
}
257258

258-
((GridLayoutManager)_recyclerView.getLayoutManager()).setSpanCount(mode.getColumnSpan());
259+
((GridLayoutManager)_recyclerView.getLayoutManager()).setSpanCount(mode.getSpanCount());
259260
}
260261

261262
public void startDrag(RecyclerView.ViewHolder viewHolder) {
@@ -591,28 +592,18 @@ private Set<UUID> cleanGroupFilter(Set<UUID> groupFilter) {
591592
}
592593

593594
private void updateDividerDecoration() {
594-
if (_verticalDividerDecoration != null) {
595-
_recyclerView.removeItemDecoration(_verticalDividerDecoration);
595+
if (_itemDecoration != null) {
596+
_recyclerView.removeItemDecoration(_itemDecoration);
596597
}
597598

598-
if(_horizontalDividerDecoration != null) {
599-
_recyclerView.removeItemDecoration(_horizontalDividerDecoration);
600-
}
601-
602-
float height = _viewMode.getDividerHeight();
603-
float width = _viewMode.getDividerWidth();
604-
if (_showProgress && height == 0) {
605-
_verticalDividerDecoration = new CompactDividerDecoration();
599+
float offset = _viewMode.getItemOffset();
600+
if (_viewMode == ViewMode.TILES) {
601+
_itemDecoration = new TileSpaceItemDecoration(offset);
606602
} else {
607-
_verticalDividerDecoration = new VerticalSpaceItemDecoration(height);
603+
_itemDecoration = new VerticalSpaceItemDecoration(offset);
608604
}
609605

610-
if (width != 0) {
611-
_horizontalDividerDecoration = new TileSpaceItemDecoration(width, height);
612-
_recyclerView.addItemDecoration(_horizontalDividerDecoration);
613-
} else {
614-
_recyclerView.addItemDecoration(_verticalDividerDecoration);
615-
}
606+
_recyclerView.addItemDecoration(_itemDecoration);
616607
}
617608

618609
private void updateEmptyState() {
@@ -642,61 +633,18 @@ public interface Listener {
642633
void onEntryListTouch();
643634
}
644635

645-
private void decorateFavoriteEntries(@NonNull View view, @NonNull RecyclerView parent) {
646-
int adapterPosition = parent.getChildAdapterPosition(view);
647-
int entryIndex = _adapter.translateEntryPosToIndex(adapterPosition);
648-
int totalFavorites = _adapter.getShownFavoritesCount();
649-
650-
if (entryIndex < totalFavorites) {
651-
ShapeAppearanceModel model = ((MaterialCardView)view).getShapeAppearanceModel();
652-
ShapeAppearanceModel.Builder builder = model.toBuilder();
653-
if ((entryIndex == 0 && totalFavorites > 1) || (entryIndex < (totalFavorites - 1))) {
654-
builder.setBottomLeftCorner(CornerFamily.ROUNDED, 0);
655-
builder.setBottomRightCorner(CornerFamily.ROUNDED, 0);
656-
}
657-
if (entryIndex > 0) {
658-
builder.setTopLeftCorner(CornerFamily.ROUNDED, 0);
659-
builder.setTopRightCorner(CornerFamily.ROUNDED, 0);
660-
}
661-
662-
((MaterialCardView)view).setShapeAppearanceModel(builder.build());
663-
}
664-
}
665-
666-
private class CompactDividerDecoration extends MaterialDividerItemDecoration {
667-
public CompactDividerDecoration() {
668-
super(requireContext(), DividerItemDecoration.VERTICAL);
669-
setDividerColorResource(requireContext(), android.R.color.transparent);
670-
setLastItemDecorated(false);
671-
setDividerThickness(MetricsHelper.convertDpToPixels(requireContext(), 0.5f));
672-
}
673-
674-
@Override
675-
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
676-
if (_adapter.isPositionErrorCard(parent.getChildAdapterPosition(view))) {
677-
outRect.top = MetricsHelper.convertDpToPixels(requireContext(), 4);
678-
return;
679-
}
680-
681-
if (_adapter.isPositionFooter(parent.getChildAdapterPosition(view))) {
682-
int pixels = MetricsHelper.convertDpToPixels(requireContext(), 20);
683-
outRect.top = pixels;
684-
outRect.bottom = pixels;
685-
return;
686-
}
636+
private class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
637+
private final int _offset;
638+
private final ShapeAppearanceModel _defaultShapeModel;
687639

688-
decorateFavoriteEntries(view, parent);
640+
private VerticalSpaceItemDecoration(float offset) {
641+
_offset = MetricsHelper.convertDpToPixels(requireContext(), offset);
689642

690-
super.getItemOffsets(outRect, view, parent, state);
691-
}
692-
}
643+
int shapeAppearanceId = getStyledAttrs(R.style.Widget_Aegis_EntryCardView,
644+
com.google.android.material.R.attr.shapeAppearance);
693645

694-
private class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
695-
private final int _height;
696-
697-
private VerticalSpaceItemDecoration(float dp) {
698-
// convert dp to pixels
699-
_height = MetricsHelper.convertDpToPixels(requireContext(), dp);
646+
_defaultShapeModel = ShapeAppearanceModel.builder(
647+
requireContext(), shapeAppearanceId, 0).build();
700648
}
701649

702650
@Override
@@ -707,57 +655,80 @@ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull R
707655
}
708656

709657
// The error card and the footer always have a top and bottom margin
710-
if (_adapter.isPositionErrorCard(adapterPosition)
711-
|| _adapter.isPositionFooter(adapterPosition)) {
712-
outRect.top = _height;
713-
outRect.bottom = _height;
658+
if (_adapter.isPositionErrorCard(adapterPosition)) {
659+
outRect.top = _viewMode == ViewMode.COMPACT ? _offset * 4 : _offset;
660+
outRect.bottom = _offset;
661+
return;
662+
}
663+
if (_adapter.isPositionFooter(adapterPosition)) {
664+
outRect.top = _offset * 2;
665+
outRect.bottom = _offset;
714666
return;
715667
}
716668

717669
int entryIndex = _adapter.translateEntryPosToIndex(adapterPosition);
718670
// The first entry should have a top margin, but only if the group chip is not shown and the error card is not shown
719671
if (entryIndex == 0 && (_groups == null || _groups.isEmpty()) && !_adapter.isErrorCardShown()) {
720-
outRect.top = _height;
672+
outRect.top = _offset;
721673
}
722674

723675
// Only non-favorite entries have a bottom margin, except for the final favorite entry
724676
int totalFavorites = _adapter.getShownFavoritesCount();
725677
if (totalFavorites == 0
726678
|| (entryIndex < _adapter.getShownEntriesCount() && !_adapter.getEntryAtPos(adapterPosition).isFavorite())
727679
|| totalFavorites == entryIndex + 1) {
728-
outRect.bottom = _height;
680+
outRect.bottom = _offset;
729681
}
730682

731-
if (totalFavorites > 0) {
732-
// If this entry is the last favorite entry in the list, it should always have
733-
// a bottom margin, regardless of the view mode
734-
if (entryIndex == totalFavorites - 1) {
735-
outRect.bottom = _height;
736-
}
683+
// The last entry should never have a bottom margin
684+
if (_adapter.getShownEntriesCount() == entryIndex + 1) {
685+
outRect.bottom = 0;
686+
}
737687

738-
// If this is the first non-favorite entry, it should have a top margin
739-
if (entryIndex == totalFavorites) {
740-
outRect.top = _height;
741-
}
688+
decorateFavoriteEntries((MaterialCardView) view, parent);
689+
}
742690

743-
decorateFavoriteEntries(view, parent);
691+
private void decorateFavoriteEntries(@NonNull MaterialCardView view, @NonNull RecyclerView parent) {
692+
int adapterPosition = parent.getChildAdapterPosition(view);
693+
int entryIndex = _adapter.translateEntryPosToIndex(adapterPosition);
694+
int totalFavorites = _adapter.getShownFavoritesCount();
695+
696+
ShapeAppearanceModel.Builder builder = _defaultShapeModel.toBuilder();
697+
if (entryIndex < totalFavorites) {
698+
if ((entryIndex == 0 && totalFavorites > 1) || (entryIndex < (totalFavorites - 1))) {
699+
builder.setBottomLeftCorner(CornerFamily.ROUNDED, 0);
700+
builder.setBottomRightCorner(CornerFamily.ROUNDED, 0);
701+
}
702+
if (entryIndex > 0) {
703+
builder.setTopLeftCorner(CornerFamily.ROUNDED, 0);
704+
builder.setTopRightCorner(CornerFamily.ROUNDED, 0);
705+
}
744706
}
745707

746-
// The last entry should never have a bottom margin
747-
if (_adapter.getShownEntriesCount() == entryIndex + 1) {
748-
outRect.bottom = 0;
708+
view.setShapeAppearanceModel(builder.build());
709+
view.setClipToOutline(true);
710+
}
711+
712+
private int getStyledAttrs(@StyleRes int styleId, @AttrRes int attrId) {
713+
TypedArray cardAttrs = null;
714+
try {
715+
cardAttrs = requireContext().obtainStyledAttributes(styleId, new int[]{attrId});
716+
TypedValue value = new TypedValue();
717+
cardAttrs.getValue(0, value);
718+
return value.data;
719+
} finally {
720+
if (cardAttrs != null) {
721+
cardAttrs.recycle();
722+
}
749723
}
750724
}
751725
}
752726

753727
private class TileSpaceItemDecoration extends RecyclerView.ItemDecoration {
754-
private final int _width;
755-
private final int _height;
728+
private final int _offset;
756729

757-
private TileSpaceItemDecoration(float width, float height) {
758-
// convert dp to pixels
759-
_width = MetricsHelper.convertDpToPixels(requireContext(), width);
760-
_height = MetricsHelper.convertDpToPixels(requireContext(), height);
730+
private TileSpaceItemDecoration(float offset) {
731+
_offset = MetricsHelper.convertDpToPixels(requireContext(), offset);
761732
}
762733

763734
@Override
@@ -767,10 +738,21 @@ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull R
767738
return;
768739
}
769740

770-
outRect.left = _width;
771-
outRect.right = _width;
772-
outRect.top = _height;
773-
outRect.bottom = _height;
741+
outRect.left = _offset;
742+
outRect.right = _offset;
743+
outRect.top = _offset;
744+
outRect.bottom = _offset;
745+
746+
if (_adapter.isPositionErrorCard(adapterPosition)
747+
|| (isInFirstEntryRow(adapterPosition) && !_adapter.isErrorCardShown())
748+
|| _adapter.isPositionFooter(adapterPosition)) {
749+
outRect.top *= 2;
750+
}
751+
}
752+
753+
private boolean isInFirstEntryRow(int pos) {
754+
int index = _adapter.translateEntryPosToIndex(pos);
755+
return index >= 0 && index < _viewMode.getSpanCount();
774756
}
775757
}
776758

0 commit comments

Comments
 (0)