Compare commits
3 Commits
262920b8b3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac1baa9b0f | ||
|
|
a753a3369c | ||
|
|
bfd34c8919 |
@@ -22,3 +22,6 @@ lto = true
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
|
[target.x86_64-pc-windows-gnu]
|
||||||
|
rustflags = ["-C", "link-args=-Wl,--subsystem,windows"]
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pub struct FlomoAiApp {
|
|||||||
|
|
||||||
model_displays: Vec<ModelDisplay>,
|
model_displays: Vec<ModelDisplay>,
|
||||||
pending_results: Option<std::thread::JoinHandle<Vec<ModelResult>>>,
|
pending_results: Option<std::thread::JoinHandle<Vec<ModelResult>>>,
|
||||||
|
pending_tests: Vec<(usize, std::thread::JoinHandle<Result<String, String>>)>,
|
||||||
|
|
||||||
settings_selected_theme: ThemeMode,
|
settings_selected_theme: ThemeMode,
|
||||||
new_prompt_title: String,
|
new_prompt_title: String,
|
||||||
@@ -65,6 +66,7 @@ impl FlomoAiApp {
|
|||||||
char_count: 0,
|
char_count: 0,
|
||||||
model_displays,
|
model_displays,
|
||||||
pending_results: None,
|
pending_results: None,
|
||||||
|
pending_tests: Vec::new(),
|
||||||
settings_selected_theme: ThemeMode::Light,
|
settings_selected_theme: ThemeMode::Light,
|
||||||
new_prompt_title: String::new(),
|
new_prompt_title: String::new(),
|
||||||
new_prompt_content: String::new(),
|
new_prompt_content: String::new(),
|
||||||
@@ -73,6 +75,28 @@ impl FlomoAiApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn poll_test_results(&mut self, ctx: &egui::Context) {
|
||||||
|
for (index, handle) in self.pending_tests.drain(..) {
|
||||||
|
match handle.join() {
|
||||||
|
Ok(Ok(response)) => {
|
||||||
|
let msg = if response.len() > 30 {
|
||||||
|
format!("成功: {}...", &response[..30])
|
||||||
|
} else {
|
||||||
|
format!("成功: {}", response)
|
||||||
|
};
|
||||||
|
self.model_displays[index].test_status = msg;
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
self.model_displays[index].test_status = format!("失败: {}", e);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
self.model_displays[index].test_status = "测试线程错误".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn poll_results(&mut self, ctx: &egui::Context) {
|
fn poll_results(&mut self, ctx: &egui::Context) {
|
||||||
if let Some(handle) = self.pending_results.take() {
|
if let Some(handle) = self.pending_results.take() {
|
||||||
if handle.is_finished() {
|
if handle.is_finished() {
|
||||||
@@ -184,6 +208,7 @@ impl FlomoAiApp {
|
|||||||
impl eframe::App for FlomoAiApp {
|
impl eframe::App for FlomoAiApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
self.poll_results(ctx);
|
self.poll_results(ctx);
|
||||||
|
self.poll_test_results(ctx);
|
||||||
|
|
||||||
if self.theme_dirty {
|
if self.theme_dirty {
|
||||||
let theme = AppTheme::from_mode(self.settings.theme_config.mode);
|
let theme = AppTheme::from_mode(self.settings.theme_config.mode);
|
||||||
@@ -287,51 +312,49 @@ impl FlomoAiApp {
|
|||||||
ui.label(egui::RichText::new("各模型返回结果").size(11.0).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new("各模型返回结果").size(11.0).color(egui::Color32::GRAY));
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
let available_width = ui.available_width();
|
let total_width = ui.available_width();
|
||||||
let column_width = (available_width - 16.0) / 3.0;
|
let spacing = 8.0;
|
||||||
|
let column_width = (total_width - spacing * 2.0) / 3.0;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.set_height(200.0);
|
ui.set_width(total_width);
|
||||||
for (i, display) in self.model_displays.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
ui.add_space(8.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.set_min_width(column_width);
|
|
||||||
|
|
||||||
|
for d in self.model_displays.iter() {
|
||||||
egui::Frame::none()
|
egui::Frame::none()
|
||||||
.fill(ui.style().visuals.widgets.inactive.bg_fill)
|
.fill(if d.enabled {
|
||||||
|
ui.style().visuals.widgets.inactive.bg_fill
|
||||||
|
} else {
|
||||||
|
egui::Color32::from_rgb(235, 235, 240)
|
||||||
|
})
|
||||||
.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(180, 180, 200)))
|
.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(180, 180, 200)))
|
||||||
.rounding(8.0)
|
.rounding(8.0)
|
||||||
.inner_margin(egui::Margin::same(10.0))
|
.inner_margin(egui::Margin::same(8.0))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.set_min_width(column_width - 20.0);
|
ui.set_min_width(column_width - 16.0);
|
||||||
|
|
||||||
let enabled_color = if display.enabled {
|
let enabled_color = if d.enabled {
|
||||||
egui::Color32::from_rgb(80, 80, 220)
|
egui::Color32::from_rgb(80, 80, 220)
|
||||||
} else {
|
} else {
|
||||||
egui::Color32::GRAY
|
egui::Color32::GRAY
|
||||||
};
|
};
|
||||||
|
|
||||||
let enabled_dot = if display.enabled { "●" } else { "○" };
|
let enabled_dot = if d.enabled { "●" } else { "○" };
|
||||||
ui.label(egui::RichText::new(format!("{} {}", enabled_dot, display.name))
|
ui.label(egui::RichText::new(format!("{} {}", enabled_dot, d.name))
|
||||||
.size(13.0).strong().color(enabled_color));
|
.size(13.0).strong().color(enabled_color));
|
||||||
|
|
||||||
ui.label(egui::RichText::new(&display.model).size(10.0).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new(&d.model).size(10.0).color(egui::Color32::GRAY));
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(4.0);
|
||||||
ui.add_sized([ui.available_width(), 1.0], egui::Separator::default());
|
ui.add_sized([ui.available_width(), 1.0], egui::Separator::default());
|
||||||
|
|
||||||
ui.add_space(6.0);
|
let status_color = match &d.status {
|
||||||
|
|
||||||
let status_color = match &display.status {
|
|
||||||
ModelStatus::Waiting => egui::Color32::GRAY,
|
ModelStatus::Waiting => egui::Color32::GRAY,
|
||||||
ModelStatus::Loading => egui::Color32::from_rgb(255, 140, 0),
|
ModelStatus::Loading => egui::Color32::from_rgb(255, 140, 0),
|
||||||
ModelStatus::Completed => egui::Color32::from_rgb(0, 160, 0),
|
ModelStatus::Completed => egui::Color32::from_rgb(0, 160, 0),
|
||||||
ModelStatus::Error(_) => egui::Color32::RED,
|
ModelStatus::Error(_) => egui::Color32::RED,
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.label(egui::RichText::new(match &display.status {
|
ui.label(egui::RichText::new(match &d.status {
|
||||||
ModelStatus::Waiting => "● 就绪",
|
ModelStatus::Waiting => "● 就绪",
|
||||||
ModelStatus::Loading => "◐ 生成中...",
|
ModelStatus::Loading => "◐ 生成中...",
|
||||||
ModelStatus::Completed => "✓ 完成",
|
ModelStatus::Completed => "✓ 完成",
|
||||||
@@ -340,26 +363,26 @@ impl FlomoAiApp {
|
|||||||
|
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
if display.result.is_empty() {
|
if d.result.is_empty() {
|
||||||
ui.label(egui::RichText::new("等待结果...").size(11.0).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new("等待结果...").size(11.0).color(egui::Color32::GRAY));
|
||||||
} else {
|
} else {
|
||||||
let mut result_text = display.result.clone();
|
let mut result_text = d.result.clone();
|
||||||
ui.add_sized(
|
ui.add_sized(
|
||||||
[ui.available_width(), 120.0],
|
[ui.available_width(), 100.0],
|
||||||
egui::TextEdit::multiline(&mut result_text)
|
egui::TextEdit::multiline(&mut result_text)
|
||||||
.desired_rows(5)
|
.desired_rows(4)
|
||||||
.frame(false),
|
.frame(false),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
if ui.small_button("复制").clicked() && !d.result.is_empty() {
|
||||||
if ui.small_button("复制").clicked() && !display.result.is_empty() {
|
self.copy_to_clipboard(&d.result);
|
||||||
self.copy_to_clipboard(&display.result);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
ui.add_space(spacing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -380,20 +403,20 @@ impl FlomoAiApp {
|
|||||||
|
|
||||||
ScrollArea::vertical().show(ui, |ui| {
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
for i in 0..self.model_displays.len() {
|
for i in 0..self.model_displays.len() {
|
||||||
let enabled = self.model_displays[i].enabled;
|
|
||||||
let name = self.model_displays[i].name.clone();
|
|
||||||
let model = self.model_displays[i].model.clone();
|
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
self.model_displays[i].enabled ^= ui.checkbox(&mut self.model_displays[i].enabled, "").changed();
|
ui.checkbox(&mut self.model_displays[i].enabled, "");
|
||||||
ui.label(egui::RichText::new(&format!("模型 {} 配置", i + 1)).size(14.0).strong());
|
let label = if self.model_displays[i].enabled {
|
||||||
|
format!("{} ({})", self.model_displays[i].name, self.model_displays[i].model)
|
||||||
|
} else {
|
||||||
|
format!("{} (已禁用)", self.model_displays[i].name)
|
||||||
|
};
|
||||||
|
ui.label(egui::RichText::new(&label).size(14.0).strong());
|
||||||
});
|
});
|
||||||
|
|
||||||
if !enabled {
|
ui.add_space(4.0);
|
||||||
ui.label(egui::RichText::new("已禁用").size(11.0).color(egui::Color32::GRAY));
|
|
||||||
} else {
|
|
||||||
ui.label("名称");
|
ui.label("名称");
|
||||||
ui.text_edit_singleline(&mut self.model_displays[i].name);
|
ui.text_edit_singleline(&mut self.model_displays[i].name);
|
||||||
|
|
||||||
@@ -423,6 +446,9 @@ impl FlomoAiApp {
|
|||||||
|
|
||||||
ui.label("Model");
|
ui.label("Model");
|
||||||
ui.text_edit_singleline(&mut self.model_displays[i].model);
|
ui.text_edit_singleline(&mut self.model_displays[i].model);
|
||||||
|
if i < self.settings.llm_configs.models.len() {
|
||||||
|
self.settings.llm_configs.models[i].model = self.model_displays[i].model.clone();
|
||||||
|
}
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
@@ -432,22 +458,21 @@ impl FlomoAiApp {
|
|||||||
.rounding(4.0)
|
.rounding(4.0)
|
||||||
.min_size(egui::vec2(60.0, 28.0));
|
.min_size(egui::vec2(60.0, 28.0));
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
if ui.add(test_btn).clicked() {
|
if ui.add(test_btn).clicked() {
|
||||||
if let Some(config) = self.settings.llm_configs.models.get(i).cloned() {
|
if let Some(config) = self.settings.llm_configs.models.get(i).cloned() {
|
||||||
self.model_displays[i].test_status = "测试中...".to_string();
|
self.model_displays[i].test_status = "测试中...".to_string();
|
||||||
let headers = self.settings.header_configs.clone();
|
let headers = self.settings.header_configs.clone();
|
||||||
let ctx_clone = ctx.clone();
|
|
||||||
let display_index = i;
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
let handle = std::thread::spawn(move || {
|
||||||
let result = test_single_llm(&config, &headers);
|
test_single_llm(&config, &headers)
|
||||||
ctx_clone.request_repaint();
|
|
||||||
});
|
});
|
||||||
|
self.pending_tests.push((i, handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.label(egui::RichText::new(&test_status).size(11.0).color(egui::Color32::GRAY));
|
ui.label(egui::RichText::new(&test_status).size(11.0).color(egui::Color32::GRAY));
|
||||||
}
|
});
|
||||||
|
|
||||||
if i < self.model_displays.len() - 1 {
|
if i < self.model_displays.len() - 1 {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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
|
||||||
@@ -31,13 +32,19 @@ import org.json.JSONObject
|
|||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var inputEditText: EditText
|
private lateinit var inputEditText: EditText
|
||||||
private lateinit var outputTextView: EditText
|
private lateinit var tvResult1: TextView
|
||||||
|
private lateinit var tvResult2: TextView
|
||||||
|
private lateinit var tvResult3: TextView
|
||||||
|
private lateinit var tvStatus1: TextView
|
||||||
|
private lateinit var tvStatus2: TextView
|
||||||
|
private lateinit var tvStatus3: TextView
|
||||||
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 var llmConfigs = listOf<LLMConfig>()
|
private var llmConfigs = listOf<LLMConfig>()
|
||||||
private var selectedLlmIndex = 0
|
private var selectedLlmIndex = 0
|
||||||
|
private val results = mutableMapOf<Int, String>()
|
||||||
|
|
||||||
@SuppressLint("MissingInflatedId", "CutPasteId", "SetTextI18n")
|
@SuppressLint("MissingInflatedId", "CutPasteId", "SetTextI18n")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -51,6 +58,30 @@ class MainActivity : AppCompatActivity() {
|
|||||||
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 btnCopyResult = findViewById<Button>(R.id.btnCopyResult)
|
val btnCopyResult = findViewById<Button>(R.id.btnCopyResult)
|
||||||
|
val configButton = findViewById<Button>(R.id.configButton)
|
||||||
|
val modelSelector = findViewById<Spinner>(R.id.headerModelSelector)
|
||||||
|
|
||||||
|
// 初始化三栏结果视图
|
||||||
|
tvResult1 = findViewById(R.id.tvResult1)
|
||||||
|
tvResult2 = findViewById(R.id.tvResult2)
|
||||||
|
tvResult3 = findViewById(R.id.tvResult3)
|
||||||
|
tvStatus1 = findViewById(R.id.tvStatus1)
|
||||||
|
tvStatus2 = findViewById(R.id.tvStatus2)
|
||||||
|
tvStatus3 = findViewById(R.id.tvStatus3)
|
||||||
|
|
||||||
|
// 配置按钮点击事件
|
||||||
|
configButton.setOnClickListener {
|
||||||
|
val intent = Intent(this, SecondActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模型选择器
|
||||||
|
modelSelector.setOnItemSelectedListener(object : android.widget.AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(parent: android.widget.AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||||
|
selectedLlmIndex = position
|
||||||
|
}
|
||||||
|
override fun onNothingSelected(parent: android.widget.AdapterView<*>) {}
|
||||||
|
})
|
||||||
|
|
||||||
loadModelsFromConfig()
|
loadModelsFromConfig()
|
||||||
loadPromptsFromConfig()
|
loadPromptsFromConfig()
|
||||||
@@ -104,8 +135,13 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
btnCopyResult.setOnClickListener {
|
btnCopyResult.setOnClickListener {
|
||||||
val textToCopy = outputTextView.text.toString()
|
val sb = StringBuilder()
|
||||||
if (textToCopy.isNotEmpty() && textToCopy != "发送消息后结果将在此显示") {
|
if (results.containsKey(0)) sb.append("模型1:\n${results[0]}\n\n")
|
||||||
|
if (results.containsKey(1)) sb.append("模型2:\n${results[1]}\n\n")
|
||||||
|
if (results.containsKey(2)) sb.append("模型3:\n${results[2]}\n\n")
|
||||||
|
|
||||||
|
val textToCopy = sb.toString().trim()
|
||||||
|
if (textToCopy.isNotEmpty()) {
|
||||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText("优化结果", textToCopy)
|
val clip = ClipData.newPlainText("优化结果", textToCopy)
|
||||||
clipboard.setPrimaryClip(clip)
|
clipboard.setPrimaryClip(clip)
|
||||||
@@ -118,6 +154,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
initQuickButtons()
|
initQuickButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
loadModelsFromConfig()
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadModelsFromConfig() {
|
private fun loadModelsFromConfig() {
|
||||||
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)
|
||||||
@@ -131,6 +172,19 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Log.e("MainActivity", "Error loading config", e)
|
Log.e("MainActivity", "Error loading config", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新模型选择器
|
||||||
|
val modelSelector = findViewById<Spinner>(R.id.headerModelSelector)
|
||||||
|
val modelNames = llmConfigs.filter { it.enabled }.map { it.name }.toMutableList()
|
||||||
|
if (modelNames.isEmpty()) {
|
||||||
|
modelNames.add("未配置模型")
|
||||||
|
}
|
||||||
|
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, modelNames)
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
modelSelector.adapter = adapter
|
||||||
|
if (selectedLlmIndex < modelNames.size) {
|
||||||
|
modelSelector.setSelection(selectedLlmIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLlmConfigsAndSend(content: String) {
|
private fun loadLlmConfigsAndSend(content: String) {
|
||||||
@@ -143,22 +197,49 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
if (configs.isEmpty()) 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 enabledConfigs = configs.filter { it.enabled && it.baseUrl.isNotEmpty() && it.model.isNotEmpty() }
|
||||||
val apiKey = config.apiKey
|
|
||||||
val model = config.model.ifEmpty { "gpt-4o" }
|
|
||||||
|
|
||||||
sendToLlm(baseUrl, apiKey, model, content)
|
if (enabledConfigs.isEmpty()) {
|
||||||
|
Toast.makeText(this, "没有启用的大模型配置", Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化所有结果显示
|
||||||
|
results.clear()
|
||||||
|
tvStatus1.text = "等待..."
|
||||||
|
tvStatus2.text = "等待..."
|
||||||
|
tvStatus3.text = "等待..."
|
||||||
|
tvResult1.text = "等待结果..."
|
||||||
|
tvResult2.text = "等待结果..."
|
||||||
|
tvResult3.text = "等待结果..."
|
||||||
|
|
||||||
|
// 为每个启用的模型启动请求
|
||||||
|
enabledConfigs.forEachIndexed { index, config ->
|
||||||
|
sendToLlm(config.baseUrl, config.apiKey, config.model, content, index, enabledConfigs.size)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("MainActivity", "Error loading LLM config", e)
|
Log.e("MainActivity", "Error loading LLM config", e)
|
||||||
outputTextView.setText("错误: ${e.message}")
|
Toast.makeText(this, "配置加载错误: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendToLlm(baseUrl: String, apiKey: String, model: String, content: String) {
|
private fun sendToLlm(baseUrl: String, apiKey: String, model: String, content: String, index: Int, total: Int) {
|
||||||
val outputStatusLabel = findViewById<TextView>(R.id.outputStatusLabel)
|
val statusView = when (index) {
|
||||||
outputStatusLabel.text = "连接中..."
|
0 -> tvStatus1
|
||||||
outputTextView.setText("正在生成...")
|
1 -> tvStatus2
|
||||||
|
2 -> tvStatus3
|
||||||
|
else -> tvStatus1
|
||||||
|
}
|
||||||
|
val resultView = when (index) {
|
||||||
|
0 -> tvResult1
|
||||||
|
1 -> tvResult2
|
||||||
|
2 -> tvResult3
|
||||||
|
else -> tvResult1
|
||||||
|
}
|
||||||
|
|
||||||
|
statusView.text = "生成中..."
|
||||||
|
resultView.text = "正在生成..."
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
@@ -198,21 +279,22 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (choices.length() > 0) {
|
if (choices.length() > 0) {
|
||||||
val message = choices.getJSONObject(0).getJSONObject("message")
|
val message = choices.getJSONObject(0).getJSONObject("message")
|
||||||
val result = message.getString("content")
|
val result = message.getString("content")
|
||||||
outputStatusLabel.text = "已完成"
|
statusView.text = "完成"
|
||||||
outputTextView.setText(result)
|
resultView.text = result
|
||||||
|
results[index] = result
|
||||||
} else {
|
} else {
|
||||||
outputStatusLabel.text = "发生错误"
|
statusView.text = "错误"
|
||||||
outputTextView.setText("API 返回空结果")
|
resultView.text = "API 返回空结果"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outputStatusLabel.text = "发生错误"
|
statusView.text = "错误"
|
||||||
outputTextView.setText("API 错误: ${response.code} ${response.message}")
|
resultView.text = "API 错误: ${response.code} ${response.message}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
outputStatusLabel.text = "发生错误"
|
statusView.text = "错误"
|
||||||
outputTextView.setText("错误: ${e.message}")
|
resultView.text = "错误: ${e.message}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,22 @@ package com.example.flomo_ai
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.InputType
|
||||||
import android.text.method.PasswordTransformationMethod
|
import android.text.method.PasswordTransformationMethod
|
||||||
import android.util.Log
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.CheckBox
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import android.widget.RadioGroup
|
||||||
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
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
|
||||||
@@ -24,31 +33,42 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||||||
|
|
||||||
class SecondActivity : AppCompatActivity() {
|
class SecondActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var cbModel1Enabled: CheckBox
|
||||||
|
private lateinit var etModelName1: EditText
|
||||||
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
|
||||||
private lateinit var etModel1: EditText
|
private lateinit var etModel1: EditText
|
||||||
private lateinit var etModelName1: EditText
|
|
||||||
private lateinit var btnTestConnection1: Button
|
private lateinit var btnTestConnection1: Button
|
||||||
private lateinit var tvTestStatus1: TextView
|
private lateinit var tvTestStatus1: TextView
|
||||||
|
|
||||||
|
private lateinit var cbModel2Enabled: CheckBox
|
||||||
|
private lateinit var etModelName2: EditText
|
||||||
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
|
||||||
private lateinit var etModel2: EditText
|
private lateinit var etModel2: EditText
|
||||||
private lateinit var etModelName2: EditText
|
|
||||||
private lateinit var btnTestConnection2: Button
|
private lateinit var btnTestConnection2: Button
|
||||||
private lateinit var tvTestStatus2: TextView
|
private lateinit var tvTestStatus2: TextView
|
||||||
|
|
||||||
|
private lateinit var cbModel3Enabled: CheckBox
|
||||||
|
private lateinit var etModelName3: EditText
|
||||||
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
|
||||||
private lateinit var etModel3: EditText
|
private lateinit var etModel3: EditText
|
||||||
private lateinit var etModelName3: EditText
|
|
||||||
private lateinit var btnTestConnection3: Button
|
private lateinit var btnTestConnection3: Button
|
||||||
private lateinit var tvTestStatus3: TextView
|
private lateinit var tvTestStatus3: TextView
|
||||||
|
|
||||||
|
private lateinit var promptContainer: LinearLayout
|
||||||
|
private lateinit var btnAddPrompt: Button
|
||||||
|
private lateinit var rgTheme: RadioGroup
|
||||||
|
private lateinit var rbThemeLight: RadioButton
|
||||||
|
private lateinit var rbThemeDark: RadioButton
|
||||||
|
private lateinit var rbThemeAuto: RadioButton
|
||||||
|
|
||||||
private var llmConfigs = mutableListOf<LLMConfig>()
|
private var llmConfigs = mutableListOf<LLMConfig>()
|
||||||
|
private var promptConfigs = mutableListOf<PromptConfig>()
|
||||||
private var selectedLlmIndex = 0
|
private var selectedLlmIndex = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -58,6 +78,7 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
initViews()
|
initViews()
|
||||||
loadConfigurations()
|
loadConfigurations()
|
||||||
|
updatePromptList()
|
||||||
|
|
||||||
findViewById<ImageButton>(R.id.btnBack).setOnClickListener {
|
findViewById<ImageButton>(R.id.btnBack).setOnClickListener {
|
||||||
finish()
|
finish()
|
||||||
@@ -65,30 +86,59 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initViews() {
|
private fun initViews() {
|
||||||
|
cbModel1Enabled = findViewById(R.id.cbModel1Enabled)
|
||||||
|
etModelName1 = findViewById(R.id.etModelName1)
|
||||||
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)
|
||||||
etModel1 = findViewById(R.id.etModel1)
|
etModel1 = findViewById(R.id.etModel1)
|
||||||
etModelName1 = findViewById(R.id.etModelName1)
|
|
||||||
btnTestConnection1 = findViewById(R.id.btnTestConnection1)
|
btnTestConnection1 = findViewById(R.id.btnTestConnection1)
|
||||||
tvTestStatus1 = findViewById(R.id.tvTestStatus1)
|
tvTestStatus1 = findViewById(R.id.tvTestStatus1)
|
||||||
|
|
||||||
|
cbModel2Enabled = findViewById(R.id.cbModel2Enabled)
|
||||||
|
etModelName2 = findViewById(R.id.etModelName2)
|
||||||
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)
|
||||||
etModel2 = findViewById(R.id.etModel2)
|
etModel2 = findViewById(R.id.etModel2)
|
||||||
etModelName2 = findViewById(R.id.etModelName2)
|
|
||||||
btnTestConnection2 = findViewById(R.id.btnTestConnection2)
|
btnTestConnection2 = findViewById(R.id.btnTestConnection2)
|
||||||
tvTestStatus2 = findViewById(R.id.tvTestStatus2)
|
tvTestStatus2 = findViewById(R.id.tvTestStatus2)
|
||||||
|
|
||||||
|
cbModel3Enabled = findViewById(R.id.cbModel3Enabled)
|
||||||
|
etModelName3 = findViewById(R.id.etModelName3)
|
||||||
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)
|
||||||
etModel3 = findViewById(R.id.etModel3)
|
etModel3 = findViewById(R.id.etModel3)
|
||||||
etModelName3 = findViewById(R.id.etModelName3)
|
|
||||||
btnTestConnection3 = findViewById(R.id.btnTestConnection3)
|
btnTestConnection3 = findViewById(R.id.btnTestConnection3)
|
||||||
tvTestStatus3 = findViewById(R.id.tvTestStatus3)
|
tvTestStatus3 = findViewById(R.id.tvTestStatus3)
|
||||||
|
|
||||||
|
promptContainer = findViewById(R.id.promptContainer)
|
||||||
|
btnAddPrompt = findViewById(R.id.btnAddPrompt)
|
||||||
|
rgTheme = findViewById(R.id.rgTheme)
|
||||||
|
rbThemeLight = findViewById(R.id.rbThemeLight)
|
||||||
|
rbThemeDark = findViewById(R.id.rbThemeDark)
|
||||||
|
rbThemeAuto = findViewById(R.id.rbThemeAuto)
|
||||||
|
|
||||||
|
// 设置当前主题
|
||||||
|
when (ThemeManager.getThemeMode(this)) {
|
||||||
|
ThemeManager.THEME_LIGHT -> rbThemeLight.isChecked = true
|
||||||
|
ThemeManager.THEME_DARK -> rbThemeDark.isChecked = true
|
||||||
|
else -> rbThemeAuto.isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主题切换监听
|
||||||
|
rgTheme.setOnCheckedChangeListener { _, checkedId ->
|
||||||
|
val mode = when (checkedId) {
|
||||||
|
R.id.rbThemeLight -> ThemeManager.THEME_LIGHT
|
||||||
|
R.id.rbThemeDark -> ThemeManager.THEME_DARK
|
||||||
|
else -> ThemeManager.THEME_FOLLOW_SYSTEM
|
||||||
|
}
|
||||||
|
ThemeManager.setThemeMode(this, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
btnAddPrompt.setOnClickListener { showAddPromptDialog() }
|
||||||
|
|
||||||
btnToggleApiKey1.setOnClickListener { toggleApiKeyVisibility(etApiKey1, btnToggleApiKey1) }
|
btnToggleApiKey1.setOnClickListener { toggleApiKeyVisibility(etApiKey1, btnToggleApiKey1) }
|
||||||
btnToggleApiKey2.setOnClickListener { toggleApiKeyVisibility(etApiKey2, btnToggleApiKey2) }
|
btnToggleApiKey2.setOnClickListener { toggleApiKeyVisibility(etApiKey2, btnToggleApiKey2) }
|
||||||
btnToggleApiKey3.setOnClickListener { toggleApiKeyVisibility(etApiKey3, btnToggleApiKey3) }
|
btnToggleApiKey3.setOnClickListener { toggleApiKeyVisibility(etApiKey3, btnToggleApiKey3) }
|
||||||
@@ -106,31 +156,40 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
val settings = Gson().fromJson(json, SettingsData::class.java)
|
val settings = Gson().fromJson(json, SettingsData::class.java)
|
||||||
llmConfigs = settings.llmConfigs?.toMutableList() ?: mutableListOf()
|
llmConfigs = settings.llmConfigs?.toMutableList() ?: mutableListOf()
|
||||||
|
promptConfigs = settings.promptConfigs?.toMutableList() ?: mutableListOf()
|
||||||
selectedLlmIndex = settings.selectedLlmIndex ?: 0
|
selectedLlmIndex = settings.selectedLlmIndex ?: 0
|
||||||
|
|
||||||
if (llmConfigs.isEmpty()) {
|
if (llmConfigs.isEmpty()) {
|
||||||
val legacyBaseUrl = settings.llmConfig?.baseUrl ?: "https://api.openai.com/v1"
|
|
||||||
val legacyApiKey = settings.llmConfig?.apiKey ?: ""
|
|
||||||
val legacyModel = settings.llmConfig?.model ?: "gpt-4o"
|
|
||||||
llmConfigs.add(LLMConfig(
|
llmConfigs.add(LLMConfig(
|
||||||
name = "默认配置",
|
name = "模型1",
|
||||||
baseUrl = legacyBaseUrl,
|
baseUrl = "https://api.openai.com/v1",
|
||||||
apiKey = legacyApiKey,
|
apiKey = "",
|
||||||
model = legacyModel
|
model = "gpt-4o",
|
||||||
|
enabled = true
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
while (llmConfigs.size < 3) {
|
while (llmConfigs.size < 3) {
|
||||||
llmConfigs.add(LLMConfig(
|
llmConfigs.add(LLMConfig(
|
||||||
name = "配置 ${llmConfigs.size + 1}",
|
name = "模型${llmConfigs.size + 1}",
|
||||||
baseUrl = "https://api.openai.com/v1",
|
baseUrl = "https://api.openai.com/v1",
|
||||||
apiKey = "",
|
apiKey = "",
|
||||||
model = "gpt-4o"
|
model = "gpt-4o",
|
||||||
|
enabled = false
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载默认提示词
|
||||||
|
if (promptConfigs.isEmpty()) {
|
||||||
|
promptConfigs.add(PromptConfig("default-1", "翻译助手", "将输入的文本翻译成指定语言"))
|
||||||
|
promptConfigs.add(PromptConfig("default-2", "代码解释", "解释代码的功能和逻辑"))
|
||||||
|
promptConfigs.add(PromptConfig("quick-1", "检查错别字", "请检查以下文本中的错别字并纠正:"))
|
||||||
|
promptConfigs.add(PromptConfig("quick-2", "总结", "请用简洁的语言总结以下文本的主要内容:"))
|
||||||
|
promptConfigs.add(PromptConfig("quick-3", "翻译", "请翻译以下文本:"))
|
||||||
|
promptConfigs.add(PromptConfig("quick-4", "润色", "请润色以下文本,使其更通顺流畅:"))
|
||||||
|
}
|
||||||
|
|
||||||
loadConfigsToViews()
|
loadConfigsToViews()
|
||||||
updateApiKeyVisibilityForAll()
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
setDefaultConfigs()
|
setDefaultConfigs()
|
||||||
}
|
}
|
||||||
@@ -139,58 +198,97 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updatePromptList() {
|
||||||
|
promptContainer.removeAllViews()
|
||||||
|
for (i in promptConfigs.indices) {
|
||||||
|
val prompt = promptConfigs[i]
|
||||||
|
val itemView = LayoutInflater.from(this).inflate(R.layout.item_prompt_config, promptContainer, false)
|
||||||
|
|
||||||
|
val tvTitle = itemView.findViewById<TextView>(R.id.tvPromptTitle)
|
||||||
|
val tvContent = itemView.findViewById<TextView>(R.id.tvPromptContent)
|
||||||
|
val btnDelete = itemView.findViewById<Button>(R.id.btnDeletePrompt)
|
||||||
|
|
||||||
|
tvTitle.text = prompt.title
|
||||||
|
tvContent.text = prompt.content
|
||||||
|
|
||||||
|
val index = i
|
||||||
|
btnDelete.setOnClickListener {
|
||||||
|
promptConfigs.removeAt(index)
|
||||||
|
updatePromptList()
|
||||||
|
}
|
||||||
|
|
||||||
|
promptContainer.addView(itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showAddPromptDialog() {
|
||||||
|
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_add_prompt, null)
|
||||||
|
val etTitle = dialogView.findViewById<EditText>(R.id.etPromptTitle)
|
||||||
|
val etContent = dialogView.findViewById<EditText>(R.id.etPromptContent)
|
||||||
|
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle("添加提示词")
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton("添加") { _, _ ->
|
||||||
|
val title = etTitle.text.toString().trim()
|
||||||
|
val content = etContent.text.toString().trim()
|
||||||
|
if (title.isNotEmpty() && content.isNotEmpty()) {
|
||||||
|
promptConfigs.add(PromptConfig(
|
||||||
|
id = "custom-${promptConfigs.size}",
|
||||||
|
title = title,
|
||||||
|
content = content
|
||||||
|
))
|
||||||
|
updatePromptList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton("取消", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadConfigsToViews() {
|
private fun loadConfigsToViews() {
|
||||||
if (llmConfigs.size > 0) {
|
if (llmConfigs.size > 0) {
|
||||||
val config1 = llmConfigs[0]
|
val config1 = llmConfigs[0]
|
||||||
|
cbModel1Enabled.isChecked = config1.enabled
|
||||||
|
etModelName1.setText(config1.name)
|
||||||
etBaseUrl1.setText(config1.baseUrl)
|
etBaseUrl1.setText(config1.baseUrl)
|
||||||
etApiKey1.setText(config1.apiKey)
|
etApiKey1.setText(config1.apiKey)
|
||||||
etModel1.setText(config1.model)
|
etModel1.setText(config1.model)
|
||||||
etModelName1.setText(config1.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (llmConfigs.size > 1) {
|
if (llmConfigs.size > 1) {
|
||||||
val config2 = llmConfigs[1]
|
val config2 = llmConfigs[1]
|
||||||
|
cbModel2Enabled.isChecked = config2.enabled
|
||||||
|
etModelName2.setText(config2.name)
|
||||||
etBaseUrl2.setText(config2.baseUrl)
|
etBaseUrl2.setText(config2.baseUrl)
|
||||||
etApiKey2.setText(config2.apiKey)
|
etApiKey2.setText(config2.apiKey)
|
||||||
etModel2.setText(config2.model)
|
etModel2.setText(config2.model)
|
||||||
etModelName2.setText(config2.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (llmConfigs.size > 2) {
|
if (llmConfigs.size > 2) {
|
||||||
val config3 = llmConfigs[2]
|
val config3 = llmConfigs[2]
|
||||||
|
cbModel3Enabled.isChecked = config3.enabled
|
||||||
|
etModelName3.setText(config3.name)
|
||||||
etBaseUrl3.setText(config3.baseUrl)
|
etBaseUrl3.setText(config3.baseUrl)
|
||||||
etApiKey3.setText(config3.apiKey)
|
etApiKey3.setText(config3.apiKey)
|
||||||
etModel3.setText(config3.model)
|
etModel3.setText(config3.model)
|
||||||
etModelName3.setText(config3.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setDefaultConfigs() {
|
private fun setDefaultConfigs() {
|
||||||
|
cbModel1Enabled.isChecked = true
|
||||||
|
etModelName1.setText("模型1")
|
||||||
etBaseUrl1.setText("https://api.openai.com/v1")
|
etBaseUrl1.setText("https://api.openai.com/v1")
|
||||||
etModel1.setText("gpt-4o")
|
etModel1.setText("gpt-4o")
|
||||||
etModelName1.setText("默认配置")
|
|
||||||
|
|
||||||
|
cbModel2Enabled.isChecked = false
|
||||||
|
etModelName2.setText("模型2")
|
||||||
etBaseUrl2.setText("https://api.openai.com/v1")
|
etBaseUrl2.setText("https://api.openai.com/v1")
|
||||||
etModel2.setText("gpt-4o")
|
etModel2.setText("gpt-4o")
|
||||||
etModelName2.setText("配置2")
|
|
||||||
|
|
||||||
|
cbModel3Enabled.isChecked = false
|
||||||
|
etModelName3.setText("模型3")
|
||||||
etBaseUrl3.setText("https://api.openai.com/v1")
|
etBaseUrl3.setText("https://api.openai.com/v1")
|
||||||
etModel3.setText("gpt-4o")
|
etModel3.setText("gpt-4o")
|
||||||
etModelName3.setText("配置3")
|
|
||||||
|
|
||||||
updateApiKeyVisibilityForAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateApiKeyVisibilityForAll() {
|
|
||||||
updateApiKeyVisibility(etApiKey1, btnToggleApiKey1)
|
|
||||||
updateApiKeyVisibility(etApiKey2, btnToggleApiKey2)
|
|
||||||
updateApiKeyVisibility(etApiKey3, btnToggleApiKey3)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateApiKeyVisibility(editText: EditText, button: ImageButton) {
|
|
||||||
val isEmpty = editText.text.toString().isEmpty()
|
|
||||||
editText.transformationMethod = if (isEmpty) null else PasswordTransformationMethod()
|
|
||||||
editText.setSelection(editText.text.length)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleApiKeyVisibility(editText: EditText, button: ImageButton) {
|
private fun toggleApiKeyVisibility(editText: EditText, button: ImageButton) {
|
||||||
@@ -314,29 +412,32 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
private fun saveConfigurations() {
|
private fun saveConfigurations() {
|
||||||
llmConfigs.clear()
|
llmConfigs.clear()
|
||||||
llmConfigs.add(LLMConfig(
|
llmConfigs.add(LLMConfig(
|
||||||
name = etModelName1.text.toString().ifEmpty { "默认配置" },
|
name = etModelName1.text.toString().ifEmpty { "模型1" },
|
||||||
baseUrl = etBaseUrl1.text.toString(),
|
baseUrl = etBaseUrl1.text.toString(),
|
||||||
apiKey = etApiKey1.text.toString(),
|
apiKey = etApiKey1.text.toString(),
|
||||||
model = etModel1.text.toString()
|
model = etModel1.text.toString(),
|
||||||
|
enabled = cbModel1Enabled.isChecked
|
||||||
))
|
))
|
||||||
llmConfigs.add(LLMConfig(
|
llmConfigs.add(LLMConfig(
|
||||||
name = etModelName2.text.toString().ifEmpty { "配置2" },
|
name = etModelName2.text.toString().ifEmpty { "模型2" },
|
||||||
baseUrl = etBaseUrl2.text.toString(),
|
baseUrl = etBaseUrl2.text.toString(),
|
||||||
apiKey = etApiKey2.text.toString(),
|
apiKey = etApiKey2.text.toString(),
|
||||||
model = etModel2.text.toString()
|
model = etModel2.text.toString(),
|
||||||
|
enabled = cbModel2Enabled.isChecked
|
||||||
))
|
))
|
||||||
llmConfigs.add(LLMConfig(
|
llmConfigs.add(LLMConfig(
|
||||||
name = etModelName3.text.toString().ifEmpty { "配置3" },
|
name = etModelName3.text.toString().ifEmpty { "模型3" },
|
||||||
baseUrl = etBaseUrl3.text.toString(),
|
baseUrl = etBaseUrl3.text.toString(),
|
||||||
apiKey = etApiKey3.text.toString(),
|
apiKey = etApiKey3.text.toString(),
|
||||||
model = etModel3.text.toString()
|
model = etModel3.text.toString(),
|
||||||
|
enabled = cbModel3Enabled.isChecked
|
||||||
))
|
))
|
||||||
|
|
||||||
val settingsData = SettingsData(
|
val settingsData = SettingsData(
|
||||||
llmConfigs = llmConfigs,
|
llmConfigs = llmConfigs,
|
||||||
selectedLlmIndex = selectedLlmIndex,
|
selectedLlmIndex = selectedLlmIndex,
|
||||||
headerConfigs = null,
|
headerConfigs = null,
|
||||||
promptConfigs = null,
|
promptConfigs = promptConfigs,
|
||||||
buttonConfigs = null,
|
buttonConfigs = null,
|
||||||
noteApiConfig = null
|
noteApiConfig = null
|
||||||
)
|
)
|
||||||
@@ -354,6 +455,8 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
val enabled: Boolean = true
|
val enabled: Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PromptConfig(val id: String, val title: String, val content: String)
|
||||||
|
|
||||||
data class SettingsData(
|
data class SettingsData(
|
||||||
val llmConfigs: List<LLMConfig>?,
|
val llmConfigs: List<LLMConfig>?,
|
||||||
val selectedLlmIndex: Int?,
|
val selectedLlmIndex: Int?,
|
||||||
@@ -365,7 +468,6 @@ class SecondActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class HeaderConfig(val key: String, val value: 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 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)
|
data class NoteApiConfig(val apiType: String, val apiUrl: String, val apiKey: String)
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<!-- 提示词选择区:左侧标签+下拉框,右侧快捷按钮 -->
|
<!-- 提示词选择区 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 提示词详情:显示名称和内容 -->
|
<!-- 提示词详情 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -212,162 +212,236 @@
|
|||||||
android:text="无特殊指令"/>
|
android:text="无特殊指令"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 大模型返回结果 -->
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="大模型返回结果"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@color/text_hint"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:letterSpacing="0.15"
|
|
||||||
android:layout_marginBottom="14dp"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="@drawable/result_card_bg"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="2dp"
|
|
||||||
android:background="@color/primary"
|
|
||||||
android:layout_marginBottom="12dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/outputStatusLabel"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="等待发送"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@color/primary"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/outputTextView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minLines="3"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:text="发送消息后结果将在此显示"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:editable="true"
|
|
||||||
android:gravity="top"
|
|
||||||
android:background="@android:drawable/edit_text"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginTop="12dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnCopyResult"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="复制结果"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@color/primary"
|
|
||||||
android:background="@drawable/button_secondary_bg"
|
|
||||||
android:minWidth="0dp"
|
|
||||||
android:minHeight="0dp"
|
|
||||||
android:layout_marginEnd="8dp"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnSaveNote"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="提交笔记"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@color/primary"
|
|
||||||
android:background="@drawable/button_secondary_bg"
|
|
||||||
android:minWidth="0dp"
|
|
||||||
android:minHeight="0dp"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<!-- 输入区域 -->
|
<!-- 输入区域 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="@color/surface"
|
android:background="@drawable/input_bg"
|
||||||
android:padding="20dp">
|
android:padding="12dp"
|
||||||
|
android:layout_marginBottom="14dp">
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/inputEditText"
|
android:id="@+id/inputEditText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minLines="3"
|
android:minLines="2"
|
||||||
android:maxLines="10"
|
android:maxLines="5"
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="输入待发送内容…"
|
android:hint="输入待发送内容…"
|
||||||
android:inputType="textCapSentences|textMultiLine"
|
android:inputType="textCapSentences|textMultiLine"
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingBottom="12dp"
|
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textColor="@color/text_secondary"
|
android:textColor="@color/text_secondary"
|
||||||
android:textColorHint="@color/text_hint"
|
android:textColorHint="@color/text_hint"
|
||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:scrollbars="vertical"/>
|
android:background="@android:color/transparent"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="8dp"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/stopButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="36dp"
|
|
||||||
android:text="停止生成"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textColor="@color/stop_generate"
|
|
||||||
android:background="@drawable/stop_button_bg"
|
|
||||||
android:paddingLeft="12dp"
|
|
||||||
android:paddingRight="12dp"
|
|
||||||
android:minWidth="0dp"
|
|
||||||
android:minHeight="0dp"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvCharCount"
|
android:id="@+id/tvCharCount"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
android:text="0/4000"
|
android:text="0/4000"
|
||||||
android:textSize="11sp"
|
android:textSize="11sp"
|
||||||
android:textColor="@color/text_hint"
|
android:textColor="@color/text_hint"/>
|
||||||
android:layout_marginEnd="12dp"/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/sendButton"
|
android:id="@+id/sendButton"
|
||||||
android:layout_width="42dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="42dp"
|
android:layout_height="32dp"
|
||||||
android:text="➤"
|
android:text="发送"
|
||||||
android:textSize="18sp"
|
android:textSize="13sp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:background="@drawable/send_button_bg"
|
android:background="@drawable/send_button_bg"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingRight="20dp"
|
||||||
android:minWidth="0dp"
|
android:minWidth="0dp"
|
||||||
android:minHeight="0dp"/>
|
android:minHeight="0dp"/>
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 三栏结果显示 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="各模型返回结果"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_hint"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:letterSpacing="0.15"
|
||||||
|
android:layout_marginBottom="10dp"/>
|
||||||
|
|
||||||
|
<!-- 模型1 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/result_card_bg"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvModel1Name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="● 模型1"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/primary"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatus1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待..."
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="@color/text_hint"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginBottom="6dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvResult1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待结果..."
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:minLines="3"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 模型2 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/result_card_bg"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvModel2Name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="○ 模型2"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/text_hint"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatus2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待..."
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="@color/text_hint"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginBottom="6dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvResult2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待结果..."
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:minLines="3"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 模型3 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/result_card_bg"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvModel3Name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="○ 模型3"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/text_hint"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvStatus3"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待..."
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="@color/text_hint"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginBottom="6dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvResult3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="等待结果..."
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:minLines="3"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 复制结果按钮 -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnCopyResult"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:text="复制所有结果"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/primary"
|
||||||
|
android:background="@drawable/button_secondary_bg"
|
||||||
|
android:layout_marginTop="8dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
File diff suppressed because it is too large
Load Diff
46
flomo-ai/app/src/main/res/layout/dialog_add_prompt.xml
Normal file
46
flomo-ai/app/src/main/res/layout/dialog_add_prompt.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?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:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="标题"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_hint"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPromptTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:hint="例如:翻译助手"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:background="@drawable/edittext_border"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="内容"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_hint"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPromptContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:hint="提示词内容..."
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@drawable/edittext_border"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:gravity="top"
|
||||||
|
android:inputType="textMultiLine"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
46
flomo-ai/app/src/main/res/layout/item_prompt_config.xml
Normal file
46
flomo-ai/app/src/main/res/layout/item_prompt_config.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?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:background="@drawable/edittext_border"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPromptTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeletePrompt"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:text="删除"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/error"
|
||||||
|
android:background="@drawable/button_secondary_bg"
|
||||||
|
android:minHeight="0dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPromptContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_hint"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user