Skip to content

Sriverse

File Tree Structure

app/
├── src/main/
│   ├── java/com/versepad/
│   │   ├── MainActivity.java
│   │   ├── models/
│   │   │   ├── Poem.java
│   │   │   ├── WordAnalysis.java
│   │   │   └── RhymeData.java
│   │   ├── services/
│   │   │   ├── DictionaryService.java
│   │   │   ├── RhymeService.java
│   │   │   ├── MeterService.java
│   │   │   ├── PhoneticsService.java
│   │   │   └── ThesaurusService.java
│   │   ├── adapters/
│   │   │   ├── SuggestionAdapter.java
│   │   │   └── PoemListAdapter.java
│   │   ├── fragments/
│   │   │   ├── EditorFragment.java
│   │   │   └── AnalysisFragment.java
│   │   └── utils/
│   │       ├── DatabaseHelper.java
│   │       └── FileUtils.java
│   ├── res/
│   │   ├── layout/
│   │   │   ├── activity_main.xml
│   │   │   ├── fragment_editor.xml
│   │   │   ├── fragment_analysis.xml
│   │   │   ├── item_suggestion.xml
│   │   │   └── item_poem.xml
│   │   ├── values/
│   │   │   ├── strings.xml
│   │   │   ├── colors.xml
│   │   │   └── styles.xml
│   │   ├── drawable/
│   │   │   └── ic_launcher.xml
│   │   └── menu/
│   │       └── main_menu.xml
│   ├── assets/
│   │   ├── cmudict.txt
│   │   ├── dictionary.txt
│   │   └── thesaurus.txt
│   └── AndroidManifest.xml
├── build.gradle (Module: app)
└── build.gradle (Project)

1. build.gradle (Project level)

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.1.0'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

2. build.gradle (Module: app)

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.versepad'
    compileSdk 34

    defaultConfig {
        applicationId "com.versepad"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.fragment:fragment:1.6.1'
    implementation 'androidx.recyclerview:recyclerview:1.3.1'

    // Text processing libraries
    implementation 'com.github.takscape:java-cmu-sphinx:1.0.0'
    implementation 'org.apache.commons:commons-lang3:3.12.0'
    implementation 'org.apache.commons:commons-text:1.10.0'

    // JSON processing
    implementation 'com.google.code.gson:gson:2.10.1'

    // Database
    implementation 'androidx.room:room-runtime:2.5.0'
    annotationProcessor 'androidx.room:room-compiler:2.5.0'

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

3. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/AppTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

4. MainActivity.java

package com.versepad;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.versepad.fragments.EditorFragment;
import com.versepad.fragments.AnalysisFragment;
import com.versepad.services.*;

public class MainActivity extends AppCompatActivity {
    private DictionaryService dictionaryService;
    private RhymeService rhymeService;
    private MeterService meterService;
    private PhoneticsService phoneticsService;
    private ThesaurusService thesaurusService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize services
        initializeServices();

        // Setup bottom navigation
        BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
        bottomNav.setOnItemSelectedListener(item -> {
            Fragment selectedFragment = null;

            if (item.getItemId() == R.id.nav_editor) {
                selectedFragment = new EditorFragment();
            } else if (item.getItemId() == R.id.nav_analysis) {
                selectedFragment = new AnalysisFragment();
            }

            if (selectedFragment != null) {
                getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, selectedFragment)
                    .commit();
            }
            return true;
        });

        // Load default fragment
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, new EditorFragment())
                .commit();
        }
    }

    private void initializeServices() {
        dictionaryService = new DictionaryService(this);
        rhymeService = new RhymeService(this);
        meterService = new MeterService(this);
        phoneticsService = new PhoneticsService(this);
        thesaurusService = new ThesaurusService(this);
    }

    public DictionaryService getDictionaryService() { return dictionaryService; }
    public RhymeService getRhymeService() { return rhymeService; }
    public MeterService getMeterService() { return meterService; }
    public PhoneticsService getPhoneticsService() { return phoneticsService; }
    public ThesaurusService getThesaurusService() { return thesaurusService; }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_save) {
            // Handle save
            return true;
        } else if (item.getItemId() == R.id.action_settings) {
            // Handle settings
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

5. Models

Poem.java

package com.versepad.models;

import java.util.Date;

public class Poem {
    private long id;
    private String title;
    private String content;
    private Date createdDate;
    private Date modifiedDate;

    public Poem() {
        this.createdDate = new Date();
        this.modifiedDate = new Date();
    }

    public Poem(String title, String content) {
        this();
        this.title = title;
        this.content = content;
    }

    // Getters and Setters
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }

    public String getTitle() { return title; }
    public void setTitle(String title) { 
        this.title = title;
        this.modifiedDate = new Date();
    }

    public String getContent() { return content; }
    public void setContent(String content) { 
        this.content = content;
        this.modifiedDate = new Date();
    }

    public Date getCreatedDate() { return createdDate; }
    public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; }

    public Date getModifiedDate() { return modifiedDate; }
    public void setModifiedDate(Date modifiedDate) { this.modifiedDate = modifiedDate; }
}

WordAnalysis.java

package com.versepad.models;

import java.util.List;

public class WordAnalysis {
    private String word;
    private String phonetic;
    private int syllableCount;
    private List<String> rhymes;
    private List<String> synonyms;
    private String definition;
    private String partOfSpeech;
    private String stressPattern;

    public WordAnalysis(String word) {
        this.word = word;
    }

    // Getters and Setters
    public String getWord() { return word; }
    public void setWord(String word) { this.word = word; }

    public String getPhonetic() { return phonetic; }
    public void setPhonetic(String phonetic) { this.phonetic = phonetic; }

    public int getSyllableCount() { return syllableCount; }
    public void setSyllableCount(int syllableCount) { this.syllableCount = syllableCount; }

    public List<String> getRhymes() { return rhymes; }
    public void setRhymes(List<String> rhymes) { this.rhymes = rhymes; }

    public List<String> getSynonyms() { return synonyms; }
    public void setSynonyms(List<String> synonyms) { this.synonyms = synonyms; }

    public String getDefinition() { return definition; }
    public void setDefinition(String definition) { this.definition = definition; }

    public String getPartOfSpeech() { return partOfSpeech; }
    public void setPartOfSpeech(String partOfSpeech) { this.partOfSpeech = partOfSpeech; }

    public String getStressPattern() { return stressPattern; }
    public void setStressPattern(String stressPattern) { this.stressPattern = stressPattern; }
}

RhymeData.java

package com.versepad.models;

public class RhymeData {
    private String word;
    private String phoneticEnding;
    private int syllableCount;

    public RhymeData(String word, String phoneticEnding, int syllableCount) {
        this.word = word;
        this.phoneticEnding = phoneticEnding;
        this.syllableCount = syllableCount;
    }

    // Getters and Setters
    public String getWord() { return word; }
    public void setWord(String word) { this.word = word; }

