Skip to content

Commit

Permalink
Merge branch 'devel' into stable
Browse files Browse the repository at this point in the history
Highlights:
- Added new source book (new spells from Elemental Evil)
- Added option to import new source packages
- Added option to reset database to initial defaults
- Added option to sort spells in ascending/descending order
- Added option to exclude selected components in filter

Fixes:
- Fixed "Thaumaturgy" spell description
- Fixed dropdown filters excluding results
  • Loading branch information
dseguin committed Sep 4, 2020
2 parents f4e8977 + f0e3705 commit bfac117
Show file tree
Hide file tree
Showing 23 changed files with 464 additions and 131 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
/.old.git
/app/release
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
*.swp
58 changes: 56 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,67 @@
D&DB
====

Android app and database for searching and displaying data for Dungeons and Dragons (5e).
![Spell Pages](images/screen_group1.png)

Pre-built binaries available in the [releases tab](https://github.com/dseguin/dndb/releases/tag/v0.1.0), or download the [latest release directly](https://github.com/dseguin/dndb/releases/download/v0.1.0/dndb-androidapi19-0.1.0.apk).
Android app and database for searching and displaying data for Dungeons & Dragons (5e).

Pre-built binaries available in the [releases tab](https://github.com/dseguin/dndb/releases/latest), or download the [latest release directly](https://github.com/dseguin/dndb/releases/download/v0.1.1/dndb-androidapi19-0.1.1.apk).

Spells
------

![Spell Filtering](images/screen_group2.png)

Spells can be sorted and filtered, and selecting one shows further details. The spells included are only the ones available under the Open Game License.

More spells can be imported from `Settings->Import Source`. These source packages follow a specific format.

Source Package Format
---------------------

[An example source package can be found here](app/src/main/res/raw/srd.zip).

In order to be imported, a source package must be a zip archive with the following structure:
```
Source.zip
↳ Manifest.xml
↳ [other files]
```

The "other files" are specified in `Manifest.xml`. The manifest file sits at the top of the folder structure and contains a list of all other files to be processed by the app.
Here's an example `Manifest.xml`:
```xml
<?xml version="1.0" encoding="utf-8"?>
<Source>
<ShortName>SRD</ShortName>
<Name>Basic Rules</Name>
<Spells>
<AssetFile>spells_SRD_A-G.sql</AssetFile>
<AssetFile>spells_SRD_H-P.sql</AssetFile>
<AssetFile>spells_SRD_R-Z.sql</AssetFile>
<AssetFile>spells_SRD_extra.sql</AssetFile>
</Spells>
</Source>
```

At the moment, all specified asset files are processed as SQLite statements.

D&DB's Database Design
----------------------

D&DB expects SQLite DML statements for adding or updating the internal database. The current database definition [can be found here](app/src/main/res/raw/spells_ddl.sql). There are no enforced foreign keys to maintain compatibility with older versions of SQLite. Just assume that any table named `<table1>_<table2>` is a join table. An example SELECT query can be seen in the comments at the top of the [previously mentioned data definition file](app/src/main/res/raw/spells_ddl.sql).

(entity relationship diagram to be added in the future for reference)

License
-------

Source code specific to D&DB is released under the MIT License.

Data for the "Basic Rules" and other included material is provided by [Wizards of the Coast](https://dnd.wizards.com) under the [Open Game License](https://media.wizards.com/2016/downloads/DND/SRD-OGL_V5.1.pdf).

The "dragon ampersand" is part of the Dungeons & Dragons logo and [is available as a press asset from Wizards of the Coast](https://dnd.wizards.com/pressassets).

This software was developed using Google's Android Studio, which is based on IntelliJ IDEA Community Edition, and released under the Apache v2 License. The software makes use of the Android SDK, which is subject to [the Android SDK terms and conditions](https://developer.android.com/studio/terms).

Other than the base libraries provided by the Android SDK and JDK, all D&DB code is original and provided under the MIT License.
6 changes: 4 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@ android {
defaultConfig {
applicationId 'ca.printf.dndb'
minSdkVersion 14
targetSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
versionName '0.1.0'

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionNameSuffix '0.1.0'
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
versionNameSuffix '0.1.0'
}
}
}
Expand Down
9 changes: 6 additions & 3 deletions app/src/main/java/ca/printf/dndb/entity/Spell.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import ca.printf.dndb.data.DndbSQLManager;

public class Spell implements Serializable {
Expand Down Expand Up @@ -97,7 +99,8 @@ public class Spell implements Serializable {
private ArrayList<String> atk_types = new ArrayList<>();
private ArrayList<String> dmg_types = new ArrayList<>();
private ArrayList<String> conditions = new ArrayList<>();
private ArrayList<String> sources = new ArrayList<>();
// <SHORTNAME, FULLNAME> (shortname for list/selection, fullname for details)
private Map<String, String> sources = new HashMap<>();
private ArrayList<String> classes = new ArrayList<>();

public Spell(long id) {this.id = id;}
Expand Down Expand Up @@ -125,7 +128,7 @@ public class Spell implements Serializable {
public ArrayList<String> getAtkTypes() {return atk_types;}
public ArrayList<String> getDmgTypes() {return dmg_types;}
public ArrayList<String> getConditions() {return conditions;}
public ArrayList<String> getSources() {return sources;}
public Map<String, String> getSources() {return sources;}
public ArrayList<String> getClasses() {return classes;}

public void setId(long id) {this.id = id;}
Expand All @@ -150,7 +153,7 @@ public class Spell implements Serializable {
public void setAtkTypes(ArrayList<String> atk_types) {this.atk_types = atk_types;}
public void setDmgTypes(ArrayList<String> dmg_types) {this.dmg_types = dmg_types;}
public void setConditions(ArrayList<String> conditions) {this.conditions = conditions;}
public void setSources(ArrayList<String> sources) {this.sources = sources;}
public void setSources(Map<String, String> sources) {this.sources = sources;}
public void setClasses(ArrayList<String> classes) {this.classes = classes;}

private static final String JOIN_SPELL_TABLE(final String TABLE) {
Expand Down
29 changes: 25 additions & 4 deletions app/src/main/java/ca/printf/dndb/list/SpellSortComparator.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,33 @@ public class SpellSortComparator implements Comparator<Spell> {
public static final String SORT_RANGE = "Range";
public static final String SORT_MATCOST = "Material Cost";
private String sortCondition;
private boolean reverseOrder;

public SpellSortComparator() {this(SORT_NAME);}
public SpellSortComparator(String sortCondition) {this.sortCondition = sortCondition;}
public void setSortCondition(String sortCondition) {this.sortCondition = sortCondition;}
public SpellSortComparator() {
this(SORT_NAME);
}

public SpellSortComparator(String sortCondition) {
this.sortCondition = sortCondition;
this.reverseOrder = false;
}

public SpellSortComparator(String sortCondition, boolean reverseOrder) {
this.sortCondition = sortCondition;
this.reverseOrder = reverseOrder;
}

public void setSortCondition(String sortCondition) {
this.sortCondition = sortCondition;
}

public void setReverseOrder(boolean sortDescending) {
this.reverseOrder = sortDescending;
}

public int compare(Spell s1, Spell s2) {
public int compare(Spell o1, Spell o2) {
Spell s1 = reverseOrder ? o2 : o1;
Spell s2 = reverseOrder ? o1 : o2;
switch(sortCondition) {
case SORT_LEVEL :
return s1.getLevel() - s2.getLevel();
Expand Down
13 changes: 7 additions & 6 deletions app/src/main/java/ca/printf/dndb/view/RootActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

import ca.printf.dndb.R;
import com.google.android.material.navigation.NavigationView;

public class RootActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static final String FRAG_DEFAULT = "FRAG_DEFAULT";
private static final String FRAG_SPELLS_LIST = "FRAG_SPELLS_LIST";
private Fragment content_frag;
private DrawerLayout drw;

Expand Down Expand Up @@ -65,7 +63,10 @@ public boolean onOptionsItemSelected(MenuItem item) {
private boolean menuAction(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_spells :
openFragSpells();
openContentFragment(new SpellsListFragment());
break;
case R.id.menu_settings :
openContentFragment(new SettingsFragment());
break;
case R.id.menu_about :
createAboutDialog().show();
Expand All @@ -86,12 +87,12 @@ private AlertDialog createAboutDialog() {
return about.create();
}

private void openFragSpells() {
private void openContentFragment(Fragment contentFrag) {
((FrameLayout)findViewById(R.id.content_frame)).removeAllViewsInLayout();
content_frag = new SpellsListFragment();
content_frag = contentFrag;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, content_frag, FRAG_SPELLS_LIST)
.replace(R.id.content_frame, content_frag)
.addToBackStack(null)
.commit();
if(drw.isDrawerOpen(GravityCompat.START))
Expand Down
98 changes: 98 additions & 0 deletions app/src/main/java/ca/printf/dndb/view/SettingsFragment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package ca.printf.dndb.view;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Supplier;
import androidx.fragment.app.Fragment;
import java.io.FileNotFoundException;
import java.io.IOException;
import ca.printf.dndb.R;
import ca.printf.dndb.data.DndbSQLManager;

public class SettingsFragment extends Fragment {
private static final int FILEPICKER_RESULT = 0xF17E;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View v = li.inflate(R.layout.fragment_settings, vg, false);
Button importBtn = v.findViewById(R.id.settings_import_source);
importBtn.setOnClickListener(importBtnListener);
Button resetBtn = v.findViewById(R.id.settings_reset_db);
resetBtn.setOnClickListener(resetBtnListener);
return v;
}

private View.OnClickListener importBtnListener = new View.OnClickListener() {
public void onClick(View v) {showFilePicker();}
};

private View.OnClickListener resetBtnListener = new View.OnClickListener() {
public void onClick(View v) {
Supplier<Void> resetAction = new Supplier<Void>() {
public Void get() {
resetDB();
return null;
}
};
confirmationDialog(getString(R.string.label_settings_reset_db_confim_msg), resetAction);
}
};

// https://riptutorial.com/android/example/14425/showing-a-file-chooser-and-reading-the-result
private void showFilePicker() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("application/zip");
i.addCategory(Intent.CATEGORY_OPENABLE);
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(i, FILEPICKER_RESULT);
}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode != FILEPICKER_RESULT || resultCode != Activity.RESULT_OK)
return;
try {
DndbSQLManager dbman = new DndbSQLManager(getContext());
SQLiteDatabase db = dbman.getWritableDatabase();
dbman.execZipPackage(db, getActivity().getContentResolver().openInputStream(data.getData()));
db.close();
} catch (FileNotFoundException | NullPointerException e) {
Log.e("onActivityResult", "Error loading file from file picker", e);
} catch (IOException e) {
Log.e("onActivityResult", "Error processing SQL in " + data.getData().toString(), e);
}
}

private void resetDB() {
DndbSQLManager dbman = new DndbSQLManager(getContext());
SQLiteDatabase db = dbman.getWritableDatabase();
Log.d("resetDB", "Clearing database with onCreate()");
dbman.onCreate(db);
db.close();
dbman.close();
}

private void confirmationDialog(String msg, final Supplier<Void> confirmAction) {
AlertDialog.Builder confirm = new AlertDialog.Builder(getContext());
confirm.setTitle(R.string.general_confirm_dialog_title);
confirm.setMessage(msg);
confirm.setNegativeButton(R.string.general_button_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {dialog.dismiss();}
});
confirm.setPositiveButton(R.string.general_button_confirm, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {confirmAction.get();}
});
confirm.create().show();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;
import ca.printf.dndb.R;
import ca.printf.dndb.entity.Spell;

Expand Down Expand Up @@ -65,7 +66,7 @@ public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
displayStringList(spell.getAtkTypes(), v, R.id.spelldetail_attacks, R.id.spelldetail_attacks_container);
displayStringList(spell.getDmgTypes(), v, R.id.spelldetail_damages, R.id.spelldetail_damages_container);
displayStringList(spell.getConditions(), v, R.id.spelldetail_conditions, R.id.spelldetail_conditions_container);
((TextView)v.findViewById(R.id.spelldetail_sources)).setText(colateStringList(spell.getSources()));
((TextView)v.findViewById(R.id.spelldetail_sources)).setText(colateStringList(spell.getSources().values()));
((TextView)v.findViewById(R.id.spelldetail_classes)).setText(colateStringList(spell.getClasses()));
return v;
}
Expand All @@ -82,7 +83,7 @@ private void displayStringList(ArrayList<String> list, View parent, int textId,
parent.findViewById(containerId).setVisibility(View.VISIBLE);
}

private String colateStringList(ArrayList<String> list) {
private String colateStringList(Collection<String> list) {
String ret = "";
String delim = "";
for(String s : list) {
Expand Down
Loading

0 comments on commit bfac117

Please sign in to comment.