Adds functional dashboard based on recycler view that picks the number of columns based on screen width.

This commit is contained in:
Tom Hicks
2021-07-05 00:01:37 -07:00
parent 26df28512c
commit 7b09098833
6 changed files with 667 additions and 500 deletions

View File

@@ -3,6 +3,7 @@ package com.majinnaibu.monstercards.models;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
@@ -531,10 +532,9 @@ public class Monster {
int dieSize = Helpers.getHitDieForSize(size); int dieSize = Helpers.getHitDieForSize(size);
int conMod = getConstitutionModifier(); int conMod = getConstitutionModifier();
// For PC style calculations use this // For PC style calculations use this
//int hpTotal = (int) Math.max(1, Math.ceil(dieSize + conMod + (hitDice - 1) * ((dieSize + 1) / 2.0 + conMod))); //return (int) Math.max(1, Math.ceil(dieSize + conMod + (hitDice - 1) * ((dieSize + 1) / 2.0 + conMod)));
// For monster style calculations use this // For monster style calculations use this
int hpTotal = (int) Math.max(1, Math.ceil(hitDice * ((dieSize + 1) / 2.0 + conMod))); return (int) Math.max(1, Math.ceil(hitDice * ((dieSize + 1) / 2.0 + conMod)));
return hpTotal;
} }
} }
@@ -817,6 +817,235 @@ public class Monster {
return actionDescriptions; return actionDescriptions;
} }
@Override
public boolean equals(@Nullable @org.jetbrains.annotations.Nullable Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Monster)) {
return false;
}
Monster other = (Monster) obj;
if (!challengeRating.equals(other.challengeRating)) {
return false;
}
if (!understandsButDescription.equals(other.understandsButDescription)) {
return false;
}
if (!languages.equals(other.languages)) {
return false;
}
if (!damageVulnerabilities.equals(other.damageVulnerabilities)) {
return false;
}
if (!damageResistances.equals(other.damageResistances)) {
return false;
}
if (!damageImmunities.equals(other.damageImmunities)) {
return false;
}
if (!conditionImmunities.equals(other.conditionImmunities)) {
return false;
}
if (!charismaSavingThrowProficiency.equals(other.charismaSavingThrowProficiency)) {
return false;
}
if (!wisdomSavingThrowProficiency.equals(other.wisdomSavingThrowProficiency)) {
return false;
}
if (!intelligenceSavingThrowProficiency.equals(other.intelligenceSavingThrowProficiency)) {
return false;
}
if (constitutionSavingThrowProficiency.equals(other.constitutionSavingThrowProficiency)) {
return false;
}
if (dexteritySavingThrowProficiency.equals(other.dexteritySavingThrowProficiency)) {
return false;
}
if (strengthSavingThrowProficiency.equals(other.strengthSavingThrowProficiency)) {
return false;
}
if (legendaryActions.equals(other.legendaryActions)) {
return false;
}
if (customChallengeRatingDescription.equals(other.customChallengeRatingDescription)) {
return false;
}
if (customSpeedDescription.equals(other.customSpeedDescription)) {
return false;
}
if (customHPDescription.equals(other.customHPDescription)) {
return false;
}
if (otherArmorDescription.equals(other.otherArmorDescription)) {
return false;
}
if (alignment.equals(other.alignment)) {
return false;
}
if (subtype.equals(other.subtype)) {
return false;
}
if (abilities.equals(other.abilities)) {
return false;
}
if (actions.equals(other.actions)) {
return false;
}
if (armorType.equals(other.armorType)) {
return false;
}
if (charismaSavingThrowAdvantage.equals(other.charismaSavingThrowAdvantage)) {
return false;
}
if (constitutionSavingThrowAdvantage.equals(other.constitutionSavingThrowAdvantage)) {
return false;
}
if (dexteritySavingThrowAdvantage.equals(other.dexteritySavingThrowAdvantage)) {
return false;
}
if (hitDice == other.hitDice) {
return false;
}
if (id.equals(other.id)) {
return false;
}
if (intelligenceSavingThrowAdvantage.equals(other.intelligenceSavingThrowAdvantage)) {
return false;
}
if (lairActions.equals(other.lairActions)) {
return false;
}
if (name.equals(other.name)) {
return false;
}
if (reactions.equals(other.reactions)) {
return false;
}
if (regionalActions.equals(other.regionalActions)) {
return false;
}
if (senses.equals(other.senses)) {
return false;
}
if (shieldBonus == other.shieldBonus) {
return false;
}
if (size.equals(other.size)) {
return false;
}
if (skills.equals(other.skills)) {
return false;
}
if (strengthSavingThrowAdvantage.equals(other.strengthSavingThrowAdvantage)) {
return false;
}
if (strengthScore == other.strengthScore) {
return false;
}
if (type.equals(other.type)) {
return false;
}
if (wisdomSavingThrowAdvantage.equals(other.wisdomSavingThrowAdvantage)) {
return false;
}
if (wisdomScore == other.wisdomScore) {
return false;
}
if (customProficiencyBonus == other.customProficiencyBonus) {
return false;
}
if (telepathyRange == other.telepathyRange) {
return false;
}
if (intelligenceScore == other.intelligenceScore) {
return false;
}
if (constitutionScore == other.constitutionScore) {
return false;
}
if (dexterityScore == other.dexterityScore) {
return false;
}
if (hasCustomSpeed == other.hasCustomSpeed) {
return false;
}
if (hasCustomHP == other.hasCustomHP) {
return false;
}
if (swimSpeed == other.swimSpeed) {
return false;
}
if (flySpeed == other.flySpeed) {
return false;
}
if (climbSpeed == other.climbSpeed) {
return false;
}
if (burrowSpeed == other.burrowSpeed) {
return false;
}
if (walkSpeed == other.walkSpeed) {
return false;
}
if (naturalArmorBonus == other.naturalArmorBonus) {
return false;
}
if (canHover == other.canHover) {
return false;
}
if (charismaScore == other.charismaScore) {
return false;
}
return true;
}
public static class Helpers { public static class Helpers {
public static int getAbilityModifierForScore(int score) { public static int getAbilityModifierForScore(int score) {
return (int) Math.floor((score - 10) / 2.0); return (int) Math.floor((score - 10) / 2.0);

View File

@@ -1,22 +1,21 @@
package com.majinnaibu.monstercards.ui.dashboard; package com.majinnaibu.monstercards.ui.dashboard;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.majinnaibu.monstercards.R; import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.data.enums.AbilityScore;
import com.majinnaibu.monstercards.data.enums.AdvantageType;
import com.majinnaibu.monstercards.data.enums.ChallengeRating;
import com.majinnaibu.monstercards.data.enums.ProficiencyType;
import com.majinnaibu.monstercards.models.Monster; import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.models.Trait;
import com.majinnaibu.monstercards.ui.shared.MCFragment; import com.majinnaibu.monstercards.ui.shared.MCFragment;
import com.majinnaibu.monstercards.utils.Logger; import com.majinnaibu.monstercards.utils.Logger;
@@ -27,8 +26,9 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
public class DashboardFragment extends MCFragment { public class DashboardFragment extends MCFragment {
private static final String MODIFIER_FORMAT = "%+d"; private static final String MODIFIER_FORMAT = "%+d";
private ViewHolder mHolder;
private DashboardViewModel mViewModel; private DashboardViewModel mViewModel;
private ViewHolder mHolder;
private DashboardRecyclerViewAdapter mAdapter;
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
@@ -36,12 +36,7 @@ public class DashboardFragment extends MCFragment {
View root = inflater.inflate(R.layout.fragment_dashboard, container, false); View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
mHolder = new ViewHolder(root); mHolder = new ViewHolder(root);
mViewModel.getMonsters().observe(getViewLifecycleOwner(), monsters -> { setupRecyclerView(mHolder.list);
setupMonsterCards(mHolder.monsterCards, R.layout.card_monster, monsters);
setupMonsterCards(mHolder.monsterShortCards, R.layout.card_monster_short, monsters);
setupMonsterCards(mHolder.monsterTiles, R.layout.tile_monster, monsters);
setupMonsterCards(mHolder.monsterShortTiles, R.layout.tile_monster_short, monsters);
});
getMonsterRepository() getMonsterRepository()
.getMonsters() .getMonsters()
@@ -54,362 +49,38 @@ public class DashboardFragment extends MCFragment {
return root; return root;
} }
private void setupMonsterCards(GridLayout parent, int layout, List<Monster> monsters) { private void setupRecyclerView(@NonNull RecyclerView recyclerView) {
LayoutInflater inflater = LayoutInflater.from(requireContext()); int columnCount = Math.max(1, (int) Math.floor(getResources().getConfiguration().screenWidthDp / 396));
parent.removeAllViews(); Logger.logWTF(String.format("Setting column count to %d", columnCount));
for (Monster monster : monsters) { Context context = requireContext();
GridLayoutManager layoutManager = new GridLayoutManager(context, columnCount);
recyclerView.setLayoutManager(layoutManager);
LiveData<List<Monster>> monsterData = mViewModel.getMonsters();
mAdapter = new DashboardRecyclerViewAdapter(monster -> {
if (monster != null) { if (monster != null) {
View root = inflater.inflate(layout, parent, false); navigateToMonsterDetail(monster);
setupCardOrTile(root, monster, layout); } else {
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(root.getLayoutParams()); Logger.logError("Can't navigate to MonsterDetailFragment with a null monster");
ViewGroup.LayoutParams lp2 = root.getLayoutParams();
layoutParams.width = 0;
layoutParams.height = GridLayout.LayoutParams.WRAP_CONTENT;
layoutParams.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 01.0f);
root.setLayoutParams(layoutParams);
parent.addView(root);
} }
});
if (monsterData != null) {
monsterData.observe(getViewLifecycleOwner(), monsters -> mAdapter.submitList(monsters));
} }
recyclerView.setAdapter(mAdapter);
} }
private void setupCardOrTile(View root, Monster monster, int layout) { private void navigateToMonsterDetail(Monster monster) {
if (layout == R.layout.card_monster || layout == R.layout.card_monster_short) { NavDirections action = DashboardFragmentDirections.actionNavigationDashboardToNavigationMonster(monster.id.toString());
MonsterCardViewHolder holder = new MonsterCardViewHolder(root); Navigation.findNavController(requireView()).navigate(action);
int numActions = monster.actions.size();
Trait action1 = numActions > 0 ? monster.actions.get(0) : null;
Trait action2 = numActions > 1 ? monster.actions.get(1) : null;
Trait action3 = numActions > 2 ? monster.actions.get(2) : null;
setupAction(holder.action1, action1);
setupAction(holder.action2, action2);
setupAction(holder.action3, action3);
int acValue = monster.getArmorClassValue();
holder.armorClass.value.setText(acValue <= 0 ? "*" : String.format("%d", acValue));
holder.challengeRating.setText(String.format("CR %s", getChallengeRatingAbbreviation(monster.challengeRating)));
setupAttribute(holder.strength, monster, AbilityScore.STRENGTH);
setupAttribute(holder.dexterity, monster, AbilityScore.DEXTERITY);
setupAttribute(holder.constitution, monster, AbilityScore.CONSTITUTION);
setupAttribute(holder.intelligence, monster, AbilityScore.INTELLIGENCE);
setupAttribute(holder.wisdom, monster, AbilityScore.WISDOM);
setupAttribute(holder.charisma, monster, AbilityScore.CHARISMA);
int hpValue = monster.getHitPointsValue();
holder.hitPoints.value.setText(hpValue <= 0 ? "*" : String.format("%d", hpValue));
holder.meta.setText(monster.getMeta());
holder.name.setText(monster.name);
} else {
MonsterTileViewHolder holder = new MonsterTileViewHolder(root);
holder.name.setText(monster.name);
holder.meta.setText(monster.getMeta());
int acValue = monster.getArmorClassValue();
holder.armorClass.value.setText(acValue <= 0 ? "*" : String.format("%d", acValue));
holder.challengeRating.value.setText(getChallengeRatingAbbreviation(monster.challengeRating));
int hpValue = monster.getHitPointsValue();
holder.hitPoints.value.setText(hpValue <= 0 ? "*" : String.format("%d", hpValue));
holder.initiative.value.setText(String.format(MODIFIER_FORMAT, monster.getDexterityModifier()));
setupAction(holder.action, monster.actions.size() > 0 ? monster.actions.get(0) : null);
}
}
private void setupAttribute(AttributeViewHolder holder, Monster monster, AbilityScore abilityScore) {
holder.name.setText(getAbilityScoreAbbreviation(abilityScore));
holder.modifier.setText(String.format(MODIFIER_FORMAT, monster.getAbilityModifier(abilityScore)));
holder.proficiency.setText(getProficiencyAbbreviation(monster.getSavingThrowProficiencyType(abilityScore)));
holder.advantage.setText(getAdvantageAbbreviation(monster.getSavingThrowAdvantageType(abilityScore)));
}
private String getAbilityScoreAbbreviation(AbilityScore abilityScore) {
switch (abilityScore) {
case STRENGTH:
return "S";
case DEXTERITY:
return "D";
case CONSTITUTION:
return "C";
case INTELLIGENCE:
return "I";
case WISDOM:
return "W";
case CHARISMA:
return "Ch";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for AbilityScore value %s", abilityScore));
return "";
}
}
private String getChallengeRatingAbbreviation(ChallengeRating challengeRating) {
Logger.logUnimplementedMethod();
switch (challengeRating) {
case CUSTOM:
return "*";
case ZERO:
return "0";
case ONE_EIGHTH:
return "1/8";
case ONE_QUARTER:
return "1/4";
case ONE_HALF:
return "1/2";
case ONE:
return "1";
case TWO:
return "2";
case THREE:
return "3";
case FOUR:
return "4";
case FIVE:
return "5";
case SIX:
return "6";
case SEVEN:
return "7";
case EIGHT:
return "8";
case NINE:
return "9";
case TEN:
return "10";
case ELEVEN:
return "11";
case TWELVE:
return "12";
case THIRTEEN:
return "13";
case FOURTEEN:
return "14";
case FIFTEEN:
return "15";
case SIXTEEN:
return "16";
case SEVENTEEN:
return "17";
case EIGHTEEN:
return "18";
case NINETEEN:
return "19";
case TWENTY:
return "20";
case TWENTY_ONE:
return "21";
case TWENTY_TWO:
return "22";
case TWENTY_THREE:
return "23";
case TWENTY_FOUR:
return "24";
case TWENTY_FIVE:
return "25";
case TWENTY_SIX:
return "26";
case TWENTY_SEVEN:
return "27";
case TWENTY_EIGHT:
return "28";
case TWENTY_NINE:
return "29";
case THIRTY:
return "30";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for ChallengeRating value %s", challengeRating));
return "";
}
}
private String getProficiencyAbbreviation(ProficiencyType proficiency) {
switch (proficiency) {
case NONE:
return "";
case EXPERTISE:
return "E";
case PROFICIENT:
return "P";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for ProficiencyType value %s", proficiency));
return "";
}
}
private String getAdvantageAbbreviation(AdvantageType advantage) {
switch (advantage) {
case NONE:
return "";
case ADVANTAGE:
return "A";
case DISADVANTAGE:
return "D";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for AdvantageType value %s", advantage));
return "";
}
}
private void setupAction(ActionViewHolder holder, Trait action) {
if (action == null) {
if (holder.root != null) {
holder.root.setVisibility(View.INVISIBLE);
}
} else {
if (holder.name != null) {
holder.name.setText(action.name);
}
if (holder.description != null) {
holder.description.setText(action.description);
}
}
} }
private static class ViewHolder { private static class ViewHolder {
final GridLayout monsterCards; final RecyclerView list;
final GridLayout monsterShortCards;
final GridLayout monsterTiles;
final GridLayout monsterShortTiles;
ViewHolder(View root) { ViewHolder(View root) {
monsterCards = root.findViewById(R.id.monsterCards); list = root.findViewById(R.id.list);
monsterShortCards = root.findViewById(R.id.monsterShortCards);
monsterTiles = root.findViewById(R.id.monsterTiles);
monsterShortTiles = root.findViewById(R.id.monsterShortTiles);
} }
} }
private static class MonsterCardViewHolder {
final TextView name;
final TextView meta;
final TextView challengeRating;
final AttributeViewHolder strength;
final AttributeViewHolder dexterity;
final AttributeViewHolder constitution;
final AttributeViewHolder intelligence;
final AttributeViewHolder wisdom;
final AttributeViewHolder charisma;
final ArmorClassViewHolder armorClass;
final HitPointsViewHolder hitPoints;
final ActionViewHolder action1;
final ActionViewHolder action2;
final ActionViewHolder action3;
MonsterCardViewHolder(View root) {
name = root.findViewById(R.id.name);
meta = root.findViewById(R.id.meta);
challengeRating = root.findViewById(R.id.challengeRating);
strength = new AttributeViewHolder(root.findViewById(R.id.strength));
dexterity = new AttributeViewHolder(root.findViewById(R.id.dexterity));
constitution = new AttributeViewHolder(root.findViewById(R.id.constitution));
intelligence = new AttributeViewHolder(root.findViewById(R.id.intelligence));
wisdom = new AttributeViewHolder(root.findViewById(R.id.wisdom));
charisma = new AttributeViewHolder(root.findViewById(R.id.charisma));
armorClass = new ArmorClassViewHolder(root.findViewById(R.id.armorClass));
hitPoints = new HitPointsViewHolder(root.findViewById(R.id.hitPoints));
action1 = new ActionViewHolder(root.findViewById(R.id.action1));
action2 = new ActionViewHolder(root.findViewById(R.id.action2));
action3 = new ActionViewHolder(root.findViewById(R.id.action3));
}
}
private static class MonsterTileViewHolder {
final TextView name;
final TextView meta;
final ArmorClassViewHolder armorClass;
final HitPointsViewHolder hitPoints;
final InitiativeViewHolder initiative;
final ChallengeRatingViewHolder challengeRating;
final ActionViewHolder action;
MonsterTileViewHolder(View root) {
name = root.findViewById(R.id.name);
meta = root.findViewById(R.id.meta);
armorClass = new ArmorClassViewHolder(root.findViewById(R.id.armorClass));
hitPoints = new HitPointsViewHolder(root.findViewById(R.id.hitPoints));
initiative = new InitiativeViewHolder(root.findViewById(R.id.initiative));
challengeRating = new ChallengeRatingViewHolder(root.findViewById(R.id.challengeRating));
action = new ActionViewHolder(root.findViewById(R.id.action));
}
}
private static class ArmorClassViewHolder {
final TextView value;
ArmorClassViewHolder(View root) {
if (root == null) {
value = null;
} else {
value = root.findViewById(R.id.value);
}
}
}
private static class HitPointsViewHolder {
final TextView value;
HitPointsViewHolder(View root) {
if (root == null) {
value = null;
} else {
value = root.findViewById(R.id.value);
}
}
}
private static class InitiativeViewHolder {
final TextView value;
InitiativeViewHolder(View root) {
if (root == null) {
value = null;
} else {
value = root.findViewById(R.id.value);
}
}
}
private static class ChallengeRatingViewHolder {
final TextView value;
ChallengeRatingViewHolder(View root) {
if (root == null) {
value = null;
} else {
value = root.findViewById(R.id.value);
}
}
}
private static class AttributeViewHolder {
final TextView name;
final TextView advantage;
final TextView proficiency;
final TextView modifier;
AttributeViewHolder(View root) {
if (root == null) {
name = null;
advantage = null;
proficiency = null;
modifier = null;
} else {
name = root.findViewById(R.id.name);
advantage = root.findViewById(R.id.advantage);
proficiency = root.findViewById(R.id.proficiency);
modifier = root.findViewById(R.id.modifier);
}
}
}
private static class ActionViewHolder {
final TextView name;
final TextView description;
final View root;
ActionViewHolder(View root) {
this.root = root;
if (root == null) {
name = null;
description = null;
} else {
name = root.findViewById(R.id.name);
description = root.findViewById(R.id.description);
}
}
}
} }

View File

@@ -0,0 +1,348 @@
package com.majinnaibu.monstercards.ui.dashboard;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
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.data.enums.AbilityScore;
import com.majinnaibu.monstercards.data.enums.AdvantageType;
import com.majinnaibu.monstercards.data.enums.ChallengeRating;
import com.majinnaibu.monstercards.data.enums.ProficiencyType;
import com.majinnaibu.monstercards.databinding.CardMonsterBinding;
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.models.Trait;
import com.majinnaibu.monstercards.utils.Logger;
import org.jetbrains.annotations.NotNull;
public class DashboardRecyclerViewAdapter extends ListAdapter<Monster, DashboardRecyclerViewAdapter.ViewHolder> {
private static final DiffUtil.ItemCallback<Monster> DIFF_CALLBACK = new DiffUtil.ItemCallback<Monster>() {
@Override
public boolean areItemsTheSame(@NonNull @NotNull Monster oldItem, @NonNull @NotNull Monster newItem) {
return oldItem.id.equals(newItem.id);
}
@Override
public boolean areContentsTheSame(@NonNull @NotNull Monster oldItem, @NonNull @NotNull Monster newItem) {
return oldItem.equals(newItem);
}
};
private final ItemCallback mOnClick;
protected DashboardRecyclerViewAdapter(ItemCallback onClick) {
super(DIFF_CALLBACK);
mOnClick = onClick;
}
@NonNull
@NotNull
@Override
public ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
return new ViewHolder(CardMonsterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(@NonNull @NotNull ViewHolder holder, int position) {
Logger.logUnimplementedMethod();
Monster monster = getItem(position);
holder.monster = monster;
holder.name.setText(monster.name);
holder.meta.setText(monster.getMeta());
holder.strengthAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.strengthSavingThrowAdvantage));
holder.strengthModifier.setText(Helpers.getModifierString(monster.getStrengthModifier()));
holder.strengthName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.STRENGTH));
holder.strengthProficiency.setText(Helpers.getProficiencyAbbreviation(monster.strengthSavingThrowProficiency));
holder.dexterityAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.dexteritySavingThrowAdvantage));
holder.dexterityModifier.setText(Helpers.getModifierString(monster.getDexterityModifier()));
holder.dexterityName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.DEXTERITY));
holder.dexterityProficiency.setText(Helpers.getProficiencyAbbreviation(monster.dexteritySavingThrowProficiency));
holder.constitutionAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.constitutionSavingThrowAdvantage));
holder.constitutionModifier.setText(Helpers.getModifierString(monster.getConstitutionModifier()));
holder.constitutionName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.CONSTITUTION));
holder.constitutionProficiency.setText(Helpers.getProficiencyAbbreviation(monster.constitutionSavingThrowProficiency));
holder.intelligenceAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.intelligenceSavingThrowAdvantage));
holder.intelligenceModifier.setText(Helpers.getModifierString(monster.getIntelligenceModifier()));
holder.intelligenceName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.INTELLIGENCE));
holder.intelligenceProficiency.setText(Helpers.getProficiencyAbbreviation(monster.intelligenceSavingThrowProficiency));
holder.wisdomAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.wisdomSavingThrowAdvantage));
holder.wisdomModifier.setText(Helpers.getModifierString(monster.getWisdomModifier()));
holder.wisdomName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.WISDOM));
holder.wisdomProficiency.setText(Helpers.getProficiencyAbbreviation(monster.wisdomSavingThrowProficiency));
holder.charismaAdvantage.setText(Helpers.getAdvantageAbbreviation(monster.charismaSavingThrowAdvantage));
holder.charismaModifier.setText(Helpers.getModifierString(monster.getCharismaModifier()));
holder.charismaName.setText(Helpers.getAbilityScoreAbbreviation(AbilityScore.CHARISMA));
holder.charismaProficiency.setText(Helpers.getProficiencyAbbreviation(monster.charismaSavingThrowProficiency));
holder.armorClass.setText(String.valueOf(monster.getArmorClassValue()));
holder.hitPoints.setText(String.valueOf(monster.getHitPointsValue()));
holder.challengeRating.setText("CR " + Helpers.getChallengeRatingAbbreviation(monster.challengeRating));
int numActions = monster.actions.size();
if (numActions > 0) {
holder.action1Group.setVisibility(View.VISIBLE);
Trait action = monster.actions.get(0);
holder.action1Description.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.description)));
holder.action1Name.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.name)));
} else {
holder.action1Group.setVisibility(View.GONE);
}
if (numActions > 1) {
holder.action2Group.setVisibility(View.VISIBLE);
Trait action = monster.actions.get(1);
holder.action2Description.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.description)));
holder.action2Name.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.name)));
} else {
holder.action2Group.setVisibility(View.GONE);
}
if (numActions > 2) {
holder.action3Group.setVisibility(View.VISIBLE);
Trait action = monster.actions.get(2);
holder.action3Description.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.description)));
holder.action3Name.setText(Html.fromHtml(CommonMarkHelper.toHtml(action.name)));
} else {
holder.action3Group.setVisibility(View.GONE);
}
holder.itemView.setOnClickListener(v -> {
if (mOnClick != null) {
mOnClick.onItemCallback(holder.monster);
}
});
}
public interface ItemCallback {
void onItemCallback(Monster monster);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView name;
public final TextView meta;
public final View action1Group;
public final TextView action1Name;
public final TextView action1Description;
public final View action2Group;
public final TextView action2Name;
public final TextView action2Description;
public final View action3Group;
public final TextView action3Name;
public final TextView action3Description;
public final TextView strengthName;
public final TextView strengthModifier;
public final TextView strengthProficiency;
public final TextView strengthAdvantage;
public final TextView dexterityName;
public final TextView dexterityModifier;
public final TextView dexterityProficiency;
public final TextView dexterityAdvantage;
public final TextView constitutionName;
public final TextView constitutionModifier;
public final TextView constitutionProficiency;
public final TextView constitutionAdvantage;
public final TextView intelligenceName;
public final TextView intelligenceModifier;
public final TextView intelligenceProficiency;
public final TextView intelligenceAdvantage;
public final TextView wisdomName;
public final TextView wisdomModifier;
public final TextView wisdomProficiency;
public final TextView wisdomAdvantage;
public final TextView charismaName;
public final TextView charismaModifier;
public final TextView charismaProficiency;
public final TextView charismaAdvantage;
public final TextView armorClass;
public final TextView hitPoints;
public final TextView challengeRating;
public Monster monster;
public ViewHolder(CardMonsterBinding binding) {
super(binding.getRoot());
name = binding.name;
meta = binding.meta;
action1Group = binding.action1.getRoot();
action1Name = binding.action1.name;
action1Description = binding.action1.description;
action2Group = binding.action2.getRoot();
action2Name = binding.action2.name;
action2Description = binding.action2.description;
action3Group = binding.action3.getRoot();
action3Name = binding.action3.name;
action3Description = binding.action3.description;
strengthName = binding.strength.name;
strengthModifier = binding.strength.modifier;
strengthProficiency = binding.strength.proficiency;
strengthAdvantage = binding.strength.advantage;
dexterityName = binding.dexterity.name;
dexterityModifier = binding.dexterity.modifier;
dexterityProficiency = binding.dexterity.proficiency;
dexterityAdvantage = binding.dexterity.advantage;
constitutionName = binding.constitution.name;
constitutionModifier = binding.constitution.modifier;
constitutionProficiency = binding.constitution.proficiency;
constitutionAdvantage = binding.constitution.advantage;
intelligenceName = binding.intelligence.name;
intelligenceModifier = binding.intelligence.modifier;
intelligenceProficiency = binding.intelligence.proficiency;
intelligenceAdvantage = binding.intelligence.advantage;
wisdomName = binding.wisdom.name;
wisdomModifier = binding.wisdom.modifier;
wisdomProficiency = binding.wisdom.proficiency;
wisdomAdvantage = binding.wisdom.advantage;
charismaName = binding.charisma.name;
charismaModifier = binding.charisma.modifier;
charismaProficiency = binding.charisma.proficiency;
charismaAdvantage = binding.charisma.advantage;
armorClass = binding.armorClass.value;
hitPoints = binding.hitPoints.value;
challengeRating = binding.challengeRating;
}
}
public static class Helpers {
public static String getModifierString(int value) {
return String.format("%+d", value);
}
public static String getAbilityScoreAbbreviation(AbilityScore abilityScore) {
switch (abilityScore) {
case STRENGTH:
return "S";
case DEXTERITY:
return "D";
case CONSTITUTION:
return "C";
case INTELLIGENCE:
return "I";
case WISDOM:
return "W";
case CHARISMA:
return "Ch";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for AbilityScore value %s", abilityScore));
return "";
}
}
public static String getChallengeRatingAbbreviation(ChallengeRating challengeRating) {
Logger.logUnimplementedMethod();
switch (challengeRating) {
case CUSTOM:
return "*";
case ZERO:
return "0";
case ONE_EIGHTH:
return "1/8";
case ONE_QUARTER:
return "1/4";
case ONE_HALF:
return "1/2";
case ONE:
return "1";
case TWO:
return "2";
case THREE:
return "3";
case FOUR:
return "4";
case FIVE:
return "5";
case SIX:
return "6";
case SEVEN:
return "7";
case EIGHT:
return "8";
case NINE:
return "9";
case TEN:
return "10";
case ELEVEN:
return "11";
case TWELVE:
return "12";
case THIRTEEN:
return "13";
case FOURTEEN:
return "14";
case FIFTEEN:
return "15";
case SIXTEEN:
return "16";
case SEVENTEEN:
return "17";
case EIGHTEEN:
return "18";
case NINETEEN:
return "19";
case TWENTY:
return "20";
case TWENTY_ONE:
return "21";
case TWENTY_TWO:
return "22";
case TWENTY_THREE:
return "23";
case TWENTY_FOUR:
return "24";
case TWENTY_FIVE:
return "25";
case TWENTY_SIX:
return "26";
case TWENTY_SEVEN:
return "27";
case TWENTY_EIGHT:
return "28";
case TWENTY_NINE:
return "29";
case THIRTY:
return "30";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for ChallengeRating value %s", challengeRating));
return "";
}
}
public static String getProficiencyAbbreviation(ProficiencyType proficiency) {
switch (proficiency) {
case NONE:
return "";
case EXPERTISE:
return "E";
case PROFICIENT:
return "P";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for ProficiencyType value %s", proficiency));
return "";
}
}
public static String getAdvantageAbbreviation(AdvantageType advantage) {
switch (advantage) {
case NONE:
return "";
case ADVANTAGE:
return "A";
case DISADVANTAGE:
return "D";
default:
Logger.logUnimplementedFeature(String.format("Get an abbreviation for AdvantageType value %s", advantage));
return "";
}
}
}
}

