diff --git a/android/sample/ai-agent-android/ai-agent-sample/.gitignore b/android/sample/ai-agent-android/ai-agent-sample/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/build.gradle.kts b/android/sample/ai-agent-android/ai-agent-sample/build.gradle.kts
new file mode 100644
index 00000000..0af44545
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/build.gradle.kts
@@ -0,0 +1,77 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.jetbrains.kotlin.android)
+ id("com.google.gms.google-services")
+}
+
+val MESSENGER_VERSION: String by project
+android {
+ namespace = "com.sendbird.sdk.aiagent.sample"
+ compileSdk = 35
+ version = MESSENGER_VERSION
+
+ defaultConfig {
+ applicationId = "com.sendbird.sdk.aiagent.sample"
+ minSdk = 21
+ targetSdk = 35
+ versionCode = 1
+ versionName = MESSENGER_VERSION
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary = true
+ }
+ }
+
+ signingConfigs {
+ create("releaseWithSignedKey") {
+ storeFile = file("../aiagent-sample-keystore")
+ storePassword = System.getenv("signedKeyPassword")
+ keyAlias = "sample"
+ keyPassword = System.getenv("signedKeyPassword")
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ isShrinkResources = true
+ signingConfig = signingConfigs.getByName("releaseWithSignedKey")
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ buildFeatures {
+ viewBinding = true
+ buildConfig = true
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+}
+
+dependencies {
+ api(libs.sdk.sendbird.aiagent.messenger)
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.androidx.activity)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+// implementation(libs.androidx.ui)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ implementation(platform("com.google.firebase:firebase-bom:33.8.0"))
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/google-services.json b/android/sample/ai-agent-android/ai-agent-sample/google-services.json
new file mode 100644
index 00000000..13215cc0
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/google-services.json
@@ -0,0 +1,29 @@
+{
+ "project_info": {
+ "project_number": "1096745601007",
+ "project_id": "sb-ai-agent-android",
+ "storage_bucket": "sb-ai-agent-android.firebasestorage.app"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:1096745601007:android:1d288a6178057e6984581a",
+ "android_client_info": {
+ "package_name": "com.sendbird.sdk.aiagent.sample"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyCsMqYa0pxUcKf73hjTLLGJ20zWVGZZGKU"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/proguard-rules.pro b/android/sample/ai-agent-android/ai-agent-sample/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/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/android/sample/ai-agent-android/ai-agent-sample/src/androidTest/java/com/sendbird/sdk/aiagent/sample/ExampleInstrumentedTest.kt b/android/sample/ai-agent-android/ai-agent-sample/src/androidTest/java/com/sendbird/sdk/aiagent/sample/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..65ed7a2e
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/androidTest/java/com/sendbird/sdk/aiagent/sample/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.sendbird.sdk.aiagent.sample
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.sendbird.sdk.aiagent.sample", appContext.packageName)
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/AndroidManifest.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..5b4b1e2e
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/ic_launcher-playstore.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/ic_launcher-playstore.png
new file mode 100644
index 00000000..a6cc84cb
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/ic_launcher-playstore.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/AgentApplication.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/AgentApplication.kt
new file mode 100644
index 00000000..d968fc10
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/AgentApplication.kt
@@ -0,0 +1,70 @@
+package com.sendbird.sdk.aiagent.sample
+
+import android.app.Application
+import android.content.res.Configuration
+import com.sendbird.android.exception.SendbirdException
+import com.sendbird.sdk.aiagent.common.utils.LoggerLevel
+import com.sendbird.sdk.aiagent.messenger.AIAgentMessenger
+import com.sendbird.sdk.aiagent.messenger.consts.MessengerThemeMode
+import com.sendbird.sdk.aiagent.messenger.interfaces.MessengerInitResultHandler
+import com.sendbird.sdk.aiagent.messenger.model.MessengerInitParams
+import com.sendbird.sdk.aiagent.messenger.model.SessionInfo
+import com.sendbird.sdk.aiagent.sample.consts.InitState
+import com.sendbird.sdk.aiagent.sample.model.SampleAppInfo
+import com.sendbird.sdk.aiagent.sample.model.us3
+import com.sendbird.sdk.aiagent.sample.utils.AbstractSessionHandler
+import com.sendbird.sdk.aiagent.sample.utils.PreferenceUtils
+import com.sendbird.sdk.aiagent.sample.utils.apiHost
+import com.sendbird.sdk.aiagent.sample.utils.getCurrentTheme
+import com.sendbird.sdk.aiagent.sample.utils.wsHost
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class AgentApplication : Application(), MessengerInitResultHandler {
+ override fun onCreate() {
+ super.onCreate()
+
+ PreferenceUtils.init(applicationContext)
+ val appInfo: SampleAppInfo = PreferenceUtils.sampleAppInfo ?: us3
+ AIAgentMessenger.initialize(MessengerInitParams(
+ context = applicationContext,
+ appId = appInfo.appId,
+ theme = applicationContext.getCurrentTheme(),
+ initResultHandler = this,
+ apiHost = appInfo.region.apiHost(),
+ wsHost = appInfo.region.wsHost(),
+ loggerLevel = LoggerLevel.VERBOSE
+ ))
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+
+ if (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
+ AIAgentMessenger.setThemeMode(MessengerThemeMode.Dark)
+ } else if (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO) {
+ AIAgentMessenger.setThemeMode(MessengerThemeMode.Light)
+ }
+ }
+
+ override fun onInitSuccess() {
+ val sessionInfo = PreferenceUtils.manualUserInfo?.let {
+ SessionInfo.ManualSessionInfo(it.userId, it.authToken, AbstractSessionHandler())
+ } ?: run {
+ SessionInfo.AnonymousSessionInfo()
+ }
+ AIAgentMessenger.updateSessionInfo(sessionInfo)
+ initState.value = InitState.SUCCEED
+ }
+
+ override fun onInitFailure(e: SendbirdException) {
+ initState.value = InitState.FAILED
+ }
+
+ override fun onMigrationStarted() {
+ initState.value = InitState.MIGRATING
+ }
+
+ companion object {
+ internal val initState = MutableStateFlow(InitState.NONE)
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/BaseSampleActivity.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/BaseSampleActivity.kt
new file mode 100644
index 00000000..6c100b2a
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/BaseSampleActivity.kt
@@ -0,0 +1,18 @@
+package com.sendbird.sdk.aiagent.sample.activities
+
+import android.content.Context
+import androidx.appcompat.app.AppCompatActivity
+import com.sendbird.sdk.aiagent.common.extensions.applyAppLocale
+import com.sendbird.sdk.aiagent.common.utils.Logger
+import java.util.Locale
+
+open class BaseSampleActivity : AppCompatActivity() {
+ override fun attachBaseContext(newBase: Context) {
+ val languageTag = Locale.getDefault().toLanguageTag()
+ Logger.i("attachBaseContext: languageTag=$languageTag")
+ if (languageTag.isNotEmpty()) {
+ val localizedContext = newBase.applyAppLocale(languageTag)
+ super.attachBaseContext(localizedContext)
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/LoginActivity.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/LoginActivity.kt
new file mode 100644
index 00000000..e324480a
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/LoginActivity.kt
@@ -0,0 +1,97 @@
+package com.sendbird.sdk.aiagent.sample.activities
+
+import android.os.Bundle
+import android.widget.Toast
+import androidx.lifecycle.lifecycleScope
+import com.sendbird.android.SendbirdChat
+import com.sendbird.sdk.aiagent.messenger.AIAgentMessenger
+import com.sendbird.sdk.aiagent.messenger.model.SessionInfo
+import com.sendbird.sdk.aiagent.sample.consts.Region
+import com.sendbird.sdk.aiagent.sample.databinding.SampleLayoutLoginBinding
+import com.sendbird.sdk.aiagent.sample.model.ManualUserInfo
+import com.sendbird.sdk.aiagent.sample.model.SampleAppInfo
+import com.sendbird.sdk.aiagent.sample.model.us3
+import com.sendbird.sdk.aiagent.sample.model.userNo1
+import com.sendbird.sdk.aiagent.sample.model.userNo2
+import com.sendbird.sdk.aiagent.sample.model.userNo3
+import com.sendbird.sdk.aiagent.sample.model.userNo4
+import com.sendbird.sdk.aiagent.sample.model.userNo5
+import com.sendbird.sdk.aiagent.sample.model.userNo6
+import com.sendbird.sdk.aiagent.sample.model.userUs3
+import com.sendbird.sdk.aiagent.sample.utils.AbstractSessionHandler
+import com.sendbird.sdk.aiagent.sample.utils.PreferenceUtils
+import com.sendbird.sdk.aiagent.sample.utils.apiHost
+import com.sendbird.sdk.aiagent.sample.utils.wsHost
+import kotlinx.coroutines.launch
+
+
+class LoginActivity : BaseSampleActivity() {
+ private val binding by lazy { SampleLayoutLoginBinding.inflate(layoutInflater) }
+ private val appInfo: SampleAppInfo by lazy { PreferenceUtils.sampleAppInfo ?: us3 }
+ private val manualUserInfo: ManualUserInfo by lazy { getDefaultAppInfo(appInfo.region) }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ with(binding) {
+ setContentView(root)
+ setDefaultInformation(this@with, manualUserInfo)
+ btnSignIn.setOnClickListener {
+ lifecycleScope.launch {
+ val userId = etUserId.text.toString()
+ val authToken = etAuthToken.text.toString()
+ val context = this@LoginActivity
+ runCatching {
+ authenticate(userId, authToken)
+ }.onFailure {
+ Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ }
+ }
+
+ private fun authenticate(userId: String, authToken: String) {
+ val userInfo = PreferenceUtils.sampleAppInfo ?: us3
+ val apiHost = userInfo.region.apiHost()
+ val wsHost = userInfo.region.wsHost()
+ SendbirdChat.connect(userId, authToken, apiHost, wsHost) { _, e ->
+ if (e != null) {
+ // Handle error
+ Toast.makeText(this@LoginActivity, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
+ return@connect
+ } else {
+ PreferenceUtils.manualUserInfo = ManualUserInfo(
+ userId = userId,
+ authToken = authToken
+ )
+ AIAgentMessenger.updateSessionInfo(SessionInfo.ManualSessionInfo(
+ userId = userId,
+ sessionToken = authToken,
+ sessionHandler = AbstractSessionHandler()
+ ))
+ finish()
+ }
+ }
+ }
+
+ private fun setDefaultInformation(binding: SampleLayoutLoginBinding, manualUserInfo: ManualUserInfo) {
+ with(binding) {
+ with(manualUserInfo) {
+ etUserId.setText(userId)
+ etAuthToken.setText(authToken)
+ }
+ }
+ }
+
+ private fun getDefaultAppInfo(region: Region): ManualUserInfo {
+ return when (region) {
+ Region.PRODUCTION -> userUs3
+ Region.NO1 -> userNo1
+ Region.NO2 -> userNo2
+ Region.NO3 -> userNo3
+ Region.NO4 -> userNo4
+ Region.NO5 -> userNo5
+ Region.NO6 -> userNo6
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SelectAppInfoActivity.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SelectAppInfoActivity.kt
new file mode 100644
index 00000000..4ca9a70b
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SelectAppInfoActivity.kt
@@ -0,0 +1,99 @@
+package com.sendbird.sdk.aiagent.sample.activities
+
+import android.os.Bundle
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.lifecycleScope
+import com.sendbird.sdk.aiagent.sample.R
+import com.sendbird.sdk.aiagent.sample.consts.Region
+import com.sendbird.sdk.aiagent.sample.databinding.SampleLayoutSelectAppInfoBinding
+import com.sendbird.sdk.aiagent.sample.model.SampleAppInfo
+import com.sendbird.sdk.aiagent.sample.model.no1
+import com.sendbird.sdk.aiagent.sample.model.no2
+import com.sendbird.sdk.aiagent.sample.model.no3
+import com.sendbird.sdk.aiagent.sample.model.no4
+import com.sendbird.sdk.aiagent.sample.model.no5
+import com.sendbird.sdk.aiagent.sample.model.no6
+import com.sendbird.sdk.aiagent.sample.model.us3
+import com.sendbird.sdk.aiagent.sample.utils.PreferenceUtils
+import com.sendbird.sdk.aiagent.sample.utils.apiHost
+import com.sendbird.sdk.aiagent.sample.utils.awaitInitializeMessenger
+import com.sendbird.sdk.aiagent.sample.utils.toRegion
+import com.sendbird.sdk.aiagent.sample.utils.wsHost
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class SelectAppInfoActivity : BaseSampleActivity() {
+ private lateinit var appInfo: SampleAppInfo
+ private val binding by lazy { SampleLayoutSelectAppInfoBinding.inflate(layoutInflater) }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ with(binding) {
+ setContentView(root)
+ val spinnerItems = resources.getStringArray(R.array.sb_regions)
+ val myAdapter = ArrayAdapter(this@SelectAppInfoActivity, R.layout.support_simple_spinner_dropdown_item, spinnerItems)
+ sRegion.adapter = myAdapter
+ sRegion.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
+ (view as TextView).setTextColor(ContextCompat.getColor(this@SelectAppInfoActivity, R.color.black))
+ appInfo = getDefaultAppInfo(spinnerItems[position].toRegion(this@SelectAppInfoActivity))
+ setDefaultInformation(this@with, appInfo)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>) {}
+ }
+ btnSave.setOnClickListener {
+ val aiAgentId = etAIAgentId.text.toString()
+ val appId = etAppId.text.toString()
+ val context = this@SelectAppInfoActivity
+ val apiHost = appInfo.region.apiHost()
+ val wsHost = appInfo.region.wsHost()
+
+ lifecycleScope.launch(Dispatchers.Default) {
+ runCatching {
+ context.awaitInitializeMessenger(appId, apiHost, wsHost)
+
+ // save local storage
+ PreferenceUtils.sampleAppInfo = appInfo.copy(
+ aiAgentId = aiAgentId,
+ appId = appId
+ )
+ withContext(Dispatchers.Main) {
+ startActivity(ViewMainActivity.newIntent(this@SelectAppInfoActivity, aiAgentId))
+ finish()
+ }
+ }.onFailure {
+ Toast.makeText(context, "Error: ${it.message}", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+ }
+ }
+
+ private fun setDefaultInformation(binding: SampleLayoutSelectAppInfoBinding, appInfo: SampleAppInfo) {
+ with(binding) {
+ with(appInfo) {
+ etAppId.setText(appId)
+ etAIAgentId.setText(aiAgentId)
+ }
+ }
+ }
+
+ private fun getDefaultAppInfo(region: Region): SampleAppInfo {
+ return when (region) {
+ Region.PRODUCTION -> us3
+ Region.NO1 -> no1
+ Region.NO2 -> no2
+ Region.NO3 -> no3
+ Region.NO4 -> no4
+ Region.NO5 -> no5
+ Region.NO6 -> no6
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SplashActivity.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SplashActivity.kt
new file mode 100644
index 00000000..5b8e77df
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/SplashActivity.kt
@@ -0,0 +1,44 @@
+package com.sendbird.sdk.aiagent.sample.activities
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.os.Bundle
+import androidx.lifecycle.lifecycleScope
+import com.sendbird.sdk.aiagent.common.utils.Logger
+import com.sendbird.sdk.aiagent.messenger.ui.widget.WaitingDialog
+import com.sendbird.sdk.aiagent.sample.AgentApplication
+import com.sendbird.sdk.aiagent.sample.consts.InitState
+import com.sendbird.sdk.aiagent.sample.databinding.SampleLayoutSplashBinding
+import com.sendbird.sdk.aiagent.sample.utils.PreferenceUtils
+import kotlinx.coroutines.launch
+
+@SuppressLint("CustomSplashScreen")
+class SplashActivity : BaseSampleActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ SampleLayoutSplashBinding.inflate(layoutInflater).apply {
+ setContentView(root)
+ }
+
+ lifecycleScope.launch {
+ AgentApplication.initState.collect {
+ Logger.i(">> collected, initState: $it")
+ when (it) {
+ InitState.NONE -> {}
+ InitState.MIGRATING -> WaitingDialog.show(this@SplashActivity)
+ InitState.FAILED, InitState.SUCCEED -> {
+ WaitingDialog.dismiss()
+ val appInfo = PreferenceUtils.sampleAppInfo
+ val intent = if (appInfo == null) {
+ Intent(this@SplashActivity, SelectAppInfoActivity::class.java)
+ } else {
+ ViewMainActivity.newIntent(this@SplashActivity, aiAgentId = appInfo.aiAgentId)
+ }
+ startActivity(intent)
+ finish()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/ViewMainActivity.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/ViewMainActivity.kt
new file mode 100644
index 00000000..573838cc
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/activities/ViewMainActivity.kt
@@ -0,0 +1,134 @@
+package com.sendbird.sdk.aiagent.sample.activities
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.core.view.WindowCompat
+import androidx.lifecycle.lifecycleScope
+import com.sendbird.sdk.aiagent.common.extensions.addRipple
+import com.sendbird.sdk.aiagent.common.extensions.aiAgentId
+import com.sendbird.sdk.aiagent.common.extensions.setAppearance
+import com.sendbird.sdk.aiagent.messenger.AIAgentMessenger
+import com.sendbird.sdk.aiagent.messenger.BuildConfig
+import com.sendbird.sdk.aiagent.messenger.consts.LaunchMode
+import com.sendbird.sdk.aiagent.messenger.model.LauncherLayoutParams
+import com.sendbird.sdk.aiagent.messenger.model.LauncherLocation
+import com.sendbird.sdk.aiagent.messenger.model.LauncherMargin
+import com.sendbird.sdk.aiagent.messenger.model.LauncherSettingsParams
+import com.sendbird.sdk.aiagent.messenger.ui.MessengerLauncher
+import com.sendbird.sdk.aiagent.messenger.ui.activity.MessengerActivity
+import com.sendbird.sdk.aiagent.sample.R
+import com.sendbird.sdk.aiagent.sample.databinding.ActivityViewMainBinding
+import com.sendbird.sdk.aiagent.sample.model.us3
+import com.sendbird.sdk.aiagent.sample.utils.PreferenceUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class ViewMainActivity : BaseSampleActivity() {
+ private lateinit var binding: ActivityViewMainBinding
+
+ private val aiAgentId: String? by lazy { intent.extras.aiAgentId }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityViewMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ initMessengerLauncher()
+ setButtonForFullScreenAiAgent()
+ initView()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ initManualUserProfile()
+ drawFingerPrint()
+ }
+
+ private fun initManualUserProfile() {
+ with(binding.ivProfile) {
+ visibility = if (PreferenceUtils.manualUserInfo == null) {
+ setImageResource(R.drawable.sample_profile_icon)
+ setOnClickListener {
+ startActivity(Intent(this@ViewMainActivity, LoginActivity::class.java))
+ }
+ VISIBLE
+ } else {
+ GONE
+ }
+ addRipple()
+ }
+ }
+
+ private fun initMessengerLauncher() {
+ aiAgentId?.let {
+ val params = LauncherLayoutParams(
+ LaunchMode.ANCHORED,
+ LauncherMargin(12, 12, 12, 12),
+ LauncherLocation.BOTTOM_END
+ )
+ MessengerLauncher(this, it, LauncherSettingsParams(layoutParams = params)).attach()
+ } ?: run {
+ Toast.makeText(this, R.string.aa_text_error_not_found_aiagent_id, Toast.LENGTH_SHORT).show()
+ finishAffinity()
+ }
+ }
+
+ private fun setButtonForFullScreenAiAgent() {
+ binding.btnViewDemo.setOnClickListener {
+ aiAgentId?.let {
+ startActivity(MessengerActivity.newIntentForConversation(this, it))
+ }
+ }
+ }
+
+ private fun initView() {
+ lifecycleScope.launch {
+ binding.tvInformation.setAppearance(R.style.Body3)
+ binding.tvInformation.setTextColor(ContextCompat.getColor(this@ViewMainActivity, R.color.white))
+
+ window.statusBarColor = ContextCompat.getColor(this@ViewMainActivity, R.color.black_500)
+ WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = false
+ binding.btnLogout.setOnClickListener {
+ lifecycleScope.launch(Dispatchers.IO) {
+ PreferenceUtils.clearAll()
+ AIAgentMessenger.deauthenticate()
+ withContext(Dispatchers.Main) {
+ finishAffinity()
+ startActivity(Intent(this@ViewMainActivity, SelectAppInfoActivity::class.java))
+ }
+ }
+ }
+ }
+ }
+
+ private fun drawFingerPrint() {
+ lifecycleScope.launch {
+ val fingerPrint = withContext(Dispatchers.IO) {
+ val region = (PreferenceUtils.sampleAppInfo ?: us3).region.name
+ val fingerPrint = "${BuildConfig.VERSION_NAME} - [$region]"
+
+ PreferenceUtils.manualUserInfo?.let {
+ "${fingerPrint}\n${it.userId}"
+ } ?: fingerPrint
+ }
+
+ withContext(Dispatchers.Main) {
+ binding.tvInformation.text = fingerPrint
+ }
+ }
+ }
+
+ companion object {
+ private const val KEY_AI_AGENT_ID = "KEY_AI_AGENT_ID"
+ internal fun newIntent(context: Context, aiAgentId: String): Intent {
+ return Intent(context, ViewMainActivity::class.java).apply {
+ putExtra(KEY_AI_AGENT_ID, aiAgentId)
+ }
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/InitState.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/InitState.kt
new file mode 100644
index 00000000..13b154a9
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/InitState.kt
@@ -0,0 +1,26 @@
+package com.sendbird.sdk.aiagent.sample.consts
+
+/**
+ * Used with Sendbird AIAgent Messenger initialization.
+ */
+enum class InitState {
+ /**
+ * Indicates the migrating state.
+ */
+ MIGRATING,
+
+ /**
+ * Indicates the failed state.
+ */
+ FAILED,
+
+ /**
+ * Indicates the succeeded state.
+ */
+ SUCCEED,
+
+ /**
+ * Indicates nothing is set.
+ */
+ NONE,
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/KeySet.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/KeySet.kt
new file mode 100644
index 00000000..f838900c
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/KeySet.kt
@@ -0,0 +1,9 @@
+package com.sendbird.sdk.aiagent.sample.consts
+
+internal object KeySet {
+ const val region = "region"
+ const val userId = "userId"
+ const val authToken = "authToken"
+ const val appId = "appId"
+ const val aiAgentId = "aiAgentId"
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/Region.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/Region.kt
new file mode 100644
index 00000000..e3dd17b4
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/consts/Region.kt
@@ -0,0 +1,29 @@
+package com.sendbird.sdk.aiagent.sample.consts
+
+import com.sendbird.sdk.aiagent.sample.R
+
+enum class Region(val value: Int) {
+ PRODUCTION(R.string.sample_region_production),
+ NO1(R.string.sample_region_no1),
+ NO2(R.string.sample_region_no2),
+ NO3(R.string.sample_region_no3),
+ NO4(R.string.sample_region_no4),
+ NO5(R.string.sample_region_no5),
+ NO6(R.string.sample_region_no6)
+ ;
+
+ companion object {
+ fun fromValue(value: Int): Region {
+ return when (value) {
+ R.string.sample_region_production -> PRODUCTION
+ R.string.sample_region_no1 -> NO1
+ R.string.sample_region_no2 -> NO2
+ R.string.sample_region_no3 -> NO3
+ R.string.sample_region_no4 -> NO4
+ R.string.sample_region_no5 -> NO5
+ R.string.sample_region_no6 -> NO6
+ else -> PRODUCTION
+ }
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/ManualUserInfo.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/ManualUserInfo.kt
new file mode 100644
index 00000000..fe7a2d3a
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/ManualUserInfo.kt
@@ -0,0 +1,59 @@
+package com.sendbird.sdk.aiagent.sample.model
+
+import com.sendbird.sdk.aiagent.sample.consts.KeySet
+import org.json.JSONObject
+
+internal data class ManualUserInfo(
+ val userId: String,
+ val authToken: String,
+) {
+ constructor(json: JSONObject): this(
+ userId = json.getString(KeySet.userId),
+ authToken = json.getString(KeySet.authToken),
+ )
+
+ constructor(jsonString: String): this(JSONObject(jsonString))
+
+ fun toJson(): JSONObject {
+ return JSONObject().apply {
+ put(KeySet.userId, userId)
+ put(KeySet.authToken, authToken)
+ }
+ }
+}
+// region public user info
+internal val userUs3 = ManualUserInfo(
+ userId = "client_user",
+ authToken = "deb776838a0dca710fffd9c38b06ed133e2d088f",
+)
+// endregion
+
+internal val userNo1 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
+
+internal val userNo2 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
+
+internal val userNo3 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
+
+internal val userNo4 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
+
+internal val userNo5 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
+
+internal val userNo6 = ManualUserInfo(
+ userId = "",
+ authToken = "",
+)
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/SampleAppInfo.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/SampleAppInfo.kt
new file mode 100644
index 00000000..b6441c3c
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/model/SampleAppInfo.kt
@@ -0,0 +1,71 @@
+package com.sendbird.sdk.aiagent.sample.model
+
+import com.sendbird.sdk.aiagent.sample.consts.KeySet
+import com.sendbird.sdk.aiagent.sample.consts.Region
+import org.json.JSONObject
+
+internal data class SampleAppInfo(
+ val region: Region,
+ val appId: String,
+ val aiAgentId: String
+) {
+ constructor(json: JSONObject): this(
+ region = Region.fromValue(json.getInt(KeySet.region)),
+ appId = json.getString(KeySet.appId),
+ aiAgentId = json.getString(KeySet.aiAgentId)
+ )
+
+ constructor(jsonString: String): this(JSONObject(jsonString))
+
+ fun toJson(): JSONObject {
+ return JSONObject().apply {
+ put(KeySet.region, region.value)
+ put(KeySet.appId, appId)
+ put(KeySet.aiAgentId, aiAgentId)
+ }
+ }
+}
+
+// region public app info
+internal val us3 = SampleAppInfo(
+ region = Region.PRODUCTION,
+ appId = "10306808-B7F3-436F-9F5C-29F431B47B73",
+ aiAgentId = "e4c57465-4773-432e-9740-f0284a951494"
+)
+// endregion
+
+internal val no1 = SampleAppInfo(
+ region = Region.NO1,
+ appId = "",
+ aiAgentId = ""
+)
+
+internal val no2 = SampleAppInfo(
+ region = Region.NO2,
+ appId = "",
+ aiAgentId = ""
+)
+
+internal val no3 = SampleAppInfo(
+ region = Region.NO3,
+ appId = "",
+ aiAgentId = ""
+)
+
+internal val no4 = SampleAppInfo(
+ region = Region.NO4,
+ appId = "",
+ aiAgentId = ""
+)
+
+internal val no5 = SampleAppInfo(
+ region = Region.NO5,
+ appId = "",
+ aiAgentId = ""
+)
+
+internal val no6 = SampleAppInfo(
+ region = Region.NO6,
+ appId = "",
+ aiAgentId = ""
+)
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/persists/Preference.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/persists/Preference.kt
new file mode 100644
index 00000000..c0e509e7
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/persists/Preference.kt
@@ -0,0 +1,26 @@
+package com.sendbird.sdk.aiagent.sample.persists
+
+import android.content.Context
+import android.content.SharedPreferences
+
+internal class Preference(context: Context, fileName: String) {
+
+ fun getString(key: String, defaultValue: String? = null): String? =
+ pref.getString(key, defaultValue) ?: defaultValue
+
+ fun putString(key: String, value: String) = pref.edit().putString(key, value).apply()
+ fun getInt(key: String, defaultValue: Int = 0): Int = pref.getInt(key, defaultValue)
+ fun putInt(key: String, value: Int) = pref.edit().putInt(key, value).apply()
+ fun getBoolean(key: String, defaultValue: Boolean = false): Boolean = pref.getBoolean(key, defaultValue)
+ fun putBoolean(key: String, value: Boolean) = pref.edit().putBoolean(key, value).apply()
+ fun clear() = pref.edit().clear().apply()
+ fun remove(key: String) = pref.edit().remove(key).apply()
+ fun contains(key: String): Boolean = pref.contains(key)
+
+ private val pref: SharedPreferences by lazy {
+ context.getSharedPreferences(
+ fileName,
+ Context.MODE_PRIVATE
+ )
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/AbstractSessionHandler.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/AbstractSessionHandler.kt
new file mode 100644
index 00000000..a5e8f9c7
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/AbstractSessionHandler.kt
@@ -0,0 +1,15 @@
+package com.sendbird.sdk.aiagent.sample.utils
+
+import com.sendbird.android.handler.SessionTokenRequester
+import com.sendbird.sdk.aiagent.messenger.interfaces.AIAgentSessionHandler
+import com.sendbird.sdk.aiagent.common.utils.Logger
+
+class AbstractSessionHandler : AIAgentSessionHandler() {
+ override fun onSessionClosed() {
+ Logger.w("Session closed")
+ }
+
+ override fun onSessionTokenRequired(sessionTokenRequester: SessionTokenRequester) {
+ Logger.w("Session token required")
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/Extensions.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/Extensions.kt
new file mode 100644
index 00000000..f1e7890d
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/Extensions.kt
@@ -0,0 +1,49 @@
+package com.sendbird.sdk.aiagent.sample.utils
+
+import android.content.Context
+import android.content.res.Configuration
+import com.sendbird.android.exception.SendbirdException
+import com.sendbird.sdk.aiagent.messenger.AIAgentMessenger
+import com.sendbird.sdk.aiagent.messenger.consts.MessengerThemeMode
+import com.sendbird.sdk.aiagent.messenger.interfaces.MessengerInitResultHandler
+import com.sendbird.sdk.aiagent.messenger.model.MessengerInitParams
+import com.sendbird.sdk.aiagent.messenger.model.SessionInfo
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+fun Context.isDarkTheme(): Boolean {
+ val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ return currentNightMode == Configuration.UI_MODE_NIGHT_YES
+}
+
+fun Context.getCurrentTheme(): MessengerThemeMode {
+ return if (applicationContext.isDarkTheme()) {
+ MessengerThemeMode.Dark
+ } else {
+ MessengerThemeMode.Light
+ }
+}
+
+suspend fun Context.awaitInitializeMessenger(appId: String, apiHost: String?, wsHost: String?) = suspendCoroutine { cont ->
+ AIAgentMessenger.initialize(
+ MessengerInitParams(
+ context = this,
+ appId = appId,
+ theme = getCurrentTheme(),
+ initResultHandler = object : MessengerInitResultHandler {
+ override fun onInitSuccess() {
+ AIAgentMessenger.updateSessionInfo(SessionInfo.AnonymousSessionInfo())
+ cont.resume(Unit)
+ }
+
+ override fun onInitFailure(e: SendbirdException) {
+ cont.resumeWithException(e)
+ }
+ override fun onMigrationStarted() {}
+ },
+ apiHost = apiHost,
+ wsHost = wsHost
+ )
+ )
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/PreferenceUtils.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/PreferenceUtils.kt
new file mode 100644
index 00000000..30bc33fa
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/PreferenceUtils.kt
@@ -0,0 +1,29 @@
+package com.sendbird.sdk.aiagent.sample.utils
+
+import android.content.Context
+import com.sendbird.sdk.aiagent.sample.model.SampleAppInfo
+import com.sendbird.sdk.aiagent.sample.model.ManualUserInfo
+import com.sendbird.sdk.aiagent.sample.persists.Preference
+
+internal object PreferenceUtils {
+ private const val PREFERENCE_KEY_MANUAL_USER_INFO = "PREFERENCE_KEY_MANUAL_USER_INFO"
+ private const val PREFERENCE_KEY_SAMPLE_APP_INFO = "PREFERENCE_KEY_SAMPLE_APP_INFO"
+
+ private lateinit var pref: Preference
+
+ fun init(context: Context) {
+ pref = Preference(context, "sendbird-ai-agent-sample")
+ }
+
+ var manualUserInfo: ManualUserInfo?
+ get() = pref.getString(PREFERENCE_KEY_MANUAL_USER_INFO)?.let { ManualUserInfo(it) }
+ set(value) = if (value == null) pref.remove(PREFERENCE_KEY_MANUAL_USER_INFO) else pref.putString(PREFERENCE_KEY_MANUAL_USER_INFO, value.toJson().toString())
+
+ var sampleAppInfo: SampleAppInfo?
+ get() = pref.getString(PREFERENCE_KEY_SAMPLE_APP_INFO)?.let { SampleAppInfo(it) }
+ set(value) = if (value == null) pref.remove(PREFERENCE_KEY_SAMPLE_APP_INFO) else pref.putString(PREFERENCE_KEY_SAMPLE_APP_INFO, value.toJson().toString())
+
+ fun clearAll() {
+ pref.clear()
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/RegionExtensions.kt b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/RegionExtensions.kt
new file mode 100644
index 00000000..762f796c
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/java/com/sendbird/sdk/aiagent/sample/utils/RegionExtensions.kt
@@ -0,0 +1,44 @@
+package com.sendbird.sdk.aiagent.sample.utils
+
+import android.content.Context
+import com.sendbird.sdk.aiagent.sample.R
+import com.sendbird.sdk.aiagent.sample.consts.Region
+
+fun Region.apiHost(): String? {
+ return when (this) {
+ Region.NO1 -> "https://api-no1.sendbirdtest.com"
+ Region.NO2 -> "https://api-no2.sendbirdtest.com"
+ Region.NO3 -> "https://api-no3.sendbirdtest.com"
+ Region.NO4 -> "https://api-no4.sendbirdtest.com"
+ Region.NO5 -> "https://api-no5.sendbirdtest.com"
+ Region.NO6 -> "https://api-no6.sendbirdtest.com"
+ else -> null
+ }
+}
+
+fun Region.wsHost(): String? {
+ return when (this) {
+ Region.NO1 -> "wss://ws-no1.sendbirdtest.com"
+ Region.NO2 -> "wss://ws-no2.sendbirdtest.com"
+ Region.NO3 -> "wss://ws-no3.sendbirdtest.com"
+ Region.NO4 -> "wss://ws-no4.sendbirdtest.com"
+ Region.NO5 -> "wss://ws-no5.sendbirdtest.com"
+ Region.NO6 -> "wss://ws-no6.sendbirdtest.com"
+ else -> null
+ }
+}
+
+fun String.toRegion(context: Context): Region {
+ context.let {
+ return when (this) {
+ context.getString(R.string.sample_region_production) -> Region.PRODUCTION
+ context.getString(R.string.sample_region_no1) -> Region.NO1
+ context.getString(R.string.sample_region_no2) -> Region.NO2
+ context.getString(R.string.sample_region_no3) -> Region.NO3
+ context.getString(R.string.sample_region_no4) -> Region.NO4
+ context.getString(R.string.sample_region_no5) -> Region.NO5
+ context.getString(R.string.sample_region_no6) -> Region.NO6
+ else -> Region.PRODUCTION
+ }
+ }
+}
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_chat.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_chat.xml
new file mode 100644
index 00000000..4f3d7e5b
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_chat.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_close.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_close.xml
new file mode 100644
index 00000000..ed06cd49
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_close.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_logout.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_logout.xml
new file mode 100644
index 00000000..dc9a9bad
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_logout.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_menu.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_menu.xml
new file mode 100644
index 00000000..4f3d7e5b
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-anydpi/ic_action_menu.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_chat.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_chat.png
new file mode 100644
index 00000000..416b2101
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_chat.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_close.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_close.png
new file mode 100644
index 00000000..ae4927ac
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_close.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_logout.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_logout.png
new file mode 100644
index 00000000..455dd606
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_logout.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_menu.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_menu.png
new file mode 100644
index 00000000..416b2101
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/ic_action_menu.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/logo.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/logo.png
new file mode 100644
index 00000000..c04c40a4
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/logo.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/main_background.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/main_background.png
new file mode 100644
index 00000000..80ed791a
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-hdpi/main_background.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_chat.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_chat.png
new file mode 100644
index 00000000..7183a823
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_chat.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_close.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_close.png
new file mode 100644
index 00000000..bb1ffb1a
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_close.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_logout.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_logout.png
new file mode 100644
index 00000000..016dd661
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_logout.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_menu.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_menu.png
new file mode 100644
index 00000000..7183a823
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-mdpi/ic_action_menu.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..7706ab9e
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_chat.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_chat.png
new file mode 100644
index 00000000..33a29fa2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_chat.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_close.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_close.png
new file mode 100644
index 00000000..ed37335c
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_close.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_logout.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_logout.png
new file mode 100644
index 00000000..16698a25
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_logout.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_menu.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_menu.png
new file mode 100644
index 00000000..33a29fa2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/ic_action_menu.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/logo.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/logo.png
new file mode 100644
index 00000000..c04c40a4
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/logo.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/main_background.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/main_background.png
new file mode 100644
index 00000000..80ed791a
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xhdpi/main_background.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_chat.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_chat.png
new file mode 100644
index 00000000..a7e0f0f2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_chat.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_close.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_close.png
new file mode 100644
index 00000000..46db4672
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_close.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_logout.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_logout.png
new file mode 100644
index 00000000..3ef8e1d2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_logout.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_menu.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_menu.png
new file mode 100644
index 00000000..a7e0f0f2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/ic_action_menu.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/logo.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/logo.png
new file mode 100644
index 00000000..c04c40a4
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/logo.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/main_background.png b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/main_background.png
new file mode 100644
index 00000000..80ed791a
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable-xxhdpi/main_background.png differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/ic_launcher_background.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..ca3826a4
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_profile_icon.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_profile_icon.xml
new file mode 100644
index 00000000..1e24cf39
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_profile_icon.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_selector_sign_in_with_user_id_button.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_selector_sign_in_with_user_id_button.xml
new file mode 100644
index 00000000..d8392044
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_selector_sign_in_with_user_id_button.xml
@@ -0,0 +1,21 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_demo_sign_in_edittext_background.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_demo_sign_in_edittext_background.xml
new file mode 100644
index 00000000..77c045c5
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_demo_sign_in_edittext_background.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_main_demo_button.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_main_demo_button.xml
new file mode 100644
index 00000000..ebb4ccff
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/drawable/sample_shape_main_demo_button.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/activity_view_main.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/activity_view_main.xml
new file mode 100644
index 00000000..83aba53b
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/activity_view_main.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_login.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_login.xml
new file mode 100644
index 00000000..c83e4e0c
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_login.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_select_app_info.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_select_app_info.xml
new file mode 100644
index 00000000..197b9e50
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_select_app_info.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_splash.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_splash.xml
new file mode 100644
index 00000000..76ab05f2
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/layout/sample_layout_splash.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..c4a603d4
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..c4a603d4
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 00000000..a40ac889
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..f37f54b2
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..ba7205ed
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 00000000..222646cd
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..49058fb0
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..e7b57cc0
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 00000000..96f4d33b
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..b4ee2faf
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..4501a454
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..c6cd22bd
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..7972c9bb
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..dcab0c50
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..68d93688
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 00000000..a7f92c6a
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..391319ff
Binary files /dev/null and b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/strings.xml
new file mode 100644
index 00000000..f3d3ac62
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ 수동
+ 자동
+ 로그아웃
+ 로그인
+ Save
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ 데모 보기
+ PRODUCTION
+ PREPROD
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-de/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-de/strings.xml
new file mode 100644
index 00000000..8ab358a2
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-de/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manuell
+ Automatisch
+ Abmelden
+ Anmelden
+ Speichern
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Demo ansehen
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-es/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-es/strings.xml
new file mode 100644
index 00000000..a1df96ca
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-es/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manual
+ Automático
+ Cerrar sesión
+ Iniciar sesión
+ Guardar
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Ver demostración
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-fr/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-fr/strings.xml
new file mode 100644
index 00000000..74c54958
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-fr/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manuel
+ Automatique
+ Se déconnecter
+ Se connecter
+ Enregistrer
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Voir la démo
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-hi/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-hi/strings.xml
new file mode 100644
index 00000000..ef1570f1
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-hi/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ मैनुअल
+ स्वचालित
+ लॉग आउट करें
+ लॉग इन करें
+ सहेजें
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ डेमो देखें
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-it/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-it/strings.xml
new file mode 100644
index 00000000..ab22a395
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-it/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manuale
+ Automatico
+ Disconnettersi
+ Accedi
+ Salva
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Guarda demo
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ja/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ja/strings.xml
new file mode 100644
index 00000000..8082a23f
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ja/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ マニュアル
+ 自動
+ ログアウト
+ ログイン
+ 保存
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ デモを見る
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ko/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ko/strings.xml
new file mode 100644
index 00000000..4e205634
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-ko/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ 수동
+ 자동
+ 로그아웃
+ 로그인
+ 저장
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ 데모 보기
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-night/themes.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..cfd4ef41
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-night/themes.xml
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-pt/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-pt/strings.xml
new file mode 100644
index 00000000..40db31e9
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-pt/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manual
+ Automático
+ Sair
+ Entrar
+ Salvar
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Ver demonstração
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-tr/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-tr/strings.xml
new file mode 100644
index 00000000..a3285702
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values-tr/strings.xml
@@ -0,0 +1,29 @@
+
+
+ ai-agent-sample
+ Manuel
+ Otomatik
+ Çıkış yap
+ Giriş yap
+ Save
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Demoyu görüntüle
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/array.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/array.xml
new file mode 100644
index 00000000..78abc4ea
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/array.xml
@@ -0,0 +1,12 @@
+
+
+
+ - @string/sample_region_production
+ - @string/sample_region_no1
+ - @string/sample_region_no2
+ - @string/sample_region_no3
+ - @string/sample_region_no4
+ - @string/sample_region_no5
+ - @string/sample_region_no6
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/colors.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/colors.xml
new file mode 100644
index 00000000..a2d72511
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #1A171F
+ #FFFFFFFF
+ #AAAAAA
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/strings.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/strings.xml
new file mode 100644
index 00000000..639c157d
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/strings.xml
@@ -0,0 +1,28 @@
+
+ ai-agent-sample
+ Manual
+ Auto
+ Log out
+ Sign In
+ Save
+ Sign in with User ID
+ Region
+ App ID
+ User ID
+ AI-AGENT ID
+ Auth Token
+ Open the ai agent
+ ComposeActivity
+ Open Compose Sample
+ Open View Sample
+ Your AI\nworkforce for\nomnichannel\nconversations
+ Solve >90% of customer service cases autonomously. Accelerate your customer journey inside and outside the app, backed by handoff to conversations with human agents.
+ Open the AI Agent Messenger
+ PRODUCTION
+ NO1
+ NO2
+ NO3
+ NO4
+ NO5
+ NO6
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/themes.xml b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/themes.xml
new file mode 100644
index 00000000..1a163e54
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/main/res/values/themes.xml
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/android/sample/ai-agent-android/ai-agent-sample/src/test/java/com/sendbird/sdk/aiagent/sample/ExampleUnitTest.kt b/android/sample/ai-agent-android/ai-agent-sample/src/test/java/com/sendbird/sdk/aiagent/sample/ExampleUnitTest.kt
new file mode 100644
index 00000000..55cf1838
--- /dev/null
+++ b/android/sample/ai-agent-android/ai-agent-sample/src/test/java/com/sendbird/sdk/aiagent/sample/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.sendbird.sdk.aiagent.sample
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/android/sample/ai-agent-android/build.gradle.kts b/android/sample/ai-agent-android/build.gradle.kts
new file mode 100644
index 00000000..6b5b3a14
--- /dev/null
+++ b/android/sample/ai-agent-android/build.gradle.kts
@@ -0,0 +1,7 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.jetbrains.kotlin.android) apply false
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.google.services) apply false
+}
diff --git a/android/sample/ai-agent-android/gradle.properties b/android/sample/ai-agent-android/gradle.properties
new file mode 100644
index 00000000..19ba5e50
--- /dev/null
+++ b/android/sample/ai-agent-android/gradle.properties
@@ -0,0 +1,27 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
+android.enableR8.fullMode=false
+
+MESSENGER_VERSION = 0.10.2
diff --git a/android/sample/ai-agent-android/gradle/libs.versions.toml b/android/sample/ai-agent-android/gradle/libs.versions.toml
new file mode 100644
index 00000000..bc1f68ac
--- /dev/null
+++ b/android/sample/ai-agent-android/gradle/libs.versions.toml
@@ -0,0 +1,87 @@
+[versions]
+agp = "8.5.2"
+glide = "4.16.0"
+kotlin = "1.7.20"
+coreKtx = "1.9.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+appcompat = "1.6.1"
+material = "1.5.0"
+lifecycleViewModelKtx = "2.5.1"
+lifecycleRuntimeKtx = "2.5.1"
+activityKtx = "1.6.1"
+fragmentKtx = "1.5.5"
+sdkKtlint = "0.50.0"
+sdkDokkaVersion = "1.9.20"
+sdkBinaryCompatibilityValidator = "0.16.0"
+googleService = "4.4.2"
+firebaseMessagingKtx = "24.1.1"
+
+sdkSendbirdKtx = "4.28.0"
+sdkSendbirdChat = "4.28.2"
+sdkSendbirdAuth = "1.0.9"
+sdkSendbirdMessageTemplate = "1.2.0"
+kotlinSerializationJson = "1.3.2"
+activity = "1.6.1"
+constraintlayout = "2.2.0"
+recyclerview = "1.3.2"
+markwon = "4.6.2"
+kotlinBom = "1.8.0"
+sdkUikit = "3.23.0"
+sdkAiAgentMessenger = "0.9.6"
+sdkDesk = "1.1.6"
+sdkMockk = "1.13.4"
+sdkKotest ="5.6.0"
+sdkKotlinxCoroutines = "1.7.1"
+androidxRules = "1.5.0"
+sdkJunit4 = "4.13.2"
+sdkRobolectric = "4.13"
+testCoreKtx = "1.5.0"
+
+[libraries]
+glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
+sdk-sendbird-ktx = { group = "com.sendbird.sdk", name = "sendbird-chat-ktx", version.ref = "sdkSendbirdKtx" }
+sdk-sendbird-chat = { group = "com.sendbird.sdk", name = "sendbird-chat", version.ref = "sdkSendbirdChat" }
+sdk-sendbird-auth = { group = "com.sendbird.sdk", name = "sendbird-auth", version.ref = "sdkSendbirdAuth" }
+sdk-sendbird-message-template = { group = "com.sendbird.sdk", name = "message-template", version.ref = "sdkSendbirdMessageTemplate" }
+sdk-sendbird-uikit = { group = "com.sendbird.sdk", name = "uikit", version.ref = "sdkUikit" }
+sdk-sendbird-aiagent-messenger = { group = "com.sendbird.sdk", name = "ai-agent-messenger", version.ref = "sdkAiAgentMessenger" }
+sdk-sendbird-desk = { group = "com.sendbird.sdk", name = "sendbird-desk-android-sdk", version.ref = "sdkDesk" }
+noties-markwon = { group = "io.noties.markwon", name = "core", version.ref = "markwon" }
+noties-markwon-image-glide = { module = "io.noties.markwon:image-glide", version.ref = "markwon" }
+sdk-pinterest-ktlint = { group = "com.pinterest", name = "ktlint", version.ref = "sdkKtlint" }
+
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
+androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
+androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewModelKtx" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
+kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinSerializationJson" }
+androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
+firebase-messaging-ktx = { group = "com.google.firebase", name = "firebase-messaging-ktx", version.ref = "firebaseMessagingKtx" }
+kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlinBom" }
+sdk-mockk = { group = "io.mockk", name = "mockk", version.ref = "sdkMockk" }
+sdk-kotest = { group = "io.kotest", name = "kotest-assertions-core", version.ref = "sdkKotest" }
+sdk-junit4 = { group = "junit", name = "junit", version.ref = "sdkJunit4" }
+sdk-kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "sdkKotlinxCoroutines" }
+androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxRules" }
+sdk-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "sdkRobolectric" }
+androidx-test-core = { group = "androidx.test", name = "core", version.ref = "testCoreKtx" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+android-library = { id = "com.android.library", version.ref = "agp" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+sdk-dokka = { id = "org.jetbrains.dokka", version.ref = "sdkDokkaVersion" }
+sdk-binary-compatibility-validator = { id = 'org.jetbrains.kotlinx.binary-compatibility-validator', version.ref = "sdkBinaryCompatibilityValidator" }
+google-services = { id = "com.google.gms.google-services", version.ref = "googleService" }
diff --git a/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.jar b/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..e708b1c0
Binary files /dev/null and b/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.properties b/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..730eabeb
--- /dev/null
+++ b/android/sample/ai-agent-android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Dec 17 10:24:20 KST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/android/sample/ai-agent-android/gradlew b/android/sample/ai-agent-android/gradlew
new file mode 100755
index 00000000..4f906e0c
--- /dev/null
+++ b/android/sample/ai-agent-android/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/android/sample/ai-agent-android/gradlew.bat b/android/sample/ai-agent-android/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/android/sample/ai-agent-android/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/sample/ai-agent-android/settings.gradle.kts b/android/sample/ai-agent-android/settings.gradle.kts
new file mode 100644
index 00000000..fa236abd
--- /dev/null
+++ b/android/sample/ai-agent-android/settings.gradle.kts
@@ -0,0 +1,27 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ mavenLocal()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven { url = uri("https://jitpack.io") }
+ maven { url = uri("https://repo.sendbird.com/public/maven") }
+ mavenLocal()
+ }
+}
+
+rootProject.name = "ai-agent-android"
+include(":ai-agent-sample")