diff --git a/example/app/src/main/AndroidManifest.xml b/example/app/src/main/AndroidManifest.xml
index 70edeba..2ad4881 100644
--- a/example/app/src/main/AndroidManifest.xml
+++ b/example/app/src/main/AndroidManifest.xml
@@ -54,6 +54,26 @@
android:name=".MeetingMinutesActivity"
android:exported="false"
android:screenOrientation="portrait" />
+
+
+
+
+
finish());
+ if (binding.btnHistory != null) {
+ binding.btnHistory.setOnClickListener(v -> startActivity(new android.content.Intent(this, MeetingMinutesHistoryActivity.class)));
+ }
binding.btnGenerate.setOnClickListener(v -> doGenerate());
binding.btnCopy.setOnClickListener(v -> copyResult());
observeViewModel();
diff --git a/example/app/src/main/java/com/ruilaizi/example/MeetingMinutesHistoryActivity.java b/example/app/src/main/java/com/ruilaizi/example/MeetingMinutesHistoryActivity.java
new file mode 100644
index 0000000..c1878a0
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/MeetingMinutesHistoryActivity.java
@@ -0,0 +1,39 @@
+package com.ruilaizi.example;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ruilaizi.example.adapter.MeetingMinutesHistoryAdapter;
+import com.ruilaizi.example.data.meeting.MeetingMinutesRepository;
+import com.ruilaizi.example.databinding.ActivityMeetingMinutesHistoryBinding;
+
+public class MeetingMinutesHistoryActivity extends AppCompatActivity {
+
+ private ActivityMeetingMinutesHistoryBinding binding;
+ private MeetingMinutesHistoryAdapter adapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityMeetingMinutesHistoryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ adapter = new MeetingMinutesHistoryAdapter();
+ adapter.setOnUseListener(content -> {
+ setResult(RESULT_OK);
+ finish();
+ });
+ binding.recycler.setLayoutManager(new LinearLayoutManager(this));
+ binding.recycler.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ MeetingMinutesRepository repo = new MeetingMinutesRepository(getApplication());
+ repo.getRecentRecords().observe(this, list -> {
+ adapter.setData(list);
+ binding.emptyText.setVisibility(list == null || list.isEmpty() ? View.VISIBLE : View.GONE);
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/PoetryActivity.java b/example/app/src/main/java/com/ruilaizi/example/PoetryActivity.java
new file mode 100644
index 0000000..3b9e9aa
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/PoetryActivity.java
@@ -0,0 +1,89 @@
+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.ActivityPoetryBinding;
+import com.ruilaizi.example.ui.PoetryViewModel;
+
+public class PoetryActivity extends AppCompatActivity {
+
+ private ActivityPoetryBinding binding;
+ private PoetryViewModel viewModel;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityPoetryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(PoetryViewModel.class);
+
+ binding.btnBack.setOnClickListener(v -> finish());
+ if (binding.btnHistory != null) {
+ binding.btnHistory.setOnClickListener(v -> startActivity(new android.content.Intent(this, PoetryHistoryActivity.class)));
+ }
+ binding.btnGenerate.setOnClickListener(v -> doGenerate());
+ binding.btnCopy.setOnClickListener(v -> copyResult());
+ observeViewModel();
+ }
+
+ private void doGenerate() {
+ String title = getText(binding.etPoetryTitle);
+ if (title.isEmpty()) {
+ Toast.makeText(this, "请输入诗词标题", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ viewModel.generate(title, getText(binding.etAuthor), getText(binding.etDynasty), getText(binding.etExtra));
+ }
+
+ 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();
+ }
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/PoetryHistoryActivity.java b/example/app/src/main/java/com/ruilaizi/example/PoetryHistoryActivity.java
new file mode 100644
index 0000000..def2f3a
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/PoetryHistoryActivity.java
@@ -0,0 +1,38 @@
+package com.ruilaizi.example;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.ruilaizi.example.adapter.PoetryHistoryAdapter;
+import com.ruilaizi.example.data.poetry.PoetryRepository;
+import com.ruilaizi.example.databinding.ActivityPoetryHistoryBinding;
+
+public class PoetryHistoryActivity extends AppCompatActivity {
+
+ private ActivityPoetryHistoryBinding binding;
+ private PoetryHistoryAdapter adapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityPoetryHistoryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ adapter = new PoetryHistoryAdapter();
+ adapter.setOnUseListener(content -> {
+ setResult(RESULT_OK);
+ finish();
+ });
+ binding.recycler.setLayoutManager(new LinearLayoutManager(this));
+ binding.recycler.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ PoetryRepository repo = new PoetryRepository(getApplication());
+ repo.getRecentRecords().observe(this, list -> {
+ adapter.setData(list);
+ binding.emptyText.setVisibility(list == null || list.isEmpty() ? View.VISIBLE : View.GONE);
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationActivity.java b/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationActivity.java
new file mode 100644
index 0000000..379b1ca
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationActivity.java
@@ -0,0 +1,93 @@
+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.ArrayAdapter;
+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.ActivityResumeOptimizationBinding;
+import com.ruilaizi.example.ui.ResumeOptimizationViewModel;
+
+public class ResumeOptimizationActivity extends AppCompatActivity {
+
+ private ActivityResumeOptimizationBinding binding;
+ private ResumeOptimizationViewModel viewModel;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityResumeOptimizationBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(ResumeOptimizationViewModel.class);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.resume_opt_types, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ binding.spinnerType.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ if (binding.btnHistory != null) {
+ binding.btnHistory.setOnClickListener(v -> startActivity(new android.content.Intent(this, ResumeOptimizationHistoryActivity.class)));
+ }
+ binding.btnGenerate.setOnClickListener(v -> doGenerate());
+ binding.btnCopy.setOnClickListener(v -> copyResult());
+ observeViewModel();
+ }
+
+ private void doGenerate() {
+ String original = getText(binding.etOriginal);
+ if (original.isEmpty()) {
+ Toast.makeText(this, "请填写简历内容或求职信要点", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String optType = binding.spinnerType.getSelectedItemPosition() == 0 ? "resume" : "cover_letter";
+ viewModel.generate(optType, original, getText(binding.etJobDesc));
+ }
+
+ 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();
+ }
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationHistoryActivity.java b/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationHistoryActivity.java
new file mode 100644
index 0000000..2672754
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationHistoryActivity.java
@@ -0,0 +1,38 @@
+package com.ruilaizi.example;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.ruilaizi.example.adapter.ResumeOptimizationHistoryAdapter;
+import com.ruilaizi.example.data.resume.ResumeOptimizationRepository;
+import com.ruilaizi.example.databinding.ActivityResumeOptimizationHistoryBinding;
+
+public class ResumeOptimizationHistoryActivity extends AppCompatActivity {
+
+ private ActivityResumeOptimizationHistoryBinding binding;
+ private ResumeOptimizationHistoryAdapter adapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityResumeOptimizationHistoryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ adapter = new ResumeOptimizationHistoryAdapter();
+ adapter.setOnUseListener(content -> {
+ setResult(RESULT_OK);
+ finish();
+ });
+ binding.recycler.setLayoutManager(new LinearLayoutManager(this));
+ binding.recycler.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ ResumeOptimizationRepository repo = new ResumeOptimizationRepository(getApplication());
+ repo.getRecentRecords().observe(this, list -> {
+ adapter.setData(list);
+ binding.emptyText.setVisibility(list == null || list.isEmpty() ? View.VISIBLE : View.GONE);
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/TravelPlanningActivity.java b/example/app/src/main/java/com/ruilaizi/example/TravelPlanningActivity.java
index 2049e2f..3efaccc 100644
--- a/example/app/src/main/java/com/ruilaizi/example/TravelPlanningActivity.java
+++ b/example/app/src/main/java/com/ruilaizi/example/TravelPlanningActivity.java
@@ -24,6 +24,9 @@ public class TravelPlanningActivity extends AppCompatActivity {
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(TravelPlanningViewModel.class);
binding.btnBack.setOnClickListener(v -> finish());
+ if (binding.btnHistory != null) {
+ binding.btnHistory.setOnClickListener(v -> startActivity(new android.content.Intent(this, TravelPlanningHistoryActivity.class)));
+ }
binding.btnGenerate.setOnClickListener(v -> doGenerate());
binding.btnCopy.setOnClickListener(v -> copyResult());
observeViewModel();
diff --git a/example/app/src/main/java/com/ruilaizi/example/TravelPlanningHistoryActivity.java b/example/app/src/main/java/com/ruilaizi/example/TravelPlanningHistoryActivity.java
new file mode 100644
index 0000000..222a1bf
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/TravelPlanningHistoryActivity.java
@@ -0,0 +1,39 @@
+package com.ruilaizi.example;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ruilaizi.example.adapter.TravelHistoryAdapter;
+import com.ruilaizi.example.data.travel.TravelPlanningRepository;
+import com.ruilaizi.example.databinding.ActivityTravelPlanningHistoryBinding;
+
+public class TravelPlanningHistoryActivity extends AppCompatActivity {
+
+ private ActivityTravelPlanningHistoryBinding binding;
+ private TravelHistoryAdapter adapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityTravelPlanningHistoryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ adapter = new TravelHistoryAdapter();
+ adapter.setOnUseListener(content -> {
+ setResult(RESULT_OK);
+ finish();
+ });
+ binding.recycler.setLayoutManager(new LinearLayoutManager(this));
+ binding.recycler.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ TravelPlanningRepository repo = new TravelPlanningRepository(getApplication());
+ repo.getRecentRecords().observe(this, list -> {
+ adapter.setData(list);
+ binding.emptyText.setVisibility(list == null || list.isEmpty() ? View.VISIBLE : View.GONE);
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/WeeklyReportActivity.java b/example/app/src/main/java/com/ruilaizi/example/WeeklyReportActivity.java
index 83070b9..9b41d66 100644
--- a/example/app/src/main/java/com/ruilaizi/example/WeeklyReportActivity.java
+++ b/example/app/src/main/java/com/ruilaizi/example/WeeklyReportActivity.java
@@ -29,6 +29,9 @@ public class WeeklyReportActivity extends AppCompatActivity {
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(WeeklyReportViewModel.class);
binding.btnBack.setOnClickListener(v -> finish());
+ if (binding.btnHistory != null) {
+ binding.btnHistory.setOnClickListener(v -> startActivity(new android.content.Intent(this, WeeklyReportHistoryActivity.class)));
+ }
binding.btnGenerate.setOnClickListener(v -> doGenerate());
binding.btnCopy.setOnClickListener(v -> copyResult());
observeViewModel();
diff --git a/example/app/src/main/java/com/ruilaizi/example/WeeklyReportHistoryActivity.java b/example/app/src/main/java/com/ruilaizi/example/WeeklyReportHistoryActivity.java
new file mode 100644
index 0000000..c820a55
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/WeeklyReportHistoryActivity.java
@@ -0,0 +1,39 @@
+package com.ruilaizi.example;
+
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ruilaizi.example.adapter.WeeklyReportHistoryAdapter;
+import com.ruilaizi.example.data.weekly.WeeklyReportRepository;
+import com.ruilaizi.example.databinding.ActivityWeeklyReportHistoryBinding;
+
+public class WeeklyReportHistoryActivity extends AppCompatActivity {
+
+ private ActivityWeeklyReportHistoryBinding binding;
+ private WeeklyReportHistoryAdapter adapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding = ActivityWeeklyReportHistoryBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ adapter = new WeeklyReportHistoryAdapter();
+ adapter.setOnUseListener(content -> {
+ setResult(RESULT_OK);
+ finish();
+ });
+ binding.recycler.setLayoutManager(new LinearLayoutManager(this));
+ binding.recycler.setAdapter(adapter);
+ binding.btnBack.setOnClickListener(v -> finish());
+ WeeklyReportRepository repo = new WeeklyReportRepository(getApplication());
+ repo.getRecentRecords().observe(this, list -> {
+ adapter.setData(list);
+ binding.emptyText.setVisibility(list == null || list.isEmpty() ? View.VISIBLE : View.GONE);
+ });
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/adapter/MeetingMinutesHistoryAdapter.java b/example/app/src/main/java/com/ruilaizi/example/adapter/MeetingMinutesHistoryAdapter.java
new file mode 100644
index 0000000..8f25a50
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/adapter/MeetingMinutesHistoryAdapter.java
@@ -0,0 +1,95 @@
+package com.ruilaizi.example.adapter;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.button.MaterialButton;
+import com.ruilaizi.example.R;
+import com.ruilaizi.example.data.db.MeetingMinutesRecord;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class MeetingMinutesHistoryAdapter extends RecyclerView.Adapter {
+
+ private final List list = new ArrayList<>();
+ private OnUseListener onUseListener;
+
+ public interface OnUseListener {
+ void onUse(String summaryContent);
+ }
+
+ public void setOnUseListener(OnUseListener listener) {
+ this.onUseListener = listener;
+ }
+
+ public void setData(List data) {
+ list.clear();
+ if (data != null) list.addAll(data);
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_meeting_minutes_history, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ MeetingMinutesRecord r = list.get(position);
+ holder.tvSummary.setText(r.title != null && !r.title.isEmpty() ? r.title : "会议纪要");
+ holder.tvContentPreview.setText(truncate(r.summaryContent, 80));
+ holder.tvTime.setText(formatTime(r.createdAt));
+ holder.btnUse.setOnClickListener(v -> {
+ if (onUseListener != null && r.summaryContent != null) onUseListener.onUse(r.summaryContent);
+ });
+ holder.btnCopy.setOnClickListener(v -> {
+ if (r.summaryContent != null && !r.summaryContent.isEmpty()) {
+ ClipboardManager cm = (ClipboardManager) holder.itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) cm.setPrimaryClip(ClipData.newPlainText("会议纪要", r.summaryContent));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ private static String truncate(String s, int maxLen) {
+ if (s == null) return "";
+ s = s.trim();
+ return s.length() <= maxLen ? s : s.substring(0, maxLen) + "…";
+ }
+ private static String formatTime(long timestamp) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date(timestamp));
+ } catch (Exception e) { return ""; }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSummary, tvContentPreview, tvTime;
+ MaterialButton btnUse, btnCopy;
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvSummary = itemView.findViewById(R.id.tv_summary);
+ tvContentPreview = itemView.findViewById(R.id.tv_content_preview);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ btnUse = itemView.findViewById(R.id.btn_use);
+ btnCopy = itemView.findViewById(R.id.btn_copy);
+ }
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/adapter/PoetryHistoryAdapter.java b/example/app/src/main/java/com/ruilaizi/example/adapter/PoetryHistoryAdapter.java
new file mode 100644
index 0000000..bb95233
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/adapter/PoetryHistoryAdapter.java
@@ -0,0 +1,95 @@
+package com.ruilaizi.example.adapter;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.button.MaterialButton;
+import com.ruilaizi.example.R;
+import com.ruilaizi.example.data.db.PoetryRecord;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class PoetryHistoryAdapter extends RecyclerView.Adapter {
+
+ private final List list = new ArrayList<>();
+ private OnUseListener onUseListener;
+
+ public interface OnUseListener {
+ void onUse(String summaryContent);
+ }
+
+ public void setOnUseListener(OnUseListener listener) {
+ this.onUseListener = listener;
+ }
+
+ public void setData(List data) {
+ list.clear();
+ if (data != null) list.addAll(data);
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_poetry_history, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ PoetryRecord r = list.get(position);
+ holder.tvSummary.setText(r.poetryTitle != null ? r.poetryTitle : "古诗词");
+ holder.tvContentPreview.setText(truncate(r.analysisContent, 80));
+ holder.tvTime.setText(formatTime(r.createdAt));
+ holder.btnUse.setOnClickListener(v -> {
+ if (onUseListener != null && r.analysisContent != null) onUseListener.onUse(r.analysisContent);
+ });
+ holder.btnCopy.setOnClickListener(v -> {
+ if (r.analysisContent != null && !r.analysisContent.isEmpty()) {
+ ClipboardManager cm = (ClipboardManager) holder.itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) cm.setPrimaryClip(ClipData.newPlainText("古诗词解析", r.analysisContent));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ private static String truncate(String s, int maxLen) {
+ if (s == null) return "";
+ s = s.trim();
+ return s.length() <= maxLen ? s : s.substring(0, maxLen) + "…";
+ }
+ private static String formatTime(long timestamp) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date(timestamp));
+ } catch (Exception e) { return ""; }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSummary, tvContentPreview, tvTime;
+ MaterialButton btnUse, btnCopy;
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvSummary = itemView.findViewById(R.id.tv_summary);
+ tvContentPreview = itemView.findViewById(R.id.tv_content_preview);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ btnUse = itemView.findViewById(R.id.btn_use);
+ btnCopy = itemView.findViewById(R.id.btn_copy);
+ }
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/adapter/ResumeOptimizationHistoryAdapter.java b/example/app/src/main/java/com/ruilaizi/example/adapter/ResumeOptimizationHistoryAdapter.java
new file mode 100644
index 0000000..e7e683f
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/adapter/ResumeOptimizationHistoryAdapter.java
@@ -0,0 +1,95 @@
+package com.ruilaizi.example.adapter;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.button.MaterialButton;
+import com.ruilaizi.example.R;
+import com.ruilaizi.example.data.db.ResumeOptimizationRecord;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class ResumeOptimizationHistoryAdapter extends RecyclerView.Adapter {
+
+ private final List list = new ArrayList<>();
+ private OnUseListener onUseListener;
+
+ public interface OnUseListener {
+ void onUse(String summaryContent);
+ }
+
+ public void setOnUseListener(OnUseListener listener) {
+ this.onUseListener = listener;
+ }
+
+ public void setData(List data) {
+ list.clear();
+ if (data != null) list.addAll(data);
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_resume_history, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ ResumeOptimizationRecord r = list.get(position);
+ holder.tvSummary.setText("cover_letter".equals(r.optType) ? "求职信" : "简历优化");
+ holder.tvContentPreview.setText(truncate(r.optimizedContent, 80));
+ holder.tvTime.setText(formatTime(r.createdAt));
+ holder.btnUse.setOnClickListener(v -> {
+ if (onUseListener != null && r.optimizedContent != null) onUseListener.onUse(r.optimizedContent);
+ });
+ holder.btnCopy.setOnClickListener(v -> {
+ if (r.optimizedContent != null && !r.optimizedContent.isEmpty()) {
+ ClipboardManager cm = (ClipboardManager) holder.itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) cm.setPrimaryClip(ClipData.newPlainText("简历优化", r.optimizedContent));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ private static String truncate(String s, int maxLen) {
+ if (s == null) return "";
+ s = s.trim();
+ return s.length() <= maxLen ? s : s.substring(0, maxLen) + "…";
+ }
+ private static String formatTime(long timestamp) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date(timestamp));
+ } catch (Exception e) { return ""; }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSummary, tvContentPreview, tvTime;
+ MaterialButton btnUse, btnCopy;
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvSummary = itemView.findViewById(R.id.tv_summary);
+ tvContentPreview = itemView.findViewById(R.id.tv_content_preview);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ btnUse = itemView.findViewById(R.id.btn_use);
+ btnCopy = itemView.findViewById(R.id.btn_copy);
+ }
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/adapter/TravelHistoryAdapter.java b/example/app/src/main/java/com/ruilaizi/example/adapter/TravelHistoryAdapter.java
new file mode 100644
index 0000000..f2287eb
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/adapter/TravelHistoryAdapter.java
@@ -0,0 +1,107 @@
+package com.ruilaizi.example.adapter;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.button.MaterialButton;
+import com.ruilaizi.example.R;
+import com.ruilaizi.example.data.db.TravelPlanRecord;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class TravelHistoryAdapter extends RecyclerView.Adapter {
+
+ private final List list = new ArrayList<>();
+ private OnUseListener onUseListener;
+
+ public interface OnUseListener {
+ void onUse(String planContent);
+ }
+
+ public void setOnUseListener(OnUseListener listener) {
+ this.onUseListener = listener;
+ }
+
+ public void setData(List data) {
+ list.clear();
+ if (data != null) {
+ list.addAll(data);
+ }
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_travel_history, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ TravelPlanRecord r = list.get(position);
+ String summary = safe(r.destination) + " | " + safe(r.days) + "天 | " + safe(r.people) + "人";
+ holder.tvSummary.setText(summary);
+ holder.tvContentPreview.setText(truncate(r.planContent, 80));
+ holder.tvTime.setText(formatTime(r.createdAt));
+ holder.btnUse.setOnClickListener(v -> {
+ if (onUseListener != null && r.planContent != null) onUseListener.onUse(r.planContent);
+ });
+ holder.btnCopy.setOnClickListener(v -> {
+ if (r.planContent != null && !r.planContent.isEmpty()) {
+ ClipboardManager cm = (ClipboardManager) holder.itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) cm.setPrimaryClip(ClipData.newPlainText("旅行攻略", r.planContent));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ private static String safe(String s) {
+ return s != null ? s : "";
+ }
+
+ private static String truncate(String s, int maxLen) {
+ if (s == null) return "";
+ s = s.trim();
+ if (s.length() <= maxLen) return s;
+ return s.substring(0, maxLen) + "…";
+ }
+
+ private static String formatTime(long timestamp) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date(timestamp));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSummary, tvContentPreview, tvTime;
+ MaterialButton btnUse, btnCopy;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvSummary = itemView.findViewById(R.id.tv_summary);
+ tvContentPreview = itemView.findViewById(R.id.tv_content_preview);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ btnUse = itemView.findViewById(R.id.btn_use);
+ btnCopy = itemView.findViewById(R.id.btn_copy);
+ }
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/adapter/WeeklyReportHistoryAdapter.java b/example/app/src/main/java/com/ruilaizi/example/adapter/WeeklyReportHistoryAdapter.java
new file mode 100644
index 0000000..57dd290
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/adapter/WeeklyReportHistoryAdapter.java
@@ -0,0 +1,107 @@
+package com.ruilaizi.example.adapter;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.button.MaterialButton;
+import com.ruilaizi.example.R;
+import com.ruilaizi.example.data.db.WeeklyReportRecord;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class WeeklyReportHistoryAdapter extends RecyclerView.Adapter {
+
+ private final List list = new ArrayList<>();
+ private OnUseListener onUseListener;
+
+ public interface OnUseListener {
+ void onUse(String reportContent);
+ }
+
+ public void setOnUseListener(OnUseListener listener) {
+ this.onUseListener = listener;
+ }
+
+ public void setData(List data) {
+ list.clear();
+ if (data != null) {
+ list.addAll(data);
+ }
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_weekly_report_history, parent, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ WeeklyReportRecord r = list.get(position);
+ String summary = "weekly".equals(r.reportType) ? "周报" : "日报";
+ holder.tvSummary.setText(summary);
+ holder.tvContentPreview.setText(truncate(r.reportContent, 80));
+ holder.tvTime.setText(formatTime(r.createdAt));
+ holder.btnUse.setOnClickListener(v -> {
+ if (onUseListener != null && r.reportContent != null) onUseListener.onUse(r.reportContent);
+ });
+ holder.btnCopy.setOnClickListener(v -> {
+ if (r.reportContent != null && !r.reportContent.isEmpty()) {
+ ClipboardManager cm = (ClipboardManager) holder.itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) cm.setPrimaryClip(ClipData.newPlainText("周报", r.reportContent));
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ private static String safe(String s) {
+ return s != null ? s : "";
+ }
+
+ private static String truncate(String s, int maxLen) {
+ if (s == null) return "";
+ s = s.trim();
+ if (s.length() <= maxLen) return s;
+ return s.substring(0, maxLen) + "…";
+ }
+
+ private static String formatTime(long timestamp) {
+ try {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date(timestamp));
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSummary, tvContentPreview, tvTime;
+ MaterialButton btnUse, btnCopy;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+ tvSummary = itemView.findViewById(R.id.tv_summary);
+ tvContentPreview = itemView.findViewById(R.id.tv_content_preview);
+ tvTime = itemView.findViewById(R.id.tv_time);
+ btnUse = itemView.findViewById(R.id.btn_use);
+ btnCopy = itemView.findViewById(R.id.btn_copy);
+ }
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/AppDatabase.java b/example/app/src/main/java/com/ruilaizi/example/data/db/AppDatabase.java
index e6064a6..5078ad7 100644
--- a/example/app/src/main/java/com/ruilaizi/example/data/db/AppDatabase.java
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/AppDatabase.java
@@ -6,7 +6,9 @@ import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
-@Database(entities = {GenerationRecord.class, OptimizeRecord.class, DirectAnswerRecord.class, MealPlanRecord.class}, version = 4, exportSchema = false)
+@Database(entities = {GenerationRecord.class, OptimizeRecord.class, DirectAnswerRecord.class, MealPlanRecord.class,
+ TravelPlanRecord.class, WeeklyReportRecord.class, MeetingMinutesRecord.class,
+ PoetryRecord.class, ResumeOptimizationRecord.class}, version = 6, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static volatile AppDatabase INSTANCE;
@@ -30,4 +32,9 @@ public abstract class AppDatabase extends RoomDatabase {
public abstract OptimizeRecordDao optimizeRecordDao();
public abstract DirectAnswerRecordDao directAnswerRecordDao();
public abstract MealPlanRecordDao mealPlanRecordDao();
+ public abstract TravelPlanRecordDao travelPlanRecordDao();
+ public abstract WeeklyReportRecordDao weeklyReportRecordDao();
+ public abstract MeetingMinutesRecordDao meetingMinutesRecordDao();
+ public abstract PoetryRecordDao poetryRecordDao();
+ public abstract ResumeOptimizationRecordDao resumeOptimizationRecordDao();
}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecord.java b/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecord.java
new file mode 100644
index 0000000..f00855a
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecord.java
@@ -0,0 +1,25 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/** 会议纪要历史记录 */
+@Entity(tableName = "meeting_minutes_records")
+public class MeetingMinutesRecord {
+
+ @PrimaryKey(autoGenerate = true)
+ public long id;
+ public String title;
+ public String rawContent;
+ public String summaryContent;
+ public long createdAt;
+
+ public MeetingMinutesRecord() {}
+
+ public MeetingMinutesRecord(String title, String rawContent, String summaryContent) {
+ this.title = title;
+ this.rawContent = rawContent;
+ this.summaryContent = summaryContent;
+ this.createdAt = System.currentTimeMillis();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecordDao.java b/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecordDao.java
new file mode 100644
index 0000000..afee124
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecordDao.java
@@ -0,0 +1,21 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface MeetingMinutesRecordDao {
+
+ @Insert
+ long insert(MeetingMinutesRecord record);
+
+ @Query("SELECT * FROM meeting_minutes_records ORDER BY createdAt DESC LIMIT 50")
+ LiveData> getRecentRecords();
+
+ @Query("DELETE FROM meeting_minutes_records WHERE id NOT IN (SELECT id FROM meeting_minutes_records ORDER BY createdAt DESC LIMIT 50)")
+ void keepOnlyRecent();
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecord.java b/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecord.java
new file mode 100644
index 0000000..34ff723
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecord.java
@@ -0,0 +1,26 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+@Entity(tableName = "poetry_records")
+public class PoetryRecord {
+
+ @PrimaryKey(autoGenerate = true)
+ public long id;
+ public String poetryTitle;
+ public String author;
+ public String dynasty;
+ public String analysisContent;
+ public long createdAt;
+
+ public PoetryRecord() {}
+
+ public PoetryRecord(String poetryTitle, String author, String dynasty, String analysisContent) {
+ this.poetryTitle = poetryTitle;
+ this.author = author;
+ this.dynasty = dynasty;
+ this.analysisContent = analysisContent;
+ this.createdAt = System.currentTimeMillis();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecordDao.java b/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecordDao.java
new file mode 100644
index 0000000..53538dd
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecordDao.java
@@ -0,0 +1,21 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface PoetryRecordDao {
+
+ @Insert
+ long insert(PoetryRecord record);
+
+ @Query("SELECT * FROM poetry_records ORDER BY createdAt DESC LIMIT 50")
+ LiveData> getRecentRecords();
+
+ @Query("DELETE FROM poetry_records WHERE id NOT IN (SELECT id FROM poetry_records ORDER BY createdAt DESC LIMIT 50)")
+ void keepOnlyRecent();
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecord.java b/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecord.java
new file mode 100644
index 0000000..4cd3963
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecord.java
@@ -0,0 +1,27 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/** 简历/求职信优化历史记录 */
+@Entity(tableName = "resume_optimization_records")
+public class ResumeOptimizationRecord {
+
+ @PrimaryKey(autoGenerate = true)
+ public long id;
+ public String optType; // resume | cover_letter
+ public String originalContent;
+ public String jobDescription;
+ public String optimizedContent;
+ public long createdAt;
+
+ public ResumeOptimizationRecord() {}
+
+ public ResumeOptimizationRecord(String optType, String originalContent, String jobDescription, String optimizedContent) {
+ this.optType = optType;
+ this.originalContent = originalContent;
+ this.jobDescription = jobDescription;
+ this.optimizedContent = optimizedContent;
+ this.createdAt = System.currentTimeMillis();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecordDao.java b/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecordDao.java
new file mode 100644
index 0000000..521c9fb
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecordDao.java
@@ -0,0 +1,21 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface ResumeOptimizationRecordDao {
+
+ @Insert
+ long insert(ResumeOptimizationRecord record);
+
+ @Query("SELECT * FROM resume_optimization_records ORDER BY createdAt DESC LIMIT 50")
+ LiveData> getRecentRecords();
+
+ @Query("DELETE FROM resume_optimization_records WHERE id NOT IN (SELECT id FROM resume_optimization_records ORDER BY createdAt DESC LIMIT 50)")
+ void keepOnlyRecent();
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecord.java b/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecord.java
new file mode 100644
index 0000000..fd4af78
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecord.java
@@ -0,0 +1,31 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/** 旅行攻略历史记录 */
+@Entity(tableName = "travel_plan_records")
+public class TravelPlanRecord {
+
+ @PrimaryKey(autoGenerate = true)
+ public long id;
+ public String destination;
+ public String days;
+ public String people;
+ public String preferences;
+ public String budget;
+ public String planContent;
+ public long createdAt;
+
+ public TravelPlanRecord() {}
+
+ public TravelPlanRecord(String destination, String days, String people, String preferences, String budget, String planContent) {
+ this.destination = destination;
+ this.days = days;
+ this.people = people;
+ this.preferences = preferences;
+ this.budget = budget;
+ this.planContent = planContent;
+ this.createdAt = System.currentTimeMillis();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecordDao.java b/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecordDao.java
new file mode 100644
index 0000000..babbc22
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecordDao.java
@@ -0,0 +1,21 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface TravelPlanRecordDao {
+
+ @Insert
+ long insert(TravelPlanRecord record);
+
+ @Query("SELECT * FROM travel_plan_records ORDER BY createdAt DESC LIMIT 50")
+ LiveData> getRecentRecords();
+
+ @Query("DELETE FROM travel_plan_records WHERE id NOT IN (SELECT id FROM travel_plan_records ORDER BY createdAt DESC LIMIT 50)")
+ void keepOnlyRecent();
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecord.java b/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecord.java
new file mode 100644
index 0000000..4d96b7a
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecord.java
@@ -0,0 +1,25 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/** 周报/日报历史记录 */
+@Entity(tableName = "weekly_report_records")
+public class WeeklyReportRecord {
+
+ @PrimaryKey(autoGenerate = true)
+ public long id;
+ public String reportType; // weekly | daily
+ public String inputContent;
+ public String reportContent;
+ public long createdAt;
+
+ public WeeklyReportRecord() {}
+
+ public WeeklyReportRecord(String reportType, String inputContent, String reportContent) {
+ this.reportType = reportType;
+ this.inputContent = inputContent;
+ this.reportContent = reportContent;
+ this.createdAt = System.currentTimeMillis();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecordDao.java b/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecordDao.java
new file mode 100644
index 0000000..f0832d4
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecordDao.java
@@ -0,0 +1,21 @@
+package com.ruilaizi.example.data.db;
+
+import androidx.lifecycle.LiveData;
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface WeeklyReportRecordDao {
+
+ @Insert
+ long insert(WeeklyReportRecord record);
+
+ @Query("SELECT * FROM weekly_report_records ORDER BY createdAt DESC LIMIT 50")
+ LiveData> getRecentRecords();
+
+ @Query("DELETE FROM weekly_report_records WHERE id NOT IN (SELECT id FROM weekly_report_records ORDER BY createdAt DESC LIMIT 50)")
+ void keepOnlyRecent();
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/meeting/MeetingMinutesRepository.java b/example/app/src/main/java/com/ruilaizi/example/data/meeting/MeetingMinutesRepository.java
index 2e2fb80..c110253 100644
--- a/example/app/src/main/java/com/ruilaizi/example/data/meeting/MeetingMinutesRepository.java
+++ b/example/app/src/main/java/com/ruilaizi/example/data/meeting/MeetingMinutesRepository.java
@@ -2,19 +2,39 @@ package com.ruilaizi.example.data.meeting;
import android.content.Context;
+import androidx.lifecycle.LiveData;
+
import com.ruilaizi.example.data.api.ApiKeyProvider;
import com.ruilaizi.example.data.api.OpenAICompletionService;
+import com.ruilaizi.example.data.db.AppDatabase;
+import com.ruilaizi.example.data.db.MeetingMinutesRecord;
+import com.ruilaizi.example.data.db.MeetingMinutesRecordDao;
+
+import java.util.List;
public class MeetingMinutesRepository {
private final MeetingMinutesGenerator generator;
+ private final MeetingMinutesRecordDao dao;
public MeetingMinutesRepository(Context context) {
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
generator = new MeetingMinutesGenerator(new OpenAICompletionService(null), apiKeyProvider);
+ dao = AppDatabase.getInstance(context).meetingMinutesRecordDao();
}
public String generate(String rawContent, String title) throws Exception {
return generator.generate(rawContent, title);
}
+
+ public void saveRecord(MeetingMinutesRecord record) {
+ new Thread(() -> {
+ dao.insert(record);
+ dao.keepOnlyRecent();
+ }).start();
+ }
+
+ public LiveData> getRecentRecords() {
+ return dao.getRecentRecords();
+ }
}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryGenerator.java b/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryGenerator.java
new file mode 100644
index 0000000..99b7596
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryGenerator.java
@@ -0,0 +1,26 @@
+package com.ruilaizi.example.data.poetry;
+
+import com.ruilaizi.example.data.api.ApiKeyProvider;
+import com.ruilaizi.example.data.api.OpenAICompletionService;
+
+public class PoetryGenerator {
+
+ private final OpenAICompletionService completionService;
+ private final ApiKeyProvider apiKeyProvider;
+
+ public PoetryGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
+ this.completionService = completionService;
+ this.apiKeyProvider = apiKeyProvider;
+ }
+
+ public String generate(String poetryTitle, String author, String dynasty, String extraRequirements) throws Exception {
+ String apiKey = apiKeyProvider.getApiKey();
+ if (apiKey == null || apiKey.isEmpty()) throw new Exception("请先在设置中配置 API Key");
+ if (poetryTitle == null || poetryTitle.trim().isEmpty()) throw new Exception("请输入诗词标题");
+ String systemPrompt = "你是一位专业的古典文学专家和古诗词翻译家。请根据用户提供的古诗词信息,提供完整的原文、现代译文、详细注释和深度解读。使用 Markdown 格式输出,标题用 # ## ###,重要内容用粗体。";
+ String userPrompt = "诗词标题:" + (poetryTitle != null ? poetryTitle.trim() : "") + "\n作者:" + (author != null ? author : "") + "\n朝代:" + (dynasty != null ? dynasty : "");
+ if (extraRequirements != null && !extraRequirements.isEmpty()) userPrompt += "\n其他要求:" + extraRequirements;
+ userPrompt += "\n\n请提供完整的古诗词解析。";
+ return completionService.chat(systemPrompt, userPrompt, 0.7f, 2000, apiKey);
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryRepository.java b/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryRepository.java
new file mode 100644
index 0000000..a57854d
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryRepository.java
@@ -0,0 +1,40 @@
+package com.ruilaizi.example.data.poetry;
+
+import android.content.Context;
+
+import androidx.lifecycle.LiveData;
+
+import com.ruilaizi.example.data.api.ApiKeyProvider;
+import com.ruilaizi.example.data.api.OpenAICompletionService;
+import com.ruilaizi.example.data.db.AppDatabase;
+import com.ruilaizi.example.data.db.PoetryRecord;
+import com.ruilaizi.example.data.db.PoetryRecordDao;
+
+import java.util.List;
+
+public class PoetryRepository {
+
+ private final PoetryGenerator generator;
+ private final PoetryRecordDao dao;
+
+ public PoetryRepository(Context context) {
+ ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
+ generator = new PoetryGenerator(new OpenAICompletionService(null), apiKeyProvider);
+ dao = AppDatabase.getInstance(context).poetryRecordDao();
+ }
+
+ public String generate(String poetryTitle, String author, String dynasty, String extraRequirements) throws Exception {
+ return generator.generate(poetryTitle, author, dynasty, extraRequirements);
+ }
+
+ public void saveRecord(PoetryRecord record) {
+ new Thread(() -> {
+ dao.insert(record);
+ dao.keepOnlyRecent();
+ }).start();
+ }
+
+ public LiveData> getRecentRecords() {
+ return dao.getRecentRecords();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationGenerator.java b/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationGenerator.java
new file mode 100644
index 0000000..cacf422
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationGenerator.java
@@ -0,0 +1,34 @@
+package com.ruilaizi.example.data.resume;
+
+import com.ruilaizi.example.data.api.ApiKeyProvider;
+import com.ruilaizi.example.data.api.OpenAICompletionService;
+
+/**
+ * 简历/求职信优化生成器,与 Flask resume_optimization 逻辑一致。
+ */
+public class ResumeOptimizationGenerator {
+
+ private final OpenAICompletionService completionService;
+ private final ApiKeyProvider apiKeyProvider;
+
+ public ResumeOptimizationGenerator(OpenAICompletionService completionService, ApiKeyProvider apiKeyProvider) {
+ this.completionService = completionService;
+ this.apiKeyProvider = apiKeyProvider;
+ }
+
+ public String generate(String optType, String originalContent, String jobDescription) throws Exception {
+ String apiKey = apiKeyProvider.getApiKey();
+ if (apiKey == null || apiKey.isEmpty()) throw new Exception("请先在设置中配置 API Key");
+ if (originalContent == null || originalContent.trim().isEmpty()) throw new Exception("请填写简历内容或求职信要点");
+ String systemPrompt;
+ String userPrompt;
+ if ("cover_letter".equals(optType)) {
+ systemPrompt = "你是一位专业的求职顾问,擅长根据简历和岗位描述撰写针对性的求职信。要求:根据用户提供的简历要点和岗位描述(如有),写一封简洁、得体的求职信。结构建议:开头称呼与应聘意向、与岗位匹配的经历与能力、结尾表达意愿与感谢。语气专业、诚恳,篇幅适中(一般 300~500 字)。";
+ userPrompt = "请根据以下内容撰写求职信:\n\n【简历/个人要点】\n" + originalContent.trim() + "\n\n" + (jobDescription != null && !jobDescription.isEmpty() ? "【岗位描述】\n" + jobDescription + "\n" : "");
+ } else {
+ systemPrompt = "你是一位专业的简历优化师,擅长在保持真实的前提下提升简历的呈现效果。要求:根据用户提供的简历内容进行优化:润色表述、突出成果与关键词、调整结构层次。若用户提供了岗位描述,请使经历与能力描述更贴合该岗位。输出使用 Markdown 格式,保持条理清晰。不要编造经历或数据,仅做表述优化。";
+ userPrompt = "请优化以下简历内容:\n\n" + originalContent.trim() + "\n\n" + (jobDescription != null && !jobDescription.isEmpty() ? "【岗位描述(供针对性优化)】\n" + jobDescription + "\n" : "");
+ }
+ return completionService.chat(systemPrompt, userPrompt, 0.5f, 2000, apiKey);
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationRepository.java b/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationRepository.java
new file mode 100644
index 0000000..c86203c
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationRepository.java
@@ -0,0 +1,40 @@
+package com.ruilaizi.example.data.resume;
+
+import android.content.Context;
+
+import androidx.lifecycle.LiveData;
+
+import com.ruilaizi.example.data.api.ApiKeyProvider;
+import com.ruilaizi.example.data.api.OpenAICompletionService;
+import com.ruilaizi.example.data.db.AppDatabase;
+import com.ruilaizi.example.data.db.ResumeOptimizationRecord;
+import com.ruilaizi.example.data.db.ResumeOptimizationRecordDao;
+
+import java.util.List;
+
+public class ResumeOptimizationRepository {
+
+ private final ResumeOptimizationGenerator generator;
+ private final ResumeOptimizationRecordDao dao;
+
+ public ResumeOptimizationRepository(Context context) {
+ ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
+ generator = new ResumeOptimizationGenerator(new OpenAICompletionService(null), apiKeyProvider);
+ dao = AppDatabase.getInstance(context).resumeOptimizationRecordDao();
+ }
+
+ public String generate(String optType, String originalContent, String jobDescription) throws Exception {
+ return generator.generate(optType, originalContent, jobDescription);
+ }
+
+ public void saveRecord(ResumeOptimizationRecord record) {
+ new Thread(() -> {
+ dao.insert(record);
+ dao.keepOnlyRecent();
+ }).start();
+ }
+
+ public LiveData> getRecentRecords() {
+ return dao.getRecentRecords();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/travel/TravelPlanningRepository.java b/example/app/src/main/java/com/ruilaizi/example/data/travel/TravelPlanningRepository.java
index 1abe278..13bbb14 100644
--- a/example/app/src/main/java/com/ruilaizi/example/data/travel/TravelPlanningRepository.java
+++ b/example/app/src/main/java/com/ruilaizi/example/data/travel/TravelPlanningRepository.java
@@ -2,19 +2,39 @@ package com.ruilaizi.example.data.travel;
import android.content.Context;
+import androidx.lifecycle.LiveData;
+
import com.ruilaizi.example.data.api.ApiKeyProvider;
import com.ruilaizi.example.data.api.OpenAICompletionService;
+import com.ruilaizi.example.data.db.AppDatabase;
+import com.ruilaizi.example.data.db.TravelPlanRecord;
+import com.ruilaizi.example.data.db.TravelPlanRecordDao;
+
+import java.util.List;
public class TravelPlanningRepository {
private final TravelPlanGenerator generator;
+ private final TravelPlanRecordDao dao;
public TravelPlanningRepository(Context context) {
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
generator = new TravelPlanGenerator(new OpenAICompletionService(null), apiKeyProvider);
+ dao = AppDatabase.getInstance(context).travelPlanRecordDao();
}
public String generate(String destination, String days, String people, String preferences, String budget) throws Exception {
return generator.generate(destination, days, people, preferences, budget);
}
+
+ public void saveRecord(TravelPlanRecord record) {
+ new Thread(() -> {
+ dao.insert(record);
+ dao.keepOnlyRecent();
+ }).start();
+ }
+
+ public LiveData> getRecentRecords() {
+ return dao.getRecentRecords();
+ }
}
diff --git a/example/app/src/main/java/com/ruilaizi/example/data/weekly/WeeklyReportRepository.java b/example/app/src/main/java/com/ruilaizi/example/data/weekly/WeeklyReportRepository.java
index 854b4b9..5721b2c 100644
--- a/example/app/src/main/java/com/ruilaizi/example/data/weekly/WeeklyReportRepository.java
+++ b/example/app/src/main/java/com/ruilaizi/example/data/weekly/WeeklyReportRepository.java
@@ -2,19 +2,39 @@ package com.ruilaizi.example.data.weekly;
import android.content.Context;
+import androidx.lifecycle.LiveData;
+
import com.ruilaizi.example.data.api.ApiKeyProvider;
import com.ruilaizi.example.data.api.OpenAICompletionService;
+import com.ruilaizi.example.data.db.AppDatabase;
+import com.ruilaizi.example.data.db.WeeklyReportRecord;
+import com.ruilaizi.example.data.db.WeeklyReportRecordDao;
+
+import java.util.List;
public class WeeklyReportRepository {
private final WeeklyReportGenerator generator;
+ private final WeeklyReportRecordDao dao;
public WeeklyReportRepository(Context context) {
ApiKeyProvider apiKeyProvider = new ApiKeyProvider(context);
generator = new WeeklyReportGenerator(new OpenAICompletionService(null), apiKeyProvider);
+ dao = AppDatabase.getInstance(context).weeklyReportRecordDao();
}
public String generate(String reportType, String content) throws Exception {
return generator.generate(reportType, content);
}
+
+ public void saveRecord(WeeklyReportRecord record) {
+ new Thread(() -> {
+ dao.insert(record);
+ dao.keepOnlyRecent();
+ }).start();
+ }
+
+ public LiveData> getRecentRecords() {
+ return dao.getRecentRecords();
+ }
}
diff --git a/example/app/src/main/java/com/ruilaizi/example/ui/MeetingMinutesViewModel.java b/example/app/src/main/java/com/ruilaizi/example/ui/MeetingMinutesViewModel.java
index 293bfb0..cb7f099 100644
--- a/example/app/src/main/java/com/ruilaizi/example/ui/MeetingMinutesViewModel.java
+++ b/example/app/src/main/java/com/ruilaizi/example/ui/MeetingMinutesViewModel.java
@@ -39,6 +39,9 @@ public class MeetingMinutesViewModel extends AndroidViewModel {
try {
String summary = repository.generate(rawContent, title);
resultLiveData.postValue(summary);
+ if (summary != null && !summary.isEmpty()) {
+ repository.saveRecord(new com.ruilaizi.example.data.db.MeetingMinutesRecord(title, rawContent, summary));
+ }
snackbarLiveData.postValue("会议纪要生成完成");
} catch (Exception e) {
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
diff --git a/example/app/src/main/java/com/ruilaizi/example/ui/PoetryViewModel.java b/example/app/src/main/java/com/ruilaizi/example/ui/PoetryViewModel.java
new file mode 100644
index 0000000..e2276d5
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/ui/PoetryViewModel.java
@@ -0,0 +1,63 @@
+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.db.PoetryRecord;
+import com.ruilaizi.example.data.poetry.PoetryRepository;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class PoetryViewModel extends AndroidViewModel {
+
+ private final PoetryRepository repository;
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+ private final MutableLiveData resultLiveData = new MutableLiveData<>("");
+ private final MutableLiveData loadingLiveData = new MutableLiveData<>(false);
+ private final MutableLiveData errorLiveData = new MutableLiveData<>();
+ private final MutableLiveData snackbarLiveData = new MutableLiveData<>();
+
+ public PoetryViewModel(@NonNull Application app) {
+ super(app);
+ repository = new PoetryRepository(app);
+ }
+
+ public LiveData getResultLiveData() { return resultLiveData; }
+ public LiveData getLoadingLiveData() { return loadingLiveData; }
+ public LiveData getErrorLiveData() { return errorLiveData; }
+ public LiveData getSnackbarLiveData() { return snackbarLiveData; }
+
+ public void generate(String poetryTitle, String author, String dynasty, String extraRequirements) {
+ loadingLiveData.setValue(true);
+ resultLiveData.setValue("");
+ errorLiveData.setValue(null);
+ executor.execute(() -> {
+ try {
+ String content = repository.generate(poetryTitle, author, dynasty, extraRequirements);
+ resultLiveData.postValue(content);
+ if (content != null && !content.isEmpty()) {
+ repository.saveRecord(new PoetryRecord(poetryTitle, author, dynasty, content));
+ }
+ 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();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/ui/ResumeOptimizationViewModel.java b/example/app/src/main/java/com/ruilaizi/example/ui/ResumeOptimizationViewModel.java
new file mode 100644
index 0000000..0e03ce2
--- /dev/null
+++ b/example/app/src/main/java/com/ruilaizi/example/ui/ResumeOptimizationViewModel.java
@@ -0,0 +1,63 @@
+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.db.ResumeOptimizationRecord;
+import com.ruilaizi.example.data.resume.ResumeOptimizationRepository;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class ResumeOptimizationViewModel extends AndroidViewModel {
+
+ private final ResumeOptimizationRepository repository;
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+ private final MutableLiveData resultLiveData = new MutableLiveData<>("");
+ private final MutableLiveData loadingLiveData = new MutableLiveData<>(false);
+ private final MutableLiveData errorLiveData = new MutableLiveData<>();
+ private final MutableLiveData snackbarLiveData = new MutableLiveData<>();
+
+ public ResumeOptimizationViewModel(@NonNull Application app) {
+ super(app);
+ repository = new ResumeOptimizationRepository(app);
+ }
+
+ public LiveData getResultLiveData() { return resultLiveData; }
+ public LiveData getLoadingLiveData() { return loadingLiveData; }
+ public LiveData getErrorLiveData() { return errorLiveData; }
+ public LiveData getSnackbarLiveData() { return snackbarLiveData; }
+
+ public void generate(String optType, String originalContent, String jobDescription) {
+ loadingLiveData.setValue(true);
+ resultLiveData.setValue("");
+ errorLiveData.setValue(null);
+ executor.execute(() -> {
+ try {
+ String content = repository.generate(optType, originalContent, jobDescription);
+ resultLiveData.postValue(content);
+ if (content != null && !content.isEmpty()) {
+ repository.saveRecord(new ResumeOptimizationRecord(optType, originalContent, jobDescription, content));
+ }
+ 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();
+ }
+}
diff --git a/example/app/src/main/java/com/ruilaizi/example/ui/TravelPlanningViewModel.java b/example/app/src/main/java/com/ruilaizi/example/ui/TravelPlanningViewModel.java
index f6e8288..e961255 100644
--- a/example/app/src/main/java/com/ruilaizi/example/ui/TravelPlanningViewModel.java
+++ b/example/app/src/main/java/com/ruilaizi/example/ui/TravelPlanningViewModel.java
@@ -39,6 +39,9 @@ public class TravelPlanningViewModel extends AndroidViewModel {
try {
String plan = repository.generate(destination, days, people, preferences, budget);
resultLiveData.postValue(plan);
+ if (plan != null && !plan.isEmpty()) {
+ repository.saveRecord(new com.ruilaizi.example.data.db.TravelPlanRecord(destination, days, people, preferences, budget, plan));
+ }
snackbarLiveData.postValue("旅行攻略生成完成");
} catch (Exception e) {
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
diff --git a/example/app/src/main/java/com/ruilaizi/example/ui/WeeklyReportViewModel.java b/example/app/src/main/java/com/ruilaizi/example/ui/WeeklyReportViewModel.java
index 89102f4..6abfa7b 100644
--- a/example/app/src/main/java/com/ruilaizi/example/ui/WeeklyReportViewModel.java
+++ b/example/app/src/main/java/com/ruilaizi/example/ui/WeeklyReportViewModel.java
@@ -39,6 +39,9 @@ public class WeeklyReportViewModel extends AndroidViewModel {
try {
String report = repository.generate(reportType, content);
resultLiveData.postValue(report);
+ if (report != null && !report.isEmpty()) {
+ repository.saveRecord(new com.ruilaizi.example.data.db.WeeklyReportRecord(reportType, content, report));
+ }
snackbarLiveData.postValue("周报/日报生成完成");
} catch (Exception e) {
errorLiveData.postValue(e != null ? e.getMessage() : "生成失败");
diff --git a/example/app/src/main/res/layout/activity_meeting_minutes.xml b/example/app/src/main/res/layout/activity_meeting_minutes.xml
index c3f5042..b3575b3 100644
--- a/example/app/src/main/res/layout/activity_meeting_minutes.xml
+++ b/example/app/src/main/res/layout/activity_meeting_minutes.xml
@@ -33,7 +33,12 @@
android:textStyle="bold"
android:textColor="@color/colorPrimary"
android:gravity="center" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_poetry.xml b/example/app/src/main/res/layout/activity_poetry.xml
new file mode 100644
index 0000000..c06c287
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_poetry.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_poetry_history.xml b/example/app/src/main/res/layout/activity_poetry_history.xml
new file mode 100644
index 0000000..983937e
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_poetry_history.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_resume_optimization.xml b/example/app/src/main/res/layout/activity_resume_optimization.xml
new file mode 100644
index 0000000..0e59e98
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_resume_optimization.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_resume_optimization_history.xml b/example/app/src/main/res/layout/activity_resume_optimization_history.xml
new file mode 100644
index 0000000..90c8cb5
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_resume_optimization_history.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_travel_planning.xml b/example/app/src/main/res/layout/activity_travel_planning.xml
index bc06cee..5c45949 100644
--- a/example/app/src/main/res/layout/activity_travel_planning.xml
+++ b/example/app/src/main/res/layout/activity_travel_planning.xml
@@ -35,7 +35,12 @@
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center" />
-
+
diff --git a/example/app/src/main/res/layout/activity_travel_planning_history.xml b/example/app/src/main/res/layout/activity_travel_planning_history.xml
new file mode 100644
index 0000000..aae34b5
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_travel_planning_history.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_weekly_report.xml b/example/app/src/main/res/layout/activity_weekly_report.xml
index 832b248..c6b7f2c 100644
--- a/example/app/src/main/res/layout/activity_weekly_report.xml
+++ b/example/app/src/main/res/layout/activity_weekly_report.xml
@@ -31,7 +31,12 @@
android:textStyle="bold"
android:textColor="@color/colorPrimary"
android:gravity="center" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/item_meeting_minutes_history.xml b/example/app/src/main/res/layout/item_meeting_minutes_history.xml
new file mode 100644
index 0000000..33d049d
--- /dev/null
+++ b/example/app/src/main/res/layout/item_meeting_minutes_history.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/item_poetry_history.xml b/example/app/src/main/res/layout/item_poetry_history.xml
new file mode 100644
index 0000000..33d049d
--- /dev/null
+++ b/example/app/src/main/res/layout/item_poetry_history.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/item_resume_history.xml b/example/app/src/main/res/layout/item_resume_history.xml
new file mode 100644
index 0000000..33d049d
--- /dev/null
+++ b/example/app/src/main/res/layout/item_resume_history.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/item_travel_history.xml b/example/app/src/main/res/layout/item_travel_history.xml
new file mode 100644
index 0000000..fbbbd8a
--- /dev/null
+++ b/example/app/src/main/res/layout/item_travel_history.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/layout/item_weekly_report_history.xml b/example/app/src/main/res/layout/item_weekly_report_history.xml
new file mode 100644
index 0000000..88e443c
--- /dev/null
+++ b/example/app/src/main/res/layout/item_weekly_report_history.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/app/src/main/res/values/arrays.xml b/example/app/src/main/res/values/arrays.xml
index e680f5b..ddb283c 100644
--- a/example/app/src/main/res/values/arrays.xml
+++ b/example/app/src/main/res/values/arrays.xml
@@ -19,6 +19,9 @@
- 50元以下
- 50-100元
- 100-150元
- 150-200元
- 200-300元
- 300-500元
- 500元以上
+
+ - 简历优化
- 求职信
+
diff --git a/example/app/src/main/res/values/strings.xml b/example/app/src/main/res/values/strings.xml
index f659fa0..43a1272 100644
--- a/example/app/src/main/res/values/strings.xml
+++ b/example/app/src/main/res/values/strings.xml
@@ -39,6 +39,16 @@
AI应用
规划历史
暂无规划记录
+ 旅行攻略历史
+ 暂无旅行攻略记录
+ 周报/日报历史
+ 暂无周报记录
+ 会议纪要历史
+ 暂无会议纪要记录
+ 古诗词解析历史
+ 暂无解析记录
+ 简历优化历史
+ 暂无优化记录
敬请期待
AI 智能应用
探索更多 AI 应用