Skip to content

About

A Compose-first “What’s New” library for Android with localization, themes, and version tracking.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

WhatsNewKit Android

English · 简体中文 · 한국어 · 日本語

Android 21+ Kotlin Jetpack Compose License

A Compose-first Android library for polished “What’s New” release notes. It matches releases by semantic version, remembers dismissed versions, supports runtime localization and themes, and works in both Compose and View/Fragment applications.

Inspired by SvenTiigi/WhatsNewKit. This is an independent Android implementation of the public concepts and behavior of the MIT-licensed original project.

Screenshots

Animated demo of theme switching, localization, and all presentation styles

Theme and locale switching · Bottom sheet · Dialog · Full screen · Fragment API · Automatic presentation reset · MP4

Sample app in Simplified Chinese with the light theme What's New bottom sheet in Simplified Chinese with the dark theme What's New bottom sheet in Arabic with right-to-left layout

Runtime locale switching · Light and dark themes · Arabic RTL layout

Highlights

  • Automatic or manual presentation.
  • Material 3 bottom sheet, dialog, and full-screen styles.
  • Exact semantic-version matching with patch fallback (1.2.7 → 1.2.0).
  • DataStore, SharedPreferences, in-memory, or custom version stores.
  • Android string resources, format arguments, and localized Markdown.
  • Runtime per-app locale switching with AppCompat.
  • System, light, and dark themes; Android 12+ dynamic color; custom ColorScheme support.
  • Drawable, ImageVector, or custom composable feature images.
  • Primary, URL, suspend custom, and custom-presentation actions.
  • Haptic feedback and localized accessibility descriptions.
  • Compose API plus WhatsNewDialogFragment for View/Fragment apps.
  • Configurable spacing, sizing, dismissal behavior, and scroll indicators.

Requirements

  • Android minSdk 21 and compileSdk 35
  • JDK 17
  • Kotlin 2.0+
  • Jetpack Compose with Material 3

Installation

Use the module in this repository

dependencies {
    implementation(project(":whatsnewkit"))
}

Publish and consume from Maven Local

./gradlew :whatsnewkit:publishToMavenLocal
// settings.gradle.kts in the consuming project
dependencyResolutionManagement {
    repositories {
        mavenLocal()
        google()
        mavenCentral()
    }
}
dependencies {
    implementation("io.github.whatsnewkit:whatsnewkit:0.1.1")
}

The artifact has not yet been published to Maven Central or another remote registry. Do not use a remote dependency URL until a release channel is configured.

Quick start

1. Add release strings

<!-- app/src/main/res/values/strings.xml -->
<resources>
    <string name="release_title">What’s **New**</string>
    <string name="feature_title">Faster search</string>
    <string name="feature_body">Results now appear instantly.</string>
    <string name="search_icon_description">Search</string>
    <string name="continue_action">Continue</string>
    <string name="learn_more_action">Learn more</string>
</resources>

2. Define releases

val releases = whatsNewCollection {
    release("1.2.0") {
        title = WhatsNewText.markdownResource(R.string.release_title)
        feature(
            titleResourceId = R.string.feature_title,
            subtitleResourceId = R.string.feature_body,
            image = WhatsNewFeatureImage.Vector(
                imageVector = Icons.Rounded.Search,
                contentDescription = WhatsNewText.resource(
                    R.string.search_icon_description,
                ),
            ),
        )
        primaryAction(R.string.continue_action)
        secondaryOpenUrl(
            R.string.learn_more_action,
            "https://example.com/release-notes",
        )
    }
}

3. Present automatically

@Composable
fun App() {
    val context = LocalContext.current
    val environment = remember(context) {
        WhatsNewEnvironment.create(
            context = context,
            whatsNewCollection = releases,
        )
    }

    MaterialTheme {
        AppNavigation()
        WhatsNewHost(
            environment = environment,
            style = WhatsNewPresentationStyle.BottomSheet,
        )
    }
}

WhatsNewEnvironment.create reads the installed app’s versionName. The matching release is shown only if it has not already been dismissed. Dismissing records the version; merely requesting a presentation does not.

Presentation styles

BottomSheet, Dialog, and FullScreen are available. Manual presentation:

var visible by remember { mutableStateOf(false) }

WhatsNewPresentation(
    whatsNew = releases.last(),
    visible = visible,
    onDismissRequest = { visible = false },
    style = WhatsNewPresentationStyle.Dialog,
    versionStore = store, // Checks before display and records after dismissal.
)

When versionStore is set, manual presentation waits for the store check and skips versions already dismissed without briefly flashing the UI. Set skipIfPresented = false for a forced preview, or omit the store to show without checking or recording.

Use the stateless WhatsNewView to embed content in an existing navigation destination.

Version matching

  • Exact matches are preferred.
  • Version suffixes such as 2.1.0-beta03 are accepted.
  • If 2.1.7 has no exact release, the environment tries 2.1.0.
  • Override WhatsNewEnvironment.whatsNew() for rollout flags, remote configuration, audience targeting, or another matching policy.
val testEnvironment = WhatsNewEnvironment(
    currentVersion = WhatsNewVersion(1, 2, 0),
    versionStore = InMemoryWhatsNewVersionStore(),
    whatsNewCollection = releases,
)

Localization

