ww
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
This commit is contained in:
@@ -107,6 +107,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (btn != null) {
|
||||
btn.setOnClickListener(v -> viewModel.generate());
|
||||
}
|
||||
MaterialButton btnOptimize = binding.btnOptimize;
|
||||
if (btnOptimize != null) {
|
||||
btnOptimize.setOnClickListener(v -> viewModel.optimizePrompt());
|
||||
}
|
||||
}
|
||||
|
||||
private void setupResultAndActions() {
|
||||
@@ -135,8 +139,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
viewModel.getLoadingLiveData().observe(this, loading -> {
|
||||
ProgressBar progress = binding.progressBar;
|
||||
MaterialButton btn = binding.btnGenerate;
|
||||
MaterialButton btnOpt = binding.btnOptimize;
|
||||
if (progress != null) progress.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
|
||||
if (btn != null) btn.setEnabled(loading == null || !loading);
|
||||
if (btnOpt != null) btnOpt.setEnabled(loading == null || !loading);
|
||||
});
|
||||
|
||||
viewModel.getErrorLiveData().observe(this, error -> {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.ruilaizi.example.data.api;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.ruilaizi.example.BuildConfig;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* OpenAI 兼容的非流式 Chat Completions 接口。
|
||||
* 用于意图分析、提示词优化等需要完整响应的场景。默认对接 DeepSeek。
|
||||
*/
|
||||
public class OpenAICompletionService {
|
||||
|
||||
private static final String TAG = "OpenAICompletion";
|
||||
private static final String CHAT_URL = "chat/completions";
|
||||
private static final String MODEL = "deepseek-chat";
|
||||
|
||||
private final String baseUrl;
|
||||
|
||||
public OpenAICompletionService(String baseUrl) {
|
||||
this.baseUrl = baseUrl == null || baseUrl.isEmpty()
|
||||
? BuildConfig.API_BASE_URL
|
||||
: baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步请求 Chat Completions,返回完整 content。
|
||||
*/
|
||||
public String chat(String systemPrompt, String userContent, float temperature, int maxTokens, String apiKey) throws Exception {
|
||||
String urlStr = baseUrl + CHAT_URL;
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setConnectTimeout(30000);
|
||||
conn.setReadTimeout(60000);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
|
||||
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("model", MODEL);
|
||||
body.put("stream", false);
|
||||
body.put("max_tokens", maxTokens);
|
||||
body.put("temperature", (double) temperature);
|
||||
JSONArray messages = new JSONArray();
|
||||
messages.put(new JSONObject().put("role", "system").put("content", systemPrompt));
|
||||
messages.put(new JSONObject().put("role", "user").put("content", userContent));
|
||||
body.put("messages", messages);
|
||||
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
os.write(body.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
int code = conn.getResponseCode();
|
||||
if (code != 200) {
|
||||
String err = readAll(conn.getErrorStream());
|
||||
throw new RuntimeException("HTTP " + code + ": " + err);
|
||||
}
|
||||
|
||||
String raw = readAll(conn.getInputStream());
|
||||
JSONObject json = new JSONObject(raw);
|
||||
JSONArray choices = json.optJSONArray("choices");
|
||||
if (choices == null || choices.length() == 0) {
|
||||
throw new RuntimeException("响应无内容");
|
||||
}
|
||||
JSONObject msg = choices.getJSONObject(0).optJSONObject("message");
|
||||
if (msg == null || !msg.has("content")) {
|
||||
throw new RuntimeException("响应格式错误");
|
||||
}
|
||||
String content = msg.optString("content", "").trim();
|
||||
if (content.isEmpty()) {
|
||||
throw new RuntimeException("模型返回为空");
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private static String readAll(java.io.InputStream is) {
|
||||
if (is == null) return "";
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) sb.append(line).append("\n");
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "readAll", e);
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.ruilaizi.example.data.prompt;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 两阶段专家提示词生成(参考《生成专业提示词代码逻辑分析》)。
|
||||
* 第一阶段:意图分析 → 第二阶段:领域专家生成。
|
||||
*/
|
||||
public class PromptGenerator {
|
||||
|
||||
private static final String TAG = "PromptGenerator";
|
||||
|
||||
private static final String INTENT_PROMPT = "你是一位资深的意图分析专家,请分析用户输入的意图和需求。\n\n" +
|
||||
"你必须严格按照以下JSON格式返回,不要添加任何其他内容:\n" +
|
||||
"{\n" +
|
||||
" \"core_intent\": \"技术\",\n" +
|
||||
" \"domain\": \"具体专业领域\",\n" +
|
||||
" \"key_requirements\": [\"需求1\", \"需求2\"],\n" +
|
||||
" \"expected_output\": \"期望输出的具体形式\",\n" +
|
||||
" \"constraints\": [\"约束1\", \"约束2\"],\n" +
|
||||
" \"keywords\": [\"关键词1\", \"关键词2\"]\n" +
|
||||
"}\n\n" +
|
||||
"注意:\n" +
|
||||
"1. 严格遵守JSON格式\n" +
|
||||
"2. core_intent必须是以下之一:技术、创意、分析、咨询\n" +
|
||||
"3. 数组至少包含1个元素\n" +
|
||||
"4. 所有字段都必须存在\n" +
|
||||
"5. 不要包含注释\n" +
|
||||
"6. 不要添加任何额外的文本";
|
||||
|
||||
private static final Map<String, String> EXPERT_TEMPLATES = new HashMap<>();
|
||||
|
||||
static {
|
||||
EXPERT_TEMPLATES.put("技术", "你是一位专业的技术领域提示工程师。基于以下意图分析,生成一个专业的技术任务提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 明确的技术背景和上下文\n2. 具体的技术要求和规范\n3. 性能和质量标准\n4. 技术约束条件\n5. 预期交付成果\n6. 评估标准\n\n使用专业技术术语,确保提示词的可执行性和可验证性。");
|
||||
EXPERT_TEMPLATES.put("创意", "你是一位专业的创意领域提示工程师。基于以下意图分析,生成一个创意设计提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 创意方向和灵感来源\n2. 风格和氛围要求\n3. 目标受众定义\n4. 设计元素规范\n5. 创意表现形式\n6. 评估标准\n\n使用专业创意术语,确保提示词的创新性和可执行性。");
|
||||
EXPERT_TEMPLATES.put("分析", "你是一位专业的数据分析提示工程师。基于以下意图分析,生成一个数据分析提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 分析目标和范围\n2. 数据要求和规范\n3. 分析方法和工具\n4. 输出格式要求\n5. 关键指标定义\n6. 质量控制标准\n\n使用专业分析术语,确保提示词的科学性和可操作性。");
|
||||
EXPERT_TEMPLATES.put("咨询", "你是一位专业的咨询领域提示工程师。基于以下意图分析,生成一个咨询服务提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 咨询问题界定\n2. 背景信息要求\n3. 分析框架设定\n4. 建议输出格式\n5. 实施考虑因素\n6. 效果评估标准\n\n使用专业咨询术语,确保提示词的专业性和实用性。");
|
||||
}
|
||||
|
||||
private static final String FALLBACK_TEMPLATE = "你是一位专业的通用领域提示工程师。基于以下意图分析,生成一个专业的提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 明确的目标定义\n2. 具体要求和规范\n3. 质量标准\n4. 约束条件\n5. 预期输出\n6. 评估标准\n\n确保提示词的清晰性和可执行性。";
|
||||
|
||||
private final OpenAICompletionService completionService;
|
||||
private final ApiKeyProvider apiKeyProvider;
|
||||
|
||||
public PromptGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
|
||||
this.completionService = completionService;
|
||||
this.apiKeyProvider = apiKeyProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步执行两阶段生成(需在后台线程调用)。
|
||||
*/
|
||||
public OptimizeResult optimize(String userInput, float temperature, int maxTokens) throws Exception {
|
||||
String apiKey = apiKeyProvider.getApiKey();
|
||||
if (apiKey == null || apiKey.isEmpty()) {
|
||||
throw new Exception("请先在设置中配置 API Key");
|
||||
}
|
||||
|
||||
// 第一阶段:意图分析
|
||||
String intentRaw = completionService.chat(INTENT_PROMPT, userInput, 0.1f, 1024, apiKey);
|
||||
intentRaw = intentRaw.replace("```json", "").replace("```", "").trim();
|
||||
|
||||
IntentAnalysis intent = parseIntent(intentRaw);
|
||||
if (intent == null) {
|
||||
throw new Exception("意图分析解析失败");
|
||||
}
|
||||
|
||||
// 第二阶段:领域专家生成
|
||||
String template = EXPERT_TEMPLATES.getOrDefault(intent.coreIntent, FALLBACK_TEMPLATE);
|
||||
String analysisStr = toJsonString(intent);
|
||||
String expertPrompt = template.replace("{analysis}", analysisStr);
|
||||
|
||||
String generated = completionService.chat(expertPrompt, userInput, temperature, maxTokens, apiKey);
|
||||
if (generated == null || generated.isEmpty()) {
|
||||
throw new Exception("生成结果为空");
|
||||
}
|
||||
|
||||
return new OptimizeResult(intent, generated.trim());
|
||||
}
|
||||
|
||||
private IntentAnalysis parseIntent(String json) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(json);
|
||||
IntentAnalysis ia = new IntentAnalysis();
|
||||
ia.coreIntent = safeStr(obj.opt("core_intent"), "技术");
|
||||
if (!Arrays.asList("技术", "创意", "分析", "咨询").contains(ia.coreIntent)) {
|
||||
ia.coreIntent = "技术";
|
||||
}
|
||||
ia.domain = safeStr(obj.opt("domain"), "未指定");
|
||||
ia.expectedOutput = safeStr(obj.opt("expected_output"), "未指定");
|
||||
ia.keyRequirements = toStrArray(obj.optJSONArray("key_requirements"));
|
||||
ia.constraints = toStrArray(obj.optJSONArray("constraints"));
|
||||
ia.keywords = toStrArray(obj.optJSONArray("keywords"));
|
||||
return ia;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "parseIntent failed: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String safeStr(Object o, String def) {
|
||||
return o != null ? String.valueOf(o).trim() : def;
|
||||
}
|
||||
|
||||
private static String[] toStrArray(JSONArray arr) {
|
||||
if (arr == null || arr.length() == 0) return new String[]{"未指定"};
|
||||
List<String> list = new ArrayList<>();
|
||||
for (int i = 0; i < arr.length(); i++) {
|
||||
list.add(String.valueOf(arr.opt(i)));
|
||||
}
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static String toJsonString(IntentAnalysis ia) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("core_intent", ia.coreIntent);
|
||||
obj.put("domain", ia.domain);
|
||||
obj.put("expected_output", ia.expectedOutput);
|
||||
obj.put("key_requirements", new JSONArray(ia.keyRequirements != null ? Arrays.asList(ia.keyRequirements) : new ArrayList<>()));
|
||||
obj.put("constraints", new JSONArray(ia.constraints != null ? Arrays.asList(ia.constraints) : new ArrayList<>()));
|
||||
obj.put("keywords", new JSONArray(ia.keywords != null ? Arrays.asList(ia.keywords) : new ArrayList<>()));
|
||||
return obj.toString(2);
|
||||
} catch (Exception e) {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
private static class IntentAnalysis {
|
||||
String coreIntent;
|
||||
String domain;
|
||||
String expectedOutput;
|
||||
String[] keyRequirements;
|
||||
String[] constraints;
|
||||
String[] keywords;
|
||||
}
|
||||
|
||||
/** 优化结果:意图分析 + 生成的提示词 */
|
||||
public static class OptimizeResult {
|
||||
public final String coreIntent;
|
||||
public final String domain;
|
||||
public final String expectedOutput;
|
||||
public final String[] keyRequirements;
|
||||
public final String[] constraints;
|
||||
public final String generatedPrompt;
|
||||
|
||||
OptimizeResult(IntentAnalysis ia, String generatedPrompt) {
|
||||
this.coreIntent = ia.coreIntent;
|
||||
this.domain = ia.domain;
|
||||
this.expectedOutput = ia.expectedOutput;
|
||||
this.keyRequirements = ia.keyRequirements != null ? ia.keyRequirements : new String[0];
|
||||
this.constraints = ia.constraints != null ? ia.constraints : new String[0];
|
||||
this.generatedPrompt = generatedPrompt;
|
||||
}
|
||||
|
||||
/** 格式化为可展示的完整文本 */
|
||||
public String toDisplayText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("【意图分析】\n");
|
||||
sb.append("核心意图:").append(coreIntent).append("\n");
|
||||
sb.append("专业领域:").append(domain).append("\n");
|
||||
sb.append("预期输出:").append(expectedOutput).append("\n");
|
||||
if (keyRequirements != null && keyRequirements.length > 0) {
|
||||
sb.append("关键需求:").append(String.join("、", keyRequirements)).append("\n");
|
||||
}
|
||||
if (constraints != null && constraints.length > 0) {
|
||||
sb.append("约束条件:").append(String.join("、", constraints)).append("\n");
|
||||
}
|
||||
sb.append("\n【优化后提示词】\n").append(generatedPrompt);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,24 +6,28 @@ import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.ruilaizi.example.data.api.AIService;
|
||||
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||
import com.ruilaizi.example.data.api.StreamCallback;
|
||||
import com.ruilaizi.example.data.db.AppDatabase;
|
||||
import com.ruilaizi.example.data.db.GenerationRecord;
|
||||
import com.ruilaizi.example.data.db.GenerationRecordDao;
|
||||
import com.ruilaizi.example.data.prompt.PromptGenerator;
|
||||
|
||||
/**
|
||||
* 生成业务仓库:调用 AI 流式接口,并缓存记录到 Room。
|
||||
* 生成业务仓库:调用 AI 流式接口、提示词优化,并缓存记录到 Room。
|
||||
*/
|
||||
public class GenerationRepository {
|
||||
|
||||
private final AIService aiService;
|
||||
private final ApiKeyProvider apiKeyProvider;
|
||||
private final GenerationRecordDao dao;
|
||||
private final PromptGenerator promptGenerator;
|
||||
|
||||
public GenerationRepository(Context context) {
|
||||
apiKeyProvider = new ApiKeyProvider(context);
|
||||
aiService = new com.ruilaizi.example.data.api.OpenAIStreamService(null);
|
||||
dao = AppDatabase.getInstance(context).generationRecordDao();
|
||||
promptGenerator = new PromptGenerator(new OpenAICompletionService(null), apiKeyProvider);
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
@@ -76,4 +80,11 @@ public class GenerationRepository {
|
||||
public LiveData<java.util.List<GenerationRecord>> getRecentRecords() {
|
||||
return dao.getRecentRecords();
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能提示词优化(两阶段专家逻辑)。需在后台线程调用。
|
||||
*/
|
||||
public PromptGenerator.OptimizeResult optimizePrompt(String input, float temperature, int maxTokens) throws Exception {
|
||||
return promptGenerator.optimize(input, temperature, maxTokens);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,12 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.ruilaizi.example.data.api.StreamCallback;
|
||||
import com.ruilaizi.example.data.db.GenerationRecord;
|
||||
import com.ruilaizi.example.data.prompt.PromptGenerator;
|
||||
import com.ruilaizi.example.data.repository.GenerationRepository;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 主界面 MVVM ViewModel:提示词、参数、生成结果、加载状态及复制/重新生成/续写。
|
||||
*/
|
||||
@@ -37,6 +41,7 @@ public class MainViewModel extends AndroidViewModel {
|
||||
private int lastTemperatureLevel = 1;
|
||||
|
||||
private final StringBuilder streamingBuffer = new StringBuilder();
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
public MainViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
@@ -67,6 +72,37 @@ public class MainViewModel extends AndroidViewModel {
|
||||
temperatureLevelLiveData.setValue(level);
|
||||
}
|
||||
|
||||
/** 智能提示词优化(两阶段专家逻辑) */
|
||||
public void optimizePrompt() {
|
||||
String prompt = promptLiveData.getValue();
|
||||
if (prompt == null) prompt = "";
|
||||
prompt = prompt.trim();
|
||||
if (prompt.isEmpty()) {
|
||||
snackbarLiveData.setValue("请输入需求描述");
|
||||
return;
|
||||
}
|
||||
if (repository.getApiKey() == null || repository.getApiKey().isEmpty()) {
|
||||
errorLiveData.setValue("请先在设置中配置 API Key");
|
||||
return;
|
||||
}
|
||||
loadingLiveData.setValue(true);
|
||||
resultLiveData.setValue("");
|
||||
errorLiveData.setValue(null);
|
||||
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
PromptGenerator.OptimizeResult result = repository.optimizePrompt(prompt, 0.7f, 1000);
|
||||
String display = result.toDisplayText();
|
||||
resultLiveData.postValue(display);
|
||||
snackbarLiveData.postValue("提示词优化完成");
|
||||
} catch (Exception e) {
|
||||
errorLiveData.postValue(e != null ? e.getMessage() : "优化失败");
|
||||
} finally {
|
||||
loadingLiveData.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 生成:使用当前 prompt 与参数 */
|
||||
public void generate() {
|
||||
String prompt = promptLiveData.getValue();
|
||||
@@ -126,16 +162,22 @@ public class MainViewModel extends AndroidViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/** 续写:把当前结果作为新 prompt,清空结果区,可编辑后生成 */
|
||||
/** 续写:把当前结果作为新 prompt,清空结果区,可编辑后生成。若为优化结果则只填入优化后的提示词部分。 */
|
||||
public void continueEdit() {
|
||||
String result = resultLiveData.getValue();
|
||||
if (result != null && !result.isEmpty()) {
|
||||
setPrompt(result);
|
||||
resultLiveData.setValue("");
|
||||
snackbarLiveData.setValue("已填入输入框,可修改后点击生成");
|
||||
} else {
|
||||
if (result == null || result.isEmpty()) {
|
||||
snackbarLiveData.setValue("暂无生成结果可续写");
|
||||
return;
|
||||
}
|
||||
String toPut = result;
|
||||
String marker = "【优化后提示词】\n";
|
||||
int idx = result.indexOf(marker);
|
||||
if (idx >= 0) {
|
||||
toPut = result.substring(idx + marker.length()).trim();
|
||||
}
|
||||
setPrompt(toPut);
|
||||
resultLiveData.setValue("");
|
||||
snackbarLiveData.setValue("已填入输入框,可修改后点击生成");
|
||||
}
|
||||
|
||||
/** 复制结果到剪贴板 */
|
||||
@@ -167,4 +209,10 @@ public class MainViewModel extends AndroidViewModel {
|
||||
public void clearSnackbar() {
|
||||
snackbarLiveData.setValue(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
executor.shutdown();
|
||||
super.onCleared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,15 +199,31 @@
|
||||
android:textColor="@color/colorTextSecondary" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_generate"
|
||||
<!-- 操作按钮区:优化 + 生成 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:text="@string/btn_generate"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@color/colorWhite"
|
||||
android:layout_marginBottom="20dp" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false"
|
||||
android:layout_marginBottom="20dp">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_optimize"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/btn_optimize"
|
||||
android:layout_marginEnd="8dp" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_generate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/btn_generate"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@color/colorWhite"
|
||||
android:layout_marginStart="8dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 加载指示 -->
|
||||
<ProgressBar
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<string name="temp_balanced">平衡</string>
|
||||
<string name="temp_creative">创意</string>
|
||||
<string name="btn_generate">生成</string>
|
||||
<string name="btn_optimize">优化提示词</string>
|
||||
<string name="result_placeholder">生成结果将显示在这里…</string>
|
||||
<string name="btn_copy">复制</string>
|
||||
<string name="btn_regenerate">重新生成</string>
|
||||
|
||||
Reference in New Issue
Block a user