Codync
免费开源的 1:1 Grok Bot 与 Muse 替代方案。可在 iPhone、Mac 或 Linux 桌面上将 Claude 代码、Codex、Cursor 以及 40 多个编码代理作为持久机器人使用。支持群组聊天、线程、应用。
AI 翻译
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Official Zenmanage feature flags SDK for Android (Kotlin/Java)
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Add feature flags to your Kotlin and Android applications in minutes. Control feature rollouts, run A/B tests, and manage runtime configuration without redeploying.
The SDK is published to Maven Central.
dependencies {
implementation("com.zenmanage:zenmanage-kotlin:1.0.2")
}
dependencies {
implementation 'com.zenmanage:zenmanage-kotlin:1.0.2'
}
import com.zenmanage.kotlin.Zenmanage
import com.zenmanage.kotlin.config.ConfigBuilder
val config = ConfigBuilder.create()
.withEnvironmentToken("mob_your_mobile_key_here")
.build()
val zenmanage = Zenmanage(config)
val enabled = zenmanage.flags()
.single("new-dashboard", false)
.isEnabled()
srv_ and mob_cli_import com.zenmanage.kotlin.context.Attribute
import com.zenmanage.kotlin.context.Context
val context = Context.single("user", "user-123", "Alice")
.addAttribute(Attribute("country", listOf("US")))
val premiumEnabled = zenmanage.flags()
.withContext(context)
.single("premium-feature", false)
.isEnabled()
import com.zenmanage.kotlin.flags.DefaultsCollection
val defaults = DefaultsCollection.fromMap(
mapOf(
"new-ui" to true,
"api-version" to "v2",
"max-items" to 100
)
)
val version = zenmanage.flags()
.withDefaults(defaults)
.single("api-version")
.asString()
val config = zenmanage.flags()
.single("checkout-config", mapOf("maxItems" to 10, "theme" to "dark"))
.asJson()
asJson() decodes both JSON objects (as Map<String, Any?>) and JSON arrays (as List<Any?>).
import com.zenmanage.kotlin.context.Context
val context = Context.single("user", "user-42")
val inRollout = zenmanage.flags()
.withContext(context)
.single("new-checkout-flow", false)
.isEnabled()
A flag's type is one of boolean, string, number, or json. Each type has a matching accessor (asBool(), asString(), asNumber(), asJson()), plus isEnabled() for boolean flags specifically.
asJson() returns the decoded value: a JSON object decodes to Map<String, Any?>, a JSON array decodes to List<Any?>.
asString(), asNumber(), and asJson() each only recognize their own value wrapper ({"string": …}, {"number": …}, {"json": …}) and fall back to a safe zero value for every other type — they never attempt a lossy conversion between types:
| Called on →Flag type ↓ | asString() | asNumber() | asJson() |
|---|---|---|---|
boolean | "" | 0 | emptyMap() |
string | the string | 0 | emptyMap() |
number | "" | the number | emptyMap() |
json | "" | 0 | the decoded map/list |
isEnabled() returns false outright for any non-boolean flag — use it, not asBool(), when you specifically mean "is this boolean flag on". A json flag whose decoded value isn't itself an object or array (a bare JSON scalar such as 5 or "x", which the API allows but which real flags won't normally use) makes asJson() return emptyMap() too — the same safe-zero-value behavior as calling it on a non-json flag, rather than wrapping the scalar or throwing.
asBool() predates this coercion convention and behaves differently: rather than switching on the flag's type, it coerces whatever the underlying value happens to be (a non-boolean Number is truthy if non-zero, a non-boolean String is parsed with toBooleanStrictOrNull(), and a json value — which asBool() does not look at — falls back to false). This SDK's asBool() is intentionally left as-is here; only asJson() follows the "recognize your own type, safe zero value otherwise" rule strictly.
Default values passed to single(key, default) or DefaultsCollection are typed from the Kotlin value itself: a Map or List default becomes a json-typed flag (not a stringified fallback), so asJson() on a missing flag with a Map/List default returns that value unchanged.
ConfigBuilder.create()
.withEnvironmentToken("mob_your_mobile_key_here")
.withCacheBackend("memory")
.build()
ConfigBuilder.create()
.withEnvironmentToken("srv_your_server_key_here")
.withCacheBackend("filesystem")
.withCacheDirectory("/tmp/zenmanage-cache")
.build()
import android.content.Context
import com.zenmanage.kotlin.cache.SharedPreferencesCache
fun createZenmanage(appContext: Context): Zenmanage {
val prefs = appContext.getSharedPreferences("zenmanage", Context.MODE_PRIVATE)
val config = ConfigBuilder.create()
.withEnvironmentToken("mob_your_mobile_key_here")
.withCache(SharedPreferencesCache(prefs))
.build()
return Zenmanage(config)
}
The API is intentionally Java-friendly:
See examples/README.md for the full sample set:
Run tests and coverage report:
./gradlew clean test jacocoTestReport jacocoTestCoverageVerification
Coverage report output:
build/reports/jacoco/test/html/index.html