    public String getPhoneticEnding() { return phoneticEnding; }
    public void setPhoneticEnding(String phoneticEnding) { this.phoneticEnding = phoneticEnding; }

    public int getSyllableCount() { return syllableCount; }
    public void setSyllableCount(int syllableCount) { this.syllableCount = syllableCount; }
}

6. Services

DictionaryService.java

package com.versepad.services;

import android.content.Context;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;

public class DictionaryService {
    private Context context;
    private Map<String, String> dictionary;
    private Map<String, String> partOfSpeechMap;

    public DictionaryService(Context context) {
        this.context = context;
        this.dictionary = new HashMap<>();
        this.partOfSpeechMap = new HashMap<>();
        loadDictionary();
    }

    private void loadDictionary() {
        try {
            InputStream is = context.getAssets().open("dictionary.txt");
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;

            while ((line = reader.readLine()) != null) {
                if (line.trim().isEmpty() || line.startsWith("#")) continue;

                String[] parts = line.split("\\|");
                if (parts.length >= 3) {
                    String word = parts[0].toLowerCase().trim();
                    String pos = parts[1].trim();
                    String definition = parts[2].trim();

                    dictionary.put(word, definition);
                    partOfSpeechMap.put(word, pos);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getDefinition(String word) {
        return dictionary.get(word.toLowerCase());
    }

    public String getPartOfSpeech(String word) {
        return partOfSpeechMap.get(word.toLowerCase());
    }

    public boolean isValidWord(String word) {
        return dictionary.containsKey(word.toLowerCase());
    }

    public List<String> findSimilarWords(String prefix) {
        List<String> similar = new ArrayList<>();
        String lowerPrefix = prefix.toLowerCase();

        for (String word : dictionary.keySet()) {
            if (word.startsWith(lowerPrefix) && similar.size() < 20) {
                similar.add(word);
            }
        }

        return similar;
    }
}

RhymeService.java

package com.versepad.services;

import android.content.Context;
import com.versepad.models.RhymeData;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

public class RhymeService {
    private Context context;
    private Map<String, String> wordToPhonetic;
    private Map<String, List<String>> phoneticToWords;

    public RhymeService(Context context) {
        this.context = context;
        this.wordToPhonetic = new HashMap<>();
        this.phoneticToWords = new HashMap<>();
        loadCMUDict();
    }

    private void loadCMUDict() {
        try {
            InputStream is = context.getAssets().open("cmudict.txt");
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;

            while ((line = reader.readLine()) != null) {
                if (line.startsWith(";;;")) continue;

                String[] parts = line.split("\\s+", 2);
                if (parts.length == 2) {
                    String word = parts[0].toLowerCase().replaceAll("\\(\\d+\\)", "");
                    String phonetic = parts[1];

                    wordToPhonetic.put(word, phonetic);

                    // Extract rhyme ending (last 2-3 phonemes)
                    String[] phonemes = phonetic.split("\\s+");
                    if (phonemes.length >= 2) {
                        String ending = String.join(" ", 
                            Arrays.copyOfRange(phonemes, Math.max(0, phonemes.length - 3), phonemes.length));

                        phoneticToWords.computeIfAbsent(ending, k -> new ArrayList<>()).add(word);
                    }
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<String> findRhymes(String word) {
        List<String> rhymes = new ArrayList<>();
        String phonetic = wordToPhonetic.get(word.toLowerCase());

        if (phonetic != null) {
            String[] phonemes = phonetic.split("\\s+");
            if (phonemes.length >= 2) {
                String ending = String.join(" ", 
                    Arrays.copyOfRange(phonemes, Math.max(0, phonemes.length - 3), phonemes.length));

                List<String> candidates = phoneticToWords.get(ending);
                if (candidates != null) {
                    for (String candidate : candidates) {
                        if (!candidate.equals(word.toLowerCase()) && rhymes.size() < 50) {
                            rhymes.add(candidate);
                        }
                    }
                }
            }
        }

        return rhymes;
    }

    public String getPhonetic(String word) {
        return wordToPhonetic.get(word.toLowerCase());
    }

    public List<String> findPerfectRhymes(String word) {
        // Perfect rhymes have identical sounds from the last stressed vowel
        return findRhymes(word);
    }

    public List<String> findNearRhymes(String word) {
        List<String> nearRhymes = new ArrayList<>();
        String phonetic = wordToPhonetic.get(word.toLowerCase());

        if (phonetic != null) {
            String[] phonemes = phonetic.split("\\s+");
            if (phonemes.length >= 1) {
                String lastPhoneme = phonemes[phonemes.length - 1];

                // Find words ending with similar sounds
                for (Map.Entry<String, String> entry : wordToPhonetic.entrySet()) {
                    if (!entry.getKey().equals(word.toLowerCase())) {
                        String[] otherPhonemes = entry.getValue().split("\\s+");
                        if (otherPhonemes.length > 0) {
                            String otherLast = otherPhonemes[otherPhonemes.length - 1];
                            if (areSimilarPhonemes(lastPhoneme, otherLast) && nearRhymes.size() < 30) {
                                nearRhymes.add(entry.getKey());
                            }
                        }
                    }
                }
            }
        }

        return nearRhymes;
    }

    private boolean areSimilarPhonemes(String phone1, String phone2) {
        // Remove stress markers
        phone1 = phone1.replaceAll("[0-2]", "");
        phone2 = phone2.replaceAll("[0-2]", "");

        // Check for similar vowel sounds
        String[] vowels = {"A", "E", "I", "O", "U", "AE", "AH", "AO", "AW", "AY", "EH", "ER", "EY", "IH", "IY", "OW", "OY", "UH", "UW"};

        for (String vowel : vowels) {
            if (phone1.startsWith(vowel) && phone2.startsWith(vowel)) {
                return true;
            }
        }

        return phone1.equals(phone2);
    }
}

MeterService.java

package com.versepad.services;

import android.content.Context;
import java.util.*;
import java.util.regex.Pattern;

public class MeterService {
    private Context context;
    private RhymeService rhymeService;
    private Pattern wordPattern;

    public MeterService(Context context) {
        this.context = context;
        this.rhymeService = new RhymeService(context);
        this.wordPattern = Pattern.compile("\\b\\w+\\b");
    }

    public String analyzeMeter(String line) {
        String[] words = line.toLowerCase().replaceAll("[^a-zA-Z\\s]", "").split("\\s+");
        StringBuilder meterPattern = new StringBuilder();

        for (String word : words) {
            if (word.trim().isEmpty()) continue;

            String stressPattern = getStressPattern(word);
            meterPattern.append(stressPattern).append(" ");
        }

        return meterPattern.toString().trim();
    }

    public String getStressPattern(String word) {
        String phonetic = rhymeService.getPhonetic(word);
        if (phonetic == null) {
            return estimateStressPattern(word);
        }

        StringBuilder pattern = new StringBuilder();
        String[] phonemes = phonetic.split("\\s+");

        for (String phoneme : phonemes) {
            if (phoneme.matches(".*[0-2]")) {
                char stress = phoneme.charAt(phoneme.length() - 1);
                switch (stress) {
                    case '1': pattern.append("/"); break; // Primary stress
                    case '2': pattern.append("\\"); break; // Secondary stress
                    case '0': pattern.append("u"); break; // Unstressed
                }
            }
        }

        return pattern.length() > 0 ? pattern.toString() : estimateStressPattern(word);
    }

    private String estimateStressPattern(String word) {
        // Simple heuristic for unknown words
        int syllables = countSyllables(word);
        if (syllables <= 1) return "/";
        if (syllables == 2) return "/u";
        if (syllables == 3) return "/uu";
        return "/" + "u".repeat(syllables - 1);
    }

    public int countSyllables(String word) {
        if (word == null || word.isEmpty()) return 0;

        word = word.toLowerCase().replaceAll("[^a-z]", "");
        if (word.isEmpty()) return 0;

        // Count vowel groups
        int count = 0;
        boolean previousWasVowel = false;

        for (char c : word.toCharArray()) {
            boolean isVowel = "aeiouy".indexOf(c) != -1;
            if (isVowel && !previousWasVowel) {
                count++;
            }
            previousWasVowel = isVowel;
        }

        // Adjust for silent e
        if (word.endsWith("e") && count > 1) {
            count--;
        }

        // Ensure at least 1 syllable
        return Math.max(1, count);
    }

    public String identifyMeterType(String poem) {
        String[] lines = poem.split("\n");
        Map<String, Integer> meterCounts = new HashMap<>();

        for (String line : lines) {
            if (line.trim().isEmpty()) continue;

            String meter = analyzeMeter(line);
            String simplified = simplifyMeterPattern(meter);
            meterCounts.put(simplified, meterCounts.getOrDefault(simplified, 0) + 1);
        }

        // Find most common pattern
        String commonPattern = meterCounts.entrySet().stream()
            .max(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElse("unknown");

        return identifyMeterName(commonPattern);
    }

    private String simplifyMeterPattern(String pattern) {
        return pattern.replaceAll("\\s+", "").replaceAll("\\\\", "u");
    }

    private String identifyMeterName(String pattern) {
        // Common meter patterns
        if (pattern.matches("(/u)+")) return "Iambic";
        if (pattern.matches("(u/)+")) return "Trochaic";
        if (pattern.matches("(uu/)+")) return "Anapestic";
        if (pattern.matches("(/uu)+")) return "Dactylic";
        return "Free Verse";
    }

    public String getVisualMeter(String line) {
        String meter = analyzeMeter(line);
        return meter.replace("/", "◉").replace("u", "○").replace("\\", "◐");
    }
}

PhoneticsService.java

package com.versepad.services;

import android.content.Context;
import java.util.*;

public class PhoneticsService {
    private Context context;
    private RhymeService rhymeService;
    private Map<String, String> phoneticToIPA;

    public PhoneticsService(Context context) {
        this.context = context;
        this.rhymeService = new RhymeService(context);
        initializePhoneticMap();
    }

    private void initializePhoneticMap() {
        phoneticToIPA = new HashMap<>();

        // Vowels
        phoneticToIPA.put("AA", "ɑ");
        phoneticToIPA.put("AE", "æ");
        phoneticToIPA.put("AH", "ʌ");
        phoneticToIPA.put("AO", "ɔ");
        phoneticToIPA.put("AW", "aʊ");
        phoneticToIPA.put("AY", "aɪ");
        phoneticToIPA.put("EH", "ɛ");
        phoneticToIPA.put("ER", "ɚ");
        phoneticToIPA.put("EY", "eɪ");
        phoneticToIPA.put("IH", "ɪ");
        phoneticToIPA.put("IY", "i");
        phoneticToIPA.put("OW", "oʊ");
        phoneticToIPA.put("OY", "ɔɪ");
        phoneticToIPA.put("UH", "ʊ");
        phoneticToIPA.put("UW", "u");

        // Consonants
        phoneticToIPA.put("B", "b");
        phoneticToIPA.put("CH", "tʃ");
        phoneticToIPA.put("D", "d");
        phoneticToIPA.put("DH", "ð");
        phoneticToIPA.put("F", "f");
        phoneticToIPA.put("G", "g");
        phoneticToIPA.put("HH", "h");
        phoneticToIPA.put("JH", "dʒ");
        phoneticToIPA.put("K", "k");
        phoneticToIPA.put("L", "l");
        phoneticToIPA.put("M", "m");
        phoneticToIPA.put("N", "n");
        phoneticToIPA.put("NG", "ŋ");
        phoneticToIPA.put("P", "p");
        phoneticToIPA.put("R", "r");
        phoneticToIPA.put("S", "s");
        phoneticToIPA.put("SH", "ʃ");
        phoneticToIPA.put("T", "t");
        phoneticToIPA.put("TH", "θ");
        phoneticToIPA.put("V", "v");
        phoneticToIPA.put("W", "w");
        phoneticToIPA.put("Y", "j");
        phoneticToIPA.put("Z", "z");
        phoneticToIPA.put("ZH", "ʒ");
    }

    public String getIPATranscription(String word) {
        String cmuPhonetic = rhymeService.getPhonetic(word);
        if (cmuPhonetic == null) {
            return null;
        }

        StringBuilder ipa = new StringBuilder();
        String[] phonemes = cmuPhonetic.split("\\s+");

        for (String phoneme : phonemes) {
            String cleanPhoneme = phoneme.replaceAll("[0-2]", "");
            String ipaSymbol = phoneticToIPA.get(cleanPhoneme);

            if (ipaSymbol != null) {
                ipa.append(ipaSymbol);

                // Add stress markers
                if (phoneme.endsWith("1")) {
                    ipa.insert(ipa.length() - ipaSymbol.length(), "ˈ");
                } else if (phoneme.endsWith("2")) {
                    ipa.insert(ipa.length() - ipaSymbol.length(), "ˌ");
                }
            }
        }

        return "/" + ipa.toString() + "/";
    }

    public String getCMUPhonetic(String word) {
        return rhymeService.getPhonetic(word);
    }

    public List<String> getAllophones(String phoneme) {
        List<String> allophones = new ArrayList<>();

        // Common allophone patterns
        switch (phoneme.replaceAll("[0-2]", "")) {
            case "T":
                allophones.addAll(Arrays.asList("t", "ʔ", "ɾ"));
                break;
            case "D":
                allophones.addAll(Arrays.asList("d", "ɾ"));
                break;
            case "L":
                allophones.addAll(Arrays.asList("l", "ɫ"));
                break;
            case "R":
                allophones.addAll(Arrays.asList("r", "ɹ", "ɾ"));
                break;
            default:
                String ipa = phoneticToIPA.get(phoneme);
                if (ipa != null) {
                    allophones.add(ipa);
                }
        }

        return allophones;
    }

    public boolean arePhonemesSimilar(String phoneme1, String phoneme2) {
        phoneme1 = phoneme1.replaceAll("[0-2]", "");
        phoneme2 = phoneme2.replaceAll("[0-2]", "");

        // Check if they're the same
        if (phoneme1.equals(phoneme2)) return true;

        // Check if they share allophones
        List<String> allo1 = getAllophones(phoneme1);
        List<String> allo2 = getAllophones(phoneme2);

        for (String a1 : allo1) {
            if (allo2.contains(a1)) return true;
        }

        return false;
    }
}

ThesaurusService.java

package com.versepad.services;

import android.content.Context;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

public class ThesaurusService {
    private Context context;
    private Map<String, List<String>> synonyms;
    private Map<String, List<String>> antonyms;

    public ThesaurusService(Context context) {
        this.context = context;
        this.synonyms = new HashMap<>();
        this.antonyms = new HashMap<>();
        loadThesaurus();
    }

    private void loadThesaurus() {
        try {
            InputStream is = context.getAssets().open("thesaurus.txt");
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;

            while ((line = reader.readLine()) != null) {
                if (line.trim().isEmpty() || line.startsWith("#")) continue;

                String[] parts = line.split("\\|");
                if (parts.length >= 3) {
                    String word = parts[0].toLowerCase().trim();
                    String type = parts[1].trim(); // "syn" or "ant"
                    String[] words = parts[2].split(",");

                    List<String> wordList = new ArrayList<>();
                    for (String w : words) {
                        wordList.add(w.trim());
                    }

                    if ("syn".equals(type)) {
                        synonyms.put(word, wordList);
                    } else if ("ant".equals(type)) {
                        antonyms.put(word, wordList);
                    }
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<String> getSynonyms(String word) {
        List<String> result = synonyms.get(word.toLowerCase());
        return result != null ? new ArrayList<>(result) : new ArrayList<>();
    }

    public List<String> getAntonyms(String word) {
        List<String> result = antonyms.get(word.toLowerCase());
        return result != null ? new ArrayList<>(result) : new ArrayList<>();
    }

    public List<String> getRelatedWords(String word) {
        List<String> related = new ArrayList<>();
        related.addAll(getSynonyms(word));
        related.addAll(getAntonyms(word));
        return related;
    }

    public List<String> findWordsByMeaning(String concept) {
        List<String> matches = new ArrayList<>();
        String lowerConcept = concept.toLowerCase();

        for (Map.Entry<String, List<String>> entry : synonyms.entrySet()) {
            if (entry.getValue().stream().anyMatch(syn -> syn.toLowerCase().contains(lowerConcept))) {
                matches.add(entry.getKey());
            }
        }

        return matches;
    }
}

7. Fragments

EditorFragment.java

package com.versepad.fragments;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.versepad.MainActivity;
import com.versepad.R;
import com.versepad.adapters.SuggestionAdapter;
import com.versepad.models.WordAnalysis;
import java.util.ArrayList;
import java.util.List;

public class EditorFragment extends Fragment {
    private EditText editTitle;
    private EditText editContent;
    private TextView textWordCount;
    private TextView textLineCount;
    private RecyclerView recyclerSuggestions;
    private SuggestionAdapter suggestionAdapter;

    private MainActivity mainActivity;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_editor, container, false);

        mainActivity = (MainActivity) getActivity();

        initializeViews(view);
        setupTextWatchers();
        setupSuggestions();

        return view;
    }

    private void initializeViews(View view) {
        editTitle = view.findViewById(R.id.edit_title);
        editContent = view.findViewById(R.id.edit_content);
        textWordCount = view.findViewById(R.id.text_word_count);
        textLineCount = view.findViewById(R.id.text_line_count);
        recyclerSuggestions = view.findViewById(R.id.recycler_suggestions);
    }

    private void setupTextWatchers() {
        editContent.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                updateStats();
                updateSuggestions();
            }

            @Override
            public void afterTextChanged(Editable s) {}
        });
    }

    private void setupSuggestions() {
        suggestionAdapter = new SuggestionAdapter(new ArrayList<>(), word -> {
            // Insert word at cursor position
            int cursorPos = editContent.getSelectionStart();
            String currentText = editContent.getText().toString();
            String newText = currentText.substring(0, cursorPos) + word + currentText.substring(cursorPos);
            editContent.setText(newText);
            editContent.setSelection(cursorPos + word.length());
        });

        recyclerSuggestions.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
        recyclerSuggestions.setAdapter(suggestionAdapter);
    }

    private void updateStats() {
        String content = editContent.getText().toString();

        // Word count
        String[] words = content.trim().split("\\s+");
        int wordCount = content.trim().isEmpty() ? 0 : words.length;

        // Line count
        int lineCount = content.split("\n").length;

        textWordCount.setText("Words: " + wordCount);
        textLineCount.setText("Lines: " + lineCount);
    }

    private void updateSuggestions() {
        String content = editContent.getText().toString();
        int cursorPos = editContent.getSelectionStart();

        // Get current word
        String currentWord = getCurrentWord(content, cursorPos);
        if (currentWord.length() < 2) {
            suggestionAdapter.updateSuggestions(new ArrayList<>());
            return;
        }

        // Get suggestions based on context
        List<String> suggestions = new ArrayList<>();

        // Rhyme suggestions
        List<String> rhymes = mainActivity.getRhymeService().findRhymes(currentWord);
        suggestions.addAll(rhymes.subList(0, Math.min(5, rhymes.size())));

        // Synonym suggestions
        List<String> synonyms = mainActivity.getThesaurusService().getSynonyms(currentWord);
        suggestions.addAll(synonyms.subList(0, Math.min(5, synonyms.size())));

        // Similar words
        List<String> similar = mainActivity.getDictionaryService().findSimilarWords(currentWord);
        suggestions.addAll(similar.subList(0, Math.min(5, similar.size())));

        suggestionAdapter.updateSuggestions(suggestions);
    }

    private String getCurrentWord(String text, int cursorPos) {
        if (cursorPos <= 0 || cursorPos > text.length()) return "";

        int start = cursorPos - 1;
        int end = cursorPos;

        // Find word boundaries
        while (start >= 0 && Character.isLetterOrDigit(text.charAt(start))) {
            start--;
        }
        start++;

        while (end < text.length() && Character.isLetterOrDigit(text.charAt(end))) {
            end++;
        }

        return text.substring(start, end);
    }

    public String getTitle() {
        return editTitle.getText().toString();
    }

    public String getContent() {
        return editContent.getText().toString();
    }

    public void setTitle(String title) {
        editTitle.setText(title);
    }

    public void setContent(String content) {
        editContent.setText(content);
        updateStats();
    }
}

AnalysisFragment.java

package com.versepad.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import com.versepad.MainActivity;
import com.versepad.R;

public class AnalysisFragment extends Fragment {
    private TextView textMeterAnalysis;
    private TextView textRhymeScheme;
    private TextView textPhoneticAnalysis;
    private TextView textStructureAnalysis;

    private MainActivity mainActivity;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_analysis, container, false);

        mainActivity = (MainActivity) getActivity();

        initializeViews(view);
        performAnalysis();

        return view;
    }

    private void initializeViews(View view) {
        textMeterAnalysis = view.findViewById(R.id.text_meter_analysis);
        textRhymeScheme = view.findViewById(R.id.text_rhyme_scheme);
        textPhoneticAnalysis = view.findViewById(R.id.text_phonetic_analysis);
        textStructureAnalysis = view.findViewById(R.id.text_structure_analysis);
    }

    private void performAnalysis() {
        // Get poem content from editor fragment
        String poemContent = getSamplePoem(); // You would get this from the editor

        if (poemContent.trim().isEmpty()) {
            showEmptyState();
            return;
        }

        analyzeMeter(poemContent);
        analyzeRhymeScheme(poemContent);
        analyzePhonetics(poemContent);
        analyzeStructure(poemContent);
    }

    private void analyzeMeter(String poem) {
        StringBuilder analysis = new StringBuilder();
        String[] lines = poem.split("\n");

        analysis.append("METER ANALYSIS:\n\n");

        for (int i = 0; i < lines.length; i++) {
            if (lines[i].trim().isEmpty()) continue;

            String meter = mainActivity.getMeterService().analyzeMeter(lines[i]);
            String visual = mainActivity.getMeterService().getVisualMeter(lines[i]);

            analysis.append("Line ").append(i + 1).append(":\n");
            analysis.append(lines[i]).append("\n");
            analysis.append(visual).append("\n");
            analysis.append("Pattern: ").append(meter).append("\n\n");
        }

        String meterType = mainActivity.getMeterService().identifyMeterType(poem);
        analysis.append("Overall Meter: ").append(meterType);

        textMeterAnalysis.setText(analysis.toString());
    }

    private void analyzeRhymeScheme(String poem) {
        StringBuilder analysis = new StringBuilder();
        String[] lines = poem.split("\n");

        analysis.append("RHYME ANALYSIS:\n\n");

        char currentScheme = 'A';
        for (int i = 0; i < lines.length; i++) {
            if (lines[i].trim().isEmpty()) continue;

            String lastWord = getLastWord(lines[i]);
            if (!lastWord.isEmpty()) {
                analysis.append("Line ").append(i + 1).append(": ");
                analysis.append(lastWord).append(" (").append(currentScheme).append(")\n");

                // Get rhymes for the last word
                java.util.List<String> rhymes = mainActivity.getRhymeService().findRhymes(lastWord);
                if (!rhymes.isEmpty()) {
                    analysis.append("Rhymes: ");
                    for (int j = 0; j < Math.min(5, rhymes.size()); j++) {
                        analysis.append(rhymes.get(j));
                        if (j < Math.min(4, rhymes.size() - 1)) analysis.append(", ");
                    }
                    analysis.append("\n");
                }
                analysis.append("\n");
            }
            currentScheme++;
        }

        textRhymeScheme.setText(analysis.toString());
    }

    private void analyzePhonetics(String poem) {
        StringBuilder analysis = new StringBuilder();
        String[] words = poem.toLowerCase().replaceAll("[^a-zA-Z\\s]", "").split("\\s+");

        analysis.append("PHONETIC ANALYSIS:\n\n");

        java.util.Set<String> uniqueWords = new java.util.LinkedHashSet<>();
        for (String word : words) {
            if (word.length() > 2) {
                uniqueWords.add(word);
            }
        }

        int count = 0;
        for (String word : uniqueWords) {
            if (count >= 10) break; // Limit to first 10 unique words

            String ipa = mainActivity.getPhoneticsService().getIPATranscription(word);
            String cmu = mainActivity.getPhoneticsService().getCMUPhonetic(word);

            analysis.append(word).append(":\n");
            if (ipa != null) {
                analysis.append("  IPA: ").append(ipa).append("\n");
            }
            if (cmu != null) {
                analysis.append("  CMU: ").append(cmu).append("\n");
            }
            analysis.append("\n");
            count++;
        }

        textPhoneticAnalysis.setText(analysis.toString());
    }

    private void analyzeStructure(String poem) {
        StringBuilder analysis = new StringBuilder();
        String[] lines = poem.split("\n");

        analysis.append("STRUCTURE ANALYSIS:\n\n");

        int totalLines = 0;
        int totalWords = 0;
        int totalSyllables = 0;

        for (String line : lines) {
            if (!line.trim().isEmpty()) {
                totalLines++;
                String[] words = line.trim().split("\\s+");
                totalWords += words.length;

                for (String word : words) {
                    totalSyllables += mainActivity.getMeterService().countSyllables(word);
                }
            }
        }

        analysis.append("Total Lines: ").append(totalLines).append("\n");
        analysis.append("Total Words: ").append(totalWords).append("\n");
        analysis.append("Total Syllables: ").append(totalSyllables).append("\n");

        if (totalLines > 0) {
            analysis.append("Average Words per Line: ").append(String.format("%.1f", (double) totalWords / totalLines)).append("\n");
            analysis.append("Average Syllables per Line: ").append(String.format("%.1f", (double) totalSyllables / totalLines)).append("\n");
        }

        textStructureAnalysis.setText(analysis.toString());
    }

    private String getLastWord(String line) {
        String[] words = line.trim().replaceAll("[^a-zA-Z\\s]", "").split("\\s+");
        return words.length > 0 ? words[words.length - 1].toLowerCase() : "";
    }

    private String getSamplePoem() {
        // This would normally get content from the editor fragment
        return "Roses are red\nViolets are blue\nSugar is sweet\nAnd so are you";
    }

    private void showEmptyState() {
        textMeterAnalysis.setText("No poem to analyze. Please write something in the editor first.");
        textRhymeScheme.setText("");
        textPhoneticAnalysis.setText("");
        textStructureAnalysis.setText("");
    }
}

8. Adapters

SuggestionAdapter.java

package com.versepad.adapters;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.versepad.R;
import java.util.List;

public class SuggestionAdapter extends RecyclerView.Adapter<SuggestionAdapter.SuggestionViewHolder> {
    private List<String> suggestions;
    private OnSuggestionClickListener listener;

    public interface OnSuggestionClickListener {
        void onSuggestionClick(String suggestion);
    }

    public SuggestionAdapter(List<String> suggestions, OnSuggestionClickListener listener) {
        this.suggestions = suggestions;
        this.listener = listener;
    }

    @NonNull
    @Override
    public SuggestionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_suggestion, parent, false);
        return new SuggestionViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull SuggestionViewHolder holder, int position) {
        String suggestion = suggestions.get(position);
        holder.textSuggestion.setText(suggestion);
        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onSuggestionClick(suggestion);
            }
        });
    }

    @Override
    public int getItemCount() {
        return suggestions.size();
    }

    public void updateSuggestions(List<String> newSuggestions) {
        this.suggestions = newSuggestions;
        notifyDataSetChanged();
    }

    static class SuggestionViewHolder extends RecyclerView.ViewHolder {
        TextView textSuggestion;

        SuggestionViewHolder(@NonNull View itemView) {
            super(itemView);
            textSuggestion = itemView.findViewById(R.id.text_suggestion);
        }
    }
}

PoemListAdapter.java

package com.versepad.adapters;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.versepad.R;
import com.versepad.models.Poem;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;

public class PoemListAdapter extends RecyclerView.Adapter<PoemListAdapter.PoemViewHolder> {
    private List<Poem> poems;
    private OnPoemClickListener listener;
    private SimpleDateFormat dateFormat;

