diff --git a/src/main/java/net/micode/notes/tool/DataUtils.java b/src/main/java/net/micode/notes/tool/DataUtils.java index 82e8d10..42b64f7 100644 --- a/src/main/java/net/micode/notes/tool/DataUtils.java +++ b/src/main/java/net/micode/notes/tool/DataUtils.java @@ -131,21 +131,33 @@ public class DataUtils { } // 检查目标ID是否是一个文件夹(TYPE_FOLDER或TYPE_SYSTEM) - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, folderId), - new String[] { NoteColumns.TYPE }, - null, - null, - null); - boolean isFolder = false; - if (cursor != null) { - if (cursor.moveToFirst()) { - int type = cursor.getInt(0); - if (type == Notes.TYPE_FOLDER || type == Notes.TYPE_SYSTEM) { - isFolder = true; + + // 特殊处理系统文件夹ID,它们是逻辑ID,不是实际的数据库记录ID + if (folderId == Notes.ID_ROOT_FOLDER || + folderId == Notes.ID_TEMPARAY_FOLDER || + folderId == Notes.ID_CALL_RECORD_FOLDER || + folderId == Notes.ID_TRASH_FOLER || + folderId == Notes.ID_PRIVACY_SPACE_FOLDER) { + isFolder = true; + Log.d(TAG, "Target is a system folder: " + folderId); + } else { + // 对于非系统文件夹,查询数据库验证 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, folderId), + new String[] { NoteColumns.TYPE }, + null, + null, + null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + int type = cursor.getInt(0); + if (type == Notes.TYPE_FOLDER || type == Notes.TYPE_SYSTEM) { + isFolder = true; + } } + cursor.close(); } - cursor.close(); } if (!isFolder) { @@ -456,4 +468,52 @@ public class DataUtils { } return snippet; } + + /** + * 获取指定文件夹中的便签数量 + *

+ * 查询指定文件夹中包含的便签数量 + *

