[{"data":1,"prerenderedAt":3311},["ShallowReactive",2],{"navigation_docs":3,"-sdks-openfeature-android":244,"-sdks-openfeature-android-surround":3304},[4,51,78,229],{"title":5,"icon":6,"modifiedAt":7,"path":8,"stem":9,"children":10,"page":6},"Getting Started",false,"2026-04-30T19:15:33.000Z","\u002Fgetting-started","1.getting-started",[11,16,21,26,31,36,41,46],{"title":12,"path":13,"stem":14,"icon":15},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F1.quickstart","i-lucide-fast-forward",{"title":17,"path":18,"stem":19,"icon":20},"Core Concepts","\u002Fgetting-started\u002Fcore-concepts","1.getting-started\u002F2.core-concepts","i-lucide-workflow",{"title":22,"path":23,"stem":24,"icon":25},"Configs Anatomy","\u002Fgetting-started\u002Fconfigs","1.getting-started\u002F3.configs","i-lucide-file-cog",{"title":27,"path":28,"stem":29,"icon":30},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F4.architecture","i-lucide-layers",{"title":32,"path":33,"stem":34,"icon":35},"JSON Schema Validation","\u002Fgetting-started\u002Fjson-schema-validation","1.getting-started\u002F5.json-schema-validation","i-lucide-braces",{"title":37,"path":38,"stem":39,"icon":40},"Testing Rules","\u002Fgetting-started\u002Ftesting-targeting-rules","1.getting-started\u002F6.testing-targeting-rules","i-lucide-flask-conical",{"title":42,"path":43,"stem":44,"icon":45},"Segments","\u002Fgetting-started\u002Fsegments","1.getting-started\u002F7.segments","i-lucide-squares-intersect",{"title":47,"path":48,"stem":49,"icon":50},"Pricing & Billing","\u002Fgetting-started\u002Fpricing-and-billing","1.getting-started\u002F8.pricing-and-billing","i-lucide-credit-card",{"title":52,"modifiedAt":53,"defaultOpen":54,"path":55,"stem":56,"children":57,"page":6},"Monitoring","2026-09-24T23:33:20.000Z",true,"\u002Fmonitoring","2.monitoring",[58,63,68,73],{"title":59,"path":60,"stem":61,"icon":62},"Activity","\u002Fmonitoring\u002Factivity","2.monitoring\u002F1.activity","i-lucide-activity",{"title":64,"path":65,"stem":66,"icon":67},"Lifecycle","\u002Fmonitoring\u002Flifecycle","2.monitoring\u002F2.lifecycle","i-lucide-recycle",{"title":69,"path":70,"stem":71,"icon":72},"Alerts","\u002Fmonitoring\u002Falerts","2.monitoring\u002F3.alerts","i-lucide-bell",{"title":74,"path":75,"stem":76,"icon":77},"Dynamic Detection","\u002Fmonitoring\u002Fdynamic-detection","2.monitoring\u002F4.dynamic-detection","i-lucide-hat-glasses",{"title":79,"modifiedAt":53,"defaultOpen":54,"path":80,"stem":81,"children":82,"page":6},"SDKs","\u002Fsdks","3.sdks",[83,88,109,134,150,181,224],{"title":84,"path":85,"stem":86,"icon":87},"Available SDKs","\u002Fsdks\u002Favailable-sdks","3.sdks\u002F1.available-sdks","i-lucide-list",{"title":89,"modifiedAt":53,"icon":90,"defaultOpen":6,"path":91,"stem":92,"children":93,"page":6},"Browser SDKs","i-lucide-monitor","\u002Fsdks\u002Fbrowser","3.sdks\u002F2.browser",[94,99,104],{"title":95,"path":96,"stem":97,"icon":98},"Javascript","\u002Fsdks\u002Fbrowser\u002Fjavascript","3.sdks\u002F2.browser\u002F1.javascript","cd-logos-javascript",{"title":100,"path":101,"stem":102,"icon":103},"React","\u002Fsdks\u002Fbrowser\u002Freact","3.sdks\u002F2.browser\u002F2.react","cd-logos-react-light",{"title":105,"path":106,"stem":107,"icon":108},"Vue","\u002Fsdks\u002Fbrowser\u002Fvue","3.sdks\u002F2.browser\u002F3.vue","cd-logos-vue",{"title":110,"modifiedAt":53,"icon":111,"defaultOpen":6,"path":112,"stem":113,"children":114,"page":6},"Mobile SDKs","i-lucide-smartphone","\u002Fsdks\u002Fmobile","3.sdks\u002F3.mobile",[115,119,124,129],{"title":116,"path":117,"stem":118,"icon":103},"React Native","\u002Fsdks\u002Fmobile\u002Freact-native","3.sdks\u002F3.mobile\u002F1.react-native",{"title":120,"path":121,"stem":122,"icon":123},"Flutter","\u002Fsdks\u002Fmobile\u002Fflutter","3.sdks\u002F3.mobile\u002F2.flutter","cd-logos-flutter",{"title":125,"path":126,"stem":127,"icon":128},"Swift","\u002Fsdks\u002Fmobile\u002Fswift","3.sdks\u002F3.mobile\u002F3.swift","cd-logos-swift",{"title":130,"path":131,"stem":132,"icon":133},"Android","\u002Fsdks\u002Fmobile\u002Fandroid","3.sdks\u002F3.mobile\u002F4.android","cd-logos-android",{"title":135,"modifiedAt":53,"icon":136,"defaultOpen":6,"path":137,"stem":138,"children":139,"page":6},"Meta-framework SDKs","i-lucide-monitor-smartphone","\u002Fsdks\u002Fmeta","3.sdks\u002F4.meta",[140,145],{"title":141,"path":142,"stem":143,"icon":144},"Next.js","\u002Fsdks\u002Fmeta\u002Fnextjs","3.sdks\u002F4.meta\u002F1.nextjs","cd-logos-nextjs",{"title":146,"path":147,"stem":148,"icon":149},"Nuxt","\u002Fsdks\u002Fmeta\u002Fnuxt","3.sdks\u002F4.meta\u002F2.nuxt","cd-logos-nuxt",{"title":151,"modifiedAt":53,"icon":152,"defaultOpen":6,"path":153,"stem":154,"children":155,"page":6},"Server SDKs","i-lucide-server","\u002Fsdks\u002Fserver","3.sdks\u002F5.server",[156,161,166,171,176],{"title":157,"path":158,"stem":159,"icon":160},"Node.js","\u002Fsdks\u002Fserver\u002Fnode-js","3.sdks\u002F5.server\u002F1.node-js","cd-logos-nodejs",{"title":162,"path":163,"stem":164,"icon":165},"Python","\u002Fsdks\u002Fserver\u002Fpython","3.sdks\u002F5.server\u002F2.python","cd-logos-python",{"title":167,"path":168,"stem":169,"icon":170},"Java","\u002Fsdks\u002Fserver\u002Fjava","3.sdks\u002F5.server\u002F3.java","cd-logos-java",{"title":172,"path":173,"stem":174,"icon":175},".NET","\u002Fsdks\u002Fserver\u002Fdotnet","3.sdks\u002F5.server\u002F4.dotnet","cd-logos-dotnet",{"title":177,"path":178,"stem":179,"icon":180},"Go","\u002Fsdks\u002Fserver\u002Fgo","3.sdks\u002F5.server\u002F5.go","cd-logos-go",{"title":182,"modifiedAt":53,"icon":183,"defaultOpen":6,"path":184,"stem":185,"children":186,"page":6},"OpenFeature Providers","cd-logos-openfeature","\u002Fsdks\u002Fopenfeature","3.sdks\u002F6.openfeature",[187,192,196,200,204,208,212,216,220],{"title":188,"path":189,"stem":190,"icon":183,"icons":191},"Web","\u002Fsdks\u002Fopenfeature\u002Fweb","3.sdks\u002F6.openfeature\u002F1.web",[183,98],{"title":125,"path":193,"stem":194,"icon":183,"icons":195},"\u002Fsdks\u002Fopenfeature\u002Fswift","3.sdks\u002F6.openfeature\u002F2.swift",[183,128],{"title":130,"path":197,"stem":198,"icon":183,"icons":199},"\u002Fsdks\u002Fopenfeature\u002Fandroid","3.sdks\u002F6.openfeature\u002F3.android",[183,133],{"title":120,"path":201,"stem":202,"icon":183,"icons":203},"\u002Fsdks\u002Fopenfeature\u002Fflutter","3.sdks\u002F6.openfeature\u002F4.flutter",[183,123],{"title":157,"path":205,"stem":206,"icon":183,"icons":207},"\u002Fsdks\u002Fopenfeature\u002Fnode-js","3.sdks\u002F6.openfeature\u002F5.node-js",[183,160],{"title":162,"path":209,"stem":210,"icon":183,"icons":211},"\u002Fsdks\u002Fopenfeature\u002Fpython","3.sdks\u002F6.openfeature\u002F6.python",[183,165],{"title":167,"path":213,"stem":214,"icon":183,"icons":215},"\u002Fsdks\u002Fopenfeature\u002Fjava","3.sdks\u002F6.openfeature\u002F7.java",[183,170],{"title":172,"path":217,"stem":218,"icon":183,"icons":219},"\u002Fsdks\u002Fopenfeature\u002Fdotnet","3.sdks\u002F6.openfeature\u002F8.dotnet",[183,175],{"title":177,"path":221,"stem":222,"icon":183,"icons":223},"\u002Fsdks\u002Fopenfeature\u002Fgo","3.sdks\u002F6.openfeature\u002F9.go",[183,180],{"title":225,"path":226,"stem":227,"icon":228},"Maven Repository","\u002Fsdks\u002Fmaven-repository","3.sdks\u002F7.maven-repository","i-lucide-package",{"title":230,"modifiedAt":53,"defaultOpen":54,"path":231,"stem":232,"children":233,"page":6},"Integrations","\u002Fintegrations","4.integrations",[234,239],{"title":235,"path":236,"stem":237,"icon":238},"Terraform","\u002Fintegrations\u002Fterraform","4.integrations\u002F1.terraform","cd-logos-terraform",{"title":240,"path":241,"stem":242,"icon":243},"MCP","\u002Fintegrations\u002Fmcp","4.integrations\u002F2.mcp","cd-logos-mcp",{"id":245,"title":246,"body":247,"description":3295,"extension":3296,"links":3297,"meta":3298,"navigation":3300,"path":197,"seo":3302,"stem":198,"__hash__":3303},"docs\u002F3.sdks\u002F6.openfeature\u002F3.android.md","OpenFeature Android Provider",{"type":248,"value":249,"toc":3280},"minimark",[250,254,259,264,281,318,332,351,354,358,381,661,664,817,820,823,904,908,925,1049,1056,1080,1091,1231,1254,1265,1270,1277,1284,1368,1383,1387,1390,1404,1407,1410,1414,1417,1481,1484,1539,1554,1590,1672,1679,1683,1690,1772,1779,1863,1867,1870,1882,2182,2188,2192,2331,2334,2397,2406,2426,2435,2440,2444,2456,2603,2614,2618,2638,2649,2725,2732,3162,3183,3203,3276],[251,252],"div",{"hidden":54,"dataTitleIcon":253},"openfeature-android",[255,256],"external-badges",{"package-url":226,"registry":257,"repo":258},"maven","android-sdk\u002Ftree\u002Fmain\u002Fconfigdirector-openfeature-android-provider",[260,261,263],"h2",{"id":262},"introduction","Introduction",[265,266,267,268,276,277,280],"p",{},"The OpenFeature Android Provider is intended to be used in combination with the ",[269,270,275],"a",{"href":271,"rel":272,"target":274},"https:\u002F\u002Fopenfeature.dev\u002Fdocs\u002Freference\u002Fsdks\u002Fclient\u002Fkotlin\u002F",[273],"noopener","_blank","OpenFeature Kotlin SDK"," in Android applications. The provider wraps the ConfigDirector ",[269,278,279],{"href":131},"Android SDK",".",[282,283,284,300],"table",{},[285,286,287],"thead",{},[288,289,290,294,297],"tr",{},[291,292,293],"th",{},"Artifact",[291,295,296],{},"Minimum SDK",[291,298,299],{},"Java bytecode",[301,302,303],"tbody",{},[288,304,305,312,315],{},[306,307,308],"td",{},[309,310,311],"code",{},"com.configdirector:openfeature-android-provider",[306,313,314],{},"21",[306,316,317],{},"11",[265,319,320,321,324,325,328,329,331],{},"It requires version 0.8.0 or newer of the OpenFeature Kotlin SDK, ",[309,322,323],{},"dev.openfeature:kotlin-sdk",". That SDK's provider contract is built on ",[309,326,327],{},"suspend"," functions and flows, so the provider is written for Kotlin; a Java application uses the ",[269,330,279],{"href":131}," directly instead.",[333,334,335,336,339,340,343,344,346,347,350],"note",{},"Before version 0.6.0 the OpenFeature Kotlin SDK was published as ",[309,337,338],{},"dev.openfeature:android-sdk",", in the package ",[309,341,342],{},"dev.openfeature.sdk",". The provider depends on the current artifact, ",[309,345,323],{},", whose package is ",[309,348,349],{},"dev.openfeature.kotlin.sdk",". Update your imports if you are upgrading from the old one.",[265,352,353],{},"Config evaluation is synchronous and reads config state the provider already holds in memory, which is what makes it safe to call from a composable or from the main thread. Telemetry about those evaluations is aggregated and reported on its own coroutine, so evaluating a config never waits on the network.",[260,355,357],{"id":356},"installation","Installation",[265,359,360,361,364,365,368,369,372,373,376,377,380],{},"The provider is published to the ",[269,362,363],{"href":226},"ConfigDirector Maven repository",". Add the repository to ",[309,366,367],{},"settings.gradle.kts"," or ",[309,370,371],{},"settings.gradle",", next to ",[309,374,375],{},"google()"," and ",[309,378,379],{},"mavenCentral()",":",[382,383,384,545],"code-group",{},[385,386,391],"pre",{"className":387,"code":388,"filename":367,"language":389,"meta":390,"style":390},"language-kotlin shiki shiki-themes material-theme-lighter github-light github-dark","dependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n        exclusiveContent {\n            forRepository {\n                maven {\n                    name = \"ConfigDirector\"\n                    url = uri(\"https:\u002F\u002Fmaven.configdirector.com\")\n                }\n            }\n            filter {\n                includeGroup(\"com.configdirector\")\n            }\n        }\n    }\n}\n","kotlin","",[309,392,393,406,414,423,431,439,447,455,469,489,495,501,509,522,527,533,539],{"__ignoreMap":390},[394,395,398,402],"span",{"class":396,"line":397},"line",1,[394,399,401],{"class":400},"sGLFI","dependencyResolutionManagement",[394,403,405],{"class":404},"su5hD"," {\n",[394,407,409,412],{"class":396,"line":408},2,[394,410,411],{"class":400},"    repositories",[394,413,405],{"class":404},[394,415,417,420],{"class":396,"line":416},3,[394,418,419],{"class":400},"        google",[394,421,422],{"class":404},"()\n",[394,424,426,429],{"class":396,"line":425},4,[394,427,428],{"class":400},"        mavenCentral",[394,430,422],{"class":404},[394,432,434,437],{"class":396,"line":433},5,[394,435,436],{"class":400},"        exclusiveContent",[394,438,405],{"class":404},[394,440,442,445],{"class":396,"line":441},6,[394,443,444],{"class":400},"            forRepository",[394,446,405],{"class":404},[394,448,450,453],{"class":396,"line":449},7,[394,451,452],{"class":400},"                maven",[394,454,405],{"class":404},[394,456,458,461,465],{"class":396,"line":457},8,[394,459,460],{"class":404},"                    name ",[394,462,464],{"class":463},"smGrS","=",[394,466,468],{"class":467},"s_sjI"," \"ConfigDirector\"\n",[394,470,472,475,477,480,483,486],{"class":396,"line":471},9,[394,473,474],{"class":404},"                    url ",[394,476,464],{"class":463},[394,478,479],{"class":400}," uri",[394,481,482],{"class":404},"(",[394,484,485],{"class":467},"\"https:\u002F\u002Fmaven.configdirector.com\"",[394,487,488],{"class":404},")\n",[394,490,492],{"class":396,"line":491},10,[394,493,494],{"class":404},"                }\n",[394,496,498],{"class":396,"line":497},11,[394,499,500],{"class":404},"            }\n",[394,502,504,507],{"class":396,"line":503},12,[394,505,506],{"class":400},"            filter",[394,508,405],{"class":404},[394,510,512,515,517,520],{"class":396,"line":511},13,[394,513,514],{"class":400},"                includeGroup",[394,516,482],{"class":404},[394,518,519],{"class":467},"\"com.configdirector\"",[394,521,488],{"class":404},[394,523,525],{"class":396,"line":524},14,[394,526,500],{"class":404},[394,528,530],{"class":396,"line":529},15,[394,531,532],{"class":404},"        }\n",[394,534,536],{"class":396,"line":535},16,[394,537,538],{"class":404},"    }\n",[394,540,542],{"class":396,"line":541},17,[394,543,544],{"class":404},"}\n",[385,546,550],{"className":547,"code":548,"filename":371,"language":549,"meta":390,"style":390},"language-groovy shiki shiki-themes material-theme-lighter github-light github-dark","dependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n        exclusiveContent {\n            forRepository {\n                maven {\n                    name = 'ConfigDirector'\n                    url = 'https:\u002F\u002Fmaven.configdirector.com'\n                }\n            }\n            filter {\n                includeGroup 'com.configdirector'\n            }\n        }\n    }\n}\n","groovy",[309,551,552,557,562,569,575,580,585,590,606,619,623,627,632,645,649,653,657],{"__ignoreMap":390},[394,553,554],{"class":396,"line":397},[394,555,556],{"class":404},"dependencyResolutionManagement {\n",[394,558,559],{"class":396,"line":408},[394,560,561],{"class":404},"    repositories {\n",[394,563,564,566],{"class":396,"line":416},[394,565,419],{"class":404},[394,567,422],{"class":568},"sP7_E",[394,570,571,573],{"class":396,"line":425},[394,572,428],{"class":404},[394,574,422],{"class":568},[394,576,577],{"class":396,"line":433},[394,578,579],{"class":404},"        exclusiveContent {\n",[394,581,582],{"class":396,"line":441},[394,583,584],{"class":404},"            forRepository {\n",[394,586,587],{"class":396,"line":449},[394,588,589],{"class":404},"                maven {\n",[394,591,592,594,596,600,603],{"class":396,"line":457},[394,593,460],{"class":404},[394,595,464],{"class":463},[394,597,599],{"class":598},"sjJ54"," '",[394,601,602],{"class":467},"ConfigDirector",[394,604,605],{"class":598},"'\n",[394,607,608,610,612,614,617],{"class":396,"line":471},[394,609,474],{"class":404},[394,611,464],{"class":463},[394,613,599],{"class":598},[394,615,616],{"class":467},"https:\u002F\u002Fmaven.configdirector.com",[394,618,605],{"class":598},[394,620,621],{"class":396,"line":491},[394,622,494],{"class":404},[394,624,625],{"class":396,"line":497},[394,626,500],{"class":404},[394,628,629],{"class":396,"line":503},[394,630,631],{"class":404},"            filter {\n",[394,633,634,637,640,643],{"class":396,"line":511},[394,635,636],{"class":404},"                includeGroup ",[394,638,639],{"class":598},"'",[394,641,642],{"class":467},"com.configdirector",[394,644,605],{"class":598},[394,646,647],{"class":396,"line":524},[394,648,500],{"class":404},[394,650,651],{"class":396,"line":529},[394,652,532],{"class":404},[394,654,655],{"class":396,"line":535},[394,656,538],{"class":404},[394,658,659],{"class":396,"line":541},[394,660,544],{"class":404},[265,662,663],{},"Then add the dependency to your app module:",[382,665,666,695,722],{},[385,667,670],{"className":387,"code":668,"filename":669,"language":389,"meta":390,"style":390},"dependencies {\n    implementation(\"com.configdirector:openfeature-android-provider:1.3.0\")\n}\n","build.gradle.kts",[309,671,672,679,691],{"__ignoreMap":390},[394,673,674,677],{"class":396,"line":397},[394,675,676],{"class":400},"dependencies",[394,678,405],{"class":404},[394,680,681,684,686,689],{"class":396,"line":408},[394,682,683],{"class":400},"    implementation",[394,685,482],{"class":404},[394,687,688],{"class":467},"\"com.configdirector:openfeature-android-provider:1.3.0\"",[394,690,488],{"class":404},[394,692,693],{"class":396,"line":416},[394,694,544],{"class":404},[385,696,699],{"className":547,"code":697,"filename":698,"language":549,"meta":390,"style":390},"dependencies {\n    implementation 'com.configdirector:openfeature-android-provider:1.3.0'\n}\n","build.gradle",[309,700,701,706,718],{"__ignoreMap":390},[394,702,703],{"class":396,"line":397},[394,704,705],{"class":404},"dependencies {\n",[394,707,708,711,713,716],{"class":396,"line":408},[394,709,710],{"class":404},"    implementation ",[394,712,639],{"class":598},[394,714,715],{"class":467},"com.configdirector:openfeature-android-provider:1.3.0",[394,717,605],{"class":598},[394,719,720],{"class":396,"line":416},[394,721,544],{"class":404},[385,723,728],{"className":724,"code":725,"filename":726,"language":727,"meta":390,"style":390},"language-toml shiki shiki-themes material-theme-lighter github-light github-dark","[versions]\nconfigdirector-openfeature = \"1.3.0\"\n\n[libraries]\nopenfeature-provider = { module = \"com.configdirector:openfeature-android-provider\", version.ref = \"configdirector-openfeature\" }\n","libs.versions.toml","toml",[309,729,730,742,758,763,772],{"__ignoreMap":390},[394,731,732,735,739],{"class":396,"line":397},[394,733,734],{"class":568},"[",[394,736,738],{"class":737},"sbgvK","versions",[394,740,741],{"class":568},"]\n",[394,743,744,747,749,752,755],{"class":396,"line":408},[394,745,746],{"class":404},"configdirector-openfeature ",[394,748,464],{"class":568},[394,750,751],{"class":598}," \"",[394,753,754],{"class":467},"1.3.0",[394,756,757],{"class":598},"\"\n",[394,759,760],{"class":396,"line":416},[394,761,762],{"emptyLinePlaceholder":54},"\n",[394,764,765,767,770],{"class":396,"line":425},[394,766,734],{"class":568},[394,768,769],{"class":737},"libraries",[394,771,741],{"class":568},[394,773,774,777,779,782,785,787,789,791,794,797,800,802,805,807,809,812,814],{"class":396,"line":433},[394,775,776],{"class":404},"openfeature-provider ",[394,778,464],{"class":568},[394,780,781],{"class":568}," {",[394,783,784],{"class":404}," module ",[394,786,464],{"class":568},[394,788,751],{"class":598},[394,790,311],{"class":467},[394,792,793],{"class":598},"\"",[394,795,796],{"class":568},",",[394,798,799],{"class":404}," version",[394,801,280],{"class":568},[394,803,804],{"class":404},"ref ",[394,806,464],{"class":568},[394,808,751],{"class":598},[394,810,811],{"class":467},"configdirector-openfeature",[394,813,793],{"class":598},[394,815,816],{"class":568}," }\n",[265,818,819],{},"The OpenFeature Kotlin SDK and the ConfigDirector Android SDK are included as transitive dependencies.",[265,821,822],{},"The provider ships Java 11 bytecode, which the OpenFeature Kotlin SDK requires, so your application module has to compile for Java 11 or newer:",[385,824,826],{"className":387,"code":825,"filename":669,"language":389,"meta":390,"style":390},"android {\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_11\n        targetCompatibility = JavaVersion.VERSION_11\n    }\n}\n\nkotlin {\n    compilerOptions {\n        jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11\n    }\n}\n",[309,827,828,835,842,852,861,865,869,873,879,886,896,900],{"__ignoreMap":390},[394,829,830,833],{"class":396,"line":397},[394,831,832],{"class":400},"android",[394,834,405],{"class":404},[394,836,837,840],{"class":396,"line":408},[394,838,839],{"class":400},"    compileOptions",[394,841,405],{"class":404},[394,843,844,847,849],{"class":396,"line":416},[394,845,846],{"class":404},"        sourceCompatibility ",[394,848,464],{"class":463},[394,850,851],{"class":404}," JavaVersion.VERSION_11\n",[394,853,854,857,859],{"class":396,"line":425},[394,855,856],{"class":404},"        targetCompatibility ",[394,858,464],{"class":463},[394,860,851],{"class":404},[394,862,863],{"class":396,"line":433},[394,864,538],{"class":404},[394,866,867],{"class":396,"line":441},[394,868,544],{"class":404},[394,870,871],{"class":396,"line":449},[394,872,762],{"emptyLinePlaceholder":54},[394,874,875,877],{"class":396,"line":457},[394,876,389],{"class":400},[394,878,405],{"class":404},[394,880,881,884],{"class":396,"line":471},[394,882,883],{"class":400},"    compilerOptions",[394,885,405],{"class":404},[394,887,888,891,893],{"class":396,"line":491},[394,889,890],{"class":404},"        jvmTarget ",[394,892,464],{"class":463},[394,894,895],{"class":404}," org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11\n",[394,897,898],{"class":396,"line":497},[394,899,538],{"class":404},[394,901,902],{"class":396,"line":503},[394,903,544],{"class":404},[260,905,907],{"id":906},"configure-and-initialize-the-client","Configure and initialize the client",[909,910,911,919,922],"ol",{},[912,913,914,915,918],"li",{},"Create an instance of the provider providing an Android context and your client SDK key. You can retrieve a client SDK key for each environment under ",[309,916,917],{},"SDK keys"," in the dashboard's navigation panel.",[912,920,921],{},"Set the OpenFeature provider, along with the initial evaluation context.",[912,923,924],{},"Get a client instance from OpenFeature.",[385,926,929],{"className":387,"code":927,"filename":928,"language":389,"meta":390,"style":390},"import com.configdirector.openfeature.ConfigDirectorProvider\nimport dev.openfeature.kotlin.sdk.ImmutableContext\nimport dev.openfeature.kotlin.sdk.OpenFeatureAPI\n\n\u002F\u002F Creating the provider makes no network calls. It throws only when the SDK key is\n\u002F\u002F blank or an option holds an unusable value.\nval provider = ConfigDirectorProvider(applicationContext, \"YOUR-CLIENT-SDK-KEY\")\n\n\u002F\u002F Connects and waits until config values are received, or until it times out.\n\u002F\u002F After a timeout the provider keeps trying to connect in the background.\nOpenFeatureAPI.setProviderAndWait(provider, ImmutableContext(targetingKey = \"user-123\"))\n\nval client = OpenFeatureAPI.getClient()\n","ConfigDirectorSetup.kt",[309,930,931,939,946,953,957,963,968,989,993,998,1003,1028,1032],{"__ignoreMap":390},[394,932,933,936],{"class":396,"line":397},[394,934,935],{"class":463},"import",[394,937,938],{"class":737}," com.configdirector.openfeature.ConfigDirectorProvider\n",[394,940,941,943],{"class":396,"line":408},[394,942,935],{"class":463},[394,944,945],{"class":737}," dev.openfeature.kotlin.sdk.ImmutableContext\n",[394,947,948,950],{"class":396,"line":416},[394,949,935],{"class":463},[394,951,952],{"class":737}," dev.openfeature.kotlin.sdk.OpenFeatureAPI\n",[394,954,955],{"class":396,"line":425},[394,956,762],{"emptyLinePlaceholder":54},[394,958,959],{"class":396,"line":433},[394,960,962],{"class":961},"sutJx","\u002F\u002F Creating the provider makes no network calls. It throws only when the SDK key is\n",[394,964,965],{"class":396,"line":441},[394,966,967],{"class":961},"\u002F\u002F blank or an option holds an unusable value.\n",[394,969,970,973,976,978,981,984,987],{"class":396,"line":449},[394,971,972],{"class":463},"val",[394,974,975],{"class":404}," provider ",[394,977,464],{"class":463},[394,979,980],{"class":400}," ConfigDirectorProvider",[394,982,983],{"class":404},"(applicationContext, ",[394,985,986],{"class":467},"\"YOUR-CLIENT-SDK-KEY\"",[394,988,488],{"class":404},[394,990,991],{"class":396,"line":457},[394,992,762],{"emptyLinePlaceholder":54},[394,994,995],{"class":396,"line":471},[394,996,997],{"class":961},"\u002F\u002F Connects and waits until config values are received, or until it times out.\n",[394,999,1000],{"class":396,"line":491},[394,1001,1002],{"class":961},"\u002F\u002F After a timeout the provider keeps trying to connect in the background.\n",[394,1004,1005,1008,1011,1014,1017,1020,1022,1025],{"class":396,"line":497},[394,1006,1007],{"class":404},"OpenFeatureAPI.",[394,1009,1010],{"class":400},"setProviderAndWait",[394,1012,1013],{"class":404},"(provider, ",[394,1015,1016],{"class":400},"ImmutableContext",[394,1018,1019],{"class":404},"(targetingKey ",[394,1021,464],{"class":463},[394,1023,1024],{"class":467}," \"user-123\"",[394,1026,1027],{"class":404},"))\n",[394,1029,1030],{"class":396,"line":503},[394,1031,762],{"emptyLinePlaceholder":54},[394,1033,1034,1036,1039,1041,1044,1047],{"class":396,"line":511},[394,1035,972],{"class":463},[394,1037,1038],{"class":404}," client ",[394,1040,464],{"class":463},[394,1042,1043],{"class":404}," OpenFeatureAPI.",[394,1045,1046],{"class":400},"getClient",[394,1048,422],{"class":404},[265,1050,1051,1052,1055],{},"The constructor throws a ",[309,1053,1054],{},"ConfigDirectorValidationException"," in only two cases, both of which are programming errors rather than runtime conditions: a blank client SDK key, and an option that cannot be used, such as a negative timeout or a base URL that is not absolute. Connection failures are not thrown from it.",[265,1057,1058,1060,1061,1063,1064,1067,1068,1071,1072,1075,1076,1079],{},[309,1059,1010],{}," is a ",[309,1062,327],{}," function that returns once the initial config state arrives or the configured timeout elapses. When it times out, the provider's initialization fails with a ",[309,1065,1066],{},"ProviderNotReadyError",", the OpenFeature SDK reports the ",[309,1069,1070],{},"Error"," status, and the provider keeps connecting in the background; it then emits ",[309,1073,1074],{},"ProviderReady"," as soon as config state arrives. Until then, flags resolve to their default values with the ",[309,1077,1078],{},"PROVIDER_NOT_READY"," error code.",[265,1081,1082,1083,1086,1087,1090],{},"Most applications register the provider once during startup and let it live for the lifetime of the app. That usually means doing so from the ",[309,1084,1085],{},"Application",", with ",[309,1088,1089],{},"setProvider",", which returns at once and initializes in the background:",[385,1092,1095],{"className":387,"code":1093,"filename":1094,"language":389,"meta":390,"style":390},"import android.app.Application\nimport com.configdirector.openfeature.ConfigDirectorProvider\nimport dev.openfeature.kotlin.sdk.ImmutableContext\nimport dev.openfeature.kotlin.sdk.OpenFeatureAPI\n\nclass MyApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n\n        val provider = ConfigDirectorProvider(this, \"YOUR-CLIENT-SDK-KEY\")\n        OpenFeatureAPI.setProvider(provider, initialContext = ImmutableContext(targetingKey = \"user-123\"))\n    }\n}\n","MyApplication.kt",[309,1096,1097,1104,1110,1116,1122,1126,1142,1146,1160,1173,1177,1200,1223,1227],{"__ignoreMap":390},[394,1098,1099,1101],{"class":396,"line":397},[394,1100,935],{"class":463},[394,1102,1103],{"class":737}," android.app.Application\n",[394,1105,1106,1108],{"class":396,"line":408},[394,1107,935],{"class":463},[394,1109,938],{"class":737},[394,1111,1112,1114],{"class":396,"line":416},[394,1113,935],{"class":463},[394,1115,945],{"class":737},[394,1117,1118,1120],{"class":396,"line":425},[394,1119,935],{"class":463},[394,1121,952],{"class":737},[394,1123,1124],{"class":396,"line":433},[394,1125,762],{"emptyLinePlaceholder":54},[394,1127,1128,1131,1134,1137,1139],{"class":396,"line":441},[394,1129,1130],{"class":463},"class",[394,1132,1133],{"class":737}," MyApplication",[394,1135,1136],{"class":404}," : ",[394,1138,1085],{"class":737},[394,1140,1141],{"class":404},"() {\n",[394,1143,1144],{"class":396,"line":449},[394,1145,762],{"emptyLinePlaceholder":54},[394,1147,1148,1152,1155,1158],{"class":396,"line":457},[394,1149,1151],{"class":1150},"sbsja","    override",[394,1153,1154],{"class":463}," fun",[394,1156,1157],{"class":400}," onCreate",[394,1159,1141],{"class":404},[394,1161,1162,1166,1168,1171],{"class":396,"line":471},[394,1163,1165],{"class":1164},"s39Yj","        super",[394,1167,280],{"class":404},[394,1169,1170],{"class":400},"onCreate",[394,1172,422],{"class":404},[394,1174,1175],{"class":396,"line":491},[394,1176,762],{"emptyLinePlaceholder":54},[394,1178,1179,1182,1184,1186,1188,1190,1193,1196,1198],{"class":396,"line":497},[394,1180,1181],{"class":463},"        val",[394,1183,975],{"class":404},[394,1185,464],{"class":463},[394,1187,980],{"class":400},[394,1189,482],{"class":404},[394,1191,1192],{"class":1164},"this",[394,1194,1195],{"class":404},", ",[394,1197,986],{"class":467},[394,1199,488],{"class":404},[394,1201,1202,1205,1207,1210,1212,1215,1217,1219,1221],{"class":396,"line":503},[394,1203,1204],{"class":404},"        OpenFeatureAPI.",[394,1206,1089],{"class":400},[394,1208,1209],{"class":404},"(provider, initialContext ",[394,1211,464],{"class":463},[394,1213,1214],{"class":400}," ImmutableContext",[394,1216,1019],{"class":404},[394,1218,464],{"class":463},[394,1220,1024],{"class":467},[394,1222,1027],{"class":404},[394,1224,1225],{"class":396,"line":511},[394,1226,538],{"class":404},[394,1228,1229],{"class":396,"line":524},[394,1230,544],{"class":404},[265,1232,1233,1234,1237,1238,1241,1242,1245,1246,1249,1250,280],{},"Flag evaluations are possible once ",[309,1235,1236],{},"OpenFeatureAPI.getStatus()"," is ",[309,1239,1240],{},"Ready","; ",[309,1243,1244],{},"OpenFeatureAPI.statusFlow"," is the same thing as a ",[309,1247,1248],{},"Flow",", which is how a screen follows it. See ",[269,1251,1253],{"href":1252},"#reading-configs-in-jetpack-compose","reading configs in Jetpack Compose",[333,1255,1256,1257,1264],{},"The underlying SDK watches the application behind the context it was given so it can release its network connection while the app is backgrounded. Pass the provider any context — the application behind it is what matters. See the ",[269,1258,1260,1263],{"href":1259},"\u002Fsdks\u002Fmobile\u002Fandroid#connection",[309,1261,1262],{},"connection"," options of the Android SDK"," to manage that yourself instead.",[1266,1267,1269],"h3",{"id":1268},"additional-configuration-options","Additional configuration options",[265,1271,1272,1273,1276],{},"Additional configuration options can be passed into the provider as ",[309,1274,1275],{},"ClientOptions"," in the optional third argument of the constructor. They are the Android SDK's own options, so configuring the provider needs the SDK's types alongside it.",[265,1278,1279,1280,1283],{},"For example, the ",[309,1281,1282],{},"metadata"," can be provided like this:",[385,1285,1287],{"className":387,"code":1286,"filename":928,"language":389,"meta":390,"style":390},"import com.configdirector.ClientOptions\nimport com.configdirector.openfeature.ConfigDirectorProvider\n\nval provider = ConfigDirectorProvider(\n    applicationContext,\n    \"YOUR-CLIENT-SDK-KEY\",\n    ClientOptions.build {\n        metadata(\"YOUR-APP-NAME\", \"1.0.2\")\n    },\n)\n",[309,1288,1289,1296,1302,1306,1319,1324,1332,1342,1359,1364],{"__ignoreMap":390},[394,1290,1291,1293],{"class":396,"line":397},[394,1292,935],{"class":463},[394,1294,1295],{"class":737}," com.configdirector.ClientOptions\n",[394,1297,1298,1300],{"class":396,"line":408},[394,1299,935],{"class":463},[394,1301,938],{"class":737},[394,1303,1304],{"class":396,"line":416},[394,1305,762],{"emptyLinePlaceholder":54},[394,1307,1308,1310,1312,1314,1316],{"class":396,"line":425},[394,1309,972],{"class":463},[394,1311,975],{"class":404},[394,1313,464],{"class":463},[394,1315,980],{"class":400},[394,1317,1318],{"class":404},"(\n",[394,1320,1321],{"class":396,"line":433},[394,1322,1323],{"class":404},"    applicationContext,\n",[394,1325,1326,1329],{"class":396,"line":441},[394,1327,1328],{"class":467},"    \"YOUR-CLIENT-SDK-KEY\"",[394,1330,1331],{"class":404},",\n",[394,1333,1334,1337,1340],{"class":396,"line":449},[394,1335,1336],{"class":404},"    ClientOptions.",[394,1338,1339],{"class":400},"build",[394,1341,405],{"class":404},[394,1343,1344,1347,1349,1352,1354,1357],{"class":396,"line":457},[394,1345,1346],{"class":400},"        metadata",[394,1348,482],{"class":404},[394,1350,1351],{"class":467},"\"YOUR-APP-NAME\"",[394,1353,1195],{"class":404},[394,1355,1356],{"class":467},"\"1.0.2\"",[394,1358,488],{"class":404},[394,1360,1361],{"class":396,"line":471},[394,1362,1363],{"class":404},"    },\n",[394,1365,1366],{"class":396,"line":491},[394,1367,488],{"class":404},[265,1369,1370,1371,1195,1373,376,1375,1378,1379,280],{},"The provider accepts the same ",[309,1372,1282],{},[309,1374,1262],{},[309,1376,1377],{},"logger"," options as the Android SDK client, ",[269,1380,1382],{"href":1381},"\u002Fsdks\u002Fmobile\u002Fandroid#additional-configuration-options","refer to the additional configuration options section of the Android SDK for a full list",[1266,1384,1386],{"id":1385},"shut-down","Shut down",[265,1388,1389],{},"Shutting OpenFeature down, or replacing the provider, closes the provider, which closes its connection and reports any pending telemetry:",[385,1391,1393],{"className":387,"code":1392,"filename":1094,"language":389,"meta":390,"style":390},"OpenFeatureAPI.shutdown()\n",[309,1394,1395],{"__ignoreMap":390},[394,1396,1397,1399,1402],{"class":396,"line":397},[394,1398,1007],{"class":404},[394,1400,1401],{"class":400},"shutdown",[394,1403,422],{"class":404},[265,1405,1406],{},"An instance serves a single registration. After it has been shut down, create a new one rather than registering it again.",[265,1408,1409],{},"Most applications never need to do this — the provider lives as long as the process, and Android reclaims everything when the process ends. Shut it down when an application wants the connection gone before that, on sign-out for instance.",[260,1411,1413],{"id":1412},"retrieve-config-values","Retrieve config values",[265,1415,1416],{},"To retrieve config values, use the OpenFeature client:",[385,1418,1421],{"className":387,"code":1419,"filename":1420,"language":389,"meta":390,"style":390},"val booleanValue = client.getBooleanValue(\"my-config-key\", false)\n\nval stringValue = client.getStringValue(\"my-string-config-key\", \"Default\")\n","MainActivity.kt",[309,1422,1423,1451,1455],{"__ignoreMap":390},[394,1424,1425,1427,1430,1432,1435,1438,1440,1443,1445,1449],{"class":396,"line":397},[394,1426,972],{"class":463},[394,1428,1429],{"class":404}," booleanValue ",[394,1431,464],{"class":463},[394,1433,1434],{"class":404}," client.",[394,1436,1437],{"class":400},"getBooleanValue",[394,1439,482],{"class":404},[394,1441,1442],{"class":467},"\"my-config-key\"",[394,1444,1195],{"class":404},[394,1446,1448],{"class":1447},"syTEX","false",[394,1450,488],{"class":404},[394,1452,1453],{"class":396,"line":408},[394,1454,762],{"emptyLinePlaceholder":54},[394,1456,1457,1459,1462,1464,1466,1469,1471,1474,1476,1479],{"class":396,"line":416},[394,1458,972],{"class":463},[394,1460,1461],{"class":404}," stringValue ",[394,1463,464],{"class":463},[394,1465,1434],{"class":404},[394,1467,1468],{"class":400},"getStringValue",[394,1470,482],{"class":404},[394,1472,1473],{"class":467},"\"my-string-config-key\"",[394,1475,1195],{"class":404},[394,1477,1478],{"class":467},"\"Default\"",[394,1480,488],{"class":404},[265,1482,1483],{},"Each OpenFeature getter maps to a ConfigDirector config type:",[282,1485,1486,1496],{},[285,1487,1488],{},[288,1489,1490,1493],{},[291,1491,1492],{},"OpenFeature getter",[291,1494,1495],{},"ConfigDirector config value",[301,1497,1498,1507,1516,1529],{},[288,1499,1500,1504],{},[306,1501,1502],{},[309,1503,1437],{},[306,1505,1506],{},"Boolean",[288,1508,1509,1513],{},[306,1510,1511],{},[309,1512,1468],{},[306,1514,1515],{},"String or enum",[288,1517,1518,1526],{},[306,1519,1520,1195,1523],{},[309,1521,1522],{},"getIntegerValue",[309,1524,1525],{},"getDoubleValue",[306,1527,1528],{},"Number",[288,1530,1531,1536],{},[306,1532,1533],{},[309,1534,1535],{},"getObjectValue",[306,1537,1538],{},"JSON object or JSON array",[265,1540,1541,1543,1544,1547,1548,1550,1551,1079],{},[309,1542,1522],{}," takes and returns an ",[309,1545,1546],{},"Int",", as the OpenFeature Kotlin SDK defines it; a value written as a decimal is truncated. ",[309,1549,1468],{}," reads a string, enum, URL, or custom config, and a JSON config as its raw document; a boolean, integer, or float config resolves to the default value with the ",[309,1552,1553],{},"TYPE_MISMATCH",[265,1555,1556,1557,1559,1560,1563,1564,1567,1568,1195,1571,1195,1574,368,1577,1580,1581,368,1584,1587,1588,1079],{},"For ",[309,1558,1535],{},", the default value decides the type the config is read as: a ",[309,1561,1562],{},"Value.Structure"," reads a JSON config as a structure, a ",[309,1565,1566],{},"Value.List"," reads one as a list, and a ",[309,1569,1570],{},"Value.String",[309,1572,1573],{},"Value.Integer",[309,1575,1576],{},"Value.Double",[309,1578,1579],{},"Value.Boolean"," reads the config as that type. A ",[309,1582,1583],{},"Value.Null",[309,1585,1586],{},"Value.Instant"," default names no type to read, so it resolves to itself with the ",[309,1589,1553],{},[385,1591,1593],{"className":387,"code":1592,"filename":1420,"language":389,"meta":390,"style":390},"import dev.openfeature.kotlin.sdk.Value\n\nval settings = client.getObjectValue(\"my-json-config-key\", Value.Structure(emptyMap()))\nval theme = settings.asStructure()?.get(\"theme\")?.asString()\n",[309,1594,1595,1602,1606,1638],{"__ignoreMap":390},[394,1596,1597,1599],{"class":396,"line":397},[394,1598,935],{"class":463},[394,1600,1601],{"class":737}," dev.openfeature.kotlin.sdk.Value\n",[394,1603,1604],{"class":396,"line":408},[394,1605,762],{"emptyLinePlaceholder":54},[394,1607,1608,1610,1613,1615,1617,1619,1621,1624,1627,1630,1632,1635],{"class":396,"line":416},[394,1609,972],{"class":463},[394,1611,1612],{"class":404}," settings ",[394,1614,464],{"class":463},[394,1616,1434],{"class":404},[394,1618,1535],{"class":400},[394,1620,482],{"class":404},[394,1622,1623],{"class":467},"\"my-json-config-key\"",[394,1625,1626],{"class":404},", Value.",[394,1628,1629],{"class":400},"Structure",[394,1631,482],{"class":404},[394,1633,1634],{"class":400},"emptyMap",[394,1636,1637],{"class":404},"()))\n",[394,1639,1640,1642,1645,1647,1650,1653,1656,1659,1661,1664,1667,1670],{"class":396,"line":425},[394,1641,972],{"class":463},[394,1643,1644],{"class":404}," theme ",[394,1646,464],{"class":463},[394,1648,1649],{"class":404}," settings.",[394,1651,1652],{"class":400},"asStructure",[394,1654,1655],{"class":404},"()?.",[394,1657,1658],{"class":400},"get",[394,1660,482],{"class":404},[394,1662,1663],{"class":467},"\"theme\"",[394,1665,1666],{"class":404},")?.",[394,1668,1669],{"class":400},"asString",[394,1671,422],{"class":404},[265,1673,1674,1675,280],{},"For additional information regarding the OpenFeature client ",[269,1676,1678],{"href":271,"rel":1677,"target":274},[273],"refer to the OpenFeature Kotlin SDK documentation",[1266,1680,1682],{"id":1681},"evaluation-details","Evaluation details",[265,1684,1685,1686,1689],{},"The detailed getters of the OpenFeature client, such as ",[309,1687,1688],{},"getBooleanDetails",", report why an evaluation produced the value that it did:",[282,1691,1692,1705],{},[285,1693,1694],{},[288,1695,1696,1699,1702],{},[291,1697,1698],{},"Outcome",[291,1700,1701],{},"Reason",[291,1703,1704],{},"Error code",[301,1706,1707,1719,1731,1746,1759],{},[288,1708,1709,1712,1717],{},[306,1710,1711],{},"A value was found",[306,1713,1714],{},[309,1715,1716],{},"TARGETING_MATCH",[306,1718],{},[288,1720,1721,1724,1729],{},[306,1722,1723],{},"The config carries no value",[306,1725,1726],{},[309,1727,1728],{},"DEFAULT",[306,1730],{},[288,1732,1733,1736,1741],{},[306,1734,1735],{},"The config key is unknown",[306,1737,1738],{},[309,1739,1740],{},"ERROR",[306,1742,1743],{},[309,1744,1745],{},"FLAG_NOT_FOUND",[288,1747,1748,1751,1755],{},[306,1749,1750],{},"No config state has arrived yet",[306,1752,1753],{},[309,1754,1740],{},[306,1756,1757],{},[309,1758,1078],{},[288,1760,1761,1764,1768],{},[306,1762,1763],{},"The value does not match the requested type",[306,1765,1766],{},[309,1767,1740],{},[306,1769,1770],{},[309,1771,1553],{},[265,1773,1774,1775,1778],{},"When a value was found, the ",[309,1776,1777],{},"variant"," is ConfigDirector's identifier for that value. In every other case the default value is returned.",[385,1780,1782],{"className":387,"code":1781,"filename":1420,"language":389,"meta":390,"style":390},"val details = client.getBooleanDetails(\"my-config-key\", false)\nLog.i(\"MyApp\", \"my-config-key is ${details.value} because ${details.reason} (variant ${details.variant})\")\n",[309,1783,1784,1807],{"__ignoreMap":390},[394,1785,1786,1788,1791,1793,1795,1797,1799,1801,1803,1805],{"class":396,"line":397},[394,1787,972],{"class":463},[394,1789,1790],{"class":404}," details ",[394,1792,464],{"class":463},[394,1794,1434],{"class":404},[394,1796,1688],{"class":400},[394,1798,482],{"class":404},[394,1800,1442],{"class":467},[394,1802,1195],{"class":404},[394,1804,1448],{"class":1447},[394,1806,488],{"class":404},[394,1808,1809,1812,1815,1817,1820,1822,1825,1828,1832,1835,1838,1841,1843,1846,1848,1851,1853,1856,1858,1861],{"class":396,"line":408},[394,1810,1811],{"class":404},"Log.",[394,1813,1814],{"class":400},"i",[394,1816,482],{"class":404},[394,1818,1819],{"class":467},"\"MyApp\"",[394,1821,1195],{"class":404},[394,1823,1824],{"class":467},"\"my-config-key is ",[394,1826,1827],{"class":598},"${",[394,1829,1831],{"class":1830},"sfo-9","details.",[394,1833,1834],{"class":1150},"value",[394,1836,1837],{"class":598},"}",[394,1839,1840],{"class":467}," because ",[394,1842,1827],{"class":598},[394,1844,1845],{"class":1830},"details.reason",[394,1847,1837],{"class":598},[394,1849,1850],{"class":467}," (variant ",[394,1852,1827],{"class":598},[394,1854,1855],{"class":1830},"details.variant",[394,1857,1837],{"class":598},[394,1859,1860],{"class":467},")\"",[394,1862,488],{"class":404},[1266,1864,1866],{"id":1865},"reading-configs-in-jetpack-compose","Reading configs in Jetpack Compose",[265,1868,1869],{},"The getters are synchronous and make no network calls, so they are safe to call directly from a composable. That gives you the current value, but the composable will not recompose on its own when the value changes.",[265,1871,1872,1873,1875,1876,1060,1879,1881],{},"To have a composable follow a config, re-read it whenever the SDK status moves or the provider reports a configuration change. ",[309,1874,1244],{}," carries the status, and ",[309,1877,1878],{},"OpenFeatureAPI.observe()",[309,1880,1248],{}," of the provider's events:",[385,1883,1886],{"className":387,"code":1884,"filename":1885,"language":389,"meta":390,"style":390},"import androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.collectAsState\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableIntStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport dev.openfeature.kotlin.sdk.OpenFeatureAPI\nimport dev.openfeature.kotlin.sdk.OpenFeatureStatus\nimport dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents\n\n@Composable\nfun DarkModeToggle() {\n    val status by OpenFeatureAPI.statusFlow.collectAsState(initial = OpenFeatureStatus.NotReady)\n    var generation by remember { mutableIntStateOf(0) }\n\n    \u002F\u002F Counts every configuration change, so the read below runs again after each one.\n    LaunchedEffect(Unit) {\n        OpenFeatureAPI.observe\u003COpenFeatureProviderEvents.ProviderConfigurationChanged>()\n            .collect { generation++ }\n    }\n\n    \u002F\u002F Re-read when the status moves -- to Ready once config state arrives, for instance --\n    \u002F\u002F and after every configuration change.\n    val darkMode = remember(status, generation) {\n        OpenFeatureAPI.getClient().getBooleanValue(\"dark-mode\", false)\n    }\n\n    Switch(checked = darkMode, onCheckedChange = null)\n}\n","DarkModeToggle.kt",[309,1887,1888,1895,1902,1909,1916,1923,1930,1937,1943,1950,1957,1961,1966,1976,2001,2029,2033,2038,2047,2069,2086,2091,2096,2102,2108,2123,2146,2151,2156,2177],{"__ignoreMap":390},[394,1889,1890,1892],{"class":396,"line":397},[394,1891,935],{"class":463},[394,1893,1894],{"class":737}," androidx.compose.runtime.Composable\n",[394,1896,1897,1899],{"class":396,"line":408},[394,1898,935],{"class":463},[394,1900,1901],{"class":737}," androidx.compose.runtime.LaunchedEffect\n",[394,1903,1904,1906],{"class":396,"line":416},[394,1905,935],{"class":463},[394,1907,1908],{"class":737}," androidx.compose.runtime.collectAsState\n",[394,1910,1911,1913],{"class":396,"line":425},[394,1912,935],{"class":463},[394,1914,1915],{"class":737}," androidx.compose.runtime.getValue\n",[394,1917,1918,1920],{"class":396,"line":433},[394,1919,935],{"class":463},[394,1921,1922],{"class":737}," androidx.compose.runtime.mutableIntStateOf\n",[394,1924,1925,1927],{"class":396,"line":441},[394,1926,935],{"class":463},[394,1928,1929],{"class":737}," androidx.compose.runtime.remember\n",[394,1931,1932,1934],{"class":396,"line":449},[394,1933,935],{"class":463},[394,1935,1936],{"class":737}," androidx.compose.runtime.setValue\n",[394,1938,1939,1941],{"class":396,"line":457},[394,1940,935],{"class":463},[394,1942,952],{"class":737},[394,1944,1945,1947],{"class":396,"line":471},[394,1946,935],{"class":463},[394,1948,1949],{"class":737}," dev.openfeature.kotlin.sdk.OpenFeatureStatus\n",[394,1951,1952,1954],{"class":396,"line":491},[394,1953,935],{"class":463},[394,1955,1956],{"class":737}," dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents\n",[394,1958,1959],{"class":396,"line":497},[394,1960,762],{"emptyLinePlaceholder":54},[394,1962,1963],{"class":396,"line":503},[394,1964,1965],{"class":737},"@Composable\n",[394,1967,1968,1971,1974],{"class":396,"line":511},[394,1969,1970],{"class":463},"fun",[394,1972,1973],{"class":400}," DarkModeToggle",[394,1975,1141],{"class":404},[394,1977,1978,1981,1984,1987,1990,1993,1996,1998],{"class":396,"line":524},[394,1979,1980],{"class":463},"    val",[394,1982,1983],{"class":404}," status ",[394,1985,1986],{"class":1150},"by",[394,1988,1989],{"class":404}," OpenFeatureAPI.statusFlow.",[394,1991,1992],{"class":400},"collectAsState",[394,1994,1995],{"class":404},"(initial ",[394,1997,464],{"class":463},[394,1999,2000],{"class":404}," OpenFeatureStatus.NotReady)\n",[394,2002,2003,2006,2009,2011,2014,2017,2020,2022,2026],{"class":396,"line":529},[394,2004,2005],{"class":463},"    var",[394,2007,2008],{"class":404}," generation ",[394,2010,1986],{"class":1150},[394,2012,2013],{"class":400}," remember",[394,2015,2016],{"class":404}," { ",[394,2018,2019],{"class":400},"mutableIntStateOf",[394,2021,482],{"class":404},[394,2023,2025],{"class":2024},"srdBf","0",[394,2027,2028],{"class":404},") }\n",[394,2030,2031],{"class":396,"line":535},[394,2032,762],{"emptyLinePlaceholder":54},[394,2034,2035],{"class":396,"line":541},[394,2036,2037],{"class":961},"    \u002F\u002F Counts every configuration change, so the read below runs again after each one.\n",[394,2039,2041,2044],{"class":396,"line":2040},18,[394,2042,2043],{"class":400},"    LaunchedEffect",[394,2045,2046],{"class":404},"(Unit) {\n",[394,2048,2050,2052,2055,2058,2061,2063,2066],{"class":396,"line":2049},19,[394,2051,1204],{"class":404},[394,2053,2054],{"class":400},"observe",[394,2056,2057],{"class":404},"\u003C",[394,2059,2060],{"class":737},"OpenFeatureProviderEvents",[394,2062,280],{"class":404},[394,2064,2065],{"class":737},"ProviderConfigurationChanged",[394,2067,2068],{"class":404},">()\n",[394,2070,2072,2075,2078,2081,2084],{"class":396,"line":2071},20,[394,2073,2074],{"class":404},"            .",[394,2076,2077],{"class":400},"collect",[394,2079,2080],{"class":404}," { generation",[394,2082,2083],{"class":463},"++",[394,2085,816],{"class":404},[394,2087,2089],{"class":396,"line":2088},21,[394,2090,538],{"class":404},[394,2092,2094],{"class":396,"line":2093},22,[394,2095,762],{"emptyLinePlaceholder":54},[394,2097,2099],{"class":396,"line":2098},23,[394,2100,2101],{"class":961},"    \u002F\u002F Re-read when the status moves -- to Ready once config state arrives, for instance --\n",[394,2103,2105],{"class":396,"line":2104},24,[394,2106,2107],{"class":961},"    \u002F\u002F and after every configuration change.\n",[394,2109,2111,2113,2116,2118,2120],{"class":396,"line":2110},25,[394,2112,1980],{"class":463},[394,2114,2115],{"class":404}," darkMode ",[394,2117,464],{"class":463},[394,2119,2013],{"class":400},[394,2121,2122],{"class":404},"(status, generation) {\n",[394,2124,2126,2128,2130,2133,2135,2137,2140,2142,2144],{"class":396,"line":2125},26,[394,2127,1204],{"class":404},[394,2129,1046],{"class":400},[394,2131,2132],{"class":404},"().",[394,2134,1437],{"class":400},[394,2136,482],{"class":404},[394,2138,2139],{"class":467},"\"dark-mode\"",[394,2141,1195],{"class":404},[394,2143,1448],{"class":1447},[394,2145,488],{"class":404},[394,2147,2149],{"class":396,"line":2148},27,[394,2150,538],{"class":404},[394,2152,2154],{"class":396,"line":2153},28,[394,2155,762],{"emptyLinePlaceholder":54},[394,2157,2159,2162,2165,2167,2170,2172,2175],{"class":396,"line":2158},29,[394,2160,2161],{"class":400},"    Switch",[394,2163,2164],{"class":404},"(checked ",[394,2166,464],{"class":463},[394,2168,2169],{"class":404}," darkMode, onCheckedChange ",[394,2171,464],{"class":463},[394,2173,2174],{"class":1164}," null",[394,2176,488],{"class":404},[394,2178,2180],{"class":396,"line":2179},30,[394,2181,544],{"class":404},[265,2183,2184,2185,2187],{},"Until the status is ",[309,2186,1240],{},", the read returns the default value, which is a good moment to show a loading state rather than flashing the in-code default on a slow connection.",[260,2189,2191],{"id":2190},"update-the-user-context","Update the user context",[385,2193,2195],{"className":387,"code":2194,"filename":1420,"language":389,"meta":390,"style":390},"import dev.openfeature.kotlin.sdk.ImmutableContext\nimport dev.openfeature.kotlin.sdk.OpenFeatureAPI\nimport dev.openfeature.kotlin.sdk.Value\n\nOpenFeatureAPI.setEvaluationContextAndWait(\n    ImmutableContext(\n        targetingKey = \"12345\", \u002F\u002F In OpenFeature, the targeting key represents the context's user ID\n        attributes = mapOf(\n            \"name\" to Value.String(\"Example User\"),\n            \u002F\u002F Any arbitrary traits which can be referenced in targeting rules\n            \"traits\" to Value.Structure(mapOf(\"region\" to Value.String(\"North America\"))),\n        ),\n    ),\n)\n",[309,2196,2197,2203,2209,2215,2219,2228,2235,2250,2262,2281,2286,2317,2322,2327],{"__ignoreMap":390},[394,2198,2199,2201],{"class":396,"line":397},[394,2200,935],{"class":463},[394,2202,945],{"class":737},[394,2204,2205,2207],{"class":396,"line":408},[394,2206,935],{"class":463},[394,2208,952],{"class":737},[394,2210,2211,2213],{"class":396,"line":416},[394,2212,935],{"class":463},[394,2214,1601],{"class":737},[394,2216,2217],{"class":396,"line":425},[394,2218,762],{"emptyLinePlaceholder":54},[394,2220,2221,2223,2226],{"class":396,"line":433},[394,2222,1007],{"class":404},[394,2224,2225],{"class":400},"setEvaluationContextAndWait",[394,2227,1318],{"class":404},[394,2229,2230,2233],{"class":396,"line":441},[394,2231,2232],{"class":400},"    ImmutableContext",[394,2234,1318],{"class":404},[394,2236,2237,2240,2242,2245,2247],{"class":396,"line":449},[394,2238,2239],{"class":404},"        targetingKey ",[394,2241,464],{"class":463},[394,2243,2244],{"class":467}," \"12345\"",[394,2246,1195],{"class":404},[394,2248,2249],{"class":961},"\u002F\u002F In OpenFeature, the targeting key represents the context's user ID\n",[394,2251,2252,2255,2257,2260],{"class":396,"line":457},[394,2253,2254],{"class":404},"        attributes ",[394,2256,464],{"class":463},[394,2258,2259],{"class":400}," mapOf",[394,2261,1318],{"class":404},[394,2263,2264,2267,2270,2273,2275,2278],{"class":396,"line":471},[394,2265,2266],{"class":467},"            \"name\"",[394,2268,2269],{"class":404}," to Value.",[394,2271,2272],{"class":400},"String",[394,2274,482],{"class":404},[394,2276,2277],{"class":467},"\"Example User\"",[394,2279,2280],{"class":404},"),\n",[394,2282,2283],{"class":396,"line":491},[394,2284,2285],{"class":961},"            \u002F\u002F Any arbitrary traits which can be referenced in targeting rules\n",[394,2287,2288,2291,2293,2295,2297,2300,2302,2305,2307,2309,2311,2314],{"class":396,"line":497},[394,2289,2290],{"class":467},"            \"traits\"",[394,2292,2269],{"class":404},[394,2294,1629],{"class":400},[394,2296,482],{"class":404},[394,2298,2299],{"class":400},"mapOf",[394,2301,482],{"class":404},[394,2303,2304],{"class":467},"\"region\"",[394,2306,2269],{"class":404},[394,2308,2272],{"class":400},[394,2310,482],{"class":404},[394,2312,2313],{"class":467},"\"North America\"",[394,2315,2316],{"class":404},"))),\n",[394,2318,2319],{"class":396,"line":503},[394,2320,2321],{"class":404},"        ),\n",[394,2323,2324],{"class":396,"line":511},[394,2325,2326],{"class":404},"    ),\n",[394,2328,2329],{"class":396,"line":524},[394,2330,488],{"class":404},[265,2332,2333],{},"The evaluation context maps onto the ConfigDirector user context as follows:",[282,2335,2336,2346],{},[285,2337,2338],{},[288,2339,2340,2343],{},[291,2341,2342],{},"OpenFeature evaluation context",[291,2344,2345],{},"ConfigDirector user context",[301,2347,2348,2361,2373,2385],{},[288,2349,2350,2357],{},[306,2351,2352,2353,2356],{},"The targeting key, or otherwise an ",[309,2354,2355],{},"id"," attribute",[306,2358,2359],{},[309,2360,2355],{},[288,2362,2363,2369],{},[306,2364,2365,2366,2356],{},"The ",[309,2367,2368],{},"name",[306,2370,2371],{},[309,2372,2368],{},[288,2374,2375,2381],{},[306,2376,2365,2377,2380],{},[309,2378,2379],{},"traits"," structure attribute",[306,2382,2383],{},[309,2384,2379],{},[288,2386,2387,2393],{},[306,2388,2389,2390,2356],{},"The boolean ",[309,2391,2392],{},"anonymous",[306,2394,2395],{},[309,2396,2392],{},[265,2398,2399,2400,2402,2403,280],{},"Any other attribute is ignored. Put the values your targeting rules depend on inside ",[309,2401,2379],{},". Traits are JSON-shaped values; a context the SDK could not send, such as one holding a trait that is not a finite number, is rejected with an ",[309,2404,2405],{},"InvalidContextError",[333,2407,2408,2409,2412,2413,2416,2417,2419,2420,2422,2423,2425],{},"In client SDKs (browser and mobile), updating the user context re-establishes a new connection to ConfigDirector servers with the new context. While the new connection is in flight, the OpenFeature SDK reports the ",[309,2410,2411],{},"Reconciling"," status and config values continue to evaluate to the currently cached values from the prior user context.",[2414,2415],"br",{},"\nAwaiting ",[309,2418,2225],{}," will wait until the new config values are downloaded or the connection times out. In the case of a timeout, the provider reports the ",[309,2421,1070],{}," status and continues to attempt to connect with the new context in the background, then emits ",[309,2424,1074],{}," once it succeeds.",[265,2427,2428,2431,2432,2434],{},[309,2429,2430],{},"setEvaluationContext",", without the wait, returns at once and reconciles in the background; follow ",[309,2433,1244],{}," to know when the new values are in effect.",[265,2436,1674,2437,280],{},[269,2438,1678],{"href":271,"rel":2439,"target":274},[273],[260,2441,2443],{"id":2442},"events","Events",[265,2445,2446,2447,2449,2450,2452,2453,2455],{},"The provider publishes its events through ",[309,2448,1878],{},", a ",[309,2451,1248],{}," filtered by event type. It emits ",[309,2454,2065],{}," whenever configs are updated on the dashboard or via the admin API, carrying the keys of the configs in the update:",[385,2457,2459],{"className":387,"code":2458,"filename":928,"language":389,"meta":390,"style":390},"import dev.openfeature.kotlin.sdk.OpenFeatureAPI\nimport dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents\n\nlifecycleScope.launch {\n    OpenFeatureAPI.observe\u003COpenFeatureProviderEvents.ProviderConfigurationChanged>().collect { event ->\n        Log.i(\"MyApp\", \"Configs updated: ${event.eventDetails?.flagsChanged}\")\n    }\n}\n\nlifecycleScope.launch {\n    OpenFeatureAPI.statusFlow.collect { status ->\n        Log.i(\"MyApp\", \"OpenFeature status: $status\")\n    }\n}\n",[309,2460,2461,2467,2473,2477,2487,2513,2540,2544,2548,2552,2560,2572,2595,2599],{"__ignoreMap":390},[394,2462,2463,2465],{"class":396,"line":397},[394,2464,935],{"class":463},[394,2466,952],{"class":737},[394,2468,2469,2471],{"class":396,"line":408},[394,2470,935],{"class":463},[394,2472,1956],{"class":737},[394,2474,2475],{"class":396,"line":416},[394,2476,762],{"emptyLinePlaceholder":54},[394,2478,2479,2482,2485],{"class":396,"line":425},[394,2480,2481],{"class":404},"lifecycleScope.",[394,2483,2484],{"class":400},"launch",[394,2486,405],{"class":404},[394,2488,2489,2492,2494,2496,2498,2500,2502,2505,2507,2510],{"class":396,"line":433},[394,2490,2491],{"class":404},"    OpenFeatureAPI.",[394,2493,2054],{"class":400},[394,2495,2057],{"class":404},[394,2497,2060],{"class":737},[394,2499,280],{"class":404},[394,2501,2065],{"class":737},[394,2503,2504],{"class":404},">().",[394,2506,2077],{"class":400},[394,2508,2509],{"class":404}," { event ",[394,2511,2512],{"class":1150},"->\n",[394,2514,2515,2518,2520,2522,2524,2526,2529,2531,2534,2536,2538],{"class":396,"line":441},[394,2516,2517],{"class":404},"        Log.",[394,2519,1814],{"class":400},[394,2521,482],{"class":404},[394,2523,1819],{"class":467},[394,2525,1195],{"class":404},[394,2527,2528],{"class":467},"\"Configs updated: ",[394,2530,1827],{"class":598},[394,2532,2533],{"class":1830},"event.eventDetails?.flagsChanged",[394,2535,1837],{"class":598},[394,2537,793],{"class":467},[394,2539,488],{"class":404},[394,2541,2542],{"class":396,"line":449},[394,2543,538],{"class":404},[394,2545,2546],{"class":396,"line":457},[394,2547,544],{"class":404},[394,2549,2550],{"class":396,"line":471},[394,2551,762],{"emptyLinePlaceholder":54},[394,2553,2554,2556,2558],{"class":396,"line":491},[394,2555,2481],{"class":404},[394,2557,2484],{"class":400},[394,2559,405],{"class":404},[394,2561,2562,2565,2567,2570],{"class":396,"line":497},[394,2563,2564],{"class":404},"    OpenFeatureAPI.statusFlow.",[394,2566,2077],{"class":400},[394,2568,2569],{"class":404}," { status ",[394,2571,2512],{"class":1150},[394,2573,2574,2576,2578,2580,2582,2584,2587,2591,2593],{"class":396,"line":503},[394,2575,2517],{"class":404},[394,2577,1814],{"class":400},[394,2579,482],{"class":404},[394,2581,1819],{"class":467},[394,2583,1195],{"class":404},[394,2585,2586],{"class":467},"\"OpenFeature status: ",[394,2588,2590],{"class":2589},"s_hVV","$status",[394,2592,793],{"class":467},[394,2594,488],{"class":404},[394,2596,2597],{"class":396,"line":511},[394,2598,538],{"class":404},[394,2600,2601],{"class":396,"line":524},[394,2602,544],{"class":404},[265,2604,2605,2606,2608,2609,2611,2612,280],{},"It emits ",[309,2607,1074],{}," when config state arrives after initialization or a context change has already timed out, which moves the status from ",[309,2610,1070],{}," back to ",[309,2613,1240],{},[260,2615,2617],{"id":2616},"test-your-code","Test your code",[265,2619,2620,2621,2625,2626,1195,2630,2633,2634,2637],{},"Code that reads flags through OpenFeature is tested by swapping the provider, so it never has to change for a test. The OpenFeature Kotlin SDK ships no in-memory provider yet, so this provider accepts a ",[2622,2623,2624],"strong",{},"test client"," from the ",[269,2627,2629],{"href":2628},"\u002Fsdks\u002Fmobile\u002Fandroid#test-your-code","Android SDK's testing tools",[309,2631,2632],{},"com.configdirector:android-sdk-testing",", instead: the SDK's real client connected to an in-memory server your test controls. The provider then resolves every flag from the values the test sets, opens no network connection, sends no telemetry, and needs no Android ",[309,2635,2636],{},"Context",", so the test runs in a plain JVM unit test, under Robolectric, or in an instrumented test alike.",[265,2639,2640,2641,2644,2645,2648],{},"The testing tools must be the same version as the ",[309,2642,2643],{},"android-sdk"," on your test classpath, which the provider brings transitively. Add them in test scope, with ",[309,2646,2647],{},"kotlinx-coroutines-test"," for the Main dispatcher a plain JVM test lacks:",[382,2650,2651,2689],{},[385,2652,2654],{"className":387,"code":2653,"filename":669,"language":389,"meta":390,"style":390},"dependencies {\n    testImplementation(\"com.configdirector:android-sdk-testing:1.7.0\")\n    testImplementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0\")\n}\n",[309,2655,2656,2662,2674,2685],{"__ignoreMap":390},[394,2657,2658,2660],{"class":396,"line":397},[394,2659,676],{"class":400},[394,2661,405],{"class":404},[394,2663,2664,2667,2669,2672],{"class":396,"line":408},[394,2665,2666],{"class":400},"    testImplementation",[394,2668,482],{"class":404},[394,2670,2671],{"class":467},"\"com.configdirector:android-sdk-testing:1.7.0\"",[394,2673,488],{"class":404},[394,2675,2676,2678,2680,2683],{"class":396,"line":416},[394,2677,2666],{"class":400},[394,2679,482],{"class":404},[394,2681,2682],{"class":467},"\"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0\"",[394,2684,488],{"class":404},[394,2686,2687],{"class":396,"line":425},[394,2688,544],{"class":404},[385,2690,2692],{"className":547,"code":2691,"filename":698,"language":549,"meta":390,"style":390},"dependencies {\n    testImplementation 'com.configdirector:android-sdk-testing:1.7.0'\n    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0'\n}\n",[309,2693,2694,2698,2710,2721],{"__ignoreMap":390},[394,2695,2696],{"class":396,"line":397},[394,2697,705],{"class":404},[394,2699,2700,2703,2705,2708],{"class":396,"line":408},[394,2701,2702],{"class":404},"    testImplementation ",[394,2704,639],{"class":598},[394,2706,2707],{"class":467},"com.configdirector:android-sdk-testing:1.7.0",[394,2709,605],{"class":598},[394,2711,2712,2714,2716,2719],{"class":396,"line":416},[394,2713,2702],{"class":404},[394,2715,639],{"class":598},[394,2717,2718],{"class":467},"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0",[394,2720,605],{"class":598},[394,2722,2723],{"class":396,"line":425},[394,2724,544],{"class":404},[265,2726,2727,2728,2731],{},"The constructor that takes a client sits behind the ",[309,2729,2730],{},"@ConfigDirectorProviderTestingApi"," opt-in, which keeps it out of application code, so the test opts in:",[385,2733,2736],{"className":387,"code":2734,"filename":2735,"language":389,"meta":390,"style":390},"import com.configdirector.openfeature.ConfigDirectorProvider\nimport com.configdirector.openfeature.ConfigDirectorProviderTestingApi\nimport com.configdirector.testing.createTestClient\nimport dev.openfeature.kotlin.sdk.ImmutableContext\nimport dev.openfeature.kotlin.sdk.OpenFeatureAPI\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.test.UnconfinedTestDispatcher\nimport kotlinx.coroutines.test.resetMain\nimport kotlinx.coroutines.test.setMain\n\n@OptIn(ConfigDirectorProviderTestingApi::class)\nclass CheckoutTest {\n\n    @Before\n    fun setUpMainDispatcher() = Dispatchers.setMain(UnconfinedTestDispatcher())\n\n    @After\n    fun tearDown() = runBlocking {\n        OpenFeatureAPI.shutdown()\n        Dispatchers.resetMain()\n    }\n\n    @Test\n    fun `shows the new checkout`() = runBlocking {\n        val testClient = createTestClient(values = mapOf(\"new-checkout\" to true, \"max-items\" to 20))\n        OpenFeatureAPI.setProviderAndWait(\n            ConfigDirectorProvider(testClient.client),\n            ImmutableContext(targetingKey = \"user-123\"),\n        )\n        val client = OpenFeatureAPI.getClient()\n\n        assertThat(client.getBooleanValue(\"new-checkout\", false)).isTrue()\n        assertThat(client.getIntegerValue(\"max-items\", 0)).isEqualTo(20)\n\n        testClient.setValue(\"new-checkout\", false)\n        assertThat(client.getBooleanValue(\"new-checkout\", false)).isFalse()\n\n        testClient.client.close()\n    }\n}\n","CheckoutTest.kt",[309,2737,2738,2744,2751,2758,2764,2770,2777,2784,2791,2798,2805,2809,2821,2830,2834,2839,2866,2870,2875,2891,2899,2909,2913,2917,2922,2937,2979,2987,2995,3008,3013,3028,3033,3060,3088,3093,3112,3136,3141,3152,3157],{"__ignoreMap":390},[394,2739,2740,2742],{"class":396,"line":397},[394,2741,935],{"class":463},[394,2743,938],{"class":737},[394,2745,2746,2748],{"class":396,"line":408},[394,2747,935],{"class":463},[394,2749,2750],{"class":737}," com.configdirector.openfeature.ConfigDirectorProviderTestingApi\n",[394,2752,2753,2755],{"class":396,"line":416},[394,2754,935],{"class":463},[394,2756,2757],{"class":737}," com.configdirector.testing.createTestClient\n",[394,2759,2760,2762],{"class":396,"line":425},[394,2761,935],{"class":463},[394,2763,945],{"class":737},[394,2765,2766,2768],{"class":396,"line":433},[394,2767,935],{"class":463},[394,2769,952],{"class":737},[394,2771,2772,2774],{"class":396,"line":441},[394,2773,935],{"class":463},[394,2775,2776],{"class":737}," kotlinx.coroutines.Dispatchers\n",[394,2778,2779,2781],{"class":396,"line":449},[394,2780,935],{"class":463},[394,2782,2783],{"class":737}," kotlinx.coroutines.runBlocking\n",[394,2785,2786,2788],{"class":396,"line":457},[394,2787,935],{"class":463},[394,2789,2790],{"class":737}," kotlinx.coroutines.test.UnconfinedTestDispatcher\n",[394,2792,2793,2795],{"class":396,"line":471},[394,2794,935],{"class":463},[394,2796,2797],{"class":737}," kotlinx.coroutines.test.resetMain\n",[394,2799,2800,2802],{"class":396,"line":491},[394,2801,935],{"class":463},[394,2803,2804],{"class":737}," kotlinx.coroutines.test.setMain\n",[394,2806,2807],{"class":396,"line":497},[394,2808,762],{"emptyLinePlaceholder":54},[394,2810,2811,2814,2817,2819],{"class":396,"line":503},[394,2812,2813],{"class":737},"@OptIn",[394,2815,2816],{"class":404},"(ConfigDirectorProviderTestingApi::",[394,2818,1130],{"class":400},[394,2820,488],{"class":404},[394,2822,2823,2825,2828],{"class":396,"line":511},[394,2824,1130],{"class":463},[394,2826,2827],{"class":737}," CheckoutTest",[394,2829,405],{"class":404},[394,2831,2832],{"class":396,"line":524},[394,2833,762],{"emptyLinePlaceholder":54},[394,2835,2836],{"class":396,"line":529},[394,2837,2838],{"class":737},"    @Before\n",[394,2840,2841,2844,2847,2850,2852,2855,2858,2860,2863],{"class":396,"line":535},[394,2842,2843],{"class":463},"    fun",[394,2845,2846],{"class":400}," setUpMainDispatcher",[394,2848,2849],{"class":404},"() ",[394,2851,464],{"class":463},[394,2853,2854],{"class":404}," Dispatchers.",[394,2856,2857],{"class":400},"setMain",[394,2859,482],{"class":404},[394,2861,2862],{"class":400},"UnconfinedTestDispatcher",[394,2864,2865],{"class":404},"())\n",[394,2867,2868],{"class":396,"line":541},[394,2869,762],{"emptyLinePlaceholder":54},[394,2871,2872],{"class":396,"line":2040},[394,2873,2874],{"class":737},"    @After\n",[394,2876,2877,2879,2882,2884,2886,2889],{"class":396,"line":2049},[394,2878,2843],{"class":463},[394,2880,2881],{"class":400}," tearDown",[394,2883,2849],{"class":404},[394,2885,464],{"class":463},[394,2887,2888],{"class":400}," runBlocking",[394,2890,405],{"class":404},[394,2892,2893,2895,2897],{"class":396,"line":2071},[394,2894,1204],{"class":404},[394,2896,1401],{"class":400},[394,2898,422],{"class":404},[394,2900,2901,2904,2907],{"class":396,"line":2088},[394,2902,2903],{"class":404},"        Dispatchers.",[394,2905,2906],{"class":400},"resetMain",[394,2908,422],{"class":404},[394,2910,2911],{"class":396,"line":2093},[394,2912,538],{"class":404},[394,2914,2915],{"class":396,"line":2098},[394,2916,762],{"emptyLinePlaceholder":54},[394,2918,2919],{"class":396,"line":2104},[394,2920,2921],{"class":737},"    @Test\n",[394,2923,2924,2926,2929,2931,2933,2935],{"class":396,"line":2110},[394,2925,2843],{"class":463},[394,2927,2928],{"class":400}," `shows the new checkout`",[394,2930,2849],{"class":404},[394,2932,464],{"class":463},[394,2934,2888],{"class":400},[394,2936,405],{"class":404},[394,2938,2939,2941,2944,2946,2949,2952,2954,2956,2958,2961,2964,2967,2969,2972,2974,2977],{"class":396,"line":2125},[394,2940,1181],{"class":463},[394,2942,2943],{"class":404}," testClient ",[394,2945,464],{"class":463},[394,2947,2948],{"class":400}," createTestClient",[394,2950,2951],{"class":404},"(values ",[394,2953,464],{"class":463},[394,2955,2259],{"class":400},[394,2957,482],{"class":404},[394,2959,2960],{"class":467},"\"new-checkout\"",[394,2962,2963],{"class":404}," to ",[394,2965,2966],{"class":1447},"true",[394,2968,1195],{"class":404},[394,2970,2971],{"class":467},"\"max-items\"",[394,2973,2963],{"class":404},[394,2975,2976],{"class":2024},"20",[394,2978,1027],{"class":404},[394,2980,2981,2983,2985],{"class":396,"line":2148},[394,2982,1204],{"class":404},[394,2984,1010],{"class":400},[394,2986,1318],{"class":404},[394,2988,2989,2992],{"class":396,"line":2153},[394,2990,2991],{"class":400},"            ConfigDirectorProvider",[394,2993,2994],{"class":404},"(testClient.client),\n",[394,2996,2997,3000,3002,3004,3006],{"class":396,"line":2158},[394,2998,2999],{"class":400},"            ImmutableContext",[394,3001,1019],{"class":404},[394,3003,464],{"class":463},[394,3005,1024],{"class":467},[394,3007,2280],{"class":404},[394,3009,3010],{"class":396,"line":2179},[394,3011,3012],{"class":404},"        )\n",[394,3014,3016,3018,3020,3022,3024,3026],{"class":396,"line":3015},31,[394,3017,1181],{"class":463},[394,3019,1038],{"class":404},[394,3021,464],{"class":463},[394,3023,1043],{"class":404},[394,3025,1046],{"class":400},[394,3027,422],{"class":404},[394,3029,3031],{"class":396,"line":3030},32,[394,3032,762],{"emptyLinePlaceholder":54},[394,3034,3036,3039,3042,3044,3046,3048,3050,3052,3055,3058],{"class":396,"line":3035},33,[394,3037,3038],{"class":400},"        assertThat",[394,3040,3041],{"class":404},"(client.",[394,3043,1437],{"class":400},[394,3045,482],{"class":404},[394,3047,2960],{"class":467},[394,3049,1195],{"class":404},[394,3051,1448],{"class":1447},[394,3053,3054],{"class":404},")).",[394,3056,3057],{"class":400},"isTrue",[394,3059,422],{"class":404},[394,3061,3063,3065,3067,3069,3071,3073,3075,3077,3079,3082,3084,3086],{"class":396,"line":3062},34,[394,3064,3038],{"class":400},[394,3066,3041],{"class":404},[394,3068,1522],{"class":400},[394,3070,482],{"class":404},[394,3072,2971],{"class":467},[394,3074,1195],{"class":404},[394,3076,2025],{"class":2024},[394,3078,3054],{"class":404},[394,3080,3081],{"class":400},"isEqualTo",[394,3083,482],{"class":404},[394,3085,2976],{"class":2024},[394,3087,488],{"class":404},[394,3089,3091],{"class":396,"line":3090},35,[394,3092,762],{"emptyLinePlaceholder":54},[394,3094,3096,3099,3102,3104,3106,3108,3110],{"class":396,"line":3095},36,[394,3097,3098],{"class":404},"        testClient.",[394,3100,3101],{"class":400},"setValue",[394,3103,482],{"class":404},[394,3105,2960],{"class":467},[394,3107,1195],{"class":404},[394,3109,1448],{"class":1447},[394,3111,488],{"class":404},[394,3113,3115,3117,3119,3121,3123,3125,3127,3129,3131,3134],{"class":396,"line":3114},37,[394,3116,3038],{"class":400},[394,3118,3041],{"class":404},[394,3120,1437],{"class":400},[394,3122,482],{"class":404},[394,3124,2960],{"class":467},[394,3126,1195],{"class":404},[394,3128,1448],{"class":1447},[394,3130,3054],{"class":404},[394,3132,3133],{"class":400},"isFalse",[394,3135,422],{"class":404},[394,3137,3139],{"class":396,"line":3138},38,[394,3140,762],{"emptyLinePlaceholder":54},[394,3142,3144,3147,3150],{"class":396,"line":3143},39,[394,3145,3146],{"class":404},"        testClient.client.",[394,3148,3149],{"class":400},"close",[394,3151,422],{"class":404},[394,3153,3155],{"class":396,"line":3154},40,[394,3156,538],{"class":404},[394,3158,3160],{"class":396,"line":3159},41,[394,3161,544],{"class":404},[265,3163,3164,3165,3167,3168,3170,3171,3174,3175,3178,3179,3182],{},"The provider hands its events to OpenFeature through the client's ",[309,3166,2442],{}," flow, which runs on the Main dispatcher. Robolectric and instrumented tests have one; a plain JVM test sets one from ",[309,3169,2647],{}," as above, and without it the first call that needs one fails with an ",[309,3172,3173],{},"IllegalStateException"," saying that ",[309,3176,3177],{},"Dispatchers.Main"," is missing. ",[309,3180,3181],{},"OpenFeatureAPI"," is one global, so tests that register their own provider on it cannot run in parallel; shut it down after each test, as above, so the next one starts from a clean slate.",[265,3184,3185,3186,1195,3188,1195,3191,3194,3195,3198,3199,3202],{},"The test client's controls are the same as with the Android SDK: ",[309,3187,3101],{},[309,3189,3190],{},"removeValue",[309,3192,3193],{},"replaceValues",", the initialization and context update holds and failures, and ",[309,3196,3197],{},"contextUpdates",". ",[269,3200,3201],{"href":2628},"The Android SDK's testing section"," describes each of them and the values they take. Through the provider:",[3204,3205,3206,3222,3228,3262],"ul",{},[912,3207,3208,3210,3211,3213,3214,3216,3217,3219,3220,1079],{},[309,3209,3101],{}," makes the OpenFeature client resolve the new value at once, with the ",[309,3212,1716],{}," reason, and the provider emit ",[309,3215,2065],{}," with that key; ",[309,3218,3190],{}," makes the flag resolve to its default value with the ",[309,3221,1745],{},[912,3223,3224,3225,3227],{},"The provider initializes the client when it is registered and updates the client's context whenever the evaluation context changes, as it does with a client it creates itself. A test client that was already initialized therefore reconnects on registration, and its ",[309,3226,3197],{}," records the provider's context after the test's own.",[912,3229,3230,3233,3234,3236,3237,3240,3241,3243,3244,3247,3248,3251,3252,3255,3256,3258,3259,3261],{},[309,3231,3232],{},"holdInitialization()"," before registering keeps ",[309,3235,1010],{}," waiting, with the status ",[309,3238,3239],{},"NotReady"," and flags at their default values with the ",[309,3242,1078],{}," error code, until ",[309,3245,3246],{},"completeInitialization()"," or the test client's ",[309,3249,3250],{},"timeoutMillis"," elapses. A timed-out or failed initialization (",[309,3253,3254],{},"failInitialization()",") fails the provider's initialization with a ",[309,3257,1066],{},", so OpenFeature reports the ",[309,3260,1070],{}," status, as a connection failure would.",[912,3263,3264,3265,3267,3268,3271,3272,3275],{},"The provider never closes a client it was given, whether it is shut down explicitly, replaced with ",[309,3266,1089],{},", or shut down with ",[309,3269,3270],{},"OpenFeatureAPI.shutdown()",". Close the test client's ",[309,3273,3274],{},"client"," when the test is done, as in the example.",[3277,3278,3279],"style",{},"html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":390,"searchDepth":408,"depth":408,"links":3281},[3282,3283,3284,3288,3292,3293,3294],{"id":262,"depth":408,"text":263},{"id":356,"depth":408,"text":357},{"id":906,"depth":408,"text":907,"children":3285},[3286,3287],{"id":1268,"depth":416,"text":1269},{"id":1385,"depth":416,"text":1386},{"id":1412,"depth":408,"text":1413,"children":3289},[3290,3291],{"id":1681,"depth":416,"text":1682},{"id":1865,"depth":416,"text":1866},{"id":2190,"depth":408,"text":2191},{"id":2442,"depth":408,"text":2443},{"id":2616,"depth":408,"text":2617},"ConfigDirector Provider for the OpenFeature Kotlin SDK on Android","md",null,{"modifiedAt":3299},"2026-10-12T00:22:49.000Z",{"title":130,"icon":183,"icons":3301},[183,133],{"title":246,"description":3295},"qRjIITQyLzgfdXQloiK8j4S4Ah0vAc8F7CQp80FPsmw",[3305,3308],{"title":125,"path":193,"stem":194,"description":3306,"icon":183,"icons":3307,"children":-1},"ConfigDirector Provider for the OpenFeature Swift SDK",[183,128],{"title":120,"path":201,"stem":202,"description":3309,"icon":183,"icons":3310,"children":-1},"ConfigDirector Provider for the OpenFeature Dart client SDK in Flutter applications",[183,123],1791783867450]