优化配置页面和首页布局:添加自定义提示词功能,简化UI设计,去掉圆角边框,添加返回首页按钮
This commit is contained in:
@@ -8,10 +8,12 @@ import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
@@ -45,12 +47,16 @@ class SecondActivity : AppCompatActivity() {
|
||||
private lateinit var etModel: EditText
|
||||
private lateinit var llHeadersList: LinearLayout
|
||||
private lateinit var btnAddHeader: Button
|
||||
private lateinit var llPromptsList: LinearLayout
|
||||
private lateinit var layoutHeaderContent: LinearLayout
|
||||
private lateinit var ivHeaderArrow: ImageView
|
||||
private lateinit var layoutHeaderToggle: LinearLayout
|
||||
|
||||
// Prompt view references
|
||||
private lateinit var llPromptList: 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 lateinit var layoutPromptContent: LinearLayout
|
||||
private lateinit var ivPromptArrow: ImageView
|
||||
private lateinit var layoutPromptToggle: LinearLayout
|
||||
|
||||
// Data storage
|
||||
private var headerConfigs = mutableListOf<HeaderConfig>()
|
||||
@@ -62,75 +68,150 @@ class SecondActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_second)
|
||||
|
||||
// Initialize views
|
||||
initViews()
|
||||
|
||||
// Load existing configurations
|
||||
loadConfigurations()
|
||||
|
||||
// Setup UI based on loaded data
|
||||
setupUI()
|
||||
|
||||
// Back button functionality
|
||||
findViewById<Button>(R.id.btnBack).setOnClickListener {
|
||||
finish()
|
||||
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
|
||||
}
|
||||
|
||||
// Add header button
|
||||
btnAddHeader.setOnClickListener {
|
||||
addHeaderEntry()
|
||||
}
|
||||
try {
|
||||
// Initialize views
|
||||
initViews()
|
||||
Log.d("SecondActivity", "onCreate: Views initialized")
|
||||
|
||||
// Add prompt button
|
||||
btnAddPrompt.setOnClickListener {
|
||||
addPromptEntry()
|
||||
}
|
||||
// Load existing configurations
|
||||
loadConfigurations()
|
||||
Log.d("SecondActivity", "onCreate: Configurations loaded")
|
||||
|
||||
// Add button
|
||||
btnAddButton.setOnClickListener {
|
||||
addButtonEntry()
|
||||
// Setup UI based on loaded data
|
||||
setupUI()
|
||||
Log.d("SecondActivity", "onCreate: UI setup completed")
|
||||
|
||||
// Back button functionality
|
||||
findViewById<ImageButton>(R.id.btnBack).setOnClickListener {
|
||||
Log.d("SecondActivity", "Back button clicked")
|
||||
finish()
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
etBaseUrl = findViewById(R.id.etBaseUrl)
|
||||
etApiKey = findViewById(R.id.etApiKey)
|
||||
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)
|
||||
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)
|
||||
|
||||
Log.d("SecondActivity", "initViews: All views found")
|
||||
|
||||
// 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)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadConfigurations() {
|
||||
Log.d("SecondActivity", "loadConfigurations: Starting")
|
||||
// Load shared preferences
|
||||
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
|
||||
val json = sharedPrefs.getString("configs", null)
|
||||
Log.d("SecondActivity", "loadConfigurations: JSON loaded: ${json?.substring(0, minOf(json.length, 100))}")
|
||||
|
||||
if (json != null) {
|
||||
try {
|
||||
// Try to load as new format first
|
||||
Log.d("SecondActivity", "loadConfigurations: Trying to parse as SettingsData")
|
||||
val settings = Gson().fromJson(json, SettingsData::class.java)
|
||||
Log.d("SecondActivity", "loadConfigurations: SettingsData parsed successfully")
|
||||
headerConfigs = settings.headerConfigs?.toMutableList() ?: mutableListOf()
|
||||
promptConfigs = settings.promptConfigs?.toMutableList() ?: mutableListOf()
|
||||
buttonConfigs = settings.buttonConfigs?.toMutableList() ?: mutableListOf()
|
||||
@@ -144,8 +225,10 @@ class SecondActivity : AppCompatActivity() {
|
||||
updateApiKeyVisibility()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("SecondActivity", "loadConfigurations: Error parsing SettingsData", e)
|
||||
// If new format fails, try to load old format for migration
|
||||
try {
|
||||
Log.d("SecondActivity", "loadConfigurations: Trying to parse as List<APIConfig>")
|
||||
val type = object : TypeToken<List<APIConfig>>() {}.type
|
||||
val oldConfigs = Gson().fromJson<List<APIConfig>>(json, type)
|
||||
if (oldConfigs.isNotEmpty()) {
|
||||
@@ -156,6 +239,7 @@ class SecondActivity : AppCompatActivity() {
|
||||
updateApiKeyVisibility()
|
||||
}
|
||||
} catch (e2: Exception) {
|
||||
Log.e("SecondActivity", "loadConfigurations: Error parsing List<APIConfig>", e2)
|
||||
// If both fail, use defaults
|
||||
etBaseUrl.setText("https://api.openai.com/v1")
|
||||
etModel.setText("gpt-4o")
|
||||
@@ -164,19 +248,17 @@ class SecondActivity : AppCompatActivity() {
|
||||
}
|
||||
} else {
|
||||
// No saved config, use defaults
|
||||
Log.d("SecondActivity", "loadConfigurations: No saved config, using defaults")
|
||||
etBaseUrl.setText("https://api.openai.com/v1")
|
||||
etModel.setText("gpt-4o")
|
||||
updateApiKeyVisibility()
|
||||
}
|
||||
Log.d("SecondActivity", "loadConfigurations: Completed")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -191,37 +273,16 @@ class SecondActivity : AppCompatActivity() {
|
||||
addHeaderEntry(header.key, header.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Setup prompts
|
||||
llPromptsList.removeAllViews()
|
||||
llPromptList.removeAllViews()
|
||||
if (promptConfigs.isEmpty()) {
|
||||
// Add default prompts from JSON
|
||||
promptConfigs.add(PromptConfig("default-1", "翻译助手", "你是一个专业翻译,请将用户输入的内容翻译成中文,保持原意,语言自然流畅。"))
|
||||
promptConfigs.add(PromptConfig("default-2", "代码解释", "你是一个资深程序员,请详细解释用户提供的代码,用中文说明其功能和逻辑。"))
|
||||
addPromptEntry() // Add one empty entry by default
|
||||
} else {
|
||||
for (prompt in promptConfigs) {
|
||||
addPromptEntry(prompt.title, prompt.content)
|
||||
}
|
||||
}
|
||||
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 = "") {
|
||||
@@ -235,89 +296,25 @@ class SecondActivity : AppCompatActivity() {
|
||||
|
||||
btnRemove.setOnClickListener {
|
||||
llHeadersList.removeView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
llHeadersList.addView(view)
|
||||
updateEmptyStates()
|
||||
}
|
||||
|
||||
private fun addPromptEntry(config: PromptConfig = PromptConfig("", "", "")) {
|
||||
private fun addPromptEntry(title: String = "", content: String = "") {
|
||||
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)
|
||||
etTitle.setText(title)
|
||||
etContent.setText(content)
|
||||
|
||||
btnRemove.setOnClickListener {
|
||||
llPromptsList.removeView(view)
|
||||
updateEmptyStates()
|
||||
llPromptList.removeView(view)
|
||||
}
|
||||
|
||||
// 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
|
||||
llPromptList.addView(view)
|
||||
}
|
||||
|
||||
// Save all configurations when leaving or explicitly saving
|
||||
@@ -340,42 +337,18 @@ class SecondActivity : AppCompatActivity() {
|
||||
|
||||
// Update prompt configs from UI
|
||||
promptConfigs.clear()
|
||||
for (i in 0 until llPromptsList.childCount) {
|
||||
val view = llPromptsList.getChildAt(i)
|
||||
for (i in 0 until llPromptList.childCount) {
|
||||
val view = llPromptList.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
|
||||
))
|
||||
promptConfigs.add(PromptConfig(id = "prompt_$i", title = title, content = content))
|
||||
}
|
||||
}
|
||||
|
||||
// Update button configs from UI
|
||||
|
||||
// Note: Buttons are removed from this UI as per new design.
|
||||
// We keep the list empty for data compatibility.
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user