diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml
index 57a954b..eaa2137 100644
--- a/Android/app/src/main/AndroidManifest.xml
+++ b/Android/app/src/main/AndroidManifest.xml
@@ -5,19 +5,22 @@
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleTask">
+
+
+
+
+
@@ -45,15 +48,8 @@
android:mimeType="text/plain"
android:scheme="file" />
-
-
-
-
-
-
+
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("%s %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("%s %s", title, text));
- }
- root.setText(formatted);
- }
-
- private void setupTraitList(@NonNull LinearLayout root, @NonNull List traits) {
- setupTraitList(root, traits, null, null);
- }
-
- private void setupTraitList(@NonNull LinearLayout root, @NonNull List 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);
- }
- }
-}
diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java b/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java
index c55893a..88ca743 100644
--- a/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java
+++ b/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java
@@ -1,18 +1,29 @@
package com.majinnaibu.monstercards;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
+import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.majinnaibu.monstercards.helpers.StringHelper;
import com.majinnaibu.monstercards.init.AppCenterInitializer;
import com.majinnaibu.monstercards.init.FlipperInitializer;
+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.Objects;
public class MainActivity extends AppCompatActivity {
@@ -48,5 +59,59 @@ public class MainActivity extends AppCompatActivity {
});
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
+ onNewIntent(getIntent());
}
-}
\ No newline at end of file
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ String json = readMonsterJSONFromIntent(intent);
+ if (!StringHelper.isNullOrEmpty(json)) {
+ NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
+ NavController navController = navHostFragment.getNavController();
+ NavDirections action = MobileNavigationDirections.actionGlobalMonsterImportFragment(json);
+ navController.navigate(action);
+ }
+ }
+
+ private String readMonsterJSONFromIntent(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 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();
+ }
+}
diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterDetailViewModel.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterDetailViewModel.java
index a0b0bed..a91a5f9 100644
--- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterDetailViewModel.java
+++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterDetailViewModel.java
@@ -180,10 +180,6 @@ public class MonsterDetailViewModel extends ViewModel {
return mMonsterId;
}
- public Monster getMonster() {
- return mMonster;
- }
-
public void setMonster(Monster monster) {
mMonster = monster;
mAbilities.setValue(mMonster.getAbilityDescriptions());
diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportFragment.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportFragment.java
index a4912f7..edb973c 100644
--- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportFragment.java
+++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportFragment.java
@@ -88,7 +88,7 @@ public class MonsterImportFragment extends MCFragment {
return root;
}
- private void setupLabeledTextView(@NonNull TextView view, String text, int titleId) {
+ private void setupLabeledTextView(TextView view, String text, int titleId) {
String title = getString(titleId);
String fullText = String.format("%s %s", title, text);
view.setText(Html.fromHtml(fullText));
@@ -184,7 +184,7 @@ public class MonsterImportFragment extends MCFragment {
return super.onOptionsItemSelected(item);
}
- private void navigateToEditMonster(@NonNull UUID monsterId) {
+ private void navigateToEditMonster(UUID monsterId) {
NavController navController = Navigation.findNavController(requireView());
NavDirections action;
action = MonsterImportFragmentDirections.actionMonsterImportFragmentToNavigationLibrary();
@@ -234,7 +234,7 @@ public class MonsterImportFragment extends MCFragment {
final TextView regionalEffects_label;
final ImageView regionalEffects_divider;
- ViewHolder(@NonNull View root) {
+ ViewHolder(View root) {
this.root = root;
name = root.findViewById(R.id.name);
meta = root.findViewById(R.id.meta);
diff --git a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportViewModel.java b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportViewModel.java
index ae13d15..31e1a80 100644
--- a/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportViewModel.java
+++ b/Android/app/src/main/java/com/majinnaibu/monstercards/ui/monster/MonsterImportViewModel.java
@@ -1,6 +1,5 @@
package com.majinnaibu.monstercards.ui.monster;
-import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
@@ -184,7 +183,7 @@ public class MonsterImportViewModel extends ViewModel {
return mMonster;
}
- public void setMonster(@NonNull Monster monster) {
+ public void setMonster(Monster monster) {
mMonster = monster;
mAbilities.setValue(mMonster.getAbilityDescriptions());
mActions.setValue(mMonster.getActionDescriptions());
diff --git a/Android/app/src/main/res/navigation/mobile_navigation.xml b/Android/app/src/main/res/navigation/mobile_navigation.xml
index 893f689..71cb792 100644
--- a/Android/app/src/main/res/navigation/mobile_navigation.xml
+++ b/Android/app/src/main/res/navigation/mobile_navigation.xml
@@ -228,4 +228,21 @@
app:argType="string" />
+
+
+
+
+
diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml
index e0e919e..dc5d036 100644
--- a/Android/app/src/main/res/values/strings.xml
+++ b/Android/app/src/main/res/values/strings.xml
@@ -121,6 +121,7 @@
Senses
Skill
Skills
+ Import Monster
Library
Search
WIS