Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ kotlin {
dependencies {
implementation(rmr.infrastructure.publish)
implementation(rmr.infrastructure.android)
implementation(libs.publish.gradlePlugin)
implementation(stack.publish.gradlePlugin)
implementation(stack.gradle.android.cacheFixGradlePlugin)
implementation(stack.kotlin.gradlePlugin)
implementation(stack.kotlin.composeCompiler.gradlePlugin)
Expand Down
5 changes: 1 addition & 4 deletions buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencyResolutionManagement {
}

versionCatalogs {
val version = "2026.02.23" // Keep it in sync with root settings.gradle.kts
val version = "2026.07.10" // Keep it in sync with root settings.gradle.kts
create("rmr") {
from("com.redmadrobot.versions:versions-redmadrobot:$version")
}
Expand All @@ -40,8 +40,5 @@ dependencyResolutionManagement {
create("stack") {
from("com.redmadrobot.versions:versions-stack:$version")
}
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Project.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
object Project {
const val MIN_SDK = 23
const val TARGET_SDK = 36
const val COMPILE_SDK = 36
const val COMPILE_SDK = 37

object BuildTypes {
const val release = "release"
Expand Down
5 changes: 1 addition & 4 deletions buildSrc/src/main/kotlin/convention-publish.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import com.redmadrobot.build.dsl.*
import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.KotlinJvm
import com.vanniktech.maven.publish.SonatypeHost

plugins {
id("com.vanniktech.maven.publish")
}

mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
publishToMavenCentral(automaticRelease = true)
signAllPublications()
// disable Javadoc because of Dokka / JDK 17: "PermittedSubclasses requires ASM9" during compilation
configure(AndroidSingleVariantLibrary(variant = "release", sourcesJar = true, publishJavadocJar = false,))
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/internal/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package internal
internal object Versions {
const val MIN_SDK = 23
const val TARGET_SDK = 36
const val COMPILE_SDK = TARGET_SDK
const val COMPILE_SDK = 37
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.emptyFlow
* application = this,
* plugins = listOf(
* ServersPlugin(preInstalledServers),
* KonfeaturePlugin(interceptor, konfeature),
* KonfeaturePlugin(konfeature = konfeature, config = config),
* )
* )
* ```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@ import com.redmadrobot.debug.core.annotation.DebugPanelInternal
* @see CommonContainer
*/
@DebugPanelInternal
public interface PluginDependencyContainer
public interface PluginDependencyContainer {
/**
* Shared no-op container for plugins that have no dependencies of their own. Return it from
* [com.redmadrobot.debug.core.plugin.Plugin.getPluginContainer] instead of allocating an empty
* anonymous implementation.
*/
public object Empty : PluginDependencyContainer
}
1 change: 1 addition & 0 deletions panel-no-op/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ dependencies {
implementation(stack.okhttp)
implementation(stack.kotlinx.coroutines.android)
implementation(rmr.konfeature)
implementation(rmr.konfeature.ui.noop)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.redmadrobot.debug.plugin.konfeature

import android.content.Context
import com.redmadrobot.konfeature.Logger
import com.redmadrobot.konfeature.builder.KonfeatureBuilder
import com.redmadrobot.konfeature.ui.KonfeatureDebugInterceptor
import com.redmadrobot.konfeature.ui.KonfeatureDebugStore

/**
* No-op counterpart of the `plugin-konfeature` `KonfeatureDebugPanelConfig`, for release builds.
*
* Mirrors the real public API so call sites compile unchanged, but the bundled store and
* interceptor come from `konfeature-ui-noop`: the store holds no overrides and the interceptor
* never overrides a value.
*/
@Suppress("UnusedPrivateProperty")
public class KonfeatureDebugPanelConfig private constructor(
internal val store: KonfeatureDebugStore,
internal val interceptor: KonfeatureDebugInterceptor,
) {
public companion object {
/** Default DataStore file name; accepted only to match the real API. */
public const val DEFAULT_PATH: String = "konfeature_debug.preferences_pb"

/** Returns a no-op config. [context], [path] and [logger] are accepted only for API parity. */
public suspend fun create(
context: Context,
path: String = DEFAULT_PATH,
logger: Logger? = null,
): KonfeatureDebugPanelConfig {
val store = KonfeatureDebugStore.create(
path = context.filesDir.resolve(path).absolutePath,
logger = logger,
)
return KonfeatureDebugPanelConfig(
store = store,
interceptor = KonfeatureDebugInterceptor(store),
)
}
}
}

/**
* No-op counterpart of the `plugin-konfeature` `applyDebugPanelConfig`. Registers the no-op
* interceptor (which never overrides a value) so the builder stays valid; changes nothing else.
*/
public fun KonfeatureBuilder.applyDebugPanelConfig(
config: KonfeatureDebugPanelConfig,
): KonfeatureBuilder {
addInterceptor(config.interceptor)
return this
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import com.redmadrobot.konfeature.Konfeature
/**
* No-op implementation of [KonfeaturePlugin] for release builds.
*
* Performs no actions; only mirrors the public constructor signature.
* Performs no actions; only mirrors the public constructor signature. The overrides store and
* interceptor come from `konfeature-ui-noop` in release builds via [KonfeatureDebugPanelConfig].
*/
@Suppress("UnusedPrivateProperty")
public class KonfeaturePlugin(
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
private val konfeature: Konfeature,
private val config: KonfeatureDebugPanelConfig,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalContext
import com.redmadrobot.debug.uikit.theme.model.ThemeMode

internal val LocalColors = compositionLocalOf { LightDebugPanelColors }
internal val LocalTypography = compositionLocalOf { DebugPanelTypographyTokens() }
internal val LocalIsDarkTheme = staticCompositionLocalOf { false }

/**
* Entry point for accessing Debug Panel design tokens.
Expand All @@ -35,6 +37,15 @@ public object DebugPanelTheme {
@Composable
@ReadOnlyComposable
get() = LocalTypography.current

/**
* Whether the theme is currently resolved to dark, taking the selected [ThemeMode] into account
* (for [ThemeMode.System] this reflects the system setting).
*/
public val isDarkTheme: Boolean
@Composable
@ReadOnlyComposable
get() = LocalIsDarkTheme.current
}

/**
Expand Down Expand Up @@ -84,6 +95,7 @@ public fun DebugPanelTheme(
CompositionLocalProvider(
LocalColors provides panelColors,
LocalTypography provides panelTypography,
LocalIsDarkTheme provides isDarkTheme,
) {
MaterialTheme(
colorScheme = materialColorScheme,
Expand Down
4 changes: 1 addition & 3 deletions plugins/plugin-konfeature/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,5 @@ android {

dependencies {
implementation(rmr.konfeature)
implementation(androidx.core)
implementation(androidx.lifecycle.runtime)
implementation(stack.timber)
implementation(rmr.konfeature.ui)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.redmadrobot.debug.plugin.konfeature

import android.content.Context
import com.redmadrobot.konfeature.Logger
import com.redmadrobot.konfeature.builder.KonfeatureBuilder
import com.redmadrobot.konfeature.ui.KonfeatureDebugInterceptor
import com.redmadrobot.konfeature.ui.KonfeatureDebugStore

/**
* Everything [KonfeaturePlugin] needs to override feature values, bundled into a single object: the
* persistent [KonfeatureDebugStore] and the [KonfeatureDebugInterceptor] backed by that same store.
*
* Bundling them guarantees the interceptor feeding your Konfeature instance and the panel showing
* the overrides share one store — the wiring callers previously had to get right by hand.
*
* Create it once on startup, attach it to your Konfeature instance with [applyDebugPanelConfig],
* then hand the same config to [KonfeaturePlugin]:
*
* ```kotlin
* val config = KonfeatureDebugPanelConfig.create(context)
* val konfeature = konfeature {
* register(MyFeatureConfig())
* applyDebugPanelConfig(config)
* }
* KonfeaturePlugin(konfeature = konfeature, config = config)
* ```
*
* @see applyDebugPanelConfig
*/
public class KonfeatureDebugPanelConfig private constructor(
internal val store: KonfeatureDebugStore,
internal val interceptor: KonfeatureDebugInterceptor,
) {
/**
* Set to `true` by [applyDebugPanelConfig]. [KonfeaturePlugin] checks it to fail fast when a
* config was created but never attached to a Konfeature instance — otherwise the panel would
* display feature values it could never actually override.
*/
internal var isAttached: Boolean = false

public companion object {
/** Default DataStore file name, resolved under `context.filesDir`. */
public const val DEFAULT_PATH: String = "konfeature_debug.preferences_pb"

/**
* Creates the store (completing its initial load of persisted overrides) and the
* interceptor backed by it.
*
* Call once on startup from any suitable coroutine scope and reuse the returned instance
* for both [applyDebugPanelConfig] and [KonfeaturePlugin].
*
* @param context used to resolve [path] under the app's files directory.
* @param path file name for the DataStore file, relative to `context.filesDir`.
* @param logger optional logger forwarded to [KonfeatureDebugStore.create].
*/
public suspend fun create(
context: Context,
path: String = DEFAULT_PATH,
logger: Logger? = null,
): KonfeatureDebugPanelConfig {
val store = KonfeatureDebugStore.create(
path = context.filesDir.resolve(path).absolutePath,
logger = logger,
)
return KonfeatureDebugPanelConfig(
store = store,
interceptor = KonfeatureDebugInterceptor(store),
)
}
}
}

/**
* Registers the debug panel's interceptor on this builder and marks [config] as attached so
* [KonfeaturePlugin] can verify the wiring.
*
* ```kotlin
* val konfeature = konfeature {
* register(MyFeatureConfig())
* applyDebugPanelConfig(config)
* }
* ```
*/
public fun KonfeatureBuilder.applyDebugPanelConfig(
config: KonfeatureDebugPanelConfig,
): KonfeatureBuilder {
addInterceptor(config.interceptor)
config.isAttached = true
return this
}
Loading
Loading