Fixed the update card reappearing for a moment after tapping it: the tap re-ran the status check and republished NeedsUpdate before the download service reported Loading, so the card flashed back in that gap. Fixed the update card never returning after cancelling a download: both the Cancelled branch and cancelDownload() forced the status to UpToDate, and the card's tap is the only thing that re-evaluates it. The status is now re-derived on the Cancelled to Idle transition, once the partially downloaded archive - which carries today's date and would otherwise be read as a current dictionary - has been cleaned up. Fixed the download progress bar staying at 0% far too long: progress was published in 3-percentage-point buckets and then scaled by the download stage weight, so the bar only moved after about 7% of the archive had transferred. Rebuilt the search field on the Material 3 TextField instead of a hand-rolled BasicTextField inside a Surface, so it picks up the standard container, cursor and icon treatment. Dictionary entry titles in the search field, the results list and the article bar now share a dictionaryTitleLarge style that keeps the font padding, so their line height lines up with the Material 3 text field.
ReSearch
A fast Android dictionary reader for DSL-format dictionaries.
Screenshots • Features • Architecture • Pipeline • Build
ReSearch is an Android dictionary reader for DSL-format dictionaries. It can download, import, index, and search multiple dictionaries.
The app stores dictionaries as DictZip files and builds compact binary indexes for fast offline headword search.
Screenshots
Features
- Fast headword search across multiple DSL dictionaries.
- Import local
.dsl,.dsl.gz, and.dsl.dzdictionary files. - Download dictionaries from configured URLs.
- Automatic extraction, DictZip compression, and indexing.
- Offline search after dictionaries are indexed.
- Random-access article loading from DictZip dictionaries.
- Background processing with progress shown in Settings and notifications.
- Dictionary management with enable, disable, update, and delete actions.
- Material 3 interface with light, dark, and system themes.
- English and Russian localization.
- About screen with open source license information.
Architecture
ReSearch is an Android app written in Kotlin and Jetpack Compose.
com.example.research/
├── common/ # shared progress, UI, file, archive, notification utilities
├── core/ # domain models, source validation, operation results
├── data/ # DictZip, DSL parsing, indexing, paging, repositories
├── feature/ # search, dictionary download, local import workflows
└── ui/ # Compose screens, article view, settings, navigation
State is exposed through Kotlin Flow and collected by Compose screens. Long-running work uses coroutines and foreground services where Android requires visible progress.
Dictionary Pipeline
ReSearch supports both downloaded and locally imported dictionaries.
Download flow:
- Resolve enabled dictionary source URLs.
- Download dictionary archives with OkHttp.
- Report progress through a foreground notification and Settings UI.
- Extract archives when needed.
- Convert raw DSL files to DictZip.
- Build missing indexes.
Import flow:
- Copy user-selected files into the app dictionary directory.
- Extract archives or convert existing DSL files to DictZip.
- Build indexes after import.
- Refresh the dictionary list as dictionaries become available.
Indexed dictionaries remain usable offline.
Indexing And Search
The indexer reads DSL files while preserving byte offsets for article lookup. It groups alternate headwords that point to the same article body, normalizes searchable words, and writes a compact binary .idx file.
For small dictionaries, entries can be sorted in memory. For large dictionaries, ReSearch uses external sorting to keep memory usage bounded.
The sparse table lets search jump into the index without scanning the full file. Article offsets point back into the DictZip dictionary, allowing ReSearch to load article text by byte range instead of reading the whole dictionary into memory.
Responsiveness
Dictionary processing can involve large files, so heavy work runs off the main thread:
- download, extraction, compression, indexing, and file operations use background dispatchers;
- indexing is serialized to avoid CPU and I/O contention;
- progress updates are throttled before reaching Compose;
- oversized article blocks are split into lazy-list chunks so reference-heavy articles render without freezing a frame on text measurement;
- orientation changes are handled without Activity recreation;
- memory pressure triggers cache trimming for indexes, search results, article charset data, and DictZip chunks;
- out-of-memory conditions on critical paths are handled explicitly where possible.
The main search screen remains usable as long as at least one indexed dictionary is available.
Tech Stack
- Kotlin 2.4.0
- Jetpack Compose (BOM 2026.06.01)
- Material 3
- Coroutines and Flow 1.11.0
- DataStore Preferences 1.2.1
- Paging 3.5.0
- OkHttp 5.4.0
- kotlinx.serialization 1.11.0
- AboutLibraries 15.0.3 metadata generation
- Android Gradle Plugin 9.2.1
Build
./gradlew :app:assembleRelease
The release APK targets arm64-v8a. English and Russian resources are packaged together.
License
MIT

