2024-09-17 09:24:50 +08:00
|
|
|
package com.example.flomo_ai
|
|
|
|
|
|
2024-09-19 19:56:55 +08:00
|
|
|
import android.annotation.SuppressLint
|
2026-03-14 20:02:31 +08:00
|
|
|
import android.content.ClipData
|
|
|
|
|
import android.content.ClipboardManager
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
import android.graphics.drawable.Drawable
|
2024-09-30 22:28:34 +08:00
|
|
|
import android.os.Bundle
|
2026-03-14 20:02:31 +08:00
|
|
|
import android.text.method.PasswordTransformationMethod
|
2026-03-15 15:52:51 +08:00
|
|
|
import android.util.Log
|
2026-03-14 20:02:31 +08:00
|
|
|
import android.view.View
|
2024-09-30 22:28:34 +08:00
|
|
|
import android.widget.Button
|
|
|
|
|
import android.widget.EditText
|
2026-03-14 20:02:31 +08:00
|
|
|
import android.widget.ImageButton
|
2026-03-15 15:52:51 +08:00
|
|
|
import android.widget.ImageView
|
2024-09-30 22:28:34 +08:00
|
|
|
import android.widget.LinearLayout
|
|
|
|
|
import android.widget.TextView
|
2026-03-14 20:02:31 +08:00
|
|
|
import android.widget.Toast
|
2026-03-22 22:43:56 +08:00
|
|
|
import android.widget.RadioGroup
|
|
|
|
|
import android.widget.RadioButton
|
2024-09-30 22:28:34 +08:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2026-03-14 20:02:31 +08:00
|
|
|
import androidx.core.content.ContextCompat
|
2026-03-22 22:43:56 +08:00
|
|
|
import com.example.flomo_ai.ui.theme.ThemeManager
|
2024-09-30 22:28:34 +08:00
|
|
|
import com.google.gson.Gson
|
|
|
|
|
import com.google.gson.reflect.TypeToken
|
2026-03-14 20:02:31 +08:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
|
import okhttp3.MediaType.Companion.toMediaType
|
|
|
|
|
import okhttp3.OkHttpClient
|
|
|
|
|
import okhttp3.Request
|
|
|
|
|
import okhttp3.RequestBody.Companion.toRequestBody
|
|
|
|
|
import okhttp3.Response
|
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
import java.util.*
|
|
|
|
|
|
|
|
|
|
// Data classes for the new settings structure
|
|
|
|
|
data class HeaderConfig(val key: String, val value: String)
|
|
|
|
|
data class PromptConfig(val id: String, val title: String, val content: String, val expanded: Boolean = false)
|
|
|
|
|
data class ButtonConfig(val id: String, val label: String, val action: String, val apiUrl: String? = null, val apiMethod: String? = null, val apiBodyTemplate: String? = null, val expanded: Boolean = false)
|
2024-09-30 22:28:34 +08:00
|
|
|
|
2024-09-17 09:24:50 +08:00
|
|
|
class SecondActivity : AppCompatActivity() {
|
2024-10-10 21:49:15 +08:00
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
// View references
|
|
|
|
|
private lateinit var etBaseUrl: EditText
|
|
|
|
|
private lateinit var etApiKey: EditText
|
|
|
|
|
private lateinit var btnToggleApiKey: ImageButton
|
|
|
|
|
private lateinit var etModel: EditText
|
|
|
|
|
private lateinit var llHeadersList: LinearLayout
|
|
|
|
|
private lateinit var btnAddHeader: Button
|
2026-03-15 15:52:51 +08:00
|
|
|
private lateinit var layoutHeaderContent: LinearLayout
|
|
|
|
|
private lateinit var ivHeaderArrow: ImageView
|
|
|
|
|
private lateinit var layoutHeaderToggle: LinearLayout
|
|
|
|
|
|
|
|
|
|
// Prompt view references
|
|
|
|
|
private lateinit var llPromptList: LinearLayout
|
2026-03-14 20:02:31 +08:00
|
|
|
private lateinit var btnAddPrompt: Button
|
2026-03-15 15:52:51 +08:00
|
|
|
private lateinit var layoutPromptContent: LinearLayout
|
|
|
|
|
private lateinit var ivPromptArrow: ImageView
|
|
|
|
|
private lateinit var layoutPromptToggle: LinearLayout
|
2026-03-22 22:43:56 +08:00
|
|
|
|
|
|
|
|
// Theme view references
|
|
|
|
|
private lateinit var rgThemeMode: RadioGroup
|
|
|
|
|
private lateinit var rbThemeFollowSystem: RadioButton
|
|
|
|
|
private lateinit var rbThemeLight: RadioButton
|
|
|
|
|
private lateinit var rbThemeDark: RadioButton
|
2026-03-14 20:02:31 +08:00
|
|
|
|
|
|
|
|
// Data storage
|
|
|
|
|
private var headerConfigs = mutableListOf<HeaderConfig>()
|
|
|
|
|
private var promptConfigs = mutableListOf<PromptConfig>()
|
|
|
|
|
private var buttonConfigs = mutableListOf<ButtonConfig>()
|
|
|
|
|
|
|
|
|
|
// API config (for backward compatibility with existing API calls)
|
|
|
|
|
private lateinit var apiConfig: APIConfig
|
2024-10-10 21:49:15 +08:00
|
|
|
|
2024-09-17 09:24:50 +08:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
super.onCreate(savedInstanceState)
|
2026-03-22 22:43:56 +08:00
|
|
|
ThemeManager.applySavedTheme(this)
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "onCreate: Starting SecondActivity")
|
|
|
|
|
try {
|
|
|
|
|
setContentView(R.layout.activity_second)
|
|
|
|
|
Log.d("SecondActivity", "onCreate: Layout set")
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("SecondActivity", "onCreate: Error setting layout", e)
|
|
|
|
|
throw e
|
|
|
|
|
}
|
2024-09-17 09:24:50 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
try {
|
|
|
|
|
// Initialize views
|
|
|
|
|
initViews()
|
|
|
|
|
Log.d("SecondActivity", "onCreate: Views initialized")
|
2026-03-14 20:02:31 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
// Load existing configurations
|
|
|
|
|
loadConfigurations()
|
|
|
|
|
Log.d("SecondActivity", "onCreate: Configurations loaded")
|
2026-03-14 20:02:31 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
// Setup UI based on loaded data
|
|
|
|
|
setupUI()
|
|
|
|
|
Log.d("SecondActivity", "onCreate: UI setup completed")
|
2024-09-17 09:24:50 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
// Back button functionality
|
|
|
|
|
findViewById<ImageButton>(R.id.btnBack).setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Back button clicked")
|
|
|
|
|
finish()
|
|
|
|
|
}
|
2024-10-09 22:28:28 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
// Home button functionality
|
|
|
|
|
findViewById<Button>(R.id.btnHome).setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Home button clicked")
|
|
|
|
|
// Create intent to go back to MainActivity
|
|
|
|
|
val intent = Intent(this, MainActivity::class.java)
|
|
|
|
|
// Clear the activity stack to start fresh
|
|
|
|
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
|
|
|
startActivity(intent)
|
|
|
|
|
finish()
|
|
|
|
|
}
|
2024-11-07 21:31:57 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
// Add header button
|
|
|
|
|
btnAddHeader.setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Add header button clicked")
|
|
|
|
|
addHeaderEntry()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add prompt button
|
|
|
|
|
btnAddPrompt.setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Add prompt button clicked")
|
|
|
|
|
addPromptEntry()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Log.d("SecondActivity", "onCreate: Completed successfully")
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("SecondActivity", "onCreate: Error during initialization", e)
|
|
|
|
|
throw e
|
2024-11-07 21:31:57 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 09:24:50 +08:00
|
|
|
private fun initViews() {
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "initViews: Starting")
|
|
|
|
|
try {
|
|
|
|
|
etBaseUrl = findViewById(R.id.etBaseUrl)
|
|
|
|
|
etApiKey = findViewById(R.id.etApiKey)
|
|
|
|
|
btnToggleApiKey = findViewById(R.id.btnToggleApiKey)
|
|
|
|
|
etModel = findViewById(R.id.etModel)
|
|
|
|
|
|
|
|
|
|
// Header Section
|
|
|
|
|
llHeadersList = findViewById(R.id.llHeadersList)
|
|
|
|
|
btnAddHeader = findViewById(R.id.btnAddHeader)
|
|
|
|
|
layoutHeaderContent = findViewById(R.id.layoutHeaderContent)
|
|
|
|
|
ivHeaderArrow = findViewById(R.id.ivHeaderArrow)
|
|
|
|
|
layoutHeaderToggle = findViewById(R.id.layoutHeaderToggle)
|
|
|
|
|
|
|
|
|
|
// Prompt Section
|
|
|
|
|
llPromptList = findViewById(R.id.llPromptList)
|
|
|
|
|
btnAddPrompt = findViewById(R.id.btnAddPrompt)
|
|
|
|
|
layoutPromptContent = findViewById(R.id.layoutPromptContent)
|
|
|
|
|
ivPromptArrow = findViewById(R.id.ivPromptArrow)
|
|
|
|
|
layoutPromptToggle = findViewById(R.id.layoutPromptToggle)
|
|
|
|
|
|
2026-03-22 22:43:56 +08:00
|
|
|
// Theme Section
|
|
|
|
|
rgThemeMode = findViewById(R.id.rgThemeMode)
|
|
|
|
|
rbThemeFollowSystem = findViewById(R.id.rbThemeFollowSystem)
|
|
|
|
|
rbThemeLight = findViewById(R.id.rbThemeLight)
|
|
|
|
|
rbThemeDark = findViewById(R.id.rbThemeDark)
|
|
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "initViews: All views found")
|
|
|
|
|
|
|
|
|
|
// Setup API key toggle
|
|
|
|
|
btnToggleApiKey.setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "API key toggle clicked")
|
|
|
|
|
val isPassword = etApiKey.transformationMethod is PasswordTransformationMethod
|
|
|
|
|
etApiKey.transformationMethod = if (isPassword) null else PasswordTransformationMethod()
|
|
|
|
|
// Move cursor to end
|
|
|
|
|
etApiKey.setSelection(etApiKey.text.length)
|
|
|
|
|
|
|
|
|
|
// Update icon based on state
|
|
|
|
|
if (isPassword) {
|
|
|
|
|
btnToggleApiKey.setImageResource(android.R.drawable.ic_menu_view) // Show eye
|
|
|
|
|
} else {
|
|
|
|
|
btnToggleApiKey.setImageResource(android.R.drawable.ic_lock_idle_lock) // Show lock
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup Header Toggle (Fold/Unfold)
|
|
|
|
|
layoutHeaderToggle.setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Header toggle clicked")
|
|
|
|
|
val isExpanded = layoutHeaderContent.visibility == View.VISIBLE
|
|
|
|
|
if (isExpanded) {
|
|
|
|
|
layoutHeaderContent.visibility = View.GONE
|
|
|
|
|
ivHeaderArrow.rotation = 0f // Point right
|
|
|
|
|
} else {
|
|
|
|
|
layoutHeaderContent.visibility = View.VISIBLE
|
|
|
|
|
ivHeaderArrow.rotation = 90f // Point down
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Setup Prompt Toggle (Fold/Unfold)
|
|
|
|
|
layoutPromptToggle.setOnClickListener {
|
|
|
|
|
Log.d("SecondActivity", "Prompt toggle clicked")
|
|
|
|
|
val isExpanded = layoutPromptContent.visibility == View.VISIBLE
|
|
|
|
|
if (isExpanded) {
|
|
|
|
|
layoutPromptContent.visibility = View.GONE
|
|
|
|
|
ivPromptArrow.rotation = 0f // Point right
|
|
|
|
|
} else {
|
|
|
|
|
layoutPromptContent.visibility = View.VISIBLE
|
|
|
|
|
ivPromptArrow.rotation = 90f // Point down
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Log.d("SecondActivity", "initViews: Completed")
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
Log.e("SecondActivity", "initViews: Error finding views", e)
|
|
|
|
|
throw e
|
2026-03-14 20:02:31 +08:00
|
|
|
}
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
private fun loadConfigurations() {
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: Starting")
|
2026-03-14 20:02:31 +08:00
|
|
|
// Load shared preferences
|
|
|
|
|
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
|
2024-09-17 09:24:50 +08:00
|
|
|
val json = sharedPrefs.getString("configs", null)
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: JSON loaded: ${json?.substring(0, minOf(json.length, 100))}")
|
2026-03-14 20:02:31 +08:00
|
|
|
|
2024-09-17 09:24:50 +08:00
|
|
|
if (json != null) {
|
2026-03-14 20:02:31 +08:00
|
|
|
try {
|
|
|
|
|
// Try to load as new format first
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: Trying to parse as SettingsData")
|
2026-03-14 20:02:31 +08:00
|
|
|
val settings = Gson().fromJson(json, SettingsData::class.java)
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: SettingsData parsed successfully")
|
2026-03-14 20:02:31 +08:00
|
|
|
headerConfigs = settings.headerConfigs?.toMutableList() ?: mutableListOf()
|
|
|
|
|
promptConfigs = settings.promptConfigs?.toMutableList() ?: mutableListOf()
|
|
|
|
|
buttonConfigs = settings.buttonConfigs?.toMutableList() ?: mutableListOf()
|
|
|
|
|
|
|
|
|
|
// Load LLM config
|
|
|
|
|
etBaseUrl.setText(settings.llmConfig?.baseUrl ?: "https://api.openai.com/v1")
|
|
|
|
|
etApiKey.setText(settings.llmConfig?.apiKey ?: "")
|
|
|
|
|
etModel.setText(settings.llmConfig?.model ?: "gpt-4o")
|
|
|
|
|
|
|
|
|
|
// Update API key visibility based on whether it has text
|
|
|
|
|
updateApiKeyVisibility()
|
|
|
|
|
|
|
|
|
|
} catch (e: Exception) {
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.e("SecondActivity", "loadConfigurations: Error parsing SettingsData", e)
|
2026-03-14 20:02:31 +08:00
|
|
|
// If new format fails, try to load old format for migration
|
|
|
|
|
try {
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: Trying to parse as List<APIConfig>")
|
2026-03-14 20:02:31 +08:00
|
|
|
val type = object : TypeToken<List<APIConfig>>() {}.type
|
|
|
|
|
val oldConfigs = Gson().fromJson<List<APIConfig>>(json, type)
|
|
|
|
|
if (oldConfigs.isNotEmpty()) {
|
|
|
|
|
val oldConfig = oldConfigs[0]
|
|
|
|
|
etBaseUrl.setText(oldConfig.url)
|
|
|
|
|
etApiKey.setText(oldConfig.key)
|
|
|
|
|
etModel.setText(oldConfig.model)
|
|
|
|
|
updateApiKeyVisibility()
|
|
|
|
|
}
|
|
|
|
|
} catch (e2: Exception) {
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.e("SecondActivity", "loadConfigurations: Error parsing List<APIConfig>", e2)
|
2026-03-14 20:02:31 +08:00
|
|
|
// If both fail, use defaults
|
|
|
|
|
etBaseUrl.setText("https://api.openai.com/v1")
|
|
|
|
|
etModel.setText("gpt-4o")
|
|
|
|
|
updateApiKeyVisibility()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// No saved config, use defaults
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: No saved config, using defaults")
|
2026-03-14 20:02:31 +08:00
|
|
|
etBaseUrl.setText("https://api.openai.com/v1")
|
|
|
|
|
etModel.setText("gpt-4o")
|
|
|
|
|
updateApiKeyVisibility()
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
2026-03-15 15:52:51 +08:00
|
|
|
Log.d("SecondActivity", "loadConfigurations: Completed")
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
private fun updateApiKeyVisibility() {
|
|
|
|
|
val isEmpty = etApiKey.text.toString().isEmpty()
|
|
|
|
|
etApiKey.transformationMethod = if (isEmpty) null else PasswordTransformationMethod()
|
|
|
|
|
// Keep cursor at end
|
|
|
|
|
etApiKey.setSelection(etApiKey.text.length)
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
private fun setupUI() {
|
|
|
|
|
// Setup headers
|
|
|
|
|
llHeadersList.removeAllViews()
|
|
|
|
|
if (headerConfigs.isEmpty()) {
|
|
|
|
|
addHeaderEntry() // Add one empty entry by default
|
|
|
|
|
} else {
|
|
|
|
|
for (header in headerConfigs) {
|
|
|
|
|
addHeaderEntry(header.key, header.value)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-15 15:52:51 +08:00
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
// Setup prompts
|
2026-03-15 15:52:51 +08:00
|
|
|
llPromptList.removeAllViews()
|
2026-03-14 20:02:31 +08:00
|
|
|
if (promptConfigs.isEmpty()) {
|
2026-03-15 15:52:51 +08:00
|
|
|
addPromptEntry() // Add one empty entry by default
|
|
|
|
|
} else {
|
|
|
|
|
for (prompt in promptConfigs) {
|
|
|
|
|
addPromptEntry(prompt.title, prompt.content)
|
|
|
|
|
}
|
2026-03-14 20:02:31 +08:00
|
|
|
}
|
2026-03-22 22:43:56 +08:00
|
|
|
|
|
|
|
|
// Setup theme
|
|
|
|
|
setupTheme()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setupTheme() {
|
|
|
|
|
// Get saved theme mode
|
|
|
|
|
val themeMode = ThemeManager.getThemeMode(this)
|
|
|
|
|
|
|
|
|
|
// Set the correct radio button
|
|
|
|
|
when (themeMode) {
|
|
|
|
|
ThemeManager.THEME_FOLLOW_SYSTEM -> rbThemeFollowSystem.isChecked = true
|
|
|
|
|
ThemeManager.THEME_LIGHT -> rbThemeLight.isChecked = true
|
|
|
|
|
ThemeManager.THEME_DARK -> rbThemeDark.isChecked = true
|
|
|
|
|
else -> rbThemeFollowSystem.isChecked = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set up radio group listener
|
|
|
|
|
rgThemeMode.setOnCheckedChangeListener { _, checkedId ->
|
|
|
|
|
val newMode = when (checkedId) {
|
|
|
|
|
R.id.rbThemeFollowSystem -> ThemeManager.THEME_FOLLOW_SYSTEM
|
|
|
|
|
R.id.rbThemeLight -> ThemeManager.THEME_LIGHT
|
|
|
|
|
R.id.rbThemeDark -> ThemeManager.THEME_DARK
|
|
|
|
|
else -> ThemeManager.THEME_FOLLOW_SYSTEM
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save and apply the new theme
|
|
|
|
|
ThemeManager.setThemeMode(this, newMode)
|
|
|
|
|
Log.d("SecondActivity", "Theme mode changed to: ${ThemeManager.getThemeModeName(newMode)}")
|
|
|
|
|
|
|
|
|
|
// Recreate activity to apply theme changes
|
|
|
|
|
recreate()
|
|
|
|
|
}
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
private fun addHeaderEntry(key: String = "", value: String = "") {
|
|
|
|
|
val view = layoutInflater.inflate(R.layout.header_entry, null)
|
|
|
|
|
val etKey = view.findViewById<EditText>(R.id.etHeaderKey)
|
|
|
|
|
val etValue = view.findViewById<EditText>(R.id.etHeaderValue)
|
|
|
|
|
val btnRemove = view.findViewById<ImageButton>(R.id.btnRemoveHeader)
|
|
|
|
|
|
|
|
|
|
etKey.setText(key)
|
|
|
|
|
etValue.setText(value)
|
|
|
|
|
|
|
|
|
|
btnRemove.setOnClickListener {
|
|
|
|
|
llHeadersList.removeView(view)
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
llHeadersList.addView(view)
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
private fun addPromptEntry(title: String = "", content: String = "") {
|
2026-03-14 20:02:31 +08:00
|
|
|
val view = layoutInflater.inflate(R.layout.prompt_entry, null)
|
|
|
|
|
val etTitle = view.findViewById<EditText>(R.id.etPromptTitle)
|
|
|
|
|
val etContent = view.findViewById<EditText>(R.id.etPromptContent)
|
|
|
|
|
val btnRemove = view.findViewById<ImageButton>(R.id.btnRemovePrompt)
|
|
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
etTitle.setText(title)
|
|
|
|
|
etContent.setText(content)
|
2026-03-14 20:02:31 +08:00
|
|
|
|
|
|
|
|
btnRemove.setOnClickListener {
|
2026-03-15 15:52:51 +08:00
|
|
|
llPromptList.removeView(view)
|
2026-03-14 20:02:31 +08:00
|
|
|
}
|
2024-09-17 09:24:50 +08:00
|
|
|
|
2026-03-15 15:52:51 +08:00
|
|
|
llPromptList.addView(view)
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
// Save all configurations when leaving or explicitly saving
|
|
|
|
|
override fun onPause() {
|
|
|
|
|
super.onPause()
|
|
|
|
|
saveConfigurations()
|
2024-09-17 09:24:50 +08:00
|
|
|
}
|
2024-10-09 22:28:28 +08:00
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
private fun saveConfigurations() {
|
|
|
|
|
// Update header configs from UI
|
|
|
|
|
headerConfigs.clear()
|
|
|
|
|
for (i in 0 until llHeadersList.childCount) {
|
|
|
|
|
val view = llHeadersList.getChildAt(i)
|
|
|
|
|
val key = view.findViewById<EditText>(R.id.etHeaderKey).text.toString()
|
|
|
|
|
val value = view.findViewById<EditText>(R.id.etHeaderValue).text.toString()
|
|
|
|
|
if (key.isNotBlank() && value.isNotBlank()) {
|
|
|
|
|
headerConfigs.add(HeaderConfig(key, value))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update prompt configs from UI
|
|
|
|
|
promptConfigs.clear()
|
2026-03-15 15:52:51 +08:00
|
|
|
for (i in 0 until llPromptList.childCount) {
|
|
|
|
|
val view = llPromptList.getChildAt(i)
|
2026-03-14 20:02:31 +08:00
|
|
|
val title = view.findViewById<EditText>(R.id.etPromptTitle).text.toString()
|
|
|
|
|
val content = view.findViewById<EditText>(R.id.etPromptContent).text.toString()
|
|
|
|
|
if (title.isNotBlank() && content.isNotBlank()) {
|
2026-03-15 15:52:51 +08:00
|
|
|
promptConfigs.add(PromptConfig(id = "prompt_$i", title = title, content = content))
|
2026-03-14 20:02:31 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-15 15:52:51 +08:00
|
|
|
|
|
|
|
|
// Note: Buttons are removed from this UI as per new design.
|
|
|
|
|
// We keep the list empty for data compatibility.
|
2026-03-14 20:02:31 +08:00
|
|
|
buttonConfigs.clear()
|
|
|
|
|
|
|
|
|
|
// Save LLM config
|
|
|
|
|
val llmConfig = LLMConfig(
|
|
|
|
|
baseUrl = etBaseUrl.text.toString(),
|
|
|
|
|
apiKey = etApiKey.text.toString(),
|
|
|
|
|
model = etModel.text.toString()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Save everything
|
|
|
|
|
val settingsData = SettingsData(
|
|
|
|
|
llmConfig = llmConfig,
|
|
|
|
|
headerConfigs = headerConfigs,
|
|
|
|
|
promptConfigs = promptConfigs,
|
|
|
|
|
buttonConfigs = buttonConfigs
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
val json = Gson().toJson(settingsData)
|
|
|
|
|
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
|
|
|
|
|
sharedPrefs.edit().putString("configs", json).apply()
|
|
|
|
|
|
|
|
|
|
// Also update the legacy APIConfig for backward compatibility
|
|
|
|
|
apiConfig = APIConfig(
|
|
|
|
|
System.currentTimeMillis(),
|
|
|
|
|
"llm-config",
|
|
|
|
|
etBaseUrl.text.toString(),
|
|
|
|
|
etApiKey.text.toString(),
|
|
|
|
|
"",
|
|
|
|
|
etModel.text.toString()
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-09-17 09:24:50 +08:00
|
|
|
|
2026-03-14 20:02:31 +08:00
|
|
|
// Legacy APIConfig class for backward compatibility with existing code
|
|
|
|
|
data class APIConfig(
|
|
|
|
|
val id: Long,
|
|
|
|
|
val name: String,
|
|
|
|
|
val url: String,
|
|
|
|
|
val key: String,
|
|
|
|
|
val secretKey: String,
|
|
|
|
|
val model: String
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// New data classes for settings structure
|
|
|
|
|
data class LLMConfig(
|
|
|
|
|
val baseUrl: String,
|
|
|
|
|
val apiKey: String,
|
|
|
|
|
val model: String
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
data class SettingsData(
|
|
|
|
|
val llmConfig: LLMConfig?,
|
|
|
|
|
val headerConfigs: List<HeaderConfig>?,
|
|
|
|
|
val promptConfigs: List<PromptConfig>?,
|
|
|
|
|
val buttonConfigs: List<ButtonConfig>?
|
|
|
|
|
)
|
|
|
|
|
}
|