    public interface OnPoemClickListener {
        void onPoemClick(Poem poem);
        void onPoemLongClick(Poem poem);
    }

    public PoemListAdapter(List<Poem> poems, OnPoemClickListener listener) {
        this.poems = poems;
        this.listener = listener;
        this.dateFormat = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());
    }

    @NonNull
    @Override
    public PoemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_poem, parent, false);
        return new PoemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PoemViewHolder holder, int position) {
        Poem poem = poems.get(position);

        holder.textTitle.setText(poem.getTitle().isEmpty() ? "Untitled" : poem.getTitle());
        holder.textPreview.setText(getPreview(poem.getContent()));
        holder.textDate.setText(dateFormat.format(poem.getModifiedDate()));

        holder.itemView.setOnClickListener(v -> {
            if (listener != null) {
                listener.onPoemClick(poem);
            }
        });

        holder.itemView.setOnLongClickListener(v -> {
            if (listener != null) {
                listener.onPoemLongClick(poem);
            }
            return true;
        });
    }

    @Override
    public int getItemCount() {
        return poems.size();
    }

    private String getPreview(String content) {
        if (content.length() <= 100) {
            return content;
        }
        return content.substring(0, 100) + "...";
    }

    public void updatePoems(List<Poem> newPoems) {
        this.poems = newPoems;
        notifyDataSetChanged();
    }

    static class PoemViewHolder extends RecyclerView.ViewHolder {
        TextView textTitle;
        TextView textPreview;
        TextView textDate;

        PoemViewHolder(@NonNull View itemView) {
            super(itemView);
            textTitle = itemView.findViewById(R.id.text_title);
            textPreview = itemView.findViewById(R.id.text_preview);
            textDate = itemView.findViewById(R.id.text_date);
        }
    }
}

