[bookmarks][android] Bookmarks sections refactoring.

This commit is contained in:
Daria Volvenkova 2019-08-18 05:04:35 +03:00 committed by Aleksey Belousov
parent 25e2390417
commit 3feb73f801
3 changed files with 422 additions and 160 deletions

View file

@ -1,5 +1,6 @@
package com.mapswithme.maps.bookmarks;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
@ -15,28 +16,338 @@ import com.mapswithme.maps.content.DataSource;
import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
import com.mapswithme.maps.widget.recycler.RecyclerLongClickListener;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.SECTION_BMKS;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.SECTION_DESC;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.SECTION_TRACKS;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.getBookmarksSectionPosition;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.getDescItemCount;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.getDescSectionPosition;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.getTracksSectionPosition;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.isSectionEmpty;
import static com.mapswithme.maps.bookmarks.Holders.BookmarkViewHolder.calculateBookmarkPosition;
import static com.mapswithme.maps.bookmarks.Holders.BaseBookmarkHolder.calculateTrackPosition;
import java.util.List;
public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookmarkHolder>
{
@NonNull
private final DataSource<BookmarkCategory> mDataSource;
// view types
static final int TYPE_TRACK = 0;
static final int TYPE_BOOKMARK = 1;
static final int TYPE_SECTION = 2;
static final int TYPE_DESC = 3;
public static class SectionPosition
{
public static final int INVALID_POSITION = -1;
public final int sectionIndex;
public final int itemIndex;
SectionPosition(int sectionInd, int itemInd)
{
sectionIndex = sectionInd;
itemIndex = itemInd;
}
boolean isTitlePosition()
{
return sectionIndex != INVALID_POSITION && itemIndex == INVALID_POSITION;
}
boolean isItemPosition()
{
return sectionIndex != INVALID_POSITION && itemIndex != INVALID_POSITION;
}
}
public static class SortedBlock
{
@NonNull
public String title;
public List<Long> bookmarkIds;
public List<Long> trackIds;
}
public abstract class SectionsDataSource
{
@NonNull
protected final DataSource<BookmarkCategory> mDataSource;
SectionsDataSource(@NonNull DataSource<BookmarkCategory> dataSource)
{
mDataSource = dataSource;
}
public BookmarkCategory getCategory()
{
return mDataSource.getData();
}
public abstract int getSectionsCount();
public abstract boolean isEditable(int sectionIndex);
public abstract boolean hasTitle(int sectionIndex);
public abstract @Nullable String getTitle(int sectionIndex, Resources rs);
public abstract int getItemsCount(int sectionIndex);
public abstract int getItemsType(int sectionIndex);
public abstract long getBookmarkId(SectionPosition pos);
public abstract long getTrackId(SectionPosition pos);
public abstract void onDelete(SectionPosition pos);
}
private class CategorySectionsDataSource extends SectionsDataSource
{
private int mSectionsCount;
private int mDescriptionSectionIndex;
private int mBookmarksSectionIndex;
private int mTracksSectionIndex;
CategorySectionsDataSource(@NonNull DataSource<BookmarkCategory> dataSource)
{
super(dataSource);
calculateSections();
}
private void calculateSections()
{
mDescriptionSectionIndex = SectionPosition.INVALID_POSITION;
mBookmarksSectionIndex = SectionPosition.INVALID_POSITION;
mTracksSectionIndex = SectionPosition.INVALID_POSITION;
mSectionsCount = 0;
if (!getCategory().getAnnotation().isEmpty() || !getCategory().getDescription().isEmpty())
{
mDescriptionSectionIndex = mSectionsCount++;
}
if (getCategory().getTracksCount() > 0)
mTracksSectionIndex = mSectionsCount++;
if (getCategory().getBookmarksCount() > 0)
mBookmarksSectionIndex = mSectionsCount++;
}
@Override
public int getSectionsCount()
{
return mSectionsCount;
}
@Override
public boolean isEditable(int sectionIndex)
{
return sectionIndex != mDescriptionSectionIndex && !getCategory().isFromCatalog();
}
@Override
public boolean hasTitle(int sectionIndex)
{
return true;
}
@Nullable
public String getTitle(int sectionIndex, Resources rs)
{
if (sectionIndex == mDescriptionSectionIndex)
return rs.getString(R.string.description);
if (sectionIndex == mTracksSectionIndex)
return rs.getString(R.string.tracks_title);
return rs.getString(R.string.bookmarks);
}
@Override
public int getItemsCount(int sectionIndex)
{
if (sectionIndex == mDescriptionSectionIndex)
return 1;
if (sectionIndex == mTracksSectionIndex)
return getCategory().getTracksCount();
if (sectionIndex == mBookmarksSectionIndex)
return getCategory().getBookmarksCount();
return 0;
}
@Override
public int getItemsType(int sectionIndex)
{
if (sectionIndex == mDescriptionSectionIndex)
return TYPE_DESC;
if (sectionIndex == mTracksSectionIndex)
return TYPE_TRACK;
if (sectionIndex == mBookmarksSectionIndex)
return TYPE_BOOKMARK;
return 0;
}
@Override
public void onDelete(SectionPosition pos)
{
calculateSections();
}
@Override
public long getBookmarkId(SectionPosition pos)
{
final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(
getCategory().getId(), pos.itemIndex);
return bookmarkId;
}
@Override
public long getTrackId(SectionPosition pos)
{
final long trackId = BookmarkManager.INSTANCE.getTrackIdByPosition(
getCategory().getId(), pos.itemIndex);
return trackId;
}
}
private class SearchResultsSectionsDataSource extends SectionsDataSource
{
@NonNull
private final List<Long> mSearchResults;
SearchResultsSectionsDataSource(@NonNull DataSource<BookmarkCategory> dataSource,
@NonNull List<Long> searchResults)
{
super(dataSource);
mSearchResults = searchResults;
}
@Override
public int getSectionsCount()
{
return 1;
}
@Override
public boolean isEditable(int sectionIndex)
{
return true;
}
@Override
public boolean hasTitle(int sectionIndex)
{
return false;
}
@Nullable
public String getTitle(int sectionIndex, Resources rs)
{
return null;
}
@Override
public int getItemsCount(int sectionIndex)
{
return mSearchResults.size();
}
@Override
public int getItemsType(int sectionIndex)
{
return TYPE_BOOKMARK;
}
@Override
public void onDelete(SectionPosition pos)
{
mSearchResults.remove(pos.itemIndex);
}
@Override
public long getBookmarkId(SectionPosition pos)
{
return mSearchResults.get(pos.itemIndex);
}
@Override
public long getTrackId(SectionPosition pos)
{
return 0;
}
}
private class SortedSectionsDataSource extends SectionsDataSource
{
@NonNull
private List<SortedBlock> mSortedBlocks;
SortedSectionsDataSource(@NonNull DataSource<BookmarkCategory> dataSource,
@NonNull List<SortedBlock> sortedBlocks)
{
super(dataSource);
mSortedBlocks = sortedBlocks;
}
@Override
public int getSectionsCount()
{
return mSortedBlocks.size();
}
@Override
public boolean isEditable(int sectionIndex)
{
return true;
}
@Override
public boolean hasTitle(int sectionIndex)
{
return true;
}
@Nullable
public String getTitle(int sectionIndex, Resources rs)
{
return mSortedBlocks.get(sectionIndex).title;
}
@Override
public int getItemsCount(int sectionIndex)
{
SortedBlock block = mSortedBlocks.get(sectionIndex);
if (block.bookmarkIds.size() > 0)
return block.bookmarkIds.size();
return block.trackIds.size();
}
@Override
public int getItemsType(int sectionIndex)
{
SortedBlock block = mSortedBlocks.get(sectionIndex);
if (block.bookmarkIds.size() > 0)
return TYPE_BOOKMARK;
return TYPE_TRACK;
}
@Override
public void onDelete(SectionPosition pos)
{
SortedBlock block = mSortedBlocks.get(pos.sectionIndex);
if (block.bookmarkIds.size() > 0)
{
block.bookmarkIds.remove(pos.itemIndex);
if (block.bookmarkIds.isEmpty())
mSortedBlocks.remove(pos.sectionIndex);
return;
}
block.trackIds.remove(pos.itemIndex);
if (block.trackIds.isEmpty())
mSortedBlocks.remove(pos.sectionIndex);
}
public long getBookmarkId(SectionPosition pos)
{
return mSortedBlocks.get(pos.sectionIndex).bookmarkIds.get(pos.itemIndex);
}
public long getTrackId(SectionPosition pos)
{
return mSortedBlocks.get(pos.sectionIndex).trackIds.get(pos.itemIndex);
}
}
@NonNull
private final DataSource<BookmarkCategory> mDataSource;
@Nullable
private List<Long> mSearchResults;
@Nullable
private List<SortedBlock> mSortedResults;
@NonNull
private SectionsDataSource mSectionsDataSource;
@Nullable
private RecyclerLongClickListener mLongClickListener;
@Nullable
@ -45,6 +356,47 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
BookmarkListAdapter(@NonNull DataSource<BookmarkCategory> dataSource)
{
mDataSource = dataSource;
refreshSections();
}
private void refreshSections()
{
if (mSearchResults != null)
mSectionsDataSource = new SearchResultsSectionsDataSource(mDataSource, mSearchResults);
else if (mSortedResults != null)
mSectionsDataSource = new SortedSectionsDataSource(mDataSource, mSortedResults);
else
mSectionsDataSource = new CategorySectionsDataSource(mDataSource);
}
private SectionPosition getSectionPosition(int position)
{
int startSectionRow = 0;
boolean hasTitle;
int sectionsCount = mSectionsDataSource.getSectionsCount();
for (int i = 0; i < sectionsCount; ++i)
{
hasTitle = mSectionsDataSource.hasTitle(i);
int sectionRowsCount = mSectionsDataSource.getItemsCount(i) + (hasTitle ? 1 : 0);
if (startSectionRow == position && hasTitle)
return new SectionPosition(i, SectionPosition.INVALID_POSITION);
if (startSectionRow + sectionRowsCount > position)
return new SectionPosition(i, position - startSectionRow - (hasTitle ? 1 : 0));
startSectionRow += sectionRowsCount;
}
return new SectionPosition(SectionPosition.INVALID_POSITION, SectionPosition.INVALID_POSITION);
}
public void setSearchResults(List<Long> searchResults)
{
mSearchResults = searchResults;
refreshSections();
}
public void setSortedResults(List<SortedBlock> sortedResults)
{
mSortedResults = sortedResults;
refreshSections();
}
public void setOnClickListener(@Nullable RecyclerClickListener listener)
@ -78,7 +430,7 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
break;
case TYPE_DESC:
View desc = inflater.inflate(R.layout.item_category_description, parent, false);
holder = new Holders.DescriptionViewHolder(desc, getCategory());
holder = new Holders.DescriptionViewHolder(desc, mSectionsDataSource.getCategory());
break;
}
@ -93,26 +445,18 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
@Override
public void onBindViewHolder(Holders.BaseBookmarkHolder holder, int position)
{
holder.bind(position, getCategory());
SectionPosition sp = getSectionPosition(position);
holder.bind(sp, mSectionsDataSource);
}
@Override
public int getItemViewType(int position)
{
final int descPos = getDescSectionPosition(getCategory());
final int bmkPos = getBookmarksSectionPosition(getCategory());
final int trackPos = getTracksSectionPosition(getCategory());
if (position == bmkPos || position == trackPos || position == descPos)
SectionPosition sp = getSectionPosition(position);
if (sp.isTitlePosition())
return TYPE_SECTION;
if (position > bmkPos && !isSectionEmpty(getCategory(), SECTION_BMKS))
return TYPE_BOOKMARK;
else if (position > trackPos && !isSectionEmpty(getCategory(), SECTION_TRACKS))
return TYPE_TRACK;
else if (position > descPos && !isSectionEmpty(getCategory(), SECTION_DESC))
return TYPE_DESC;
if (sp.isItemPosition())
return mSectionsDataSource.getItemsType(sp.sectionIndex);
throw new IllegalArgumentException("Position not found: " + position);
}
@ -125,10 +469,15 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
@Override
public int getItemCount()
{
return getCategory().size()
+ (isSectionEmpty(getCategory(), SECTION_TRACKS) ? 0 : 1)
+ (isSectionEmpty(getCategory(), SECTION_BMKS) ? 0 : 1)
+ getDescItemCount(getCategory());
int itemCount = 0;
int sectionsCount = mSectionsDataSource.getSectionsCount();
for (int i = 0; i < sectionsCount; ++i)
{
itemCount += mSectionsDataSource.getItemsCount(i);
if (mSectionsDataSource.hasTitle(i))
++itemCount;
}
return itemCount;
}
// FIXME: remove this heavy method and use BoomarkInfo class instead.
@ -137,23 +486,16 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
if (getItemViewType(position) == TYPE_DESC)
throw new UnsupportedOperationException("Not supported here! Position = " + position);
SectionPosition pos = getSectionPosition(position);
if (getItemViewType(position) == TYPE_TRACK)
{
int relativePos = calculateTrackPosition(getCategory(), position);
final long trackId = BookmarkManager.INSTANCE.getTrackIdByPosition(getCategory().getId(), relativePos);
final long trackId = mSectionsDataSource.getTrackId(pos);
return BookmarkManager.INSTANCE.getTrack(trackId);
}
else
{
final int pos = calculateBookmarkPosition(getCategory(), position);
final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(getCategory().getId(), pos);
final long bookmarkId = mSectionsDataSource.getBookmarkId(pos);
return BookmarkManager.INSTANCE.getBookmark(bookmarkId);
}
}
@NonNull
private BookmarkCategory getCategory()
{
return mDataSource.getData();
}
}

