From 18cb2e6614c6164eaa24f4fb74cd68c3b943b89d Mon Sep 17 00:00:00 2001 From: rjb <263303411@qq.com> Date: Thu, 5 Mar 2026 00:21:57 +0800 Subject: [PATCH] aia --- example/app/src/main/AndroidManifest.xml | 20 ++++ .../ruilaizi/example/AIServicesActivity.java | 5 +- .../example/MeetingMinutesActivity.java | 3 + .../MeetingMinutesHistoryActivity.java | 39 ++++++ .../com/ruilaizi/example/PoetryActivity.java | 89 ++++++++++++++ .../example/PoetryHistoryActivity.java | 38 ++++++ .../example/ResumeOptimizationActivity.java | 93 +++++++++++++++ .../ResumeOptimizationHistoryActivity.java | 38 ++++++ .../example/TravelPlanningActivity.java | 3 + .../TravelPlanningHistoryActivity.java | 39 ++++++ .../example/WeeklyReportActivity.java | 3 + .../example/WeeklyReportHistoryActivity.java | 39 ++++++ .../adapter/MeetingMinutesHistoryAdapter.java | 95 +++++++++++++++ .../example/adapter/PoetryHistoryAdapter.java | 95 +++++++++++++++ .../ResumeOptimizationHistoryAdapter.java | 95 +++++++++++++++ .../example/adapter/TravelHistoryAdapter.java | 107 +++++++++++++++++ .../adapter/WeeklyReportHistoryAdapter.java | 107 +++++++++++++++++ .../ruilaizi/example/data/db/AppDatabase.java | 9 +- .../example/data/db/MeetingMinutesRecord.java | 25 ++++ .../data/db/MeetingMinutesRecordDao.java | 21 ++++ .../example/data/db/PoetryRecord.java | 26 ++++ .../example/data/db/PoetryRecordDao.java | 21 ++++ .../data/db/ResumeOptimizationRecord.java | 27 +++++ .../data/db/ResumeOptimizationRecordDao.java | 21 ++++ .../example/data/db/TravelPlanRecord.java | 31 +++++ .../example/data/db/TravelPlanRecordDao.java | 21 ++++ .../example/data/db/WeeklyReportRecord.java | 25 ++++ .../data/db/WeeklyReportRecordDao.java | 21 ++++ .../meeting/MeetingMinutesRepository.java | 20 ++++ .../example/data/poetry/PoetryGenerator.java | 26 ++++ .../example/data/poetry/PoetryRepository.java | 40 +++++++ .../resume/ResumeOptimizationGenerator.java | 34 ++++++ .../resume/ResumeOptimizationRepository.java | 40 +++++++ .../data/travel/TravelPlanningRepository.java | 20 ++++ .../data/weekly/WeeklyReportRepository.java | 20 ++++ .../example/ui/MeetingMinutesViewModel.java | 3 + .../ruilaizi/example/ui/PoetryViewModel.java | 63 ++++++++++ .../ui/ResumeOptimizationViewModel.java | 63 ++++++++++ .../example/ui/TravelPlanningViewModel.java | 3 + .../example/ui/WeeklyReportViewModel.java | 3 + .../res/layout/activity_meeting_minutes.xml | 7 +- .../activity_meeting_minutes_history.xml | 54 +++++++++ .../src/main/res/layout/activity_poetry.xml | 112 ++++++++++++++++++ .../res/layout/activity_poetry_history.xml | 54 +++++++++ .../layout/activity_resume_optimization.xml | 106 +++++++++++++++++ .../activity_resume_optimization_history.xml | 54 +++++++++ .../res/layout/activity_travel_planning.xml | 7 +- .../activity_travel_planning_history.xml | 54 +++++++++ .../res/layout/activity_weekly_report.xml | 7 +- .../layout/activity_weekly_report_history.xml | 54 +++++++++ .../layout/item_meeting_minutes_history.xml | 24 ++++ .../main/res/layout/item_poetry_history.xml | 24 ++++ .../main/res/layout/item_resume_history.xml | 24 ++++ .../main/res/layout/item_travel_history.xml | 67 +++++++++++ .../res/layout/item_weekly_report_history.xml | 24 ++++ example/app/src/main/res/values/arrays.xml | 3 + example/app/src/main/res/values/strings.xml | 10 ++ 57 files changed, 2169 insertions(+), 7 deletions(-) create mode 100644 example/app/src/main/java/com/ruilaizi/example/MeetingMinutesHistoryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/PoetryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/PoetryHistoryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/ResumeOptimizationHistoryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/TravelPlanningHistoryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/WeeklyReportHistoryActivity.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/adapter/MeetingMinutesHistoryAdapter.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/adapter/PoetryHistoryAdapter.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/adapter/ResumeOptimizationHistoryAdapter.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/adapter/TravelHistoryAdapter.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/adapter/WeeklyReportHistoryAdapter.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecord.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/MeetingMinutesRecordDao.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecord.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/PoetryRecordDao.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecord.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/ResumeOptimizationRecordDao.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecord.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/TravelPlanRecordDao.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecord.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/db/WeeklyReportRecordDao.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryGenerator.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/poetry/PoetryRepository.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationGenerator.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/data/resume/ResumeOptimizationRepository.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/ui/PoetryViewModel.java create mode 100644 example/app/src/main/java/com/ruilaizi/example/ui/ResumeOptimizationViewModel.java create mode 100644 example/app/src/main/res/layout/activity_meeting_minutes_history.xml create mode 100644 example/app/src/main/res/layout/activity_poetry.xml create mode 100644 example/app/src/main/res/layout/activity_poetry_history.xml create mode 100644 example/app/src/main/res/layout/activity_resume_optimization.xml create mode 100644 example/app/src/main/res/layout/activity_resume_optimization_history.xml create mode 100644 example/app/src/main/res/layout/activity_travel_planning_history.xml create mode 100644 example/app/src/main/res/layout/activity_weekly_report_history.xml create mode 100644 example/app/src/main/res/layout/item_meeting_minutes_history.xml create mode 100644 example/app/src/main/res/layout/item_poetry_history.xml create mode 100644 example/app/src/main/res/layout/item_resume_history.xml create mode 100644 example/app/src/main/res/layout/item_travel_history.xml create mode 100644 example/app/src/main/res/layout/item_weekly_report_history.xml 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 应用