+ * + * @param resolver ContentResolver对象 + * @param folderId 文件夹ID + * @return 便签数量 + */ + public static int getNoteCount(ContentResolver resolver, long folderId) { + Cursor cursor = resolver.query( + Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(folderId) }, + null + ); + + int count = 0; + if (cursor != null) { + if (cursor.moveToFirst()) { + try { + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get note count failed:" + e.toString()); + } finally { + cursor.close(); + } + } + } + return count; + } + + /** + * 获取未分类的便签数量 + *

+ * 查询直接在根文件夹中的便签数量,即未分类的便签 + *

+ * + * @param resolver ContentResolver对象 + * @return 未分类的便签数量 + */ + public static int getUncategorizedNoteCount(ContentResolver resolver) { + // 未分类的便签就是直接在根文件夹中的便签 + return getNoteCount(resolver, Notes.ID_ROOT_FOLDER); + } } diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 29f4275..6ffe5f4 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -70,6 +70,12 @@ import android.widget.Toast; import android.widget.GridView; import android.widget.BaseAdapter; import android.view.ViewGroup; +import android.widget.ScrollView; +import androidx.drawerlayout.widget.DrawerLayout; +import android.view.Gravity; +import androidx.core.view.GravityCompat; +import java.util.List; +import java.util.ArrayList; import net.micode.notes.R; import net.micode.notes.data.Notes; @@ -115,10 +121,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt // 背景选项 private static final String[] BACKGROUND_OPTIONS = { - "高山流水", "风中树叶", "长河落日" + "高山流水", "风中树叶", "长河落日" }; private static final int[] BACKGROUND_RESOURCES = { - R.drawable.background_mountain, R.drawable.background_leaves, R.drawable.background_sunset + R.drawable.background_mountain, R.drawable.background_leaves, R.drawable.background_sunset }; private ListEditState mState; @@ -152,9 +158,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private NoteItemData mFocusNoteDataItem; - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO; + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER; - private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.PARENT_ID + "=?) OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; + private static final String ROOT_FOLDER_SELECTION = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=?"; // 隐私空间相关常量 private static final int REQUEST_CODE_PRIVACY_SPACE = 104; @@ -179,11 +185,39 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private int mCurrentBackgroundResource = R.drawable.list_background; private String mCurrentBackgroundPath = ""; + // 侧边栏相关变量 + private Button mSidebarToggleButton; + private ListView mFolderListView; + private SidebarAdapter mFolderAdapter; + private ListView mUserFolderListView; + private SidebarAdapter mUserFolderAdapter; + private ImageView mAddFolderButton; + + // 标签选择相关 + private String mSelectedTag = ""; + + // 分类侧边栏相关 + private SidebarAdapter mCategoryAdapter = null; + private ListView mCategoryListView = null; + + // 标签侧边栏相关 + private SidebarAdapter mTagAdapter = null; + private ListView mTagListView = null; + + // 快速视图相关 + private SidebarAdapter mQuickViewAdapter = null; + private ListView mQuickViewListView = null; + + // 统计相关 + private TextView mTotalNotesTextView = null; + private TextView mPinnedNotesTextView = null; + private TextView mTodoNotesTextView = null; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_list); - + // 获取隐私空间ID Intent intent = getIntent(); if (intent != null) { @@ -192,8 +226,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mCurrentPrivacySpaceId = ""; } } - + initResources(); + initSidebar(); initBackground(); /** @@ -370,13 +405,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); mPrivacyBackButton = (Button) findViewById(R.id.btn_privacy_back); mPrivacyBackButton.setOnClickListener(this); + mSidebarToggleButton = (Button) findViewById(R.id.btn_sidebar_toggle); + mSidebarToggleButton.setOnClickListener(this); mDispatch = false; mDispatchY = 0; mOriginY = 0; - mTitleBar = (TextView) findViewById(R.id.tv_title_bar); mMainContent = (LinearLayout) findViewById(R.id.main_content); // 获取根视图作为背景容器 - mBackgroundContainer = (RelativeLayout) findViewById(R.id.note_list_root); + mBackgroundContainer = (RelativeLayout) findViewById(R.id.main_content_container); if (mBackgroundContainer == null) { // 如果找不到,使用content view View contentView = getWindow().getDecorView().findViewById(android.R.id.content); @@ -404,7 +440,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } mState = ListEditState.NOTE_LIST; mModeCallBack = new ModeCallback(); - + // 检查是否处于隐私空间中 updatePrivacySpaceUI(); @@ -747,8 +783,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }; - // 当前选中的标签,用于筛选 - private String mSelectedTag = ""; + private void startAsyncNotesListQuery() { // 临时删除功能:输入"delete:1"删除ID为1的项目 @@ -766,45 +801,69 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt String baseSelection = ""; ArrayList argsList = new ArrayList<>(); - // 根据是否有搜索关键词构建不同的查询条件 - if (!TextUtils.isEmpty(mSearchQuery)) { - // 有搜索关键词的情况 - if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { - // 根文件夹:需要将搜索条件应用到两个部分 - baseSelection = "((" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=? AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?))" - + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0 AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)))"; - argsList.add(String.valueOf(mCurrentFolderId)); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); + // 处理未分类情况 + if (mCurrentFolderId == -1) { + // 未分类:查询所有不在文件夹中的便签 + if (!TextUtils.isEmpty(mSearchQuery)) { + // 有搜索关键词的情况 + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=" + Notes.ID_ROOT_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)"; argsList.add("%" + mSearchQuery + "%"); argsList.add("%" + mSearchQuery + "%"); argsList.add("%" + mSearchQuery + "%"); } else { - // 普通文件夹 - baseSelection = NORMAL_SELECTION + " AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)"; - argsList.add(String.valueOf(mCurrentFolderId)); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); + // 没有搜索关键词的情况 + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=" + Notes.ID_ROOT_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; } } else { - // 没有搜索关键词的情况 - baseSelection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; - argsList.add(String.valueOf(mCurrentFolderId)); + // 普通文件夹或根文件夹 + // 根据是否有搜索关键词构建不同的查询条件 + if (!TextUtils.isEmpty(mSearchQuery)) { + // 有搜索关键词的情况 + if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + // 全部:查询所有文件夹中的便签,包括多级文件夹,但排除回收站 + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)"; + argsList.add("%" + mSearchQuery + "%"); + argsList.add("%" + mSearchQuery + "%"); + argsList.add("%" + mSearchQuery + "%"); + } else { + // 普通文件夹:同时查询下一级文件夹和当前文件夹中的便签 + baseSelection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " OR " + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + ") AND " + NoteColumns.PARENT_ID + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)"; + argsList.add(String.valueOf(mCurrentFolderId)); + argsList.add("%" + mSearchQuery + "%"); + argsList.add("%" + mSearchQuery + "%"); + argsList.add("%" + mSearchQuery + "%"); + } + } else { + // 没有搜索关键词的情况 + if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + // 全部:查询所有文件夹中的便签,包括多级文件夹,但排除回收站 + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; + } else { + // 普通文件夹:同时查询下一级文件夹和当前文件夹中的便签 + baseSelection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " OR " + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + ") AND " + NoteColumns.PARENT_ID + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER; + argsList.add(String.valueOf(mCurrentFolderId)); + } + } } // 添加标签筛选条件 if (!TextUtils.isEmpty(mSelectedTag)) { - if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + if (mCurrentFolderId == -1) { + // 未分类情况 + if (baseSelection.contains("AND")) { + baseSelection += " AND " + NoteColumns.TAGS + " LIKE ?"; + } else { + baseSelection += " AND " + NoteColumns.TAGS + " LIKE ?"; + } + argsList.add("%" + mSelectedTag + "%"); + } else if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { // 根文件夹需要特殊处理 String tagCondition = " AND " + NoteColumns.TAGS + " LIKE ?"; // 根据是否有搜索关键词构建不同的标签筛选条件 if (!TextUtils.isEmpty(mSearchQuery)) { // 有搜索关键词的情况 - baseSelection = "((" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=? AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)" + tagCondition + ")" - + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0 AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)" + tagCondition + "))"; + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND (" + NoteColumns.TITLE + " LIKE ? OR " + NoteColumns.SNIPPET + " LIKE ? OR " + NoteColumns.TAGS + " LIKE ?)" + tagCondition; // 重新构建参数列表 argsList.clear(); @@ -813,20 +872,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt argsList.add("%" + mSearchQuery + "%"); argsList.add("%" + mSearchQuery + "%"); argsList.add("%" + mSelectedTag + "%"); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSearchQuery + "%"); - argsList.add("%" + mSelectedTag + "%"); } else { // 没有搜索关键词的情况 - baseSelection = "((" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?" + tagCondition + ")" - + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0" + tagCondition + "))"; + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + tagCondition; // 重新构建参数列表 argsList.clear(); argsList.add(String.valueOf(mCurrentFolderId)); argsList.add("%" + mSelectedTag + "%"); - argsList.add("%" + mSelectedTag + "%"); } } else { // 普通文件夹 @@ -843,19 +896,27 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt if (!TextUtils.isEmpty(mCurrentPrivacySpaceId)) { // 在隐私空间中,只显示该隐私空间的便签 String privacyCondition = NoteColumns.PRIVACY_SPACE_ID + "=?"; - if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + if (mCurrentFolderId == -1) { + // 未分类情况 + baseSelection = "(" + baseSelection + ") AND " + privacyCondition; + argsList.add(mCurrentPrivacySpaceId); + } else if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { // 根文件夹需要特殊处理 if (!TextUtils.isEmpty(mSearchQuery) || !TextUtils.isEmpty(mSelectedTag)) { // 有搜索关键词或标签的情况 baseSelection = "(" + baseSelection + ") AND " + privacyCondition; } else { // 没有搜索关键词和标签的情况 - baseSelection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=? AND " + privacyCondition + ")"; + baseSelection = NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "=? AND " + privacyCondition; // 重新构建参数列表 argsList.clear(); argsList.add(String.valueOf(mCurrentFolderId)); argsList.add(mCurrentPrivacySpaceId); } + if (!TextUtils.isEmpty(mSearchQuery) || !TextUtils.isEmpty(mSelectedTag)) { + // 有搜索关键词或标签的情况,需要添加隐私空间ID参数 + argsList.add(mCurrentPrivacySpaceId); + } } else { // 普通文件夹 if (!TextUtils.isEmpty(mSearchQuery) || !TextUtils.isEmpty(mSelectedTag)) { @@ -863,40 +924,39 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt baseSelection = "(" + baseSelection + ") AND " + privacyCondition; } else { // 没有搜索关键词和标签的情况 - baseSelection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + privacyCondition; + baseSelection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_FOLDER + " AND " + privacyCondition; // 重新构建参数列表 argsList.clear(); argsList.add(String.valueOf(mCurrentFolderId)); argsList.add(mCurrentPrivacySpaceId); } - } - if (!TextUtils.isEmpty(mSearchQuery) || !TextUtils.isEmpty(mSelectedTag)) { - // 有搜索关键词或标签的情况,需要添加隐私空间ID参数 - argsList.add(mCurrentPrivacySpaceId); + if (!TextUtils.isEmpty(mSearchQuery) || !TextUtils.isEmpty(mSelectedTag)) { + // 有搜索关键词或标签的情况,需要添加隐私空间ID参数 + argsList.add(mCurrentPrivacySpaceId); + } } } else { // 不在隐私空间中,只显示非隐私空间的便签 String privacyCondition = "(" + NoteColumns.PRIVACY_SPACE_ID + "='' OR " + NoteColumns.PRIVACY_SPACE_ID + " IS NULL)"; - if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { - // 根文件夹需要特殊处理 - baseSelection = "(" + baseSelection + ") AND " + privacyCondition; - } else { - // 普通文件夹 - if (baseSelection.contains("AND")) { - baseSelection += " AND " + privacyCondition; - } else { - baseSelection += " WHERE " + privacyCondition; - } - } + baseSelection = "(" + baseSelection + ") AND " + privacyCondition; } selection = baseSelection; selectionArgs = argsList.toArray(new String[argsList.size()]); + // 修改排序方式,确保文件夹显示在便签之前 + String sortOrder = ""; + if (mCurrentFolderId != Notes.ID_ROOT_FOLDER && mCurrentFolderId != -1) { + // 普通文件夹:先按类型排序(文件夹在前),再按修改日期排序 + sortOrder = NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"; + } else { + // 根文件夹或未分类:按原排序方式 + sortOrder = NoteColumns.IS_PINNED + " DESC," + NoteColumns.PIN_PRIORITY + " DESC," + + NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"; + } mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs, - NoteColumns.IS_PINNED + " DESC," + NoteColumns.PIN_PRIORITY + " DESC," + - NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); + sortOrder); } private final class BackgroundQueryHandler extends AsyncQueryHandler { @@ -953,12 +1013,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } private void batchDelete() { + if (mNotesListAdapter == null) { + Log.e(TAG, "mNotesListAdapter is null in batchDelete"); + Toast.makeText(this, "删除失败:适配器为空", Toast.LENGTH_SHORT).show(); + return; + } + + final HashSet selectedIds = mNotesListAdapter.getSelectedItemIds(); + if (selectedIds == null || selectedIds.isEmpty()) { + Log.e(TAG, "No items selected for deletion"); + Toast.makeText(this, "删除失败:未选择便签", Toast.LENGTH_SHORT).show(); + return; + } + new AsyncTask>() { protected HashSet doInBackground(Void... unused) { HashSet widgets = mNotesListAdapter.getSelectedWidget(); // Always move notes to trash folder for better user experience - if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter - .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + if (!DataUtils.batchMoveToFolder(mContentResolver, selectedIds, Notes.ID_TRASH_FOLER)) { Log.e(TAG, "Move notes to trash folder error, should not happens"); } return widgets; @@ -974,7 +1046,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - mModeCallBack.finishActionMode(); + if (mModeCallBack != null) { + mModeCallBack.finishActionMode(); + } + // 重新加载侧边栏数据,更新数量显示 + loadSidebarData(); + // 重新加载便签列表,让删除的便签立即消失 + startAsyncNotesListQuery(); + // 显示删除成功的提示 + Toast.makeText(NotesListActivity.this, "已移至回收站", Toast.LENGTH_SHORT).show(); } }.execute(); } @@ -988,10 +1068,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt values.put(NoteColumns.PARENT_ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.LOCAL_MODIFIED, 1); values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - - int rows = mContentResolver.update(Notes.CONTENT_NOTE_URI, values, + + int rows = mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?", new String[] { String.valueOf(itemId) }); - + if (rows > 0) { Log.d(TAG, "Successfully moved item " + itemId + " to trash"); Toast.makeText(this, "项目已移至回收站", Toast.LENGTH_SHORT).show(); @@ -999,7 +1079,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Log.e(TAG, "Failed to move item " + itemId + " to trash"); Toast.makeText(this, "删除失败,请重试", Toast.LENGTH_SHORT).show(); } - + startAsyncNotesListQuery(); } @@ -1067,9 +1147,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mCurrentPrivacySpaceId = ""; updatePrivacySpaceUI(); startAsyncNotesListQuery(); + } else if (v.getId() == R.id.btn_sidebar_toggle) { + // 切换侧边栏显示/隐藏 + toggleSidebar(); } } - + /** * 更新隐私空间相关的UI元素 */ @@ -1162,6 +1245,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); values.put(NoteColumns.PARENT_ID, mCurrentFolderId); mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + // 重新加载侧边栏数据,确保新建的文件夹能显示出来 + loadSidebarData(); } dialog.dismiss(); } @@ -1208,11 +1293,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt new String[] { String.valueOf(mCurrentFolderId) }, null ); - + if (cursor != null && cursor.moveToFirst()) { long parentId = cursor.getLong(0); String folderName = cursor.getString(1); - + if (parentId == Notes.ID_ROOT_FOLDER) { // 返回到根文件夹 mCurrentFolderId = Notes.ID_ROOT_FOLDER; @@ -1255,7 +1340,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + startAsyncNotesListQuery(); break; case CALL_RECORD_FOLDER: @@ -1471,6 +1556,504 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt from.startActivityIfNeeded(intent, -1); } + private View mAllItemView; + private TextView mAllTitleView; + private TextView mAllCountView; + private View mUncategorizedItemView; + private TextView mUncategorizedTitleView; + private TextView mUncategorizedCountView; + + private void initSidebar() { + mAllItemView = findViewById(R.id.all_item); + mAllTitleView = findViewById(R.id.all_title); + mAllCountView = findViewById(R.id.all_count); + mUncategorizedItemView = findViewById(R.id.uncategorized_item); + mUncategorizedTitleView = findViewById(R.id.uncategorized_title); + mUncategorizedCountView = findViewById(R.id.uncategorized_count); + mUserFolderListView = findViewById(R.id.user_folder_list); + mAddFolderButton = findViewById(R.id.add_folder_button); + + // 加载侧边栏数据 + loadSidebarData(); + + // 设置全部项点击事件 + mAllItemView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // 更新选中状态 + updateTopItemSelection(true); + // 切换到全部 + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + startAsyncNotesListQuery(); + // 关闭侧边栏 + toggleSidebar(); + } + }); + + // 设置未分类项点击事件 + mUncategorizedItemView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // 更新选中状态 + updateTopItemSelection(false); + // 切换到未分类 + mCurrentFolderId = -1; + startAsyncNotesListQuery(); + // 关闭侧边栏 + toggleSidebar(); + } + }); + + // 设置用户文件夹列表点击事件 + mUserFolderListView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(position); + if (item != null) { + // 更新选中状态 + updateUserFolderSelection(position); + // 切换到对应的文件夹 + mCurrentFolderId = item.getCategoryId(); + startAsyncNotesListQuery(); + // 关闭侧边栏 + toggleSidebar(); + } + } + }); + + // 设置用户文件夹列表长按事件,实现删除文件夹功能 + mUserFolderListView.setOnItemLongClickListener(new OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(position); + if (item != null) { + final long folderId = item.getCategoryId(); + if (folderId == Notes.ID_ROOT_FOLDER) { + // 根文件夹不能删除 + Toast.makeText(NotesListActivity.this, "根文件夹不能删除", Toast.LENGTH_SHORT).show(); + return true; + } + + // 显示确认删除对话框 + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle("确认删除"); + builder.setMessage("确定要删除文件夹 \"" + item.getTitle() + "\" 吗?"); + builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // 删除文件夹 + deleteFolder(folderId); + // 重新加载侧边栏数据 + loadSidebarData(); + // 显示删除成功提示 + Toast.makeText(NotesListActivity.this, "文件夹已删除", Toast.LENGTH_SHORT).show(); + } + }); + builder.setNegativeButton("取消", null); + builder.show(); + return true; + } + return false; + } + }); + + // 设置添加文件夹按钮点击事件 + mAddFolderButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + showCreateOrModifyFolderDialog(true); + // 关闭侧边栏 + toggleSidebar(); + } + }); + } + + private void loadSidebarData() { + // 加载全部项数据 + int totalNotesCount = getTotalNoteCount(); + mAllCountView.setText(String.valueOf(totalNotesCount)); + if (totalNotesCount > 0) { + mAllCountView.setVisibility(View.VISIBLE); + } else { + mAllCountView.setVisibility(View.GONE); + } + + // 加载未分类项数据 + int uncategorizedCount = DataUtils.getUncategorizedNoteCount(mContentResolver); + mUncategorizedCountView.setText(String.valueOf(uncategorizedCount)); + if (uncategorizedCount > 0) { + mUncategorizedCountView.setVisibility(View.VISIBLE); + } else { + mUncategorizedCountView.setVisibility(View.GONE); + } + + // 加载用户文件夹(递归加载,显示层级结构) + List userFolderItems = new ArrayList<>(); + loadFoldersRecursive(Notes.ID_ROOT_FOLDER, userFolderItems, 0); + + // 创建并设置用户文件夹适配器 + mUserFolderAdapter = new SidebarAdapter(this, userFolderItems); + mUserFolderListView.setAdapter(mUserFolderAdapter); + + // 默认选中全部项 + updateTopItemSelection(true); + } + + private void updateFolderSelection(int position) { + // 取消所有顶部选项的选中状态 + if (mFolderAdapter != null) { + for (int i = 0; i < mFolderAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mFolderAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + // 设置当前选项为选中状态 + SidebarAdapter.SidebarItem currentItem = (SidebarAdapter.SidebarItem) mFolderAdapter.getItem(position); + if (currentItem != null) { + currentItem.setSelected(true); + } + mFolderAdapter.notifyDataSetChanged(); + } + // 取消所有用户文件夹的选中状态 + if (mUserFolderAdapter != null) { + for (int i = 0; i < mUserFolderAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + mUserFolderAdapter.notifyDataSetChanged(); + } + } + + private void updateTopItemSelection(boolean selectAll) { + // 重置所有顶部选项的状态 + mAllItemView.setBackgroundColor(getResources().getColor(R.color.surface, null)); + mAllTitleView.setTextColor(getResources().getColor(R.color.onSurface, null)); + mAllCountView.setTextColor(getResources().getColor(R.color.primary, null)); + mUncategorizedItemView.setBackgroundColor(getResources().getColor(R.color.surface, null)); + mUncategorizedTitleView.setTextColor(getResources().getColor(R.color.onSurface, null)); + mUncategorizedCountView.setTextColor(getResources().getColor(R.color.primary, null)); + + // 取消所有用户文件夹的选中状态 + if (mUserFolderAdapter != null) { + for (int i = 0; i < mUserFolderAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + mUserFolderAdapter.notifyDataSetChanged(); + } + + // 设置选中状态 + if (selectAll) { + mAllItemView.setBackgroundColor(getResources().getColor(R.color.primary, null)); + mAllTitleView.setTextColor(getResources().getColor(R.color.onPrimary, null)); + mAllCountView.setTextColor(getResources().getColor(R.color.onPrimary, null)); + } else { + mUncategorizedItemView.setBackgroundColor(getResources().getColor(R.color.primary, null)); + mUncategorizedTitleView.setTextColor(getResources().getColor(R.color.onPrimary, null)); + mUncategorizedCountView.setTextColor(getResources().getColor(R.color.onPrimary, null)); + } + } + + private void updateUserFolderSelection(int position) { + // 重置所有顶部选项的状态 + mAllItemView.setBackgroundColor(getResources().getColor(R.color.surface, null)); + mAllTitleView.setTextColor(getResources().getColor(R.color.onSurface, null)); + mAllCountView.setTextColor(getResources().getColor(R.color.primary, null)); + mUncategorizedItemView.setBackgroundColor(getResources().getColor(R.color.surface, null)); + mUncategorizedTitleView.setTextColor(getResources().getColor(R.color.onSurface, null)); + mUncategorizedCountView.setTextColor(getResources().getColor(R.color.primary, null)); + + // 取消所有用户文件夹的选中状态 + if (mUserFolderAdapter != null) { + for (int i = 0; i < mUserFolderAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + + // 设置当前文件夹为选中状态 + SidebarAdapter.SidebarItem currentItem = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(position); + if (currentItem != null) { + currentItem.setSelected(true); + } + mUserFolderAdapter.notifyDataSetChanged(); + } + } + + /** + * 更新侧边栏的选中状态,根据当前文件夹ID选择对应的侧边栏项 + */ + private void updateSidebarSelection() { + if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + // 当前在根文件夹,选中全部项 + updateTopItemSelection(true); + } else if (mCurrentFolderId == -1) { + // 当前在未分类,选中未分类项 + updateTopItemSelection(false); + } else { + // 当前在用户文件夹,找到并选中对应的文件夹项 + if (mUserFolderAdapter != null) { + for (int i = 0; i < mUserFolderAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(i); + if (item != null && item.getCategoryId() == mCurrentFolderId) { + updateUserFolderSelection(i); + break; + } + } + } + } + } + + /** + * 获取所有文件夹中的便签总数,包括多级文件夹 + */ + private int getTotalNoteCount() { + // 递归获取所有文件夹中的便签数量 + return getNoteCountRecursive(Notes.ID_ROOT_FOLDER); + } + + /** + * 递归获取文件夹及其子文件夹中的便签数量 + */ + private int getNoteCountRecursive(long folderId) { + int count = 0; + + // 获取当前文件夹中的便签数量(只统计普通便签,不统计待办事项) + Cursor noteCursor = mContentResolver.query( + Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(folderId) }, + null + ); + + if (noteCursor != null) { + if (noteCursor.moveToFirst()) { + try { + count = noteCursor.getInt(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get note count failed:" + e.toString()); + } finally { + noteCursor.close(); + } + } + } + + // 获取当前文件夹中的子文件夹 + Cursor folderCursor = mContentResolver.query( + Notes.CONTENT_NOTE_URI, + new String[] { NoteColumns.ID }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(folderId) }, + null + ); + + if (folderCursor != null) { + while (folderCursor.moveToNext()) { + try { + long childFolderId = folderCursor.getLong(0); + // 递归获取子文件夹中的便签数量 + count += getNoteCountRecursive(childFolderId); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get folder id failed:" + e.toString()); + } + } + folderCursor.close(); + } + + return count; + } + + /** + * 递归加载文件夹及其子文件夹,显示层级结构 + */ + private void loadFoldersRecursive(long parentFolderId, List folderItems, int level) { + // 获取当前文件夹下的子文件夹 + Cursor folderCursor = mContentResolver.query( + Notes.CONTENT_NOTE_URI, + new String[]{NoteColumns.ID, NoteColumns.SNIPPET, NoteColumns.NOTES_COUNT}, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?", + new String[]{String.valueOf(Notes.TYPE_FOLDER), String.valueOf(parentFolderId)}, + NoteColumns.MODIFIED_DATE + " DESC" + ); + + if (folderCursor != null) { + while (folderCursor.moveToNext()) { + try { + long folderId = folderCursor.getLong(0); + String folderName = folderCursor.getString(1); + int noteCount = folderCursor.getInt(2); + + // 根据层级添加缩进 + StringBuilder indentedName = new StringBuilder(); + for (int i = 0; i < level; i++) { + indentedName.append(" "); // 每级添加两个空格的缩进 + } + indentedName.append(folderName); + + // 添加文件夹项 + folderItems.add(new SidebarAdapter.SidebarItem(indentedName.toString(), noteCount, folderId)); + + // 递归加载子文件夹 + loadFoldersRecursive(folderId, folderItems, level + 1); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "load folder failed:" + e.toString()); + } + } + folderCursor.close(); + } + } + + private void initCategoryData() { + // 模拟分类数据,实际应该从数据库获取 + List categories = new ArrayList<>(); + categories.add(new SidebarAdapter.SidebarItem("全部便签", 15, Notes.ID_ROOT_FOLDER)); + categories.add(new SidebarAdapter.SidebarItem("工作", 8, 1)); + categories.add(new SidebarAdapter.SidebarItem("生活", 5, 2)); + categories.add(new SidebarAdapter.SidebarItem("学习", 2, 3)); + + mCategoryAdapter = new SidebarAdapter(this, categories); + mCategoryListView.setAdapter(mCategoryAdapter); + + // 默认选中第一个分类 + if (!categories.isEmpty()) { + categories.get(0).setSelected(true); + mCategoryAdapter.notifyDataSetChanged(); + } + } + + private void initTagData() { + // 模拟标签数据,实际应该从数据库获取 + List tags = new ArrayList<>(); + tags.add(new SidebarAdapter.SidebarItem("重要", 5, "重要")); + tags.add(new SidebarAdapter.SidebarItem("紧急", 3, "紧急")); + tags.add(new SidebarAdapter.SidebarItem("想法", 4, "想法")); + tags.add(new SidebarAdapter.SidebarItem("计划", 2, "计划")); + + mTagAdapter = new SidebarAdapter(this, tags); + mTagListView.setAdapter(mTagAdapter); + } + + private void initQuickViewData() { + // 初始化快捷视图数据 + List quickViews = new ArrayList<>(); + quickViews.add(new SidebarAdapter.SidebarItem("全部便签", 15, SidebarAdapter.SidebarItem.QuickViewType.ALL)); + quickViews.add(new SidebarAdapter.SidebarItem("已置顶", 3, SidebarAdapter.SidebarItem.QuickViewType.PINNED)); + quickViews.add(new SidebarAdapter.SidebarItem("今天", 2, SidebarAdapter.SidebarItem.QuickViewType.TODAY)); + quickViews.add(new SidebarAdapter.SidebarItem("无标签", 6, SidebarAdapter.SidebarItem.QuickViewType.UNTAGGED)); + + mQuickViewAdapter = new SidebarAdapter(this, quickViews); + mQuickViewListView.setAdapter(mQuickViewAdapter); + + // 默认选中第一个快捷视图 + if (!quickViews.isEmpty()) { + quickViews.get(0).setSelected(true); + mQuickViewAdapter.notifyDataSetChanged(); + } + } + + private void initNoteStatistics() { + // 模拟统计数据,实际应该从数据库获取 + mTotalNotesTextView.setText("15"); + mPinnedNotesTextView.setText("3"); + mTodoNotesTextView.setText("2"); + } + + private void updateCategorySelection(int position) { + // 取消所有分类的选中状态 + if (mCategoryAdapter != null) { + for (int i = 0; i < mCategoryAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mCategoryAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + // 设置当前分类为选中状态 + SidebarAdapter.SidebarItem currentItem = (SidebarAdapter.SidebarItem) mCategoryAdapter.getItem(position); + if (currentItem != null) { + currentItem.setSelected(true); + } + mCategoryAdapter.notifyDataSetChanged(); + } + } + + private void updateTagSelection(int position) { + // 取消所有标签的选中状态 + if (mTagAdapter != null) { + for (int i = 0; i < mTagAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mTagAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + // 设置当前标签为选中状态 + SidebarAdapter.SidebarItem currentItem = (SidebarAdapter.SidebarItem) mTagAdapter.getItem(position); + if (currentItem != null) { + currentItem.setSelected(true); + } + mTagAdapter.notifyDataSetChanged(); + } + } + + private void updateQuickViewSelection(int position) { + // 取消所有快捷视图的选中状态 + if (mQuickViewAdapter != null) { + for (int i = 0; i < mQuickViewAdapter.getCount(); i++) { + SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mQuickViewAdapter.getItem(i); + if (item != null) { + item.setSelected(false); + } + } + // 设置当前快捷视图为选中状态 + SidebarAdapter.SidebarItem currentItem = (SidebarAdapter.SidebarItem) mQuickViewAdapter.getItem(position); + if (currentItem != null) { + currentItem.setSelected(true); + } + mQuickViewAdapter.notifyDataSetChanged(); + } + } + + private void applyQuickViewFilter(SidebarAdapter.SidebarItem.QuickViewType type) { + // 根据快捷视图类型应用不同的筛选条件 + switch (type) { + case ALL: + // 清除所有筛选条件 + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mSelectedTag = ""; + break; + case PINNED: + // 只显示已置顶的便签 + // 这里需要在startAsyncNotesListQuery中添加置顶筛选逻辑 + break; + case TODAY: + // 只显示今天创建或修改的便签 + // 这里需要在startAsyncNotesListQuery中添加日期筛选逻辑 + break; + case UNTAGGED: + // 只显示无标签的便签 + mSelectedTag = ""; + // 这里需要在startAsyncNotesListQuery中添加无标签筛选逻辑 + break; + } + } + + private void toggleSidebar() { + DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); + if (drawerLayout != null) { + if (drawerLayout.isDrawerOpen(GravityCompat.START)) { + drawerLayout.closeDrawer(GravityCompat.START); + } else { + drawerLayout.openDrawer(GravityCompat.START); + } + } + } + private class OnListItemClickListener implements OnItemClickListener { public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -1485,27 +2068,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return; } - switch (mState) { - case NOTE_LIST: - case SUB_FOLDER: - if (item.getType() == Notes.TYPE_FOLDER - || item.getType() == Notes.TYPE_SYSTEM) { - openFolder(item); - } else if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in NOTE_LIST or SUB_FOLDER"); - } - break; - case CALL_RECORD_FOLDER: - if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in CALL_RECORD_FOLDER"); - } - break; - default: - break; + // 处理便签点击 + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } + // 处理文件夹点击 + else if (item.getType() == Notes.TYPE_FOLDER) { + // 更新当前文件夹ID + mCurrentFolderId = item.getId(); + // 重新加载便签列表,显示该文件夹的内容 + startAsyncNotesListQuery(); + // 更新侧边栏的选中状态 + updateSidebarSelection(); } } } @@ -1736,14 +2310,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); mCurrentBackgroundType = sp.getString(PREFERENCE_BACKGROUND + "_type", BACKGROUND_TYPE_DEFAULT); mCurrentBackgroundPath = sp.getString(PREFERENCE_BACKGROUND + "_path", ""); - + if (BACKGROUND_TYPE_DEFAULT.equals(mCurrentBackgroundType)) { int backgroundIndex = sp.getInt(PREFERENCE_BACKGROUND + "_index", 0); if (backgroundIndex >= 0 && backgroundIndex < BACKGROUND_RESOURCES.length) { mCurrentBackgroundResource = BACKGROUND_RESOURCES[backgroundIndex]; } } - + // 更新背景 updateBackground(); } @@ -1755,7 +2329,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt if (mBackgroundContainer == null) { return; } - + try { if (BACKGROUND_TYPE_DEFAULT.equals(mCurrentBackgroundType)) { // 使用默认背景 @@ -1773,23 +2347,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt // 如果是内部存储路径 bitmap = android.graphics.BitmapFactory.decodeFile(mCurrentBackgroundPath); } - + if (bitmap != null) { // 计算屏幕尺寸 android.util.DisplayMetrics displayMetrics = new android.util.DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; - + // 缩放图片以适配屏幕 android.graphics.Bitmap scaledBitmap = android.graphics.Bitmap.createScaledBitmap( bitmap, screenWidth, screenHeight, true); - + android.graphics.drawable.BitmapDrawable drawable = new android.graphics.drawable.BitmapDrawable(getResources(), scaledBitmap); // 设置背景图片的缩放方式 drawable.setTileModeXY(android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP); mBackgroundContainer.setBackground(drawable); - + // 释放原始 bitmap if (bitmap != scaledBitmap) { bitmap.recycle(); @@ -1817,7 +2391,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt SharedPreferences.Editor editor = sp.edit(); editor.putString(PREFERENCE_BACKGROUND + "_type", mCurrentBackgroundType); editor.putString(PREFERENCE_BACKGROUND + "_path", mCurrentBackgroundPath); - + if (BACKGROUND_TYPE_DEFAULT.equals(mCurrentBackgroundType)) { // 找到当前背景资源的索引 int index = 0; @@ -1829,7 +2403,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } editor.putInt(PREFERENCE_BACKGROUND + "_index", index); } - + editor.apply(); } @@ -1839,12 +2413,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private void showBackgroundSelectorDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("选择背景"); - + // 创建背景选项数组,添加从相册选择的选项 String[] options = new String[BACKGROUND_OPTIONS.length + 1]; System.arraycopy(BACKGROUND_OPTIONS, 0, options, 0, BACKGROUND_OPTIONS.length); options[BACKGROUND_OPTIONS.length] = "从相册选择"; - + builder.setItems(options, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -1865,7 +2439,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } }); - + builder.show(); } diff --git a/src/main/java/net/micode/notes/ui/SidebarAdapter.java b/src/main/java/net/micode/notes/ui/SidebarAdapter.java new file mode 100644 index 0000000..060a3e1 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/SidebarAdapter.java @@ -0,0 +1,142 @@ +package net.micode.notes.ui; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import net.micode.notes.R; + +import java.util.List; + +public class SidebarAdapter extends BaseAdapter { + private Context mContext; + private List mItems; + + public SidebarAdapter(Context context, List items) { + mContext = context; + mItems = items; + } + + @Override + public int getCount() { + return mItems != null ? mItems.size() : 0; + } + + @Override + public Object getItem(int position) { + return mItems != null ? mItems.get(position) : null; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(mContext).inflate(R.layout.sidebar_item, parent, false); + } + + SidebarItem item = mItems.get(position); + TextView titleTextView = convertView.findViewById(R.id.item_title); + TextView countTextView = convertView.findViewById(R.id.item_count); + + titleTextView.setText(item.getTitle()); + if (item.getCount() > 0) { + countTextView.setVisibility(View.VISIBLE); + countTextView.setText(String.valueOf(item.getCount())); + } else { + countTextView.setVisibility(View.GONE); + } + + if (item.isSelected()) { + convertView.setBackgroundColor(mContext.getResources().getColor(R.color.primary, null)); + titleTextView.setTextColor(mContext.getResources().getColor(R.color.onPrimary, null)); + countTextView.setTextColor(mContext.getResources().getColor(R.color.onPrimary, null)); + } else { + convertView.setBackgroundColor(mContext.getResources().getColor(R.color.surface, null)); + titleTextView.setTextColor(mContext.getResources().getColor(R.color.onSurface, null)); + countTextView.setTextColor(mContext.getResources().getColor(R.color.primary, null)); + } + + return convertView; + } + + public static class SidebarItem { + private String mTitle; + private int mCount; + private boolean mSelected; + private String mTag; + private long mCategoryId; + private QuickViewType mQuickViewType; + + public SidebarItem(String title) { + mTitle = title; + mCount = 0; + mSelected = false; + } + + public SidebarItem(String title, int count) { + mTitle = title; + mCount = count; + mSelected = false; + } + + public SidebarItem(String title, int count, long categoryId) { + mTitle = title; + mCount = count; + mSelected = false; + mCategoryId = categoryId; + } + + public SidebarItem(String title, int count, String tag) { + mTitle = title; + mCount = count; + mSelected = false; + mTag = tag; + } + + public SidebarItem(String title, int count, QuickViewType type) { + mTitle = title; + mCount = count; + mSelected = false; + mQuickViewType = type; + } + + public String getTitle() { + return mTitle; + } + + public int getCount() { + return mCount; + } + + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + mSelected = selected; + } + + public String getTag() { + return mTag; + } + + public long getCategoryId() { + return mCategoryId; + } + + public QuickViewType getQuickViewType() { + return mQuickViewType; + } + + public enum QuickViewType { + ALL, PINNED, TODAY, UNTAGGED + } + } +} diff --git a/src/main/res/drawable/count_badge.xml b/src/main/res/drawable/count_badge.xml new file mode 100644 index 0000000..6e5f178 --- /dev/null +++ b/src/main/res/drawable/count_badge.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_folder_add.xml b/src/main/res/drawable/ic_folder_add.xml new file mode 100644 index 0000000..13f65ef --- /dev/null +++ b/src/main/res/drawable/ic_folder_add.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_format_color_bold.jpg b/src/main/res/drawable/ic_format_color_bold.jpg new file mode 100644 index 0000000..29c3985 Binary files /dev/null and b/src/main/res/drawable/ic_format_color_bold.jpg differ diff --git a/src/main/res/drawable/ic_format_color_bold.png b/src/main/res/drawable/ic_format_color_bold.png deleted file mode 100644 index b877ab8..0000000 Binary files a/src/main/res/drawable/ic_format_color_bold.png and /dev/null differ diff --git a/src/main/res/drawable/ic_format_color_fill.jpg b/src/main/res/drawable/ic_format_color_fill.jpg new file mode 100644 index 0000000..4913d74 Binary files /dev/null and b/src/main/res/drawable/ic_format_color_fill.jpg differ diff --git a/src/main/res/drawable/ic_format_color_fill.png b/src/main/res/drawable/ic_format_color_fill.png deleted file mode 100644 index ab75d8b..0000000 Binary files a/src/main/res/drawable/ic_format_color_fill.png and /dev/null differ diff --git a/src/main/res/drawable/ic_format_color_text.jpg b/src/main/res/drawable/ic_format_color_text.jpg new file mode 100644 index 0000000..1e1221c Binary files /dev/null and b/src/main/res/drawable/ic_format_color_text.jpg differ diff --git a/src/main/res/drawable/ic_format_color_text.png b/src/main/res/drawable/ic_format_color_text.png deleted file mode 100644 index 645e83f..0000000 Binary files a/src/main/res/drawable/ic_format_color_text.png and /dev/null differ diff --git a/src/main/res/drawable/ic_format_italic.jpg b/src/main/res/drawable/ic_format_italic.jpg new file mode 100644 index 0000000..12c85d7 Binary files /dev/null and b/src/main/res/drawable/ic_format_italic.jpg differ diff --git a/src/main/res/drawable/ic_format_italic.png b/src/main/res/drawable/ic_format_italic.png deleted file mode 100644 index bdb9a04..0000000 Binary files a/src/main/res/drawable/ic_format_italic.png and /dev/null differ diff --git a/src/main/res/drawable/ic_format_underlined.jpg b/src/main/res/drawable/ic_format_underlined.jpg new file mode 100644 index 0000000..6c6d44a Binary files /dev/null and b/src/main/res/drawable/ic_format_underlined.jpg differ diff --git a/src/main/res/drawable/ic_format_underlined.png b/src/main/res/drawable/ic_format_underlined.png deleted file mode 100644 index 31ea58c..0000000 Binary files a/src/main/res/drawable/ic_format_underlined.png and /dev/null differ diff --git a/src/main/res/layout/note_list.xml b/src/main/res/layout/note_list.xml index 636abff..1ad1008 100644 --- a/src/main/res/layout/note_list.xml +++ b/src/main/res/layout/note_list.xml @@ -15,175 +15,221 @@ limitations under the License. --> - - + + android:layout_height="match_parent"> - + + + +