Adds import monster activity.
This commit is contained in:
@@ -12,6 +12,40 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
<activity
|
||||||
|
android:name=".ImportMonsterActivity"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="Import Monster"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:priority="50">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<action android:name="android.intent.action.EDIT" />
|
||||||
|
<action android:name="android.intent.action.PICK" />
|
||||||
|
<action android:name="android.intent.action.INSERT" />
|
||||||
|
<action android:name="android.intent.action.INSERT_OR_EDIT" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.ALTERNATIVE" />
|
||||||
|
<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:mimeType="text/plain"
|
||||||
|
android:scheme="content" />
|
||||||
|
<data
|
||||||
|
android:mimeType="application/octet-stream"
|
||||||
|
android:scheme="content" />
|
||||||
|
<data
|
||||||
|
android:mimeType="text/plain"
|
||||||
|
android:scheme="file" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
@@ -25,4 +59,5 @@
|
|||||||
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
|
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
|
||||||
|
</manifest>
|
||||||
|
|||||||
@@ -0,0 +1,318 @@
|
|||||||
|
package com.majinnaibu.monstercards;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||||
|
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
|
||||||
|
import com.majinnaibu.monstercards.helpers.MonsterImportHelper;
|
||||||
|
import com.majinnaibu.monstercards.helpers.StringHelper;
|
||||||
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
import com.majinnaibu.monstercards.ui.monster.MonsterDetailViewModel;
|
||||||
|
import com.majinnaibu.monstercards.utils.Logger;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.rxjava3.observers.DisposableCompletableObserver;
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
|
public class ImportMonsterActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ViewHolder mHolder;
|
||||||
|
|
||||||
|
private MonsterDetailViewModel mViewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.fragment_monster);
|
||||||
|
mHolder = new ViewHolder(findViewById(android.R.id.content));
|
||||||
|
mViewModel = new ViewModelProvider(this).get(MonsterDetailViewModel.class);
|
||||||
|
|
||||||
|
mViewModel.getName().observe(this, mHolder.name::setText);
|
||||||
|
mViewModel.getMeta().observe(this, mHolder.meta::setText);
|
||||||
|
mViewModel.getArmorClass().observe(this, armorText -> setupLabeledTextView(mHolder.armorClass, armorText, R.string.label_armor_class));
|
||||||
|
mViewModel.getHitPoints().observe(this, hitPoints -> setupLabeledTextView(mHolder.hitPoints, hitPoints, R.string.label_hit_points));
|
||||||
|
mViewModel.getSpeed().observe(this, speed -> setupLabeledTextView(mHolder.speed, speed, R.string.label_speed));
|
||||||
|
mViewModel.getStrength().observe(this, mHolder.strength::setText);
|
||||||
|
mViewModel.getDexterity().observe(this, mHolder.dexterity::setText);
|
||||||
|
mViewModel.getConstitution().observe(this, mHolder.constitution::setText);
|
||||||
|
mViewModel.getIntelligence().observe(this, mHolder.intelligence::setText);
|
||||||
|
mViewModel.getWisdom().observe(this, mHolder.wisdom::setText);
|
||||||
|
mViewModel.getCharisma().observe(this, mHolder.charisma::setText);
|
||||||
|
mViewModel.getSavingThrows().observe(this, savingThrows -> setupOptionalTextView(mHolder.savingThrows, savingThrows, R.string.label_saving_throws));
|
||||||
|
mViewModel.getSkills().observe(this, skills -> setupOptionalTextView(mHolder.skills, skills, R.string.label_skills));
|
||||||
|
mViewModel.getDamageVulnerabilities().observe(this, damageTypes -> setupOptionalTextView(mHolder.damageVulnerabilities, damageTypes, R.string.label_damage_vulnerabilities));
|
||||||
|
mViewModel.getDamageResistances().observe(this, damageTypes -> setupOptionalTextView(mHolder.damageResistances, damageTypes, R.string.label_damage_resistances));
|
||||||
|
mViewModel.getDamageImmunities().observe(this, damageTypes -> setupOptionalTextView(mHolder.damageImmunities, damageTypes, R.string.label_damage_immunities));
|
||||||
|
mViewModel.getConditionImmunities().observe(this, conditionImmunities -> setupOptionalTextView(mHolder.conditionImmunities, conditionImmunities, R.string.label_condition_immunities));
|
||||||
|
mViewModel.getSenses().observe(this, senses -> setupOptionalTextView(mHolder.senses, senses, R.string.label_senses));
|
||||||
|
mViewModel.getLanguages().observe(this, languages -> setupOptionalTextView(mHolder.languages, languages, R.string.label_languages));
|
||||||
|
mViewModel.getChallenge().observe(this, challengeRating -> setupLabeledTextView(mHolder.challenge, challengeRating, R.string.label_challenge_rating));
|
||||||
|
mViewModel.getAbilities().observe(this, abilities -> setupTraitList(mHolder.abilities, abilities));
|
||||||
|
mViewModel.getActions().observe(this, actions -> setupTraitList(mHolder.actions, actions, mHolder.actions_label, mHolder.actions_divider));
|
||||||
|
mViewModel.getReactions().observe(this, reactions -> setupTraitList(mHolder.reactions, reactions, mHolder.reactions_label, mHolder.reactions_divider));
|
||||||
|
mViewModel.getRegionalEffects().observe(this, regionalEffects -> setupTraitList(mHolder.regionalEffects, regionalEffects, mHolder.regionalEffects_label, mHolder.regionalEffects_divider));
|
||||||
|
mViewModel.getLairActions().observe(this, lairActions -> setupTraitList(mHolder.lairActions, lairActions, mHolder.lairActions_label, mHolder.lairActions_divider));
|
||||||
|
mViewModel.getLegendaryActions().observe(this, legendaryActions -> setupTraitList(mHolder.legendaryActions, legendaryActions, mHolder.legendaryActions_label, mHolder.legendaryActions_divider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Logger.logDebug("onCreateView");
|
||||||
|
Monster monster = readMonsterFromIntent(getIntent());
|
||||||
|
if (monster != null) {
|
||||||
|
mViewModel.setMonster(monster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Monster readMonsterFromIntent(Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
String type = intent.getType();
|
||||||
|
String json;
|
||||||
|
Uri uri = null;
|
||||||
|
if ("android.intent.action.SEND".equals(action) && "text/plain".equals(type)) {
|
||||||
|
uri = extras.getParcelable("android.intent.extra.STREAM");
|
||||||
|
} else if ("android.intent.action.VIEW".equals(action) && ("text/plain".equals(type) || "application/octet-stream".equals(type))) {
|
||||||
|
uri = intent.getData();
|
||||||
|
} else {
|
||||||
|
Logger.logError(String.format("unexpected launch configuration action: %s, type: %s, uri: %s", action, type, uri));
|
||||||
|
}
|
||||||
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
json = readContentsOfUri(uri);
|
||||||
|
if (StringHelper.isNullOrEmpty(json)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return MonsterImportHelper.fromJSON(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readContentsOfUri(Uri uri) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
try (InputStream inputStream =
|
||||||
|
getContentResolver().openInputStream(uri);
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(Objects.requireNonNull(inputStream)))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
builder.append(line);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.logError("error reading file", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupLabeledTextView(TextView view, String text, int titleId) {
|
||||||
|
String title = getString(titleId);
|
||||||
|
String fullText = String.format("<b>%s</b> %s", title, text);
|
||||||
|
view.setText(Html.fromHtml(fullText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupOptionalTextView(TextView root, String text, int titleId) {
|
||||||
|
String title = getString(titleId);
|
||||||
|
if (StringHelper.isNullOrEmpty(text)) {
|
||||||
|
root.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
root.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
Spanned formatted;
|
||||||
|
if (StringHelper.isNullOrEmpty(title)) {
|
||||||
|
formatted = Html.fromHtml(text);
|
||||||
|
} else {
|
||||||
|
formatted = Html.fromHtml(String.format("<b>%s</b> %s", title, text));
|
||||||
|
}
|
||||||
|
root.setText(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTraitList(@NonNull LinearLayout root, @NonNull List<String> traits) {
|
||||||
|
setupTraitList(root, traits, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTraitList(@NonNull LinearLayout root, @NonNull List<String> traits, View label, View divider) {
|
||||||
|
int visibility = traits.size() > 0 ? View.VISIBLE : View.GONE;
|
||||||
|
DisplayMetrics displayMetrics = null;
|
||||||
|
Resources resources = getResources();
|
||||||
|
if (resources != null) {
|
||||||
|
displayMetrics = resources.getDisplayMetrics();
|
||||||
|
}
|
||||||
|
root.removeAllViews();
|
||||||
|
for (String action : traits) {
|
||||||
|
TextView tvAction = new TextView(this);
|
||||||
|
// 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
|
||||||
|
tvAction.setText(Html.fromHtml(CommonMarkHelper.toHtml(action)));
|
||||||
|
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);
|
||||||
|
tvAction.setLayoutParams(layoutParams);
|
||||||
|
root.addView(tvAction);
|
||||||
|
}
|
||||||
|
root.setVisibility(visibility);
|
||||||
|
if (label != null) {
|
||||||
|
label.setVisibility(visibility);
|
||||||
|
}
|
||||||
|
if (divider != null) {
|
||||||
|
divider.setVisibility(visibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.import_monster, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.menu_action_import_monster) {
|
||||||
|
Logger.logDebug("Menu Item Selected");
|
||||||
|
Monster monster = mViewModel.getMonster();
|
||||||
|
if (monster != null) {
|
||||||
|
monster.id = UUID.randomUUID();
|
||||||
|
MonsterCardsApplication application = (MonsterCardsApplication) getApplication();
|
||||||
|
MonsterRepository repository = application.getMonsterRepository();
|
||||||
|
repository.addMonster(monster).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableCompletableObserver() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
Snackbar.make(
|
||||||
|
mHolder.root,
|
||||||
|
getString(R.string.snackbar_monster_created, monster.name),
|
||||||
|
Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("Action", (_view) -> navigateToEditMonster(monster.id))
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
||||||
|
Logger.logError("Error creating monster", e);
|
||||||
|
Snackbar.make(mHolder.root, getString(R.string.snackbar_failed_to_create_monster), Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Logger.logWTF("monsterId cannot be null.");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateToEditMonster(UUID monsterId) {
|
||||||
|
Logger.logUnimplementedFeature(String.format("navigate to editing the monster %s", monsterId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ViewHolder {
|
||||||
|
final View root;
|
||||||
|
final TextView name;
|
||||||
|
final TextView meta;
|
||||||
|
final TextView armorClass;
|
||||||
|
final TextView hitPoints;
|
||||||
|
final TextView speed;
|
||||||
|
final TextView strength;
|
||||||
|
final TextView dexterity;
|
||||||
|
final TextView constitution;
|
||||||
|
final TextView intelligence;
|
||||||
|
final TextView wisdom;
|
||||||
|
final TextView charisma;
|
||||||
|
final TextView savingThrows;
|
||||||
|
final TextView skills;
|
||||||
|
final TextView damageVulnerabilities;
|
||||||
|
final TextView damageResistances;
|
||||||
|
final TextView damageImmunities;
|
||||||
|
final TextView conditionImmunities;
|
||||||
|
final TextView senses;
|
||||||
|
final TextView languages;
|
||||||
|
final TextView challenge;
|
||||||
|
final LinearLayout abilities;
|
||||||
|
final LinearLayout actions;
|
||||||
|
final TextView actions_label;
|
||||||
|
final ImageView actions_divider;
|
||||||
|
final LinearLayout reactions;
|
||||||
|
final TextView reactions_label;
|
||||||
|
final ImageView reactions_divider;
|
||||||
|
final LinearLayout legendaryActions;
|
||||||
|
final TextView legendaryActions_label;
|
||||||
|
final ImageView legendaryActions_divider;
|
||||||
|
final LinearLayout lairActions;
|
||||||
|
final TextView lairActions_label;
|
||||||
|
final ImageView lairActions_divider;
|
||||||
|
final LinearLayout regionalEffects;
|
||||||
|
final TextView regionalEffects_label;
|
||||||
|
final ImageView regionalEffects_divider;
|
||||||
|
|
||||||
|
ViewHolder(View root) {
|
||||||
|
this.root = root;
|
||||||
|
name = root.findViewById(R.id.name);
|
||||||
|
meta = root.findViewById(R.id.meta);
|
||||||
|
armorClass = root.findViewById(R.id.armorClass);
|
||||||
|
hitPoints = root.findViewById(R.id.hitPoints);
|
||||||
|
speed = root.findViewById(R.id.speed);
|
||||||
|
strength = root.findViewById(R.id.strength);
|
||||||
|
dexterity = root.findViewById(R.id.dexterity);
|
||||||
|
constitution = root.findViewById(R.id.constitution);
|
||||||
|
intelligence = root.findViewById(R.id.intelligence);
|
||||||
|
wisdom = root.findViewById(R.id.wisdom);
|
||||||
|
charisma = root.findViewById(R.id.charisma);
|
||||||
|
savingThrows = root.findViewById(R.id.savingThrows);
|
||||||
|
skills = root.findViewById(R.id.skills);
|
||||||
|
damageVulnerabilities = root.findViewById(R.id.damageVulnerabilities);
|
||||||
|
damageResistances = root.findViewById(R.id.damageResistances);
|
||||||
|
damageImmunities = root.findViewById(R.id.damageImmunities);
|
||||||
|
conditionImmunities = root.findViewById(R.id.conditionImmunities);
|
||||||
|
senses = root.findViewById(R.id.senses);
|
||||||
|
languages = root.findViewById(R.id.languages);
|
||||||
|
challenge = root.findViewById(R.id.challenge);
|
||||||
|
abilities = root.findViewById(R.id.abilities);
|
||||||
|
actions = root.findViewById(R.id.actions);
|
||||||
|
actions_divider = root.findViewById(R.id.actions_divider);
|
||||||
|
actions_label = root.findViewById(R.id.actions_label);
|
||||||
|
reactions = root.findViewById(R.id.reactions);
|
||||||
|
reactions_divider = root.findViewById(R.id.reactions_divider);
|
||||||
|
reactions_label = root.findViewById(R.id.reactions_label);
|
||||||
|
legendaryActions = root.findViewById(R.id.legendaryActions);
|
||||||
|
legendaryActions_divider = root.findViewById(R.id.legendaryActions_divider);
|
||||||
|
legendaryActions_label = root.findViewById(R.id.legendaryActions_label);
|
||||||
|
lairActions = root.findViewById(R.id.lairActions);
|
||||||
|
lairActions_divider = root.findViewById(R.id.lairActions_divider);
|
||||||
|
lairActions_label = root.findViewById(R.id.lairActions_label);
|
||||||
|
regionalEffects = root.findViewById(R.id.regionalEffects);
|
||||||
|
regionalEffects_divider = root.findViewById(R.id.regionalEffects_divider);
|
||||||
|
regionalEffects_label = root.findViewById(R.id.regionalEffects_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
package com.majinnaibu.monstercards.helpers;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.majinnaibu.monstercards.data.converters.ArmorTypeConverter;
|
||||||
|
import com.majinnaibu.monstercards.data.converters.ChallengeRatingConverter;
|
||||||
|
import com.majinnaibu.monstercards.data.enums.AbilityScore;
|
||||||
|
import com.majinnaibu.monstercards.data.enums.AdvantageType;
|
||||||
|
import com.majinnaibu.monstercards.data.enums.ProficiencyType;
|
||||||
|
import com.majinnaibu.monstercards.models.Language;
|
||||||
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
import com.majinnaibu.monstercards.models.Skill;
|
||||||
|
import com.majinnaibu.monstercards.models.Trait;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class MonsterImportHelper {
|
||||||
|
public static Monster fromJSON(String json) {
|
||||||
|
JsonParser parser = new JsonParser();
|
||||||
|
JsonObject rootDict = parser.parse(json).getAsJsonObject();
|
||||||
|
|
||||||
|
Monster monster = new Monster();
|
||||||
|
monster.name = Helpers.getString(rootDict, "name");
|
||||||
|
monster.size = Helpers.getString(rootDict, "size");
|
||||||
|
monster.type = Helpers.getString(rootDict, "type");
|
||||||
|
monster.subtype = Helpers.getString(rootDict, "tag");
|
||||||
|
monster.alignment = Helpers.getString(rootDict, "alignment");
|
||||||
|
monster.hitDice = Helpers.getInt(rootDict, "hitDice");
|
||||||
|
monster.armorType = ArmorTypeConverter.armorTypeFromStringValue(Helpers.getString(rootDict, "armorName"));
|
||||||
|
monster.shieldBonus = Helpers.getInt(rootDict, "shieldBonus");
|
||||||
|
monster.naturalArmorBonus = Helpers.getInt(rootDict, "natArmorBonus");
|
||||||
|
monster.otherArmorDescription = Helpers.getString(rootDict, "otherArmorDesc");
|
||||||
|
monster.walkSpeed = Helpers.getInt(rootDict, "speed");
|
||||||
|
monster.burrowSpeed = Helpers.getInt(rootDict, "burrowSpeed");
|
||||||
|
monster.climbSpeed = Helpers.getInt(rootDict, "climbSpeed");
|
||||||
|
monster.flySpeed = Helpers.getInt(rootDict, "flySpeed");
|
||||||
|
monster.canHover = Helpers.getBool(rootDict, "hover");
|
||||||
|
monster.swimSpeed = Helpers.getInt(rootDict, "swimSpeed");
|
||||||
|
monster.hasCustomHP = Helpers.getBool(rootDict, "customHP");
|
||||||
|
monster.hasCustomSpeed = Helpers.getBool(rootDict, "customSpeed");
|
||||||
|
monster.customHPDescription = Helpers.getString(rootDict, "hpText");
|
||||||
|
monster.customSpeedDescription = Helpers.getString(rootDict, "speedDesc");
|
||||||
|
monster.strengthScore = Helpers.getInt(rootDict, "strPoints");
|
||||||
|
monster.dexterityScore = Helpers.getInt(rootDict, "dexPoints");
|
||||||
|
monster.constitutionScore = Helpers.getInt(rootDict, "conPoints");
|
||||||
|
monster.intelligenceScore = Helpers.getInt(rootDict, "intPoints");
|
||||||
|
monster.wisdomScore = Helpers.getInt(rootDict, "wisPoints");
|
||||||
|
monster.charismaScore = Helpers.getInt(rootDict, "chaPoints");
|
||||||
|
Helpers.addSense(monster, rootDict, "blindsight");
|
||||||
|
// Helpers.getBool(rootDict, "blind");
|
||||||
|
Helpers.addSense(monster, rootDict, "darkvision");
|
||||||
|
Helpers.addSense(monster, rootDict, "tremorsense");
|
||||||
|
Helpers.addSense(monster, rootDict, "truesight");
|
||||||
|
monster.telepathyRange = Helpers.getInt(rootDict, "telepathy");
|
||||||
|
monster.challengeRating = ChallengeRatingConverter.challengeRatingFromStringValue(Helpers.getString(rootDict, "cr"));
|
||||||
|
monster.customChallengeRatingDescription = Helpers.getString(rootDict, "customCr");
|
||||||
|
monster.customProficiencyBonus = Helpers.getInt(rootDict, "customProf");
|
||||||
|
// Helpers.getBool(rootDict, "isLegendary");
|
||||||
|
// Helpers.getString(rootDict, "legendariesDescription");
|
||||||
|
// Helpers.getBool(rootDict, "isLair");
|
||||||
|
// Helpers.getString(rootDict, "lairDescription");
|
||||||
|
// Helpers.getString(rootDict, "lairDescriptionEnd");
|
||||||
|
// Helpers.getBool(rootDict, "isRegional");
|
||||||
|
// Helpers.getString(rootDict, "regionalDescription");
|
||||||
|
// Helpers.getString(rootDict, "regionalDescriptionEnd");
|
||||||
|
// properties: []
|
||||||
|
monster.abilities = Helpers.getListOfTraits(rootDict, "abilities");
|
||||||
|
monster.actions = Helpers.getListOfTraits(rootDict, "actions");
|
||||||
|
monster.reactions = Helpers.getListOfTraits(rootDict, "reactions");
|
||||||
|
monster.legendaryActions = Helpers.getListOfTraits(rootDict, "legendaries");
|
||||||
|
monster.lairActions = Helpers.getListOfTraits(rootDict, "lairs");
|
||||||
|
monster.regionalActions = Helpers.getListOfTraits(rootDict, "regionals");
|
||||||
|
Helpers.addSavingThrows(monster, rootDict);
|
||||||
|
// skills: []
|
||||||
|
monster.skills = Helpers.getSetOfSkills(rootDict);
|
||||||
|
// damagetypes: []
|
||||||
|
// specialdamage: []
|
||||||
|
monster.damageImmunities = Helpers.getSetOfDamageTypes(rootDict, "damageTypes", "i");
|
||||||
|
monster.damageImmunities.addAll(Helpers.getSetOfDamageTypes(rootDict, "specialdamage", "i"));
|
||||||
|
monster.damageResistances = Helpers.getSetOfDamageTypes(rootDict, "damageTypes", "r");
|
||||||
|
monster.damageResistances.addAll(Helpers.getSetOfDamageTypes(rootDict, "specialdamage", "r"));
|
||||||
|
monster.damageVulnerabilities = Helpers.getSetOfDamageTypes(rootDict, "damageTypes", "v");
|
||||||
|
monster.damageVulnerabilities.addAll(Helpers.getSetOfDamageTypes(rootDict, "specialdamage", "v"));
|
||||||
|
// conditions: []
|
||||||
|
monster.conditionImmunities = Helpers.getSetOfDamageTypes(rootDict, "conditions");
|
||||||
|
// languages: []
|
||||||
|
monster.languages = Helpers.getSetOfLanguages(rootDict, "languages");
|
||||||
|
// understandsBut: ""
|
||||||
|
monster.understandsButDescription = Helpers.getString(rootDict, "understandsBut");
|
||||||
|
// shortName: ""
|
||||||
|
// doubleColumns: true
|
||||||
|
// separationPoint: -1
|
||||||
|
// damage: []
|
||||||
|
// pluralName: ""
|
||||||
|
|
||||||
|
return monster;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Helpers {
|
||||||
|
public static String getString(JsonObject dict, String name) {
|
||||||
|
return getString(dict, name, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getString(@NotNull JsonObject dict, String name, String defaultValue) {
|
||||||
|
if (dict.has(name)) {
|
||||||
|
return dict.get(name).getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt(JsonObject dict, String name) {
|
||||||
|
return getInt(dict, name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt(@NotNull JsonObject dict, String name, int defaultValue) {
|
||||||
|
if (dict.has(name)) {
|
||||||
|
JsonElement element = dict.get(name);
|
||||||
|
if (element.isJsonPrimitive()) {
|
||||||
|
JsonPrimitive rawValue = element.getAsJsonPrimitive();//dict.getAsJsonPrimitive(name);
|
||||||
|
if (rawValue.isNumber()) {
|
||||||
|
return rawValue.getAsInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getBool(JsonObject dict, String name) {
|
||||||
|
return getBool(dict, name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getBool(@NotNull JsonObject dict, String name, boolean defaultValue) {
|
||||||
|
if (dict.has(name)) {
|
||||||
|
JsonElement element = dict.get(name);
|
||||||
|
if (element.isJsonPrimitive()) {
|
||||||
|
JsonPrimitive rawValue = element.getAsJsonPrimitive();
|
||||||
|
if (rawValue.isBoolean()) {
|
||||||
|
return rawValue.getAsBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static String formatDistance(String name, int distance) {
|
||||||
|
return String.format("%s %d ft.", name, distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addSense(Monster monster, JsonObject root, String name) {
|
||||||
|
int distance = Helpers.getInt(root, name);
|
||||||
|
if (distance > 0) {
|
||||||
|
monster.senses.add(Helpers.formatDistance(name, distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static List<Trait> getListOfTraits(@NotNull JsonObject dict, String name) {
|
||||||
|
ArrayList<Trait> traits = new ArrayList<>();
|
||||||
|
if (dict.has(name)) {
|
||||||
|
JsonElement arrayElement = dict.get(name);
|
||||||
|
if (arrayElement.isJsonArray()) {
|
||||||
|
JsonArray array = arrayElement.getAsJsonArray();
|
||||||
|
int size = array.size();
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
JsonElement jsonElement = array.get(index);
|
||||||
|
if (jsonElement.isJsonObject()) {
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
String traitName = Helpers.getString(jsonObject, "name");
|
||||||
|
String description = Helpers.getString(jsonObject, "description");
|
||||||
|
Trait trait = new Trait(traitName, description);
|
||||||
|
traits.add(trait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addSavingThrows(Monster monster, JsonObject root) {
|
||||||
|
if (root.has("sthrows")) {
|
||||||
|
JsonElement arrayElement = root.get("sthrows");
|
||||||
|
if (arrayElement.isJsonArray()) {
|
||||||
|
JsonArray array = arrayElement.getAsJsonArray();
|
||||||
|
int size = array.size();
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
JsonElement jsonElement = array.get(index);
|
||||||
|
if (jsonElement.isJsonObject()) {
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
String name = Helpers.getString(jsonObject, "name");
|
||||||
|
if ("str".equals(name)) {
|
||||||
|
monster.strengthSavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
} else if ("dex".equals(name)) {
|
||||||
|
monster.dexteritySavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
} else if ("con".equals(name)) {
|
||||||
|
monster.constitutionSavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
} else if ("int".equals(name)) {
|
||||||
|
monster.intelligenceSavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
} else if ("wis".equals(name)) {
|
||||||
|
monster.wisdomSavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
} else if ("cha".equals(name)) {
|
||||||
|
monster.charismaSavingThrowProficiency = ProficiencyType.PROFICIENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Skill> getSetOfSkills(JsonObject root) {
|
||||||
|
HashSet<Skill> skills = new HashSet<>();
|
||||||
|
if (root.has("skills")) {
|
||||||
|
JsonElement arrayElement = root.get("skills");
|
||||||
|
if (arrayElement.isJsonArray()) {
|
||||||
|
JsonArray array = arrayElement.getAsJsonArray();
|
||||||
|
int size = array.size();
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
JsonElement jsonElement = array.get(index);
|
||||||
|
if (jsonElement.isJsonObject()) {
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
String name = Helpers.getString(jsonObject, "name");
|
||||||
|
String stat = Helpers.getString(jsonObject, "stat");
|
||||||
|
String note = Helpers.getString(jsonObject, "note");
|
||||||
|
|
||||||
|
Skill skill = new Skill(name, AbilityScore.valueOfString(stat), AdvantageType.NONE, " (ex)".equals(note) ? ProficiencyType.EXPERTISE : ProficiencyType.PROFICIENT);
|
||||||
|
skills.add(skill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return skills;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getSetOfDamageTypes(JsonObject rootDict, String name) {
|
||||||
|
return getSetOfDamageTypes(rootDict, name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getSetOfDamageTypes(JsonObject root, String name, String type) {
|
||||||
|
HashSet<String> damageTypes = new HashSet<>();
|
||||||
|
if (root.has(name)) {
|
||||||
|
JsonElement arrayElement = root.get(name);
|
||||||
|
if (arrayElement.isJsonArray()) {
|
||||||
|
JsonArray array = arrayElement.getAsJsonArray();
|
||||||
|
int size = array.size();
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
JsonElement jsonElement = array.get(index);
|
||||||
|
if (jsonElement.isJsonObject()) {
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
String dtName = Helpers.getString(jsonObject, "name");
|
||||||
|
String dtType = Helpers.getString(jsonObject, "type");
|
||||||
|
if (type == null || type.equals(dtType)) {
|
||||||
|
damageTypes.add(dtName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return damageTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Language> getSetOfLanguages(JsonObject root, String name) {
|
||||||
|
HashSet<Language> languages = new HashSet<>();
|
||||||
|
if (root.has(name)) {
|
||||||
|
JsonElement arrayElement = root.get(name);
|
||||||
|
if (arrayElement.isJsonArray()) {
|
||||||
|
JsonArray array = arrayElement.getAsJsonArray();
|
||||||
|
int size = array.size();
|
||||||
|
for (int index = 0; index < size; index++) {
|
||||||
|
JsonElement jsonElement = array.get(index);
|
||||||
|
if (jsonElement.isJsonObject()) {
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
String languageName = Helpers.getString(jsonObject, "name");
|
||||||
|
boolean canSpeak = Helpers.getBool(jsonObject, "speaks");
|
||||||
|
Language language = new Language(languageName, canSpeak);
|
||||||
|
languages.add(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -180,6 +180,10 @@ public class MonsterDetailViewModel extends ViewModel {
|
|||||||
return mMonsterId;
|
return mMonsterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Monster getMonster() {
|
||||||
|
return mMonster;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMonster(Monster monster) {
|
public void setMonster(Monster monster) {
|
||||||
mMonster = monster;
|
mMonster = monster;
|
||||||
mAbilities.setValue(mMonster.getAbilityDescriptions());
|
mAbilities.setValue(mMonster.getAbilityDescriptions());
|
||||||
|
|||||||
5
app/src/main/res/drawable/ic_library_add_24.xml
Normal file
5
app/src/main/res/drawable/ic_library_add_24.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM19,11h-4v4h-2v-4L9,11L9,9h4L13,5h2v4h4v2z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/menu/import_monster.xml
Normal file
9
app/src/main/res/menu/import_monster.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_action_import_monster"
|
||||||
|
android:icon="@drawable/ic_library_add_24"
|
||||||
|
android:title="@string/action_import_monster"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
<string name="action_add_skill">Add Skill</string>
|
<string name="action_add_skill">Add Skill</string>
|
||||||
<string name="action_add_trait">Add Trait</string>
|
<string name="action_add_trait">Add Trait</string>
|
||||||
<string name="action_edit">Edit</string>
|
<string name="action_edit">Edit</string>
|
||||||
|
<string name="action_import_monster">Import Monster</string>
|
||||||
<string name="app_name">MonsterCards</string>
|
<string name="app_name">MonsterCards</string>
|
||||||
<string name="charisma_abbreviation">CHA</string>
|
<string name="charisma_abbreviation">CHA</string>
|
||||||
<string name="constitution_abbreviation">CON</string>
|
<string name="constitution_abbreviation">CON</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user