A comprehensive guide for developers creating plugins for IReader.
New to plugin development? Check out the Plugin Creation Guide for step-by-step tutorials with complete code examples.
- Introduction
- Project Purpose
- Architecture Overview
- Plugin Types
- Getting Started
- Advanced Features
- Best Practices
- Testing & Debugging
- Publishing
- Monetization
IReader is a cross-platform novel/book reader application built with Kotlin Multiplatform. The plugin system allows developers to extend IReader's functionality without modifying the core application.
- Theme Plugins: Custom color schemes, typography, backgrounds
- TTS Plugins: Text-to-speech engines (cloud or local)
- Translation Plugins: Translation services for reading in different languages
- Feature Plugins: Custom features like dictionaries, note-taking, AI assistants
- AI Plugins: Summarization, character analysis, Q&A about books
- Extensibility: Users can customize their reading experience
- Community: Developers can contribute without core access
- Modularity: Features can be added/removed independently
- Monetization: Developers can earn from premium plugins
- Innovation: Experiment with new features safely
- Provide a stable, well-documented API
- Enable rich plugin capabilities while maintaining security
- Support cross-platform (Android, iOS, Desktop)
- Foster a healthy plugin ecosystem
┌─────────────────────────────────────────────────────────────┐
│ IReader App │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Reader │ │ Library │ │ Plugin Manager │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Plugin API │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Theme │ │ TTS │ │ Translate│ │ Feature │ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │ Plugin │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Plugin Sandbox │
│ • Resource limits • Permission control • Isolation │
└─────────────────────────────────────────────────────────────┘
| Component | Description |
|---|---|
| Plugin API | Interfaces and utilities for plugin development |
| Plugin Manager | Loads, enables, disables plugins |
| Plugin Sandbox | Security and resource isolation |
| Plugin Registry | Tracks installed plugins |
| Plugin Marketplace | Discovery and distribution |
Customize the visual appearance of IReader.
@IReaderPlugin
class OceanTheme : ThemePlugin {
override fun getColorScheme(isDark: Boolean) = ThemeColorScheme(
primary = if (isDark) Color(0xFF64B5F6) else Color(0xFF1976D2),
background = if (isDark) Color(0xFF0D1B2A) else Color(0xFFF5F5F5),
// ... more colors
)
}Use Cases:
- Dark/light themes
- High contrast themes
- Seasonal themes
- Brand themes
Add text-to-speech capabilities.
@IReaderPlugin
class CloudTTS : TTSPlugin {
override suspend fun speak(text: String, voice: VoiceConfig): Result<AudioStream> {
// Call TTS API and return audio stream
}
override fun getAvailableVoices() = listOf(
VoiceModel("en-us-1", "English (US)", "en-US", VoiceGender.FEMALE)
)
}Use Cases:
- Cloud TTS (Google, Amazon, Azure)
- Local TTS engines
- Custom voice models
- Multi-language support
Enable reading in different languages.
@IReaderPlugin
class DeepLTranslation : TranslationPlugin {
override suspend fun translate(text: String, from: String, to: String): Result<String> {
// Call translation API
}
}Use Cases:
- Cloud translation (DeepL, Google, etc.)
- Offline translation
- Specialized dictionaries
- Language learning tools
Add custom functionality to the reader.
@IReaderPlugin
class DictionaryPlugin : FeaturePlugin {
override fun getMenuItems() = listOf(
PluginMenuItem("lookup", "Look up word", "dictionary")
)
override fun onReaderContext(context: ReaderContext): PluginAction? {
context.selectedText?.let { word ->
return PluginAction.Navigate("dictionary/$word")
}
return null
}
}Use Cases:
- Dictionary lookup
- Note-taking
- Bookmarks with tags
- Reading statistics
- Social sharing
Intelligent text processing capabilities.
@IReaderPlugin
class SummarizerPlugin : AIPlugin {
override val aiCapabilities = listOf(
AICapability.SUMMARIZATION,
AICapability.CHARACTER_ANALYSIS
)
override suspend fun summarize(text: String, options: SummarizationOptions): AIResult<String> {
// Use local or cloud AI
}
}Use Cases:
- Chapter/book summarization
- Character analysis
- Q&A about the book
- Content recommendations
- Sentiment analysis
- JDK 17 or higher
- Gradle 8.10+
- IDE: IntelliJ IDEA or Android Studio
git clone https://github.com/IReaderorg/IReader-plugins.git
cd IReader-pluginsmkdir -p plugins/features/my-plugin/src/main/kotlin// plugins/features/my-plugin/build.gradle.kts
plugins {
id("ireader-plugin")
}
pluginConfig {
id.set("com.yourname.my-plugin")
name.set("My Awesome Plugin")
version.set("1.0.0")
versionCode.set(1)
description.set("Does something awesome")
author.set("Your Name")
type.set(PluginType.FEATURE)
permissions.set(listOf(Permission.READER_CONTEXT))
}// plugins/features/my-plugin/src/main/kotlin/MyPlugin.kt
package com.yourname.myplugin
import ireader.plugin.api.*
import ireader.plugin.annotations.*
@IReaderPlugin
@PluginInfo(
id = "com.yourname.my-plugin",
name = "My Awesome Plugin",
version = "1.0.0",
versionCode = 1,
description = "Does something awesome",
author = "Your Name"
)
class MyPlugin : FeaturePlugin {
override val manifest = PluginManifest(
id = "com.yourname.my-plugin",
name = "My Awesome Plugin",
version = "1.0.0",
versionCode = 1,
description = "Does something awesome",
author = PluginAuthor("Your Name"),
type = PluginType.FEATURE,
permissions = listOf(PluginPermission.READER_CONTEXT),
minIReaderVersion = "1.0.0",
platforms = listOf(Platform.ANDROID, Platform.DESKTOP)
)
override fun initialize(context: PluginContext) {
// Setup your plugin
}
override fun cleanup() {
// Release resources
}
override fun getMenuItems() = listOf(
PluginMenuItem("action1", "Do Something")
)
override fun getScreens() = emptyList<PluginScreen>()
override fun onReaderContext(context: ReaderContext): PluginAction? = null
}# Build your plugin
./gradlew :plugins:features:my-plugin:assemble
# Build all plugins
./gradlew buildAllPlugins
# Generate repository
./gradlew repoPlugins can access reading statistics:
class StatsPlugin : FeaturePlugin {
private lateinit var analyticsApi: ReadingAnalyticsApi
override fun initialize(context: PluginContext) {
analyticsApi = context.getApi(ReadingAnalyticsApi::class)
}
suspend fun showStats() {
val stats = analyticsApi.getOverallStats()
val streak = analyticsApi.getCurrentStreak()
// Display to user
}
}Track and display character information:
class CharacterPlugin : FeaturePlugin {
private lateinit var characterApi: CharacterDatabaseApi
override fun initialize(context: PluginContext) {
characterApi = context.getApi(CharacterDatabaseApi::class)
}
suspend fun showCharacters(bookId: Long) {
val characters = characterApi.getCharactersForBook(bookId)
val relationships = characters.flatMap {
characterApi.getRelationships(it.id)
}
// Display character map
}
}Combine plugins into workflows:
// In IReader app, users can create pipelines:
// Translate → TTS (translate text, then read aloud)
// Summarize → Translate (summarize, then translate summary)Plugins can communicate via events:
class MyPlugin : FeaturePlugin, EventHandler {
override fun getSubscribedEventTypes() = setOf(
CommonEventTypes.TEXT_SELECTED,
CommonEventTypes.CHAPTER_CHANGED
)
override suspend fun handleEvent(event: PluginEvent) {
when (event.eventType) {
CommonEventTypes.TEXT_SELECTED -> {
val text = event.payload["text"]
// Handle selected text
}
}
}
}- Lazy Loading: Don't load resources until needed
- Caching: Cache API responses and computed results
- Background Work: Use coroutines for heavy operations
- Memory: Release resources in
cleanup()
- Minimal Permissions: Only request what you need
- Input Validation: Validate all user input
- Secure Storage: Use PluginContext for sensitive data
- API Keys: Never hardcode API keys
- Responsive: Show loading states
- Error Handling: Graceful error messages
- Offline Support: Handle network failures
- Localization: Support multiple languages
- Documentation: Comment public APIs
- Testing: Write unit tests
- Versioning: Follow semantic versioning
- Changelog: Document changes
# Run tests
./gradlew :plugins:features:my-plugin:test
# Check for issues
./gradlew :plugins:features:my-plugin:checkEnable hot reload in IReader for faster development:
- Enable Developer Mode in IReader settings
- Point to your local plugin directory
- Changes reload automatically
// Use logging
context.log("Debug message", LogLevel.DEBUG)
// Check plugin state
context.log("State: ${myState}", LogLevel.INFO)./gradlew :plugins:features:my-plugin:assembleRelease./gradlew repoUpload repo/ directory to:
- GitHub Pages
- Your own server
- Any static hosting
Users add your repository in: IReader → Community Hub → Plugin Repositories → Add
Default model. Great for building reputation.
pluginConfig {
monetization.set(PluginMonetizationType.PREMIUM)
price.set(4.99)
currency.set("USD")
trialDays.set(7)
}Basic features free, premium features paid.
pluginConfig {
monetization.set(PluginMonetizationType.FREEMIUM)
}
// In your plugin
fun isPremiumFeature(): Boolean {
return context.isPurchased() || context.isInTrial()
}- Plugin Creation Guide - Step-by-step tutorials
- Project Overview - What this project is about
- Quick Reference - Cheat sheet for common tasks
- AI Context - Context for AI assistants
- Example Plugins - Reference implementations
- IReader Documentation
- Discord Community
- GitHub Issues
Plugins are licensed under MPL 2.0 unless otherwise specified.