Adds collections to search and disambiguates results.
This commit is contained in:
@@ -8,6 +8,7 @@ import com.majinnaibu.monstercards.models.Collection;
|
||||
import com.majinnaibu.monstercards.models.CollectionMonster;
|
||||
import com.majinnaibu.monstercards.models.CollectionWithCount;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.SearchResultItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -50,6 +51,43 @@ public class MonsterRepository {
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Flowable<List<SearchResultItem>> searchAll(String searchText) {
|
||||
Flowable<List<Monster>> monstersFlowable = m_db.monsterDAO()
|
||||
.getAll()
|
||||
.map(monsters -> {
|
||||
ArrayList<Monster> filteredMonsters = new ArrayList<>();
|
||||
for (Monster monster : monsters) {
|
||||
if (Helpers.monsterMatchesSearch(monster, searchText)) {
|
||||
filteredMonsters.add(monster);
|
||||
}
|
||||
}
|
||||
return (List<Monster>) filteredMonsters;
|
||||
});
|
||||
|
||||
Flowable<List<Collection>> collectionsFlowable = m_db.collectionDAO()
|
||||
.getAll()
|
||||
.map(collections -> {
|
||||
ArrayList<Collection> filteredCollections = new ArrayList<>();
|
||||
for (Collection collection : collections) {
|
||||
if (StringHelper.isNullOrEmpty(searchText) || StringHelper.containsCaseInsensitive(collection.name, searchText)) {
|
||||
filteredCollections.add(collection);
|
||||
}
|
||||
}
|
||||
return (List<Collection>) filteredCollections;
|
||||
});
|
||||
|
||||
return Flowable.combineLatest(monstersFlowable, collectionsFlowable, (monsters, collections) -> {
|
||||
ArrayList<SearchResultItem> results = new ArrayList<>();
|
||||
for (Monster monster : monsters) {
|
||||
results.add(new SearchResultItem(monster));
|
||||
}
|
||||
for (Collection collection : collections) {
|
||||
results.add(new SearchResultItem(collection));
|
||||
}
|
||||
return (List<SearchResultItem>) results;
|
||||
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Flowable<Monster> getMonster(@NonNull UUID monsterId) {
|
||||
return m_db.monsterDAO()
|
||||
.loadAllByIds(new String[]{monsterId.toString()})
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.majinnaibu.monstercards.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class SearchResultItem {
|
||||
public enum Type {
|
||||
MONSTER,
|
||||
COLLECTION
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public final Type type;
|
||||
|
||||
@Nullable
|
||||
public final Monster monster;
|
||||
|
||||
@Nullable
|
||||
public final Collection collection;
|
||||
|
||||
public SearchResultItem(@NonNull Monster monster) {
|
||||
this.type = Type.MONSTER;
|
||||
this.monster = monster;
|
||||
this.collection = null;
|
||||
}
|
||||
|
||||
public SearchResultItem(@NonNull Collection collection) {
|
||||
this.type = Type.COLLECTION;
|
||||
this.monster = null;
|
||||
this.collection = collection;
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,14 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.models.SearchResultItem;
|
||||
import com.majinnaibu.monstercards.ui.shared.MCFragment;
|
||||
|
||||
public class SearchFragment extends MCFragment {
|
||||
@@ -22,7 +25,17 @@ public class SearchFragment extends MCFragment {
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(R.layout.fragment_search, container, false);
|
||||
MonsterRepository repository = this.getMonsterRepository();
|
||||
SearchResultsRecyclerViewAdapter adapter = new SearchResultsRecyclerViewAdapter(repository, null);
|
||||
SearchResultsRecyclerViewAdapter adapter = new SearchResultsRecyclerViewAdapter(repository, item -> {
|
||||
if (item != null) {
|
||||
if (item.type == SearchResultItem.Type.MONSTER && item.monster != null) {
|
||||
NavDirections action = SearchFragmentDirections.actionNavigationSearchToNavigationMonster(item.monster.id.toString());
|
||||
Navigation.findNavController(requireView()).navigate(action);
|
||||
} else if (item.type == SearchResultItem.Type.COLLECTION && item.collection != null) {
|
||||
NavDirections action = SearchFragmentDirections.actionNavigationSearchToCollectionDetailFragment(item.collection.id.toString());
|
||||
Navigation.findNavController(requireView()).navigate(action);
|
||||
}
|
||||
}
|
||||
});
|
||||
final RecyclerView recyclerView = root.findViewById(R.id.monster_list);
|
||||
assert recyclerView != null;
|
||||
setupRecyclerView(recyclerView, adapter);
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.majinnaibu.monstercards.ui.search;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -10,7 +11,10 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.helpers.StringHelper;
|
||||
import com.majinnaibu.monstercards.models.Collection;
|
||||
import com.majinnaibu.monstercards.models.Monster;
|
||||
import com.majinnaibu.monstercards.models.SearchResultItem;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -23,7 +27,7 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
|
||||
private final MonsterRepository mRepository;
|
||||
private final ItemCallback mOnClickHandler;
|
||||
private String mSearchText;
|
||||
private List<Monster> mValues;
|
||||
private List<SearchResultItem> mValues;
|
||||
private Disposable mSubscriptionHandler;
|
||||
|
||||
public SearchResultsRecyclerViewAdapter(MonsterRepository repository,
|
||||
@@ -42,9 +46,9 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
|
||||
mSubscriptionHandler.dispose();
|
||||
}
|
||||
mSearchText = searchText;
|
||||
Flowable<List<Monster>> foundMonsters = mRepository.searchMonsters(mSearchText);
|
||||
mSubscriptionHandler = foundMonsters.subscribe(monsters -> {
|
||||
mValues = monsters;
|
||||
Flowable<List<SearchResultItem>> resultsFlowable = mRepository.searchAll(mSearchText);
|
||||
mSubscriptionHandler = resultsFlowable.subscribe(results -> {
|
||||
mValues = results;
|
||||
notifyDataSetChanged();
|
||||
},
|
||||
throwable -> Logger.logError("Error performing search", throwable));
|
||||
@@ -54,18 +58,52 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.monster_list_content, parent, false);
|
||||
.inflate(R.layout.search_result_list_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
|
||||
Monster monster = mValues.get(position);
|
||||
holder.mContentView.setText(monster.name);
|
||||
holder.itemView.setTag(monster);
|
||||
SearchResultItem item = mValues.get(position);
|
||||
if (item.type == SearchResultItem.Type.MONSTER && item.monster != null) {
|
||||
Monster monster = item.monster;
|
||||
holder.mTitleView.setText(monster.name);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!StringHelper.isNullOrEmpty(monster.alignment)) {
|
||||
sb.append(monster.alignment);
|
||||
}
|
||||
String cr = monster.getChallengeRatingDescription();
|
||||
if (!StringHelper.isNullOrEmpty(cr)) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("CR ").append(cr);
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
holder.mSubtitleView.setText(sb.toString());
|
||||
holder.mSubtitleView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.mSubtitleView.setVisibility(View.GONE);
|
||||
}
|
||||
holder.mIconView.setVisibility(View.GONE);
|
||||
} else if (item.type == SearchResultItem.Type.COLLECTION && item.collection != null) {
|
||||
Collection collection = item.collection;
|
||||
holder.mTitleView.setText(collection.name);
|
||||
|
||||
if (!StringHelper.isNullOrEmpty(collection.description)) {
|
||||
holder.mSubtitleView.setText(collection.description);
|
||||
holder.mSubtitleView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.mSubtitleView.setVisibility(View.GONE);
|
||||
}
|
||||
holder.mIconView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
holder.itemView.setTag(item);
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (mOnClickHandler != null) {
|
||||
mOnClickHandler.onItem(monster);
|
||||
mOnClickHandler.onItem(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -76,15 +114,19 @@ public class SearchResultsRecyclerViewAdapter extends RecyclerView.Adapter<Searc
|
||||
}
|
||||
|
||||
public interface ItemCallback {
|
||||
void onItem(Monster monster);
|
||||
void onItem(SearchResultItem item);
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final TextView mContentView;
|
||||
final TextView mTitleView;
|
||||
final TextView mSubtitleView;
|
||||
final ImageView mIconView;
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
mContentView = view.findViewById(R.id.content);
|
||||
mTitleView = view.findViewById(R.id.title);
|
||||
mSubtitleView = view.findViewById(R.id.subtitle);
|
||||
mIconView = view.findViewById(R.id.icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user