Adds abilities to monster cards.
Adds CommonMark dependency and CommonMarkHelper to render it to html.
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 30
|
||||||
buildToolsVersion "29.0.3"
|
buildToolsVersion '30.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.majinnaibu.monstercards"
|
applicationId "com.majinnaibu.monstercards"
|
||||||
minSdkVersion 22
|
minSdkVersion 22
|
||||||
targetSdkVersion 29
|
targetSdkVersion 30
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ dependencies {
|
|||||||
implementation 'androidx.navigation:navigation-fragment:2.3.0'
|
implementation 'androidx.navigation:navigation-fragment:2.3.0'
|
||||||
implementation 'androidx.navigation:navigation-ui:2.3.0'
|
implementation 'androidx.navigation:navigation-ui:2.3.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
|
implementation 'com.atlassian.commonmark:commonmark:0.15.2'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
|
|||||||
@@ -4,9 +4,20 @@ import org.commonmark.node.Document;
|
|||||||
import org.commonmark.node.Node;
|
import org.commonmark.node.Node;
|
||||||
import org.commonmark.node.Paragraph;
|
import org.commonmark.node.Paragraph;
|
||||||
import org.commonmark.parser.Parser;
|
import org.commonmark.parser.Parser;
|
||||||
|
import org.commonmark.renderer.NodeRenderer;
|
||||||
|
import org.commonmark.renderer.html.HtmlNodeRendererContext;
|
||||||
|
import org.commonmark.renderer.html.HtmlNodeRendererFactory;
|
||||||
import org.commonmark.renderer.html.HtmlRenderer;
|
import org.commonmark.renderer.html.HtmlRenderer;
|
||||||
|
|
||||||
public final class CommonMarkHelper {
|
public final class CommonMarkHelper {
|
||||||
|
private static final class MyNodeRendererFactory implements HtmlNodeRendererFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NodeRenderer create(HtmlNodeRendererContext context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String toHtml(String rawCommonMark) {
|
public static String toHtml(String rawCommonMark) {
|
||||||
Parser parser = Parser.builder().build();
|
Parser parser = Parser.builder().build();
|
||||||
Node document = parser.parse(rawCommonMark);
|
Node document = parser.parse(rawCommonMark);
|
||||||
@@ -15,7 +26,7 @@ public final class CommonMarkHelper {
|
|||||||
if (parent1 == parent2 && parent1 instanceof Paragraph) {
|
if (parent1 == parent2 && parent1 instanceof Paragraph) {
|
||||||
document = new Document();
|
document = new Document();
|
||||||
Node child = parent1.getFirstChild();
|
Node child = parent1.getFirstChild();
|
||||||
while (child != null) {
|
while(child != null) {
|
||||||
Node nextChild = child.getNext();
|
Node nextChild = child.getNext();
|
||||||
document.appendChild(child);
|
document.appendChild(child);
|
||||||
child = nextChild;//child.getNext();
|
child = nextChild;//child.getNext();
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.majinnaibu.monstercards.models;
|
||||||
|
|
||||||
|
public class Ability {
|
||||||
|
|
||||||
|
public Ability(String name, String description) {
|
||||||
|
mName = name;
|
||||||
|
mDescription = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mName;
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
public void setName(String name) {
|
||||||
|
mName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mDescription;
|
||||||
|
public String getDescription() {
|
||||||
|
return mDescription;
|
||||||
|
}
|
||||||
|
public void setDescription(String description) {
|
||||||
|
mDescription = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,11 +13,12 @@ import java.util.Set;
|
|||||||
public class Monster {
|
public class Monster {
|
||||||
|
|
||||||
public Monster() {
|
public Monster() {
|
||||||
|
mAbilities = new ArrayList<>();
|
||||||
|
mConditionImmunities = new HashSet<>();
|
||||||
|
mDamageTypes = new HashSet<>();
|
||||||
|
mLanguages = new HashSet<>();
|
||||||
mSavingThrows = new HashSet<>();
|
mSavingThrows = new HashSet<>();
|
||||||
mSkills = new HashSet<>();
|
mSkills = new HashSet<>();
|
||||||
mDamageTypes = new HashSet<>();
|
|
||||||
mConditionImmunities = new HashSet<>();
|
|
||||||
mLanguages = new HashSet<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String mName;
|
private String mName;
|
||||||
@@ -840,4 +841,142 @@ public class Monster {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getChallengeRatingDescription() {
|
||||||
|
String challengeRating = getChallengeRating();
|
||||||
|
if ("*".equals(challengeRating)) {
|
||||||
|
// Custom CR
|
||||||
|
return getCustomChallengeRating();
|
||||||
|
} else if ("0".equals(challengeRating)) {
|
||||||
|
return "0 (10 XP)";
|
||||||
|
} else if ("1/8".equals(challengeRating)) {
|
||||||
|
return "1/8 (25 XP)";
|
||||||
|
} else if ("1/4".equals(challengeRating)) {
|
||||||
|
return "1/4 (50 XP)";
|
||||||
|
} else if ("1/2".equals(challengeRating)) {
|
||||||
|
return "1/2 (100 XP)";
|
||||||
|
} else if ("1".equals(challengeRating)) {
|
||||||
|
return "1 (200 XP)";
|
||||||
|
} else if ("2".equals(challengeRating)) {
|
||||||
|
return "2 (450 XP)";
|
||||||
|
} else if ("3".equals(challengeRating)) {
|
||||||
|
return "3 (700 XP)";
|
||||||
|
} else if ("4".equals(challengeRating)) {
|
||||||
|
return "4 (1,100 XP)";
|
||||||
|
} else if ("5".equals(challengeRating)) {
|
||||||
|
return "5 (1,800 XP)";
|
||||||
|
} else if ("6".equals(challengeRating)) {
|
||||||
|
return "6 (2,300 XP)";
|
||||||
|
} else if ("7".equals(challengeRating)) {
|
||||||
|
return "7 (2,900 XP)";
|
||||||
|
} else if ("8".equals(challengeRating)) {
|
||||||
|
return "8 (3,900 XP)";
|
||||||
|
} else if ("9".equals(challengeRating)) {
|
||||||
|
return "9 (5,000 XP)";
|
||||||
|
} else if ("10".equals(challengeRating)) {
|
||||||
|
return "10 (5,900 XP)";
|
||||||
|
} else if ("11".equals(challengeRating)) {
|
||||||
|
return "11 (7,200 XP)";
|
||||||
|
} else if ("12".equals(challengeRating)) {
|
||||||
|
return "12 (8,400 XP)";
|
||||||
|
} else if ("13".equals(challengeRating)) {
|
||||||
|
return "13 (10,000 XP)";
|
||||||
|
} else if ("14".equals(challengeRating)) {
|
||||||
|
return "14 (11,500 XP)";
|
||||||
|
} else if ("15".equals(challengeRating)) {
|
||||||
|
return "15 (13,000 XP)";
|
||||||
|
} else if ("16".equals(challengeRating)) {
|
||||||
|
return "16 (15,000 XP)";
|
||||||
|
} else if ("17".equals(challengeRating)) {
|
||||||
|
return "17 (18,000 XP)";
|
||||||
|
} else if ("18".equals(challengeRating)) {
|
||||||
|
return "18 (20,000 XP)";
|
||||||
|
} else if ("19".equals(challengeRating)) {
|
||||||
|
return "19 (22,000 XP)";
|
||||||
|
} else if ("20".equals(challengeRating)) {
|
||||||
|
return "20 (25,000 XP)";
|
||||||
|
} else if ("21".equals(challengeRating)) {
|
||||||
|
return "21 (33,000 XP)";
|
||||||
|
} else if ("22".equals(challengeRating)) {
|
||||||
|
return "22 (41,000 XP)";
|
||||||
|
} else if ("23".equals(challengeRating)) {
|
||||||
|
return "23 (50,000 XP)";
|
||||||
|
} else if ("24".equals(challengeRating)) {
|
||||||
|
return "24 (62,000 XP)";
|
||||||
|
} else if ("25".equals(challengeRating)) {
|
||||||
|
return "25 (75,000 XP)";
|
||||||
|
} else if ("26".equals(challengeRating)) {
|
||||||
|
return "26 (90,000 XP)";
|
||||||
|
} else if ("27".equals(challengeRating)) {
|
||||||
|
return "27 (105,000 XP)";
|
||||||
|
} else if ("28".equals(challengeRating)) {
|
||||||
|
return "28 (120,000 XP)";
|
||||||
|
} else if ("29".equals(challengeRating)) {
|
||||||
|
return "29 (135,000 XP)";
|
||||||
|
} else if ("30".equals(challengeRating)) {
|
||||||
|
return "30 (155,000 XP)";
|
||||||
|
} else {
|
||||||
|
return getCustomChallengeRating();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Ability> mAbilities;
|
||||||
|
public List<Ability> getAbilities() {
|
||||||
|
return mAbilities;
|
||||||
|
}
|
||||||
|
public void addAbility(Ability ability) {
|
||||||
|
mAbilities.add(ability);
|
||||||
|
}
|
||||||
|
public void removeAbility(Ability ability) {
|
||||||
|
mAbilities.remove(ability);
|
||||||
|
}
|
||||||
|
public void clearAbilities() {
|
||||||
|
mAbilities.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAbilityDescriptions() {
|
||||||
|
ArrayList<String> abilities = new ArrayList<>();
|
||||||
|
for (Ability ability : getAbilities()) {
|
||||||
|
abilities.add(getPlaceholderReplacedText(String.format("__%s__ %s", ability.getName(), ability.getDescription())));
|
||||||
|
}
|
||||||
|
return abilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlaceholderReplacedText(String rawText) {
|
||||||
|
return rawText
|
||||||
|
.replaceAll("\\[STR SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("strength")))
|
||||||
|
.replaceAll("\\[STR ATK]", String.format(Locale.US, "%+d", getAttackBonus("strength")))
|
||||||
|
.replaceAll("\\[DEX SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("dexterity")))
|
||||||
|
.replaceAll("\\[DEX ATK]", String.format(Locale.US, "%+d", getAttackBonus("dexterity")))
|
||||||
|
.replaceAll("\\[CON SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("constitution")))
|
||||||
|
.replaceAll("\\[CON ATK]", String.format(Locale.US, "%+d", getAttackBonus("constitution")))
|
||||||
|
.replaceAll("\\[INT SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("intelligence")))
|
||||||
|
.replaceAll("\\[INT ATK]", String.format(Locale.US, "%+d", getAttackBonus("intelligence")))
|
||||||
|
.replaceAll("\\[WIS SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("wisdom")))
|
||||||
|
.replaceAll("\\[WIS ATK]", String.format(Locale.US, "%+d", getAttackBonus("wisdom")))
|
||||||
|
.replaceAll("\\[CHA SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("charisma")))
|
||||||
|
.replaceAll("\\[CHA ATK]", String.format(Locale.US, "%+d", getAttackBonus("charisma")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSavingThrow(String name) {
|
||||||
|
Set<SavingThrow> sts = getSavingThrows();
|
||||||
|
for(SavingThrow st : sts) {
|
||||||
|
if (name.equals(st.getName())) {
|
||||||
|
return getAbilityModifier(name) + getProficiencyBonus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getAbilityModifier(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWisdomSave() {
|
||||||
|
return String.format(Locale.US, "%+d", getSavingThrow("wis"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpellSaveDC(String abilityScoreName) {
|
||||||
|
return 8 + getProficiencyBonus() + getAbilityModifier(abilityScoreName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAttackBonus(String abilityScoreName) {
|
||||||
|
return getProficiencyBonus() + getAbilityModifier(abilityScoreName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
package com.majinnaibu.monstercards.ui.monster;
|
package com.majinnaibu.monstercards.ui.monster;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.TypedValue;
|
||||||
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.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -14,13 +20,17 @@ import androidx.lifecycle.Observer;
|
|||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import com.majinnaibu.monstercards.R;
|
import com.majinnaibu.monstercards.R;
|
||||||
|
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
|
||||||
import com.majinnaibu.monstercards.helpers.StringHelper;
|
import com.majinnaibu.monstercards.helpers.StringHelper;
|
||||||
|
import com.majinnaibu.monstercards.models.Ability;
|
||||||
import com.majinnaibu.monstercards.models.DamageType;
|
import com.majinnaibu.monstercards.models.DamageType;
|
||||||
import com.majinnaibu.monstercards.models.Language;
|
import com.majinnaibu.monstercards.models.Language;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
import com.majinnaibu.monstercards.models.SavingThrow;
|
import com.majinnaibu.monstercards.models.SavingThrow;
|
||||||
import com.majinnaibu.monstercards.models.Skill;
|
import com.majinnaibu.monstercards.models.Skill;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
public class MonsterFragment extends Fragment {
|
public class MonsterFragment extends Fragment {
|
||||||
|
|
||||||
@@ -99,11 +109,14 @@ public class MonsterFragment extends Fragment {
|
|||||||
monster.addLanguage(new Language("French", true));
|
monster.addLanguage(new Language("French", true));
|
||||||
monster.addLanguage(new Language("Mermataur", false));
|
monster.addLanguage(new Language("Mermataur", false));
|
||||||
monster.addLanguage(new Language("Goldfish", false));
|
monster.addLanguage(new Language("Goldfish", false));
|
||||||
|
|
||||||
// Challenge Rating
|
// Challenge Rating
|
||||||
monster.setChallengeRating("*");
|
monster.setChallengeRating("*");
|
||||||
monster.setCustomChallengeRating("Infinite (0XP)");
|
monster.setCustomChallengeRating("Infinite (0XP)");
|
||||||
monster.setCustomProficiencyBonus(4);
|
monster.setCustomProficiencyBonus(4);
|
||||||
|
// Abilities
|
||||||
|
monster.addAbility(new Ability("Spellcasting", "The acolyte is a 1st-level spellcaster. Its spellcasting ability is Wisdom (spell save DC [WIS SAVE], [WIS ATK] to hit with spell attacks). The acolyte has following cleric spells prepared:\n\n\n> Cantrips (at will): _light, sacred flame, thaumaturgy_\n> 1st level (3 slots): _bless, cure wounds, sanctuary_"));
|
||||||
|
monster.addAbility(new Ability("Amphibious", "The dragon can breathe air and water."));
|
||||||
|
monster.addAbility(new Ability("Legendary Resistance (3/Day)", "If the dragon fails a saving throw, it can choose to succeed instead."));
|
||||||
// END remove block
|
// END remove block
|
||||||
monsterViewModel = new ViewModelProvider(this).get(MonsterViewModel.class);
|
monsterViewModel = new ViewModelProvider(this).get(MonsterViewModel.class);
|
||||||
View root = inflater.inflate(R.layout.fragment_monster, container, false);
|
View root = inflater.inflate(R.layout.fragment_monster, container, false);
|
||||||
@@ -301,6 +314,43 @@ public class MonsterFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final TextView monsterChallenge = root.findViewById(R.id.challenge);
|
||||||
|
monsterViewModel.getChallenge().observe(getViewLifecycleOwner(), new Observer<String>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(String challengeRating) {
|
||||||
|
monsterChallenge.setText(Html.fromHtml("<b>Challenge</b> " + challengeRating));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final LinearLayout monsterAbilities = root.findViewById(R.id.abilities);
|
||||||
|
monsterViewModel.getAbilities().observe(getViewLifecycleOwner(), new Observer<List<String>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<String> abilities) {
|
||||||
|
Context context = getContext();
|
||||||
|
DisplayMetrics displayMetrics = null;
|
||||||
|
if (context != null) {
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
if (resources != null) {
|
||||||
|
displayMetrics = resources.getDisplayMetrics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
monsterAbilities.removeAllViews();
|
||||||
|
if (abilities != null) {
|
||||||
|
for (String ability : abilities) {
|
||||||
|
TextView tvAbility = new TextView(context);
|
||||||
|
// TODO: Handle multiline block quotes specially so they stay multiline.
|
||||||
|
// TODO: Replace QuoteSpans in the result of fromHtml with something like this https://stackoverflow.com/questions/7717567/how-to-style-blockquotes-in-android-textviews to make them indent as expected
|
||||||
|
Spanned spannedText = Html.fromHtml(CommonMarkHelper.toHtml(ability));
|
||||||
|
tvAbility.setText(spannedText);
|
||||||
|
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
layoutParams.topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, displayMetrics);
|
||||||
|
tvAbility.setLayoutParams(layoutParams);
|
||||||
|
monsterAbilities.addView(tvAbility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModel;
|
|||||||
|
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class MonsterViewModel extends ViewModel {
|
public class MonsterViewModel extends ViewModel {
|
||||||
|
|
||||||
public MonsterViewModel() {
|
public MonsterViewModel() {
|
||||||
@@ -48,6 +51,10 @@ public class MonsterViewModel extends ViewModel {
|
|||||||
mSenses.setValue("");
|
mSenses.setValue("");
|
||||||
mLanguages = new MutableLiveData<>();
|
mLanguages = new MutableLiveData<>();
|
||||||
mLanguages.setValue("");
|
mLanguages.setValue("");
|
||||||
|
mChallenge = new MutableLiveData<>();
|
||||||
|
mChallenge.setValue("");
|
||||||
|
mAbilities = new MutableLiveData<>();
|
||||||
|
mAbilities.setValue(new ArrayList<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private MutableLiveData<String> mName;
|
private MutableLiveData<String> mName;
|
||||||
@@ -126,6 +133,14 @@ public class MonsterViewModel extends ViewModel {
|
|||||||
public LiveData<String> getLanguages() {
|
public LiveData<String> getLanguages() {
|
||||||
return mLanguages;
|
return mLanguages;
|
||||||
}
|
}
|
||||||
|
private MutableLiveData<String> mChallenge;
|
||||||
|
public LiveData<String> getChallenge() {
|
||||||
|
return mChallenge;
|
||||||
|
}
|
||||||
|
private MutableLiveData<List<String>> mAbilities;
|
||||||
|
public LiveData<List<String>> getAbilities() {
|
||||||
|
return mAbilities;
|
||||||
|
}
|
||||||
|
|
||||||
private Monster mMonster;
|
private Monster mMonster;
|
||||||
public void setMonster(Monster monster) {
|
public void setMonster(Monster monster) {
|
||||||
@@ -149,5 +164,7 @@ public class MonsterViewModel extends ViewModel {
|
|||||||
mConditionImmunities.setValue(mMonster.getConditionImmunitiesDescription());
|
mConditionImmunities.setValue(mMonster.getConditionImmunitiesDescription());
|
||||||
mSenses.setValue(monster.getSensesDescription());
|
mSenses.setValue(monster.getSensesDescription());
|
||||||
mLanguages.setValue(mMonster.getLanguagesDescription());
|
mLanguages.setValue(mMonster.getLanguagesDescription());
|
||||||
|
mChallenge.setValue(mMonster.getChallengeRatingDescription());
|
||||||
|
mAbilities.setValue(mMonster.getAbilityDescriptions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,5 +409,33 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@+id/senses"
|
app:layout_constraintTop_toBottomOf="@+id/senses"
|
||||||
tools:text="Languages" />
|
tools:text="Languages" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/challenge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/languages"
|
||||||
|
tools:text="Challenge" />
|
||||||
|
|
||||||
|
<!-- Abilities -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/abilities"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/challenge"
|
||||||
|
tools:text="Damage Vulnerabilities" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
Reference in New Issue
Block a user