From ddb58622d6b93dccd7d3fd12772b6034d10bc461 Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Wed, 16 Sep 2026 22:04:30 -0700 Subject: [PATCH] Makes monster editor smoother and fixes colors. --- .../majinnaibu/monstercards/MainActivity.java | 11 +- .../ui/editmonster/EditMonsterFragment.java | 504 ++++++++++---- .../ui/editmonster/EditMonsterViewModel.java | 32 + .../main/res/drawable/ic_expand_less_24.xml | 10 + .../main/res/drawable/ic_expand_more_24.xml | 10 + .../src/main/res/layout/component_stepper.xml | 1 + .../layout/fragment_edit_ability_scores.xml | 4 +- .../main/res/layout/fragment_edit_armor.xml | 4 +- .../res/layout/fragment_edit_basic_info.xml | 2 + .../layout/fragment_edit_challenge_rating.xml | 2 + .../main/res/layout/fragment_edit_monster.xml | 658 ++++++++++++++---- .../layout/fragment_edit_saving_throws.xml | 3 +- .../main/res/layout/fragment_edit_speed.xml | 4 +- Android/app/src/main/res/values/styles.xml | 7 +- 14 files changed, 944 insertions(+), 308 deletions(-) create mode 100644 Android/app/src/main/res/drawable/ic_expand_less_24.xml create mode 100644 Android/app/src/main/res/drawable/ic_expand_more_24.xml diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java b/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java index 02054c5..ceb56f5 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java @@ -58,20 +58,11 @@ public class MainActivity extends AppCompatActivity { @SuppressWarnings("ConstantConditions") @Override protected void onCreate(Bundle savedInstanceState) { - WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + WindowCompat.setDecorFitsSystemWindows(getWindow(), true); super.onCreate(savedInstanceState); AppCenterInitializer.init(getApplication()); setContentView(R.layout.activity_main); - View container = findViewById(R.id.container); - if (container != null) { - ViewCompat.setOnApplyWindowInsetsListener(container, (v, windowInsets) -> { - Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(insets.left, insets.top, insets.right, 0); - return windowInsets; - }); - } - BottomNavigationView navView = findViewById(R.id.nav_view); // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java index 0e24067..84d1a70 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterFragment.java @@ -1,9 +1,11 @@ package com.majinnaibu.monstercards.ui.editmonster; import android.os.Bundle; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.TextView; import androidx.activity.OnBackPressedCallback; @@ -14,7 +16,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavController; import androidx.navigation.NavDirections; -import androidx.navigation.Navigation; +import androidx.navigation.fragment.NavHostFragment; import com.google.android.material.snackbar.Snackbar; import com.majinnaibu.monstercards.R; @@ -22,10 +24,11 @@ import com.majinnaibu.monstercards.data.MonsterRepository; import com.majinnaibu.monstercards.data.enums.StringType; import com.majinnaibu.monstercards.data.enums.TraitType; import com.majinnaibu.monstercards.models.Monster; -import com.majinnaibu.monstercards.ui.monster.MonsterDetailFragmentArgs; import com.majinnaibu.monstercards.ui.shared.MCFragment; import com.majinnaibu.monstercards.utils.Logger; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.UUID; @@ -43,21 +46,34 @@ public class EditMonsterFragment extends MCFragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - MonsterRepository repository = getMonsterRepository(); - Bundle arguments = getArguments(); - assert arguments != null; - UUID monsterId = UUID.fromString(MonsterDetailFragmentArgs.fromBundle(arguments).getMonsterId()); - - 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_monster, container, false); mHolder = new ViewHolder(root); + MonsterRepository repository = getMonsterRepository(); + Bundle arguments = getArguments(); + if (arguments == null) { + Logger.logError("Arguments bundle is null in EditMonsterFragment"); + return root; + } + + String monsterIdStr = EditMonsterFragmentArgs.fromBundle(arguments).getMonsterId(); + if (monsterIdStr == null || monsterIdStr.isEmpty()) { + Logger.logError("monsterId is null or empty in EditMonsterFragment"); + return root; + } + + UUID monsterId = UUID.fromString(monsterIdStr); + + try { + NavController navController = NavHostFragment.findNavController(this); + NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.edit_monster_navigation); + mViewModel = new ViewModelProvider(backStackEntry).get(EditMonsterViewModel.class); + } catch (Exception e) { + mViewModel = new ViewModelProvider(this).get(EditMonsterViewModel.class); + } + setTitle(getString(R.string.title_editMonster_fmt, getString(R.string.default_monster_name))); - // TODO: Show a loading spinner until we have the monster loaded. if (mViewModel.hasError() || !mViewModel.hasLoaded() || !Objects.equals(mViewModel.getMonsterId().getValue(), monsterId)) { repository.getMonster(monsterId).toObservable() .firstOrError() @@ -73,7 +89,6 @@ public class EditMonsterFragment extends MCFragment { @Override public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { - // TODO: Show an error state. Logger.logError(e); mViewModel.setHasError(true); mViewModel.setErrorMessage(e.toString()); @@ -82,101 +97,9 @@ public class EditMonsterFragment extends MCFragment { }); } - mHolder.basicInfoButton.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditBasicInfoFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.armorButton.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditArmorFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.speedButton.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSpeedFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.abilityScoresButton.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditAbilityScoresFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.savingThrows.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSavingThrowsFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.challengeRating.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditChallengeRatingFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.skills.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSkillsFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.senses.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.SENSE); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.conditionImmunities.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.CONDITION_IMMUNITY); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.damageImmunities.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_IMMUNITY); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.damageResistances.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_RESISTANCE); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.damageVulnerabilities.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_VULNERABILITY); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.languages.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditLanguagesFragment(); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.abilities.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.ABILITY); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.actions.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.ACTION); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.lairActions.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.LAIR_ACTION); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.legendaryActions.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.LEGENDARY_ACTION); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.reactions.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.REACTIONS); - Navigation.findNavController(requireView()).navigate(action); - }); - - mHolder.regionalActions.setOnClickListener(v -> { - NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.REGIONAL_ACTION); - Navigation.findNavController(requireView()).navigate(action); - }); - + setupAccordions(); + setupSummaries(); + setupSubClickListeners(); requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { @Override @@ -187,7 +110,6 @@ public class EditMonsterFragment extends MCFragment { alertDialog.setTitle("Unsaved Changes"); alertDialog.setMessage("Do you want to save your changes?"); alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Save", (dialog, id) -> { - // Save the monster. Navigate up if the save is successful. Show a SnackBar if there was an error. getMonsterRepository().saveMonster(mViewModel.buildMonster()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -195,29 +117,23 @@ public class EditMonsterFragment extends MCFragment { new DisposableCompletableObserver() { @Override public void onComplete() { - Navigation.findNavController(requireView()).navigateUp(); + NavHostFragment.findNavController(EditMonsterFragment.this).navigateUp(); } @Override public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) { - Logger.logError("Error creating monster", e); + Logger.logError("Error saving monster", e); assert view != null; Snackbar.make(view, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); }); - alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Discard", (dialog, id) -> { - // Navigate up ignoring unsaved changes. - Navigation.findNavController(requireView()).navigateUp(); - }); - alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Cancel", (dialog, id) -> { - // Do nothing. - }); + alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Discard", (dialog, id) -> NavHostFragment.findNavController(EditMonsterFragment.this).navigateUp()); + alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Cancel", (dialog, id) -> {}); alertDialog.show(); } else { - // No changes so we can safely leave. - Navigation.findNavController(requireView()).navigateUp(); + NavHostFragment.findNavController(EditMonsterFragment.this).navigateUp(); } } }); @@ -225,42 +141,344 @@ public class EditMonsterFragment extends MCFragment { return root; } - private static class ViewHolder { + private void setupAccordions() { + if (mHolder.headerBasicInfo != null && mHolder.bodyBasicInfo != null && mHolder.chevronBasicInfo != null) { + setupAccordion(mHolder.headerBasicInfo, mHolder.bodyBasicInfo, mHolder.chevronBasicInfo); + } + if (mHolder.headerCombatAbilities != null && mHolder.bodyCombatAbilities != null && mHolder.chevronCombatAbilities != null) { + setupAccordion(mHolder.headerCombatAbilities, mHolder.bodyCombatAbilities, mHolder.chevronCombatAbilities); + } + if (mHolder.headerSavesSkillsLanguages != null && mHolder.bodySavesSkillsLanguages != null && mHolder.chevronSavesSkillsLanguages != null) { + setupAccordion(mHolder.headerSavesSkillsLanguages, mHolder.bodySavesSkillsLanguages, mHolder.chevronSavesSkillsLanguages); + } + if (mHolder.headerDefenses != null && mHolder.bodyDefenses != null && mHolder.chevronDefenses != null) { + setupAccordion(mHolder.headerDefenses, mHolder.bodyDefenses, mHolder.chevronDefenses); + } + if (mHolder.headerTraitsActions != null && mHolder.bodyTraitsActions != null && mHolder.chevronTraitsActions != null) { + setupAccordion(mHolder.headerTraitsActions, mHolder.bodyTraitsActions, mHolder.chevronTraitsActions); + } + } - TextView basicInfoButton; - TextView armorButton; - TextView speedButton; - TextView abilityScoresButton; - TextView savingThrows; - TextView skills; - TextView conditionImmunities; - TextView damageImmunities; - TextView damageResistances; - TextView damageVulnerabilities; - TextView senses; - TextView languages; - TextView challengeRating; - TextView abilities; - TextView actions; - TextView reactions; - TextView legendaryActions; - TextView lairActions; - TextView regionalActions; + private void setupAccordion(View header, View body, ImageView chevron) { + header.setOnClickListener(v -> { + boolean isExpanded = body.getVisibility() == View.VISIBLE; + body.setVisibility(isExpanded ? View.GONE : View.VISIBLE); + chevron.setImageResource(isExpanded ? R.drawable.ic_expand_more_24 : R.drawable.ic_expand_less_24); + }); + } + + private void setupSummaries() { + mViewModel.getName().observe(getViewLifecycleOwner(), v -> updateBasicInfoSummary()); + mViewModel.getSize().observe(getViewLifecycleOwner(), v -> updateBasicInfoSummary()); + mViewModel.getType().observe(getViewLifecycleOwner(), v -> updateBasicInfoSummary()); + mViewModel.getChallengeRating().observe(getViewLifecycleOwner(), v -> updateBasicInfoSummary()); + + mViewModel.getStrength().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + mViewModel.getDexterity().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + mViewModel.getConstitution().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + mViewModel.getIntelligence().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + mViewModel.getWisdom().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + mViewModel.getCharisma().observe(getViewLifecycleOwner(), v -> updateCombatAbilitiesSummary()); + + mViewModel.getSkills().observe(getViewLifecycleOwner(), v -> updateSavesSkillsSummary()); + mViewModel.getLanguages().observe(getViewLifecycleOwner(), v -> updateSavesSkillsSummary()); + + mViewModel.getConditionImmunities().observe(getViewLifecycleOwner(), v -> updateDefensesSummary()); + mViewModel.getDamageImmunities().observe(getViewLifecycleOwner(), v -> updateDefensesSummary()); + mViewModel.getDamageResistances().observe(getViewLifecycleOwner(), v -> updateDefensesSummary()); + mViewModel.getDamageVulnerabilities().observe(getViewLifecycleOwner(), v -> updateDefensesSummary()); + + mViewModel.getAbilities().observe(getViewLifecycleOwner(), v -> updateTraitsActionsSummary()); + mViewModel.getActions().observe(getViewLifecycleOwner(), v -> updateTraitsActionsSummary()); + mViewModel.getReactions().observe(getViewLifecycleOwner(), v -> updateTraitsActionsSummary()); + } + + private void updateBasicInfoSummary() { + if (mHolder.summaryBasicInfo == null) return; + String name = mViewModel.getName().getValue(); + String size = mViewModel.getSize().getValue(); + String type = mViewModel.getType().getValue(); + + List parts = new ArrayList<>(); + if (!TextUtils.isEmpty(name)) parts.add(name); + if (!TextUtils.isEmpty(size)) parts.add(size); + if (!TextUtils.isEmpty(type)) parts.add(type); + + mHolder.summaryBasicInfo.setText(parts.isEmpty() ? "Tap to edit basic info" : TextUtils.join(", ", parts)); + } + + private void updateCombatAbilitiesSummary() { + if (mHolder.summaryCombatAbilities == null) return; + Integer str = mViewModel.getStrength().getValue(); + Integer dex = mViewModel.getDexterity().getValue(); + Integer con = mViewModel.getConstitution().getValue(); + Integer intel = mViewModel.getIntelligence().getValue(); + Integer wis = mViewModel.getWisdom().getValue(); + Integer cha = mViewModel.getCharisma().getValue(); + + String summary = String.format("STR %d | DEX %d | CON %d | INT %d | WIS %d | CHA %d", + str != null ? str : 10, + dex != null ? dex : 10, + con != null ? con : 10, + intel != null ? intel : 10, + wis != null ? wis : 10, + cha != null ? cha : 10); + mHolder.summaryCombatAbilities.setText(summary); + } + + private void updateSavesSkillsSummary() { + if (mHolder.summarySavesSkillsLanguages == null) return; + int skillsCount = mViewModel.getSkills().getValue() != null ? mViewModel.getSkills().getValue().size() : 0; + int languagesCount = mViewModel.getLanguages().getValue() != null ? mViewModel.getLanguages().getValue().size() : 0; + + mHolder.summarySavesSkillsLanguages.setText(String.format("%d Skills, %d Languages", skillsCount, languagesCount)); + } + + private void updateDefensesSummary() { + if (mHolder.summaryDefenses == null) return; + List defenses = new ArrayList<>(); + if (mViewModel.getConditionImmunities().getValue() != null && !mViewModel.getConditionImmunities().getValue().isEmpty()) { + defenses.add(mViewModel.getConditionImmunities().getValue().size() + " Condition Immunities"); + } + if (mViewModel.getDamageImmunities().getValue() != null && !mViewModel.getDamageImmunities().getValue().isEmpty()) { + defenses.add(mViewModel.getDamageImmunities().getValue().size() + " Damage Immunities"); + } + if (mViewModel.getDamageResistances().getValue() != null && !mViewModel.getDamageResistances().getValue().isEmpty()) { + defenses.add(mViewModel.getDamageResistances().getValue().size() + " Resistances"); + } + + mHolder.summaryDefenses.setText(defenses.isEmpty() ? "None" : TextUtils.join(", ", defenses)); + } + + private void updateTraitsActionsSummary() { + if (mHolder.summaryTraitsActions == null) return; + int abilitiesCount = mViewModel.getAbilities().getValue() != null ? mViewModel.getAbilities().getValue().size() : 0; + int actionsCount = mViewModel.getActions().getValue() != null ? mViewModel.getActions().getValue().size() : 0; + int reactionsCount = mViewModel.getReactions().getValue() != null ? mViewModel.getReactions().getValue().size() : 0; + + mHolder.summaryTraitsActions.setText(String.format("%d Abilities, %d Actions, %d Reactions", abilitiesCount, actionsCount, reactionsCount)); + } + + private void setupSubClickListeners() { + if (mHolder.basicInfoButton != null) { + mHolder.basicInfoButton.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditBasicInfoFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.challengeRating != null) { + mHolder.challengeRating.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditChallengeRatingFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.armorButton != null) { + mHolder.armorButton.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditArmorFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.speedButton != null) { + mHolder.speedButton.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSpeedFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.abilityScoresButton != null) { + mHolder.abilityScoresButton.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditAbilityScoresFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.savingThrows != null) { + mHolder.savingThrows.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSavingThrowsFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.skills != null) { + mHolder.skills.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditSkillsFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.senses != null) { + mHolder.senses.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.SENSE); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.languages != null) { + mHolder.languages.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditLanguagesFragment(); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.conditionImmunities != null) { + mHolder.conditionImmunities.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.CONDITION_IMMUNITY); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.damageImmunities != null) { + mHolder.damageImmunities.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_IMMUNITY); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.damageResistances != null) { + mHolder.damageResistances.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_RESISTANCE); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.damageVulnerabilities != null) { + mHolder.damageVulnerabilities.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditStringsFragment(StringType.DAMAGE_VULNERABILITY); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.abilities != null) { + mHolder.abilities.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.ABILITY); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.actions != null) { + mHolder.actions.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.ACTION); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.reactions != null) { + mHolder.reactions.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.REACTIONS); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.legendaryActions != null) { + mHolder.legendaryActions.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.LEGENDARY_ACTION); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.lairActions != null) { + mHolder.lairActions.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.LAIR_ACTION); + NavHostFragment.findNavController(this).navigate(action); + }); + } + + if (mHolder.regionalActions != null) { + mHolder.regionalActions.setOnClickListener(v -> { + NavDirections action = EditMonsterFragmentDirections.actionEditMonsterFragmentToEditTraitListFragment(TraitType.REGIONAL_ACTION); + NavHostFragment.findNavController(this).navigate(action); + }); + } + } + + private static class ViewHolder { + final View headerBasicInfo; + final View bodyBasicInfo; + final ImageView chevronBasicInfo; + final TextView summaryBasicInfo; + + final View headerCombatAbilities; + final View bodyCombatAbilities; + final ImageView chevronCombatAbilities; + final TextView summaryCombatAbilities; + + final View headerSavesSkillsLanguages; + final View bodySavesSkillsLanguages; + final ImageView chevronSavesSkillsLanguages; + final TextView summarySavesSkillsLanguages; + + final View headerDefenses; + final View bodyDefenses; + final ImageView chevronDefenses; + final TextView summaryDefenses; + + final View headerTraitsActions; + final View bodyTraitsActions; + final ImageView chevronTraitsActions; + final TextView summaryTraitsActions; + + final TextView basicInfoButton; + final TextView challengeRating; + final TextView armorButton; + final TextView speedButton; + final TextView abilityScoresButton; + final TextView savingThrows; + final TextView skills; + final TextView senses; + final TextView languages; + final TextView conditionImmunities; + final TextView damageImmunities; + final TextView damageResistances; + final TextView damageVulnerabilities; + final TextView abilities; + final TextView actions; + final TextView reactions; + final TextView legendaryActions; + final TextView lairActions; + final TextView regionalActions; ViewHolder(@NonNull View root) { + headerBasicInfo = root.findViewById(R.id.headerBasicInfo); + bodyBasicInfo = root.findViewById(R.id.bodyBasicInfo); + chevronBasicInfo = root.findViewById(R.id.chevronBasicInfo); + summaryBasicInfo = root.findViewById(R.id.summaryBasicInfo); + + headerCombatAbilities = root.findViewById(R.id.headerCombatAbilities); + bodyCombatAbilities = root.findViewById(R.id.bodyCombatAbilities); + chevronCombatAbilities = root.findViewById(R.id.chevronCombatAbilities); + summaryCombatAbilities = root.findViewById(R.id.summaryCombatAbilities); + + headerSavesSkillsLanguages = root.findViewById(R.id.headerSavesSkillsLanguages); + bodySavesSkillsLanguages = root.findViewById(R.id.bodySavesSkillsLanguages); + chevronSavesSkillsLanguages = root.findViewById(R.id.chevronSavesSkillsLanguages); + summarySavesSkillsLanguages = root.findViewById(R.id.summarySavesSkillsLanguages); + + headerDefenses = root.findViewById(R.id.headerDefenses); + bodyDefenses = root.findViewById(R.id.bodyDefenses); + chevronDefenses = root.findViewById(R.id.chevronDefenses); + summaryDefenses = root.findViewById(R.id.summaryDefenses); + + headerTraitsActions = root.findViewById(R.id.headerTraitsActions); + bodyTraitsActions = root.findViewById(R.id.bodyTraitsActions); + chevronTraitsActions = root.findViewById(R.id.chevronTraitsActions); + summaryTraitsActions = root.findViewById(R.id.summaryTraitsActions); + basicInfoButton = root.findViewById(R.id.basicInfo); + challengeRating = root.findViewById(R.id.challengeRating); armorButton = root.findViewById(R.id.armor); speedButton = root.findViewById(R.id.speed); abilityScoresButton = root.findViewById(R.id.abilityScores); savingThrows = root.findViewById(R.id.savingThrows); skills = root.findViewById(R.id.skills); + senses = root.findViewById(R.id.senses); + languages = root.findViewById(R.id.languages); conditionImmunities = root.findViewById(R.id.conditionImmunities); damageImmunities = root.findViewById(R.id.damageImmunities); damageResistances = root.findViewById(R.id.damageResistances); damageVulnerabilities = root.findViewById(R.id.damageVulnerabilities); - senses = root.findViewById(R.id.senses); - languages = root.findViewById(R.id.languages); - challengeRating = root.findViewById(R.id.challengeRating); abilities = root.findViewById(R.id.abilities); actions = root.findViewById(R.id.actions); reactions = root.findViewById(R.id.reactions); diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java index 00f76f8..133b95e 100644 --- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java +++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/editmonster/EditMonsterViewModel.java @@ -702,6 +702,38 @@ public class EditMonsterViewModel extends ChangeTrackedViewModel { return mDamageResistances; } + public LiveData> getDamageVulnerabilities() { + return mDamageVulnerabilities; + } + + public LiveData> getConditionImmunities() { + return mConditionImmunities; + } + + public LiveData> getAbilities() { + return mAbilities; + } + + public LiveData> getActions() { + return mActions; + } + + public LiveData> getReactions() { + return mReactions; + } + + public LiveData> getLairActions() { + return mLairActions; + } + + public LiveData> getLegendaryActions() { + return mLegendaryActions; + } + + public LiveData> getRegionalActions() { + return mRegionalActions; + } + public LiveData> getLanguages() { return mLanguages; } diff --git a/Android/app/src/main/res/drawable/ic_expand_less_24.xml b/Android/app/src/main/res/drawable/ic_expand_less_24.xml new file mode 100644 index 0000000..1550145 --- /dev/null +++ b/Android/app/src/main/res/drawable/ic_expand_less_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/Android/app/src/main/res/drawable/ic_expand_more_24.xml b/Android/app/src/main/res/drawable/ic_expand_more_24.xml new file mode 100644 index 0000000..adc215c --- /dev/null +++ b/Android/app/src/main/res/drawable/ic_expand_more_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/Android/app/src/main/res/layout/component_stepper.xml b/Android/app/src/main/res/layout/component_stepper.xml index 34aa568..5ecf210 100644 --- a/Android/app/src/main/res/layout/component_stepper.xml +++ b/Android/app/src/main/res/layout/component_stepper.xml @@ -59,6 +59,7 @@ android:id="@+id/decrement" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginEnd="12dp" android:paddingVertical="8dp" android:text="@string/label_decrement_field" app:layout_constraintBottom_toBottomOf="parent" diff --git a/Android/app/src/main/res/layout/fragment_edit_ability_scores.xml b/Android/app/src/main/res/layout/fragment_edit_ability_scores.xml index f709537..89f2885 100644 --- a/Android/app/src/main/res/layout/fragment_edit_ability_scores.xml +++ b/Android/app/src/main/res/layout/fragment_edit_ability_scores.xml @@ -4,7 +4,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.editmonster.EditBasicInfoFragment"> + android:clipToPadding="false" + android:padding="16dp" + tools:context=".ui.editmonster.EditAbilityScoresFragment"> + android:clipToPadding="false" + android:padding="16dp" + tools:context=".ui.editmonster.EditArmorFragment"> + android:layout_height="match_parent" + android:clipToPadding="false" + android:padding="8dp"> - + + android:layout_marginBottom="8dp" + app:cardCornerRadius="8dp" + app:cardElevation="2dp"> - + - + - + - + - + + - + - + - + + + + - + + android:layout_marginBottom="8dp" + app:cardCornerRadius="8dp" + app:cardElevation="2dp"> - + - + - + - + - + + - + - + - + - + + + + + + + android:layout_marginBottom="8dp" + app:cardCornerRadius="8dp" + app:cardElevation="2dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/app/src/main/res/layout/fragment_edit_saving_throws.xml b/Android/app/src/main/res/layout/fragment_edit_saving_throws.xml index 7d9df22..85a007b 100644 --- a/Android/app/src/main/res/layout/fragment_edit_saving_throws.xml +++ b/Android/app/src/main/res/layout/fragment_edit_saving_throws.xml @@ -3,7 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - + android:clipToPadding="false" + android:padding="16dp" tools:context=".ui.editmonster.EditSavingThrowsFragment"> + android:clipToPadding="false" + android:padding="16dp" + tools:context=".ui.editmonster.EditSpeedFragment"> -