Adds OnMoveCallback to the SwipeToDeleteCallback class.
Makes traits orderable.
This commit is contained in:
		| @@ -7,23 +7,22 @@ import androidx.room.TypeConverters; | ||||
| import com.majinnaibu.monstercards.data.MonsterDAO; | ||||
| import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.ListOfTraitsConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.SetOfLanguageConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.SetOfSkillConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.SetOfStringConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.SetOfTraitConverter; | ||||
| import com.majinnaibu.monstercards.data.converters.UUIDConverter; | ||||
| import com.majinnaibu.monstercards.models.Monster; | ||||
| import com.majinnaibu.monstercards.models.MonsterFTS; | ||||
|  | ||||
| @SuppressWarnings("unused") | ||||
| @Database(entities = {Monster.class, MonsterFTS.class}, version = 3) | ||||
| @TypeConverters({ | ||||
|         ArmorTypeConverter.class, | ||||
|         ChallengeRatingConverter.class, | ||||
|         ListOfTraitsConverter.class, | ||||
|         SetOfLanguageConverter.class, | ||||
|         SetOfSkillConverter.class, | ||||
|         SetOfStringConverter.class, | ||||
|         SetOfTraitConverter.class, | ||||
|         UUIDConverter.class, | ||||
| }) | ||||
| public abstract class AppDatabase extends RoomDatabase { | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| package com.majinnaibu.monstercards.data.converters; | ||||
|  | ||||
| import androidx.room.TypeConverter; | ||||
|  | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.reflect.TypeToken; | ||||
| import com.majinnaibu.monstercards.models.Trait; | ||||
|  | ||||
| import java.lang.reflect.Type; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
|  | ||||
| public class SetOfTraitConverter { | ||||
|     @TypeConverter | ||||
|     public static String fromSetOfTrait(Set<Trait> traits) { | ||||
|         Gson gson = new Gson(); | ||||
|         return gson.toJson(traits); | ||||
|     } | ||||
|  | ||||
|     @TypeConverter | ||||
|     public static Set<Trait> setOfTraitFromString(String string) { | ||||
|         Gson gson = new Gson(); | ||||
|         Type setType = new TypeToken<HashSet<Trait>>() { | ||||
|         }.getType(); | ||||
|         return gson.fromJson(string, setType); | ||||
|     } | ||||
| } | ||||
| @@ -187,22 +187,22 @@ public class Monster { | ||||
|     public Set<Language> languages; | ||||
|  | ||||
|     @ColumnInfo(name = "abilities", defaultValue = "[]") | ||||
|     public Set<Trait> abilities; | ||||
|     public List<Trait> abilities; | ||||
|  | ||||
|     @ColumnInfo(name = "actions", defaultValue = "[]") | ||||
|     public Set<Trait> actions; | ||||
|     public List<Trait> actions; | ||||
|  | ||||
|     @ColumnInfo(name = "reactions", defaultValue = "[]") | ||||
|     public Set<Trait> reactions; | ||||
|     public List<Trait> reactions; | ||||
|  | ||||
|     @ColumnInfo(name = "lair_actions", defaultValue = "[]") | ||||
|     public Set<Trait> lairActions; | ||||
|     public List<Trait> lairActions; | ||||
|  | ||||
|     @ColumnInfo(name = "legendary_actions", defaultValue = "[]") | ||||
|     public Set<Trait> legendaryActions; | ||||
|     public List<Trait> legendaryActions; | ||||
|  | ||||
|     @ColumnInfo(name = "regional_actions", defaultValue = "[]") | ||||
|     public Set<Trait> regionalActions; | ||||
|     public List<Trait> regionalActions; | ||||
|  | ||||
|     public Monster() { | ||||
|         id = UUID.randomUUID(); | ||||
| @@ -258,12 +258,12 @@ public class Monster { | ||||
|         damageVulnerabilities = new HashSet<>(); | ||||
|         conditionImmunities = new HashSet<>(); | ||||
|         languages = new HashSet<>(); | ||||
|         abilities = new HashSet<>(); | ||||
|         actions = new HashSet<>(); | ||||
|         reactions = new HashSet<>(); | ||||
|         lairActions = new HashSet<>(); | ||||
|         legendaryActions = new HashSet<>(); | ||||
|         regionalActions = new HashSet<>(); | ||||
|         abilities = new ArrayList<>(); | ||||
|         actions = new ArrayList<>(); | ||||
|         reactions = new ArrayList<>(); | ||||
|         lairActions = new ArrayList<>(); | ||||
|         legendaryActions = new ArrayList<>(); | ||||
|         regionalActions = new ArrayList<>(); | ||||
|     } | ||||
|  | ||||
|     public static int getAbilityModifierForScore(int score) { | ||||
|   | ||||
| @@ -75,11 +75,11 @@ public class EditLanguagesFragment extends MCFragment { | ||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||
|         recyclerView.addItemDecoration(dividerItemDecoration); | ||||
|  | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> { | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> { | ||||
|             if (position > 0) { | ||||
|                 mViewModel.removeLanguage(position - 1); | ||||
|             } | ||||
|         })); | ||||
|         }, null)); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -992,12 +992,12 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel { | ||||
|         monster.damageVulnerabilities = new HashSet<>(mDamageVulnerabilities.getValue()); | ||||
|         monster.conditionImmunities = new HashSet<>(mConditionImmunities.getValue()); | ||||
|         monster.languages = new HashSet<>(mLanguages.getValue()); | ||||
|         monster.abilities = new HashSet<>(mAbilities.getValue()); | ||||
|         monster.actions = new HashSet<>(mActions.getValue()); | ||||
|         monster.reactions = new HashSet<>(mReactions.getValue()); | ||||
|         monster.lairActions = new HashSet<>(mLairActions.getValue()); | ||||
|         monster.legendaryActions = new HashSet<>(mLegendaryActions.getValue()); | ||||
|         monster.regionalActions = new HashSet<>(mRegionalActions.getValue()); | ||||
|         monster.abilities = new ArrayList<>(mAbilities.getValue()); | ||||
|         monster.actions = new ArrayList<>(mActions.getValue()); | ||||
|         monster.reactions = new ArrayList<>(mReactions.getValue()); | ||||
|         monster.lairActions = new ArrayList<>(mLairActions.getValue()); | ||||
|         monster.legendaryActions = new ArrayList<>(mLegendaryActions.getValue()); | ||||
|         monster.regionalActions = new ArrayList<>(mRegionalActions.getValue()); | ||||
|  | ||||
|         return monster; | ||||
|     } | ||||
| @@ -1176,12 +1176,30 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("SameParameterValue") | ||||
|     private static class Helpers { | ||||
|         static String addStringToList(String newString, MutableLiveData<List<String>> strings) { | ||||
|             return addItemToList(strings, newString, String::compareToIgnoreCase); | ||||
|     public boolean moveTrait(TraitType type, int from, int to) { | ||||
|         switch (type) { | ||||
|             case ABILITY: | ||||
|                 return Helpers.moveItemInList(mAbilities, from, to); | ||||
|             case ACTION: | ||||
|                 return Helpers.moveItemInList(mActions, from, to); | ||||
|             case LAIR_ACTION: | ||||
|                 return Helpers.moveItemInList(mLairActions, from, to); | ||||
|             case LEGENDARY_ACTION: | ||||
|                 return Helpers.moveItemInList(mLegendaryActions, from, to); | ||||
|             case REACTIONS: | ||||
|                 return Helpers.moveItemInList(mReactions, from, to); | ||||
|             case REGIONAL_ACTION: | ||||
|                 return Helpers.moveItemInList(mRegionalActions, from, to); | ||||
|             default: | ||||
|                 Logger.logUnimplementedFeature(String.format("Unrecognized TraitType: %s", type)); | ||||
|                 return false; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("SameParameterValue") | ||||
|     private static class Helpers { | ||||
|         static <T> T addItemToList(MutableLiveData<List<T>> listData, T newItem) { | ||||
|             return addItemToList(listData, newItem, null); | ||||
|         } | ||||
| @@ -1228,6 +1246,7 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel { | ||||
|             listData.setValue(newList); | ||||
|         } | ||||
|  | ||||
|         @SuppressWarnings("unused") | ||||
|         static <T> void replaceItemInList(MutableLiveData<List<T>> listData, int position, T newItem) { | ||||
|             replaceItemInList(listData, position, newItem, null); | ||||
|         } | ||||
| @@ -1266,5 +1285,23 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel { | ||||
|             } | ||||
|             return value; | ||||
|         } | ||||
|  | ||||
|         static <T> boolean moveItemInList(ChangeTrackedLiveData<List<T>> listData, int from, int to) { | ||||
|             List<T> oldList = listData.getValue(); | ||||
|             if (oldList == null) { | ||||
|                 oldList = new ArrayList<>(); | ||||
|             } | ||||
|             ArrayList<T> newList = new ArrayList<>(oldList); | ||||
|             T item = oldList.get(from); | ||||
|             if (from > to) { | ||||
|                 from = from + 1; | ||||
|             } else if (to > from) { | ||||
|                 to = to + 1; | ||||
|             } | ||||
|             newList.add(to, item); | ||||
|             newList.remove(from); | ||||
|             listData.setValue(newList); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -70,7 +70,7 @@ public class EditSkillsFragment extends MCFragment { | ||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||
|         recyclerView.addItemDecoration(dividerItemDecoration); | ||||
|  | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeSkill)); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeSkill(position), null)); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -101,7 +101,7 @@ public class EditStringsFragment extends MCFragment { | ||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||
|         recyclerView.addItemDecoration(dividerItemDecoration); | ||||
|  | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> mViewModel.removeString(mStringType, position))); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeString(mStringType, position), null)); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ public class EditTraitsFragment extends MCFragment { | ||||
|     private EditMonsterViewModel mViewModel; | ||||
|     private ViewHolder mHolder; | ||||
|     private TraitType mTraitType; | ||||
|     private EditTraitsRecyclerViewAdapter mAdapter; | ||||
|  | ||||
|  | ||||
|     @Override | ||||
| @@ -88,22 +89,21 @@ public class EditTraitsFragment extends MCFragment { | ||||
|         recyclerView.setLayoutManager(layoutManager); | ||||
|  | ||||
|         LiveData<List<Trait>> traitData = mViewModel.getTraits(mTraitType); | ||||
|         if (traitData != null) { | ||||
|             traitData.observe(getViewLifecycleOwner(), traits -> { | ||||
|                 EditTraitsRecyclerViewAdapter adapter = new EditTraitsRecyclerViewAdapter(traits, trait -> { | ||||
|         mAdapter = new EditTraitsRecyclerViewAdapter(trait -> { | ||||
|             if (trait != null) { | ||||
|                 navigateToEditTrait(trait); | ||||
|             } else { | ||||
|                 Logger.logError("Can't navigate to EditTraitFragment with a null trait"); | ||||
|             } | ||||
|         }); | ||||
|                 recyclerView.setAdapter(adapter); | ||||
|             }); | ||||
|         if (traitData != null) { | ||||
|             traitData.observe(getViewLifecycleOwner(), traits -> mAdapter.submitList(traits)); | ||||
|         } | ||||
|         recyclerView.setAdapter(mAdapter); | ||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||
|         recyclerView.addItemDecoration(dividerItemDecoration); | ||||
|  | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, position -> mViewModel.removeTrait(mTraitType, position))); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, (position, direction) -> mViewModel.removeTrait(mTraitType, position), (from, to) -> mViewModel.moveTrait(mTraitType, from, to))); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,9 @@ import android.view.LayoutInflater; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.DiffUtil; | ||||
| import androidx.recyclerview.widget.ListAdapter; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import com.majinnaibu.monstercards.databinding.FragmentEditTraitsListItemBinding; | ||||
| @@ -11,14 +14,23 @@ import com.majinnaibu.monstercards.models.Trait; | ||||
|  | ||||
| import org.jetbrains.annotations.NotNull; | ||||
|  | ||||
| import java.util.List; | ||||
| public class EditTraitsRecyclerViewAdapter extends ListAdapter<Trait, EditTraitsRecyclerViewAdapter.ViewHolder> { | ||||
|     private static final DiffUtil.ItemCallback<Trait> DIFF_CALLBACK = new DiffUtil.ItemCallback<Trait>() { | ||||
|  | ||||
| public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTraitsRecyclerViewAdapter.ViewHolder> { | ||||
|     private final List<Trait> mValues; | ||||
|         @Override | ||||
|         public boolean areItemsTheSame(@NonNull @NotNull Trait oldItem, @NonNull @NotNull Trait newItem) { | ||||
|             return oldItem.equals(newItem); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public boolean areContentsTheSame(@NonNull @NotNull Trait oldItem, @NonNull @NotNull Trait newItem) { | ||||
|             return oldItem.equals(newItem); | ||||
|         } | ||||
|     }; | ||||
|     private final ItemCallback mOnClick; | ||||
|  | ||||
|     public EditTraitsRecyclerViewAdapter(List<Trait> items, ItemCallback onClick) { | ||||
|         mValues = items; | ||||
|     protected EditTraitsRecyclerViewAdapter(ItemCallback onClick) { | ||||
|         super(DIFF_CALLBACK); | ||||
|         mOnClick = onClick; | ||||
|     } | ||||
|  | ||||
| @@ -30,8 +42,8 @@ public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTrai | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(final ViewHolder holder, int position) { | ||||
|         holder.mItem = mValues.get(position); | ||||
|         holder.mContentView.setText(mValues.get(position).name); | ||||
|         holder.mItem = getItem(position); | ||||
|         holder.mContentView.setText(holder.mItem.name); | ||||
|         holder.itemView.setOnClickListener(v -> { | ||||
|             if (mOnClick != null) { | ||||
|                 mOnClick.onItemCallback(holder.mItem); | ||||
| @@ -39,11 +51,6 @@ public class EditTraitsRecyclerViewAdapter extends RecyclerView.Adapter<EditTrai | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return mValues.size(); | ||||
|     } | ||||
|  | ||||
|     public interface ItemCallback { | ||||
|         void onItemCallback(Trait trait); | ||||
|     } | ||||
|   | ||||
| @@ -76,7 +76,7 @@ public class LibraryFragment extends MCFragment { | ||||
|         DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); | ||||
|         recyclerView.addItemDecoration(dividerItemDecoration); | ||||
|  | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), adapter::deleteItem)); | ||||
|         ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(requireContext(), (position, direction) -> adapter.deleteItem(position), null)); | ||||
|         itemTouchHelper.attachToRecyclerView(recyclerView); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -21,11 +21,14 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { | ||||
|     private final Drawable icon; | ||||
|     private final ColorDrawable background; | ||||
|     private final Paint clearPaint; | ||||
|     private final OnDeleteCallback mOnDelete; | ||||
|     private final OnSwipeCallback mOnDelete; | ||||
|     private final OnMoveCallback mOnMove; | ||||
|     private final Context mContext; | ||||
|     public SwipeToDeleteCallback(Context context, OnDeleteCallback onDelete) { | ||||
|         super(0, ItemTouchHelper.LEFT); | ||||
|  | ||||
|     public SwipeToDeleteCallback(Context context, OnSwipeCallback onDelete, OnMoveCallback onMove) { | ||||
|         super(onMove == null ? 0 : ItemTouchHelper.UP | ItemTouchHelper.DOWN, onDelete == null ? 0 : ItemTouchHelper.LEFT); | ||||
|         mOnDelete = onDelete; | ||||
|         mOnMove = onMove; | ||||
|         mContext = context; | ||||
|         icon = ContextCompat.getDrawable(mContext, R.drawable.ic_delete_white_36); | ||||
|         background = new ColorDrawable(context.getResources().getColor(R.color.red)); | ||||
| @@ -39,6 +42,11 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { | ||||
|             @NonNull RecyclerView.ViewHolder viewHolder, | ||||
|             @NonNull RecyclerView.ViewHolder target | ||||
|     ) { | ||||
|         if (mOnMove != null) { | ||||
|             int from = viewHolder.getAdapterPosition(); | ||||
|             int to = target.getAdapterPosition(); | ||||
|             return mOnMove.onMove(from, to); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @@ -46,7 +54,7 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { | ||||
|     public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { | ||||
|         if (mOnDelete != null) { | ||||
|             int position = viewHolder.getAdapterPosition(); | ||||
|             mOnDelete.onDelete(position); | ||||
|             mOnDelete.onSwipe(position, direction); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -80,7 +88,11 @@ public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { | ||||
|         super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); | ||||
|     } | ||||
|  | ||||
|     public interface OnDeleteCallback { | ||||
|         void onDelete(int position); | ||||
|     public interface OnSwipeCallback { | ||||
|         void onSwipe(int position, int direction); | ||||
|     } | ||||
|  | ||||
|     public interface OnMoveCallback { | ||||
|         boolean onMove(int from, int to); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Tom Hicks
					Tom Hicks