feat: 简化SecondActivity,支持3个模型配置

This commit is contained in:
xiaji
2026-05-08 23:22:06 +08:00
parent 8cc254ebc1
commit 1fef271a20
2 changed files with 165 additions and 834 deletions

View File

@@ -4,13 +4,11 @@ import android.annotation.SuppressLint
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
@@ -20,10 +18,8 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.example.flomo_ai.ui.theme.ThemeManager import com.example.flomo_ai.ui.theme.ThemeManager
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
@@ -35,64 +31,32 @@ import org.json.JSONObject
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var inputEditText: EditText private lateinit var inputEditText: EditText
private lateinit var configButton: Button
private lateinit var outputStatusLabel: TextView
private lateinit var outputTextView: EditText private lateinit var outputTextView: EditText
private lateinit var promptSelector: Spinner private lateinit var promptSelector: Spinner
private lateinit var promptNameText: TextView private lateinit var promptNameText: TextView
private lateinit var promptContentText: TextView private lateinit var promptContentText: TextView
private lateinit var headerModelSelector: Spinner
// Data classes matching SecondActivity private var llmConfigs = listOf<LLMConfig>()
data class HeaderConfig(val key: String, val value: String) private var selectedLlmIndex = 0
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)
data class LLMConfig(val name: String, val baseUrl: String, val apiKey: String, val model: String)
data class SettingsData(
val llmConfigs: List<LLMConfig>?,
val selectedLlmIndex: Int?,
val headerConfigs: List<HeaderConfig>?,
val promptConfigs: List<PromptConfig>?,
val buttonConfigs: List<ButtonConfig>?,
val llmConfig: LLMConfig? = null
)
@SuppressLint("MissingInflatedId", "CutPasteId", "SetTextI18n") @SuppressLint("MissingInflatedId", "CutPasteId", "SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager.applySavedTheme(this) ThemeManager.applySavedTheme(this)
Log.d("MainActivity", "onCreate: Starting MainActivity")
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
Log.d("MainActivity", "onCreate: Layout set")
promptSelector = findViewById<Spinner>(R.id.promptSelector) promptSelector = findViewById<Spinner>(R.id.promptSelector)
promptNameText = findViewById<TextView>(R.id.promptNameText) promptNameText = findViewById<TextView>(R.id.promptNameText)
promptContentText = findViewById<TextView>(R.id.promptContentText) promptContentText = findViewById<TextView>(R.id.promptContentText)
inputEditText = findViewById<EditText>(R.id.inputEditText) inputEditText = findViewById<EditText>(R.id.inputEditText)
val sendButton = findViewById<Button>(R.id.sendButton) val sendButton = findViewById<Button>(R.id.sendButton)
val stopButton = findViewById<Button>(R.id.stopButton)
outputStatusLabel = findViewById<TextView>(R.id.outputStatusLabel)
outputTextView = findViewById<EditText>(R.id.outputTextView)
val btnCopyResult = findViewById<Button>(R.id.btnCopyResult) val btnCopyResult = findViewById<Button>(R.id.btnCopyResult)
val headerTitle = findViewById<TextView>(R.id.headerTitle)
headerModelSelector = findViewById<Spinner>(R.id.headerModelSelector)
Log.d("MainActivity", "onCreate: Views initialized")
headerTitle.text = "AI优化"
loadModelsFromConfig() loadModelsFromConfig()
// Initialize quick action buttons
initQuickButtons()
// Load prompts from configuration
loadPromptsFromConfig() loadPromptsFromConfig()
outputStatusLabel.text = "等待发送"
outputTextView.setText("")
// Setup prompt selector listener
promptSelector.setOnItemSelectedListener(object : android.widget.AdapterView.OnItemSelectedListener { promptSelector.setOnItemSelectedListener(object : android.widget.AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: android.widget.AdapterView<*>, view: android.view.View?, position: Int, id: Long) { override fun onItemSelected(parent: android.widget.AdapterView<*>, view: View?, position: Int, id: Long) {
val selectedTitle = promptSelector.getItemAtPosition(position) as String val selectedTitle = promptSelector.getItemAtPosition(position) as String
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -112,8 +76,6 @@ class MainActivity : AppCompatActivity() {
} else { } else {
promptContentText.visibility = View.GONE promptContentText.visibility = View.GONE
} }
Log.d("MainActivity", "Prompt selected: $selectedTitle, content: $content")
} }
override fun onNothingSelected(parent: android.widget.AdapterView<*>) { override fun onNothingSelected(parent: android.widget.AdapterView<*>) {
@@ -124,11 +86,11 @@ class MainActivity : AppCompatActivity() {
}) })
sendButton.setOnClickListener { sendButton.setOnClickListener {
Log.d("MainActivity", "Send button clicked")
val inputText = inputEditText.text.toString() val inputText = inputEditText.text.toString()
if (inputText.isNotEmpty()) { if (inputText.isEmpty()) {
outputStatusLabel.text = "连接中…" Toast.makeText(this, "请输入内容", Toast.LENGTH_SHORT).show()
outputTextView.setText("正在生成...") return@setOnClickListener
}
val promptContent = promptContentText.text.toString() val promptContent = promptContentText.text.toString()
val fullContent = if (promptContent.isNotEmpty() && promptContent != "无特殊指令") { val fullContent = if (promptContent.isNotEmpty() && promptContent != "无特殊指令") {
@@ -136,19 +98,74 @@ class MainActivity : AppCompatActivity() {
} else { } else {
inputText inputText
} }
Log.d("MainActivity", "Full content to send: $fullContent")
// 加载配置并发送请求
loadLlmConfigsAndSend(fullContent)
}
btnCopyResult.setOnClickListener {
val textToCopy = outputTextView.text.toString()
if (textToCopy.isNotEmpty() && textToCopy != "发送消息后结果将在此显示") {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("优化结果", textToCopy)
clipboard.setPrimaryClip(clip)
Toast.makeText(this, "结果已复制到剪贴板", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "没有可复制的内容", Toast.LENGTH_SHORT).show()
}
}
initQuickButtons()
}
private fun loadModelsFromConfig() {
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
val json = sharedPrefs.getString("configs", null)
if (json != null) {
try {
val settings = Gson().fromJson(json, SettingsData::class.java)
llmConfigs = settings.llmConfigs ?: emptyList()
selectedLlmIndex = settings.selectedLlmIndex ?: 0
} catch (e: Exception) {
Log.e("MainActivity", "Error loading config", e)
}
}
}
private fun loadLlmConfigsAndSend(content: String) {
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
val json = sharedPrefs.getString("configs", null) ?: return
try {
val settings = Gson().fromJson(json, SettingsData::class.java)
val configs = settings.llmConfigs ?: return
if (configs.isEmpty()) return
val config = if (selectedLlmIndex < configs.size) configs[selectedLlmIndex] else configs[0]
val baseUrl = config.baseUrl.ifEmpty { "https://api.openai.com/v1" }
val apiKey = config.apiKey
val model = config.model.ifEmpty { "gpt-4o" }
sendToLlm(baseUrl, apiKey, model, content)
} catch (e: Exception) {
Log.e("MainActivity", "Error loading LLM config", e)
outputTextView.setText("错误: ${e.message}")
}
}
private fun sendToLlm(baseUrl: String, apiKey: String, model: String, content: String) {
val outputStatusLabel = findViewById<TextView>(R.id.outputStatusLabel)
outputStatusLabel.text = "连接中..."
outputTextView.setText("正在生成...")
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val llmConfig = getSelectedModelConfig()
val baseUrl = llmConfig.baseUrl.ifEmpty { "https://open.bigmodel.cn/api/paas/v4" }
val apiKey = llmConfig.apiKey
val model = llmConfig.model.ifEmpty { "glm-4.7-flash" }
val messagesJson = JSONArray().apply { val messagesJson = JSONArray().apply {
put(JSONObject().apply { put(JSONObject().apply {
put("role", "user") put("role", "user")
put("content", fullContent) put("content", content)
}) })
} }
@@ -156,7 +173,6 @@ class MainActivity : AppCompatActivity() {
put("model", model) put("model", model)
put("messages", messagesJson) put("messages", messagesJson)
put("max_tokens", 65536) put("max_tokens", 65536)
put("temperature", 1.0)
} }
val client = OkHttpClient() val client = OkHttpClient()
@@ -181,9 +197,9 @@ class MainActivity : AppCompatActivity() {
val choices = responseJson.getJSONArray("choices") val choices = responseJson.getJSONArray("choices")
if (choices.length() > 0) { if (choices.length() > 0) {
val message = choices.getJSONObject(0).getJSONObject("message") val message = choices.getJSONObject(0).getJSONObject("message")
val content = message.getString("content") val result = message.getString("content")
outputStatusLabel.text = "已完成" outputStatusLabel.text = "已完成"
outputTextView.setText(content) outputTextView.setText(result)
} else { } else {
outputStatusLabel.text = "发生错误" outputStatusLabel.text = "发生错误"
outputTextView.setText("API 返回空结果") outputTextView.setText("API 返回空结果")
@@ -198,160 +214,18 @@ class MainActivity : AppCompatActivity() {
outputStatusLabel.text = "发生错误" outputStatusLabel.text = "发生错误"
outputTextView.setText("错误: ${e.message}") outputTextView.setText("错误: ${e.message}")
} }
Log.e("MainActivity", "Error calling API", e)
} }
} }
} else {
Log.w("MainActivity", "Input text is empty")
Toast.makeText(this, "请输入内容", Toast.LENGTH_SHORT).show()
}
}
stopButton.setOnClickListener {
Log.d("MainActivity", "Stop button clicked")
outputStatusLabel.text = "已停止"
Toast.makeText(this, "生成已停止", Toast.LENGTH_SHORT).show()
}
btnCopyResult.setOnClickListener {
Log.d("MainActivity", "btnCopyResult clicked")
val textToCopy = outputTextView.text.toString()
if (textToCopy.isNotEmpty() && textToCopy != "发送消息后结果将在此显示") {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("优化结果", textToCopy)
clipboard.setPrimaryClip(clip)
Toast.makeText(this, "结果已复制到剪贴板", Toast.LENGTH_SHORT).show()
Log.d("MainActivity", "Text copied to clipboard")
} else {
Toast.makeText(this, "没有可复制的内容", Toast.LENGTH_SHORT).show()
Log.w("MainActivity", "No text to copy")
}
}
val btnSaveNote = findViewById<Button>(R.id.btnSaveNote)
btnSaveNote.setOnClickListener {
Log.d("MainActivity", "btnSaveNote clicked")
val textToSave = outputTextView.text.toString()
if (textToSave.isNotEmpty() && textToSave != "发送消息后结果将在此显示") {
saveToNoteApi(textToSave)
}
private fun saveToNoteApi(content: String) {
try {
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
val json = sharedPrefs.getString("configs", null)
if (json == null) {
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show()
return
}
val settings = Gson().fromJson(json, NoteSettingsData::class.java)
val noteConfig = settings.noteApiConfig
if (noteConfig == null || noteConfig.apiUrl.isBlank()) {
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show()
return
}
outputStatusLabel.text = "提交中..."
CoroutineScope(Dispatchers.Main).launch {
try {
val result = submitToNoteApi(noteConfig.apiType, noteConfig.apiUrl, noteConfig.apiKey, content)
if (result) {
outputStatusLabel.text = "已提交"
Toast.makeText(this@MainActivity, "笔记已保存", Toast.LENGTH_SHORT).show()
inputEditText.text.clear()
} else {
outputStatusLabel.text = "提交失败"
Toast.makeText(this@MainActivity, "保存失败,请检查配置", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
outputStatusLabel.text = "提交失败"
Toast.makeText(this@MainActivity, "保存失败: ${e.message}", Toast.LENGTH_SHORT).show()
Log.e("MainActivity", "saveToNoteApi error", e)
}
}
} catch (e: Exception) {
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show()
Log.e("MainActivity", "saveToNoteApi error", e)
}
}
private suspend fun submitToNoteApi(apiType: String, apiUrl: String, apiKey: String, content: String): Boolean {
return withContext(Dispatchers.IO) {
try {
val client = OkHttpClient()
val requestBody = when (apiType) {
"Flomo" -> {
val json = JSONObject()
.put("content", content)
.put("content_type", "markdown")
json.toString().toRequestBody("application/json".toMediaType())
}
"Notion" -> {
val json = JSONObject()
.put("parent", JSONObject().put("database_id", apiKey))
.put("properties", JSONObject()
.put("Name", JSONObject()
.put("title", JSONArray()
.put(JSONObject().put("text", JSONObject().put("content", "AI优化结果")))
)
)
)
json.toString().toRequestBody("application/json".toMediaType())
}
else -> {
val json = JSONObject().put("content", content)
json.toString().toRequestBody("application/json".toMediaType())
}
}
val request = Request.Builder()
.url(apiUrl)
.addHeader("Content-Type", "application/json")
.post(requestBody)
.build()
val response = client.newCall(request).execute()
response.isSuccessful
} catch (e: Exception) {
Log.e("MainActivity", "submitToNoteApi error", e)
false
}
}
}
data class NoteApiConfig(
val apiType: String,
val apiUrl: String,
val apiKey: String
)
data class NoteSettingsData(
val noteApiConfig: NoteApiConfig?
)
}
if (selectedIndex >= models.size) {
selectedIndex = 0
}
return models[selectedIndex]
} }
private fun loadPromptsFromConfig() { private fun loadPromptsFromConfig() {
Log.d("MainActivity", "loadPromptsFromConfig: Starting")
try { try {
// Load shared preferences
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE) val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
val json = sharedPrefs.getString("configs", null) val json = sharedPrefs.getString("configs", null)
val promptTitles = mutableListOf<String>() val promptTitles = mutableListOf<String>()
val promptContents = mutableListOf<String>() val promptContents = mutableListOf<String>()
// Add default prompt
promptTitles.add("无系统提示词") promptTitles.add("无系统提示词")
promptContents.add("无特殊指令") promptContents.add("无特殊指令")
@@ -366,16 +240,11 @@ class MainActivity : AppCompatActivity() {
promptContents.add(prompt.content) promptContents.add(prompt.content)
} }
} }
Log.d("MainActivity", "Loaded ${promptConfigs.size} custom prompts from config")
} catch (e: Exception) { } catch (e: Exception) {
Log.e("MainActivity", "Error loading prompts from config", e) Log.e("MainActivity", "Error loading prompts", e)
} }
} else {
Log.d("MainActivity", "No saved config found, using default prompts")
} }
// Add default prompts if no custom prompts found
if (promptTitles.size == 1) { if (promptTitles.size == 1) {
promptTitles.add("翻译助手") promptTitles.add("翻译助手")
promptContents.add("将输入的文本翻译成指定语言") promptContents.add("将输入的文本翻译成指定语言")
@@ -383,7 +252,6 @@ class MainActivity : AppCompatActivity() {
promptContents.add("解释代码的功能和逻辑") promptContents.add("解释代码的功能和逻辑")
} }
// Add quick action prompts
if (!promptTitles.contains("检查错别字")) { if (!promptTitles.contains("检查错别字")) {
promptTitles.add("检查错别字") promptTitles.add("检查错别字")
promptContents.add("请检查以下文本中的错别字并纠正:") promptContents.add("请检查以下文本中的错别字并纠正:")
@@ -401,38 +269,19 @@ class MainActivity : AppCompatActivity() {
promptContents.add("请润色以下文本,使其更通顺流畅:") promptContents.add("请润色以下文本,使其更通顺流畅:")
} }
// Store prompt contents in a map for easy access
val promptMap = mutableMapOf<String, String>() val promptMap = mutableMapOf<String, String>()
for (i in promptTitles.indices) { for (i in promptTitles.indices) {
promptMap[promptTitles[i]] = promptContents[i] promptMap[promptTitles[i]] = promptContents[i]
} }
// Save prompt map to use in onItemSelectedListener
promptSelector.tag = promptMap promptSelector.tag = promptMap
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, promptTitles) val adapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, promptTitles)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
promptSelector.adapter = adapter promptSelector.adapter = adapter
promptSelector.setSelection(0) promptSelector.setSelection(0)
Log.d("MainActivity", "loadPromptsFromConfig: Completed with ${promptTitles.size} prompts")
} catch (e: Exception) { } catch (e: Exception) {
Log.e("MainActivity", "loadPromptsFromConfig: Error", e) Log.e("MainActivity", "loadPromptsFromConfig error", e)
// Fallback to default prompts
val promptTitles = listOf("无系统提示词", "翻译助手", "代码解释")
val promptContents = listOf("无特殊指令", "将输入的文本翻译成指定语言", "解释代码的功能和逻辑")
val promptMap = mutableMapOf<String, String>()
for (i in promptTitles.indices) {
promptMap[promptTitles[i]] = promptContents[i]
}
promptSelector.tag = promptMap
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, promptTitles)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
promptSelector.adapter = adapter
promptSelector.setSelection(0)
} }
} }
@@ -442,21 +291,10 @@ class MainActivity : AppCompatActivity() {
val btnTranslate = findViewById<LinearLayout>(R.id.btnTranslate) val btnTranslate = findViewById<LinearLayout>(R.id.btnTranslate)
val btnPolishing = findViewById<LinearLayout>(R.id.btnPolishing) val btnPolishing = findViewById<LinearLayout>(R.id.btnPolishing)
btnCheckTypos.setOnClickListener { btnCheckTypos.setOnClickListener { selectPrompt("检查错别字") }
selectPrompt("检查错别字") btnSummarize.setOnClickListener { selectPrompt("总结") }
} btnTranslate.setOnClickListener { selectPrompt("翻译") }
btnPolishing.setOnClickListener { selectPrompt("润色") }
btnSummarize.setOnClickListener {
selectPrompt("总结")
}
btnTranslate.setOnClickListener {
selectPrompt("翻译")
}
btnPolishing.setOnClickListener {
selectPrompt("润色")
}
} }
private fun selectPrompt(promptName: String) { private fun selectPrompt(promptName: String) {
@@ -480,153 +318,18 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun saveToNoteApi(content: String) { data class LLMConfig(val name: String, val baseUrl: String, val apiKey: String, val model: String, val enabled: Boolean = true)
try { data class SettingsData(
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE) val llmConfigs: List<LLMConfig>?,
val json = sharedPrefs.getString("configs", null) val selectedLlmIndex: Int?,
val headerConfigs: List<HeaderConfig>?,
if (json == null) { val promptConfigs: List<PromptConfig>?,
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show() val buttonConfigs: List<ButtonConfig>?,
return val noteApiConfig: NoteApiConfig?,
} val llmConfig: LLMConfig? = null
val settings = Gson().fromJson(json, NoteSettingsData::class.java)
val noteConfig = settings.noteApiConfig
if (noteConfig == null || noteConfig.apiUrl.isBlank()) {
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show()
return
}
outputStatusLabel.text = "提交中..."
CoroutineScope(Dispatchers.Main).launch {
try {
val result = submitToNoteApi(noteConfig.apiType, noteConfig.apiUrl, noteConfig.apiKey, content)
if (result) {
outputStatusLabel.text = "已提交"
Toast.makeText(this@MainActivity, "笔记已保存", Toast.LENGTH_SHORT).show()
} else {
outputStatusLabel.text = "提交失败"
Toast.makeText(this@MainActivity, "保存失败,请检查配置", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
outputStatusLabel.text = "提交失败"
Toast.makeText(this@MainActivity, "保存失败: ${e.message}", Toast.LENGTH_SHORT).show()
Log.e("MainActivity", "saveToNoteApi error", e)
}
}
} catch (e: Exception) {
Toast.makeText(this, "请先配置笔记API", Toast.LENGTH_SHORT).show()
Log.e("MainActivity", "saveToNoteApi error", e)
}
}
private suspend fun submitToNoteApi(apiType: String, apiUrl: String, apiKey: String, content: String): Boolean {
return withContext(Dispatchers.IO) {
try {
val client = OkHttpClient()
val (requestBody, useAuth) = when (apiType) {
"Flomo" -> {
val json = JSONObject()
.put("content", content)
.put("content_type", "markdown")
Pair(json.toString().toRequestBody("application/json".toMediaType()), false)
}
"Notion" -> {
val json = JSONObject()
.put("parent", JSONObject().put("database_id", apiKey))
.put("properties", JSONObject()
.put("Name", JSONObject()
.put("title", JSONArray()
.put(JSONObject().put("text", JSONObject().put("content", "AI优化结果")))
)
)
)
Pair(json.toString().toRequestBody("application/json".toMediaType()), true)
}
else -> {
val json = JSONObject().put("content", content)
Pair(json.toString().toRequestBody("application/json".toMediaType()), apiKey.isNotBlank())
}
}
val requestBuilder = Request.Builder()
.url(apiUrl)
.addHeader("Content-Type", "application/json")
.post(requestBody)
if (useAuth && apiKey.isNotBlank()) {
requestBuilder.addHeader("Authorization", "Bearer $apiKey")
}
val request = requestBuilder.build()
val response = client.newCall(request).execute()
response.isSuccessful
} catch (e: Exception) {
Log.e("MainActivity", "submitToNoteApi error", e)
false
}
}
private suspend fun submitToNoteApi(apiType: String, apiUrl: String, apiKey: String, content: String): Boolean {
return withContext(Dispatchers.IO) {
try {
val client = OkHttpClient()
val requestBody = when (apiType) {
"Flomo" -> {
val json = JSONObject()
.put("content", content)
.put("content_type", "markdown")
json.toString().toRequestBody("application/json".toMediaType())
}
"Notion" -> {
val json = JSONObject()
.put("parent", JSONObject().put("database_id", apiKey))
.put("properties", JSONObject()
.put("Name", JSONObject()
.put("title", JSONArray()
.put(JSONObject().put("text", JSONObject().put("content", "AI优化结果")))
)
)
)
json.toString().toRequestBody("application/json".toMediaType())
}
else -> {
val json = JSONObject().put("content", content)
json.toString().toRequestBody("application/json".toMediaType())
}
}
val request = Request.Builder()
.url(apiUrl)
.addHeader("Content-Type", "application/json")
.post(requestBody)
.build()
val response = client.newCall(request).execute()
response.isSuccessful
} catch (e: Exception) {
Log.e("MainActivity", "submitToNoteApi error", e)
false
}
}
}
data class NoteApiConfig(
val apiType: String,
val apiUrl: String,
val apiKey: String
)
}
// Legacy APIConfig class for backward compatibility
data class APIConfig(
val id: Long,
val name: String,
val url: String,
val key: String,
val secretKey: String,
val model: String
) )
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)
data class NoteApiConfig(val apiType: String, val apiUrl: String, val apiKey: String)
}

View File

@@ -1,31 +1,18 @@
package com.example.flomo_ai package com.example.flomo_ai
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.text.method.PasswordTransformationMethod import android.text.method.PasswordTransformationMethod
import android.util.Log import android.util.Log
import android.view.View
import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import android.widget.RadioGroup
import android.widget.RadioButton
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.example.flomo_ai.ui.theme.ThemeManager import com.example.flomo_ai.ui.theme.ThemeManager
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -34,18 +21,9 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody 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() { class SecondActivity : AppCompatActivity() {
// View references - Model 1
private lateinit var etBaseUrl1: EditText private lateinit var etBaseUrl1: EditText
private lateinit var etApiKey1: EditText private lateinit var etApiKey1: EditText
private lateinit var btnToggleApiKey1: ImageButton private lateinit var btnToggleApiKey1: ImageButton
@@ -54,7 +32,6 @@ class SecondActivity : AppCompatActivity() {
private lateinit var btnTestConnection1: Button private lateinit var btnTestConnection1: Button
private lateinit var tvTestStatus1: TextView private lateinit var tvTestStatus1: TextView
// View references - Model 2
private lateinit var etBaseUrl2: EditText private lateinit var etBaseUrl2: EditText
private lateinit var etApiKey2: EditText private lateinit var etApiKey2: EditText
private lateinit var btnToggleApiKey2: ImageButton private lateinit var btnToggleApiKey2: ImageButton
@@ -63,7 +40,6 @@ class SecondActivity : AppCompatActivity() {
private lateinit var btnTestConnection2: Button private lateinit var btnTestConnection2: Button
private lateinit var tvTestStatus2: TextView private lateinit var tvTestStatus2: TextView
// View references - Model 3
private lateinit var etBaseUrl3: EditText private lateinit var etBaseUrl3: EditText
private lateinit var etApiKey3: EditText private lateinit var etApiKey3: EditText
private lateinit var btnToggleApiKey3: ImageButton private lateinit var btnToggleApiKey3: ImageButton
@@ -72,105 +48,23 @@ class SecondActivity : AppCompatActivity() {
private lateinit var btnTestConnection3: Button private lateinit var btnTestConnection3: Button
private lateinit var tvTestStatus3: TextView private lateinit var tvTestStatus3: TextView
// Header view references
private lateinit var llHeadersList: LinearLayout
private lateinit var btnAddHeader: Button
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 layoutPromptContent: LinearLayout
private lateinit var ivPromptArrow: ImageView
private lateinit var layoutPromptToggle: LinearLayout
// Theme view references
private lateinit var rgThemeMode: RadioGroup
private lateinit var rbThemeFollowSystem: RadioButton
private lateinit var rbThemeLight: RadioButton
private lateinit var rbThemeDark: RadioButton
// Note API view references
private lateinit var spNoteApiType: Spinner
private lateinit var etNoteApiUrl: EditText
private lateinit var etNoteApiKey: EditText
private lateinit var btnToggleNoteApiKey: ImageButton
// Data storage
private var llmConfigs = mutableListOf<LLMConfig>() private var llmConfigs = mutableListOf<LLMConfig>()
private var selectedLlmIndex = 0 private var selectedLlmIndex = 0
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
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeManager.applySavedTheme(this) ThemeManager.applySavedTheme(this)
Log.d("SecondActivity", "onCreate: Starting SecondActivity")
try {
setContentView(R.layout.activity_second) setContentView(R.layout.activity_second)
Log.d("SecondActivity", "onCreate: Layout set")
} catch (e: Exception) {
Log.e("SecondActivity", "onCreate: Error setting layout", e)
throw e
}
try {
// Initialize views
initViews() initViews()
Log.d("SecondActivity", "onCreate: Views initialized")
// Load existing configurations
loadConfigurations() loadConfigurations()
Log.d("SecondActivity", "onCreate: Configurations loaded")
// Setup UI based on loaded data
setupUI()
Log.d("SecondActivity", "onCreate: UI setup completed")
// Back button functionality
findViewById<ImageButton>(R.id.btnBack).setOnClickListener { findViewById<ImageButton>(R.id.btnBack).setOnClickListener {
Log.d("SecondActivity", "Back button clicked")
finish() finish()
} }
// Home button functionality
findViewById<Button>(R.id.btnHome).setOnClickListener {
Log.d("SecondActivity", "Home button clicked")
val intent = Intent(this, MainActivity::class.java)
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() { private fun initViews() {
Log.d("SecondActivity", "initViews: Starting")
try {
// Model 1
etBaseUrl1 = findViewById(R.id.etBaseUrl1) etBaseUrl1 = findViewById(R.id.etBaseUrl1)
etApiKey1 = findViewById(R.id.etApiKey1) etApiKey1 = findViewById(R.id.etApiKey1)
btnToggleApiKey1 = findViewById(R.id.btnToggleApiKey1) btnToggleApiKey1 = findViewById(R.id.btnToggleApiKey1)
@@ -179,7 +73,6 @@ class SecondActivity : AppCompatActivity() {
btnTestConnection1 = findViewById(R.id.btnTestConnection1) btnTestConnection1 = findViewById(R.id.btnTestConnection1)
tvTestStatus1 = findViewById(R.id.tvTestStatus1) tvTestStatus1 = findViewById(R.id.tvTestStatus1)
// Model 2
etBaseUrl2 = findViewById(R.id.etBaseUrl2) etBaseUrl2 = findViewById(R.id.etBaseUrl2)
etApiKey2 = findViewById(R.id.etApiKey2) etApiKey2 = findViewById(R.id.etApiKey2)
btnToggleApiKey2 = findViewById(R.id.btnToggleApiKey2) btnToggleApiKey2 = findViewById(R.id.btnToggleApiKey2)
@@ -188,7 +81,6 @@ class SecondActivity : AppCompatActivity() {
btnTestConnection2 = findViewById(R.id.btnTestConnection2) btnTestConnection2 = findViewById(R.id.btnTestConnection2)
tvTestStatus2 = findViewById(R.id.tvTestStatus2) tvTestStatus2 = findViewById(R.id.tvTestStatus2)
// Model 3
etBaseUrl3 = findViewById(R.id.etBaseUrl3) etBaseUrl3 = findViewById(R.id.etBaseUrl3)
etApiKey3 = findViewById(R.id.etApiKey3) etApiKey3 = findViewById(R.id.etApiKey3)
btnToggleApiKey3 = findViewById(R.id.btnToggleApiKey3) btnToggleApiKey3 = findViewById(R.id.btnToggleApiKey3)
@@ -197,115 +89,22 @@ class SecondActivity : AppCompatActivity() {
btnTestConnection3 = findViewById(R.id.btnTestConnection3) btnTestConnection3 = findViewById(R.id.btnTestConnection3)
tvTestStatus3 = findViewById(R.id.tvTestStatus3) tvTestStatus3 = findViewById(R.id.tvTestStatus3)
// Header Section btnToggleApiKey1.setOnClickListener { toggleApiKeyVisibility(etApiKey1, btnToggleApiKey1) }
llHeadersList = findViewById(R.id.llHeadersList) btnToggleApiKey2.setOnClickListener { toggleApiKeyVisibility(etApiKey2, btnToggleApiKey2) }
btnAddHeader = findViewById(R.id.btnAddHeader) btnToggleApiKey3.setOnClickListener { toggleApiKeyVisibility(etApiKey3, btnToggleApiKey3) }
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)
// Theme Section
rgThemeMode = findViewById(R.id.rgThemeMode)
rbThemeFollowSystem = findViewById(R.id.rbThemeFollowSystem)
rbThemeLight = findViewById(R.id.rbThemeLight)
rbThemeDark = findViewById(R.id.rbThemeDark)
// Note API Section
spNoteApiType = findViewById(R.id.spNoteApiType)
etNoteApiUrl = findViewById(R.id.etNoteApiUrl)
etNoteApiKey = findViewById(R.id.etNoteApiKey)
btnToggleNoteApiKey = findViewById(R.id.btnToggleNoteApiKey)
Log.d("SecondActivity", "initViews: All views found")
// Setup API key toggle for Model 1
btnToggleApiKey1.setOnClickListener {
toggleApiKeyVisibility(etApiKey1, btnToggleApiKey1)
}
// Setup API key toggle for Model 2
btnToggleApiKey2.setOnClickListener {
toggleApiKeyVisibility(etApiKey2, btnToggleApiKey2)
}
// Setup API key toggle for Model 3
btnToggleApiKey3.setOnClickListener {
toggleApiKeyVisibility(etApiKey3, btnToggleApiKey3)
}
// 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
} else {
layoutHeaderContent.visibility = View.VISIBLE
ivHeaderArrow.rotation = 90f
}
}
// 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
} else {
layoutPromptContent.visibility = View.VISIBLE
ivPromptArrow.rotation = 90f
}
}
// Setup Note API key toggle
btnToggleNoteApiKey.setOnClickListener {
val isPassword = etNoteApiKey.transformationMethod is PasswordTransformationMethod
etNoteApiKey.transformationMethod = if (isPassword) null else PasswordTransformationMethod()
etNoteApiKey.setSelection(etNoteApiKey.text.length)
if (isPassword) {
btnToggleNoteApiKey.setImageResource(android.R.drawable.ic_menu_view)
} else {
btnToggleNoteApiKey.setImageResource(android.R.drawable.ic_lock_idle_lock)
}
}
// Test connection buttons
btnTestConnection1.setOnClickListener { testConnection1() } btnTestConnection1.setOnClickListener { testConnection1() }
btnTestConnection2.setOnClickListener { testConnection2() } btnTestConnection2.setOnClickListener { testConnection2() }
btnTestConnection3.setOnClickListener { testConnection3() } btnTestConnection3.setOnClickListener { testConnection3() }
Log.d("SecondActivity", "initViews: Completed")
} catch (e: Exception) {
Log.e("SecondActivity", "initViews: Error finding views", e)
throw e
}
} }
private fun loadConfigurations() { private fun loadConfigurations() {
Log.d("SecondActivity", "loadConfigurations: Starting")
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE) val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
val json = sharedPrefs.getString("configs", null) val json = sharedPrefs.getString("configs", null)
Log.d("SecondActivity", "loadConfigurations: JSON loaded: ${json?.substring(0, minOf(json.length, 100))}")
if (json != null) { if (json != null) {
try { try {
Log.d("SecondActivity", "loadConfigurations: Trying to parse as SettingsData")
val settings = Gson().fromJson(json, SettingsData::class.java) 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()
llmConfigs = settings.llmConfigs?.toMutableList() ?: mutableListOf() llmConfigs = settings.llmConfigs?.toMutableList() ?: mutableListOf()
selectedLlmIndex = settings.selectedLlmIndex ?: 0 selectedLlmIndex = settings.selectedLlmIndex ?: 0
@@ -330,47 +129,14 @@ class SecondActivity : AppCompatActivity() {
)) ))
} }
if (selectedLlmIndex >= llmConfigs.size) {
selectedLlmIndex = 0
}
loadConfigsToViews() loadConfigsToViews()
settings.noteApiConfig?.let { noteConfig ->
val apiTypes = listOf("Flomo", "Notion", "Joplin", "Custom")
val typeIndex = apiTypes.indexOf(noteConfig.apiType)
if (typeIndex >= 0) {
spNoteApiType.setSelection(typeIndex)
}
etNoteApiUrl.setText(noteConfig.apiUrl)
etNoteApiKey.setText(noteConfig.apiKey)
}
updateApiKeyVisibilityForAll() updateApiKeyVisibilityForAll()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("SecondActivity", "loadConfigurations: Error parsing SettingsData", e)
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()) {
val oldConfig = oldConfigs[0]
etBaseUrl1.setText(oldConfig.url)
etApiKey1.setText(oldConfig.key)
etModel1.setText(oldConfig.model)
updateApiKeyVisibilityForAll()
}
} catch (e2: Exception) {
Log.e("SecondActivity", "loadConfigurations: Error parsing List<APIConfig>", e2)
setDefaultConfigs() setDefaultConfigs()
} }
}
} else { } else {
Log.d("SecondActivity", "loadConfigurations: No saved config, using defaults")
setDefaultConfigs() setDefaultConfigs()
} }
Log.d("SecondActivity", "loadConfigurations: Completed")
} }
private fun loadConfigsToViews() { private fun loadConfigsToViews() {
@@ -427,61 +193,6 @@ class SecondActivity : AppCompatActivity() {
editText.setSelection(editText.text.length) editText.setSelection(editText.text.length)
} }
private fun setupUI() {
// Setup headers
llHeadersList.removeAllViews()
if (headerConfigs.isEmpty()) {
addHeaderEntry()
} else {
for (header in headerConfigs) {
addHeaderEntry(header.key, header.value)
}
}
// Setup prompts
llPromptList.removeAllViews()
if (promptConfigs.isEmpty()) {
addPromptEntry()
} else {
for (prompt in promptConfigs) {
addPromptEntry(prompt.title, prompt.content)
}
}
// Setup theme
setupTheme()
// Setup Note API spinner
val noteApiTypes = listOf("Flomo", "Notion", "Joplin", "Custom")
val noteApiAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, noteApiTypes)
noteApiAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spNoteApiType.adapter = noteApiAdapter
}
private fun setupTheme() {
val themeMode = ThemeManager.getThemeMode(this)
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
}
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
}
ThemeManager.setThemeMode(this, newMode)
Log.d("SecondActivity", "Theme mode changed to: ${ThemeManager.getThemeModeName(newMode)}")
recreate()
}
}
private fun toggleApiKeyVisibility(editText: EditText, button: ImageButton) { private fun toggleApiKeyVisibility(editText: EditText, button: ImageButton) {
val isPassword = editText.transformationMethod is PasswordTransformationMethod val isPassword = editText.transformationMethod is PasswordTransformationMethod
editText.transformationMethod = if (isPassword) null else PasswordTransformationMethod() editText.transformationMethod = if (isPassword) null else PasswordTransformationMethod()
@@ -595,46 +306,12 @@ class SecondActivity : AppCompatActivity() {
) )
} }
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)
}
llHeadersList.addView(view)
}
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 btnRemove = view.findViewById<ImageButton>(R.id.btnRemovePrompt)
etTitle.setText(title)
etContent.setText(content)
btnRemove.setOnClickListener {
llPromptList.removeView(view)
}
llPromptList.addView(view)
}
// Save all configurations when leaving or explicitly saving
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
saveConfigurations() saveConfigurations()
} }
private fun saveConfigurations() { private fun saveConfigurations() {
// Update LLM configs from views
llmConfigs.clear() llmConfigs.clear()
llmConfigs.add(LLMConfig( llmConfigs.add(LLMConfig(
name = etModelName1.text.toString().ifEmpty { "默认配置" }, name = etModelName1.text.toString().ifEmpty { "默认配置" },
@@ -655,68 +332,20 @@ class SecondActivity : AppCompatActivity() {
model = etModel3.text.toString() model = etModel3.text.toString()
)) ))
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))
}
}
promptConfigs.clear()
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(id = "prompt_$i", title = title, content = content))
}
}
buttonConfigs.clear()
val noteApiConfig = NoteApiConfig(
apiType = spNoteApiType.selectedItem.toString(),
apiUrl = etNoteApiUrl.text.toString(),
apiKey = etNoteApiKey.text.toString()
)
val settingsData = SettingsData( val settingsData = SettingsData(
llmConfigs = llmConfigs, llmConfigs = llmConfigs,
selectedLlmIndex = selectedLlmIndex, selectedLlmIndex = selectedLlmIndex,
headerConfigs = headerConfigs, headerConfigs = null,
promptConfigs = promptConfigs, promptConfigs = null,
buttonConfigs = buttonConfigs, buttonConfigs = null,
noteApiConfig = noteApiConfig noteApiConfig = null
) )
val json = Gson().toJson(settingsData) val json = Gson().toJson(settingsData)
val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE) val sharedPrefs = getSharedPreferences("APIConfigs", Context.MODE_PRIVATE)
sharedPrefs.edit().putString("configs", json).apply() sharedPrefs.edit().putString("configs", json).apply()
apiConfig = APIConfig(
System.currentTimeMillis(),
"llm-config",
etBaseUrl1.text.toString(),
etApiKey1.text.toString(),
"",
etModel1.text.toString()
)
} }
// 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( data class LLMConfig(
val name: String, val name: String,
val baseUrl: String, val baseUrl: String,
@@ -725,14 +354,8 @@ class SecondActivity : AppCompatActivity() {
val enabled: Boolean = true val enabled: Boolean = true
) )
data class NoteApiConfig(
val apiType: String,
val apiUrl: String,
val apiKey: String
)
data class SettingsData( data class SettingsData(
val llmConfigs: List<LLMConfig>, val llmConfigs: List<LLMConfig>?,
val selectedLlmIndex: Int?, val selectedLlmIndex: Int?,
val headerConfigs: List<HeaderConfig>?, val headerConfigs: List<HeaderConfig>?,
val promptConfigs: List<PromptConfig>?, val promptConfigs: List<PromptConfig>?,
@@ -740,4 +363,9 @@ class SecondActivity : AppCompatActivity() {
val noteApiConfig: NoteApiConfig?, val noteApiConfig: NoteApiConfig?,
val llmConfig: LLMConfig? = null val llmConfig: LLMConfig? = null
) )
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)
data class NoteApiConfig(val apiType: String, val apiUrl: String, val apiKey: String)
} }