diff --git a/app/src/main/java/com/majinnaibu/monstercards/helpers/ArrayHelper.java b/app/src/main/java/com/majinnaibu/monstercards/helpers/ArrayHelper.java new file mode 100644 index 0000000..961db8d --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/helpers/ArrayHelper.java @@ -0,0 +1,15 @@ +package com.majinnaibu.monstercards.helpers; + +import java.util.Objects; + +public final class ArrayHelper { + public static int indexOf(Object[] array, Object target) { + for (int index = 0; index < array.length; index++) { + if (Objects.equals(array[index], target)) { + return index; + } + } + + return -1; + } +} diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditArmorFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditArmorFragment.java new file mode 100644 index 0000000..a913d27 --- /dev/null +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditArmorFragment.java @@ -0,0 +1,107 @@ +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.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SwitchCompat; +import androidx.fragment.app.Fragment; +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.data.enums.ArmorType; +import com.majinnaibu.monstercards.helpers.ArrayHelper; +import com.majinnaibu.monstercards.utils.TextChangedListener; + +@SuppressWarnings("FieldCanBeLocal") +public class EditArmorFragment extends Fragment { + private EditMonsterViewModel mViewModel; + private ViewHolder mHolder; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + 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); + + // Inflate the layout for this fragment + View root = inflater.inflate(R.layout.fragment_edit_armor, container, false); + + mHolder = new ViewHolder(root); + + mHolder.armorType.setAdapter(new ArrayAdapter(requireContext(), R.layout.dropdown_list_item, ArmorType.values()) { + @NonNull + @Override + public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + ArmorType item = getItem(position); + TextView view = (TextView) super.getView(position, convertView, parent); + view.setText(item.displayName); + return view; + } + + @Override + public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + ArmorType item = getItem(position); + TextView view = (TextView) super.getDropDownView(position, convertView, parent); + view.setText(item.displayName); + return view; + } + }); + mHolder.armorType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + ArmorType selectedItem = (ArmorType) parent.getItemAtPosition(position); + mViewModel.setArmorType(selectedItem); + } + + @Override + public void onNothingSelected(AdapterView parent) { + mViewModel.setArmorType(ArmorType.NONE); + } + }); + mHolder.armorType.setSelection(ArrayHelper.indexOf(ArmorType.values(), mViewModel.getArmorType().getValue())); + + mHolder.naturalArmorBonus.setText(mViewModel.getNaturalArmorBonusValueAsString()); + mHolder.naturalArmorBonus.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setNaturalArmorBonus(s.toString())))); + + mHolder.hasShield.setChecked(mViewModel.getHasShieldValueAsBoolean()); + mHolder.hasShield.setOnCheckedChangeListener((buttonView, isChecked) -> mViewModel.setHasShield(isChecked)); + + mHolder.shieldBonus.setText(mViewModel.getShieldBonusValueAsString()); + mHolder.shieldBonus.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setShieldBonus(s.toString())))); + + mHolder.customArmor.setText(mViewModel.getCustomArmor().getValue()); + mHolder.customArmor.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setCustomArmor(s.toString())))); + + return root; + } + + private static class ViewHolder { + private final Spinner armorType; + private final EditText naturalArmorBonus; + private final SwitchCompat hasShield; + private final EditText shieldBonus; + private final EditText customArmor; + + ViewHolder(View root) { + armorType = root.findViewById(R.id.armorType); + naturalArmorBonus = root.findViewById(R.id.naturalArmorBonus); + hasShield = root.findViewById(R.id.hasShield); + shieldBonus = root.findViewById(R.id.shieldBonus); + customArmor = root.findViewById(R.id.customArmor); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java index 3b9b045..9a99c88 100644 --- a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditBasicInfoFragment.java @@ -6,7 +6,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; -import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavController; @@ -15,12 +14,8 @@ import androidx.navigation.Navigation; import com.google.android.material.switchmaterial.SwitchMaterial; import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.ui.MCFragment; -import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.TextChangedListener; -/** - * A simple {@link Fragment} subclass. - */ @SuppressWarnings("FieldCanBeLocal") public class EditBasicInfoFragment extends MCFragment { private EditMonsterViewModel mViewModel; @@ -38,52 +33,28 @@ public class EditBasicInfoFragment extends MCFragment { mHolder = new ViewHolder(root); mHolder.name.setText(mViewModel.getName().getValue()); - mHolder.name.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setName(s.toString()); - Logger.logDebug(String.format("Monster Name changed to %s", mViewModel.getName().getValue())); - })); + mHolder.name.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setName(s.toString()))); mHolder.size.setText(mViewModel.getSize().getValue()); - mHolder.size.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setSize(s.toString()); - Logger.logDebug(String.format("Monster Size changed to %s", mViewModel.getSize().getValue())); - })); + mHolder.size.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setSize(s.toString()))); mHolder.type.setText(mViewModel.getType().getValue()); - mHolder.type.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setType(s.toString()); - Logger.logDebug(String.format("Monster Type changed to %s", mViewModel.getType().getValue())); - })); + mHolder.type.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setType(s.toString()))); mHolder.subtype.setText(mViewModel.getSubtype().getValue()); - mHolder.subtype.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setSubtype(s.toString()); - Logger.logDebug(String.format("Monster Subtype changed to %s", mViewModel.getSubtype().getValue())); - })); + mHolder.subtype.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setSubtype(s.toString()))); mHolder.alignment.setText(mViewModel.getAlignment().getValue()); - mHolder.alignment.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setAlignment(s.toString()); - Logger.logDebug(String.format("Monster Alignment changed to %s", mViewModel.getAlignment().getValue())); - })); + mHolder.alignment.addTextChangedListener(new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setAlignment(s.toString()))); mHolder.customHitPoints.setText(mViewModel.getCustomHitPoints().getValue()); - mHolder.customHitPoints.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setCustomHitPoints(s.toString()); - Logger.logDebug(String.format("Monster Custom Hit Points changed to %s", mViewModel.getCustomHitPoints().getValue())); - }))); + mHolder.customHitPoints.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setCustomHitPoints(s.toString())))); mHolder.hitDice.setText(mViewModel.getHitDiceValueAsString()); - mHolder.hitDice.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> { - mViewModel.setHitDice(s.toString()); - Logger.logDebug(String.format("Monster Hit Dice changed to %s", mViewModel.getHitDiceValueAsString())); - }))); + mHolder.hitDice.addTextChangedListener((new TextChangedListener((TextChangedListener.OnTextChangedCallback) (s, start, before, count) -> mViewModel.setHitDice(s.toString())))); mHolder.hasCustomHitPoints.setChecked(mViewModel.getHasCustomHitPointsValueAsBoolean()); - mHolder.hasCustomHitPoints.setOnCheckedChangeListener((button, isChecked) -> { - mViewModel.setHasCustomHitPoints(isChecked); - Logger.logDebug(String.format("Monster Has Custom Hit Points changed to %s", isChecked ? "TRUE" : "FALSE")); - }); + mHolder.hasCustomHitPoints.setOnCheckedChangeListener((button, isChecked) -> mViewModel.setHasCustomHitPoints(isChecked)); return root; } 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 a011985..d0d34b9 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 @@ -86,6 +86,12 @@ public class EditMonsterFragment extends MCFragment { assert view != null; Navigation.findNavController(view).navigate(action); }); + mHolder.armorButton.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditArmorFragment(); + View view = getView(); + assert view != null; + Navigation.findNavController(view).navigate(action); + }); requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { @Override @@ -137,9 +143,11 @@ public class EditMonsterFragment extends MCFragment { private static class ViewHolder { TextView basicInfoButton; + TextView armorButton; ViewHolder(View root) { basicInfoButton = root.findViewById(R.id.basicInfo); + armorButton = root.findViewById(R.id.armor); } } } \ No newline at end of file diff --git a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java index afb9bdf..e0d1fdb 100644 --- a/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java +++ b/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java @@ -5,26 +5,34 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; +import com.majinnaibu.monstercards.data.enums.ArmorType; import com.majinnaibu.monstercards.helpers.StringHelper; import com.majinnaibu.monstercards.models.Monster; +import com.majinnaibu.monstercards.utils.Logger; +import java.util.Objects; import java.util.UUID; @SuppressWarnings({"ConstantConditions", "unused"}) public class EditMonsterViewModel extends ViewModel { - private final MutableLiveData mName; private final MutableLiveData mMonsterId; - private final MutableLiveData mErrorMessage; private final MutableLiveData mHasError; private final MutableLiveData mHasLoaded; + private final MutableLiveData mHasChanges; + private final MutableLiveData mHasCustomHitPoints; + private final MutableLiveData mHasShield; + private final MutableLiveData mArmorType; + private final MutableLiveData mHitDice; + private final MutableLiveData mNaturalArmorBonus; + private final MutableLiveData mShieldBonus; + private final MutableLiveData mName; + private final MutableLiveData mErrorMessage; private final MutableLiveData mSize; private final MutableLiveData mType; private final MutableLiveData mSubtype; private final MutableLiveData mAlignment; private final MutableLiveData mCustomHitPoints; - private final MutableLiveData mHasChanges; - private final MutableLiveData mHitDice; - private final MutableLiveData mHasCustomHitPoints; + private final MutableLiveData mCustomArmor; public EditMonsterViewModel() { @@ -39,7 +47,12 @@ public class EditMonsterViewModel extends ViewModel { mAlignment = new MutableLiveData<>(""); mCustomHitPoints = new MutableLiveData<>(""); mHitDice = new MutableLiveData<>(0); + mNaturalArmorBonus = new MutableLiveData<>(0); mHasCustomHitPoints = new MutableLiveData<>(false); + mArmorType = new MutableLiveData<>(ArmorType.NONE); + mHasShield = new MutableLiveData<>(false); + mShieldBonus = new MutableLiveData<>(0); + mCustomArmor = new MutableLiveData<>(""); // TODO: consider initializing this to true so all new monsters need saving mHasChanges = new MutableLiveData<>(false); } @@ -54,7 +67,12 @@ public class EditMonsterViewModel extends ViewModel { mAlignment.setValue(monster.alignment); mCustomHitPoints.setValue(monster.customHPDescription); mHitDice.setValue(monster.hitDice); + mNaturalArmorBonus.setValue(monster.naturalArmorBonus); mHasCustomHitPoints.setValue(monster.hasCustomHP); + mArmorType.setValue(monster.armorType); + mHasShield.setValue(monster.shieldBonus != 0); + mShieldBonus.setValue(monster.shieldBonus); + mCustomArmor.setValue(monster.otherArmorDescription); mHasChanges.setValue(false); } @@ -63,8 +81,10 @@ public class EditMonsterViewModel extends ViewModel { } public void setName(@NonNull String name) { - mName.setValue(name); - mHasChanges.setValue(true); + if (!Objects.equals(mName.getValue(), name)) { + mName.setValue(name); + mHasChanges.setValue(true); + } } public LiveData getMonsterId() { @@ -108,8 +128,10 @@ public class EditMonsterViewModel extends ViewModel { } public void setSize(@NonNull String size) { - mSize.setValue(size); - mHasChanges.setValue(true); + if (!Objects.equals(mSize.getValue(), size)) { + mSize.setValue(size); + mHasChanges.setValue(true); + } } public LiveData getType() { @@ -117,17 +139,21 @@ public class EditMonsterViewModel extends ViewModel { } public void setType(@NonNull String type) { - mType.setValue(type); - mHasChanges.setValue(true); + if (!Objects.equals(mType.getValue(), type)) { + mType.setValue(type); + mHasChanges.setValue(true); + } } public LiveData getSubtype() { return mSubtype; } - public void setSubtype(@NonNull String subType) { - mSubtype.setValue(subType); - mHasChanges.setValue(true); + public void setSubtype(@NonNull String subtype) { + if (!Objects.equals(mSubtype.getValue(), subtype)) { + mSubtype.setValue(subtype); + mHasChanges.setValue(true); + } } public LiveData getAlignment() { @@ -135,8 +161,10 @@ public class EditMonsterViewModel extends ViewModel { } public void setAlignment(@NonNull String alignment) { - mAlignment.setValue(alignment); - mHasChanges.setValue(true); + if (!Objects.equals(mAlignment.getValue(), alignment)) { + mAlignment.setValue(alignment); + mHasChanges.setValue(true); + } } public LiveData getCustomHitPoints() { @@ -144,8 +172,10 @@ public class EditMonsterViewModel extends ViewModel { } public void setCustomHitPoints(String customHitPoints) { - mCustomHitPoints.setValue(customHitPoints); - mHasChanges.setValue(true); + if (!Objects.equals(mCustomHitPoints.getValue(), customHitPoints)) { + mCustomHitPoints.setValue(customHitPoints); + mHasChanges.setValue(true); + } } public LiveData getHasChanges() { @@ -165,8 +195,10 @@ public class EditMonsterViewModel extends ViewModel { } public void setHitDice(int hitDice) { - mHitDice.setValue(hitDice); - mHasChanges.setValue(true); + if (!Objects.equals(mHitDice.getValue(), hitDice)) { + mHitDice.setValue(hitDice); + mHasChanges.setValue(true); + } } public void setHitDice(String hitDice) { @@ -178,19 +210,93 @@ public class EditMonsterViewModel extends ViewModel { return mHitDice.getValue().toString(); } + public LiveData getNaturalArmorBonus() { + return mNaturalArmorBonus; + } + + public void setNaturalArmorBonus(int naturalArmorBonus) { + if (!Objects.equals(mNaturalArmorBonus.getValue(), naturalArmorBonus)) { + mNaturalArmorBonus.setValue(naturalArmorBonus); + mHasChanges.setValue(true); + } + } + + public void setNaturalArmorBonus(String naturalArmorBonus) { + Integer parsedValue = StringHelper.parseInt(naturalArmorBonus); + this.setNaturalArmorBonus(parsedValue != null ? parsedValue : 0); + } + + public String getNaturalArmorBonusValueAsString() { + return mNaturalArmorBonus.getValue().toString(); + } + public LiveData getHasCustomHitPoints() { return mHasCustomHitPoints; } public void setHasCustomHitPoints(boolean hasCustomHitPoints) { - mHasCustomHitPoints.setValue(hasCustomHitPoints); - mHasChanges.setValue(true); + if (!Objects.equals(mHasCustomHitPoints.getValue(), hasCustomHitPoints)) { + mHasCustomHitPoints.setValue(hasCustomHitPoints); + mHasChanges.setValue(true); + } } public boolean getHasCustomHitPointsValueAsBoolean() { return mHasCustomHitPoints.getValue(); } + public LiveData getArmorType() { + return mArmorType; + } + + public void setArmorType(ArmorType armorType) { + Logger.logDebug(String.format("Setting ArmorType to %s", armorType.displayName)); + if (!Objects.equals(mArmorType.getValue(), armorType)) { + mArmorType.setValue(armorType); + mHasChanges.setValue(true); + } + } + + public LiveData getHasShield() { + return mHasShield; + } + + public void setHasShield(boolean hasShield) { + mHasShield.setValue(hasShield); + mHasChanges.setValue(true); + } + + public boolean getHasShieldValueAsBoolean() { + return mHasShield.getValue(); + } + + public LiveData getShieldBonus() { + return mShieldBonus; + } + + public void setShieldBonus(int shieldBonus) { + mShieldBonus.setValue(shieldBonus); + mHasChanges.setValue(true); + } + + public void setShieldBonus(String shieldBonus) { + Integer parsedValue = StringHelper.parseInt(shieldBonus); + this.setShieldBonus(parsedValue != null ? parsedValue : 0); + } + + public LiveData getCustomArmor() { + return mCustomArmor; + } + + public void setCustomArmor(String customArmor) { + mCustomArmor.setValue(customArmor); + mHasChanges.setValue(true); + } + + public String getShieldBonusValueAsString() { + return mShieldBonus.getValue().toString(); + } + public Monster buildMonster() { Monster monster = new Monster(); @@ -203,6 +309,10 @@ public class EditMonsterViewModel extends ViewModel { monster.customHPDescription = mCustomHitPoints.getValue(); monster.hitDice = mHitDice.getValue(); monster.hasCustomHP = mHasCustomHitPoints.getValue(); + monster.armorType = mArmorType.getValue(); + monster.naturalArmorBonus = mNaturalArmorBonus.getValue(); + monster.shieldBonus = mShieldBonus.getValue(); + monster.otherArmorDescription = mCustomArmor.getValue(); return monster; } diff --git a/app/src/main/res/layout/dropdown_list_item.xml b/app/src/main/res/layout/dropdown_list_item.xml new file mode 100644 index 0000000..0fe7e82 --- /dev/null +++ b/app/src/main/res/layout/dropdown_list_item.xml @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_edit_armor.xml b/app/src/main/res/layout/fragment_edit_armor.xml new file mode 100644 index 0000000..36c4206 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_armor.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index e8eee2d..f9db55e 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -75,12 +75,20 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fcb9d4d..76c790e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,49 +1,53 @@ + Add monster + Edit Actions MonsterCards CHA CON + Unnamed Monster DEX INT - Query - section divider - STR - Collections - Dashboard - Library - Search - WIS - Add monster - Edit - Basic Info - Armor - Speed Ability Scores - Saving Throws - Skills + Abilities + Actions + Alignment + Armor + Basic Info + Challenge Rating Condition Immunities + Custom Armor + Custom HP Damage Immunities Damage Resistances Damage Vulnerabilities - Senses - Languages - Challenge Rating - Abilities - Actions - Reactions - Legendary Actions + Has Custom HP + Has a Shield + Hit Dice Lair Actions + Languages + Legendary Actions + Natural Armor Bonus + Name + Reactions Regional Actions - Edit %1$s - Unnamed Monster + Saving Throws + Query + Senses + Shield Bonus + Size + Skills + Speed + Subtype + Type + section divider Failed to create monster %1$s created - Name - Size - Type - Subtype - Alignment - Custom HP - Has Custom HP - Hit Dice + STR + Collections + Dashboard + Edit %1$s + Library + Search + WIS \ No newline at end of file