|
|
|
|
@ -70,6 +70,7 @@ import androidx.appcompat.widget.Toolbar;
|
|
|
|
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
|
|
|
|
|
|
|
|
import net.micode.notes.cloud.SyncManager;
|
|
|
|
|
import net.micode.notes.cloud.SyncDialogManager;
|
|
|
|
|
|
|
|
|
|
import net.micode.notes.R;
|
|
|
|
|
import net.micode.notes.data.Notes;
|
|
|
|
|
@ -117,7 +118,8 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
private enum ListEditState {
|
|
|
|
|
NOTE_LIST, // 普通笔记列表状态
|
|
|
|
|
SUB_FOLDER, // 子文件夹浏览状态
|
|
|
|
|
CALL_RECORD_FOLDER // 通话记录文件夹浏览状态
|
|
|
|
|
CALL_RECORD_FOLDER, // 通话记录文件夹浏览状态
|
|
|
|
|
SEARCH // 搜索状态
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 当前列表状态
|
|
|
|
|
@ -153,6 +155,11 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
// 下拉刷新布局
|
|
|
|
|
private SwipeRefreshLayout mSwipeRefreshLayout;
|
|
|
|
|
|
|
|
|
|
// 搜索相关变量
|
|
|
|
|
private String mSearchKey = ""; // 搜索关键词
|
|
|
|
|
private View mSearchContainer; // 搜索容器视图
|
|
|
|
|
private EditText mSearchEditText; // 搜索输入框
|
|
|
|
|
|
|
|
|
|
// 普通查询条件(非根文件夹)
|
|
|
|
|
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
|
|
|
|
|
@ -206,19 +213,35 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
if (itemData == null || itemData.getType() != Notes.TYPE_NOTE) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查询笔记的原始父文件夹ID
|
|
|
|
|
long originParentId = Notes.ID_ROOT_FOLDER; // 默认恢复到根文件夹
|
|
|
|
|
Cursor cursor = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, itemData.getId()),
|
|
|
|
|
new String[]{NoteColumns.ORIGIN_PARENT_ID}, null, null, null);
|
|
|
|
|
if (cursor != null) {
|
|
|
|
|
if (cursor.moveToFirst()) {
|
|
|
|
|
originParentId = cursor.getLong(0);
|
|
|
|
|
// 如果原始文件夹ID无效或者是回收站,则恢复到根文件夹
|
|
|
|
|
if (originParentId <= 0 || originParentId == Notes.ID_TRASH_FOLDER) {
|
|
|
|
|
originParentId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示确认对话框
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
|
builder.setTitle("恢复笔记");
|
|
|
|
|
builder.setMessage("确定要恢复这条笔记吗?");
|
|
|
|
|
builder.setIcon(android.R.drawable.ic_dialog_info);
|
|
|
|
|
final long targetFolderId = originParentId;
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
// 恢复笔记到根文件夹
|
|
|
|
|
// 恢复笔记到原始文件夹
|
|
|
|
|
HashSet<Long> ids = new HashSet<Long>();
|
|
|
|
|
ids.add(itemData.getId());
|
|
|
|
|
if (DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_ROOT_FOLDER)) {
|
|
|
|
|
if (DataUtils.batchMoveToFolder(mContentResolver, ids, targetFolderId)) {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "笔记已恢复", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} else {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "恢复失败", Toast.LENGTH_SHORT).show();
|
|
|
|
|
@ -330,8 +353,8 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onRefresh() {
|
|
|
|
|
// 下拉刷新时触发同步
|
|
|
|
|
triggerSync();
|
|
|
|
|
// 下拉刷新时只执行下载操作
|
|
|
|
|
executeDownload();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 设置刷新颜色
|
|
|
|
|
@ -575,10 +598,11 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
// 根据当前文件夹选择查询条件
|
|
|
|
|
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
|
|
|
|
|
: NORMAL_SELECTION;
|
|
|
|
|
// 修改排序规则:先按类型降序(系统文件夹在前),再按alerted_date降序(置顶便签在前),最后按修改时间降序
|
|
|
|
|
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
|
|
|
|
|
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
|
|
|
|
|
String.valueOf(mCurrentFolderId)
|
|
|
|
|
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
|
|
|
|
|
}, NoteColumns.TYPE + " DESC," + NoteColumns.ALERTED_DATE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -654,10 +678,19 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
|
|
|
|
|
// 获取选中笔记的小部件属性
|
|
|
|
|
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
|
|
|
|
|
// 无论是否同步模式,都将笔记移动到回收站
|
|
|
|
|
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
|
|
|
|
|
.getSelectedItemIds(), Notes.ID_TRASH_FOLDER)) {
|
|
|
|
|
Log.e(TAG, "Move notes to trash folder error, should not happens");
|
|
|
|
|
HashSet<Long> selectedIds = mNotesListAdapter.getSelectedItemIds();
|
|
|
|
|
|
|
|
|
|
// 根据当前文件夹决定删除方式
|
|
|
|
|
if (mCurrentFolderId == Notes.ID_TRASH_FOLDER) {
|
|
|
|
|
// 在回收站中,彻底删除笔记
|
|
|
|
|
if (!DataUtils.batchDeleteNotes(mContentResolver, selectedIds)) {
|
|
|
|
|
Log.e(TAG, "Delete notes error, should not happens");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 不在回收站中,将笔记移动到回收站
|
|
|
|
|
if (!DataUtils.batchMoveToFolder(mContentResolver, selectedIds, Notes.ID_TRASH_FOLDER)) {
|
|
|
|
|
Log.e(TAG, "Move notes to trash folder error, should not happens");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return widgets;
|
|
|
|
|
}
|
|
|
|
|
@ -683,25 +716,51 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
*/
|
|
|
|
|
private void batchPinNotes() {
|
|
|
|
|
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
|
|
|
|
|
private int pinnedCount = 0;
|
|
|
|
|
private int unpinnedCount = 0;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
|
|
|
|
|
// 获取选中笔记的小部件属性
|
|
|
|
|
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
|
|
|
|
|
HashSet<Long> selectedIds = mNotesListAdapter.getSelectedItemIds();
|
|
|
|
|
|
|
|
|
|
// 批量更新笔记的alerted_date字段为一个很大的值,表示置顶
|
|
|
|
|
// 批量更新笔记的alerted_date字段
|
|
|
|
|
for (Long noteId : selectedIds) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(NoteColumns.ALERTED_DATE, 9999999999999L); // 很大的值,表示置顶
|
|
|
|
|
values.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 查询笔记当前的alerted_date值
|
|
|
|
|
Cursor cursor = getContentResolver().query(
|
|
|
|
|
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
|
|
|
|
|
new String[]{NoteColumns.ALERTED_DATE},
|
|
|
|
|
null, null, null);
|
|
|
|
|
|
|
|
|
|
long currentAlertedDate = 0;
|
|
|
|
|
if (cursor != null && cursor.moveToFirst()) {
|
|
|
|
|
currentAlertedDate = cursor.getLong(0);
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
|
|
|
|
|
// 判断是否已经置顶(alerted_date大于当前时间)
|
|
|
|
|
if (currentAlertedDate > System.currentTimeMillis()) {
|
|
|
|
|
// 已经置顶,取消置顶
|
|
|
|
|
values.put(NoteColumns.ALERTED_DATE, 0); // 0表示未置顶
|
|
|
|
|
unpinnedCount++;
|
|
|
|
|
} else {
|
|
|
|
|
// 未置顶,设置置顶
|
|
|
|
|
values.put(NoteColumns.ALERTED_DATE, 9999999999999L); // 很大的值,表示置顶
|
|
|
|
|
pinnedCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
values.put(NoteColumns.LOCAL_MODIFIED, 1);
|
|
|
|
|
values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
|
|
|
|
|
|
|
|
|
getContentResolver().update(
|
|
|
|
|
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
|
|
|
|
|
values, null, null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Error pinning note: " + noteId, e);
|
|
|
|
|
Log.e(TAG, "Error toggling pin status: " + noteId, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return widgets;
|
|
|
|
|
@ -718,7 +777,16 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "已置顶" + mNotesListAdapter.getSelectedCount() + "条笔记", Toast.LENGTH_SHORT).show();
|
|
|
|
|
|
|
|
|
|
// 显示相应的提示信息
|
|
|
|
|
if (pinnedCount > 0 && unpinnedCount > 0) {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "已置顶" + pinnedCount + "条笔记,已取消置顶" + unpinnedCount + "条笔记", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} else if (pinnedCount > 0) {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "已置顶" + pinnedCount + "条笔记", Toast.LENGTH_SHORT).show();
|
|
|
|
|
} else if (unpinnedCount > 0) {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "已取消置顶" + unpinnedCount + "条笔记", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mModeCallBack.finishActionMode(); // 结束多选模式
|
|
|
|
|
}
|
|
|
|
|
}.execute();
|
|
|
|
|
@ -927,6 +995,9 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
public void onBackPressed() {
|
|
|
|
|
// 处理返回键,根据当前状态返回上一级或退出
|
|
|
|
|
switch (mState) {
|
|
|
|
|
case SEARCH:
|
|
|
|
|
exitSearch();
|
|
|
|
|
break;
|
|
|
|
|
case SUB_FOLDER:
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
@ -1079,6 +1150,8 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
getMenuInflater().inflate(R.menu.sub_folder, menu);
|
|
|
|
|
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
|
|
|
|
|
getMenuInflater().inflate(R.menu.call_record_folder, menu);
|
|
|
|
|
} else if (mState == ListEditState.SEARCH) {
|
|
|
|
|
// 搜索状态下不显示菜单,因为搜索框已经占据了工具栏
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "Wrong state:" + mState);
|
|
|
|
|
}
|
|
|
|
|
@ -1115,10 +1188,128 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onSearchRequested() {
|
|
|
|
|
startSearch(null, false, null /* appData */, false);
|
|
|
|
|
toggleToSearch();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示搜索框
|
|
|
|
|
* 在根目录时,显示顶部搜索框;在子目录时,切换到根目录再显示搜索框
|
|
|
|
|
*/
|
|
|
|
|
private void showSearch() {
|
|
|
|
|
if (mState != ListEditState.SEARCH) {
|
|
|
|
|
// 切换到搜索状态
|
|
|
|
|
mState = ListEditState.SEARCH;
|
|
|
|
|
// 隐藏新建笔记按钮
|
|
|
|
|
mAddNewNote.setVisibility(View.GONE);
|
|
|
|
|
// 隐藏标题栏,显示搜索容器
|
|
|
|
|
mTitleBar.setVisibility(View.GONE);
|
|
|
|
|
mSearchContainer = findViewById(R.id.search_container);
|
|
|
|
|
mSearchContainer.setVisibility(View.VISIBLE);
|
|
|
|
|
mSearchEditText = (EditText) mSearchContainer.findViewById(R.id.search_input);
|
|
|
|
|
// 设置搜索输入框监听器
|
|
|
|
|
mSearchEditText.addTextChangedListener(new TextWatcher() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterTextChanged(Editable s) {
|
|
|
|
|
mSearchKey = s.toString();
|
|
|
|
|
// 根据搜索关键字刷新列表
|
|
|
|
|
startSearchQuery();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听搜索框软键盘的"完成"键
|
|
|
|
|
mSearchEditText.setOnEditorActionListener((v, actionId, event) -> {
|
|
|
|
|
if (actionId == android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH) {
|
|
|
|
|
mSearchKey = mSearchEditText.getText().toString();
|
|
|
|
|
startSearchQuery();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mSearchEditText.requestFocus();
|
|
|
|
|
showSoftInput();
|
|
|
|
|
|
|
|
|
|
// 设置取消按钮点击监听器
|
|
|
|
|
Button cancelButton = (Button) mSearchContainer.findViewById(R.id.search_cancel);
|
|
|
|
|
cancelButton.setOnClickListener(new OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
exitSearch();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 启动搜索查询
|
|
|
|
|
*/
|
|
|
|
|
private void startSearchQuery() {
|
|
|
|
|
// 只搜索SNIPPET列,因为CONTENT列在DataColumns中
|
|
|
|
|
String selection = NoteColumns.SNIPPET + " LIKE ?";
|
|
|
|
|
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
|
|
|
|
|
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection,
|
|
|
|
|
new String[] {"%" + mSearchKey + "%"},
|
|
|
|
|
NoteColumns.MODIFIED_DATE + " DESC");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 切换到搜索状态
|
|
|
|
|
* 如果当前不在搜索状态,调用 showSearch;如果已经在搜索状态但有新的搜索关键字,
|
|
|
|
|
* 则直接刷新搜索结果
|
|
|
|
|
*/
|
|
|
|
|
private void toggleToSearch() {
|
|
|
|
|
if (mState != ListEditState.SEARCH) {
|
|
|
|
|
// 如果当前不在根目录,先切换到根目录
|
|
|
|
|
if (mCurrentFolderId != Notes.ID_ROOT_FOLDER) {
|
|
|
|
|
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
showSearch();
|
|
|
|
|
} else {
|
|
|
|
|
if (!mSearchKey.equals(mSearchEditText.getText().toString())) {
|
|
|
|
|
startSearchQuery();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 退出搜索状态
|
|
|
|
|
*/
|
|
|
|
|
private void exitSearch() {
|
|
|
|
|
if (mState == ListEditState.SEARCH) {
|
|
|
|
|
// 切换回普通笔记列表状态
|
|
|
|
|
mState = ListEditState.NOTE_LIST;
|
|
|
|
|
// 清空搜索关键字
|
|
|
|
|
mSearchKey = "";
|
|
|
|
|
// 隐藏搜索容器,显示标题栏
|
|
|
|
|
if (mSearchContainer != null) {
|
|
|
|
|
mSearchContainer.setVisibility(View.GONE);
|
|
|
|
|
}
|
|
|
|
|
mTitleBar.setVisibility(View.VISIBLE);
|
|
|
|
|
mTitleBar.setText(R.string.app_name);
|
|
|
|
|
// 显示新建笔记按钮
|
|
|
|
|
mAddNewNote.setVisibility(View.VISIBLE);
|
|
|
|
|
// 隐藏软键盘
|
|
|
|
|
if (mSearchEditText != null) {
|
|
|
|
|
hideSoftInput(mSearchEditText);
|
|
|
|
|
}
|
|
|
|
|
// 刷新笔记列表
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
// 重新创建菜单,确保设置图标显示
|
|
|
|
|
invalidateOptionsMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 导出笔记为文本文件
|
|
|
|
|
*/
|
|
|
|
|
@ -1228,6 +1419,13 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
Log.e(TAG, "Wrong note type in CALL_RECORD_FOLDER");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SEARCH:
|
|
|
|
|
if (item.getType() == Notes.TYPE_NOTE) {
|
|
|
|
|
openNode(item); // 打开笔记
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "Wrong note type in SEARCH");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@ -1264,13 +1462,45 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
* 触发同步操作
|
|
|
|
|
*/
|
|
|
|
|
private void triggerSync() {
|
|
|
|
|
SyncManager.getInstance(this).sync(new SyncManager.SyncCallback() {
|
|
|
|
|
SyncDialogManager.showSyncOptionsDialog(this, new SyncDialogManager.SyncOptionListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onUploadSelected() {
|
|
|
|
|
SyncDialogManager.showUploadConfirmDialog(NotesListActivity.this, new SyncDialogManager.ConfirmListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onConfirm() {
|
|
|
|
|
executeUpload();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onCancel() {
|
|
|
|
|
// 取消操作,返回选择对话框
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onDownloadSelected() {
|
|
|
|
|
executeDownload();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onCanceled() {
|
|
|
|
|
// 取消操作
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 执行上传操作
|
|
|
|
|
*/
|
|
|
|
|
private void executeUpload() {
|
|
|
|
|
SyncManager.getInstance(this).triggerUpload(new SyncManager.SyncCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSyncStart() {
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "正在同步...", Toast.LENGTH_SHORT).show();
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "正在上传...", Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 开始刷新动画
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(true);
|
|
|
|
|
@ -1284,7 +1514,58 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "同步成功", Toast.LENGTH_SHORT).show();
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 停止刷新动画
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
}
|
|
|
|
|
// 刷新笔记列表
|
|
|
|
|
startAsyncNotesListQuery();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSyncFailed(final String errorMessage) {
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 停止刷新动画
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 执行下载操作
|
|
|
|
|
*/
|
|
|
|
|
private void executeDownload() {
|
|
|
|
|
SyncManager.getInstance(this).triggerDownload(new SyncManager.SyncCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSyncStart() {
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "正在下载...", Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 开始刷新动画
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSyncSuccess() {
|
|
|
|
|
runOnUiThread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 停止刷新动画
|
|
|
|
|
if (mSwipeRefreshLayout != null) {
|
|
|
|
|
mSwipeRefreshLayout.setRefreshing(false);
|
|
|
|
|
@ -1618,9 +1899,12 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示文件夹密码输入对话框
|
|
|
|
|
* 支持密码验证和忘记密码功能(通过密保问题重置密码)
|
|
|
|
|
* @param folderId 文件夹ID
|
|
|
|
|
*/
|
|
|
|
|
private void showFolderPasswordDialog(final long folderId) {
|
|
|
|
|
Log.d(TAG, "Showing folder password dialog for folderId: " + folderId);
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
|
builder.setTitle("输入密码");
|
|
|
|
|
builder.setIcon(android.R.drawable.ic_lock_idle_lock);
|
|
|
|
|
@ -1635,11 +1919,15 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String password = passwordInput.getText().toString();
|
|
|
|
|
String password = passwordInput.getText().toString().trim();
|
|
|
|
|
Log.d(TAG, "User entered password for folder: " + folderId);
|
|
|
|
|
|
|
|
|
|
if (checkFolderPassword(folderId, password)) {
|
|
|
|
|
Log.i(TAG, "Folder password verified successfully, entering folder: " + folderId);
|
|
|
|
|
// 密码正确,进入文件夹
|
|
|
|
|
enterEncryptedFolder(folderId);
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "Folder password verification failed for folder: " + folderId);
|
|
|
|
|
// 密码错误,显示提示
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "密码错误,请重新输入", Toast.LENGTH_SHORT).show();
|
|
|
|
|
// 重新显示密码输入对话框
|
|
|
|
|
@ -1651,23 +1939,37 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
// 设置取消按钮
|
|
|
|
|
builder.setNegativeButton("取消", null);
|
|
|
|
|
|
|
|
|
|
// 添加"忘记密码"按钮,点击后通过密保问题验证重置密码
|
|
|
|
|
builder.setNeutralButton(R.string.btn_forgot_password, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
Log.d(TAG, "User clicked forgot password button for folder: " + folderId);
|
|
|
|
|
// 显示通过密保问题重置密码的对话框
|
|
|
|
|
showResetPasswordWithSecurityQuestionsDialog(folderId);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 显示对话框
|
|
|
|
|
builder.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 通过密保问题重置文件夹密码
|
|
|
|
|
* 验证成功后允许用户设置新密码
|
|
|
|
|
* @param folderId 文件夹ID
|
|
|
|
|
*/
|
|
|
|
|
private void showResetPasswordWithSecurityQuestionsDialog(final long folderId) {
|
|
|
|
|
Log.d(TAG, "Showing reset password with security questions dialog for folder: " + folderId);
|
|
|
|
|
|
|
|
|
|
// 检查是否设置了密保问题
|
|
|
|
|
if (!NotesPreferenceActivity.hasSecurityQuestionsSet(this)) {
|
|
|
|
|
Log.w(TAG, "Security questions not set, cannot reset folder password");
|
|
|
|
|
Toast.makeText(this, "请先在设置中设置密保问题", Toast.LENGTH_SHORT).show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
|
builder.setTitle("忘记密码");
|
|
|
|
|
builder.setTitle(R.string.title_forgot_password);
|
|
|
|
|
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
|
|
|
|
builder.setMessage("请回答密保问题以重置密码");
|
|
|
|
|
|
|
|
|
|
@ -1689,15 +1991,20 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
builder.setView(layout);
|
|
|
|
|
|
|
|
|
|
// 设置确定按钮
|
|
|
|
|
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String name = nameInput.getText().toString();
|
|
|
|
|
String birthday = birthdayInput.getText().toString();
|
|
|
|
|
String name = nameInput.getText().toString().trim();
|
|
|
|
|
String birthday = birthdayInput.getText().toString().trim();
|
|
|
|
|
|
|
|
|
|
Log.d(TAG, "Verifying security questions for folder password reset - name: " + name);
|
|
|
|
|
|
|
|
|
|
if (NotesPreferenceActivity.verifySecurityQuestions(NotesListActivity.this, name, birthday)) {
|
|
|
|
|
Log.i(TAG, "Security questions verified successfully for folder: " + folderId);
|
|
|
|
|
// 验证成功,显示设置新密码的对话框
|
|
|
|
|
showSetNewFolderPasswordDialog(folderId);
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "Security questions verification failed for folder password reset");
|
|
|
|
|
// 验证失败,显示提示
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "密保问题回答错误", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
@ -1705,7 +2012,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置取消按钮
|
|
|
|
|
builder.setNegativeButton("取消", null);
|
|
|
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
|
|
|
|
|
|
// 显示对话框
|
|
|
|
|
builder.show();
|
|
|
|
|
@ -1713,25 +2020,35 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示设置新文件夹密码的对话框
|
|
|
|
|
* 用户设置新密码后,需要重新输入密码才能进入文件夹
|
|
|
|
|
* @param folderId 文件夹ID
|
|
|
|
|
*/
|
|
|
|
|
private void showSetNewFolderPasswordDialog(final long folderId) {
|
|
|
|
|
Log.d(TAG, "Showing set new folder password dialog for folder: " + folderId);
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
|
builder.setTitle("设置新密码");
|
|
|
|
|
builder.setIcon(android.R.drawable.ic_dialog_info);
|
|
|
|
|
builder.setMessage("验证成功,请设置新密码");
|
|
|
|
|
|
|
|
|
|
// 创建密码输入框
|
|
|
|
|
final EditText passwordInput = new EditText(this);
|
|
|
|
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
|
|
|
|
passwordInput.setHint("请输入新密码");
|
|
|
|
|
builder.setView(passwordInput);
|
|
|
|
|
LinearLayout layout = new LinearLayout(this);
|
|
|
|
|
layout.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
|
layout.setPadding(40, 20, 40, 20);
|
|
|
|
|
layout.addView(passwordInput);
|
|
|
|
|
builder.setView(layout);
|
|
|
|
|
|
|
|
|
|
// 设置确定按钮
|
|
|
|
|
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
|
|
|
|
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String newPassword = passwordInput.getText().toString();
|
|
|
|
|
String newPassword = passwordInput.getText().toString().trim();
|
|
|
|
|
|
|
|
|
|
if (!newPassword.isEmpty()) {
|
|
|
|
|
Log.i(TAG, "Setting new password for folder: " + folderId);
|
|
|
|
|
// 保存新密码
|
|
|
|
|
SharedPreferences preferences = getSharedPreferences("notes_prefs", Context.MODE_PRIVATE);
|
|
|
|
|
SharedPreferences.Editor editor = preferences.edit();
|
|
|
|
|
@ -1739,8 +2056,10 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
editor.apply();
|
|
|
|
|
|
|
|
|
|
// 显示提示
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "密码重置成功", Toast.LENGTH_SHORT).show();
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "密码重置成功,请使用新密码进入文件夹", Toast.LENGTH_SHORT).show();
|
|
|
|
|
Log.i(TAG, "Folder password reset successfully for folder: " + folderId);
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "New folder password is empty");
|
|
|
|
|
// 密码为空,显示提示
|
|
|
|
|
Toast.makeText(NotesListActivity.this, "密码不能为空", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
@ -1748,7 +2067,7 @@ public class NotesListActivity extends AppCompatActivity implements OnClickListe
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置取消按钮
|
|
|
|
|
builder.setNegativeButton("取消", null);
|
|
|
|
|
builder.setNegativeButton(android.R.string.cancel, null);
|
|
|
|
|
|
|
|
|
|
// 显示对话框
|
|
|
|
|
builder.show();
|
|
|
|
|
|