From 3a918fea6fe3d0c542bad74a9c15f8f8707bd08b Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Fri, 25 Jun 2021 14:33:40 -0700 Subject: [PATCH] Adds ability editor. --- .../ui/editmonster/EditAbilitiesFragment.java | 89 +++++++++++++++++++ .../EditAbilitiesRecyclerViewAdapter.java | 67 ++++++++++++++ .../ui/editmonster/EditAbilityFragment.java | 82 +++++++++++++++++ .../ui/editmonster/EditMonsterFragment.java | 5 ++ .../ui/editmonster/EditTraitViewModel.java | 61 +++++++++++++ .../layout/fragment_edit_abilities_list.xml | 33 +++++++ .../fragment_edit_abilities_list_item.xml | 13 +++ .../main/res/layout/fragment_edit_trait.xml | 37 ++++++++ .../main/res/navigation/mobile_navigation.xml | 22 +++++ app/src/main/res/values/strings.xml | 1 + 10 files changed, 410 insertions(+) create mode 100644 app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesFragment.java create mode 100644 app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesRecyclerViewAdapter.java create mode 100644 app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilityFragment.java create mode 100644 app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditTraitViewModel.java create mode 100644 app/src/main/res/layout/fragment_edit_abilities_list.xml create mode 100644 app/src/main/res/layout/fragment_edit_abilities_list_item.xml create mode 100644 app/src/main/res/layout/fragment_edit_trait.xml diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesFragment.java new file mode 100644 index 0000000..ce0a0d8 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesFragment.java @@ -0,0 +1,89 @@ +package com.majinnaibu.monstercards.ui.editmonster; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavBackStackEntry; +import androidx.navigation.NavController; +import androidx.navigation.NavDirections; +import androidx.navigation.Navigation; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.majinnaibu.monstercards.R; +import com.majinnaibu.monstercards.models.Trait; +import com.majinnaibu.monstercards.ui.shared.MCFragment; +import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; +import com.majinnaibu.monstercards.utils.Logger; + +public class EditAbilitiesFragment extends MCFragment { + private EditMonsterViewModel mViewModel; + private ViewHolder mHolder; + + private void navigateToEditAbility(Trait trait) { + NavDirections action = EditAbilitiesFragmentDirections.actionEditAbilitiesFragmentToEditAbilityFragment(trait.description, trait.name); + Navigation.findNavController(requireView()).navigate(action); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); + NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); + mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); + View root = inflater.inflate(R.layout.fragment_edit_abilities_list, container, false); + mHolder = new ViewHolder(root); + setupRecyclerView(mHolder.list); + setupAddAbilityButton(mHolder.addAbility); + return root; + } + + private void setupRecyclerView(@NonNull RecyclerView recyclerView) { + Context context = requireContext(); + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + recyclerView.setLayoutManager(layoutManager); + + mViewModel.getAbilities().observe(getViewLifecycleOwner(), abilities -> { + EditAbilitiesRecyclerViewAdapter adapter = new EditAbilitiesRecyclerViewAdapter(abilities, ability -> { + if (ability != null) { + navigateToEditAbility(ability); + } else { + Logger.logError("Can't navigate to EditAbilityFragment with a null ability"); + } + }); + recyclerView.setAdapter(adapter); + }); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); + recyclerView.addItemDecoration(dividerItemDecoration); + + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeAbility)); + itemTouchHelper.attachToRecyclerView(recyclerView); + } + + private void setupAddAbilityButton(@NonNull FloatingActionButton fab) { + fab.setOnClickListener(view -> { + Trait ability = mViewModel.addNewAbility(); + navigateToEditAbility(ability); + }); + } + + private static class ViewHolder { + RecyclerView list; + FloatingActionButton addAbility; + + ViewHolder(View root) { + list = root.findViewById(R.id.list); + addAbility = root.findViewById(R.id.add_ability); + } + } +} diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesRecyclerViewAdapter.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesRecyclerViewAdapter.java new file mode 100644 index 0000000..32f4f44 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilitiesRecyclerViewAdapter.java @@ -0,0 +1,67 @@ +package com.majinnaibu.monstercards.ui.editmonster; + +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.majinnaibu.monstercards.databinding.FragmentEditAbilitiesListItemBinding; +import com.majinnaibu.monstercards.models.Trait; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class EditAbilitiesRecyclerViewAdapter extends RecyclerView.Adapter { + private final List mValues; + private final ItemCallback mOnClick; + + public EditAbilitiesRecyclerViewAdapter(List items, ItemCallback onClick) { + mValues = items; + mOnClick = onClick; + } + + @NotNull + @Override + public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) { + return new ViewHolder(FragmentEditAbilitiesListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + holder.mItem = mValues.get(position); + holder.mContentView.setText(mValues.get(position).name); + holder.itemView.setOnClickListener(v -> { + if (mOnClick != null) { + mOnClick.onItemCallback(holder.mItem); + } + }); + } + + @Override + public int getItemCount() { + return mValues.size(); + } + + public interface ItemCallback { + void onItemCallback(Trait trait); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + public final TextView mContentView; + public Trait mItem; + + public ViewHolder(FragmentEditAbilitiesListItemBinding binding) { + super(binding.getRoot()); + mContentView = binding.content; + } + + @NotNull + @Override + public String toString() { + return super.toString() + " '" + mContentView.getText() + "'"; + } + + } +} diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilityFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilityFragment.java new file mode 100644 index 0000000..ee3a4e9 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditAbilityFragment.java @@ -0,0 +1,82 @@ +package com.majinnaibu.monstercards.ui.editmonster; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; + +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.NavBackStackEntry; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; + +import com.majinnaibu.monstercards.R; +import com.majinnaibu.monstercards.models.Trait; +import com.majinnaibu.monstercards.ui.shared.MCFragment; +import com.majinnaibu.monstercards.utils.Logger; +import com.majinnaibu.monstercards.utils.TextChangedListener; + +public class EditAbilityFragment extends MCFragment { + private EditMonsterViewModel mEditMonsterViewModel; + private EditTraitViewModel mViewModel; + private ViewHolder mHolder; + private Trait mOldValue; + + @Override + public void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) { + mViewModel = new ViewModelProvider(this).get(EditTraitViewModel.class); + if (getArguments() != null) { + EditAbilityFragmentArgs args = EditAbilityFragmentArgs.fromBundle(getArguments()); + mOldValue = new Trait(args.getName(), args.getDescription()); + mViewModel.copyFromTrait(mOldValue); + } else { + Logger.logWTF("EditAbilityFragment needs arguments"); + mOldValue = null; + } + + super.onCreate(savedInstanceState); + } + + @Nullable + @org.jetbrains.annotations.Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); + NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); + mEditMonsterViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); + View root = inflater.inflate(R.layout.fragment_edit_trait, container, false); + mHolder = new ViewHolder(root); + + mHolder.name.setText(mViewModel.getNameAsString()); + mHolder.name.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setName(s.toString()))); + + mHolder.description.setText(mViewModel.getDescriptionAsString()); + mHolder.description.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setDescription(s.toString()))); + + requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (mViewModel.hasChanges()) { + mEditMonsterViewModel.replaceAbility(mOldValue, mViewModel.getAbilityValue()); + } + Navigation.findNavController(requireView()).navigateUp(); + } + }); + + return root; + } + + private static class ViewHolder { + EditText description; + EditText name; + + ViewHolder(View root) { + description = root.findViewById(R.id.description); + name = root.findViewById(R.id.name); + } + } +} diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java index 708ee88..1841e13 100644 --- a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java @@ -145,6 +145,11 @@ public class EditMonsterFragment extends MCFragment { Navigation.findNavController(requireView()).navigate(action); }); + mHolder.abilities.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditAbilitiesFragment(); + Navigation.findNavController(requireView()).navigate(action); + }); + requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditTraitViewModel.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditTraitViewModel.java new file mode 100644 index 0000000..8293160 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditTraitViewModel.java @@ -0,0 +1,61 @@ +package com.majinnaibu.monstercards.ui.editmonster; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import com.majinnaibu.monstercards.models.Trait; +import com.majinnaibu.monstercards.ui.shared.ChangeTrackedViewModel; +import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData; + +public class EditTraitViewModel extends ChangeTrackedViewModel { + private final ChangeTrackedLiveData mName; + private final ChangeTrackedLiveData mDescription; + private final MutableLiveData mAbility; + + public EditTraitViewModel() { + super(); + mName = new ChangeTrackedLiveData<>("", this::makeDirty); + mDescription = new ChangeTrackedLiveData<>("", this::makeDirty); + mAbility = new MutableLiveData<>(makeAbility()); + } + + public LiveData getName() { + return mName; + } + + public void setName(String name) { + mName.setValue(name); + mAbility.setValue(makeAbility()); + } + + public String getNameAsString() { + return mName.getValue(); + } + + public LiveData getDescription() { + return mDescription; + } + + public void setDescription(String description) { + mDescription.setValue(description); + mAbility.setValue(makeAbility()); + } + + public String getDescriptionAsString() { + return mDescription.getValue(); + } + + public Trait getAbilityValue() { + return mAbility.getValue(); + } + + public void copyFromTrait(Trait trait) { + makeClean(); + mName.resetValue(trait.name); + mDescription.resetValue(trait.description); + } + + private Trait makeAbility() { + return new Trait(mName.getValue(), mDescription.getValue()); + } +} diff --git a/app/src/main/res/layout/fragment_edit_abilities_list.xml b/app/src/main/res/layout/fragment_edit_abilities_list.xml new file mode 100644 index 0000000..890e5b2 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_abilities_list.xml @@ -0,0 +1,33 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_abilities_list_item.xml b/app/src/main/res/layout/fragment_edit_abilities_list_item.xml new file mode 100644 index 0000000..6e9049a --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_abilities_list_item.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_trait.xml b/app/src/main/res/layout/fragment_edit_trait.xml new file mode 100644 index 0000000..a055046 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_trait.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 64226ac..d2f48e8 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -111,6 +111,9 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8476c0a..3ee197d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ + Add Ability Add Condition Add Damage Type Add Language