|
|
|
|
@ -61,6 +61,8 @@ import android.widget.PopupMenu;
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.net.Uri; // [新增] 修复错误 1 & 2
|
|
|
|
|
import android.graphics.drawable.Drawable; // [新增] 修复错误 3 & 4
|
|
|
|
|
|
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
|
import androidx.appcompat.view.ActionMode;
|
|
|
|
|
@ -102,7 +104,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
|
|
|
|
|
|
|
|
|
|
private enum ListEditState {
|
|
|
|
|
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
|
|
|
|
|
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER, PRIVATE_FOLDER, RECYCLE_BIN
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private ListEditState mState;
|
|
|
|
|
@ -131,6 +133,10 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
private ModeCallback mModeCallBack;
|
|
|
|
|
|
|
|
|
|
private View mFolderNavScrollView;
|
|
|
|
|
|
|
|
|
|
private androidx.swiperefreshlayout.widget.SwipeRefreshLayout mSwipeRefreshLayout;
|
|
|
|
|
|
|
|
|
|
private static final String TAG = "NotesListActivity";
|
|
|
|
|
|
|
|
|
|
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
|
|
|
|
|
@ -213,6 +219,103 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 进入回收站
|
|
|
|
|
private void enterRecycleBin() {
|
|
|
|
|
mState = ListEditState.RECYCLE_BIN;
|
|
|
|
|
mCurrentFolderId = Notes.ID_TRASH_FOLER; // 关键:指向垃圾桶 ID (-3)
|
|
|
|
|
|
|
|
|
|
mTitleBar.setVisibility(View.VISIBLE);
|
|
|
|
|
mTitleBar.setText(R.string.title_recycle_bin);
|
|
|
|
|
|
|
|
|
|
// 1. 隐藏多余 UI
|
|
|
|
|
mAddNewNote.hide();
|
|
|
|
|
if (mFolderNavScrollView != null) mFolderNavScrollView.setVisibility(View.GONE);
|
|
|
|
|
if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setEnabled(false);
|
|
|
|
|
|
|
|
|
|
// 2. 强制切换为瀑布流 (Staggered Grid)
|
|
|
|
|
// 无论用户设置是什么,回收站强制瀑布流
|
|
|
|
|
mRecyclerView.setLayoutManager(new androidx.recyclerview.widget.StaggeredGridLayoutManager(
|
|
|
|
|
2, androidx.recyclerview.widget.StaggeredGridLayoutManager.VERTICAL));
|
|
|
|
|
// 添加间距装饰器 (防止重复添加需先移除)
|
|
|
|
|
while (mRecyclerView.getItemDecorationCount() > 0) {
|
|
|
|
|
mRecyclerView.removeItemDecorationAt(0);
|
|
|
|
|
}
|
|
|
|
|
mRecyclerView.addItemDecoration(new WaterfallItemDecoration(20));
|
|
|
|
|
mAdapter.notifyDataSetChanged();
|
|
|
|
|
|
|
|
|
|
// 3. 刷新菜单 (隐藏系统菜单)
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
|
|
|
|
|
|
// 4. 查询数据
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
|
|
|
|
|
getIntent().setAction(Intent.ACTION_MAIN);
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (mState) {
|
|
|
|
|
case SUB_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
break;
|
|
|
|
|
case CALL_RECORD_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
break;
|
|
|
|
|
case PRIVATE_FOLDER:
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
// [修改] 恢复 FAB
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
// [新增] 恢复顶部文件夹导航栏
|
|
|
|
|
if (mFolderNavScrollView != null) {
|
|
|
|
|
mFolderNavScrollView.setVisibility(View.VISIBLE);
|
|
|
|
|
}
|
|
|
|
|
// [修改] 恢复下拉刷新
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
// [新增] 刷新菜单栏(恢复显示)
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
|
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
break;
|
|
|
|
|
case NOTE_LIST:
|
|
|
|
|
super.onBackPressed();
|
|
|
|
|
break;
|
|
|
|
|
case RECYCLE_BIN: // [新增]
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
|
|
|
|
|
// 恢复 UI
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
if (mFolderNavScrollView != null) mFolderNavScrollView.setVisibility(View.VISIBLE);
|
|
|
|
|
if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.setEnabled(true);
|
|
|
|
|
|
|
|
|
|
// 恢复用户之前的视图设置 (列表或瀑布流)
|
|
|
|
|
updateViewMode();
|
|
|
|
|
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onStart() {
|
|
|
|
|
super.onStart();
|
|
|
|
|
@ -223,9 +326,32 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
@Override
|
|
|
|
|
protected void onResume() {
|
|
|
|
|
super.onResume();
|
|
|
|
|
applyCustomBackground();
|
|
|
|
|
updateViewMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void applyCustomBackground() {
|
|
|
|
|
SharedPreferences settings = getSharedPreferences(NotesPreferenceActivity.PREFERENCE_NAME, Context.MODE_PRIVATE);
|
|
|
|
|
String uriString = settings.getString("custom_list_background_uri", "");
|
|
|
|
|
View rootView = findViewById(R.id.note_list_root);
|
|
|
|
|
|
|
|
|
|
if (rootView != null) {
|
|
|
|
|
if (!TextUtils.isEmpty(uriString)) {
|
|
|
|
|
try {
|
|
|
|
|
InputStream is = getContentResolver().openInputStream(Uri.parse(uriString));
|
|
|
|
|
Drawable d = Drawable.createFromStream(is, uriString);
|
|
|
|
|
rootView.setBackground(d);
|
|
|
|
|
if (is != null) is.close();
|
|
|
|
|
return; // 如果有自定义图,直接返回
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 加载失败则继续向下执行 fallback
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rootView.setBackgroundResource(R.drawable.list_background);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initResources() {
|
|
|
|
|
mContentResolver = this.getContentResolver();
|
|
|
|
|
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
|
|
|
|
|
@ -233,6 +359,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
mRecyclerView = findViewById(R.id.notes_list_recycler);
|
|
|
|
|
mAdapter = new NotesListItemAdapter(this);
|
|
|
|
|
mRecyclerView.setAdapter(mAdapter);
|
|
|
|
|
mFolderNavScrollView = findViewById(R.id.folder_nav_scroll);
|
|
|
|
|
|
|
|
|
|
// 1. 绑定 Toolbar
|
|
|
|
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
|
|
|
@ -321,6 +448,87 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
mTitleBar = (TextView) findViewById(R.id.tv_title_bar);
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
mFolderNavContainer = (LinearLayout) findViewById(R.id.folder_nav_container);
|
|
|
|
|
|
|
|
|
|
// [新增] 初始化下拉组件
|
|
|
|
|
mSwipeRefreshLayout = findViewById(R.id.swipe_refresh_layout);
|
|
|
|
|
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
|
|
|
|
|
android.R.color.holo_green_light,
|
|
|
|
|
android.R.color.holo_orange_light,
|
|
|
|
|
android.R.color.holo_red_light);
|
|
|
|
|
|
|
|
|
|
mSwipeRefreshLayout.setOnRefreshListener(new androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onRefresh() {
|
|
|
|
|
// 下拉触发,直接尝试进入私密模式
|
|
|
|
|
showPasswordDialog();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void showPasswordDialog() {
|
|
|
|
|
final EditText et = new EditText(this);
|
|
|
|
|
et.setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
|
|
|
|
et.setHint("1234"); // 提示当前硬编码密码
|
|
|
|
|
|
|
|
|
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle(R.string.input_password)
|
|
|
|
|
.setView(et)
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String pwd = et.getText().toString();
|
|
|
|
|
if ("1234".equals(pwd)) {
|
|
|
|
|
enterPrivateFolder();
|
|
|
|
|
} else {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, R.string.password_error, Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
// 无论成功失败,停止刷新动画
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
// 监听取消事件,防止动画一直转
|
|
|
|
|
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onCancel(DialogInterface dialog) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
dialog.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 进入私密模式逻辑
|
|
|
|
|
private void enterPrivateFolder() {
|
|
|
|
|
mState = ListEditState.PRIVATE_FOLDER;
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
|
|
|
|
|
mTitleBar.setVisibility(View.VISIBLE);
|
|
|
|
|
mTitleBar.setText(R.string.title_private_folder);
|
|
|
|
|
|
|
|
|
|
// [修改] 隐藏 FAB
|
|
|
|
|
mAddNewNote.hide();
|
|
|
|
|
|
|
|
|
|
// [新增] 隐藏顶部文件夹导航栏
|
|
|
|
|
if (mFolderNavScrollView != null) {
|
|
|
|
|
mFolderNavScrollView.setVisibility(View.GONE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [修改] 禁用下拉刷新
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 刷新菜单栏(触发 onPrepareOptionsMenu 以隐藏菜单)
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
|
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateViewMode() {
|
|
|
|
|
@ -372,7 +580,8 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void getItemOffsets(Rect outRect, View view, androidx.recyclerview.widget.RecyclerView parent, androidx.recyclerview.widget.RecyclerView.State state) {
|
|
|
|
|
public void getItemOffsets(Rect outRect, View view, androidx.recyclerview.widget.RecyclerView parent,
|
|
|
|
|
androidx.recyclerview.widget.RecyclerView.State state) {
|
|
|
|
|
// 获取当前条目的布局参数
|
|
|
|
|
androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams lp =
|
|
|
|
|
(androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
|
|
|
|
|
@ -403,21 +612,62 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
|
|
|
getMenuInflater().inflate(R.menu.note_list_options, menu);
|
|
|
|
|
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
|
|
|
|
|
menu.findItem(R.id.menu_rename).setOnMenuItemClickListener(this);
|
|
|
|
|
|
|
|
|
|
// 获取所有菜单项的引用
|
|
|
|
|
MenuItem deleteItem = menu.findItem(R.id.delete);
|
|
|
|
|
MenuItem restoreItem = menu.findItem(R.id.menu_restore);
|
|
|
|
|
MenuItem renameItem = menu.findItem(R.id.menu_rename);
|
|
|
|
|
MenuItem pinItem = menu.findItem(R.id.menu_pin);
|
|
|
|
|
MenuItem unpinItem = menu.findItem(R.id.menu_unpin);
|
|
|
|
|
MenuItem privateItem = menu.findItem(R.id.menu_private);
|
|
|
|
|
mMoveMenu = menu.findItem(R.id.move);
|
|
|
|
|
// [Fix Start] 增加 null 检查
|
|
|
|
|
if (mFocusNoteDataItem == null || mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|
|
|
|
|
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
|
|
|
|
|
mMoveMenu.setVisible(false);
|
|
|
|
|
|
|
|
|
|
// 统一绑定监听器
|
|
|
|
|
if (deleteItem != null) deleteItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (restoreItem != null) restoreItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (renameItem != null) renameItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (pinItem != null) pinItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (unpinItem != null) unpinItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (privateItem != null) privateItem.setOnMenuItemClickListener(this);
|
|
|
|
|
if (mMoveMenu != null) mMoveMenu.setOnMenuItemClickListener(this);
|
|
|
|
|
|
|
|
|
|
// [新增] 根据状态控制菜单显隐
|
|
|
|
|
if (mState == ListEditState.RECYCLE_BIN) {
|
|
|
|
|
// === 回收站模式 ===
|
|
|
|
|
// 显示:还原、删除(彻底删除)
|
|
|
|
|
if (restoreItem != null) restoreItem.setVisible(true);
|
|
|
|
|
if (deleteItem != null) deleteItem.setVisible(true);
|
|
|
|
|
|
|
|
|
|
// 隐藏:移动、置顶、私密、重命名
|
|
|
|
|
if (mMoveMenu != null) mMoveMenu.setVisible(false);
|
|
|
|
|
if (renameItem != null) renameItem.setVisible(false);
|
|
|
|
|
if (pinItem != null) pinItem.setVisible(false);
|
|
|
|
|
if (unpinItem != null) unpinItem.setVisible(false);
|
|
|
|
|
if (privateItem != null) privateItem.setVisible(false);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
mMoveMenu.setVisible(true);
|
|
|
|
|
mMoveMenu.setOnMenuItemClickListener(this);
|
|
|
|
|
// === 普通模式 / 私密模式 ===
|
|
|
|
|
// 隐藏:还原
|
|
|
|
|
if (restoreItem != null) restoreItem.setVisible(false);
|
|
|
|
|
|
|
|
|
|
// 处理 Move 菜单的特殊逻辑
|
|
|
|
|
if (mMoveMenu != null) {
|
|
|
|
|
if (mState == ListEditState.PRIVATE_FOLDER) {
|
|
|
|
|
// 私密模式强制显示移动(用于移出)
|
|
|
|
|
mMoveMenu.setVisible(true);
|
|
|
|
|
} else if (mFocusNoteDataItem == null || mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|
|
|
|
|
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
|
|
|
|
|
mMoveMenu.setVisible(false);
|
|
|
|
|
} else {
|
|
|
|
|
mMoveMenu.setVisible(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pin/Unpin/Private/Rename 的显隐会在 updateMenu() 中进一步动态刷新,这里保持默认即可
|
|
|
|
|
}
|
|
|
|
|
// [Fix End]
|
|
|
|
|
|
|
|
|
|
mActionMode = mode;
|
|
|
|
|
mAdapter.setChoiceMode(true);
|
|
|
|
|
// mRecyclerView.setLongClickable(false); // RecyclerView doesn't have this, Adapter handles it
|
|
|
|
|
mAddNewNote.hide();
|
|
|
|
|
|
|
|
|
|
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
|
|
|
|
|
@ -426,13 +676,12 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
|
|
|
|
|
(Button) customView.findViewById(R.id.selection_menu),
|
|
|
|
|
R.menu.note_list_dropdown);
|
|
|
|
|
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
|
|
|
|
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
|
|
|
|
public boolean onMenuItemClick(MenuItem item) {
|
|
|
|
|
mAdapter.selectAll(!mAdapter.isAllSelected());
|
|
|
|
|
updateMenu();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@ -455,9 +704,22 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
// Update ActionMode menu
|
|
|
|
|
if (mActionMode != null) {
|
|
|
|
|
Menu menu = mActionMode.getMenu();
|
|
|
|
|
MenuItem renameItem = menu.findItem(R.id.menu_rename);
|
|
|
|
|
if (renameItem != null) {
|
|
|
|
|
renameItem.setVisible(selectedCount == 1);
|
|
|
|
|
// [检查] 这里的 ID 必须和 XML 里的 ID 完全一致
|
|
|
|
|
MenuItem pinItem = menu.findItem(R.id.menu_pin);
|
|
|
|
|
MenuItem unpinItem = menu.findItem(R.id.menu_unpin); // 确保 XML 里叫 menu_unpin
|
|
|
|
|
|
|
|
|
|
if (pinItem != null && unpinItem != null) {
|
|
|
|
|
boolean isAllPinned = mAdapter.isAllSelectedItemsPinned();
|
|
|
|
|
// [Debug] 打印最终判断结果
|
|
|
|
|
android.util.Log.e("NoteDebug", "UI Logic: isAllPinned = " + isAllPinned);
|
|
|
|
|
|
|
|
|
|
if (isAllPinned) {
|
|
|
|
|
pinItem.setVisible(false);
|
|
|
|
|
unpinItem.setVisible(true);
|
|
|
|
|
} else {
|
|
|
|
|
pinItem.setVisible(true);
|
|
|
|
|
unpinItem.setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -495,34 +757,125 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
|
|
|
|
|
|
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
|
case R.id.delete:
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
|
|
|
|
|
builder.setTitle(getString(R.string.alert_title_delete));
|
|
|
|
|
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
|
|
|
|
builder.setMessage(getString(R.string.alert_message_delete_notes,
|
|
|
|
|
mAdapter.getSelectedCount()));
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok,
|
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
|
public void onClick(DialogInterface dialog,
|
|
|
|
|
int which) {
|
|
|
|
|
batchDelete();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// [修改] 区分回收站模式和普通模式
|
|
|
|
|
if (mState == ListEditState.RECYCLE_BIN) {
|
|
|
|
|
// 回收站内 -> 彻底删除
|
|
|
|
|
builder.setMessage(getString(R.string.alert_delete_forever));
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok,
|
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
batchDelete(); // 执行物理删除
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 普通模式 -> 移入回收站 (软删除)
|
|
|
|
|
builder.setMessage(getString(R.string.alert_message_delete_notes,
|
|
|
|
|
mAdapter.getSelectedCount()));
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok,
|
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
batchMoveToTrash(); // [调用新写的移入回收站方法]
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
|
builder.show();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.menu_restore:
|
|
|
|
|
// [新增] 还原操作
|
|
|
|
|
batchRestore();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.move:
|
|
|
|
|
startQueryDestinationFolders();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.menu_rename:
|
|
|
|
|
showRenameDialog();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.menu_pin:
|
|
|
|
|
updateNoteStatus(NoteColumns.IS_PINNED, 1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.menu_unpin:
|
|
|
|
|
updateNoteStatus(NoteColumns.IS_PINNED, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case R.id.menu_private:
|
|
|
|
|
builder.setTitle(getString(R.string.menu_private));
|
|
|
|
|
builder.setMessage(getString(R.string.alert_move_to_private));
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
updateNoteStatus(NoteColumns.IS_PRIVATE, 1);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
|
builder.show();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 移入回收站
|
|
|
|
|
private void batchMoveToTrash() {
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
|
protected Void doInBackground(Void... unused) {
|
|
|
|
|
// 调用 DataUtils 新增的方法
|
|
|
|
|
DataUtils.batchMoveToTrash(mContentResolver, mAdapter.getSelectedItemIds());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(Void v) {
|
|
|
|
|
mModeCallBack.finishActionMode();
|
|
|
|
|
startAsyncNotesListQuery(); // 刷新
|
|
|
|
|
}
|
|
|
|
|
}.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 从回收站还原
|
|
|
|
|
private void batchRestore() {
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
|
protected Void doInBackground(Void... unused) {
|
|
|
|
|
DataUtils.batchRestoreFromTrash(mContentResolver, mAdapter.getSelectedItemIds());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(Void v) {
|
|
|
|
|
mModeCallBack.finishActionMode();
|
|
|
|
|
startAsyncNotesListQuery(); // 刷新
|
|
|
|
|
}
|
|
|
|
|
}.execute();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [新增] 辅助方法:批量更新状态
|
|
|
|
|
private void updateNoteStatus(final String column, final int value) {
|
|
|
|
|
new AsyncTask<Void, Void, Void>() {
|
|
|
|
|
protected Void doInBackground(Void... unused) {
|
|
|
|
|
DataUtils.batchUpdateField(mContentResolver, mAdapter.getSelectedItemIds(), column, value);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPostExecute(Void v) {
|
|
|
|
|
mModeCallBack.finishActionMode();
|
|
|
|
|
// [关键新增] 操作完成后,强制重新查询数据库以刷新列表排序
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
}.execute();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void showRenameDialog() {
|
|
|
|
|
@ -534,22 +887,29 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
final long noteId = ids.iterator().next();
|
|
|
|
|
final EditText et = new EditText(this);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle("重命名便签标题")
|
|
|
|
|
.setTitle("重命名便签标题") // 建议后续提取到 strings.xml
|
|
|
|
|
.setView(et)
|
|
|
|
|
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String newTitle = et.getText().toString();
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(NoteColumns.TITLE, newTitle);
|
|
|
|
|
getContentResolver().update(Notes.CONTENT_NOTE_URI, values,
|
|
|
|
|
NoteColumns.ID + "=?", new String[]{String.valueOf(noteId)});
|
|
|
|
|
mModeCallBack.finishActionMode(); // 退出选择模式
|
|
|
|
|
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 建议同时更新修改时间或标记
|
|
|
|
|
|
|
|
|
|
getContentResolver().update(Notes.CONTENT_NOTE_URI, values,
|
|
|
|
|
NoteColumns.ID + "=?", new String[]{String.valueOf(noteId)});
|
|
|
|
|
|
|
|
|
|
mModeCallBack.finishActionMode();
|
|
|
|
|
|
|
|
|
|
// [Fix Start] 新增:重命名后立即刷新列表
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
// [Fix End]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton("取消", null)
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -572,17 +932,25 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
selectionArgs = new String[] {
|
|
|
|
|
"%" + query + "%"
|
|
|
|
|
};
|
|
|
|
|
} else if (mState == ListEditState.PRIVATE_FOLDER) {
|
|
|
|
|
// [新增] 私密模式查询:查询所有 is_private = 1 的便签
|
|
|
|
|
selection = NoteColumns.IS_PRIVATE + "=1 AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
|
|
|
|
|
selectionArgs = null;
|
|
|
|
|
} else {
|
|
|
|
|
// [修改] 普通模式:必须增加 is_private = 0 的条件,防止私密便签显示在普通列表中
|
|
|
|
|
selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
|
|
|
|
|
: NORMAL_SELECTION;
|
|
|
|
|
// 拼接过滤私密的条件
|
|
|
|
|
selection += " AND " + NoteColumns.IS_PRIVATE + "=0";
|
|
|
|
|
|
|
|
|
|
selectionArgs = new String[] {
|
|
|
|
|
String.valueOf(mCurrentFolderId)
|
|
|
|
|
String.valueOf(mCurrentFolderId)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
|
|
|
|
|
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs,
|
|
|
|
|
NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
|
|
|
|
|
NoteColumns.TYPE + " DESC," + NoteColumns.IS_PINNED + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final class BackgroundQueryHandler extends AsyncQueryHandler {
|
|
|
|
|
@ -626,8 +994,16 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
|
|
|
|
|
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
// 1. 执行移动操作 (更新 parent_id)
|
|
|
|
|
DataUtils.batchMoveToFolder(mContentResolver,
|
|
|
|
|
mAdapter.getSelectedItemIds(), adapter.getItemId(which));
|
|
|
|
|
|
|
|
|
|
// [新增] 2. 如果当前是私密模式,移动意味着"移出",必须解除私密状态 (is_private = 0)
|
|
|
|
|
if (mState == ListEditState.PRIVATE_FOLDER) {
|
|
|
|
|
DataUtils.batchUpdateField(mContentResolver,
|
|
|
|
|
mAdapter.getSelectedItemIds(), NoteColumns.IS_PRIVATE, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Toast.makeText(
|
|
|
|
|
NotesListActivity.this,
|
|
|
|
|
getString(R.string.format_move_notes_to_folder,
|
|
|
|
|
@ -635,6 +1011,9 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
adapter.getFolderName(NotesListActivity.this, which)),
|
|
|
|
|
Toast.LENGTH_SHORT).show();
|
|
|
|
|
mModeCallBack.finishActionMode();
|
|
|
|
|
|
|
|
|
|
// [关键] 强制刷新列表,否则移走的便签还会残留在界面上
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
builder.show();
|
|
|
|
|
@ -802,7 +1181,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
values.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
|
|
|
|
|
+ "=?", new String[] {
|
|
|
|
|
String.valueOf(mFocusNoteDataItem.getId())
|
|
|
|
|
String.valueOf(mFocusNoteDataItem.getId())
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if (!TextUtils.isEmpty(name)) {
|
|
|
|
|
@ -811,6 +1190,14 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
|
|
|
|
|
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
|
|
|
|
|
}
|
|
|
|
|
// [Fix Start] 关键修复:刷新数据
|
|
|
|
|
// 1. 刷新下方的列表 (虽然文件夹不在列表里显示,但保持数据一致性)
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
|
|
|
|
|
// 2. [新增] 刷新顶部的文件夹导航栏 Tab
|
|
|
|
|
rebuildFolderNavigation();
|
|
|
|
|
// [Fix End]
|
|
|
|
|
|
|
|
|
|
dialog.dismiss();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@ -842,37 +1229,6 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
|
|
|
|
|
getIntent().setAction(Intent.ACTION_MAIN);
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (mState) {
|
|
|
|
|
case SUB_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
break;
|
|
|
|
|
case CALL_RECORD_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
mAddNewNote.show();
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
break;
|
|
|
|
|
case NOTE_LIST:
|
|
|
|
|
super.onBackPressed();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateWidget(int appWidgetId, int appWidgetType) {
|
|
|
|
|
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
|
|
|
|
@ -959,6 +1315,11 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
|
|
|
// [修改] 私密模式 OR 回收站模式,都清空菜单
|
|
|
|
|
if (mState == ListEditState.PRIVATE_FOLDER || mState == ListEditState.RECYCLE_BIN) {
|
|
|
|
|
menu.clear();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// 移除之前的 menu.clear(),因为这会清空 onCreate 加载的内容
|
|
|
|
|
// 仅保留原有的动态逻辑,例如同步按钮的状态切换
|
|
|
|
|
if (mState == ListEditState.NOTE_LIST) {
|
|
|
|
|
@ -1012,9 +1373,14 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
createNewNote();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case R.id.menu_search:
|
|
|
|
|
case R.id.menu_search: {
|
|
|
|
|
onSearchRequested();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case R.id.menu_recycle_bin: {
|
|
|
|
|
enterRecycleBin();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|