ai应用
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:
@@ -34,6 +34,26 @@
|
|||||||
android:name=".MealPlanningHistoryActivity"
|
android:name=".MealPlanningHistoryActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".AIServicesActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".PlaceholderActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".TravelPlanningActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".WeeklyReportActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
<activity
|
||||||
|
android:name=".MeetingMinutesActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.ruilaizi.example;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.databinding.ActivityAiServicesBinding;
|
||||||
|
import com.ruilaizi.example.databinding.ItemAiServiceBinding;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 智能应用列表:有参考实现的跳转对应页,无的跳转占位页。
|
||||||
|
*/
|
||||||
|
public class AIServicesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final class Entry {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final String badge;
|
||||||
|
@Nullable final Class<?> activityClass;
|
||||||
|
final boolean placeholder;
|
||||||
|
|
||||||
|
Entry(String title, String subtitle, String badge, @Nullable Class<?> activityClass) {
|
||||||
|
this.title = title;
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
this.badge = badge;
|
||||||
|
this.activityClass = activityClass;
|
||||||
|
this.placeholder = (activityClass == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ActivityAiServicesBinding binding = ActivityAiServicesBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
buildEntries();
|
||||||
|
binding.btnBack.setOnClickListener(v -> finish());
|
||||||
|
|
||||||
|
LinearLayout container = binding.containerAiList;
|
||||||
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
Entry e = entries.get(i);
|
||||||
|
ItemAiServiceBinding itemBinding = ItemAiServiceBinding.inflate(inflater, container, false);
|
||||||
|
itemBinding.itemTitle.setText(e.title);
|
||||||
|
itemBinding.itemSubtitle.setText(e.subtitle);
|
||||||
|
itemBinding.itemBadge.setText(e.badge);
|
||||||
|
itemBinding.itemBadge.setVisibility(View.VISIBLE);
|
||||||
|
final int index = i;
|
||||||
|
itemBinding.getRoot().setOnClickListener(v -> onItemClick(entries.get(index)));
|
||||||
|
container.addView(itemBinding.getRoot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildEntries() {
|
||||||
|
entries.clear();
|
||||||
|
// 有参考实现的:跳转对应 Activity
|
||||||
|
entries.add(new Entry("智能饭菜规划", "AI智能推荐营养搭配,让每一餐都健康美味", "热门", MealPlanningActivity.class));
|
||||||
|
entries.add(new Entry("提示词优化", "两阶段专家生成专业提示词", "通用", null)); // 主界面已有,可跳回主界面
|
||||||
|
entries.add(new Entry("旅行攻略规划", "根据目的地与天数,AI 生成行程与实用建议", "出行", TravelPlanningActivity.class));
|
||||||
|
entries.add(new Entry("智能周报生成", "输入工作要点,一键生成规范周报/日报", "办公", WeeklyReportActivity.class));
|
||||||
|
entries.add(new Entry("会议纪要整理", "粘贴会议转写或要点,一键生成结构化会议纪要", "办公", MeetingMinutesActivity.class));
|
||||||
|
// 以下为占位
|
||||||
|
entries.add(new Entry("古诗词解析", "深度解析古诗词意境,感受中华文化之美", "文化", null));
|
||||||
|
entries.add(new Entry("古诗词收藏", "收藏喜爱的古诗词,建立个人文化宝库", "收藏", null));
|
||||||
|
entries.add(new Entry("简历/求职信优化", "润色简历或根据岗位生成针对性求职信", "求职", null));
|
||||||
|
entries.add(new Entry("智能提示词优化2号专家", "需求分析 + 领域专家生成高质量提示词", "专家", null));
|
||||||
|
entries.add(new Entry("智能提示词优化3号专家", "需求分析 + 领域专家生成,含历史记录", "专家", null));
|
||||||
|
entries.add(new Entry("读书笔记/摘要", "生成摘要、金句、思维导图式大纲或读后感", "文化", null));
|
||||||
|
entries.add(new Entry("育儿/教育助手", "育儿建议、睡前故事、简单科普或习题讲解", "教育", null));
|
||||||
|
entries.add(new Entry("健身/运动计划", "根据目标与场地生成一周训练计划", "健康", null));
|
||||||
|
entries.add(new Entry("合同/条款解读", "人话版要点、风险提示与建议关注条款", "法律", null));
|
||||||
|
entries.add(new Entry("小红书/短视频脚本", "生成标题、分镜文案、口播稿", "创作", null));
|
||||||
|
entries.add(new Entry("面试模拟/问答准备", "按岗位类型出题并给参考答案与点评", "求职", null));
|
||||||
|
entries.add(new Entry("节日/祝福语生成", "祝福语、贺卡文案、红包配文", "生活", null));
|
||||||
|
entries.add(new Entry("个人复盘/周复盘", "结构化复盘与下周重点", "成长", null));
|
||||||
|
entries.add(new Entry("待办/日程整理", "零散待办生成按优先级与日期的日程清单", "效率", null));
|
||||||
|
entries.add(new Entry("邮件润色/回复建议", "草稿或要点生成得体邮件回复", "办公", null));
|
||||||
|
entries.add(new Entry("演讲稿/汇报稿", "根据主题与场合生成演讲稿或汇报大纲", "办公", null));
|
||||||
|
entries.add(new Entry("取名/品牌名", "公司名、产品名或宝宝取名建议", "创意", null));
|
||||||
|
entries.add(new Entry("菜谱/做法", "根据食材或菜名生成详细做法", "生活", null));
|
||||||
|
entries.add(new Entry("学习计划/备考规划", "生成学习计划与复习建议", "教育", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onItemClick(Entry e) {
|
||||||
|
if (e.activityClass != null) {
|
||||||
|
startActivity(new Intent(this, e.activityClass));
|
||||||
|
} else if ("提示词优化".equals(e.title)) {
|
||||||
|
// 主界面已有优化提示词,返回主界面
|
||||||
|
startActivity(new Intent(this, MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(this, PlaceholderActivity.class);
|
||||||
|
intent.putExtra(PlaceholderActivity.EXTRA_TITLE, e.title);
|
||||||
|
intent.putExtra(PlaceholderActivity.EXTRA_SUBTITLE, e.subtitle);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,8 +48,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (binding.btnHistory != null) {
|
if (binding.btnHistory != null) {
|
||||||
binding.btnHistory.setOnClickListener(v -> openHistory());
|
binding.btnHistory.setOnClickListener(v -> openHistory());
|
||||||
}
|
}
|
||||||
if (binding.btnMealPlanning != null) {
|
if (binding.btnAiServices != null) {
|
||||||
binding.btnMealPlanning.setOnClickListener(v -> startActivity(new Intent(this, MealPlanningActivity.class)));
|
binding.btnAiServices.setOnClickListener(v -> startActivity(new Intent(this, AIServicesActivity.class)));
|
||||||
}
|
}
|
||||||
observeViewModel();
|
observeViewModel();
|
||||||
showApiKeyHintIfNeeded();
|
showApiKeyHintIfNeeded();
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.ruilaizi.example;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.ruilaizi.example.databinding.ActivityMeetingMinutesBinding;
|
||||||
|
import com.ruilaizi.example.ui.MeetingMinutesViewModel;
|
||||||
|
|
||||||
|
public class MeetingMinutesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ActivityMeetingMinutesBinding binding;
|
||||||
|
private MeetingMinutesViewModel viewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
binding = ActivityMeetingMinutesBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MeetingMinutesViewModel.class);
|
||||||
|
|
||||||
|
binding.btnBack.setOnClickListener(v -> finish());
|
||||||
|
binding.btnGenerate.setOnClickListener(v -> doGenerate());
|
||||||
|
binding.btnCopy.setOnClickListener(v -> copyResult());
|
||||||
|
observeViewModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doGenerate() {
|
||||||
|
String raw = getText(binding.etRawContent);
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
Toast.makeText(this, "请粘贴或输入会议内容", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String title = getText(binding.etTitle);
|
||||||
|
viewModel.generate(raw, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(TextInputEditText et) {
|
||||||
|
return et.getText() != null ? et.getText().toString().trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyResult() {
|
||||||
|
String text = binding.tvResult.getText() != null ? binding.tvResult.getText().toString() : "";
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), "暂无内容可复制", Snackbar.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
if (cm != null) {
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText("会议纪要", text));
|
||||||
|
Snackbar.make(binding.getRoot(), "已复制到剪贴板", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeViewModel() {
|
||||||
|
viewModel.getResultLiveData().observe(this, text -> {
|
||||||
|
if (text != null) {
|
||||||
|
binding.tvResult.setText(text);
|
||||||
|
binding.tvResult.setTextColor(getResources().getColor(R.color.colorTextDark, getTheme()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getLoadingLiveData().observe(this, loading -> {
|
||||||
|
binding.progressBar.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
|
||||||
|
binding.btnGenerate.setEnabled(loading == null || !loading);
|
||||||
|
});
|
||||||
|
viewModel.getErrorLiveData().observe(this, error -> {
|
||||||
|
if (error != null && !error.isEmpty()) {
|
||||||
|
new AlertDialog.Builder(this).setMessage(error).setPositiveButton(android.R.string.ok, (d, w) -> viewModel.clearError()).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getSnackbarLiveData().observe(this, msg -> {
|
||||||
|
if (msg != null && !msg.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), msg, Snackbar.LENGTH_SHORT).show();
|
||||||
|
viewModel.clearSnackbar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ruilaizi.example;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.databinding.ActivityPlaceholderBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 占位页:无参考实现的功能仅显示标题与「敬请期待」。
|
||||||
|
*/
|
||||||
|
public class PlaceholderActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String EXTRA_TITLE = "title";
|
||||||
|
public static final String EXTRA_SUBTITLE = "subtitle";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ActivityPlaceholderBinding binding = ActivityPlaceholderBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
String title = getIntent() != null && getIntent().hasExtra(EXTRA_TITLE)
|
||||||
|
? getIntent().getStringExtra(EXTRA_TITLE) : "AI 应用";
|
||||||
|
String subtitle = getIntent() != null && getIntent().hasExtra(EXTRA_SUBTITLE)
|
||||||
|
? getIntent().getStringExtra(EXTRA_SUBTITLE) : "";
|
||||||
|
|
||||||
|
binding.placeholderTitle.setText(title);
|
||||||
|
binding.placeholderSubtitle.setText(subtitle != null ? subtitle : "");
|
||||||
|
binding.placeholderHint.setText(R.string.placeholder_coming_soon);
|
||||||
|
binding.btnBack.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.ruilaizi.example;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.ruilaizi.example.databinding.ActivityTravelPlanningBinding;
|
||||||
|
import com.ruilaizi.example.ui.TravelPlanningViewModel;
|
||||||
|
|
||||||
|
public class TravelPlanningActivity extends AppCompatActivity {
|
||||||
|
private ActivityTravelPlanningBinding binding;
|
||||||
|
private TravelPlanningViewModel viewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
binding = ActivityTravelPlanningBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(TravelPlanningViewModel.class);
|
||||||
|
binding.btnBack.setOnClickListener(v -> finish());
|
||||||
|
binding.btnGenerate.setOnClickListener(v -> doGenerate());
|
||||||
|
binding.btnCopy.setOnClickListener(v -> copyResult());
|
||||||
|
observeViewModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doGenerate() {
|
||||||
|
String dest = getText(binding.etDestination);
|
||||||
|
if (dest.isEmpty()) {
|
||||||
|
android.widget.Toast.makeText(this, "请填写目的地", android.widget.Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String days = getText(binding.etDays);
|
||||||
|
String people = getText(binding.etPeople);
|
||||||
|
String prefs = getText(binding.etPreferences);
|
||||||
|
viewModel.generate(dest, days.isEmpty() ? "3" : days, people.isEmpty() ? "2" : people, prefs, "适中");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(TextInputEditText et) {
|
||||||
|
return et.getText() != null ? et.getText().toString().trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyResult() {
|
||||||
|
String text = binding.tvResult.getText() != null ? binding.tvResult.getText().toString() : "";
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), "暂无内容可复制", Snackbar.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
if (cm != null) {
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText("旅行攻略", text));
|
||||||
|
Snackbar.make(binding.getRoot(), "已复制到剪贴板", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeViewModel() {
|
||||||
|
viewModel.getResultLiveData().observe(this, text -> {
|
||||||
|
if (text != null) {
|
||||||
|
binding.tvResult.setText(text);
|
||||||
|
binding.tvResult.setTextColor(getResources().getColor(R.color.colorTextDark, getTheme()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getLoadingLiveData().observe(this, loading -> {
|
||||||
|
binding.progressBar.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
|
||||||
|
binding.btnGenerate.setEnabled(loading == null || !loading);
|
||||||
|
});
|
||||||
|
viewModel.getErrorLiveData().observe(this, error -> {
|
||||||
|
if (error != null && !error.isEmpty()) {
|
||||||
|
new AlertDialog.Builder(this).setMessage(error).setPositiveButton(android.R.string.ok, (d, w) -> viewModel.clearError()).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getSnackbarLiveData().observe(this, msg -> {
|
||||||
|
if (msg != null && !msg.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), msg, Snackbar.LENGTH_SHORT).show();
|
||||||
|
viewModel.clearSnackbar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.ruilaizi.example;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.ruilaizi.example.databinding.ActivityWeeklyReportBinding;
|
||||||
|
import com.ruilaizi.example.ui.WeeklyReportViewModel;
|
||||||
|
|
||||||
|
public class WeeklyReportActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ActivityWeeklyReportBinding binding;
|
||||||
|
private WeeklyReportViewModel viewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
binding = ActivityWeeklyReportBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(WeeklyReportViewModel.class);
|
||||||
|
|
||||||
|
binding.btnBack.setOnClickListener(v -> finish());
|
||||||
|
binding.btnGenerate.setOnClickListener(v -> doGenerate());
|
||||||
|
binding.btnCopy.setOnClickListener(v -> copyResult());
|
||||||
|
observeViewModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doGenerate() {
|
||||||
|
String content = getText(binding.etContent);
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
Toast.makeText(this, "请填写工作要点或内容", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
viewModel.generate("weekly", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(TextInputEditText et) {
|
||||||
|
return et.getText() != null ? et.getText().toString().trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyResult() {
|
||||||
|
String text = binding.tvResult.getText() != null ? binding.tvResult.getText().toString() : "";
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), "暂无内容可复制", Snackbar.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
if (cm != null) {
|
||||||
|
cm.setPrimaryClip(ClipData.newPlainText("周报", text));
|
||||||
|
Snackbar.make(binding.getRoot(), "已复制到剪贴板", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeViewModel() {
|
||||||
|
viewModel.getResultLiveData().observe(this, text -> {
|
||||||
|
if (text != null) {
|
||||||
|
binding.tvResult.setText(text);
|
||||||
|
binding.tvResult.setTextColor(getResources().getColor(R.color.colorTextDark, getTheme()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getLoadingLiveData().observe(this, loading -> {
|
||||||
|
binding.progressBar.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
|
||||||
|
binding.btnGenerate.setEnabled(loading == null || !loading);
|
||||||
|
});
|
||||||
|
viewModel.getErrorLiveData().observe(this, error -> {
|
||||||
|
if (error != null && !error.isEmpty()) {
|
||||||
|
new AlertDialog.Builder(this).setMessage(error).setPositiveButton(android.R.string.ok, (d, w) -> viewModel.clearError()).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewModel.getSnackbarLiveData().observe(this, msg -> {
|
||||||
|
if (msg != null && !msg.isEmpty()) {
|
||||||
|
Snackbar.make(binding.getRoot(), msg, Snackbar.LENGTH_SHORT).show();
|
||||||
|
viewModel.clearSnackbar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.ruilaizi.example.data.meeting;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会议纪要整理生成器,与 Flask meeting_minutes 逻辑一致。
|
||||||
|
*/
|
||||||
|
public class MeetingMinutesGenerator {
|
||||||
|
|
||||||
|
private static final String SYSTEM_PROMPT = "你是一位专业的会议记录助手,擅长将零散的会议转写或要点整理成结构清晰的会议纪要。\n\n要求:\n"
|
||||||
|
+ "1. 从用户输入中提炼:会议主题、时间/参与人(如有)、讨论要点、结论与决议、待办事项(含责任人/截止时间若可推断)。\n"
|
||||||
|
+ "2. 使用 Markdown 格式:一级标题为「一、会议概要」「二、讨论要点」「三、结论与决议」「四、待办事项」等,二级标题和列表项简明扼要。\n"
|
||||||
|
+ "3. 语气正式、条理清楚,便于归档和分享。若信息不足可合理归纳,不要编造具体人名与数据。";
|
||||||
|
|
||||||
|
private final OpenAICompletionService completionService;
|
||||||
|
private final ApiKeyProvider apiKeyProvider;
|
||||||
|
|
||||||
|
public MeetingMinutesGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
|
||||||
|
this.completionService = completionService;
|
||||||
|
this.apiKeyProvider = apiKeyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String rawContent, String title) throws Exception {
|
||||||
|
String apiKey = apiKeyProvider.getApiKey();
|
||||||
|
if (apiKey == null || apiKey.isEmpty()) throw new Exception("请先在设置中配置 API Key");
|
||||||
|
if (rawContent == null || rawContent.trim().isEmpty()) throw new Exception("请粘贴或输入会议内容");
|
||||||
|
String userPrompt = "请将以下会议内容整理成会议纪要:\n\n" + (title != null && !title.isEmpty() ? "会议主题:" + title + "\n\n" : "") + rawContent.trim();
|
||||||
|
return completionService.chat(SYSTEM_PROMPT, userPrompt, 0.4f, 2000, apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.ruilaizi.example.data.meeting;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
public class MeetingMinutesRepository {
|
||||||
|
|
||||||
|
private final MeetingMinutesGenerator generator;
|
||||||
|
|
||||||
|
public MeetingMinutesRepository(Context context) {
|
||||||
|
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
|
||||||
|
generator = new MeetingMinutesGenerator(new OpenAICompletionService(null), apiKeyProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String rawContent, String title) throws Exception {
|
||||||
|
return generator.generate(rawContent, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.ruilaizi.example.data.travel;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
/** 旅行攻略生成器,与 Flask travel_planning 逻辑一致。 */
|
||||||
|
public class TravelPlanGenerator {
|
||||||
|
|
||||||
|
private static final String SYSTEM_PROMPT = "你是一位专业的旅行规划师,擅长根据目的地、天数和偏好生成实用、可执行的旅行攻略。\n\n"
|
||||||
|
+ "请按以下 **Markdown 结构** 输出:\n**一、行程概览**\n**二、每日行程**(按天,上午/下午/晚上)\n"
|
||||||
|
+ "**三、实用信息**(交通、住宿、美食、预算、注意事项)。要求:具体可执行、符合预算与偏好。";
|
||||||
|
|
||||||
|
private final OpenAICompletionService completionService;
|
||||||
|
private final ApiKeyProvider apiKeyProvider;
|
||||||
|
|
||||||
|
public TravelPlanGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
|
||||||
|
this.completionService = completionService;
|
||||||
|
this.apiKeyProvider = apiKeyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String destination, String days, String people, String preferences, String budget) throws Exception {
|
||||||
|
String apiKey = apiKeyProvider.getApiKey();
|
||||||
|
if (apiKey == null || apiKey.isEmpty()) throw new Exception("请先在设置中配置 API Key");
|
||||||
|
if (destination == null || destination.trim().isEmpty()) throw new Exception("请填写目的地");
|
||||||
|
String user = "目的地:" + destination.trim() + "。出行天数:" + (days != null ? days : "3")
|
||||||
|
+ "。人数:" + (people != null ? people : "2") + "。偏好:" + (preferences != null ? preferences : "无")
|
||||||
|
+ "。预算:" + (budget != null ? budget : "适中") + "。请生成旅行攻略。";
|
||||||
|
return completionService.chat(SYSTEM_PROMPT, user, 0.6f, 2000, apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.ruilaizi.example.data.travel;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
public class TravelPlanningRepository {
|
||||||
|
|
||||||
|
private final TravelPlanGenerator generator;
|
||||||
|
|
||||||
|
public TravelPlanningRepository(Context context) {
|
||||||
|
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
|
||||||
|
generator = new TravelPlanGenerator(new OpenAICompletionService(null), apiKeyProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String destination, String days, String people, String preferences, String budget) throws Exception {
|
||||||
|
return generator.generate(destination, days, people, preferences, budget);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.ruilaizi.example.data.weekly;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能周报/日报生成器,与 Flask weekly_report 逻辑一致。
|
||||||
|
*/
|
||||||
|
public class WeeklyReportGenerator {
|
||||||
|
|
||||||
|
private final OpenAICompletionService completionService;
|
||||||
|
private final ApiKeyProvider apiKeyProvider;
|
||||||
|
|
||||||
|
public WeeklyReportGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
|
||||||
|
this.completionService = completionService;
|
||||||
|
this.apiKeyProvider = apiKeyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String reportType, String content) throws Exception {
|
||||||
|
String apiKey = apiKeyProvider.getApiKey();
|
||||||
|
if (apiKey == null || apiKey.isEmpty()) throw new Exception("请先在设置中配置 API Key");
|
||||||
|
if (content == null || content.trim().isEmpty()) throw new Exception("请填写工作要点或内容");
|
||||||
|
String typeName = "weekly".equals(reportType) ? "周报" : "日报";
|
||||||
|
String systemPrompt = "你是一位专业的职场写作助手,擅长将零散的工作要点整理成规范、简洁的" + typeName + "。\n\n要求:\n"
|
||||||
|
+ "1. 根据用户输入的工作要点或流水记录,归纳成结构清晰的" + typeName + "。\n"
|
||||||
|
+ "2. 使用 Markdown 格式:一级标题为「一、本周/今日工作完成情况」等,二级标题区分模块,列表项简明扼要。\n"
|
||||||
|
+ "3. 可适当补充「二、下周/明日计划」「三、问题与风险」等小节(若用户未提供则简要生成)。\n"
|
||||||
|
+ "4. 语气正式、条理清楚,便于直接复制到飞书/钉钉/邮件。";
|
||||||
|
String userPrompt = "请将以下内容整理成一份" + typeName + ":\n\n" + content.trim();
|
||||||
|
return completionService.chat(systemPrompt, userPrompt, 0.5f, 1500, apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.ruilaizi.example.data.weekly;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.api.ApiKeyProvider;
|
||||||
|
import com.ruilaizi.example.data.api.OpenAICompletionService;
|
||||||
|
|
||||||
|
public class WeeklyReportRepository {
|
||||||
|
|
||||||
|
private final WeeklyReportGenerator generator;
|
||||||
|
|
||||||
|
public WeeklyReportRepository(Context context) {
|
||||||
|
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
|
||||||
|
generator = new WeeklyReportGenerator(new OpenAICompletionService(null), apiKeyProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String generate(String reportType, String content) throws Exception {
|
||||||
|
return generator.generate(reportType, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.ruilaizi.example.ui;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.meeting.MeetingMinutesRepository;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class MeetingMinutesViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private final MeetingMinutesRepository repository;
|
||||||
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private final MutableLiveData<String> resultLiveData = new MutableLiveData<>("");
|
||||||
|
private final MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>(false);
|
||||||
|
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<String> snackbarLiveData = new MutableLiveData<>();
|
||||||
|
|
||||||
|
public MeetingMinutesViewModel(@NonNull Application app) {
|
||||||
|
super(app);
|
||||||
|
repository = new MeetingMinutesRepository(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<String> getResultLiveData() { return resultLiveData; }
|
||||||
|
public LiveData<Boolean> getLoadingLiveData() { return loadingLiveData; }
|
||||||
|
public LiveData<String> getErrorLiveData() { return errorLiveData; }
|
||||||
|
public LiveData<String> getSnackbarLiveData() { return snackbarLiveData; }
|
||||||
|
|
||||||
|
public void generate(String rawContent, String title) {
|
||||||
|
loadingLiveData.setValue(true);
|
||||||
|
resultLiveData.setValue("");
|
||||||
|
errorLiveData.setValue(null);
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
String summary = repository.generate(rawContent, title);
|
||||||
|
resultLiveData.postValue(summary);
|
||||||
|
snackbarLiveData.postValue("会议纪要生成完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
|
||||||
|
} finally {
|
||||||
|
loadingLiveData.postValue(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearError() { errorLiveData.setValue(null); }
|
||||||
|
public void clearSnackbar() { snackbarLiveData.setValue(null); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
executor.shutdown();
|
||||||
|
super.onCleared();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.ruilaizi.example.ui;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.travel.TravelPlanningRepository;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class TravelPlanningViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private final TravelPlanningRepository repository;
|
||||||
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private final MutableLiveData<String> resultLiveData = new MutableLiveData<>("");
|
||||||
|
private final MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>(false);
|
||||||
|
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<String> snackbarLiveData = new MutableLiveData<>();
|
||||||
|
|
||||||
|
public TravelPlanningViewModel(@NonNull Application app) {
|
||||||
|
super(app);
|
||||||
|
repository = new TravelPlanningRepository(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<String> getResultLiveData() { return resultLiveData; }
|
||||||
|
public LiveData<Boolean> getLoadingLiveData() { return loadingLiveData; }
|
||||||
|
public LiveData<String> getErrorLiveData() { return errorLiveData; }
|
||||||
|
public LiveData<String> getSnackbarLiveData() { return snackbarLiveData; }
|
||||||
|
|
||||||
|
public void generate(String destination, String days, String people, String preferences, String budget) {
|
||||||
|
loadingLiveData.setValue(true);
|
||||||
|
resultLiveData.setValue("");
|
||||||
|
errorLiveData.setValue(null);
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
String plan = repository.generate(destination, days, people, preferences, budget);
|
||||||
|
resultLiveData.postValue(plan);
|
||||||
|
snackbarLiveData.postValue("旅行攻略生成完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
|
||||||
|
} finally {
|
||||||
|
loadingLiveData.postValue(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearError() { errorLiveData.setValue(null); }
|
||||||
|
public void clearSnackbar() { snackbarLiveData.setValue(null); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
executor.shutdown();
|
||||||
|
super.onCleared();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.ruilaizi.example.ui;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.ruilaizi.example.data.weekly.WeeklyReportRepository;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
public class WeeklyReportViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private final WeeklyReportRepository repository;
|
||||||
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
private final MutableLiveData<String> resultLiveData = new MutableLiveData<>("");
|
||||||
|
private final MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>(false);
|
||||||
|
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<String> snackbarLiveData = new MutableLiveData<>();
|
||||||
|
|
||||||
|
public WeeklyReportViewModel(@NonNull Application app) {
|
||||||
|
super(app);
|
||||||
|
repository = new WeeklyReportRepository(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<String> getResultLiveData() { return resultLiveData; }
|
||||||
|
public LiveData<Boolean> getLoadingLiveData() { return loadingLiveData; }
|
||||||
|
public LiveData<String> getErrorLiveData() { return errorLiveData; }
|
||||||
|
public LiveData<String> getSnackbarLiveData() { return snackbarLiveData; }
|
||||||
|
|
||||||
|
public void generate(String reportType, String content) {
|
||||||
|
loadingLiveData.setValue(true);
|
||||||
|
resultLiveData.setValue("");
|
||||||
|
errorLiveData.setValue(null);
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
String report = repository.generate(reportType, content);
|
||||||
|
resultLiveData.postValue(report);
|
||||||
|
snackbarLiveData.postValue("周报/日报生成完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
|
||||||
|
} finally {
|
||||||
|
loadingLiveData.postValue(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearError() { errorLiveData.setValue(null); }
|
||||||
|
public void clearSnackbar() { snackbarLiveData.setValue(null); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
executor.shutdown();
|
||||||
|
super.onCleared();
|
||||||
|
}
|
||||||
|
}
|
||||||
56
example/app/src/main/res/layout/activity_ai_services.xml
Normal file
56
example/app/src/main/res/layout/activity_ai_services.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/colorSurface">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="返回" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/ai_services_title"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:gravity="center" />
|
||||||
|
<View android:layout_width="48dp" android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:text="@string/ai_services_subtitle"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="24dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/container_ai_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btn_meal_planning"
|
android:id="@+id/btn_ai_services"
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/btn_meal_planning"
|
android:text="@string/btn_ai_services"
|
||||||
android:layout_marginEnd="8dp" />
|
android:layout_marginEnd="8dp" />
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btn_history"
|
android:id="@+id/btn_history"
|
||||||
|
|||||||
93
example/app/src/main/res/layout/activity_meeting_minutes.xml
Normal file
93
example/app/src/main/res/layout/activity_meeting_minutes.xml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorSurface"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="返回" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="会议纪要整理"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:gravity="center" />
|
||||||
|
<View android:layout_width="48dp" android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<LinearLayout
|
||||||
|
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/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/et_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="如:产品需求评审"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="会议内容/转写 *" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/et_raw_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minLines="6"
|
||||||
|
android:hint="粘贴会议转写或要点…"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_generate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="生成会议纪要"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:textColor="@color/colorWhite" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<ProgressBar android:id="@+id/progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="纪要结果" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="8dp" />
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:minHeight="80dp"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
<ScrollView android:layout_width="match_parent" android:layout_height="wrap_content" android:fillViewport="true" android:padding="16dp">
|
||||||
|
<TextView android:id="@+id/tv_result" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:lineSpacingMultiplier="1.2" android:textColor="@color/colorText" />
|
||||||
|
</ScrollView>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
<com.google.android.material.button.MaterialButton android:id="@+id/btn_copy" style="@style/Widget.MaterialComponents.Button.OutlinedButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/btn_copy" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
43
example/app/src/main/res/layout/activity_placeholder.xml
Normal file
43
example/app/src/main/res/layout/activity_placeholder.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:background="@color/colorSurface"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:text="返回" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/placeholder_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/placeholder_subtitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/placeholder_hint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/colorText" />
|
||||||
|
</LinearLayout>
|
||||||
114
example/app/src/main/res/layout/activity_travel_planning.xml
Normal file
114
example/app/src/main/res/layout/activity_travel_planning.xml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorSurface"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<!-- 标题栏:返回 + 标题 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="返回" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="旅行攻略规划"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center" />
|
||||||
|
<View android:layout_width="48dp" android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 旅行参数卡片 -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
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/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText android:id="@+id/et_destination" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="如:北京" android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="出行天数" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText android:id="@+id/et_days" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="3" android:inputType="number" android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="人数" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText android:id="@+id/et_people" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="2" android:inputType="number" android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="偏好/预算" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText android:id="@+id/et_preferences" android:layout_width="match_parent" android:layout_height="wrap_content" android:minLines="2" android:hint="如:喜欢人文,预算适中" android:inputType="textMultiLine" android:layout_marginBottom="16dp" />
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_generate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="生成旅行攻略"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
|
android:textColor="@color/colorWhite" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress_bar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="攻略结果" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="8dp" />
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:padding="16dp">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_result"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/colorText"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:lineSpacingMultiplier="1.2" />
|
||||||
|
</ScrollView>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_copy"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/btn_copy" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
62
example/app/src/main/res/layout/activity_weekly_report.xml
Normal file
62
example/app/src/main/res/layout/activity_weekly_report.xml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorSurface"
|
||||||
|
android:fillViewport="true">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_back"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="返回" />
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="智能周报生成"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:gravity="center" />
|
||||||
|
<View android:layout_width="48dp" android:layout_height="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<LinearLayout
|
||||||
|
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/colorTextSecondary" android:layout_marginBottom="4dp" />
|
||||||
|
<com.google.android.material.textfield.TextInputEditText android:id="@+id/et_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:minLines="5" android:hint="输入本周或当日工作要点、流水记录…" android:inputType="textMultiLine" android:layout_marginBottom="16dp" />
|
||||||
|
<com.google.android.material.button.MaterialButton android:id="@+id/btn_generate" android:layout_width="match_parent" android:layout_height="48dp" android:text="生成周报/日报" app:backgroundTint="@color/colorPrimary" android:textColor="@color/colorWhite" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
<ProgressBar android:id="@+id/progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" android:layout_marginBottom="12dp" />
|
||||||
|
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="生成结果" android:textSize="12sp" android:textColor="@color/colorTextSecondary" android:layout_marginBottom="8dp" />
|
||||||
|
<com.google.android.material.card.MaterialCardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardBackgroundColor="@color/colorCard" app:cardElevation="2dp" app:cardCornerRadius="12dp" android:minHeight="80dp" android:layout_marginBottom="12dp">
|
||||||
|
<ScrollView android:layout_width="match_parent" android:layout_height="wrap_content" android:fillViewport="true" android:padding="16dp">
|
||||||
|
<TextView android:id="@+id/tv_result" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14sp" android:lineSpacingMultiplier="1.2" android:textColor="@color/colorText" />
|
||||||
|
</ScrollView>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
<com.google.android.material.button.MaterialButton android:id="@+id/btn_copy" style="@style/Widget.MaterialComponents.Button.OutlinedButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/btn_copy" />
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
52
example/app/src/main/res/layout/item_ai_service.xml
Normal file
52
example/app/src/main/res/layout/item_ai_service.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
app:cardBackgroundColor="@color/colorCard"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/colorTextDark" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_badge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:background="@color/colorPrimary"
|
||||||
|
android:textColor="@color/colorWhite" />
|
||||||
|
</LinearLayout>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_subtitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@@ -36,6 +36,10 @@
|
|||||||
<string name="meal_planning_btn_generate">生成饭菜规划</string>
|
<string name="meal_planning_btn_generate">生成饭菜规划</string>
|
||||||
<string name="meal_planning_result_placeholder">填写左侧参数后点击「生成饭菜规划」</string>
|
<string name="meal_planning_result_placeholder">填写左侧参数后点击「生成饭菜规划」</string>
|
||||||
<string name="btn_meal_planning">饭菜规划</string>
|
<string name="btn_meal_planning">饭菜规划</string>
|
||||||
|
<string name="btn_ai_services">AI应用</string>
|
||||||
<string name="meal_planning_history_title">规划历史</string>
|
<string name="meal_planning_history_title">规划历史</string>
|
||||||
<string name="meal_planning_history_empty">暂无规划记录</string>
|
<string name="meal_planning_history_empty">暂无规划记录</string>
|
||||||
|
<string name="placeholder_coming_soon">敬请期待</string>
|
||||||
|
<string name="ai_services_title">AI 智能应用</string>
|
||||||
|
<string name="ai_services_subtitle">探索更多 AI 应用</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user