Replace configuration page with new settings interface featuring LLM config, custom headers, preset prompts, and custom buttons sections
This commit is contained in:
@@ -1,290 +1,413 @@
|
||||
package com.example.flomo_ai
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.graphics.Color
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
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)
|
||||
|
||||
class SecondActivity : AppCompatActivity() {
|
||||
private lateinit var etApiName: EditText
|
||||
private lateinit var etApiUrl: EditText
|
||||
|
||||
// View references
|
||||
private lateinit var etBaseUrl: EditText
|
||||
private lateinit var etApiKey: EditText
|
||||
private lateinit var etApiSecretKey: EditText
|
||||
private lateinit var etApiModel: EditText
|
||||
private lateinit var btnSave: Button
|
||||
private lateinit var llConfigList: LinearLayout
|
||||
private lateinit var btnToggleApiKey: ImageButton
|
||||
private lateinit var etModel: EditText
|
||||
private lateinit var llHeadersList: LinearLayout
|
||||
private lateinit var btnAddHeader: Button
|
||||
private lateinit var llPromptsList: LinearLayout
|
||||
private lateinit var btnAddPrompt: Button
|
||||
private lateinit var tvEmptyPrompts: TextView
|
||||
private lateinit var llButtonsList: LinearLayout
|
||||
private lateinit var btnAddButton: Button
|
||||
private lateinit var tvEmptyButtons: TextView
|
||||
|
||||
private var configs = mutableListOf<APIConfig>()
|
||||
private var editingId: Long? = null
|
||||
// Data storage
|
||||
private var headerConfigs = mutableListOf<HeaderConfig>()
|
||||
private var promptConfigs = mutableListOf<PromptConfig>()
|
||||
private var buttonConfigs = mutableListOf<ButtonConfig>()
|
||||
|
||||
private val pickImageLauncher = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
|
||||
if (uri!= null) {
|
||||
// 处理选中的图片
|
||||
}
|
||||
}
|
||||
private fun setButtonListeners() {
|
||||
val buttonIds = listOf(
|
||||
R.id.button_holo_red_light,
|
||||
R.id.button_holo_green_light,
|
||||
R.id.button_holo_blue_light,
|
||||
R.id.button_holo_orange_light
|
||||
)
|
||||
|
||||
buttonIds.forEach { buttonId ->
|
||||
findViewById<Button>(buttonId).setOnClickListener {
|
||||
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
|
||||
val editor = sharedPrefs.edit()
|
||||
// 获取颜色资源 ID
|
||||
val colorResId = getColorForButtonId(buttonId)
|
||||
// 使用正则表达式从资源名称中提取颜色值并存储
|
||||
val colorValue = extractColorValue(buttonId)
|
||||
editor.putString("buttonColor", colorValue)
|
||||
editor.apply()
|
||||
Log.d("SharedPrefsDebug", "Stored color value in SecondActivity: $colorValue")
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun getColorForButtonId(buttonId: Int): Int {
|
||||
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
// API 23 及以上使用 Context.getColor()
|
||||
baseContext.getColor(findColorResourceId(buttonId))
|
||||
} else {
|
||||
// 低版本使用 ResourcesCompat.getColor()
|
||||
ResourcesCompat.getColor(resources, findColorResourceId(buttonId), null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findColorResourceId(buttonId: Int): Int {
|
||||
return when (buttonId) {
|
||||
R.id.button_holo_red_light -> android.R.color.holo_red_light
|
||||
R.id.button_holo_green_light -> android.R.color.holo_green_light
|
||||
R.id.button_holo_blue_light -> android.R.color.holo_blue_light
|
||||
R.id.button_holo_orange_light -> android.R.color.holo_orange_light
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractColorValue(buttonId: Int): String {
|
||||
val resourceName = resources.getResourceEntryName(buttonId)
|
||||
val regex = Regex(".*_(.*)_light")
|
||||
val matchResult = regex.find(resourceName)
|
||||
return matchResult?.groupValues?.get(1)?: ""
|
||||
}
|
||||
// API config (for backward compatibility with existing API calls)
|
||||
private lateinit var apiConfig: APIConfig
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(androidx.appcompat.R.style.Theme_AppCompat)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_second)
|
||||
|
||||
// Initialize views
|
||||
initViews()
|
||||
loadConfigs()
|
||||
displayConfigs()
|
||||
|
||||
val btnGoBack: Button = findViewById(R.id.btnGoBack)
|
||||
btnGoBack.setOnClickListener {
|
||||
// Load existing configurations
|
||||
loadConfigurations()
|
||||
|
||||
// Setup UI based on loaded data
|
||||
setupUI()
|
||||
|
||||
// Back button functionality
|
||||
findViewById<Button>(R.id.btnBack).setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
btnSave.setOnClickListener {
|
||||
if (editingId != null) {
|
||||
updateConfig()
|
||||
} else {
|
||||
addConfig()
|
||||
}
|
||||
}
|
||||
//颜色按钮的监听
|
||||
setButtonListeners()
|
||||
|
||||
// 背景按钮的监听
|
||||
val buttonChooseImage = findViewById<Button>(R.id.buttonChooseImage)
|
||||
buttonChooseImage.setOnClickListener {
|
||||
pickImageLauncher.launch(null)
|
||||
// Add header button
|
||||
btnAddHeader.setOnClickListener {
|
||||
addHeaderEntry()
|
||||
}
|
||||
|
||||
// Add prompt button
|
||||
btnAddPrompt.setOnClickListener {
|
||||
addPromptEntry()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_CODE_PICK_IMAGE && resultCode == RESULT_OK && data!= null) {
|
||||
val selectedImageUri: Uri? = data.data
|
||||
if (selectedImageUri!= null) {
|
||||
// 将选择的图片 URI 传递回 MainActivity
|
||||
val resultIntent = Intent()
|
||||
resultIntent.putExtra("selectedImageUri", selectedImageUri.toString())
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
finish()
|
||||
// Add button
|
||||
btnAddButton.setOnClickListener {
|
||||
addButtonEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_CODE_PICK_IMAGE = 1000
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
etApiName = findViewById(R.id.etApiName)
|
||||
etApiUrl = findViewById(R.id.etApiUrl)
|
||||
etBaseUrl = findViewById(R.id.etBaseUrl)
|
||||
etApiKey = findViewById(R.id.etApiKey)
|
||||
etApiSecretKey = findViewById(R.id.etApiSecretKey)
|
||||
etApiModel = findViewById(R.id.etApiModel)
|
||||
btnSave = findViewById(R.id.btnSave)
|
||||
llConfigList = findViewById(R.id.llConfigList)
|
||||
btnToggleApiKey = findViewById(R.id.btnToggleApiKey)
|
||||
etModel = findViewById(R.id.etModel)
|
||||
llHeadersList = findViewById(R.id.llHeadersList)
|
||||
btnAddHeader = findViewById(R.id.btnAddHeader)
|
||||
llPromptsList = findViewById(R.id.llPromptsList)
|
||||
btnAddPrompt = findViewById(R.id.btnAddPrompt)
|
||||
tvEmptyPrompts = findViewById(R.id.tvEmptyPrompts)
|
||||
llButtonsList = findViewById(R.id.llButtonsList)
|
||||
btnAddButton = findViewById(R.id.btnAddButton)
|
||||
tvEmptyButtons = findViewById(R.id.tvEmptyButtons)
|
||||
|
||||
// Setup API key toggle
|
||||
btnToggleApiKey.setOnClickListener {
|
||||
val isPassword = etApiKey.transformationMethod is PasswordTransformationMethod
|
||||
etApiKey.transformationMethod = if (isPassword) null else PasswordTransformationMethod()
|
||||
// Move cursor to end
|
||||
etApiKey.setSelection(etApiKey.text.length)
|
||||
// Toggle icon
|
||||
val iconRes = if (isPassword)
|
||||
android.R.drawable.ic_lock_idle_lock else
|
||||
android.R.drawable.ic_lock_idle_unlocked
|
||||
btnToggleApiKey.setImageResource(iconRes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadConfigs() {
|
||||
// 获取一个名为 "APIConfigs" 的共享偏好设置
|
||||
val sharedPrefs = getSharedPreferences("APIConfigs", MODE_PRIVATE)
|
||||
private fun loadConfigurations() {
|
||||
// Load shared preferences
|
||||
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
|
||||
val json = sharedPrefs.getString("configs", null)
|
||||
|
||||
if (json != null) {
|
||||
// 创建一个 TypeToken 的实例,用于表示一个包含 APIConfig 对象的列表类型
|
||||
try {
|
||||
// Try to load as new format first
|
||||
val settings = Gson().fromJson(json, SettingsData::class.java)
|
||||
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) {
|
||||
// If new format fails, try to load old format for migration
|
||||
try {
|
||||
val type = object : TypeToken<List<APIConfig>>() {}.type
|
||||
configs = Gson().fromJson(json, 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) {
|
||||
// If both fail, use defaults
|
||||
etBaseUrl.setText("https://api.openai.com/v1")
|
||||
etModel.setText("gpt-4o")
|
||||
updateApiKeyVisibility()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No saved config, use defaults
|
||||
etBaseUrl.setText("https://api.openai.com/v1")
|
||||
etModel.setText("gpt-4o")
|
||||
updateApiKeyVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveConfigs() {
|
||||
val sharedPrefs = getSharedPreferences("APIConfigs", MODE_PRIVATE)
|
||||
val json = Gson().toJson(configs)
|
||||
private fun updateApiKeyVisibility() {
|
||||
val isEmpty = etApiKey.text.toString().isEmpty()
|
||||
etApiKey.transformationMethod = if (isEmpty) null else PasswordTransformationMethod()
|
||||
val iconRes = if (isEmpty || etApiKey.transformationMethod == null)
|
||||
android.R.drawable.ic_lock_idle_lock else
|
||||
android.R.drawable.ic_lock_idle_unlocked
|
||||
btnToggleApiKey.setImageResource(iconRes)
|
||||
// Keep cursor at end
|
||||
etApiKey.setSelection(etApiKey.text.length)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup prompts
|
||||
llPromptsList.removeAllViews()
|
||||
if (promptConfigs.isEmpty()) {
|
||||
// Add default prompts from JSON
|
||||
promptConfigs.add(PromptConfig("default-1", "翻译助手", "你是一个专业翻译,请将用户输入的内容翻译成中文,保持原意,语言自然流畅。"))
|
||||
promptConfigs.add(PromptConfig("default-2", "代码解释", "你是一个资深程序员,请详细解释用户提供的代码,用中文说明其功能和逻辑。"))
|
||||
}
|
||||
for (prompt in promptConfigs) {
|
||||
addPromptEntry(prompt)
|
||||
}
|
||||
updateEmptyStates()
|
||||
|
||||
// Setup buttons
|
||||
llButtonsList.removeAllViews()
|
||||
if (buttonConfigs.isEmpty()) {
|
||||
// Add default buttons from JSON
|
||||
buttonConfigs.add(ButtonConfig("btn-copy", "复制结果", "copy"))
|
||||
buttonConfigs.add(ButtonConfig(
|
||||
"btn-webhook",
|
||||
"发送到飞书",
|
||||
"api",
|
||||
"https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx",
|
||||
"POST",
|
||||
"{\"msg_type\": \"text\", \"content\": {\"text\": \"{output}\"}}"
|
||||
))
|
||||
}
|
||||
for (button in buttonConfigs) {
|
||||
addButtonEntry(button)
|
||||
}
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
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)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
llHeadersList.addView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
private fun addPromptEntry(config: PromptConfig = PromptConfig("", "", "")) {
|
||||
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 btnExpand = view.findViewById<Button>(R.id.btnExpandPrompt)
|
||||
val btnRemove = view.findViewById<ImageButton>(R.id.btnRemovePrompt)
|
||||
val vDivider = view.findViewById<View>(R.id.viewDivider)
|
||||
|
||||
etTitle.setText(config.title)
|
||||
etContent.setText(config.content)
|
||||
// Set expanded state (we'd need to store this in the view tag or similar)
|
||||
|
||||
btnRemove.setOnClickListener {
|
||||
llPromptsList.removeView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
// For simplicity, we're not implementing expand/collapse here
|
||||
// but in a full implementation we would toggle the content visibility
|
||||
|
||||
llPromptsList.addView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
private fun addButtonEntry(config: ButtonConfig = ButtonConfig("", "", "")) {
|
||||
val view = layoutInflater.inflate(R.layout.button_entry, null)
|
||||
val etLabel = view.findViewById<EditText>(R.id.etButtonLabel)
|
||||
val btnAction = view.findViewById<Button>(R.id.btnButtonAction)
|
||||
val btnRemove = view.findViewById<ImageButton>(R.id.btnRemoveButton)
|
||||
val llApiFields = view.findViewById<LinearLayout>(R.id.llApiFields)
|
||||
|
||||
etLabel.setText(config.label)
|
||||
btnAction.text = when (config.action) {
|
||||
"copy" -> "复制输出内容"
|
||||
"api" -> "提交到第三方 API"
|
||||
else -> "未知操作"
|
||||
}
|
||||
|
||||
// Show/hide API fields based on action
|
||||
llApiFields.visibility = if (config.action == "api") View.VISIBLE else View.GONE
|
||||
|
||||
btnAction.setOnClickListener {
|
||||
// Cycle through action options
|
||||
val newAction = when (btnAction.text.toString()) {
|
||||
"复制输出内容" -> "提交到第三方 API"
|
||||
"提交到第三方 API" -> "未知操作"
|
||||
else -> "复制输出内容"
|
||||
}
|
||||
btnAction.text = when (newAction) {
|
||||
"复制输出内容" -> "复制输出内容"
|
||||
"提交到第三方 API" -> "提交到第三方 API"
|
||||
else -> "未知操作"
|
||||
}
|
||||
llApiFields.visibility = if (newAction == "提交到第三方 API") View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
// If we have existing API config, populate fields
|
||||
if (config.action == "api") {
|
||||
// In a full implementation, we would populate the API fields here
|
||||
}
|
||||
|
||||
btnRemove.setOnClickListener {
|
||||
llButtonsList.removeView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
llButtonsList.addView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
private fun updateEmptyStates() {
|
||||
// Update prompts empty state
|
||||
tvEmptyPrompts.visibility = if (llPromptsList.childCount == 0) View.VISIBLE else View.GONE
|
||||
|
||||
// Update buttons empty state
|
||||
tvEmptyButtons.visibility = if (llButtonsList.childCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
// Save all configurations when leaving or explicitly saving
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
saveConfigurations()
|
||||
}
|
||||
|
||||
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()
|
||||
for (i in 0 until llPromptsList.childCount) {
|
||||
val view = llPromptsList.getChildAt(i)
|
||||
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()) {
|
||||
promptConfigs.add(PromptConfig(
|
||||
if (title.equals("翻译助手", ignoreCase = true)) "default-1"
|
||||
else if (title.equals("代码解释", ignoreCase = true)) "default-2"
|
||||
else UUID.randomUUID().toString(),
|
||||
title,
|
||||
content
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Update button configs from UI
|
||||
buttonConfigs.clear()
|
||||
for (i in 0 until llButtonsList.childCount) {
|
||||
val view = llButtonsList.getChildAt(i)
|
||||
val label = view.findViewById<EditText>(R.id.etButtonLabel).text.toString()
|
||||
val actionText = view.findViewById<Button>(R.id.btnButtonAction).text.toString()
|
||||
val action = when (actionText) {
|
||||
"复制输出内容" -> "copy"
|
||||
"提交到第三方 API" -> "api"
|
||||
else -> "unknown"
|
||||
}
|
||||
if (label.isNotBlank()) {
|
||||
buttonConfigs.add(ButtonConfig(
|
||||
if (label.equals("复制结果", ignoreCase = true)) "btn-copy"
|
||||
else if (label.equals("发送到飞书", ignoreCase = true)) "btn-webhook"
|
||||
else UUID.randomUUID().toString(),
|
||||
label,
|
||||
action
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
)
|
||||
}
|
||||
|
||||
private fun addConfig() {
|
||||
val name = etApiName.text.toString()
|
||||
val url = etApiUrl.text.toString()
|
||||
val key = etApiKey.text.toString()
|
||||
val secretKey = etApiSecretKey.text.toString()
|
||||
val model = etApiModel.text.toString()
|
||||
|
||||
// 生成唯一的 id
|
||||
val id = System.currentTimeMillis()
|
||||
// 创建新的配置项
|
||||
val newConfig = APIConfig(id, name, url, key, secretKey, model)
|
||||
// 添加配置项
|
||||
configs.add(newConfig)
|
||||
// 保存配置
|
||||
saveConfigs()
|
||||
// 显示配置
|
||||
displayConfigs()
|
||||
// 清空输入框
|
||||
clearInputs()
|
||||
}
|
||||
|
||||
private fun updateConfig() {
|
||||
val name = etApiName.text.toString()
|
||||
val url = etApiUrl.text.toString()
|
||||
val key = etApiKey.text.toString()
|
||||
val secretKey = etApiSecretKey.text.toString()
|
||||
val model = etApiModel.text.toString()
|
||||
|
||||
// 获取编辑的配置项 id
|
||||
val id = editingId ?: return
|
||||
// 更新配置项
|
||||
val updatedConfig = APIConfig(id, name, url, key, secretKey, model)
|
||||
val existingConfigIndex = configs.indexOfFirst { it.id == id }
|
||||
if (existingConfigIndex != -1) {
|
||||
configs[existingConfigIndex] = updatedConfig
|
||||
}
|
||||
// 保存配置
|
||||
saveConfigs()
|
||||
// 显示配置
|
||||
displayConfigs()
|
||||
// 清空输入框
|
||||
clearInputs()
|
||||
// 重置编辑状态
|
||||
editingId = null
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("MissingInflatedId")
|
||||
private fun displayConfigs() {
|
||||
llConfigList.removeAllViews()
|
||||
for (config in configs) {
|
||||
// 为每个配置项加载对应的布局文件
|
||||
val configView = layoutInflater.inflate(R.layout.item_api_config, null)
|
||||
// 设置各项文本信息
|
||||
// 获取并设置 Name 的 TextView 前景色和背景色
|
||||
val tvName = configView.findViewById<TextView>(R.id.tvName)
|
||||
tvName.text = "Name: ${config.name}"
|
||||
tvName.setTextColor(Color.BLACK)
|
||||
|
||||
// 获取并设置 URL 的 TextView 前景色和背景色
|
||||
val tvUrl = configView.findViewById<TextView>(R.id.tvUrl)
|
||||
tvUrl.text = "URL: ${config.url.take(32)}..."
|
||||
tvUrl.setTextColor(Color.BLACK)
|
||||
|
||||
// 获取并设置 Key 的 TextView 前景色和背景色
|
||||
val tvKey = configView.findViewById<TextView>(R.id.tvKey)
|
||||
tvKey.text = "Key: ${config.key.take(4)}..."
|
||||
tvKey.setTextColor(Color.BLACK)
|
||||
|
||||
// 获取并设置 SecretKey 的 TextView 前景色和背景色
|
||||
val tvSecretKey = configView.findViewById<TextView>(R.id.tvSecretKey)
|
||||
tvSecretKey.text = "Secret Key: ${config.secretKey.take(4)}..."
|
||||
tvSecretKey.setTextColor(Color.BLACK)
|
||||
|
||||
// 获取并设置 model 的 TextView 前景色和背景色
|
||||
val tvApiModel = configView.findViewById<TextView>(R.id.tvApiModel)
|
||||
tvApiModel.text = "Model: ${config.model}"
|
||||
tvApiModel.setTextColor(Color.BLACK)
|
||||
|
||||
// 设置编辑按钮点击事件
|
||||
configView.findViewById<Button>(R.id.btnEdit).setOnClickListener {
|
||||
editConfig(config)
|
||||
}
|
||||
|
||||
// 设置删除按钮点击事件
|
||||
configView.findViewById<Button>(R.id.btnDelete).setOnClickListener {
|
||||
deleteConfig(config.id)
|
||||
}
|
||||
|
||||
// 将包含配置信息的视图添加到父布局中
|
||||
llConfigList.addView(configView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun editConfig(config: APIConfig) {
|
||||
etApiName.setText(config.name)
|
||||
etApiUrl.setText(config.url)
|
||||
etApiKey.setText(config.key)
|
||||
etApiSecretKey.setText(config.secretKey)
|
||||
etApiModel.setText(config.model)
|
||||
// 设置编辑状态
|
||||
editingId = config.id
|
||||
btnSave.text = "更新配置"
|
||||
}
|
||||
|
||||
private fun deleteConfig(id: Long) {
|
||||
configs.removeAll { it.id == id }
|
||||
saveConfigs()
|
||||
displayConfigs()
|
||||
}
|
||||
|
||||
private fun clearInputs() {
|
||||
etApiName.text.clear()
|
||||
etApiUrl.text.clear()
|
||||
etApiKey.text.clear()
|
||||
etApiSecretKey.text.clear()
|
||||
etApiModel.text.clear()
|
||||
btnSave.text = "保存配置"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Legacy APIConfig class for backward compatibility with existing code
|
||||
data class APIConfig(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
@@ -293,3 +416,18 @@ data class APIConfig(
|
||||
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>?
|
||||
)
|
||||
}
|
||||
@@ -3,185 +3,371 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Header -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
tools:ignore="ExtraText">
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnGoBack"
|
||||
android:id="@+id/btnBack"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/back_to_main" />
|
||||
android:text="返回主界面"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headerTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="设置"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@android:color/black"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headerSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="配置自动保存到本地"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- LLM Config Section -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="文字处理AI区的配置"
|
||||
android:textSize="16sp" />
|
||||
android:layout_height="wrap_content"
|
||||
android:text="大模型配置"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiButtonName"
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="设置 API 接口地址、密钥与模型"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Base URL Field -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="API 按钮显示名称"
|
||||
android:minHeight="48dp" />
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Base URL"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiName"
|
||||
android:id="@+id/etBaseUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="API 名称"
|
||||
android:minHeight="48dp" />
|
||||
android:layout_height="48dp"
|
||||
android:hint="https://api.openai.com/v1"
|
||||
android:inputType="textUri"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiUrl"
|
||||
</LinearLayout>
|
||||
|
||||
<!-- API Key Field -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="API URL"
|
||||
android:minHeight="48dp" />
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="API Key"
|
||||
android:textSize="14sp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnToggleApiKey"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_lock_idle_lock"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiKey"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="@string/api_key"
|
||||
android:minHeight="48dp" />
|
||||
android:layout_height="48dp"
|
||||
android:hint="sk-..."
|
||||
android:inputType="textPassword"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiSecretKey"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="API Secret Key"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiModel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:hint="API 模型类型"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/save_config" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Model Field -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llConfigList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<!-- 图片选择区域 -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewImageHint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="点击按钮选择图片作为背景"
|
||||
android:layout_below="@id/buttonChooseImage"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="10dp" />
|
||||
android:text="模型"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonChooseImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="选择图片"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="20dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- 颜色选择区域 -->
|
||||
<RelativeLayout
|
||||
<EditText
|
||||
android:id="@+id/etModel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="48dp"
|
||||
android:hint="gpt-4o"
|
||||
android:inputType="text"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Custom Headers Section -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statustextView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="点击以下按钮,设置主页状态栏的颜色"
|
||||
android:textSize="16sp" />
|
||||
android:text="自定义请求头"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="向请求附加额外的 HTTP Header"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Headers List -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llHeadersList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/statustextView">
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_holo_red_light"
|
||||
<!-- Default Header Entry -->
|
||||
<LinearLayout
|
||||
android:id="@+id/headerEntryTemplate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etHeaderKey"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@android:color/holo_red_light"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_holo_green_light"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_weight="1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Header 名称"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_holo_green_light"
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/darker_gray"
|
||||
android:layout_marginHorizontal="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etHeaderValue"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@android:color/holo_green_light"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_holo_blue_light"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_weight="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/button_holo_red_light"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:hint="值"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnRemoveHeader"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Add Header Button -->
|
||||
<Button
|
||||
android:id="@+id/button_holo_blue_light"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@android:color/holo_blue_light"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_holo_orange_light"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_weight="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/button_holo_green_light"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck" />
|
||||
android:id="@+id/btnAddHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="添加请求头"
|
||||
android:layout_gravity="start"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Preset Prompts Section -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="预设提示词"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="管理可快速选用的系统提示词"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Prompts List -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llPromptsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<!-- Prompt entries will be added dynamically -->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Add Prompt Button -->
|
||||
<Button
|
||||
android:id="@+id/button_holo_orange_light"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@android:color/holo_orange_light"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_weight="1"
|
||||
app:layout_constraintStart_toEndOf="@+id/button_holo_blue_light"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck" />
|
||||
android:id="@+id/btnAddPrompt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="添加提示词"
|
||||
android:layout_gravity="start"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<!-- Empty State -->
|
||||
<TextView
|
||||
android:id="@+id/tvEmptyPrompts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂无预设提示词"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="12dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Custom Buttons Section -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="自定义按钮"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="配置主界面中显示的操作按钮"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Buttons List -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llButtonsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<!-- Button entries will be added dynamically -->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Add Button -->
|
||||
<Button
|
||||
android:id="@+id/btnAddButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="添加按钮"
|
||||
android:layout_gravity="start"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp"/>
|
||||
|
||||
<!-- Empty State -->
|
||||
<TextView
|
||||
android:id="@+id/tvEmptyButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂无自定义按钮"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="12dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
93
app/src/main/res/layout/button_entry.xml
Normal file
93
app/src/main/res/layout/button_entry.xml
Normal file
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="12sp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8sp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etButtonLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:hint="按钮标签"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnButtonAction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="复制输出内容"
|
||||
android:layout_marginStart="8sp"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnRemoveButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginStart="8sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- API Configuration Fields (shown when action is "api") -->
|
||||
<LinearLayout
|
||||
android:id="@+id/llApiFields"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:hint="API URL"
|
||||
android:inputType="textUri"
|
||||
android:background="@android:color/white"
|
||||
android:padding="8sp"
|
||||
android:layout_marginBottom="4sp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiMethod"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="HTTP Method (POST/GET)"
|
||||
android:inputType="text"
|
||||
android:background="@android:color/white"
|
||||
android:padding="8sp"
|
||||
android:layout_marginEnd="4sp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etApiBodyTemplate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:hint="请求体模板 (使用 {output} 占位符)"
|
||||
android:inputType="textMultiLine"
|
||||
android:minLines="2"
|
||||
android:background="@android:color/white"
|
||||
android:padding="8sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
42
app/src/main/res/layout/header_entry.xml
Normal file
42
app/src/main/res/layout/header_entry.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="8sp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etHeaderKey"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="Header 名称"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/darker_gray"
|
||||
android:layout_marginHorizontal="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etHeaderValue"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:hint="值"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnRemoveHeader"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
60
app/src/main/res/layout/prompt_entry.xml
Normal file
60
app/src/main/res/layout/prompt_entry.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@drawable/edittext_border"
|
||||
android:padding="12sp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8sp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etPromptTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="提示词标题"
|
||||
android:inputType="text"
|
||||
android:padding="4sp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnRemovePrompt"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etPromptContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:hint="提示词内容(System Prompt)"
|
||||
android:inputType="textMultiLine"
|
||||
android:minLines="4"
|
||||
android:background="@android:color/white"
|
||||
android:padding="8sp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnExpandPrompt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="展开"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="4sp"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/viewDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@android:color/darker_gray"
|
||||
android:layout_marginVertical="8sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user