First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-library")
}
android {
namespace = "io.element.android.libraries.featureflag.api"
}
dependencies {
implementation(projects.appconfig)
implementation(projects.libraries.core)
implementation(libs.coroutines.core)
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector 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
import io.element.android.libraries.core.meta.BuildMeta
interface Feature {
/**
* Unique key to identify the feature.
*/
val key: String
/**
* Title to show in the UI. Not needed to be translated as it's only dev accessible.
*/
val title: String
/**
* Optional description to give more context on the feature.
*/
val description: String?
/**
* Calculate the default value of the feature (enabled or disabled) given a [BuildMeta].
*/
val defaultValue: (BuildMeta) -> Boolean
/**
* Whether the feature is finished or not.
* If false: the feature is still in development, it will appear in the developer options screen to be able to enable it and test it.
* If true: the feature is finished, it will not appear in the developer options screen.
*/
val isFinished: Boolean
/**
* Whether the feature is only available in Labs (and not in developer options).
* Feature flags that set this to `true` can be enabled by any users, not only those that have enabled developer mode.
*/
val isInLabs: Boolean
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector 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
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
interface FeatureFlagService {
/**
* @param feature the feature to check for
*
* @return true if the feature is enabled
*/
suspend fun isFeatureEnabled(feature: Feature): Boolean = isFeatureEnabledFlow(feature).first()
/**
* @param feature the feature to check for
*
* @return a flow of booleans, true if the feature is enabled, false if it is disabled.
*/
fun isFeatureEnabledFlow(feature: Feature): Flow<Boolean>
/**
* @param feature the feature to enable or disable
* @param enabled true to enable the feature
*
* @return true if the method succeeds, ie if a [io.element.android.libraries.featureflag.impl.MutableFeatureFlagProvider]
* is registered
*/
suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean
/**
* @return the list of available features that can be toggled.
* @param includeFinishedFeatures whether to include finished features, default is false
* @param isInLabs whether the user is in labs (to include lab features), default is false
*/
fun getAvailableFeatures(
includeFinishedFeatures: Boolean = false,
isInLabs: Boolean = false,
): List<Feature>
}
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector 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
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
/**
* To enable or disable a FeatureFlags, change the `defaultValue` value.
*/
enum class FeatureFlags(
override val key: String,
override val title: String,
override val description: String? = null,
override val defaultValue: (BuildMeta) -> Boolean,
override val isFinished: Boolean,
override val isInLabs: Boolean = false,
) : Feature {
RoomDirectorySearch(
key = "feature.roomdirectorysearch",
title = "Room directory search",
description = "Allow user to search for public rooms in their homeserver",
defaultValue = { false },
isFinished = false,
),
ShowBlockedUsersDetails(
key = "feature.showBlockedUsersDetails",
title = "Show blocked users details",
description = "Show the name and avatar of blocked users in the blocked users list",
defaultValue = { false },
isFinished = false,
),
SyncOnPush(
key = "feature.syncOnPush",
title = "Sync on push",
description = "Subscribe to room sync when a push is received",
defaultValue = { true },
isFinished = false,
),
OnlySignedDeviceIsolationMode(
key = "feature.onlySignedDeviceIsolationMode",
title = "Exclude insecure devices when sending/receiving messages",
description = "This setting controls how end-to-end encryption (E2E) keys are shared." +
" Enabling it will prevent the inclusion of devices that have not been explicitly verified by their owners." +
" You'll have to stop and re-open the app manually for that setting to take effect.",
defaultValue = { false },
isFinished = false,
),
EnableKeyShareOnInvite(
key = "feature.enableKeyShareOnInvite",
title = "Share encrypted history with new members",
description = "When inviting a user to an encrypted room that has history visibility set to \"shared\"," +
" share encrypted history with that user, and accept encrypted history when you are invited to such a room." +
"\nRequires an app restart to take effect." +
"\n\nWARNING: this feature is EXPERIMENTAL and not all security precautions are implemented." +
" Do not enable on production accounts.",
defaultValue = { false },
isFinished = false,
),
Knock(
key = "feature.knock",
title = "Ask to join",
description = "Allow creating rooms which users can request access to.",
defaultValue = { false },
isFinished = false,
),
Space(
key = "feature.space",
title = "Spaces",
defaultValue = { true },
isFinished = false,
),
PrintLogsToLogcat(
key = "feature.print_logs_to_logcat",
title = "Print logs to logcat",
description = "Print logs to logcat in addition to log files. Requires an app restart to take effect." +
"\n\nWARNING: this will make the logs visible in the device logs and may affect performance. " +
"It's not intended for daily usage in release builds.",
defaultValue = { buildMeta -> buildMeta.buildType != BuildType.RELEASE },
// False so it's displayed in the developer options screen
isFinished = false,
),
SelectableMediaQuality(
key = "feature.selectable_media_quality",
title = "Select media quality per upload",
description = "You can select the media quality for each attachment you upload.",
defaultValue = { false },
// False so it's displayed in the developer options screen
isFinished = false,
),
Threads(
key = "feature.thread_timeline",
title = "Threads",
description = "Renders thread messages as a dedicated timeline. Restarting the app is required for this setting to fully take effect.",
defaultValue = { false },
isFinished = false,
isInLabs = true,
),
MultiAccount(
key = "feature.multi_account",
title = "Multi accounts",
description = "Allow the application to connect to multiple accounts at the same time." +
"\n\nWARNING: this feature is EXPERIMENTAL and UNSTABLE.",
defaultValue = { false },
isFinished = false,
),
SyncNotificationsWithWorkManager(
key = "feature.sync_notifications_with_workmanager",
title = "Sync notifications with WorkManager",
description = "Use WorkManager to schedule notification sync tasks when a push is received." +
" This should improve reliability and battery usage.",
defaultValue = { true },
isFinished = false,
),
}