9. Utils

DatabaseHelper.java

package com.versepad.utils;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.versepad.models.Poem;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "versepad.db";
    private static final int DATABASE_VERSION = 1;

    private static final String TABLE_POEMS = "poems";
    private static final String COLUMN_ID = "id";
    private static final String COLUMN_TITLE = "title";
    private static final String COLUMN_CONTENT = "content";
    private static final String COLUMN_CREATED_DATE = "created_date";
    private static final String COLUMN_MODIFIED_DATE = "modified_date";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE " + TABLE_POEMS + " (" +
                COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COLUMN_TITLE + " TEXT, " +
                COLUMN_CONTENT + " TEXT, " +
                COLUMN_CREATED_DATE + " INTEGER, " +
                COLUMN_MODIFIED_DATE + " INTEGER)";
        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_POEMS);
        onCreate(db);
    }

    public long insertPoem(Poem poem) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();

        values.put(COLUMN_TITLE, poem.getTitle());
        values.put(COLUMN_CONTENT, poem.getContent());
        values.put(COLUMN_CREATED_DATE, poem.getCreatedDate().getTime());
        values.put(COLUMN_MODIFIED_DATE, poem.getModifiedDate().getTime());

        long id = db.insert(TABLE_POEMS, null, values);
        db.close();
        return id;
    }

    public void updatePoem(Poem poem) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();

        values.put(COLUMN_TITLE, poem.getTitle());
        values.put(COLUMN_CONTENT, poem.getContent());
        values.put(COLUMN_MODIFIED_DATE, new Date().getTime());

        db.update(TABLE_POEMS, values, COLUMN_ID + " = ?", new String[]{String.valueOf(poem.getId())});
        db.close();
    }

    public void deletePoem(long id) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_POEMS, COLUMN_ID + " = ?", new String[]{String.valueOf(id)});
        db.close();
    }

    public List<Poem> getAllPoems() {
        List<Poem> poems = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor cursor = db.query(TABLE_POEMS, null, null, null, null, null, COLUMN_MODIFIED_DATE + " DESC");

        if (cursor.moveToFirst()) {
            do {
                Poem poem = new Poem();
                poem.setId(cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
                poem.setTitle(cursor.getString(cursor.getColumnIndex(COLUMN_TITLE)));
                poem.setContent(cursor.getString(cursor.getColumnIndex(COLUMN_CONTENT)));
                poem.setCreatedDate(new Date(cursor.getLong(cursor.getColumnIndex(COLUMN_CREATED_DATE))));
                poem.setModifiedDate(new Date(cursor.getLong(cursor.getColumnIndex(COLUMN_MODIFIED_DATE))));

                poems.add(poem);
            } while (cursor.moveToNext());
        }

        cursor.close();
        db.close();
        return poems;
    }

    public Poem getPoemById(long id) {
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.query(TABLE_POEMS, null, COLUMN_ID + " = ?", new String[]{String.valueOf(id)}, null, null, null);

        Poem poem = null;
        if (cursor.moveToFirst()) {
            poem = new Poem();
            poem.setId(cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
            poem.setTitle(cursor.getString(cursor.getColumnIndex(COLUMN_TITLE)));
            poem.setContent(cursor.getString(cursor.getColumnIndex(COLUMN_CONTENT)));
            poem.setCreatedDate(new Date(cursor.getLong(cursor.getColumnIndex(COLUMN_CREATED_DATE))));
            poem.setModifiedDate(new Date(cursor.getLong(cursor.getColumnIndex(COLUMN_MODIFIED_DATE))));
        }

        cursor.close();
        db.close();
        return poem;
    }
}

FileUtils.java package com.versepad.utils;

import android.content.Context; import android.os.Environment; import android.widget.Toast; import com.versepad.models.Poem; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale;

public class FileUtils { private static final String POEMS_DIRECTORY = "VersePad"; private static final String FILE_EXTENSION = ".txt";

public static boolean saveToFile(Context context, Poem poem) {
    try {
        File documentsDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), POEMS_DIRECTORY);
        if (!documentsDir.exists()) {
            documentsDir.mkdirs();
        }

        String fileName = sanitizeFileName(poem.getTitle().isEmpty() ? "Untitled" : poem.getTitle()) + FILE_EXTENSION;
        File file = new File(documentsDir, fileName);

        FileWriter writer = new FileWriter(file);
        writer.write("Title: " + poem.getTitle() + "\n");
        writer.write("Created: " + formatDate(poem.getCreatedDate()) + "\n");
        writer.write("Modified: " + formatDate(poem.getModifiedDate()) + "\n");
        writer.write("\n" + poem.getContent());
        writer.close();

        Toast.makeText(context, "Poem saved to " + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
        return true;
    } catch (IOException e) {
        Toast.makeText(context, "Error saving file: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        return false;
    }
}

public static String loadFromFile(Context context, String fileName) {
    try {
        File documentsDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), POEMS_DIRECTORY);
        File file = new File(documentsDir, fileName);

        if (!file.exists()) {
            return null;
        }

        BufferedReader reader = new BufferedReader(new FileReader(file));
        StringBuilder content = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            content.append(line).append("\n");
        }
        reader.close();

        return content.toString();
    } catch (IOException e) {
        Toast.makeText(context, "Error loading file: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        return null;
    }
}

