Skip to content

feat(settings): add custom theme import and dynamic theming support - #133

Merged
LandWarderer2772 merged 2 commits into
AppFuton:develfrom
bhoomik-codes:feature/custom-theme-support
Nov 2, 2026
Merged

LandWarderer2772 merged 2 commits into
AppFuton:develfrom
bhoomik-codes:feature/custom-theme-support

Conversation

@bhoomik-codes

Copy link
Copy Markdown

Summary

Adds support for creating, importing, managing, and applying custom user themes via JSON files (Idle Fantasy Material 3 compatible format).

Users can now import custom palette files, select their imported themes from a dedicated dialog or the existing color scheme carousel, and have their chosen seed colors dynamically applied throughout the application on Android 6.0+ (API 23+).


Why this is needed

  • Personalization: Users were previously restricted to built-in presets (Miku, Asuka, Totoro, etc.) or system Monet (Android 12+ only).
  • Universal Dynamic Theming: Allows users on older Android versions (API 23–31) to enjoy content-based Material dynamic theming derived from their custom theme seed colors using Material Color Utilities without requiring OS Monet engine support.
  • Interoperability: Supports importing JSON palette definitions from community tools.

What was done

  1. JSON Parsing & Validation (ImportedTheme.kt):
    • Implemented a parser validating schema, size limits (≤ 256 KB), color format (hex/argb), base mode (light / dark), and extraction of primary seed and contrast colors.
  2. Settings & In-Memory Caching (AppSettings.kt):
    • Added importedThemes, selectedCustomThemeId, importCustomThemes(), and deleteCustomTheme().
    • Built with an in-memory @Volatile cache to avoid re-parsing JSON on every property access, preserving strict performance requirements (per CONTRIBUTING.md).
  3. Application Theming (BaseActivity.kt):
    • Applied custom seed color dynamic theming using DynamicColors.applyToActivityIfAvailable with DynamicColorsOptions.Builder().setContentBasedSource(...).
    • Operates universally on Android 6.0–14+ without gating behind isDynamicColorAvailable().
  4. Theme Preference & Management UI (ThemeChooserPreference.kt, AppearanceSettingsFragment.kt):
    • Added preferences to import theme files, switch between custom themes, and delete themes.
    • Handled file reading asynchronously on Dispatchers.IO using runCatchingCancellable to prevent UI thread blockage/ANRs during cloud storage imports.
    • Handled proper activity recreation (postRestart()) when switching between custom themes.
    • Preserved card preview shapes in ThemeChooserPreference by tinting shapes directly with the seed color rather than flattening the card background.

Files Changed

File Changes
app/src/main/kotlin/.../core/prefs/ImportedTheme.kt [New] Theme model and JSON parser for custom palette files.
app/src/main/kotlin/.../core/prefs/AppSettings.kt Persistent storage, in-memory cache, and management methods for imported custom themes.
app/src/main/kotlin/.../core/prefs/ColorScheme.kt Added CUSTOM entry to the available color schemes list when custom themes exist.
app/src/main/kotlin/.../core/ui/BaseActivity.kt Universal content-based theming via DynamicColorsOptions.setContentBasedSource(...).
app/src/main/kotlin/.../settings/AppearanceSettingsFragment.kt UI for file picker import, selection dialog, deletion, and coroutine-based async reading.
app/src/main/kotlin/.../settings/utils/ThemeChooserPreference.kt Preview rendering with shape tinting and refresh() method.
app/src/main/res/values/strings.xml String resources for custom theme management dialogs and actions.
app/src/main/res/values/themes.xml Added ThemeOverlay.Futon.Custom.Light and ThemeOverlay.Futon.Custom.Dark.
app/src/main/res/xml/pref_appearance.xml Added preference entries for managing, importing, and deleting custom themes.

Verification

  • Tested compilation via ./gradlew compileDebugKotlin (Build succeeded).
  • Zero new external dependencies added (complies with CONTRIBUTING.md APK size rule).
  • File I/O executed on Dispatchers.IO (complies with performance rule).
  • Preserved docstrings and existing coding standards.

…amic color gating

- Gate CUSTOM color scheme behind DynamicColors.isDynamicColorAvailable()
- Dynamically resolve custom theme overlay based on system night mode
- Share imported themes cache in AppSettings companion and fix deletion fallback
- Support top-level metadata, role aliases, and multiple hex formats in theme parser
- Prevent coroutine context leaks in AppearanceSettingsFragment
- Fix preview indicator shape tinting in ThemeChooserPreference
- Add unit tests for theme import parsing and roundtripping
@LandWarderer2772

Copy link
Copy Markdown
Member

Tested on my phone running android 17, works as expected.

@bhoomik-codes

Copy link
Copy Markdown
Author

So, you are going to merge it ?

@LandWarderer2772
LandWarderer2772 merged commit fb96eb9 into AppFuton:devel Nov 2, 2026
1 check failed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Futon project Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants