diff --git a/doc/ruanjian/.gitignore b/doc/ruanjian/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/doc/ruanjian/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/doc/ruanjian/.idea/.gitignore b/doc/ruanjian/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/doc/ruanjian/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/doc/ruanjian/.idea/compiler.xml b/doc/ruanjian/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/doc/ruanjian/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/deploymentTargetSelector.xml b/doc/ruanjian/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..8b6e693 --- /dev/null +++ b/doc/ruanjian/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/gradle.xml b/doc/ruanjian/.idea/gradle.xml new file mode 100644 index 0000000..7b3006b --- /dev/null +++ b/doc/ruanjian/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/kotlinc.xml b/doc/ruanjian/.idea/kotlinc.xml new file mode 100644 index 0000000..0fc3113 --- /dev/null +++ b/doc/ruanjian/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/migrations.xml b/doc/ruanjian/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/doc/ruanjian/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/misc.xml b/doc/ruanjian/.idea/misc.xml new file mode 100644 index 0000000..b2c751a --- /dev/null +++ b/doc/ruanjian/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/.idea/runConfigurations.xml b/doc/ruanjian/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/doc/ruanjian/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/app/.gitignore b/doc/ruanjian/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/doc/ruanjian/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/doc/ruanjian/app/build.gradle.kts b/doc/ruanjian/app/build.gradle.kts new file mode 100644 index 0000000..af39c2b --- /dev/null +++ b/doc/ruanjian/app/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + id("com.android.application") + kotlin("android") +} + +android { + namespace = "com.example.ruanjian" + compileSdk = 33 + + defaultConfig { + applicationId = "com.example.ruanjian" + minSdk = 21 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") + implementation("androidx.core:core-ktx:1.10.1") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.9.0") + implementation("androidx.recyclerview:recyclerview:1.3.0") +} diff --git a/doc/ruanjian/app/proguard-rules.pro b/doc/ruanjian/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/doc/ruanjian/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/doc/ruanjian/app/src/androidTest/java/com/example/ruanjian/ExampleInstrumentedTest.java b/doc/ruanjian/app/src/androidTest/java/com/example/ruanjian/ExampleInstrumentedTest.java new file mode 100644 index 0000000..bcf196f --- /dev/null +++ b/doc/ruanjian/app/src/androidTest/java/com/example/ruanjian/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.ruanjian; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.ruanjian", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/AndroidManifest.xml b/doc/ruanjian/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8fa65ce --- /dev/null +++ b/doc/ruanjian/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/ic_launcher-playstore.png b/doc/ruanjian/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..7c74649 Binary files /dev/null and b/doc/ruanjian/app/src/main/ic_launcher-playstore.png differ diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/AddTaskActivity.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/AddTaskActivity.java new file mode 100644 index 0000000..266e6d1 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/AddTaskActivity.java @@ -0,0 +1,32 @@ +package com.example.ruanjian; + +import android.os.Bundle; +import android.widget.Button; +import android.widget.EditText; + +import androidx.appcompat.app.AppCompatActivity; + +public class AddTaskActivity extends AppCompatActivity { + + private EditText editTextTask; + private Button btnSave; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_add_task); + + editTextTask = findViewById(R.id.editTextTask); + btnSave = findViewById(R.id.btnSave); + + btnSave.setOnClickListener(v -> { + String content = editTextTask.getText().toString().trim(); + if (!content.isEmpty()) { + // 新建 Task 对象,默认优先级为 "中" + Task task = new Task(content, "中"); + MainActivity.taskList.add(task); // 添加 Task 对象 + } + finish(); // 关闭当前页面,返回主界面 + }); + } +} diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/DetailActivity.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/DetailActivity.java new file mode 100644 index 0000000..e0e2605 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/DetailActivity.java @@ -0,0 +1,201 @@ +package com.example.ruanjian; + +import android.app.AlarmManager; +import android.app.DatePickerDialog; +import android.app.PendingIntent; +import android.app.TimePickerDialog; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ArrayAdapter; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +public class DetailActivity extends AppCompatActivity { + + private EditText editTextTaskDetail; + private Spinner spinnerPriority; + private Button buttonSave, buttonDelete, buttonCancel, btnSetDeadline; + private TextView tvDeadline; + private int taskIndex = -1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_detail); + + // 初始化原有视图 + editTextTaskDetail = findViewById(R.id.editTextTaskDetail); + spinnerPriority = findViewById(R.id.spinnerPriority); + buttonSave = findViewById(R.id.buttonSave); + buttonDelete = findViewById(R.id.buttonDelete); + buttonCancel = findViewById(R.id.buttonCancel); + + // 初始化新增视图 + btnSetDeadline = findViewById(R.id.btnSetDeadline); + tvDeadline = findViewById(R.id.tvDeadline); + + // 原有优先级下拉列表设置 + ArrayAdapter priorityAdapter = new ArrayAdapter<>( + this, + android.R.layout.simple_spinner_item, + new String[]{"高", "中", "低"}); + priorityAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerPriority.setAdapter(priorityAdapter); + + // 原有任务数据加载 + taskIndex = getIntent().getIntExtra("index", -1); + if (taskIndex != -1 && taskIndex < MainActivity.taskList.size()) { + Task task = MainActivity.taskList.get(taskIndex); + editTextTaskDetail.setText(task.getContent()); + + // 原有优先级设置 + int spinnerPos = priorityAdapter.getPosition(task.getPriority()); + spinnerPriority.setSelection(spinnerPos); + + // 新增截止时间显示 + if (task.getDeadline() != null) { + tvDeadline.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(task.getDeadline())); + } + } else { + Toast.makeText(this, "任务不存在", Toast.LENGTH_SHORT).show(); + finish(); + } + + // 原有按钮点击事件 + buttonSave.setOnClickListener(v -> { + String newContent = editTextTaskDetail.getText().toString().trim(); + String newPriority = spinnerPriority.getSelectedItem().toString(); + + if (!newContent.isEmpty()) { + Task originalTask = MainActivity.taskList.get(taskIndex); + Task updatedTask = new Task(newContent, newPriority); + // 保留原有创建时间和完成状态 + updatedTask.setCreateTime(originalTask.getCreateTime()); + updatedTask.setCompleted(originalTask.isCompleted()); + // 保留新增的截止时间和提醒设置 + updatedTask.setDeadline(originalTask.getDeadline()); + updatedTask.setHasReminder(originalTask.hasReminder()); + + MainActivity.taskList.set(taskIndex, updatedTask); + Toast.makeText(DetailActivity.this, "任务已保存", Toast.LENGTH_SHORT).show(); + finish(); + } else { + Toast.makeText(DetailActivity.this, "任务内容不能为空", Toast.LENGTH_SHORT).show(); + } + }); + + buttonDelete.setOnClickListener(v -> { + MainActivity.taskList.remove(taskIndex); + Toast.makeText(DetailActivity.this, "任务已删除", Toast.LENGTH_SHORT).show(); + finish(); + }); + + buttonCancel.setOnClickListener(v -> { + Toast.makeText(DetailActivity.this, "已取消,未保存任务", Toast.LENGTH_SHORT).show(); + finish(); + }); + + // 新增截止时间设置按钮 + btnSetDeadline.setOnClickListener(v -> showDateTimePicker()); + } + + // 新增方法:显示日期时间选择器 + private void showDateTimePicker() { + final Calendar calendar = Calendar.getInstance(); + Task currentTask = MainActivity.taskList.get(taskIndex); + + // 如果已有截止时间,使用该时间初始化 + if (currentTask.getDeadline() != null) { + calendar.setTime(currentTask.getDeadline()); + } + + // 先选择日期 + new DatePickerDialog( + this, + (view, year, month, day) -> { + calendar.set(year, month, day); + // 再选择时间 + new TimePickerDialog( + this, + (view1, hour, minute) -> { + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + + // 设置截止时间 + currentTask.setDeadline(calendar.getTime()); + tvDeadline.setText(new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(calendar.getTime())); + + // 询问是否设置提醒 + showReminderDialog(currentTask); + }, + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + true) + .show(); + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)) + .show(); + } + + // 新增方法:显示提醒设置对话框 + private void showReminderDialog(Task task) { + new AlertDialog.Builder(this) + .setTitle("设置提醒") + .setMessage("要在截止时间前15分钟提醒吗?") + .setPositiveButton("是", (dialog, which) -> { + task.setHasReminder(true); + scheduleReminder(task); + Toast.makeText(this, "已设置提醒", Toast.LENGTH_SHORT).show(); + }) + .setNegativeButton("否", (dialog, which) -> { + task.setHasReminder(false); + cancelReminder(task); + }) + .show(); + } + + // 新增方法:设置提醒 + private void scheduleReminder(Task task) { + if (task.getDeadline() == null) return; + + AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); + Intent intent = new Intent(this, ReminderReceiver.class); + intent.putExtra("task_content", task.getContent()); + + PendingIntent pendingIntent = PendingIntent.getBroadcast( + this, + task.getContent().hashCode(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + // 截止时间前15分钟提醒 + long reminderTime = task.getDeadline().getTime() - (15 * 60 * 1000); + alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent); + } + + // 新增方法:取消提醒 + private void cancelReminder(Task task) { + AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); + Intent intent = new Intent(this, ReminderReceiver.class); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + this, + task.getContent().hashCode(), + intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + alarmManager.cancel(pendingIntent); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainActivity.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainActivity.java new file mode 100644 index 0000000..1c6a15f --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainActivity.java @@ -0,0 +1,172 @@ +package com.example.ruanjian; + +import android.app.AlertDialog; +import android.content.Intent; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.ArrayAdapter; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; + +public class MainActivity extends AppCompatActivity { + + private RecyclerView recyclerView; + private TaskAdapter adapter; + private Button btnAddTask, btnSort; + private EditText editTextSearch; + private int currentSortType = 0; // 0=默认,1=优先级,2=时间 + + public static ArrayList taskList = new ArrayList<>(); + private ArrayList filteredList = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + recyclerView = findViewById(R.id.recyclerView); + btnAddTask = findViewById(R.id.btnAddTask); + btnSort = findViewById(R.id.btnSort); + editTextSearch = findViewById(R.id.editTextSearch); + + adapter = new TaskAdapter(filteredList, position -> { + Task clickedTask = filteredList.get(position); + int realIndex = taskList.indexOf(clickedTask); + Intent intent = new Intent(MainActivity.this, DetailActivity.class); + intent.putExtra("index", realIndex); + startActivity(intent); + }); + + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(adapter); + + btnAddTask.setOnClickListener(v -> showAddTaskDialog()); + btnSort.setOnClickListener(v -> showSortDialog()); + + editTextSearch.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) { + filterTasks(s.toString()); + } + @Override public void afterTextChanged(Editable s) {} + }); + + filterTasks(""); + } + + private void filterTasks(String query) { + filteredList.clear(); + for (Task task : taskList) { + if (task.getContent().toLowerCase().contains(query.toLowerCase())) { + filteredList.add(task); + } + } + applyCurrentSort(); + adapter.notifyDataSetChanged(); + } + + private void applyCurrentSort() { + switch (currentSortType) { + case 1: + sortByPriority(); + break; + case 2: + sortByCreateTime(); + break; + default: + // 默认不排序 + break; + } + } + + private void sortByPriority() { + Collections.sort(filteredList, (t1, t2) -> { + int p1 = getPriorityValue(t1.getPriority()); + int p2 = getPriorityValue(t2.getPriority()); + return p2 - p1; // 降序 + }); + currentSortType = 1; + adapter.notifyDataSetChanged(); + } + + private void sortByCreateTime() { + Collections.sort(filteredList, (t1, t2) -> + t2.getCreateTime().compareTo(t1.getCreateTime()) + ); + currentSortType = 2; + adapter.notifyDataSetChanged(); + } + + private int getPriorityValue(String priority) { + switch (priority) { + case "高": return 3; + case "中": return 2; + case "低": return 1; + default: return 0; + } + } + + private void showAddTaskDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("添加任务"); + + final EditText input = new EditText(this); + input.setHint("请输入任务内容"); + input.setSingleLine(); + input.setPadding(30, 20, 30, 20); + + final Spinner prioritySpinner = new Spinner(this); + ArrayAdapter spinnerAdapter = new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, + new String[]{"高", "中", "低"}); + spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + prioritySpinner.setAdapter(spinnerAdapter); + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(30, 20, 30, 10); + layout.addView(input); + layout.addView(prioritySpinner); + + builder.setView(layout); + + builder.setPositiveButton("确定", (dialog, which) -> { + String content = input.getText().toString().trim(); + String priority = prioritySpinner.getSelectedItem().toString(); + if (!content.isEmpty()) { + taskList.add(new Task(content, priority)); + filterTasks(editTextSearch.getText().toString()); + } + }); + + builder.setNegativeButton("取消", null); + builder.show(); + } + + private void showSortDialog() { + new AlertDialog.Builder(this) + .setTitle("排序方式") + .setItems(new String[]{"默认顺序", "按优先级排序", "按创建时间排序"}, (dialog, which) -> { + currentSortType = which; + applyCurrentSort(); + }) + .show(); + } + @Override + protected void onResume() { + super.onResume(); + filterTasks(editTextSearch.getText().toString()); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainApplication.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainApplication.java new file mode 100644 index 0000000..67937b7 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/MainApplication.java @@ -0,0 +1,11 @@ +package com.example.ruanjian; + +import android.app.Application; + +public class MainApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + NotificationHelper.createNotificationChannel(this); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/NotificationHelper.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/NotificationHelper.java new file mode 100644 index 0000000..68398d9 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/NotificationHelper.java @@ -0,0 +1,41 @@ +// NotificationHelper.java +package com.example.ruanjian; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import androidx.core.app.NotificationCompat; + +public class NotificationHelper { + private static final String CHANNEL_ID = "task_reminder_channel"; + + public static void createNotificationChannel(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + "Task Reminders", + NotificationManager.IMPORTANCE_HIGH); + channel.setDescription("Channel for task reminder notifications"); + + NotificationManager manager = context.getSystemService(NotificationManager.class); + manager.createNotificationChannel(channel); + } + } + + public static void showNotification(Context context, String title, String content) { + Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(title) + .setContentText(content) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setAutoCancel(true) + .build(); + + NotificationManager manager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + manager.notify((int) System.currentTimeMillis(), notification); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/ReminderReceiver.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ReminderReceiver.java new file mode 100644 index 0000000..87400c9 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ReminderReceiver.java @@ -0,0 +1,17 @@ +// ReminderReceiver.java +package com.example.ruanjian; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class ReminderReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String taskContent = intent.getStringExtra("task_content"); + NotificationHelper.showNotification( + context, + "任务提醒", + "您的任务 '" + taskContent + "' 即将到期!"); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/Task.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/Task.java new file mode 100644 index 0000000..99f1f22 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/Task.java @@ -0,0 +1,79 @@ +package com.example.ruanjian; + +import java.util.Date; + +public class Task { + private String content; + private String priority; // "高", "中", "低" + private boolean completed; + private Date createTime; + private Date deadline; // 新增截止时间 + private boolean hasReminder; // 新增是否设置提醒 + + public Task(String content, String priority) { + this.content = content; + this.priority = priority; + this.completed = false; + this.createTime = new Date(); + this.deadline = null; + this.hasReminder = false; + } + + // 原有方法 + public String getContent() { + return content; + } + + public String getPriority() { + return priority; + } + + public boolean isCompleted() { + return completed; + } + + public Date getCreateTime() { + return createTime; + } + + public void setContent(String content) { + this.content = content; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + // 新增方法 + public Date getDeadline() { + return deadline; + } + + public void setDeadline(Date deadline) { + this.deadline = deadline; + } + + public boolean hasReminder() { + return hasReminder; + } + + public void setHasReminder(boolean hasReminder) { + this.hasReminder = hasReminder; + } + + // 新增辅助方法 + public String getFormattedDeadline() { + if (deadline == null) { + return "未设置"; + } + return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm").format(deadline); + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/TaskAdapter.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/TaskAdapter.java new file mode 100644 index 0000000..6d77407 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/TaskAdapter.java @@ -0,0 +1,114 @@ +package com.example.ruanjian; + +import android.graphics.Color; +import android.graphics.Paint; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; + +public class TaskAdapter extends RecyclerView.Adapter { + + private ArrayList taskList; + private OnItemClickListener listener; + + public interface OnItemClickListener { + void onItemClick(int position); + } + + public TaskAdapter(ArrayList taskList, OnItemClickListener listener) { + this.taskList = taskList; + this.listener = listener; + } + + @NonNull + @Override + public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_task, parent, false); + return new TaskViewHolder(view); + } + + @Override + + public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) { + Task task = taskList.get(position); + + holder.textViewTask.setText(task.getContent()); + + // 先移除监听,避免复用导致多次触发 + holder.checkBoxCompleted.setOnCheckedChangeListener(null); + holder.checkBoxCompleted.setChecked(task.isCompleted()); + + if (task.isCompleted()) { + holder.textViewTask.setPaintFlags(holder.textViewTask.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + holder.textViewTask.setTextColor(Color.GRAY); + } else { + holder.textViewTask.setPaintFlags(holder.textViewTask.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + switch (task.getPriority()) { + case "高": + holder.textViewTask.setTextColor(Color.RED); + break; + case "中": + holder.textViewTask.setTextColor(Color.parseColor("#FF9800")); // 橙色 + break; + case "低": + holder.textViewTask.setTextColor(Color.parseColor("#4CAF50")); // 绿色 + break; + default: + holder.textViewTask.setTextColor(Color.BLACK); + break; + } + } + + // 重新设置监听,避免闪退 + holder.checkBoxCompleted.setOnCheckedChangeListener((buttonView, isChecked) -> { + task.setCompleted(isChecked); + // 直接更新UI,不调用 notifyItemChanged(position) + if (isChecked) { + holder.textViewTask.setPaintFlags(holder.textViewTask.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + holder.textViewTask.setTextColor(Color.GRAY); + } else { + holder.textViewTask.setPaintFlags(holder.textViewTask.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); + switch (task.getPriority()) { + case "高": + holder.textViewTask.setTextColor(Color.RED); + break; + case "中": + holder.textViewTask.setTextColor(Color.parseColor("#FF9800")); + break; + case "低": + holder.textViewTask.setTextColor(Color.parseColor("#4CAF50")); + break; + default: + holder.textViewTask.setTextColor(Color.BLACK); + break; + } + } + }); + + holder.itemView.setOnClickListener(v -> listener.onItemClick(position)); + } + + @Override + public int getItemCount() { + return taskList.size(); + } + + static class TaskViewHolder extends RecyclerView.ViewHolder { + TextView textViewTask; + CheckBox checkBoxCompleted; + + public TaskViewHolder(@NonNull View itemView) { + super(itemView); + textViewTask = itemView.findViewById(R.id.textViewTask); + checkBoxCompleted = itemView.findViewById(R.id.checkBoxCompleted); + } + } +} \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoAdapter.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoAdapter.java new file mode 100644 index 0000000..40a3cc6 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoAdapter.java @@ -0,0 +1,62 @@ +package com.example.ruanjian; + +import android.graphics.Paint; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; + +public class ToDoAdapter extends RecyclerView.Adapter { + private ArrayList todoList; + + public ToDoAdapter(ArrayList todoList) { + this.todoList = todoList; + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + public TextView todoText; + public CardView card; + + public ViewHolder(View view) { + super(view); + todoText = view.findViewById(R.id.todoText); + card = view.findViewById(R.id.cardView); + } + } + + @Override + public ToDoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_todo, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + ToDoItem item = todoList.get(position); + holder.todoText.setText(item.getText()); + + if (item.isDone()) { + holder.todoText.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG); + holder.card.setCardBackgroundColor(0xFFE0E0E0); // 灰色 + } else { + holder.todoText.setPaintFlags(0); + holder.card.setCardBackgroundColor(0xFFFFFFFF); // 白色 + } + + holder.itemView.setOnClickListener(v -> { + item.toggleDone(); + notifyItemChanged(position); + }); + } + + @Override + public int getItemCount() { + return todoList.size(); + } +} diff --git a/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoItem.java b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoItem.java new file mode 100644 index 0000000..ce9fed3 --- /dev/null +++ b/doc/ruanjian/app/src/main/java/com/example/ruanjian/ToDoItem.java @@ -0,0 +1,23 @@ +package com.example.ruanjian; + +public class ToDoItem { + private String text; + private boolean isDone; + + public ToDoItem(String text) { + this.text = text; + this.isDone = false; + } + + public String getText() { + return text; + } + + public boolean isDone() { + return isDone; + } + + public void toggleDone() { + this.isDone = !this.isDone; + } +} diff --git a/doc/ruanjian/app/src/main/res/drawable/an.png b/doc/ruanjian/app/src/main/res/drawable/an.png new file mode 100644 index 0000000..a8a6a5c Binary files /dev/null and b/doc/ruanjian/app/src/main/res/drawable/an.png differ diff --git a/doc/ruanjian/app/src/main/res/drawable/bg_main.jpg b/doc/ruanjian/app/src/main/res/drawable/bg_main.jpg new file mode 100644 index 0000000..add66ab Binary files /dev/null and b/doc/ruanjian/app/src/main/res/drawable/bg_main.jpg differ diff --git a/doc/ruanjian/app/src/main/res/drawable/bg_todo.xml b/doc/ruanjian/app/src/main/res/drawable/bg_todo.xml new file mode 100644 index 0000000..5a404a3 --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/bg_todo.xml @@ -0,0 +1,5 @@ + + diff --git a/doc/ruanjian/app/src/main/res/drawable/detail_background.xml b/doc/ruanjian/app/src/main/res/drawable/detail_background.xml new file mode 100644 index 0000000..62e7f42 --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/detail_background.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/doc/ruanjian/app/src/main/res/drawable/dialog_bg.xml b/doc/ruanjian/app/src/main/res/drawable/dialog_bg.xml new file mode 100644 index 0000000..a68b45a --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/dialog_bg.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/res/drawable/edittext_bg.xml b/doc/ruanjian/app/src/main/res/drawable/edittext_bg.xml new file mode 100644 index 0000000..c652bae --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/edittext_bg.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/doc/ruanjian/app/src/main/res/drawable/gou.png b/doc/ruanjian/app/src/main/res/drawable/gou.png new file mode 100644 index 0000000..3a3a06e Binary files /dev/null and b/doc/ruanjian/app/src/main/res/drawable/gou.png differ diff --git a/doc/ruanjian/app/src/main/res/drawable/gu.png b/doc/ruanjian/app/src/main/res/drawable/gu.png new file mode 100644 index 0000000..4c8ef95 Binary files /dev/null and b/doc/ruanjian/app/src/main/res/drawable/gu.png differ diff --git a/doc/ruanjian/app/src/main/res/drawable/ic_launcher_background.xml b/doc/ruanjian/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/ruanjian/app/src/main/res/drawable/ic_launcher_foreground.xml b/doc/ruanjian/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/res/drawable/ic_notification.xml b/doc/ruanjian/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..44ac014 --- /dev/null +++ b/doc/ruanjian/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/doc/ruanjian/app/src/main/res/drawable/wa.png b/doc/ruanjian/app/src/main/res/drawable/wa.png new file mode 100644 index 0000000..60065c0 Binary files /dev/null and b/doc/ruanjian/app/src/main/res/drawable/wa.png differ diff --git a/doc/ruanjian/app/src/main/res/layout/activity_add_task.xml b/doc/ruanjian/app/src/main/res/layout/activity_add_task.xml new file mode 100644 index 0000000..ba25232 --- /dev/null +++ b/doc/ruanjian/app/src/main/res/layout/activity_add_task.xml @@ -0,0 +1,25 @@ + + + + + +