public static boolean exportToText(Context context, Poem poem) {
    return saveToFile(context, poem);
}

public static String sharePoem(Poem poem) {
    StringBuilder shareText = new StringBuilder();
    shareText.append("Title: ").append(poem.getTitle()).append("\n\n");
    shareText.append(poem.getContent()).append("\n\n");
    shareText.append("Created with VersePad");
    return shareText.toString();
}

private static String sanitizeFileName(String fileName) {
    return fileName.replaceAll("[^a-zA-Z0-9._-]", "_");
}

private static String formatDate(Date date) {
    SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.getDefault());
    return formatter.format(date);
}

public static boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    return Environment.MEDIA_MOUNTED.equals(state);
}

public static boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    return Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
}

}

activity_main.xml

<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:background="?android:attr/windowBackground"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:menu="@menu/bottom_navigation" />

fragment_editor.xml

<EditText
    android:id="@+id/edit_title"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:hint="@string/hint_title"
    android:textSize="18sp"
    android:textStyle="bold"
    android:background="@android:color/transparent"
    android:padding="8dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

<View
    android:id="@+id/divider"
    android:layout_width="0dp"
    android:layout_height="1dp"
    android:background="@android:color/darker_gray"
    android:layout_marginTop="8dp"
    app:layout_constraintTop_toBottomOf="@+id/edit_title"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

