diff --git a/samples/auth-demo/build.gradle.kts b/samples/auth-demo/build.gradle.kts
new file mode 100644
index 00000000..b6f40518
--- /dev/null
+++ b/samples/auth-demo/build.gradle.kts
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024. Uber Technologies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.ubercab.presidio.auth_demo"
+ compileSdk = 34
+
+ defaultConfig {
+ applicationId = "com.ubercab.presidio.demo"
+ minSdk = 26
+ targetSdk = 34
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables { useSupportLibrary = true }
+ }
+
+ buildTypes {
+ getByName("debug") { matchingFallbacks += listOf("release") }
+ release {
+ isMinifyEnabled = false
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions { jvmTarget = "1.8" }
+ buildFeatures { compose = true }
+ composeOptions { kotlinCompilerExtensionVersion = "1.5.11" }
+}
+
+dependencies {
+ implementation(libs.core.ktx)
+ implementation(libs.lifecycle.runtime.ktx)
+ implementation(libs.activity.compose)
+ implementation(platform(libs.compose.bom))
+ implementation(libs.material3)
+ implementation("com.uber.sdk2:core:2.0.1-BETA")
+ implementation("com.uber.sdk2:authentication:2.0.1-BETA")
+ testImplementation(libs.junit.junit)
+}
diff --git a/samples/auth-demo/proguard-rules.pro b/samples/auth-demo/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/samples/auth-demo/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/samples/auth-demo/src/main/AndroidManifest.xml b/samples/auth-demo/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..8163141e
--- /dev/null
+++ b/samples/auth-demo/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/DemoActivity.kt b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/DemoActivity.kt
new file mode 100644
index 00000000..137bb04c
--- /dev/null
+++ b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/DemoActivity.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024. Uber Technologies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.ubercab.presidio.demo
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.uber.sdk2.auth.UberAuthClientImpl
+import com.uber.sdk2.auth.request.AuthContext
+import com.uber.sdk2.auth.request.AuthDestination
+import com.uber.sdk2.auth.request.AuthType
+import com.uber.sdk2.auth.request.CrossApp
+import com.uber.sdk2.auth.request.PrefillInfo
+import com.uber.sdk2.auth.response.UberToken
+import com.uber.sdk2.core.ui.UberButton
+import com.ubercab.presidio.demo.ui.theme.UberandroidsdkTheme
+
+class DemoActivity : ComponentActivity() {
+
+ private val uberAuthLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ logResult(result.resultCode, result.data)
+ }
+
+ private fun logResult(resultCode: Int, data: Intent?) {
+ when (resultCode) {
+ RESULT_OK -> {
+ val uberToken = data?.getParcelableExtra("EXTRA_UBER_TOKEN")
+ Toast.makeText(this, "Uber Token: ${uberToken?.accessToken}", Toast.LENGTH_SHORT).show()
+ }
+ else -> {
+ val errorMessage = data?.getStringExtra("EXTRA_ERROR")
+ Toast.makeText(this, "Error: $errorMessage", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ UberandroidsdkTheme {
+ Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ AuthScreen(
+ context = this@DemoActivity,
+ launcher = uberAuthLauncher,
+ authDestination = AuthDestination.InApp,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AuthScreen(
+ context: Context,
+ launcher: ActivityResultLauncher,
+ authDestination: AuthDestination,
+) {
+ val email = remember { mutableStateOf("") }
+ val firstName = remember { mutableStateOf("") }
+ val lastName = remember { mutableStateOf("") }
+ val phoneNumber = remember { mutableStateOf("") }
+
+ TextFieldWithLabel("Email", email)
+ TextFieldWithLabel("First Name", firstName)
+ TextFieldWithLabel("Last Name", lastName)
+ TextFieldWithLabel("Phone Number", phoneNumber)
+
+ var expanded by remember { mutableStateOf(false) }
+ var selectedOptionText by remember { mutableStateOf("Select an option") }
+ val options = listOf("Rider", "Eats", "Driver", "InApp")
+
+ Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
+ ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = !expanded }) {
+ Box(
+ modifier =
+ Modifier.fillMaxWidth()
+ .clickable { expanded = true }
+ .menuAnchor()
+ .padding(16.dp) // Optional: padding for better touch target
+ ) {
+ TextField(
+ value = selectedOptionText,
+ onValueChange = {},
+ label = { Text("Selected Option") },
+ modifier = Modifier.fillMaxWidth(),
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
+ singleLine = true,
+ enabled = false, // Disable the TextField to prevent keyboard
+ )
+ }
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ options.forEach { label ->
+ DropdownMenuItem(
+ onClick = {
+ selectedOptionText = label
+ expanded = false
+ },
+ text = { Text(text = label) },
+ )
+ }
+ }
+ }
+ }
+
+ val authDestination =
+ when (selectedOptionText) {
+ "Rider" -> AuthDestination.CrossAppSso(listOf(CrossApp.Rider, CrossApp.Eats))
+ "Eats" -> AuthDestination.CrossAppSso(listOf(CrossApp.Eats, CrossApp.Rider))
+ "Driver" -> AuthDestination.CrossAppSso(listOf(CrossApp.Driver, CrossApp.Rider))
+ "InApp" -> AuthDestination.InApp
+ else -> AuthDestination.InApp
+ }
+
+ AuthButton(
+ context = context,
+ launcher = launcher,
+ authDestination = authDestination,
+ buttonText = "Authenticate",
+ PrefillInfo(email.value, firstName.value, lastName.value, phoneNumber.value),
+ )
+}
+
+@Composable
+fun TextFieldWithLabel(label: String, textState: MutableState) {
+ TextField(
+ value = textState.value,
+ onValueChange = { textState.value = it },
+ label = { Text(label) },
+ modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
+ colors =
+ TextFieldDefaults.colors(
+ disabledContainerColor = Color.White,
+ focusedIndicatorColor = MaterialTheme.colorScheme.primary,
+ unfocusedIndicatorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
+ focusedTextColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ )
+}
+
+@Composable
+fun AuthButton(
+ context: Context,
+ launcher: ActivityResultLauncher,
+ authDestination: AuthDestination,
+ buttonText: String,
+ prefillInfo: PrefillInfo? = null,
+) {
+ UberButton(
+ text = buttonText,
+ onClick = {
+ // Invoke the Uber authentication call here
+ UberAuthClientImpl()
+ .authenticate(
+ context as DemoActivity,
+ launcher,
+ AuthContext(authDestination, AuthType.PKCE(), prefillInfo),
+ )
+ },
+ )
+}
diff --git a/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Color.kt b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Color.kt
new file mode 100644
index 00000000..73cba511
--- /dev/null
+++ b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Color.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024. Uber Technologies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.ubercab.presidio.demo.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
diff --git a/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Theme.kt b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Theme.kt
new file mode 100644
index 00000000..669bb451
--- /dev/null
+++ b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Theme.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024. Uber Technologies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.ubercab.presidio.demo.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme =
+ darkColorScheme(primary = Purple80, secondary = PurpleGrey80, tertiary = Pink80)
+
+private val LightColorScheme =
+ lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40,
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+ )
+
+@Composable
+fun UberandroidsdkTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit,
+) {
+ val colorScheme =
+ when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content)
+}
diff --git a/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Type.kt b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Type.kt
new file mode 100644
index 00000000..3b41f6f8
--- /dev/null
+++ b/samples/auth-demo/src/main/java/com/ubercab/presidio/demo/ui/theme/Type.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024. Uber Technologies
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.ubercab.presidio.demo.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography =
+ Typography(
+ bodyLarge =
+ TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp,
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+ )
diff --git a/samples/auth-demo/src/main/res/drawable/ic_launcher_background.xml b/samples/auth-demo/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..07d5da9c
--- /dev/null
+++ b/samples/auth-demo/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/auth-demo/src/main/res/drawable/ic_launcher_foreground.xml b/samples/auth-demo/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 00000000..2b068d11
--- /dev/null
+++ b/samples/auth-demo/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher.png b/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..998bf5f2
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png b/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..338f8888
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launch_round.png b/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launch_round.png
new file mode 100644
index 00000000..770c4ad3
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launch_round.png differ
diff --git a/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launcher.png b/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..cad757b3
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher.png b/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..86844e93
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..0f3bcd91
Binary files /dev/null and b/samples/auth-demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/samples/auth-demo/src/main/res/raw/sso_config.json b/samples/auth-demo/src/main/res/raw/sso_config.json
new file mode 100644
index 00000000..7e886c0e
--- /dev/null
+++ b/samples/auth-demo/src/main/res/raw/sso_config.json
@@ -0,0 +1,5 @@
+{
+ "client_id": "your_client_id",
+ "redirect_uri": "your_redirect_uri",
+ "scope": "your_scopes_comma_separated"
+}
diff --git a/samples/auth-demo/src/main/res/values/colors.xml b/samples/auth-demo/src/main/res/values/colors.xml
new file mode 100644
index 00000000..f8c6127d
--- /dev/null
+++ b/samples/auth-demo/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/samples/auth-demo/src/main/res/values/strings.xml b/samples/auth-demo/src/main/res/values/strings.xml
new file mode 100644
index 00000000..3e25bc54
--- /dev/null
+++ b/samples/auth-demo/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Auth Demo
+
\ No newline at end of file
diff --git a/samples/auth-demo/src/main/res/values/themes.xml b/samples/auth-demo/src/main/res/values/themes.xml
new file mode 100644
index 00000000..675317bd
--- /dev/null
+++ b/samples/auth-demo/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 165cf445..4d6a9505 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -45,6 +45,7 @@ include(
":core",
":core-android",
":rides-android",
+ ":samples:auth-demo",
":samples:request-button-sample",
":samples:login-sample",
":samples:login-with-auth-code-demo",