diff --git a/src/main/java/net/micode/notes/tool/DataUtils.java b/src/main/java/net/micode/notes/tool/DataUtils.java index 5a8b867..82e8d10 100644 --- a/src/main/java/net/micode/notes/tool/DataUtils.java +++ b/src/main/java/net/micode/notes/tool/DataUtils.java @@ -130,6 +130,29 @@ public class DataUtils { return true; } + // 检查目标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; + } + } + cursor.close(); + } + + if (!isFolder) { + Log.d(TAG, "target id is not a folder: " + folderId); + return false; + } + ArrayList operationList = new ArrayList(); for (long id : ids) { ContentProviderOperation.Builder builder = ContentProviderOperation diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 3d3c90f..f4c1511 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -115,10 +115,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; @@ -183,7 +183,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_list); - + // 获取隐私空间ID Intent intent = getIntent(); if (intent != null) { @@ -192,7 +192,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mCurrentPrivacySpaceId = ""; } } - + initResources(); initBackground(); @@ -254,17 +254,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt protected Boolean doInBackground(Void... unused) { HashSet ids = mNotesListAdapter.getSelectedItemIds(); for (Long id : ids) { - // 查询当前便签的置顶状态 + // 查询当前项目的置顶状态 Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI, - new String[] { NoteColumns.IS_PINNED }, + new String[] { NoteColumns.IS_PINNED, NoteColumns.TYPE }, NoteColumns.ID + "=?", new String[] { String.valueOf(id) }, null); if (cursor != null && cursor.moveToFirst()) { boolean isPinned = cursor.getInt(0) > 0; + int type = cursor.getInt(1); cursor.close(); - // 更新置顶状态 + // 更新置顶状态,支持笔记和文件夹 ContentValues values = new ContentValues(); values.put(NoteColumns.IS_PINNED, isPinned ? 0 : 1); values.put(NoteColumns.PIN_PRIORITY, isPinned ? 0 : System.currentTimeMillis()); @@ -403,7 +404,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } mState = ListEditState.NOTE_LIST; mModeCallBack = new ModeCallback(); - + // 检查是否处于隐私空间中 updatePrivacySpaceUI(); @@ -575,6 +576,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt if (tagsMenu != null) { tagsMenu.setOnMenuItemClickListener(this); } + // 确保移动菜单项在有文件夹时可见 + if (mMoveMenu != null) { + if (DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this); + } + } mActionMode = mode; mNotesListAdapter.setChoiceMode(true); mNotesListView.setLongClickable(false); @@ -741,6 +751,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private String mSelectedTag = ""; private void startAsyncNotesListQuery() { + // 临时删除功能:输入"delete:1"删除ID为1的项目 + if (!TextUtils.isEmpty(mSearchQuery) && mSearchQuery.equals("delete:1")) { + Log.d(TAG, "User requested to delete item with ID 1"); + deleteItemById(1); + mSearchQuery = ""; + return; + } + String selection; String[] selectionArgs; @@ -961,6 +979,30 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } + /** + * 删除指定ID的项目(用于处理特殊情况,如消失的文件夹) + */ + private void deleteItemById(long itemId) { + // 直接更新项目的父ID为回收站,绕过batchMoveToFolder的检查 + ContentValues values = new ContentValues(); + 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, + 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(); + } else { + Log.e(TAG, "Failed to move item " + itemId + " to trash"); + Toast.makeText(this, "删除失败,请重试", Toast.LENGTH_SHORT).show(); + } + + startAsyncNotesListQuery(); + } + private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); @@ -1025,7 +1067,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt startAsyncNotesListQuery(); } } - + /** * 更新隐私空间相关的UI元素 */ @@ -1108,6 +1150,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt ContentValues values = new ContentValues(); values.put(NoteColumns.SNIPPET, name); values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + values.put(NoteColumns.PARENT_ID, mCurrentFolderId); mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); } dialog.dismiss(); @@ -1145,10 +1188,37 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt public void onBackPressed() { switch (mState) { case SUB_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; + // 查询当前文件夹的父文件夹ID + Cursor cursor = mContentResolver.query( + Notes.CONTENT_NOTE_URI, + new String[] { NoteColumns.PARENT_ID, NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String[] { String.valueOf(mCurrentFolderId) }, + null + ); + + if (cursor != null && cursor.moveToFirst()) { + long parentId = cursor.getLong(0); + String folderName = cursor.getString(1); + cursor.close(); + + if (parentId == Notes.ID_ROOT_FOLDER) { + // 返回到根文件夹 + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mTitleBar.setVisibility(View.GONE); + } else { + // 返回到上一级文件夹 + mCurrentFolderId = parentId; + mState = ListEditState.SUB_FOLDER; + mTitleBar.setText(folderName); + mTitleBar.setVisibility(View.VISIBLE); + } + } else if (cursor != null) { + cursor.close(); + } + startAsyncNotesListQuery(); - mTitleBar.setVisibility(View.GONE); break; case CALL_RECORD_FOLDER: mCurrentFolderId = Notes.ID_ROOT_FOLDER; @@ -1367,7 +1437,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt if (view instanceof NotesListItem) { NoteItemData item = ((NotesListItem) view).getItemData(); if (mNotesListAdapter.isInChoiceMode()) { - if (item.getType() == Notes.TYPE_NOTE) { + if (item.getType() == Notes.TYPE_NOTE || item.getType() == Notes.TYPE_FOLDER) { position = position - mNotesListView.getHeaderViewsCount(); mModeCallBack.onItemCheckedStateChanged(null, position, id, !mNotesListAdapter.isSelectedItem(position)); @@ -1377,21 +1447,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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"); + Log.e(TAG, "Wrong note type in NOTE_LIST or SUB_FOLDER"); } break; - case SUB_FOLDER: case CALL_RECORD_FOLDER: if (item.getType() == Notes.TYPE_NOTE) { openNode(item); } else { - Log.e(TAG, "Wrong note type in SUB_FOLDER"); + Log.e(TAG, "Wrong note type in CALL_RECORD_FOLDER"); } break; default: @@ -1403,30 +1473,34 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } private void startQueryDestinationFolders() { - String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; - selection = (mState == ListEditState.NOTE_LIST) ? selection: - "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + // 查询所有有效的文件夹,包括普通文件夹、系统文件夹和根文件夹 + // 排除当前文件夹和回收站文件夹 + String selection = "(" + NoteColumns.TYPE + "=? OR " + NoteColumns.TYPE + "=?) AND " + NoteColumns.ID + "<>? AND " + NoteColumns.ID + "<>?"; + String[] selectionArgs = new String[] { + String.valueOf(Notes.TYPE_FOLDER), + String.valueOf(Notes.TYPE_SYSTEM), + String.valueOf(mCurrentFolderId), + String.valueOf(Notes.ID_TRASH_FOLER) + }; mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, FoldersListAdapter.PROJECTION, selection, - new String[] { - String.valueOf(Notes.TYPE_FOLDER), - String.valueOf(Notes.ID_TRASH_FOLER), - String.valueOf(mCurrentFolderId) - }, + selectionArgs, NoteColumns.MODIFIED_DATE + " DESC"); } public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData(); - if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + Log.d(TAG, "Long click item: id=" + id + ", type=" + mFocusNoteDataItem.getType() + ", parentId=" + mFocusNoteDataItem.getParentId()); + if ((mFocusNoteDataItem.getType() == Notes.TYPE_NOTE || mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) && !mNotesListAdapter.isInChoiceMode()) { if (mNotesListView.startActionMode(mModeCallBack) != null) { mModeCallBack.onItemCheckedStateChanged(null, position, id, true); mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + return true; } else { Log.e(TAG, "startActionMode fails"); } @@ -1445,7 +1519,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Cursor cursor = mContentResolver.query( Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.TAGS }, - NoteColumns.TAGS + " <> '' AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + NoteColumns.TAGS + " <> ''", null, NoteColumns.TAGS + " ASC" ); @@ -1562,7 +1636,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Cursor cursor = mContentResolver.query( Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.TAGS }, - NoteColumns.TAGS + " <> '' AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + NoteColumns.TAGS + " <> '' AND (" + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " OR " + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + ")", null, NoteColumns.TAGS + " ASC" ); @@ -1622,14 +1696,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(); } @@ -1641,7 +1715,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt if (mBackgroundContainer == null) { return; } - + try { if (BACKGROUND_TYPE_DEFAULT.equals(mCurrentBackgroundType)) { // 使用默认背景 @@ -1659,23 +1733,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(); @@ -1703,7 +1777,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; @@ -1715,7 +1789,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } editor.putInt(PREFERENCE_BACKGROUND + "_index", index); } - + editor.apply(); } @@ -1725,12 +1799,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) { @@ -1751,7 +1825,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } }); - + builder.show(); } diff --git a/src/main/java/net/micode/notes/ui/NotesListAdapter.java b/src/main/java/net/micode/notes/ui/NotesListAdapter.java index 2e715da..3a0f8e7 100644 --- a/src/main/java/net/micode/notes/ui/NotesListAdapter.java +++ b/src/main/java/net/micode/notes/ui/NotesListAdapter.java @@ -159,7 +159,8 @@ public class NotesListAdapter extends CursorAdapter { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { if (cursor.moveToPosition(i)) { - if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { + int type = NoteItemData.getNoteType(cursor); + if (type == Notes.TYPE_NOTE || type == Notes.TYPE_FOLDER) { setCheckedItem(i, checked); } } @@ -308,7 +309,8 @@ public class NotesListAdapter extends CursorAdapter { for (int i = 0; i < getCount(); i++) { Cursor c = (Cursor) getItem(i); if (c != null) { - if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { + int type = NoteItemData.getNoteType(c); + if (type == Notes.TYPE_NOTE || type == Notes.TYPE_FOLDER) { mNotesCount++; } } else { diff --git a/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/main/java/net/micode/notes/ui/NotesListItem.java index 738835d..5dfef3b 100644 --- a/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -94,7 +94,7 @@ public class NotesListItem extends LinearLayout { */ public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { // 设置复选框可见性和选中状态 - if (choiceMode && data.getType() == Notes.TYPE_NOTE) { + if (choiceMode && (data.getType() == Notes.TYPE_NOTE || data.getType() == Notes.TYPE_FOLDER)) { mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(checked); } else { @@ -135,6 +135,14 @@ public class NotesListItem extends LinearLayout { + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setVisibility(View.GONE); + // 设置置顶图标 + if (data.isPinned()) { + mPinned.setVisibility(View.VISIBLE); + } else { + mPinned.setVisibility(View.GONE); + } + // 设置锁定图标 + mLocked.setVisibility(View.GONE); } else { // 普通笔记 String title = data.getTitle(); diff --git a/src/main/res/menu/sub_folder.xml b/src/main/res/menu/sub_folder.xml index b00de26..ee41cc7 100644 --- a/src/main/res/menu/sub_folder.xml +++ b/src/main/res/menu/sub_folder.xml @@ -21,4 +21,8 @@ + + \ No newline at end of file