diff --git a/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java b/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java index 56e6239cd5..ceab97eab0 100644 --- a/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java +++ b/android/src/com/mapswithme/maps/bookmarks/BookmarkListAdapter.java @@ -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 { - @NonNull - private final DataSource 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 bookmarkIds; + public List trackIds; + } + + public abstract class SectionsDataSource + { + @NonNull + protected final DataSource mDataSource; + + SectionsDataSource(@NonNull DataSource 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 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 mSearchResults; + + SearchResultsSectionsDataSource(@NonNull DataSource dataSource, + @NonNull List 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 mSortedBlocks; + + SortedSectionsDataSource(@NonNull DataSource dataSource, + @NonNull List 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 mDataSource; + @Nullable + private List mSearchResults; + @Nullable + private List mSortedResults; + + @NonNull + private SectionsDataSource mSectionsDataSource; + @Nullable private RecyclerLongClickListener mLongClickListener; @Nullable @@ -45,6 +356,47 @@ public class BookmarkListAdapter extends RecyclerView.Adapter 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 searchResults) + { + mSearchResults = searchResults; + refreshSections(); + } + + public void setSortedResults(List sortedResults) + { + mSortedResults = sortedResults; + refreshSections(); } public void setOnClickListener(@Nullable RecyclerClickListener listener) @@ -78,7 +430,7 @@ public class BookmarkListAdapter extends RecyclerView.Adapter 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 ids = new ArrayList(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() diff --git a/android/src/com/mapswithme/maps/bookmarks/Holders.java b/android/src/com/mapswithme/maps/bookmarks/Holders.java index c7e796ea4e..23dfa6e729 100644 --- a/android/src/com/mapswithme/maps/bookmarks/Holders.java +++ b/android/src/com/mapswithme/maps/bookmarks/Holders.java @@ -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 getSections() - { - final List 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)