Adds option to import from Open5e.com
This commit is contained in:
@@ -19,6 +19,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.majinnaibu.monstercards"
|
applicationId "com.majinnaibu.monstercards"
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
|
//noinspection OldTargetApi
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|||||||
@@ -21,16 +21,20 @@ import androidx.navigation.fragment.NavHostFragment;
|
|||||||
import androidx.navigation.ui.AppBarConfiguration;
|
import androidx.navigation.ui.AppBarConfiguration;
|
||||||
import androidx.navigation.ui.NavigationUI;
|
import androidx.navigation.ui.NavigationUI;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar;
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.majinnaibu.monstercards.helpers.MonsterImportHelper;
|
import com.majinnaibu.monstercards.helpers.MonsterImportHelper;
|
||||||
import com.majinnaibu.monstercards.helpers.StringHelper;
|
import com.majinnaibu.monstercards.helpers.StringHelper;
|
||||||
import com.majinnaibu.monstercards.importers.BinderImporter;
|
import com.majinnaibu.monstercards.importers.BinderImporter;
|
||||||
import com.majinnaibu.monstercards.importers.DnDBeyondImporter;
|
import com.majinnaibu.monstercards.importers.DnDBeyondImporter;
|
||||||
|
import com.majinnaibu.monstercards.importers.Open5eApiWrapper;
|
||||||
import com.majinnaibu.monstercards.init.AppCenterInitializer;
|
import com.majinnaibu.monstercards.init.AppCenterInitializer;
|
||||||
import com.majinnaibu.monstercards.models.BinderExport;
|
import com.majinnaibu.monstercards.models.BinderExport;
|
||||||
import com.majinnaibu.monstercards.models.Monster;
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
import com.majinnaibu.monstercards.utils.Logger;
|
import com.majinnaibu.monstercards.utils.Logger;
|
||||||
|
import com.majinnaibu.monstercards.utils.SnackbarHelper;
|
||||||
import com.majinnaibu.monstercards.utils.ToastHelper;
|
import com.majinnaibu.monstercards.utils.ToastHelper;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -44,10 +48,13 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private Disposable mOpen5eImportDisposable;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == android.R.id.home) {
|
if (item.getItemId() == android.R.id.home) {
|
||||||
@@ -67,7 +74,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
AppCenterInitializer.init(getApplication());
|
AppCenterInitializer.init(getApplication());
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
com.google.android.material.appbar.MaterialToolbar toolbar = findViewById(R.id.toolbar);
|
MaterialToolbar toolbar = findViewById(R.id.toolbar);
|
||||||
if (toolbar != null) {
|
if (toolbar != null) {
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
}
|
}
|
||||||
@@ -202,6 +209,55 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void importAllFromOpen5e() {
|
||||||
|
if (mOpen5eImportDisposable != null && !mOpen5eImportDisposable.isDisposed()) {
|
||||||
|
ToastHelper.showShort(this, "Open5e import already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
View rootView = findViewById(android.R.id.content);
|
||||||
|
Snackbar snackbar = SnackbarHelper.makeIndefinite(rootView, R.string.snackbar_importing_open5e);
|
||||||
|
snackbar.setAction(R.string.action_cancel, v -> {
|
||||||
|
if (mOpen5eImportDisposable != null && !mOpen5eImportDisposable.isDisposed()) {
|
||||||
|
mOpen5eImportDisposable.dispose();
|
||||||
|
SnackbarHelper.showLong(rootView, R.string.snackbar_open5e_import_cancelled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
snackbar.show();
|
||||||
|
|
||||||
|
mOpen5eImportDisposable = Single.fromCallable(() -> {
|
||||||
|
int totalImported = 0;
|
||||||
|
String nextUrl = null;
|
||||||
|
do {
|
||||||
|
if (mOpen5eImportDisposable != null && mOpen5eImportDisposable.isDisposed()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Open5eApiWrapper.Open5ePageResult page = Open5eApiWrapper.fetchPage(nextUrl);
|
||||||
|
for (Monster monster : page.monsters) {
|
||||||
|
if (mOpen5eImportDisposable != null && mOpen5eImportDisposable.isDisposed()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
((MonsterCardsApplication) getApplication()).getMonsterRepository()
|
||||||
|
.saveMonster(monster)
|
||||||
|
.blockingAwait();
|
||||||
|
totalImported++;
|
||||||
|
}
|
||||||
|
nextUrl = page.nextUrl;
|
||||||
|
} while (nextUrl != null && !nextUrl.isEmpty());
|
||||||
|
return totalImported;
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(result -> {
|
||||||
|
snackbar.dismiss();
|
||||||
|
SnackbarHelper.showLong(rootView, R.string.snackbar_open5e_import_complete);
|
||||||
|
}, throwable -> {
|
||||||
|
snackbar.dismiss();
|
||||||
|
Logger.logError("Failed to import all Open5e monsters", throwable);
|
||||||
|
SnackbarHelper.showLong(rootView, R.string.snackbar_open5e_import_failed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void importMultipleFilesFromUris(@NonNull List<Uri> uris) {
|
public void importMultipleFilesFromUris(@NonNull List<Uri> uris) {
|
||||||
if (uris.isEmpty()) return;
|
if (uris.isEmpty()) return;
|
||||||
if (uris.size() == 1) {
|
if (uris.size() == 1) {
|
||||||
@@ -370,4 +426,12 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (mOpen5eImportDisposable != null && !mOpen5eImportDisposable.isDisposed()) {
|
||||||
|
mOpen5eImportDisposable.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.majinnaibu.monstercards.importers;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.majinnaibu.monstercards.models.Monster;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.majinnaibu.monstercards.utils.Logger;
|
||||||
|
|
||||||
|
public class Open5eApiWrapper {
|
||||||
|
private static final String BASE_URL = "https://api.open5e.com/v2/creatures/";
|
||||||
|
|
||||||
|
public static class Open5ePageResult {
|
||||||
|
public List<Monster> monsters = new ArrayList<>();
|
||||||
|
@Nullable public String nextUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static Open5ePageResult fetchPage(@Nullable String urlStr) throws Exception {
|
||||||
|
if (urlStr == null || urlStr.isEmpty()) {
|
||||||
|
urlStr = BASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = new URL(urlStr);
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setRequestMethod("GET");
|
||||||
|
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Android; Mobile)");
|
||||||
|
conn.setRequestProperty("Accept", "application/json");
|
||||||
|
conn.setConnectTimeout(15000);
|
||||||
|
conn.setReadTimeout(15000);
|
||||||
|
|
||||||
|
int code = conn.getResponseCode();
|
||||||
|
if (code != 200) {
|
||||||
|
throw new IllegalStateException("Open5e API returned HTTP " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try (InputStream in = conn.getInputStream();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String jsonPayload = sb.toString();
|
||||||
|
JsonObject root = JsonParser.parseString(jsonPayload).getAsJsonObject();
|
||||||
|
|
||||||
|
Open5ePageResult result = new Open5ePageResult();
|
||||||
|
if (root.has("next") && !root.get("next").isJsonNull()) {
|
||||||
|
result.nextUrl = root.get("next").getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.has("results") && root.get("results").isJsonArray()) {
|
||||||
|
JsonArray results = root.getAsJsonArray("results");
|
||||||
|
Open5eImporter importer = new Open5eImporter();
|
||||||
|
for (int i = 0; i < results.size(); i++) {
|
||||||
|
JsonElement el = results.get(i);
|
||||||
|
if (el.isJsonObject()) {
|
||||||
|
try {
|
||||||
|
Monster monster = importer.parse(el.toString());
|
||||||
|
result.monsters.add(monster);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.logError("Failed to parse Open5e monster", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,7 +82,16 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
parseArmorClass(props, monster);
|
parseArmorClass(props, monster);
|
||||||
parseHitPoints(props, monster);
|
parseHitPoints(props, monster);
|
||||||
|
|
||||||
|
if (props.has("speed") && props.get("speed").isJsonObject()) {
|
||||||
|
JsonObject speedObj = props.getAsJsonObject("speed");
|
||||||
|
monster.walkSpeed = getInt(speedObj, "walk", 30);
|
||||||
|
monster.flySpeed = getInt(speedObj, "fly", 0);
|
||||||
|
monster.swimSpeed = getInt(speedObj, "swim", 0);
|
||||||
|
monster.burrowSpeed = getInt(speedObj, "burrow", 0);
|
||||||
|
monster.climbSpeed = getInt(speedObj, "climb", 0);
|
||||||
|
} else {
|
||||||
monster.walkSpeed = getInt(props, "speed", 30);
|
monster.walkSpeed = getInt(props, "speed", 30);
|
||||||
|
}
|
||||||
if (props.has("speed_desc")) {
|
if (props.has("speed_desc")) {
|
||||||
String speedDesc = getString(props, "speed_desc");
|
String speedDesc = getString(props, "speed_desc");
|
||||||
if (!speedDesc.isEmpty()) {
|
if (!speedDesc.isEmpty()) {
|
||||||
@@ -159,6 +168,10 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
crStr = props.get("challenge_rating").getAsString();
|
crStr = props.get("challenge_rating").getAsString();
|
||||||
}
|
}
|
||||||
if (!crStr.isEmpty()) {
|
if (!crStr.isEmpty()) {
|
||||||
|
crStr = crStr.replace(".0", "");
|
||||||
|
if (crStr.equals("0.125")) crStr = "1/8";
|
||||||
|
else if (crStr.equals("0.25")) crStr = "1/4";
|
||||||
|
else if (crStr.equals("0.5")) crStr = "1/2";
|
||||||
monster.challengeRating = ChallengeRating.valueOfString(crStr);
|
monster.challengeRating = ChallengeRating.valueOfString(crStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +227,12 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
private void parseSavingThrows(JsonObject props, Monster monster) {
|
private void parseSavingThrows(JsonObject props, Monster monster) {
|
||||||
if (!props.has("saving_throws")) return;
|
if (!props.has("saving_throws")) return;
|
||||||
JsonElement savesEl = props.get("saving_throws");
|
JsonElement savesEl = props.get("saving_throws");
|
||||||
if (savesEl.isJsonArray()) {
|
if (savesEl.isJsonObject()) {
|
||||||
|
JsonObject savesObj = savesEl.getAsJsonObject();
|
||||||
|
for (String key : savesObj.keySet()) {
|
||||||
|
applySaveProficiency(monster, key.toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
} else if (savesEl.isJsonArray()) {
|
||||||
JsonArray arr = savesEl.getAsJsonArray();
|
JsonArray arr = savesEl.getAsJsonArray();
|
||||||
for (int i = 0; i < arr.size(); i++) {
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
String saveStr = arr.get(i).getAsString().toLowerCase(Locale.ROOT);
|
String saveStr = arr.get(i).getAsString().toLowerCase(Locale.ROOT);
|
||||||
@@ -233,7 +251,13 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseSkills(JsonObject props, Monster monster) {
|
private void parseSkills(JsonObject props, Monster monster) {
|
||||||
if (!props.has("skills")) return;
|
if (props.has("skill_bonuses") && props.get("skill_bonuses").isJsonObject()) {
|
||||||
|
JsonObject skillsObj = props.getAsJsonObject("skill_bonuses");
|
||||||
|
for (String key : skillsObj.keySet()) {
|
||||||
|
AbilityScore stat = getAbilityForSkill(key);
|
||||||
|
monster.skills.add(new Skill(capitalize(key.replace("_", " ")), stat, AdvantageType.NONE, ProficiencyType.PROFICIENT));
|
||||||
|
}
|
||||||
|
} else if (props.has("skills")) {
|
||||||
JsonElement skillsEl = props.get("skills");
|
JsonElement skillsEl = props.get("skills");
|
||||||
if (skillsEl.isJsonArray()) {
|
if (skillsEl.isJsonArray()) {
|
||||||
JsonArray arr = skillsEl.getAsJsonArray();
|
JsonArray arr = skillsEl.getAsJsonArray();
|
||||||
@@ -247,6 +271,7 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private AbilityScore getAbilityForSkill(String skillName) {
|
private AbilityScore getAbilityForSkill(String skillName) {
|
||||||
String lower = skillName.toLowerCase(Locale.ROOT);
|
String lower = skillName.toLowerCase(Locale.ROOT);
|
||||||
@@ -274,7 +299,26 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
private void parseLanguages(JsonObject props, Monster monster) {
|
private void parseLanguages(JsonObject props, Monster monster) {
|
||||||
if (props.has("languages")) {
|
if (props.has("languages")) {
|
||||||
JsonElement langEl = props.get("languages");
|
JsonElement langEl = props.get("languages");
|
||||||
if (langEl.isJsonArray()) {
|
if (langEl.isJsonObject()) {
|
||||||
|
if (langEl.getAsJsonObject().has("data") && langEl.getAsJsonObject().get("data").isJsonArray()) {
|
||||||
|
JsonArray arr = langEl.getAsJsonObject().getAsJsonArray("data");
|
||||||
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
|
if (arr.get(i).isJsonObject()) {
|
||||||
|
String langName = getString(arr.get(i).getAsJsonObject(), "name");
|
||||||
|
if (!langName.isEmpty()) {
|
||||||
|
monster.languages.add(new Language(langName, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (langEl.getAsJsonObject().has("as_string")) {
|
||||||
|
String langStr = langEl.getAsJsonObject().get("as_string").getAsString();
|
||||||
|
for (String l : langStr.split(",")) {
|
||||||
|
if (!l.trim().isEmpty()) {
|
||||||
|
monster.languages.add(new Language(l.trim(), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (langEl.isJsonArray()) {
|
||||||
JsonArray arr = langEl.getAsJsonArray();
|
JsonArray arr = langEl.getAsJsonArray();
|
||||||
for (int i = 0; i < arr.size(); i++) {
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
String langName = arr.get(i).getAsString();
|
String langName = arr.get(i).getAsString();
|
||||||
@@ -294,11 +338,19 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseDamageAndConditions(JsonObject props, Monster monster) {
|
private void parseDamageAndConditions(JsonObject props, Monster monster) {
|
||||||
|
if (props.has("resistances_and_immunities") && props.get("resistances_and_immunities").isJsonObject()) {
|
||||||
|
JsonObject resObj = props.getAsJsonObject("resistances_and_immunities");
|
||||||
|
parseStringSet(resObj, "damage_immunities", monster.damageImmunities);
|
||||||
|
parseStringSet(resObj, "damage_resistances", monster.damageResistances);
|
||||||
|
parseStringSet(resObj, "damage_vulnerabilities", monster.damageVulnerabilities);
|
||||||
|
parseStringSet(resObj, "condition_immunities", monster.conditionImmunities);
|
||||||
|
} else {
|
||||||
parseStringSet(props, "damage_immunities", monster.damageImmunities);
|
parseStringSet(props, "damage_immunities", monster.damageImmunities);
|
||||||
parseStringSet(props, "damage_resistances", monster.damageResistances);
|
parseStringSet(props, "damage_resistances", monster.damageResistances);
|
||||||
parseStringSet(props, "damage_vulnerabilities", monster.damageVulnerabilities);
|
parseStringSet(props, "damage_vulnerabilities", monster.damageVulnerabilities);
|
||||||
parseStringSet(props, "condition_immunities", monster.conditionImmunities);
|
parseStringSet(props, "condition_immunities", monster.conditionImmunities);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void parseStringSet(JsonObject props, String key, java.util.Set<String> set) {
|
private void parseStringSet(JsonObject props, String key, java.util.Set<String> set) {
|
||||||
if (!props.has(key)) return;
|
if (!props.has(key)) return;
|
||||||
@@ -306,8 +358,14 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
if (el.isJsonArray()) {
|
if (el.isJsonArray()) {
|
||||||
JsonArray arr = el.getAsJsonArray();
|
JsonArray arr = el.getAsJsonArray();
|
||||||
for (int i = 0; i < arr.size(); i++) {
|
for (int i = 0; i < arr.size(); i++) {
|
||||||
String val = arr.get(i).getAsString();
|
JsonElement item = arr.get(i);
|
||||||
|
if (item.isJsonObject() && item.getAsJsonObject().has("name")) {
|
||||||
|
String val = item.getAsJsonObject().get("name").getAsString();
|
||||||
if (!val.isEmpty()) set.add(capitalize(val));
|
if (!val.isEmpty()) set.add(capitalize(val));
|
||||||
|
} else if (item.isJsonPrimitive()) {
|
||||||
|
String val = item.getAsString();
|
||||||
|
if (!val.isEmpty()) set.add(capitalize(val));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (el.isJsonPrimitive()) {
|
} else if (el.isJsonPrimitive()) {
|
||||||
for (String val : el.getAsString().split(",")) {
|
for (String val : el.getAsString().split(",")) {
|
||||||
@@ -357,7 +415,12 @@ public class Open5eImporter implements EntityImporter<Monster> {
|
|||||||
|
|
||||||
private static String getString(JsonObject obj, String key, String defaultVal) {
|
private static String getString(JsonObject obj, String key, String defaultVal) {
|
||||||
if (obj.has(key) && !obj.get(key).isJsonNull()) {
|
if (obj.has(key) && !obj.get(key).isJsonNull()) {
|
||||||
return obj.get(key).getAsString();
|
JsonElement el = obj.get(key);
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
return el.getAsString();
|
||||||
|
} else if (el.isJsonObject() && el.getAsJsonObject().has("name")) {
|
||||||
|
return el.getAsJsonObject().get("name").getAsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,13 @@ public class MCFragment extends Fragment {
|
|||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
view.findViewById(R.id.button_import_open5e).setOnClickListener(v -> {
|
||||||
|
if (getActivity() instanceof MainActivity) {
|
||||||
|
((MainActivity) getActivity()).importAllFromOpen5e();
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ public class SnackbarHelper {
|
|||||||
return make(view, message, DEFAULT_LONG_DURATION);
|
return make(view, message, DEFAULT_LONG_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Snackbar makeIndefinite(@NonNull View view, @StringRes int resId) {
|
||||||
|
return Snackbar.make(view, resId, Snackbar.LENGTH_INDEFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Snackbar makeIndefinite(@NonNull View view, @NonNull CharSequence message) {
|
||||||
|
return Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
public static Snackbar make(@NonNull View view, @NonNull CharSequence message, int durationMs) {
|
public static Snackbar make(@NonNull View view, @NonNull CharSequence message, int durationMs) {
|
||||||
return Snackbar.make(view, message, durationMs);
|
return Snackbar.make(view, message, durationMs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,11 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/action_import_monster_from_file" />
|
android:text="@string/action_import_monster_from_file" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_import_open5e"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/action_import_from_open5e" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -163,6 +163,7 @@
|
|||||||
<string name="snackbar_dashboard_cleared">Dashboard cleared</string>
|
<string name="snackbar_dashboard_cleared">Dashboard cleared</string>
|
||||||
<string name="no_monsters_available">No monsters available in library. Create one first!</string>
|
<string name="no_monsters_available">No monsters available in library. Create one first!</string>
|
||||||
<string name="action_import_from_url">Import URL</string>
|
<string name="action_import_from_url">Import URL</string>
|
||||||
|
<string name="action_import_from_open5e">From Open5e.com</string>
|
||||||
<string name="dialog_import_title">Import</string>
|
<string name="dialog_import_title">Import</string>
|
||||||
<string name="dialog_import_message">Enter or paste a URL, ID, or data payload:</string>
|
<string name="dialog_import_message">Enter or paste a URL, ID, or data payload:</string>
|
||||||
<string name="dialog_import_url_hint">e.g. D&D Beyond URL or Open5e JSON</string>
|
<string name="dialog_import_url_hint">e.g. D&D Beyond URL or Open5e JSON</string>
|
||||||
@@ -225,4 +226,9 @@
|
|||||||
<string name="dialog_remove_all_from_collection_title">Remove All Monsters?</string>
|
<string name="dialog_remove_all_from_collection_title">Remove All Monsters?</string>
|
||||||
<string name="dialog_remove_all_from_collection_message">Are you sure you want to remove all monsters from this collection?</string>
|
<string name="dialog_remove_all_from_collection_message">Are you sure you want to remove all monsters from this collection?</string>
|
||||||
<string name="snackbar_collection_cleared">Removed all monsters from collection</string>
|
<string name="snackbar_collection_cleared">Removed all monsters from collection</string>
|
||||||
|
<string name="snackbar_importing_open5e">Importing monsters from Open5e…</string>
|
||||||
|
<string name="action_cancel">Cancel</string>
|
||||||
|
<string name="snackbar_open5e_import_complete">Finished importing Open5e monsters.</string>
|
||||||
|
<string name="snackbar_open5e_import_cancelled">Cancelled Open5e import.</string>
|
||||||
|
<string name="snackbar_open5e_import_failed">Failed to import from Open5e.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user