<EditText
    android:id="@+id/edit_content"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:hint="@string/hint_content"
    android:gravity="top"
    android:textSize="16sp"
    android:background="@android:color/transparent"
    android:padding="8dp"
    android:scrollbars="vertical"
    android:inputType="textMultiLine"
    app:layout_constraintTop_toBottomOf="@+id/divider"
    app:layout_constraintBottom_toTopOf="@+id/stats_container"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

<LinearLayout
    android:id="@+id/stats_container"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp"
    android:background="@color/light_gray"
    app:layout_constraintBottom_toTopOf="@+id/recycler_suggestions"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent">

    <TextView
        android:id="@+id/text_word_count"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/words_0"
        android:textSize="12sp"
        android:textColor="@color/dark_gray" />

    <TextView
        android:id="@+id/text_line_count"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/lines_0"
        android:textSize="12sp"
        android:textColor="@color/dark_gray"
        android:gravity="end" />

</LinearLayout>

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler_suggestions"
    android:layout_width="0dp"
    android:layout_height="60dp"
    android:background="@color/suggestion_background"
    android:padding="8dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

fragment_analysis.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:cardCornerRadius="8dp"
        app:cardElevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/meter_analysis"
                android:textSize="18sp"
                android:textStyle="bold"
                android:textColor="@color/primary_text"
                android:layout_marginBottom="8dp" />

            <TextView
                android:id="@+id/text_meter_analysis"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/no_content_to_analyze"
                android:textSize="14sp"
                android:fontFamily="monospace"
                android:textColor="@color/secondary_text" />

        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:cardCornerRadius="8dp"
        app:cardElevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/rhyme_analysis"
                android:textSize="18sp"
                android:textStyle="bold"
                android:textColor="@color/primary_text"
                android:layout_marginBottom="8dp" />

            <TextView
                android:id="@+id/text_rhyme_scheme"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/no_content_to_analyze"
                android:textSize="14sp"
                android:fontFamily="monospace"
                android:textColor="@color/secondary_text" />

        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:cardCornerRadius="8dp"
        app:cardElevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/phonetic_analysis"
                android:textSize="18sp"
                android:textStyle="bold"
                android:textColor="@color/primary_text"
                android:layout_marginBottom="8dp" />

            <TextView
                android:id="@+id/text_phonetic_analysis"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/no_content_to_analyze"
                android:textSize="14sp"
                android:fontFamily="monospace"
                android:textColor="@color/secondary_text" />

        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:cardCornerRadius="8dp"
        app:cardElevation="4dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/structure_analysis"
                android:textSize="18sp"
                android:textStyle="bold"
                android:textColor="@color/primary_text"
                android:layout_marginBottom="8dp" />

            <TextView
                android:id="@+id/text_structure_analysis"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/no_content_to_analyze"
                android:textSize="14sp"
                android:fontFamily="monospace"
                android:textColor="@color/secondary_text" />

        </LinearLayout>
    </com.google.android.material.card.MaterialCardView>