View file

@ -45,7 +45,9 @@ import com.mapswithme.util.sharing.ShareOption;
import com.mapswithme.util.sharing.SharingHelper;
import com.mapswithme.util.statistics.Statistics;
import java.util.SortedMap;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListAdapter>
implements RecyclerLongClickListener,
@ -65,7 +67,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListA
private CategoryDataSource mCategoryDataSource;
private int mSelectedPosition;
private long[] mSearchResults;
private List<Long> mSearchResults;
private long[] mSortResults;
private boolean mSearchMode = false;
@ -144,6 +146,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListA
{
super.onStart();
Crashlytics.log("onStart");
SearchEngine.INSTANCE.addBookmarkListener(this);
BookmarkManager.INSTANCE.addSharingListener(this);
BookmarkManager.INSTANCE.addCatalogListener(mCatalogListener);
}
@ -170,6 +173,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListA
{
super.onStop();
Crashlytics.log("onStop");
SearchEngine.INSTANCE.removeBookmarkListener(this);
BookmarkManager.INSTANCE.removeSharingListener(this);
BookmarkManager.INSTANCE.removeCatalogListener(mCatalogListener);
}
@ -275,12 +279,12 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListA
getActivity().invalidateOptionsMenu();
}
public void runSearch()
public void runSearch(@NonNull String query)
{
SearchEngine.INSTANCE.cancel();
mLastQueryTimestamp = System.nanoTime();
if (SearchEngine.INSTANCE.searchInBookmarks(mToolbarController.getQuery(),
if (SearchEngine.INSTANCE.searchInBookmarks(query,
mCategoryDataSource.getData().getId(),
mLastQueryTimestamp))
{
@ -307,13 +311,24 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<BookmarkListA
private void updateSearchResults(@Nullable long[] bookmarkIds)
{
mSearchResults = bookmarkIds;
ArrayList<Long> ids = new ArrayList<Long>(bookmarkIds.length);
for (long id : bookmarkIds)
ids.add(id);
mSearchResults = ids;
BookmarkListAdapter adapter = getAdapter();
adapter.setSearchResults(mSearchResults);
adapter.notifyDataSetChanged();
}
public void cancelSearch()
{
SearchEngine.INSTANCE.cancel();
mToolbarController.showProgress(false);
BookmarkListAdapter adapter = getAdapter();
adapter.setSearchResults(null);
adapter.notifyDataSetChanged();
}
public void activateSearch()

View file

@ -209,14 +209,6 @@ public class Holders
static abstract class BaseBookmarkHolder extends RecyclerView.ViewHolder
{
static final int SECTION_TRACKS = 0;
static final int SECTION_BMKS = 1;
static final int SECTION_DESC = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ SECTION_TRACKS, SECTION_BMKS, SECTION_DESC})
public @interface Section {}
@NonNull
private final View mView;
@ -226,79 +218,8 @@ public class Holders
mView = itemView;
}
abstract void bind(int position, @NonNull BookmarkCategory category);
static int calculateTrackPosition(@NonNull BookmarkCategory category, int position)
{
return position
- (isSectionEmpty(category, SECTION_TRACKS) ? 0 : 1)
- getDescItemCount(category);
}
static boolean isSectionEmpty(@NonNull BookmarkCategory category, @Section int section)
{
switch (section)
{
case SECTION_TRACKS:
return category.getTracksCount() == 0;
case SECTION_BMKS:
return category.getBookmarksCount() == 0;
case SECTION_DESC:
return TextUtils.isEmpty(category.getDescription()) && TextUtils.isEmpty(category.getAnnotation());
default:
throw new IllegalArgumentException("There is no section with index " + section);
}
}
static int getSectionForPosition(@NonNull BookmarkCategory category, int position)
{
if (position == getDescSectionPosition(category))
return SECTION_DESC;
if (position == getTracksSectionPosition(category))
return SECTION_TRACKS;
if (position == getBookmarksSectionPosition(category))
return SECTION_BMKS;
throw new IllegalArgumentException("There is no section in position " + position);
}
static int getDescSectionPosition(@NonNull BookmarkCategory category)
{
if (isSectionEmpty(category, SECTION_DESC))
return RecyclerView.NO_POSITION;
return 0;
}
static int getTracksSectionPosition(@NonNull BookmarkCategory category)
{
if (isSectionEmpty(category, SECTION_TRACKS))
return RecyclerView.NO_POSITION;
return getDescItemCount(category);
}
static int getBookmarksSectionPosition(@NonNull BookmarkCategory category)
{
if (isSectionEmpty(category, SECTION_BMKS))
return RecyclerView.NO_POSITION;
int beforeCurrentSectionItemsCount = getTracksSectionPosition(category);
return (beforeCurrentSectionItemsCount == RecyclerView.NO_POSITION
? getDescItemCount(category)
: beforeCurrentSectionItemsCount)
+ getTrackItemCount(category);
}
private static int getTrackItemCount(@NonNull BookmarkCategory category)
{
return category.getTracksCount() + (isSectionEmpty(category, SECTION_TRACKS) ? 0 : 1);
}
static int getDescItemCount(@NonNull BookmarkCategory category)
{
return isSectionEmpty(category, SECTION_DESC) ? 0 : /* section header */ 1 + /* non empty desc */ 1;
}
abstract void bind(@NonNull BookmarkListAdapter.SectionPosition position,
@NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource);
void setOnClickListener(@Nullable RecyclerClickListener listener)
{
@ -345,11 +266,12 @@ public class Holders
}
@Override
void bind(int position, @NonNull BookmarkCategory category)
void bind(@NonNull BookmarkListAdapter.SectionPosition position,
@NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource)
{
int pos = calculateBookmarkPosition(category, position);
final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(category.getId(), pos);
BookmarkInfo bookmark = new BookmarkInfo(category.getId(), bookmarkId);
final long bookmarkId = sectionsDataSource.getBookmarkId(position);
BookmarkInfo bookmark = new BookmarkInfo(sectionsDataSource.getCategory().getId(),
bookmarkId);
mName.setText(bookmark.getTitle());
final Location loc = LocationHelper.INSTANCE.getSavedLocation();
@ -359,15 +281,6 @@ public class Holders
UiUtils.hideIf(TextUtils.isEmpty(distanceValue), mDistance);
mIcon.setImageResource(bookmark.getIcon().getSelectedResId());
}
static int calculateBookmarkPosition(@NonNull BookmarkCategory category, int position)
{
// Since bookmarks are always below tracks and header we should take it into account
// during the bookmark's position calculation.
return calculateTrackPosition(category, position)
- category.getTracksCount()
- (isSectionEmpty(category, SECTION_BMKS) ? 0 : 1);
}
}
static class TrackViewHolder extends BaseBookmarkHolder
@ -388,10 +301,10 @@ public class Holders
}
@Override
void bind(int position, @NonNull BookmarkCategory category)
void bind(@NonNull BookmarkListAdapter.SectionPosition position,
@NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource)
{
int relativePos = calculateTrackPosition(category, position);
final long trackId = BookmarkManager.INSTANCE.getTrackIdByPosition(category.getId(), relativePos);
final long trackId = sectionsDataSource.getTrackId(position);
Track track = BookmarkManager.INSTANCE.getTrack(trackId);
mName.setText(track.getName());
mDistance.setText(new StringBuilder().append(mDistance.getContext()
@ -417,19 +330,10 @@ public class Holders
}
@Override
void bind(int position, @NonNull BookmarkCategory category)
void bind(@NonNull BookmarkListAdapter.SectionPosition position,
@NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource)
{
final int sectionIndex = getSectionForPosition(category, position);
mView.setText(getSections().get(sectionIndex));
}
private List<String> getSections()
{
final List<String> sections = new ArrayList<>();
sections.add(mView.getContext().getString(R.string.tracks_title));
sections.add(mView.getContext().getString(R.string.bookmarks));
sections.add(mView.getContext().getString(R.string.description));
return sections;
mView.setText(sectionsDataSource.getTitle(position.sectionIndex, mView.getResources()));
}
}
@ -468,11 +372,12 @@ public class Holders
}
@Override
void bind(int position, @NonNull BookmarkCategory category)
void bind(@NonNull BookmarkListAdapter.SectionPosition position,
@NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource)
{
mTitle.setText(category.getName());
bindAuthor(category);
bindDescriptionIfEmpty(category);
mTitle.setText(sectionsDataSource.getCategory().getName());
bindAuthor(sectionsDataSource.getCategory());
bindDescriptionIfEmpty(sectionsDataSource.getCategory());
}
private void bindDescriptionIfEmpty(@NonNull BookmarkCategory category)