Add translations with standard Android directories such as values-zh-rCN, values-ko, and values-ar. Resource-backed text is resolved against the active locale when composed. The built-in default title/action and sample cover en, zh-CN, zh-TW, ko, ja, es, pt-BR, de, fr, ru, and RTL ar:

WhatsNewText.resource(R.string.feature_body)
WhatsNewText.resource(R.string.welcome_message, userName)
WhatsNewText.markdownResource(R.string.release_title)

The DSL provides resource overloads for title, feature, primaryAction, and secondaryOpenUrl. Image descriptions also accept WhatsNewText, so screen-reader labels can be localized. Pass null only for a purely decorative image.

Runtime language switching:

AppCompatDelegate.setApplicationLocales(
    LocaleListCompat.forLanguageTags("ko"),
)

// Follow the system locale again.
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())

For Arabic and other RTL locales, enable android:supportsRtl="true" on the consuming app. To expose the supported locales in Android 13+ system app-language settings, also provide an Android localeConfig; see the sample manifest and res/xml/locales_config.xml.

For locale persistence on Android 12 and lower, add this service inside <application>:

<service
    android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
    android:enabled="false"
    android:exported="false">
    <meta-data android:name="autoStoreLocales" android:value="true" />
</service>

Theme switching

var mode by rememberSaveable { mutableStateOf(WhatsNewThemeMode.System) }
val themeConfig = WhatsNewThemeConfig(mode = mode, useDynamicColor = true)

WhatsNewHost(environment = environment, themeConfig = themeConfig)

WhatsNewThemeMode supports System, Light, and Dark. Android 12+ dynamic color is enabled by default. Omit themeConfig to inherit the host Material theme.

val brandedTheme = WhatsNewThemeConfig(
    mode = WhatsNewThemeMode.System,
    useDynamicColor = false,
    lightColorScheme = lightColorScheme(primary = BrandBlue),
    darkColorScheme = darkColorScheme(primary = BrandBlueLight),
)

Version stores

val store = DataStoreWhatsNewVersionStore(context)
store.save(WhatsNewVersion(1, 2, 0))
store.hasPresented(WhatsNewVersion(1, 2, 0))
store.remove(WhatsNewVersion(1, 2, 0)) // Reset one version.
store.clear()

Also available: SharedPreferencesWhatsNewVersionStore, InMemoryWhatsNewVersionStore, or a custom WhatsNewVersionStore for Room, Firebase, account sync, or another backend.

Secondary actions

WhatsNewSecondaryAction.OpenUrl(WhatsNewText("Read more"), releaseNotesUrl)

WhatsNewSecondaryAction.Custom(WhatsNewText("Enable feature")) {
    repository.enableFeature()
    dismiss()
}

WhatsNewSecondaryAction.Present(WhatsNewText("Details")) { dismiss ->
    DetailsScreen(onClose = dismiss)
}

View and Fragment applications

WhatsNewDialogFragment.show(
    fragmentManager = supportFragmentManager,
    whatsNew = release,
    versionStore = DataStoreWhatsNewVersionStore(this),
    asBottomSheet = true,
)

Use the suspend WhatsNewDialogFragment.showIfNeeded(...) from a lifecycle coroutine to skip a version already recorded by the store. The existing show(...) API remains the forced-display entry point.

Models may contain callbacks and composables, so Fragment payloads are retained in-process across configuration changes but cannot be reconstructed after process death. Drive automatic presentation again from the Activity after process recreation.

Layout customization

val compactLayout = WhatsNewLayout.Default.copy(
    contentPadding = PaddingValues(20.dp),
    featureListSpacing = 16.dp,
    featureImageSize = 44.dp,
    showsScrollIndicator = false,
    dismissOnClickOutside = true,
)

Colors use Material 3 semantic tokens by default. Releases and actions can override their colors.

Run the sample

  1. Open the repository in Android Studio and select JDK 17.
  2. Wait for Gradle sync, then select the sample run configuration.
  3. Start an API 21+ emulator or connect a device.
  4. Run the app and try language, theme, bottom-sheet, dialog, full-screen, and Fragment modes.
./gradlew :sample:assembleDebug
adb install -r sample/build/outputs/apk/debug/sample-debug.apk

Build and test

./gradlew \
  :whatsnewkit:testDebugUnitTest \
  :whatsnewkit:lintDebug \
  :sample:lintDebug \
  :whatsnewkit:assembleRelease \
  :sample:assembleDebug

Release AAR: whatsnewkit/build/outputs/aar/whatsnewkit-release.aar

GitHub release checklist

  1. Update the version in whatsnewkit/build.gradle.kts.
  2. Update documentation and release notes.
  3. Run the complete build and test command above.
  4. Create an annotated tag such as v0.1.1.
  5. Push the branch and tag to GitHub.
  6. Create a GitHub Release and attach whatsnewkit-release.aar for direct downloads if desired.
  7. Document compatibility, features, fixes, and upgrade steps in the release description.
git tag -a v0.1.1 -m "WhatsNewKit Android 0.1.1"
git push origin main
git push origin v0.1.1

Project structure

whatsnewkit/
├── whatsnewkit/   Android library
├── sample/        Runnable sample app
├── README.md      English documentation
├── README.zh-CN.md
├── README.ko.md
└── README.ja.md

License

Released under the MIT License.

About

A Compose-first “What’s New” library for Android with localization, themes, and version tracking.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages