core
The catalogue, the session, the downloads and the reader — everything the SDK does that isn't drawing a screen of its own.
Take this module alone to build your own interface on top of the catalogue. Everything starts at CGS, which hands out the four services once CGS.init has run:
| Authentication | CGS.auth | Opening a session from your own backend token, and picking the active profile. |
| Content | CGS.content | Publications, issues, articles, videos, media. |
| Downloads | CGS.download | Fetching an issue for offline reading, and following its progress. |
| Reader | CGS.reader | Opening an issue page by page, or an article as text, in a full-screen reader. |
How results are shaped
Three shapes cover the catalogue and the session, and which one you get says how the call behaves:
CgsResult — a one-shot call that either succeeded or failed with a CgsError. Nothing is thrown.
StateFlow<CgsStreamState<T>>— a single item, observed. A failure can still hold the copy last cached, so a transient network problem never has to blank the screen.PaginatedResource — a list. Observe its
state, callloadNextPage()as the user scrolls,refresh()to force a round trip.
A download is an Item of its own, with a state and a progress to follow.
Content follows the session: sign in as another account or switch profile, and every flow restarts against the new session on its own, dropping what it held. Each one is also bound to the CoroutineScope you pass in and stops five seconds after nothing collects it, so nothing keeps running once your screen is gone. Downloads are kept per profile as well: Download.trackedIssues lists the active profile's.
Opening an issue or an article
Every entry point is an open overload — what is being opened is said by the argument, not by the name — and each takes identifiers, so any of them can be called straight from a tap: Reader.open takes an issue id, an ArticleId, or a list of them to swipe through. Each launches a full-screen Activity of its own and returns immediately; the reader fetches what it needs behind its own loading state. An issue already on the device opens offline, one that is not is downloaded on the spot — and kept, as if the download had been started through Download. An article is fetched as it opens, so it needs a connection unless it was opened moments before.
CGS.reader().open(activity, issueId, page = 12)Downloading ahead of time, and following a download's progress, is what CGS.download is for. Reading never requires it.
Every open also takes a theme, the reader's palette. Left out, the reader draws with the engine's own colors; to match your brand, subclass OneReaderColors and override only the colors you care about:
class BrandReaderColors : OneReaderColors() {
override val primary = OneReaderColor.parse("#E10E49")
}
CGS.reader().open(activity, issueId, theme = OneReaderTheme(colors = BrandReaderColors()))The rendering itself is done by the OneReader engine, which this module brings in as a dependency (co.cafeyn:cgs-reader). Its theme — OneReaderTheme, OneReaderColors and OneReaderColor, in com.milibris.onereader.theme — is the only part of it meant to be used; nothing else in it is meant to be called.
Permissions
This module and the engine it depends on add these permissions to your merged manifest, and declare the activities the reader opens along with them, so there is nothing to add on your side:
INTERNET | Every round trip the SDK makes: the session, the catalogue, the downloads. |
ACCESS_NETWORK_STATE | Letting the reader reload an image that failed once the connection comes back. |
WAKE_LOCK, FOREGROUND_SERVICE, FOREGROUND_SERVICE_MEDIA_PLAYBACK | Declared with the engine's read-aloud service. The reader does not offer reading aloud, so nothing uses them. |
All of them are install-time permissions — added by the manifest merger, never requested at runtime.
Packages
Opening and holding a session, and choosing the profile it reads as. You bring a token from your own backend; CgsAuth exchanges it for an SDK session and reports its state.
One small service per content domain — publications, issues, articles, videos, media. They all follow the same shape: get(id, scope) for one item, all(…, scope) for a paginated list — plus Issues.addIns, an issue's add-ins as a paginated list.
CgsError, the sealed hierarchy recoverable failures are reported through — inside a CgsResult, a CgsStreamState or a list's LoadState.Error. A failed download is the exception: it reports Failed, with no error attached. Sealed so a when over it stays exhaustive as the SDK grows.
Where the SDK's diagnostics go. Ships with a Logcat implementation and a silent one; supply your own Logger to route them into your existing stack. The reader's rendering engine logs to Logcat on its own, whichever you pick.