Add hooks to allow providing additional feature flags in EX pro

We modify the existing `FeaturesProvider` interface so that there can be more
than one of them, and the Features provided by *all* of the instances are
shown. We can then make another instance in element-android-enterprise, which
exposes enterprise-only features.
This commit is contained in:
Richard van der Hoff
2026-08-12 09:30:49 +01:00
parent fae7df16f9
commit 5b728d911b
3 changed files with 27 additions and 8 deletions
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.featureflag.api
/**
* Provides a list of available [Feature]s that the application supports.
* Used by [FeatureFlagService.getAvailableFeatures] to obtain a list of features
* that can be presented to the user.
*
* Multiple instances can exist within the application, and all instances are polled
* to provide features for the list. This means that additional features can be provided
* by compile-time extensions.
*/
fun interface FeaturesProvider {
fun provide(): List<Feature>
}
@@ -14,6 +14,7 @@ import dev.zacsweers.metro.SingleIn
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.Feature
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeaturesProvider
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@@ -22,7 +23,7 @@ import kotlinx.coroutines.flow.flowOf
class DefaultFeatureFlagService( class DefaultFeatureFlagService(
private val providers: Set<@JvmSuppressWildcards FeatureFlagProvider>, private val providers: Set<@JvmSuppressWildcards FeatureFlagProvider>,
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
private val featuresProvider: FeaturesProvider, private val featuresProviders: Set<FeaturesProvider>,
) : FeatureFlagService { ) : FeatureFlagService {
override fun isFeatureEnabledFlow(feature: Feature): Flow<Boolean> { override fun isFeatureEnabledFlow(feature: Feature): Flow<Boolean> {
return if (feature.isFinished) { return if (feature.isFinished) {
@@ -47,7 +48,7 @@ class DefaultFeatureFlagService(
includeFinishedFeatures: Boolean, includeFinishedFeatures: Boolean,
isInLabs: Boolean, isInLabs: Boolean,
): List<Feature> { ): List<Feature> {
return featuresProvider.provide().filter { flag -> return featuresProviders.flatMap { provider -> provider.provide() }.filter { flag ->
(includeFinishedFeatures || !flag.isFinished) && (includeFinishedFeatures || !flag.isFinished) &&
flag.isInLabs == isInLabs flag.isInLabs == isInLabs
} }
@@ -9,15 +9,12 @@
package io.element.android.libraries.featureflag.impl package io.element.android.libraries.featureflag.impl
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesIntoSet
import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.Feature
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.api.FeaturesProvider
fun interface FeaturesProvider { @ContributesIntoSet(AppScope::class)
fun provide(): List<Feature>
}
@ContributesBinding(AppScope::class)
class DefaultFeaturesProvider : FeaturesProvider { class DefaultFeaturesProvider : FeaturesProvider {
override fun provide(): List<Feature> = FeatureFlags.entries override fun provide(): List<Feature> = FeatureFlags.entries
} }