diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillFragment.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillFragment.java index 0e5a93e..1eb08dc 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillFragment.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillFragment.java @@ -9,6 +9,7 @@ import android.widget.EditText; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavController; @@ -19,25 +20,24 @@ import com.majinnaibu.monstercards.models.Skill; import com.majinnaibu.monstercards.ui.components.AbilityScorePicker; import com.majinnaibu.monstercards.ui.components.AdvantagePicker; import com.majinnaibu.monstercards.ui.components.ProficiencyPicker; -import com.majinnaibu.monstercards.ui.shared.MCFragment; import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.TextChangedListener; -public class EditSkillFragment extends MCFragment { +public class EditSkillFragment extends Fragment { private EditMonsterViewModel mEditMonsterViewModel; private EditSkillViewModel mViewModel; private ViewHolder mHolder; private Skill mOldSkill; @Override - public void onCreate(@Nullable Bundle savedInstanceState) { + public void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) { mViewModel = new ViewModelProvider(this).get(EditSkillViewModel.class); if (getArguments() != null) { EditSkillFragmentArgs args = EditSkillFragmentArgs.fromBundle(getArguments()); + mViewModel.copyFromSkill(args.getName(), args.getAbilityScore(), args.getProficiency(), args.getAdvantage()); mOldSkill = new Skill(args.getName(), args.getAbilityScore(), args.getAdvantage(), args.getProficiency()); - mViewModel.copyFromSkill(mOldSkill); } else { - Logger.logWTF("EditSkillFragment needs arguments."); + Logger.logWTF("This should never happen. EditSkillFragment needs arguments."); mOldSkill = null; } super.onCreate(savedInstanceState); @@ -49,7 +49,10 @@ public class EditSkillFragment extends MCFragment { 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); + + // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_edit_skill, container, false); + mHolder = new ViewHolder(root); mHolder.abilityScore.setValue(mViewModel.getAbilityScore().getValue()); @@ -77,19 +80,13 @@ public class EditSkillFragment extends MCFragment { return root; } - @Override - public void onStart() { - super.onStart(); - mHolder.name.requestFocus(); - } - private static class ViewHolder { AbilityScorePicker abilityScore; AdvantagePicker advantage; ProficiencyPicker proficiency; EditText name; - ViewHolder(@NonNull View root) { + ViewHolder(View root) { abilityScore = root.findViewById(R.id.abilityScore); advantage = root.findViewById(R.id.advantage); proficiency = root.findViewById(R.id.proficiency); diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillViewModel.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillViewModel.java index dd53c77..1d33390 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillViewModel.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillViewModel.java @@ -1,37 +1,57 @@ package com.majinnaibu.monstercards.ui.editmonster; -import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; import com.majinnaibu.monstercards.data.enums.AbilityScore; import com.majinnaibu.monstercards.data.enums.AdvantageType; import com.majinnaibu.monstercards.data.enums.ProficiencyType; import com.majinnaibu.monstercards.models.Skill; -import com.majinnaibu.monstercards.ui.shared.ChangeTrackedViewModel; import com.majinnaibu.monstercards.utils.ChangeTrackedLiveData; -public class EditSkillViewModel extends ChangeTrackedViewModel { +public class EditSkillViewModel extends ViewModel { private final ChangeTrackedLiveData mAbilityScore; private final ChangeTrackedLiveData mAdvantageType; + private final MutableLiveData mHasChanges; private final ChangeTrackedLiveData mProficiencyType; private final ChangeTrackedLiveData mName; private final ChangeTrackedLiveData mSkill; public EditSkillViewModel() { - super(); - mAbilityScore = new ChangeTrackedLiveData<>(AbilityScore.STRENGTH, this::makeDirty); - mAdvantageType = new ChangeTrackedLiveData<>(AdvantageType.NONE, this::makeDirty); - mProficiencyType = new ChangeTrackedLiveData<>(ProficiencyType.NONE, this::makeDirty); - mName = new ChangeTrackedLiveData<>("New Skill", this::makeDirty); - mSkill = new ChangeTrackedLiveData<>(makeSkill(), this::makeDirty); + mHasChanges = new MutableLiveData<>(false); + ChangeTrackedLiveData.OnValueDirtiedCallback onDirtied = () -> mHasChanges.setValue(true); + + mAbilityScore = new ChangeTrackedLiveData<>(AbilityScore.STRENGTH, onDirtied); + mAdvantageType = new ChangeTrackedLiveData<>(AdvantageType.NONE, onDirtied); + mProficiencyType = new ChangeTrackedLiveData<>(ProficiencyType.NONE, onDirtied); + mName = new ChangeTrackedLiveData<>("Unknown Skill", onDirtied); + mSkill = new ChangeTrackedLiveData<>(makeSkill(), onDirtied); } - public void copyFromSkill(@NonNull Skill skill) { + public EditSkillViewModel(Skill skill) { + mHasChanges = new MutableLiveData<>(false); + ChangeTrackedLiveData.OnValueDirtiedCallback onDirtied = () -> mHasChanges.setValue(true); + + mAbilityScore = new ChangeTrackedLiveData<>(skill.abilityScore, onDirtied); + mAdvantageType = new ChangeTrackedLiveData<>(skill.advantageType, onDirtied); + mProficiencyType = new ChangeTrackedLiveData<>(skill.proficiencyType, onDirtied); + mName = new ChangeTrackedLiveData<>(skill.name, onDirtied); + mSkill = new ChangeTrackedLiveData<>(makeSkill(), onDirtied); + } + + public void copyFromSkill(Skill skill) { mAbilityScore.resetValue(skill.abilityScore); mAdvantageType.resetValue(skill.advantageType); mProficiencyType.resetValue(skill.proficiencyType); mName.resetValue(skill.name); - makeClean(); + } + + public void copyFromSkill(String name, AbilityScore abilityScore, ProficiencyType proficiency, AdvantageType advantage) { + mAbilityScore.resetValue(abilityScore); + mAdvantageType.resetValue(advantage); + mProficiencyType.resetValue(proficiency); + mName.resetValue(name); } public LiveData getSkill() { @@ -74,7 +94,14 @@ public class EditSkillViewModel extends ChangeTrackedViewModel { mSkill.setValue(makeSkill()); } - @NonNull + public LiveData getHasChanges() { + return mHasChanges; + } + + public boolean hasChanges() { + return mHasChanges.getValue(); + } + private Skill makeSkill() { return new Skill(mName.getValue(), mAbilityScore.getValue(), mAdvantageType.getValue(), mProficiencyType.getValue()); } diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillsFragment.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillsFragment.java index 684331e..13de066 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillsFragment.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditSkillsFragment.java @@ -3,9 +3,6 @@ package com.majinnaibu.monstercards.ui.editmonster; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -14,57 +11,87 @@ import androidx.fragment.app.Fragment; 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.Skill; +import com.majinnaibu.monstercards.ui.shared.SwipeToDeleteCallback; +import com.majinnaibu.monstercards.utils.Logger; /** * A fragment representing a list of Items. */ +@SuppressWarnings("FieldCanBeLocal") public class EditSkillsFragment extends Fragment { private EditMonsterViewModel mViewModel; -// private ViewHolder mHolder; + private ViewHolder mHolder; + + private void navigateToEditSkill(Skill skill) { + NavDirections action = EditSkillsFragmentDirections.actionEditSkillsFragmentToEditSkillFragment(skill.name, skill.abilityScore, skill.proficiencyType, skill.advantageType); + View view = getView(); + assert view != null; + Navigation.findNavController(view).navigate(action); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_edit_skills_list, container, false); - 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); - setHasOptionsMenu(true); - // Set the adapter - if (view instanceof RecyclerView) { - Context context = view.getContext(); - RecyclerView recyclerView = (RecyclerView) view; - recyclerView.setLayoutManager(new LinearLayoutManager(context)); - mViewModel.getSkills().observe(getViewLifecycleOwner(), skills -> recyclerView.setAdapter(new EditSkillsRecyclerViewAdapter(mViewModel.getSkillsArray()))); - } + View root = inflater.inflate(R.layout.fragment_edit_skills_list, container, false); - return view; + mHolder = new ViewHolder(root); + setupRecyclerView(mHolder.list); + setupAddSkillButton(mHolder.addSkill); + + return root; } - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - inflater.inflate(R.menu.edit_skills_menu, menu); - super.onCreateOptionsMenu(menu, inflater); + private void setupRecyclerView(@NonNull RecyclerView recyclerView) { + Context context = requireContext(); + LinearLayoutManager layoutManager = new LinearLayoutManager(context); + recyclerView.setLayoutManager(layoutManager); + + mViewModel.getSkills().observe(getViewLifecycleOwner(), skills -> { + EditSkillsRecyclerViewAdapter adapter = new EditSkillsRecyclerViewAdapter(mViewModel.getSkillsArray(), skill -> { + if (skill != null) { + navigateToEditSkill(skill); + } else { + Logger.logError("Can't navigate to EditSkill with a null skill"); + } + }, null); + recyclerView.setAdapter(adapter); + }); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(context, layoutManager.getOrientation()); + recyclerView.addItemDecoration(dividerItemDecoration); + + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(context, mViewModel::removeSkill)); + itemTouchHelper.attachToRecyclerView(recyclerView); } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.menu_action_add_skill) { - mViewModel.addNewSkill(); - // TODO: navigate to editing the new skill -// NavDirections action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterDetailViewModel.getId().getValue().toString()); -// View view = getView(); -// assert view != null; -// Navigation.findNavController(view).navigate(action); - return true; + private void setupAddSkillButton(@NonNull FloatingActionButton fab) { + fab.setOnClickListener(view -> { + Skill newSkill = mViewModel.addNewSkill(); + navigateToEditSkill(newSkill); + }); + } + + private static class ViewHolder { + RecyclerView list; + FloatingActionButton addSkill; + + ViewHolder(View root) { + this.list = root.findViewById(R.id.list); + this.addSkill = root.findViewById(R.id.add_skill); } - return super.onOptionsItemSelected(item); } } diff --git a/Android/app/src/main/res/navigation/mobile_navigation.xml b/Android/app/src/main/res/navigation/mobile_navigation.xml index 15ab555..4ddc3c9 100644 --- a/Android/app/src/main/res/navigation/mobile_navigation.xml +++ b/Android/app/src/main/res/navigation/mobile_navigation.xml @@ -127,7 +127,28 @@ android:id="@+id/editSkillsFragment" android:name="com.majinnaibu.monstercards.ui.editmonster.EditSkillsFragment" android:label="fragment_edit_skills_list" - tools:layout="@layout/fragment_edit_skills_list" /> + tools:layout="@layout/fragment_edit_skills_list"> + + + + + + + + - - \ No newline at end of file +