</LinearLayout>

item_suggestion.xml

<TextView
    android:id="@+id/text_suggestion"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingStart="12dp"
    android:paddingEnd="12dp"
    android:paddingTop="6dp"
    android:paddingBottom="6dp"
    android:textSize="14sp"
    android:textColor="@color/suggestion_text"
    android:singleLine="true"
    android:background="?android:attr/selectableItemBackground" />

item_poem.xml

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/text_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Untitled"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="@color/primary_text"
        android:layout_marginBottom="4dp" />

    <TextView
        android:id="@+id/text_preview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Poem preview..."
        android:textSize="14sp"
        android:textColor="@color/secondary_text"
        android:maxLines="3"
        android:ellipsize="end"
        android:layout_marginBottom="8dp" />

    <TextView
        android:id="@+id/text_date"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Jan 01, 2025"
        android:textSize="12sp"
        android:textColor="@color/tertiary_text"
        android:gravity="end" />

</LinearLayout>

strings.xml

VersePad

<!-- Navigation -->
<string name="nav_editor">Editor</string>
<string name="nav_analysis">Analysis</string>
<string name="nav_library">Library</string>

<!-- Menu -->
<string name="action_save">Save</string>
<string name="action_settings">Settings</string>
<string name="action_export">Export</string>
<string name="action_share">Share</string>
<string name="action_delete">Delete</string>

<!-- Editor -->
<string name="hint_title">Enter poem title...</string>
<string name="hint_content">Write your poem here...</string>
<string name="words_0">Words: 0</string>
<string name="lines_0">Lines: 0</string>

<!-- Analysis -->
<string name="meter_analysis">Meter Analysis</string>
<string name="rhyme_analysis">Rhyme Analysis</string>
<string name="phonetic_analysis">Phonetic Analysis</string>
<string name="structure_analysis">Structure Analysis</string>
<string name="no_content_to_analyze">No content to analyze. Please write something in the editor first.</string>