View File

@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:background="@drawable/rectangle_background"> android:background="@drawable/rectangle_background">
<TextView <TextView

View File

@@ -1,145 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.dashboard.DashboardFragment"> tools:context=".ui.dashboard.DashboardFragment">
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_margin="8dp" android:divider="?android:attr/dividerVertical"
android:orientation="vertical"> android:dividerPadding="@dimen/text_margin"
app:layoutManager="LinearLayoutManager"
<GridLayout app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/monsterCards" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="match_parent" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"
android:columnCount="2"> tools:listitem="@layout/card_monster" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- <include-->
<!-- layout="@layout/card_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
</GridLayout>
<GridLayout
android:id="@+id/monsterShortCards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2">
<!-- <include-->
<!-- layout="@layout/card_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
</GridLayout>
<GridLayout
android:id="@+id/monsterTiles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2">
<!-- <include-->
<!-- layout="@layout/tile_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/tile_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
<!-- <include-->
<!-- layout="@layout/tile_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/tile_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
</GridLayout>
<GridLayout
android:id="@+id/monsterShortTiles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2">
<!-- <include-->
<!-- layout="@layout/tile_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
<!-- <include-->
<!-- layout="@layout/tile_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
<!-- <include-->
<!-- layout="@layout/tile_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
<!-- <include-->
<!-- layout="@layout/tile_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
</GridLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
<!-- <include-->
<!-- layout="@layout/card_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/card_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/tile_monster"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp"/>-->
<!-- <include-->
<!-- layout="@layout/tile_monster_short"-->
<!-- android:layout_width="0dp"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_columnWeight="1"-->
<!-- android:layout_marginVertical="8dp" />-->
</LinearLayout>