Files
MonsterCards/docs/Import-Export.md

9.2 KiB

Import & Export Architecture & Plan

This document outlines the modular architecture and step-by-step technical roadmap for handling monster stat block imports, exports, and sharing across multiple formats in the Monster Cards application.


1. Modular Importer Architecture

To keep import/export code clean and maintainable, all format parsers implement a shared interface:

public interface EntityImporter<T> {
    /**
     * Determines whether the given input string or payload can be handled by this importer.
     */
    boolean canImport(@NonNull String input);

    /**
     * Parses the raw input string into internal domain memory objects (e.g., Monster).
     */
    @NonNull
    T parse(@NonNull String input) throws Exception;
}

The output of any EntityImporter is an in-memory domain model (such as Monster), which is then passed to MonsterImportFragment for UI preview and Room database persistence.


2. Step-by-Step Implementation Roadmap

  ┌────────────────----------------────────────────────────┐
  │ Step 0: Restrict File Extension Intent Filters         │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 1: Refactor Import Code to Shared Interface       │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 2: Update Tetra-cube Importer to Newest Format    │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 3: Import from D&D Beyond Character URL           │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 4: Export to Custom Internal Format (Open5e)      │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 5: Import from Custom Internal Format (Open5e)      │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 6: Generic Android Share Button Feature           │
  └────────────────────────────────────────────────────────┘
                               │
                               v
  ┌────────────────----------------────────────────────────┐
  │ Step 7: Specific Share Targets (NFC, Bluetooth, URL)   │
  └────────────────────────────────────────────────────────┘

Step 0: Restrict File Extension Intent Filters

Restrict the Android app so it reacts only to .monster and .monster.txt files instead of all generic .txt files.

  1. AndroidManifest.xml: Add android:pathPattern and android:pathSuffix constraints (.monster and .monster.txt) to the <intent-filter> for MainActivity.
  2. MainActivity.java: Implement runtime display name validation querying OpenableColumns.DISPLAY_NAME via ContentResolver to filter out non-monster files passed via content:// URIs.

Step 1: Refactor Import Code into Importer Architecture

Extract the current parsing logic out of MonsterImportHelper.java into a standalone importer class implementing EntityImporter<Monster>.

  • New Class: TetraCubeMonsterImporter implements EntityImporter<Monster>
  • Responsibilities:
    • canImport(json): Validates that the JSON string contains Tetra-cube properties (e.g. hitDice, armorName, strPoints).
    • parse(json): Converts the raw JSON payload into an in-memory Monster object.

Step 2: Update Tetra-cube Importer to Newest Format

Update TetraCubeMonsterImporter to support newer fields from the Tetra-cube generator source (js/statblock-script.js):

  • New Fields to Parse:
    • bonusActions: Array of { name, desc } objects \rightarrow mapped to monster.actions or bonus action structure.
    • mythics / isMythic / mythicDescription: Array of mythic action objects and intro text \rightarrow mapped to monster.legendaryActions.
    • blind: Parse boolean flag to append "(blind beyond this radius)" to blindsight.
    • Section intro text: Parse legendariesDescription, lairDescription, regionalDescription.

Step 3: Import from D&D Beyond Character URL

Allow users to import monsters or characters directly from a D&D Beyond URL.

  • Target URL Structure: https://www.dndbeyond.com/characters/49074997 (where 49074997 is the internal character ID).
  • Service Endpoints: D&D Beyond character pages load data from background JSON endpoints such as: https://character-service.dndbeyond.com/character/v2/character/49074997
  • New Importer Class: DnDBeyondImporter implements EntityImporter<Monster>
  • Pipeline:
    1. Extract character ID (49074997) from user-provided D&D Beyond URL.
    2. Perform an asynchronous HTTP request (via OkHttp/Retrofit) to fetch the character JSON payload.
    3. Map D&D Beyond JSON fields (stats, modifiers, AC, HP, speed, actions, traits, spells) to our internal Monster memory model.
    4. Pass the parsed Monster to MonsterImportFragment for review and persistence.

Step 4: Export to Custom Internal Format (Open5e Specification)

Implement export capability to output monsters and collections using our two-tier Open5e JSON specification (/Users/tom/Projects/TTRPG/CharacterDataFiles/rulesets/open5e/import-export.md).

  • New Class: Open5eExporter
  • Output Architecture:
    • Universal Envelope (entity.json): uuid, ruleset_id: "open5e", entity_type: "character", display_name, version, tags, properties.
    • Character Payload (character.json): Export ability scores, combat vitals (AC, HP formula, speed), proficiency bonuses, actions, reactions, legendary actions, features, and equipped items (weapon.json, armor.json, shield.json, spell.json).

Step 5: Import from Custom Internal Format (Open5e Specification)

Implement import capability for custom Open5e JSON files.

  • New Class: Open5eImporter implements EntityImporter<Monster>
  • Pipeline:
    • canImport(json): Validates "ruleset_id": "open5e" and "entity_type": "character".
    • parse(json): Unpacks properties into internal Monster domain models.

Step 6: Generic Android Share Button Feature

Implement a generic "Share" feature allowing users to export and share monsters or collections from detail screens.

  • UI Action: Add a "Share" item to action menus in MonsterDetailFragment and CollectionDetailFragment.
  • Android Intent: Uses standard Android ACTION_SEND intent with Intent.EXTRA_STREAM or Intent.EXTRA_TEXT to pass exported files to other apps (e.g. Email, Drive, Messaging, Files).

Step 7: Specific Share Targets & Channels

Extend the generic sharing feature with specialized, direct sharing channels (to be implemented as individual sub-steps):

  • Sub-Step 7.1 (NFC Sharing): Share monster data directly between devices via NFC (NDEF records / Android Beam).
  • Sub-Step 7.2 (Bluetooth / Wi-Fi Direct): Share monster files directly between nearby Android devices via Bluetooth / Wi-Fi.
  • Sub-Step 7.3 (Web URL with Embedded Payload): Generate a shareable Web URL containing compressed/base64-encoded monster JSON data.