From b24598159ff38649a0b8d0964f87a6b5919cedd3 Mon Sep 17 00:00:00 2001 From: gy <2293314358@qq.com> Date: Fri, 30 Jan 2026 21:08:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B5=84=E6=BA=90=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/net/micode/notes/data/Notes.java | 4 + .../notes/data/NotesDatabaseHelper.java | 30 ++- .../net/micode/notes/ui/AgendaFragment.java | 216 ++++++++++++------ .../net/micode/notes/ui/NoteEditActivity.java | 89 ++++++++ .../micode/notes/ui/NotesListActivity.java | 26 ++- 5 files changed, 288 insertions(+), 77 deletions(-) diff --git a/src/Notes-master/src/net/micode/notes/data/Notes.java b/src/Notes-master/src/net/micode/notes/data/Notes.java index 1a7d510..a5ab921 100644 --- a/src/Notes-master/src/net/micode/notes/data/Notes.java +++ b/src/Notes-master/src/net/micode/notes/data/Notes.java @@ -237,6 +237,10 @@ public class Notes { * Type: TEXT */ public static final String EMOTION_TAG = "emotion_tag"; + + public static final String AGENDA_END_DATE = "agenda_end_date"; + + public static final String TIME_LABEL = "time_label"; } // [新增] 用户账号表列名定义 diff --git a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index aa05ee7..833a0a4 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -30,7 +30,7 @@ import net.micode.notes.data.Notes.NoteColumns; public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME = "note.db"; - private static final int DB_VERSION = 9; + private static final int DB_VERSION = 10; public interface TABLE { public static final String NOTE = "note"; @@ -75,7 +75,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.IS_AGENDA + " INTEGER DEFAULT 0," + NoteColumns.AGENDA_DATE + " INTEGER DEFAULT 0," + NoteColumns.IS_COMPLETED + " INTEGER DEFAULT 0," + - NoteColumns.EMOTION_TAG + " TEXT" + + NoteColumns.EMOTION_TAG + " TEXT," + + NoteColumns.AGENDA_END_DATE + " INTEGER DEFAULT 0," + + NoteColumns.TIME_LABEL + " TEXT DEFAULT ''" + ")"; // [新增] 用户表建表语句 @@ -380,6 +382,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { oldVersion = 9; } + // [新增点] 处理 v10 的升级 + if (oldVersion < 10) { + upgradeToV10(db); + oldVersion = 10; + } + if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); @@ -391,6 +399,22 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } } + private void upgradeToV10(SQLiteDatabase db) { + Log.d(TAG, "Upgrading database to version 10..."); + // 使用 try-catch 确保即使字段已存在(手动清过数据的情况)也不会崩 + try { + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.AGENDA_END_DATE + " INTEGER DEFAULT 0"); + } catch (Exception e) { + Log.w(TAG, "Column agenda_end_date already exists"); + } + + try { + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TIME_LABEL + " TEXT DEFAULT ''"); + } catch (Exception e) { + Log.w(TAG, "Column time_label already exists"); + } + } + // 确保存在 V6 升级方法,防止字段缺失 private void upgradeToV6(SQLiteDatabase db) { // 使用 try-catch 忽略"列已存在"的错误,防止重复添加崩溃 @@ -451,5 +475,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 2. 创建新表 try { db.execSQL(CREATE_USER_TABLE_SQL); } catch(Exception e){ Log.e(TAG, "Create user table failed: " + e); } try { db.execSQL(CREATE_CHAT_TABLE_SQL); } catch(Exception e){ Log.e(TAG, "Create chat table failed: " + e); } + try { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN agenda_end_date INTEGER DEFAULT 0"); } catch(Exception e){} + try { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN time_label TEXT DEFAULT ''"); } catch(Exception e){} } } diff --git a/src/Notes-master/src/net/micode/notes/ui/AgendaFragment.java b/src/Notes-master/src/net/micode/notes/ui/AgendaFragment.java index bc3de59..a85da6e 100644 --- a/src/Notes-master/src/net/micode/notes/ui/AgendaFragment.java +++ b/src/Notes-master/src/net/micode/notes/ui/AgendaFragment.java @@ -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 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); + } } } \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java index cbf0aae..ec0ee41 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java @@ -61,6 +61,9 @@ import android.view.MotionEvent; import android.net.Uri; import android.graphics.drawable.Drawable; import android.graphics.Color; +import net.micode.notes.tool.DeepSeekHelper; +import android.app.ProgressDialog; +import net.micode.notes.data.Notes.NoteColumns; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; @@ -1200,12 +1203,98 @@ public class NoteEditActivity extends AppCompatActivity implements OnClickListen case R.id.menu_delete_remind: mWorkingNote.setAlertDate(0, false); break; + case R.id.menu_add_to_agenda_ai: + performAIAnalysis(); // [新增] + break; default: break; } return true; } + // 3. 添加核心处理方法 + private void performAIAnalysis() { + // 获取当前便签内容(自动保存先,确保内容最新) + saveNote(); + String content = mWorkingNote.getContent(); + + // 简单的去HTML标签处理(如果之前没做,这里简单做一个,避免把HTML标签发给AI) + String plainText = android.text.Html.fromHtml(content).toString(); + + if (TextUtils.isEmpty(plainText.trim())) { + showToast(R.string.error_note_empty_for_clock); // 复用“内容为空”的提示 + return; + } + + // 显示加载框 + ProgressDialog progressDialog = new ProgressDialog(this); + progressDialog.setMessage("DeepSeek 正在分析日程..."); + progressDialog.setCancelable(false); + progressDialog.show(); + + // 调用 AI + DeepSeekHelper.analyzeContent(plainText, new DeepSeekHelper.AICallback() { + @Override + public void onSuccess(java.util.List results) { + progressDialog.dismiss(); + int count = 0; + + for (DeepSeekHelper.AgendaResult res : results) { + try { + // --- 第一步:插入 Note 表 (增强型元数据) --- + ContentValues noteValues = new ContentValues(); + noteValues.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + noteValues.put(NoteColumns.IS_AGENDA, 1); + + // 在 NoteEditActivity.java 中确保 end_date 有值 + noteValues.put(Notes.NoteColumns.AGENDA_DATE, res.startTime); + // [关键修复] 如果 AI 返回的结束时间等于开始时间(点事件), + // 也要确保 end_date 被写入,而不是保留默认值 0 + noteValues.put(Notes.NoteColumns.AGENDA_END_DATE, res.endTime > 0 ? res.endTime : res.startTime); + // [新增] 存储显示标签(如 "16:00" 或 "12月") + noteValues.put(NoteColumns.TIME_LABEL, res.timeLabel); + + noteValues.put(NoteColumns.SNIPPET, res.title); + noteValues.put(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER); + noteValues.put(NoteColumns.LOCAL_MODIFIED, 1); + + Uri newNoteUri = getContentResolver().insert(Notes.CONTENT_NOTE_URI, noteValues); + + if (newNoteUri != null) { + long newNoteId = ContentUris.parseId(newNoteUri); + + // --- 第二步:插入 Data 表 (正文内容) --- + ContentValues dataValues = new ContentValues(); + dataValues.put(Notes.DataColumns.NOTE_ID, newNoteId); + dataValues.put(Notes.DataColumns.MIME_TYPE, Notes.TextNote.CONTENT_ITEM_TYPE); + // AI 生成的备注信息 + dataValues.put(Notes.DataColumns.CONTENT, "AI 识别日程:" + res.title); + + getContentResolver().insert(Notes.CONTENT_DATA_URI, dataValues); + count++; + } + } catch (Exception e) { + Log.e(TAG, "Insert AI Agenda Failed", e); + } + } + + if (count > 0) { + Toast.makeText(NoteEditActivity.this, + "成功添加 " + count + " 个日程到日历", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(NoteEditActivity.this, + "添加失败,请查看日志", Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onFailure(String error) { + progressDialog.dismiss(); + Toast.makeText(NoteEditActivity.this, error, Toast.LENGTH_LONG).show(); + } + }); + } + private void setReminder() { DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); d.setOnDateTimeSetListener(new OnDateTimeSetListener() { diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java index 289d154..ae8108f 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java @@ -500,7 +500,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe } else if (itemId == R.id.nav_agenda) { // --- 切换到日程视图模式 --- findViewById(R.id.fragment_container).setVisibility(View.VISIBLE); - if (getSupportActionBar() != null) getSupportActionBar().setTitle("智能日程"); + if (getSupportActionBar() != null) getSupportActionBar().setTitle("日程"); // 使用 TAG 查找,避免重复创建引发的 GMS 校验冲突 androidx.fragment.app.Fragment agenda = fm.findFragmentByTag("AGENDA"); @@ -982,28 +982,38 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe String[] selectionArgs; if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) { + // --- 分支 1:搜索模式 --- String query = getIntent().getStringExtra(SearchManager.QUERY); + // [修改点] 搜索时除了排除回收站,也要排除日程项,保证主列表搜索结果纯净 selection = NoteColumns.SNIPPET + " LIKE ? AND " + NoteColumns.PARENT_ID + "<>" - + Notes.ID_TRASH_FOLER; + + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.IS_AGENDA + "=0"; selectionArgs = new String[] { - "%" + query + "%" + "%" + query + "%" }; } else if (mState == ListEditState.PRIVATE_FOLDER) { - // [新增] 私密模式查询:查询所有 is_private = 1 的便签 - selection = NoteColumns.IS_PRIVATE + "=1 AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + // --- 分支 2:私密文件夹模式 --- + // [修改点] 即使在私密空间,我们也希望日程只出现在“日程”Tab中,所以追加 IS_AGENDA=0 + selection = NoteColumns.IS_PRIVATE + "=1 AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + + " AND " + NoteColumns.IS_AGENDA + "=0"; selectionArgs = null; } else { - // [修改] 普通模式:必须增加 is_private = 0 的条件,防止私密便签显示在普通列表中 + // --- 分支 3:普通/根目录/自定义文件夹模式 --- + // ROOT_FOLDER_SELECTION 或 NORMAL_SELECTION 已经包含了 parent_id 的过滤 selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; - // 拼接过滤私密的条件 - selection += " AND " + NoteColumns.IS_PRIVATE + "=0"; + + // [核心修改点]: + // 1. 排除私密便签 (IS_PRIVATE=0) + // 2. 排除日程项 (IS_AGENDA=0) + // 这样日程项就会从“便签”Tab 彻底消失,只在“日程”Tab 通过特定查询显示 + selection += " AND " + NoteColumns.IS_PRIVATE + "=0 AND " + NoteColumns.IS_AGENDA + "=0"; selectionArgs = new String[] { String.valueOf(mCurrentFolderId) }; } + // 执行查询:排序规则保持不变(类型、置顶、修改时间) mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs, NoteColumns.TYPE + " DESC," + NoteColumns.IS_PINNED + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); -- 2.34.1