<!-- Messages -->
<string name="poem_saved">Poem saved successfully</string>
<string name="poem_deleted">Poem deleted</string>
<string name="confirm_delete">Are you sure you want to delete this poem?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="cancel">Cancel</string>
<string name="ok">OK</string>

<!-- Errors -->
<string name="error_saving">Error saving poem</string>
<string name="error_loading">Error loading poem</string>
<string name="error_permission">Storage permission required</string>

<!-- Suggestions -->
<string name="suggestions_rhymes">Rhymes</string>
<string name="suggestions_synonyms">Synonyms</string>
<string name="suggestions_similar">Similar</string>

<!-- Export -->
<string name="export_success">Poem exported successfully</string>
<string name="export_failed">Failed to export poem</string>
<string name="share_poem">Share Poem</string>

colors.xml

#6200EE #3700B3 #03DAC6 #018786

<!-- Background Colors -->
<color name="background">#FFFFFF</color>
<color name="surface">#FFFFFF</color>
<color name="error">#B00020</color>

<!-- Text Colors -->
<color name="primary_text">#212121</color>
<color name="secondary_text">#757575</color>
<color name="tertiary_text">#9E9E9E</color>
<color name="on_primary">#FFFFFF</color>
<color name="on_secondary">#000000</color>
<color name="on_background">#000000</color>
<color name="on_surface">#000000</color>
<color name="on_error">#FFFFFF</color>

<!-- Custom Colors -->
<color name="light_gray">#F5F5F5</color>
<color name="dark_gray">#616161</color>
<color name="accent">#FF4081</color>

<!-- Suggestion Colors -->
<color name="suggestion_background">#FAFAFA</color>
<color name="suggestion_card">#E8F5E8</color>
<color name="suggestion_text">#2E7D32</color>

<!-- Status Colors -->
<color name="success">#4CAF50</color>
<color name="warning">#FF9800</color>
<color name="info">#2196F3</color>

<!-- Rhyme Colors -->
<color name="rhyme_a">#FFCDD2</color>
<color name="rhyme_b">#C8E6C9</color>
<color name="rhyme_c">#BBDEFB</color>
<color name="rhyme_d">#F8BBD9</color>
<color name="rhyme_e">#FFECB3</color>

styles.xml

<!-- No Action Bar theme for splash screen -->
<style name="AppTheme.NoActionBar" parent="AppTheme">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

<!-- Editor specific styles -->
<style name="EditorTitle">
    <item name="android:textSize">20sp</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textColor">@color/primary_text</item>
    <item name="android:padding">12dp</item>
</style>

<style name="EditorContent">
    <item name="android:textSize">16sp</item>
    <item name="android:textColor">@color/primary_text</item>
    <item name="android:lineSpacingExtra">4dp</item>
    <item name="android:padding">12dp</item>
</style>

<!-- Analysis styles -->
<style name="AnalysisTitle">
    <item name="android:textSize">18sp</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textColor">@color/primary</item>
    <item name="android:layout_marginBottom">8dp</item>
</style>

<style name="AnalysisContent">
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">@color/secondary_text</item>
    <item name="android:fontFamily">monospace</item>
    <item name="android:lineSpacingExtra">2dp</item>
</style>

<!-- Button styles -->
<style name="PrimaryButton" parent="Widget.MaterialComponents.Button">
    <item name="backgroundTint">@color/primary</item>
    <item name="android:textColor">@color/on_primary</item>
    <item name="cornerRadius">8dp</item>
</style>

<style name="SecondaryButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
    <item name="strokeColor">@color/primary</item>
    <item name="android:textColor">@color/primary</item>
    <item name="cornerRadius">8dp</item>
</style>

<!-- Card styles -->
<style name="AnalysisCard">
    <item name="cardCornerRadius">12dp</item>
    <item name="cardElevation">4dp</item>
    <item name="android:layout_margin">8dp</item>
    <item name="cardBackgroundColor">@color/surface</item>
</style>

<style name="SuggestionChip">
    <item name="chipBackgroundColor">@color/suggestion_card</item>
    <item name="android:textColor">@color/suggestion_

To replace the textual dictionary and thesaurus files with libraries, you can use the following Java libraries that provide dictionary and thesaurus functionality:

1. Replace dictionary.txt with a Library

For dictionary functionality (definitions, parts of speech, etc.), you can use: - WordNet (via JAWS or JWI): A lexical database for English. - Library: JAWS (Java API for WordNet Searching) - Example:

import net.sf.extjwnl.dictionary.Dictionary;

public class DictionaryService {
    private Dictionary dictionary;

    public DictionaryService(Context context) {
        try {
            this.dictionary = Dictionary.getDefaultResourceInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getDefinition(String word) {
        try {
            IndexWord indexWord = dictionary.lookupIndexWord(POS.NOUN, word);
            if (indexWord != null) {
                Synset[] senses = indexWord.getSenses();
                return senses[0].getGloss(); // Return the first definition
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

2. Replace thesaurus.txt with a Library

For thesaurus functionality (synonyms, antonyms), you can use: - WordNet (same as above): Provides synonyms and antonyms. - Example:

public List<String> getSynonyms(String word) {
    List<String> synonyms = new ArrayList<>();
    try {
        IndexWord indexWord = dictionary.lookupIndexWord(POS.NOUN, word);
        if (indexWord != null) {
            for (Synset synset : indexWord.getSenses()) {
                for (Word w : synset.getWords()) {
                    synonyms.add(w.getLemma());
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return synonyms;
}

3. Replace cmudict.txt with a Library

For phonetic and rhyming functionality, you can use: - CMU Pronouncing Dictionary (via a Java wrapper): - Library: cmudict-java - Example:

import edu.cmu.sphinx.cmu.dict.CMUDict;

public class RhymeService {
    private CMUDict cmuDict;

    public RhymeService(Context context) {
        this.cmuDict = new CMUDict();
    }

    public String getPhonetic(String word) {
        return cmuDict.getPhonemes(word);
    }
}

Updated build.gradle (Module: app)

Add the dependencies for these libraries:

dependencies {
    // WordNet (for dictionary and thesaurus)
    implementation 'net.sf.extjwnl:extjwnl:2.0.2'
    implementation 'net.sf.extjwnl:extjwnl-data-wn31:2.0.2'

    // CMU Pronouncing Dictionary (for phonetics and rhymes)
    implementation 'edu.cmu.sphinx:cmudict:1.0.0'

    // Other existing dependencies...
}

Key Changes:

  1. Remove the assets/ files (dictionary.txt, thesaurus.txt, cmudict.txt) since they are no longer needed.
  2. Update DictionaryService, ThesaurusService, and RhymeService to use the libraries instead of file parsing.
  3. Ensure offline functionality by bundling the required data files with the app (WordNet data files can be included in assets/ if needed).

Benefits:

  • No manual file parsing: Libraries handle the data loading and querying.
  • Better accuracy: WordNet and CMU Dict are well-maintained resources.
  • Scalability: Easier to extend functionality (e.g., adding more linguistic features).

Let me know if you'd like help implementing any of these changes!