Wholphin/DEVELOPMENT.md
Ray 8f98526558
CI & Docs updates (#1284)
Increases the heap size for gradle builds in CI

Also adds some docs about where/how app settings are stored.

No user facing changes
2026-04-20 17:41:31 -04:00

5.2 KiB

Wholphin developer's guide

See also the Contributing guide for general information on contributing to the project.

Overview

This project is an Android TV client for Jellyfin. It is written in Kotlin and uses the official Jellyfin Kotlin SDK to interact with the server.

The app is a single Activity (MainActivity) with MVVM architecture.

The app uses:

Getting started

We follow GitHub's fork & pull request model for contributions.

After forking and cloning your fork, you can import the project into Android Studio.

Development environment

It is recommended to use a recent version of Android Studio. Make sure the version is compatible with Wholphin's AGP version.

Code formatting should follow ktlint's rules. Find the ktlint version in .pre-commit-config.yaml. Optionally, install the ktlint plugin in Android Studio to run automatically. Configure the version in Settings->Tools->KtLint->Ruleset Version.

Also, it's recommend to add an extra ruleset jar for Compose-specific KtLint: https://mrmans0n.github.io/compose-rules/ktlint/#using-with-ktlint-cli-or-the-ktlint-intellij-plugin

Also setup pre-commit which will run ktlint as well on each commit, plus check for other common issues.

Extensions

Wholphin uses several native components for extra playback compatibility. This includes Media3 ffmpeg/av1 decoders and libmpv. These extensions are not required to build the app, but without them some functionality will not work.

If you want to include these in a local build, see the instructions here for configuring the repository.

You can also build the extensions locally from https://github.com/damontecres/wholphin-extensions and include them in app/libs. The gradle build dependency resolution prefers these local files over fetching from the remote maven registry.

Code organization

Code is split into several packages:

  • data - app-specific data models and services
  • preferences - Non-UI related code for user settings and preferences
  • services - hilt injectable services often used by ViewModels for API calls
  • ui - User interface code and ViewModels
  • util - Utility classes and functions

Settings

There are a few different ways user settings are stored:

  1. AppPreferences via DataStore
  2. Room database
  3. Key-Value DataStore

App preferences

These are generally settings that apply across the whole app regardless of the currently active user.

The AppPreferences object can be retrieved from the UserPreferencesService or directly via injecting DataStore<AppPreferences>.

The AppPreference (note the s differences) objects are used to create the UI for configuring settings using the composable functions in com.github.damontecres.wholphin.ui.preferences.

How to add a new app setting
  1. Add entry in WholphinDataStore.proto & build to generate classes
  2. Add new AppPreference object in AppPreference.kt
  3. Add new object to a PreferenceGroup (listed in AppPreference.kt)
  4. Update AppPreferencesSerializer to set the default value for new installs
  5. If needed, update AppUpgradeHandler to set the default value for app upgrades
    • Since preferences use proto3, the default values are zero, false, or the first enum, so only need this step if the default value is different

Room settings

These are settings were generally are applied per user. They are stored in several different tables. The entities are defined in com.github.damontecres.wholphin.data.model.

Additionally, all server and user info for both Jellyfin and Seerr are stored in Room tables.

Some examples are:

  • JellyfinServer
  • JellyfinUser
  • SeerrServer
  • LibraryDisplayInfo
  • ItemPlayback

Some of the models, such as GetItemsFilter or ViewOptions, are stored as JSON in a column instead of defined entities. This should be used sparingly.

Key-value settings

These settings are similar to Room ones and are usually stored per user. These can be accessed via KeyValueService. The values are @Serializable objects saved as JSON.

This should be used sparingly.