Codync
무료 오픈소스 1:1 대안, Grok Bot과 Muse 대신
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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