diff --git a/app/src/main/java/com/example/CQUPT/adapter/CourseAdapter.java b/app/src/main/java/com/example/CQUPT/adapter/CourseAdapter.java index da09e55..573be1b 100644 --- a/app/src/main/java/com/example/CQUPT/adapter/CourseAdapter.java +++ b/app/src/main/java/com/example/CQUPT/adapter/CourseAdapter.java @@ -1,15 +1,21 @@ package com.example.CQUPT.adapter; +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import com.example.CQUPT.R; import com.example.CQUPT.model.Course; +import com.google.android.material.card.MaterialCardView; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.List; @@ -51,6 +57,7 @@ public int getItemCount() { } static class CourseViewHolder extends RecyclerView.ViewHolder { + private final MaterialCardView cardView; private final TextView courseTimeText; private final TextView courseNameText; private final TextView locationText; @@ -60,6 +67,7 @@ static class CourseViewHolder extends RecyclerView.ViewHolder { public CourseViewHolder(@NonNull View itemView) { super(itemView); + cardView = (MaterialCardView) itemView; courseTimeText = itemView.findViewById(R.id.courseTimeText); courseNameText = itemView.findViewById(R.id.courseNameText); locationText = itemView.findViewById(R.id.locationText); @@ -69,12 +77,57 @@ public CourseViewHolder(@NonNull View itemView) { } public void bind(Course course, int currentWeek) { + Context context = itemView.getContext(); + + // 设置基本信息 courseTimeText.setText(course.getTimeRange()); courseNameText.setText(course.getName()); locationText.setText(course.getLocation()); teacherText.setText(course.getTeacher()); weekRangeText.setText(course.getWeekRange()); currentWeekText.setText(String.format("第%d周", currentWeek)); + + // 判断课程是否在当前周 + boolean isInCurrentWeek = currentWeek >= course.getStartWeek() && currentWeek <= course.getEndWeek(); + + // 根据是否在当前周设置卡片样式 + if (isInCurrentWeek) { + cardView.setCardBackgroundColor(ContextCompat.getColor(context, R.color.purple_50)); + cardView.setStrokeColor(ContextCompat.getColor(context, R.color.purple_200)); + cardView.setStrokeWidth(2); + courseNameText.setTextColor(ContextCompat.getColor(context, R.color.purple_700)); + } else { + cardView.setCardBackgroundColor(Color.WHITE); + cardView.setStrokeColor(Color.TRANSPARENT); + cardView.setStrokeWidth(0); + courseNameText.setTextColor(ContextCompat.getColor(context, android.R.color.black)); + } + + // 设置点击事件显示详情 + cardView.setOnClickListener(v -> showCourseDetail(context, course, currentWeek)); + } + + private void showCourseDetail(Context context, Course course, int currentWeek) { + View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_course_detail, null); + + // 设置详情对话框的内容 + TextView courseNameTitle = dialogView.findViewById(R.id.courseNameTitle); + TextView timeText = dialogView.findViewById(R.id.timeText); + TextView weekRangeDetailText = dialogView.findViewById(R.id.weekRangeDetailText); + TextView locationDetailText = dialogView.findViewById(R.id.locationDetailText); + TextView teacherDetailText = dialogView.findViewById(R.id.teacherDetailText); + + courseNameTitle.setText(course.getName()); + timeText.setText(course.getTimeRange()); + weekRangeDetailText.setText(String.format("%s(当前第%d周)", course.getWeekRange(), currentWeek)); + locationDetailText.setText(course.getLocation()); + teacherDetailText.setText(course.getTeacher()); + + // 创建并显示对话框 + new MaterialAlertDialogBuilder(context) + .setView(dialogView) + .setPositiveButton("关闭", null) + .show(); } } } diff --git a/app/src/main/java/com/example/CQUPT/ui/home/HomeFragment.java b/app/src/main/java/com/example/CQUPT/ui/home/HomeFragment.java index 5f4cde2..43b9d3c 100644 --- a/app/src/main/java/com/example/CQUPT/ui/home/HomeFragment.java +++ b/app/src/main/java/com/example/CQUPT/ui/home/HomeFragment.java @@ -1,6 +1,7 @@ package com.example.CQUPT.ui.home; import android.app.DatePickerDialog; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -15,7 +16,9 @@ import com.example.CQUPT.adapter.CourseAdapter; import com.example.CQUPT.databinding.FragmentClassesBinding; +import com.example.CQUPT.ui.settings.SettingsFragment; import com.google.android.material.button.MaterialButton; +import com.google.android.material.snackbar.Snackbar; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -31,7 +34,7 @@ public class HomeFragment extends Fragment { private Calendar currentDate; private SimpleDateFormat dateFormat; private SimpleDateFormat weekDayFormat; - private Calendar semesterStartDate; // 添加学期开始日期 + private Calendar semesterStartDate; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -42,6 +45,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, initializeFormats(); setupRecyclerView(); setupDateNavigation(); + setupRetryButton(); observeViewModel(); updateDateDisplay(); @@ -81,6 +85,12 @@ private void setupDateNavigation() { dateButton.setOnClickListener(v -> showDatePicker()); } + private void setupRetryButton() { + binding.retryButton.setOnClickListener(v -> { + loadCoursesForDate(currentDate.getTime()); + }); + } + private void navigateDay(int offset) { currentDate.add(Calendar.DAY_OF_MONTH, offset); updateDateDisplay(); @@ -119,9 +129,55 @@ private void loadCoursesForDate(Date date) { } private void observeViewModel() { + // 观察课程数据 homeViewModel.getCourses().observe(getViewLifecycleOwner(), courses -> { courseAdapter.setCourses(courses); + updateViewVisibility(courses.isEmpty()); + }); + + // 观察加载状态 + homeViewModel.getIsLoading().observe(getViewLifecycleOwner(), isLoading -> { + binding.loadingProgress.setVisibility(isLoading ? View.VISIBLE : View.GONE); + binding.recyclerCourses.setVisibility(isLoading ? View.GONE : View.VISIBLE); + binding.errorCard.setVisibility(View.GONE); + }); + + // 观察错误信息 + homeViewModel.getErrorMessage().observe(getViewLifecycleOwner(), errorMessage -> { + if (errorMessage != null) { + if (errorMessage.equals("请先在设置中配置学号")) { + showStudentIdError(); + } else { + showError(errorMessage); + } + } else { + binding.errorCard.setVisibility(View.GONE); + } + }); + } + + private void updateViewVisibility(boolean isEmpty) { + binding.emptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE); + binding.recyclerCourses.setVisibility(isEmpty ? View.GONE : View.VISIBLE); + } + + private void showError(String message) { + binding.errorCard.setVisibility(View.VISIBLE); + binding.errorText.setText(message); + binding.recyclerCourses.setVisibility(View.GONE); + binding.emptyView.setVisibility(View.GONE); + } + + private void showStudentIdError() { + binding.errorCard.setVisibility(View.VISIBLE); + binding.errorText.setText("请先设置学号才能查看课表"); + binding.retryButton.setText("去设置"); + binding.retryButton.setOnClickListener(v -> { + // 跳转到设置页面 + startActivity(new Intent(requireContext(), SettingsFragment.class)); }); + binding.recyclerCourses.setVisibility(View.GONE); + binding.emptyView.setVisibility(View.GONE); } @Override diff --git a/app/src/main/java/com/example/CQUPT/ui/home/HomeViewModel.java b/app/src/main/java/com/example/CQUPT/ui/home/HomeViewModel.java index 11bb552..0203e0e 100644 --- a/app/src/main/java/com/example/CQUPT/ui/home/HomeViewModel.java +++ b/app/src/main/java/com/example/CQUPT/ui/home/HomeViewModel.java @@ -1,27 +1,50 @@ package com.example.CQUPT.ui.home; +import android.app.Application; +import android.content.SharedPreferences; +import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; +import androidx.preference.PreferenceManager; +import com.example.CQUPT.api.RetrofitClient; +import com.example.CQUPT.api.TimetableResponse; +import com.example.CQUPT.api.CourseSchedule; import com.example.CQUPT.model.Course; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Locale; -public class HomeViewModel extends ViewModel { +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +public class HomeViewModel extends AndroidViewModel { + private static final String PREF_STUDENT_ID = "student_id"; private final MutableLiveData> mCourses; private final MutableLiveData mCurrentDate; + private final MutableLiveData isLoading; + private final MutableLiveData errorMessage; + private final SimpleDateFormat apiDateFormat; + private final SimpleDateFormat apiTimeFormat; + private final SharedPreferences sharedPreferences; - public HomeViewModel() { + public HomeViewModel(Application application) { + super(application); mCourses = new MutableLiveData<>(); mCurrentDate = new MutableLiveData<>(); - loadCoursesForDate(new Date()); // 加载当天课程 - // 设置当前日期 - mCurrentDate.setValue("2024年1月1日 星期一"); + isLoading = new MutableLiveData<>(false); + errorMessage = new MutableLiveData<>(); + apiDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + apiTimeFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application); + + loadCoursesForDate(new Date()); } public LiveData> getCourses() { @@ -32,47 +55,90 @@ public LiveData getCurrentDate() { return mCurrentDate; } + public LiveData getIsLoading() { + return isLoading; + } + + public LiveData getErrorMessage() { + return errorMessage; + } + public void setCurrentDate(String date) { mCurrentDate.setValue(date); } public void loadCoursesForDate(Date date) { - // TODO: 在实际应用中,这里应该从数据库或网络加载指定日期的课程数据 - // 现在使用模拟数据进行演示 - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); - + String studentId = sharedPreferences.getString(PREF_STUDENT_ID, null); + if (studentId == null || studentId.equals("未设置")) { + errorMessage.setValue("请先在设置中配置学号"); + return; + } + + isLoading.setValue(true); + RetrofitClient.getInstance() + .getTimetableService() + .getTimetable(studentId) + .enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + isLoading.setValue(false); + if (response.isSuccessful() && response.body() != null && response.body().isSuccessful()) { + List schedules = response.body().getCourseSchedules(); + if (schedules != null) { + List coursesForDate = filterCoursesForDate(schedules, date); + mCourses.setValue(coursesForDate); + if (coursesForDate.isEmpty()) { + errorMessage.setValue("今天没有课程"); + } else { + errorMessage.setValue(null); + } + } + } else { + String error = response.body() != null ? response.body().getMessage() : "获取课程数据失败"; + errorMessage.setValue(error); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + isLoading.setValue(false); + errorMessage.setValue("网络请求失败: " + t.getMessage()); + } + }); + } + + private List filterCoursesForDate(List schedules, Date targetDate) { List courses = new ArrayList<>(); - - // 根据星期几返回不同的课程安排 - switch (dayOfWeek) { - case Calendar.MONDAY: - courses.add(new Course("高等数学", "08:00", "09:40", "教学楼2-301", "张三", 1, 16)); - courses.add(new Course("大学英语", "10:00", "11:40", "教学楼3-201", "李四", 1, 12)); - break; - case Calendar.TUESDAY: - courses.add(new Course("Java程序设计", "14:00", "15:40", "实验楼1-501", "王五", 2, 17)); - courses.add(new Course("数据结构", "16:00", "17:40", "教学楼4-401", "赵六", 3, 15)); - break; - case Calendar.WEDNESDAY: - courses.add(new Course("计算机网络", "08:00", "09:40", "教学楼1-301", "孙七", 1, 14)); - courses.add(new Course("操作系统", "10:00", "11:40", "实验楼2-401", "周八", 4, 16)); - break; - case Calendar.THURSDAY: - courses.add(new Course("软件工程", "14:00", "15:40", "教学楼5-201", "吴九", 2, 15)); - courses.add(new Course("数据库系统", "16:00", "17:40", "实验楼3-301", "郑十", 1, 13)); - break; - case Calendar.FRIDAY: - courses.add(new Course("计算机组成原理", "08:00", "09:40", "教学楼6-401", "刘一", 3, 18)); - courses.add(new Course("编译原理", "10:00", "11:40", "教学楼2-501", "陈二", 5, 16)); - break; - // 周末没有课程 - case Calendar.SATURDAY: - case Calendar.SUNDAY: - break; + String targetDateStr = apiDateFormat.format(targetDate); + + for (CourseSchedule schedule : schedules) { + try { + if (schedule.getDate().equals(targetDateStr)) { + // 转换时间格式 + String startTime = schedule.getStartTime().substring(0, 5); // 取"HH:mm"部分 + String endTime = schedule.getEndTime().substring(0, 5); // 取"HH:mm"部分 + + // 获取周数范围 + List weekNums = schedule.getWeekNums(); + int startWeek = weekNums.isEmpty() ? 1 : weekNums.get(0); + int endWeek = weekNums.isEmpty() ? 1 : weekNums.get(weekNums.size() - 1); + + Course course = new Course( + schedule.getTitle(), + startTime, + endTime, + schedule.getLocation(), + schedule.getData().getTeacherName(), + startWeek, + endWeek + ); + courses.add(course); + } + } catch (Exception e) { + e.printStackTrace(); + } } - - mCourses.setValue(courses); + + return courses; } } \ No newline at end of file diff --git a/app/src/main/java/com/example/CQUPT/ui/news/NewsFragment.java b/app/src/main/java/com/example/CQUPT/ui/news/NewsFragment.java index c949e5d..50498f6 100644 --- a/app/src/main/java/com/example/CQUPT/ui/news/NewsFragment.java +++ b/app/src/main/java/com/example/CQUPT/ui/news/NewsFragment.java @@ -80,7 +80,6 @@ private void setupRecyclerView() { newsRecyclerView.setAdapter(newsAdapter); newsAdapter.setOnItemClickListener(newsItem -> { - // TODO: 实现新闻详情页面跳转 Bundle args = new Bundle(); args.putString("id", newsItem.getId()); args.putString("title", newsItem.getTitle()); @@ -152,7 +151,7 @@ private void showError(String message) { } private String getWeeklySummary() { - // 这里应该是调用后端API获取周摘要的逻辑 + // 这里是调用后端API获取周摘要的逻辑 // 为了演示,这里返回一个模拟的摘要 return "这是本周新闻的AI摘要。包含了重要事件1、重要事件2、重要事件3等内容..."; } diff --git a/app/src/main/java/com/example/CQUPT/ui/settings/SettingsFragment.java b/app/src/main/java/com/example/CQUPT/ui/settings/SettingsFragment.java index 775917d..2e279b8 100644 --- a/app/src/main/java/com/example/CQUPT/ui/settings/SettingsFragment.java +++ b/app/src/main/java/com/example/CQUPT/ui/settings/SettingsFragment.java @@ -3,9 +3,11 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.text.InputType; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.TextView; import android.widget.Switch; @@ -22,10 +24,12 @@ public class SettingsFragment extends Fragment { private TextView startPageValue; private TextView sessionValue; + private TextView studentIdValue; private Switch appNotificationSwitch; private SharedPreferences sharedPreferences; private static final String PREF_START_PAGE = "start_page"; private static final String PREF_SESSION_ID = "session_id"; + private static final String PREF_STUDENT_ID = "student_id"; private static final String PREF_APP_NOTIFICATION_ENABLED = "app_notification_enabled"; @Nullable @@ -38,20 +42,24 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c // 初始化视图 startPageValue = view.findViewById(R.id.start_page_value); sessionValue = view.findViewById(R.id.session_value); + studentIdValue = view.findViewById(R.id.student_id_value); appNotificationSwitch = view.findViewById(R.id.app_notification_switch); View startPageContainer = view.findViewById(R.id.start_page_container); View sessionContainer = view.findViewById(R.id.session_container); + View studentIdContainer = view.findViewById(R.id.student_id_container); View appNotificationContainer = view.findViewById(R.id.app_notification_container); // 设置当前值 updateStartPageValue(); updateSessionValue(); + updateStudentIdValue(); boolean notificationEnabled = sharedPreferences.getBoolean(PREF_APP_NOTIFICATION_ENABLED, false); appNotificationSwitch.setChecked(notificationEnabled); // 设置点击事件 startPageContainer.setOnClickListener(v -> showStartPageDialog()); sessionContainer.setOnClickListener(v -> startLoginWebView()); + studentIdContainer.setOnClickListener(v -> showStudentIdDialog()); appNotificationContainer.setOnClickListener(v -> startAppNotificationSettings()); appNotificationSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { sharedPreferences.edit().putBoolean(PREF_APP_NOTIFICATION_ENABLED, isChecked).apply(); @@ -74,6 +82,34 @@ private void updateSessionValue() { sessionValue.setText(sessionId); } + private void updateStudentIdValue() { + String studentId = sharedPreferences.getString(PREF_STUDENT_ID, "未设置"); + studentIdValue.setText(studentId); + } + + private void showStudentIdDialog() { + EditText input = new EditText(requireContext()); + input.setInputType(InputType.TYPE_CLASS_NUMBER); + input.setHint("请输入学号"); + String currentStudentId = sharedPreferences.getString(PREF_STUDENT_ID, ""); + if (!"未设置".equals(currentStudentId)) { + input.setText(currentStudentId); + } + + new AlertDialog.Builder(requireContext()) + .setTitle("设置学号") + .setView(input) + .setPositiveButton("确定", (dialog, which) -> { + String studentId = input.getText().toString().trim(); + if (!studentId.isEmpty()) { + sharedPreferences.edit().putString(PREF_STUDENT_ID, studentId).apply(); + updateStudentIdValue(); + } + }) + .setNegativeButton("取消", null) + .show(); + } + private void startLoginWebView() { Intent intent = new Intent(requireContext(), LoginWebViewActivity.class); startActivity(intent); diff --git a/app/src/main/java/com/example/CQUPT/ui/slideshow/SlideshowFragment.java b/app/src/main/java/com/example/CQUPT/ui/slideshow/SlideshowFragment.java index cee0df1..2dc76a3 100644 --- a/app/src/main/java/com/example/CQUPT/ui/slideshow/SlideshowFragment.java +++ b/app/src/main/java/com/example/CQUPT/ui/slideshow/SlideshowFragment.java @@ -44,6 +44,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, } private void importCalendar() { + //隐藏收起键盘 KeyboardUtils.hideKeyboard(requireActivity()); String studentId = binding.studentIdInput.getText().toString(); if (studentId.isEmpty()) { diff --git a/app/src/main/res/layout/fragment_classes.xml b/app/src/main/res/layout/fragment_classes.xml index c9fba5b..043b6b0 100644 --- a/app/src/main/res/layout/fragment_classes.xml +++ b/app/src/main/res/layout/fragment_classes.xml @@ -57,6 +57,56 @@ app:layout_constraintTop_toBottomOf="@id/dateNavigationLayout" tools:listitem="@layout/item_course" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + #FFBB86FC #FF6200EE #FF3700B3 + #FFF3E5F5 #FF03DAC5 #FF018786 #FF000000