|
|
|
|
@ -1,12 +1,17 @@
|
|
|
|
|
package net.micode.notes.ui;
|
|
|
|
|
|
|
|
|
|
import android.app.AlertDialog;
|
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
|
import android.content.ContentValues;
|
|
|
|
|
import android.database.Cursor;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
|
import android.widget.Button;
|
|
|
|
|
import android.widget.CalendarView;
|
|
|
|
|
import android.widget.EditText;
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
|
@ -16,18 +21,24 @@ import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
|
import net.micode.notes.R;
|
|
|
|
|
import net.micode.notes.data.Notes;
|
|
|
|
|
import net.micode.notes.data.Notes.NoteColumns;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Calendar;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import android.widget.ImageButton; // [关键修复] 导入图片按钮类
|
|
|
|
|
|
|
|
|
|
public class AgendaFragment extends Fragment {
|
|
|
|
|
private RecyclerView mRecyclerView;
|
|
|
|
|
private NotesListItemAdapter mAdapter;
|
|
|
|
|
private AgendaAdapter mAdapter; // [关键修复] 类型改为 AgendaAdapter
|
|
|
|
|
private CalendarView mCalendarView;
|
|
|
|
|
private TextView mTvEmpty;
|
|
|
|
|
private long mSelectedDayStart;
|
|
|
|
|
private long mSelectedDayEnd;
|
|
|
|
|
|
|
|
|
|
private TextView tvDateHeader;
|
|
|
|
|
private View emptyState;
|
|
|
|
|
private View emptyState; // <--- 确保有这一行
|
|
|
|
|
private long mSelectedDayStart, mSelectedDayEnd;
|
|
|
|
|
|
|
|
|
|
private boolean isCalendarExpanded = true;
|
|
|
|
|
private ImageButton btnToggleCalendar;
|
|
|
|
|
private int mQuickHour = -1;
|
|
|
|
|
private int mQuickMinute = -1;
|
|
|
|
|
private TextView tvQuickTime;
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@Override
|
|
|
|
|
@ -42,86 +53,157 @@ public class AgendaFragment extends Fragment {
|
|
|
|
|
mRecyclerView = view.findViewById(R.id.agenda_list);
|
|
|
|
|
tvDateHeader = view.findViewById(R.id.tv_agenda_date_header);
|
|
|
|
|
emptyState = view.findViewById(R.id.ll_empty_state);
|
|
|
|
|
btnToggleCalendar = view.findViewById(R.id.btn_toggle_calendar);
|
|
|
|
|
|
|
|
|
|
// [新增] 日历折叠逻辑
|
|
|
|
|
btnToggleCalendar.setOnClickListener(v -> {
|
|
|
|
|
if (isCalendarExpanded) {
|
|
|
|
|
// 执行收起
|
|
|
|
|
mCalendarView.setVisibility(View.GONE);
|
|
|
|
|
btnToggleCalendar.setImageResource(android.R.drawable.arrow_down_float);
|
|
|
|
|
isCalendarExpanded = false;
|
|
|
|
|
} else {
|
|
|
|
|
// 执行展开
|
|
|
|
|
mCalendarView.setVisibility(View.VISIBLE);
|
|
|
|
|
btnToggleCalendar.setImageResource(android.R.drawable.arrow_up_float);
|
|
|
|
|
isCalendarExpanded = true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
|
|
|
|
mAdapter = new NotesListItemAdapter(getContext());
|
|
|
|
|
mAdapter = new AgendaAdapter(getContext()); // [修复] 赋值
|
|
|
|
|
mRecyclerView.setAdapter(mAdapter);
|
|
|
|
|
|
|
|
|
|
// 默认显示今天
|
|
|
|
|
updateDateDisplay(Calendar.getInstance().get(Calendar.YEAR),
|
|
|
|
|
Calendar.getInstance().get(Calendar.MONTH),
|
|
|
|
|
Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
|
|
|
|
|
mCalendarView.setOnDateChangeListener((v, y, m, d) -> {
|
|
|
|
|
updateDateDisplay(y, m, d);
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
|
|
|
|
|
// [体验优化] 点击日期后,如果日历是展开的,则自动收起
|
|
|
|
|
if (isCalendarExpanded) {
|
|
|
|
|
mCalendarView.setVisibility(View.GONE);
|
|
|
|
|
btnToggleCalendar.setImageResource(android.R.drawable.arrow_down_float);
|
|
|
|
|
isCalendarExpanded = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置监听器
|
|
|
|
|
mAdapter.setOnAgendaActionListener(new AgendaAdapter.OnAgendaActionListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onToggleComplete(AgendaAdapter.AgendaItem item) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(NoteColumns.IS_COMPLETED, item.isCompleted ? 0 : 1);
|
|
|
|
|
getContext().getContentResolver().update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?", new String[]{String.valueOf(item.id)});
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDelete(AgendaAdapter.AgendaItem item) {
|
|
|
|
|
new AlertDialog.Builder(getContext())
|
|
|
|
|
.setTitle("确认删除")
|
|
|
|
|
.setMessage("删除此日程?")
|
|
|
|
|
.setPositiveButton("删除", (d, w) -> {
|
|
|
|
|
getContext().getContentResolver().delete(Notes.CONTENT_NOTE_URI, NoteColumns.ID + "=?", new String[]{String.valueOf(item.id)});
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
}).setNegativeButton("取消", null).show();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tvQuickTime = view.findViewById(R.id.tv_set_quick_time);
|
|
|
|
|
|
|
|
|
|
// 1. 点击“全天”弹出时间选择器
|
|
|
|
|
tvQuickTime.setOnClickListener(v -> {
|
|
|
|
|
Calendar now = Calendar.getInstance();
|
|
|
|
|
new android.app.TimePickerDialog(getContext(), (view1, hourOfDay, minute) -> {
|
|
|
|
|
mQuickHour = hourOfDay;
|
|
|
|
|
mQuickMinute = minute;
|
|
|
|
|
tvQuickTime.setText(String.format("%02d:%02d", hourOfDay, minute));
|
|
|
|
|
}, now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), true).show();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mCalendarView.setOnDateChangeListener((view1, year, month, dayOfMonth) -> {
|
|
|
|
|
updateDateDisplay(year, month, dayOfMonth);
|
|
|
|
|
// 2. 修改 btn_quick_add 的点击逻辑
|
|
|
|
|
view.findViewById(R.id.btn_quick_add).setOnClickListener(v -> {
|
|
|
|
|
EditText et = view.findViewById(R.id.et_quick_add);
|
|
|
|
|
String title = et.getText().toString().trim();
|
|
|
|
|
if (TextUtils.isEmpty(title)) return;
|
|
|
|
|
|
|
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
|
cv.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
|
|
|
|
|
cv.put(NoteColumns.IS_AGENDA, 1);
|
|
|
|
|
cv.put(NoteColumns.SNIPPET, title);
|
|
|
|
|
|
|
|
|
|
// 计算具体时间
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.setTimeInMillis(mSelectedDayStart); // 基于日历选中的那一天
|
|
|
|
|
|
|
|
|
|
if (mQuickHour != -1) {
|
|
|
|
|
cal.set(Calendar.HOUR_OF_DAY, mQuickHour);
|
|
|
|
|
cal.set(Calendar.MINUTE, mQuickMinute);
|
|
|
|
|
cv.put(NoteColumns.TIME_LABEL, String.format("%02d:%02d", mQuickHour, mQuickMinute));
|
|
|
|
|
cv.put(NoteColumns.AGENDA_DATE, cal.getTimeInMillis());
|
|
|
|
|
cv.put(NoteColumns.AGENDA_END_DATE, cal.getTimeInMillis()); // 点事件
|
|
|
|
|
} else {
|
|
|
|
|
cv.put(NoteColumns.TIME_LABEL, "全天");
|
|
|
|
|
cv.put(NoteColumns.AGENDA_DATE, mSelectedDayStart);
|
|
|
|
|
cv.put(NoteColumns.AGENDA_END_DATE, mSelectedDayEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv.put(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
|
|
|
|
|
cv.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
getContext().getContentResolver().insert(Notes.CONTENT_NOTE_URI, cv);
|
|
|
|
|
|
|
|
|
|
// 重置 UI
|
|
|
|
|
et.setText("");
|
|
|
|
|
tvQuickTime.setText("全天");
|
|
|
|
|
mQuickHour = -1;
|
|
|
|
|
mQuickMinute = -1;
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mCalendarView.setOnDateChangeListener((v, y, m, d) -> {
|
|
|
|
|
updateDateDisplay(y, m, d);
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
updateDateDisplay(Calendar.getInstance().get(Calendar.YEAR), Calendar.getInstance().get(Calendar.MONTH), Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
|
|
|
|
|
loadAgendaData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateDateDisplay(int year, int month, int day) {
|
|
|
|
|
tvDateHeader.setText(String.format("%d年%d月%d日 的日程", year, month + 1, day));
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.set(year, month, day);
|
|
|
|
|
calculateDayRange(cal.getTimeInMillis());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算选中日期的 [00:00:00, 23:59:59] 时间戳
|
|
|
|
|
private void calculateDayRange(long timeInMillis) {
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.setTimeInMillis(timeInMillis);
|
|
|
|
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
|
|
cal.set(Calendar.MINUTE, 0);
|
|
|
|
|
cal.set(Calendar.SECOND, 0);
|
|
|
|
|
cal.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
cal.set(year, month, day, 0, 0, 0);
|
|
|
|
|
mSelectedDayStart = cal.getTimeInMillis();
|
|
|
|
|
|
|
|
|
|
cal.set(Calendar.HOUR_OF_DAY, 23);
|
|
|
|
|
cal.set(Calendar.MINUTE, 59);
|
|
|
|
|
cal.set(Calendar.SECOND, 59);
|
|
|
|
|
cal.set(year, month, day, 23, 59, 59);
|
|
|
|
|
mSelectedDayEnd = cal.getTimeInMillis();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadAgendaData() {
|
|
|
|
|
// 增加空检查,防止 fragment 已经 detach 时还执行异步回调
|
|
|
|
|
if (!isAdded() || getContext() == null) return;
|
|
|
|
|
|
|
|
|
|
ContentResolver resolver = getContext().getContentResolver();
|
|
|
|
|
|
|
|
|
|
// 使用异步查询(推荐)或者简单的后台线程
|
|
|
|
|
new Thread(() -> {
|
|
|
|
|
String selection = NoteColumns.IS_AGENDA + "=1 AND "
|
|
|
|
|
+ NoteColumns.AGENDA_DATE + " >= ? AND "
|
|
|
|
|
+ NoteColumns.AGENDA_DATE + " <= ?";
|
|
|
|
|
|
|
|
|
|
String[] selectionArgs = new String[] {
|
|
|
|
|
String.valueOf(mSelectedDayStart),
|
|
|
|
|
String.valueOf(mSelectedDayEnd)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 执行查询
|
|
|
|
|
final Cursor cursor = resolver.query(
|
|
|
|
|
Notes.CONTENT_NOTE_URI,
|
|
|
|
|
NoteItemData.PROJECTION,
|
|
|
|
|
selection,
|
|
|
|
|
selectionArgs,
|
|
|
|
|
NoteColumns.AGENDA_DATE + " ASC"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 切回主线程更新 UI
|
|
|
|
|
if (getActivity() != null) {
|
|
|
|
|
getActivity().runOnUiThread(() -> {
|
|
|
|
|
mAdapter.changeCursor(cursor);
|
|
|
|
|
if (cursor == null || cursor.getCount() == 0) {
|
|
|
|
|
emptyState.setVisibility(View.VISIBLE);
|
|
|
|
|
mRecyclerView.setVisibility(View.GONE);
|
|
|
|
|
} else {
|
|
|
|
|
emptyState.setVisibility(View.GONE);
|
|
|
|
|
mRecyclerView.setVisibility(View.VISIBLE);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (getContext() == null) return;
|
|
|
|
|
Cursor cursor = getContext().getContentResolver().query(
|
|
|
|
|
Notes.CONTENT_NOTE_URI,
|
|
|
|
|
new String[]{NoteColumns.ID, NoteColumns.SNIPPET, NoteColumns.TIME_LABEL, NoteColumns.AGENDA_DATE, NoteColumns.IS_COMPLETED},
|
|
|
|
|
// [关键修复] 删掉最后的 OR agenda_end_date = 0
|
|
|
|
|
NoteColumns.IS_AGENDA + "=1 AND " + NoteColumns.AGENDA_DATE + "<=? AND " + NoteColumns.AGENDA_END_DATE + ">=?",
|
|
|
|
|
new String[]{String.valueOf(mSelectedDayEnd), String.valueOf(mSelectedDayStart)},
|
|
|
|
|
null);
|
|
|
|
|
|
|
|
|
|
List<AgendaAdapter.AgendaItem> list = new ArrayList<>();
|
|
|
|
|
if (cursor != null) {
|
|
|
|
|
while (cursor.moveToNext()) {
|
|
|
|
|
AgendaAdapter.AgendaItem item = new AgendaAdapter.AgendaItem();
|
|
|
|
|
item.id = cursor.getLong(0);
|
|
|
|
|
item.title = cursor.getString(1);
|
|
|
|
|
item.timeLabel = cursor.getString(2);
|
|
|
|
|
item.startTime = cursor.getLong(3);
|
|
|
|
|
item.isCompleted = cursor.getInt(4) == 1;
|
|
|
|
|
list.add(item);
|
|
|
|
|
}
|
|
|
|
|
}).start();
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
mAdapter.updateData(list);
|
|
|
|
|
// [关键修复] 使用 emptyState 控制显示
|
|
|
|
|
if (emptyState != null) {
|
|
|
|
|
emptyState.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|