再传一次漂流瓶,新增回收站功能

zhongkai_branch
zk 1 month ago
parent b692e7e75a
commit c3f56673d7

@ -0,0 +1,569 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.HashSet;
public class MemoryBottleDialog extends Dialog implements View.OnClickListener {
private static final String PREF_MEMORY_FOLDER_ID = "pref_memory_bottle_folder_id";
private static final List<MemoryEntry> sAllEntries = new ArrayList<>();
private static final List<MemoryEntry> sRemainingEntries = new ArrayList<>();
private static final Random sRandom = new Random();
private static long sFolderId = Long.MIN_VALUE;
private final Activity mActivity;
private Button mAddButton;
private Button mBrowseButton;
private long mMemoryFolderId = -1;
private boolean mEntriesLoaded;
private boolean mLoading;
private boolean mBrowseLoading;
private LoadTask mLoadTask;
private BrowseTask mBrowseTask;
private PendingAction mPendingAction = PendingAction.NONE;
public MemoryBottleDialog(Activity activity) {
super(activity, android.R.style.Theme_Light_NoTitleBar);
mActivity = activity;
}
@Override
protected void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.memory_bottle);
getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
initResources();
}
@Override
public void dismiss() {
if (mLoadTask != null) {
mLoadTask.cancel(true);
mLoadTask = null;
}
if (mBrowseTask != null) {
mBrowseTask.cancel(true);
mBrowseTask = null;
}
super.dismiss();
}
private void initResources() {
mAddButton = (Button) findViewById(R.id.btn_memory_add);
mBrowseButton = (Button) findViewById(R.id.btn_memory_browse);
mAddButton.setOnClickListener(this);
mBrowseButton.setOnClickListener(this);
updateButtonState();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_memory_add:
requestAction(PendingAction.ADD);
break;
case R.id.btn_memory_browse:
requestAction(PendingAction.BROWSE);
break;
default:
break;
}
}
private void requestAction(PendingAction action) {
if (mLoading || mBrowseLoading) {
Toast.makeText(mActivity, R.string.memory_bottle_loading, Toast.LENGTH_SHORT).show();
return;
}
if (mMemoryFolderId <= 0) {
mPendingAction = action;
Toast.makeText(mActivity, R.string.memory_bottle_loading, Toast.LENGTH_SHORT).show();
startLoadTask(action == PendingAction.BROWSE);
return;
}
if (action == PendingAction.BROWSE && !mEntriesLoaded) {
mPendingAction = action;
Toast.makeText(mActivity, R.string.memory_bottle_loading, Toast.LENGTH_SHORT).show();
startLoadTask(true);
return;
}
if (action == PendingAction.ADD) {
showAddDialog();
} else if (action == PendingAction.BROWSE) {
browseMemory();
}
}
private void startLoadTask(boolean loadEntries) {
if (mLoadTask != null) {
return;
}
setLoading(true);
mLoadTask = new LoadTask(this, loadEntries);
mLoadTask.execute();
}
private void setLoading(boolean loading) {
mLoading = loading;
updateButtonState();
}
private void setBrowseLoading(boolean loading) {
mBrowseLoading = loading;
updateButtonState();
}
private void updateButtonState() {
boolean enabled = !(mLoading || mBrowseLoading);
if (mAddButton != null) {
mAddButton.setEnabled(enabled);
}
if (mBrowseButton != null) {
mBrowseButton.setEnabled(enabled);
}
}
private void showAddDialog() {
final EditText editText = new EditText(mActivity);
int padding = (int) (mActivity.getResources().getDisplayMetrics().density * 16);
editText.setPadding(padding, padding, padding, padding);
editText.setGravity(Gravity.TOP | Gravity.START);
editText.setMinLines(4);
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
editText.setHint(R.string.memory_bottle_add_hint);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(R.string.memory_bottle_add_title);
builder.setView(editText);
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, null);
final AlertDialog dialog = builder.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String content = editText.getText().toString().trim();
if (TextUtils.isEmpty(content)) {
Toast.makeText(mActivity, R.string.memory_bottle_empty_input,
Toast.LENGTH_SHORT).show();
return;
}
if (createMemoryNote(content)) {
dialog.dismiss();
}
}
});
}
private boolean createMemoryNote(String content) {
if (mMemoryFolderId <= 0) {
Toast.makeText(mActivity, R.string.memory_bottle_folder_error, Toast.LENGTH_SHORT).show();
return false;
}
WorkingNote note = WorkingNote.createEmptyNote(mActivity, mMemoryFolderId,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.getDefaultBgId(mActivity));
note.setWorkingText(content);
if (!note.saveNote()) {
Toast.makeText(mActivity, R.string.memory_bottle_save_failed, Toast.LENGTH_SHORT).show();
return false;
}
long noteId = note.getNoteId();
long createdDate = queryNoteCreatedDate(noteId);
MemoryEntry entry = new MemoryEntry(noteId, createdDate, content);
sAllEntries.add(entry);
sRemainingEntries.add(entry);
mEntriesLoaded = true;
Toast.makeText(mActivity, R.string.memory_bottle_save_success, Toast.LENGTH_SHORT).show();
return true;
}
private long queryNoteCreatedDate(long noteId) {
Cursor cursor = mActivity.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
new String[] { NoteColumns.CREATED_DATE },
null, null, null);
if (cursor == null) {
return System.currentTimeMillis();
}
try {
if (cursor.moveToFirst()) {
return cursor.getLong(0);
}
} finally {
cursor.close();
}
return System.currentTimeMillis();
}
private void browseMemory() {
if (sAllEntries.isEmpty()) {
Toast.makeText(mActivity, R.string.memory_bottle_empty, Toast.LENGTH_SHORT).show();
return;
}
if (sRemainingEntries.isEmpty()) {
showBrowseFinishedDialog();
return;
}
showRandomEntry();
}
private void showBrowseFinishedDialog() {
new AlertDialog.Builder(mActivity)
.setMessage(R.string.memory_bottle_browse_done)
.setPositiveButton(R.string.memory_bottle_restart, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
resetRemainingEntries();
showRandomEntry();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void resetRemainingEntries() {
sRemainingEntries.clear();
sRemainingEntries.addAll(sAllEntries);
}
private void showRandomEntry() {
int index = sRandom.nextInt(sRemainingEntries.size());
MemoryEntry entry = sRemainingEntries.remove(index);
startBrowseTask(entry);
}
private void startBrowseTask(MemoryEntry entry) {
if (mBrowseTask != null) {
return;
}
setBrowseLoading(true);
mBrowseTask = new BrowseTask(this, entry);
mBrowseTask.execute();
}
private String formatEntryMessage(long createdDate, String content) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
String date = format.format(new Date(createdDate));
return mActivity.getString(R.string.memory_bottle_entry_format, date, content);
}
private long ensureMemoryFolder() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mActivity);
long storedId = sp.getLong(PREF_MEMORY_FOLDER_ID, Long.MIN_VALUE);
if (storedId > 0 && DataUtils.visibleInNoteDatabase(mActivity.getContentResolver(),
storedId, Notes.TYPE_FOLDER)) {
return storedId;
}
String folderName = mActivity.getString(R.string.memory_bottle_folder_name);
long folderId = queryFolderIdByName(folderName);
if (folderId > 0) {
sp.edit().putLong(PREF_MEMORY_FOLDER_ID, folderId).commit();
return folderId;
}
folderId = createMemoryFolder(folderName);
if (folderId > 0) {
sp.edit().putLong(PREF_MEMORY_FOLDER_ID, folderId).commit();
return folderId;
}
return -1;
}
private long queryFolderIdByName(String name) {
ContentResolver resolver = mActivity.getContentResolver();
Cursor cursor = resolver.query(
Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.ID },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?"
+ " AND " + NoteColumns.SNIPPET + "=?",
new String[] { String.valueOf(Notes.TYPE_FOLDER),
String.valueOf(Notes.ID_TRASH_FOLER), name },
null);
if (cursor == null) {
return 0;
}
try {
if (cursor.moveToFirst()) {
return cursor.getLong(0);
}
} finally {
cursor.close();
}
return 0;
}
private long createMemoryFolder(String name) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
android.net.Uri uri = mActivity.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
if (uri == null) {
return 0;
}
try {
return Long.parseLong(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
return 0;
}
}
private String queryNoteContent(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(
Notes.CONTENT_DATA_URI,
new String[] { DataColumns.CONTENT },
DataColumns.NOTE_ID + "=? AND " + DataColumns.MIME_TYPE + "=?",
new String[] { String.valueOf(noteId), TextNote.CONTENT_ITEM_TYPE },
null);
if (cursor == null) {
return "";
}
try {
if (cursor.moveToFirst()) {
return cursor.getString(0);
}
} finally {
cursor.close();
}
return "";
}
private List<MemoryEntry> loadEntriesFromDatabase(long folderId) {
List<MemoryEntry> entries = new ArrayList<>();
ContentResolver resolver = mActivity.getContentResolver();
Cursor cursor = resolver.query(
Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.ID, NoteColumns.CREATED_DATE },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(folderId) },
NoteColumns.CREATED_DATE + " DESC");
if (cursor == null) {
return entries;
}
try {
while (cursor.moveToNext()) {
long noteId = cursor.getLong(0);
long createdDate = cursor.getLong(1);
entries.add(new MemoryEntry(noteId, createdDate, ""));
}
} finally {
cursor.close();
}
return entries;
}
private static final class MemoryEntry {
private final long id;
private final long createdDate;
private final String content;
private MemoryEntry(long id, long createdDate, String content) {
this.id = id;
this.createdDate = createdDate;
this.content = content;
}
}
private static final class LoadResult {
private final long folderId;
private final List<MemoryEntry> entries;
private final boolean loadedEntries;
private LoadResult(long folderId, List<MemoryEntry> entries, boolean loadedEntries) {
this.folderId = folderId;
this.entries = entries;
this.loadedEntries = loadedEntries;
}
}
private static final class LoadTask extends AsyncTask<Void, Void, LoadResult> {
private final WeakReference<MemoryBottleDialog> mRef;
private final boolean mLoadEntries;
private LoadTask(MemoryBottleDialog dialog, boolean loadEntries) {
mRef = new WeakReference<>(dialog);
mLoadEntries = loadEntries;
}
@Override
protected LoadResult doInBackground(Void... params) {
MemoryBottleDialog dialog = mRef.get();
if (dialog == null) {
return null;
}
long folderId = dialog.ensureMemoryFolder();
List<MemoryEntry> entries = new ArrayList<>();
if (folderId > 0 && mLoadEntries) {
entries = dialog.loadEntriesFromDatabase(folderId);
}
return new LoadResult(folderId, entries, mLoadEntries);
}
@Override
protected void onPostExecute(LoadResult result) {
MemoryBottleDialog dialog = mRef.get();
if (dialog == null || !dialog.isShowing()) {
return;
}
dialog.mLoadTask = null;
dialog.setLoading(false);
if (result == null || result.folderId <= 0) {
Toast.makeText(dialog.mActivity, R.string.memory_bottle_folder_error,
Toast.LENGTH_SHORT).show();
dialog.mPendingAction = PendingAction.NONE;
return;
}
dialog.mMemoryFolderId = result.folderId;
sFolderId = result.folderId;
if (result.loadedEntries) {
sAllEntries.clear();
sAllEntries.addAll(result.entries);
sRemainingEntries.clear();
sRemainingEntries.addAll(result.entries);
dialog.mEntriesLoaded = true;
} else if (sFolderId == result.folderId) {
dialog.mEntriesLoaded = !sAllEntries.isEmpty();
}
PendingAction pending = dialog.mPendingAction;
dialog.mPendingAction = PendingAction.NONE;
if (pending == PendingAction.ADD) {
dialog.showAddDialog();
} else if (pending == PendingAction.BROWSE) {
dialog.browseMemory();
}
}
}
private static final class BrowseResult {
private final MemoryEntry entry;
private final String content;
private BrowseResult(MemoryEntry entry, String content) {
this.entry = entry;
this.content = content;
}
}
private static final class BrowseTask extends AsyncTask<Void, Void, BrowseResult> {
private final WeakReference<MemoryBottleDialog> mRef;
private final MemoryEntry mEntry;
private BrowseTask(MemoryBottleDialog dialog, MemoryEntry entry) {
mRef = new WeakReference<>(dialog);
mEntry = entry;
}
@Override
protected BrowseResult doInBackground(Void... params) {
MemoryBottleDialog dialog = mRef.get();
if (dialog == null) {
return null;
}
String content = mEntry.content;
if (TextUtils.isEmpty(content)) {
content = dialog.queryNoteContent(dialog.mActivity.getContentResolver(), mEntry.id);
}
if (TextUtils.isEmpty(content)) {
content = dialog.mActivity.getString(R.string.memory_bottle_missing_content);
}
return new BrowseResult(mEntry, content);
}
@Override
protected void onPostExecute(BrowseResult result) {
MemoryBottleDialog dialog = mRef.get();
if (dialog == null || !dialog.isShowing()) {
return;
}
dialog.mBrowseTask = null;
dialog.setBrowseLoading(false);
if (result == null) {
return;
}
String message = dialog.formatEntryMessage(result.entry.createdDate, result.content);
new AlertDialog.Builder(dialog.mActivity)
.setTitle(R.string.memory_bottle_title)
.setMessage(message)
.setPositiveButton(R.string.memory_bottle_close, null)
.setNegativeButton(R.string.memory_bottle_delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
dialog.deleteMemoryEntry(result.entry);
}
})
.show();
}
}
private void deleteMemoryEntry(MemoryEntry entry) {
if (mMemoryFolderId <= 0) {
Toast.makeText(mActivity, R.string.memory_bottle_folder_error, Toast.LENGTH_SHORT).show();
return;
}
HashSet<Long> ids = new HashSet<Long>();
ids.add(entry.id);
if (DataUtils.batchMoveToTrash(mActivity.getContentResolver(), ids, mMemoryFolderId)) {
sAllEntries.remove(entry);
sRemainingEntries.remove(entry);
Toast.makeText(mActivity, R.string.memory_bottle_delete_success, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mActivity, R.string.memory_bottle_delete_failed, Toast.LENGTH_SHORT).show();
}
}
private enum PendingAction {
NONE,
ADD,
BROWSE
}
}

@ -31,80 +31,89 @@ import java.util.HashSet;
import java.util.Iterator;
public class NotesListAdapter extends CursorAdapter {//Adapter作为Activity和ListView之间的适配器将Cursor数据转换为ListView可以理解的视图格式
private static final String TAG = "NotesListAdapter"; // 日志标签
private Context mContext; // 上下文对象
private HashMap<Integer, Boolean> mSelectedIndex; // 选中项索引映射
private int mNotesCount; // 笔记数量(排除文件夹)
private boolean mChoiceMode; // 是否处于选择模式
public static class AppWidgetAttribute { // 小部件属性类用于存储小部件的ID和类型
public int widgetId; // 小部件ID
public int widgetType; // 小部件类型
public class NotesListAdapter extends CursorAdapter {
private static final String TAG = "NotesListAdapter";
private Context mContext;
private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount;
private boolean mChoiceMode;
private boolean mIncludeFolders;
public static class AppWidgetAttribute {
public int widgetId;
public int widgetType;
};
public NotesListAdapter(Context context) { // 构造函数创建NotesListAdapter实例
public NotesListAdapter(Context context) {
super(context, null);
mSelectedIndex = new HashMap<Integer, Boolean>(); // 初始化选中项映射
mSelectedIndex = new HashMap<Integer, Boolean>();
mContext = context;
mNotesCount = 0;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context); // 创建新的NotesListItem
return new NotesListItem(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor); // 创建数据模型
((NotesListItem) view).bind(context, itemData, mChoiceMode, // 绑定数据到视图
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
}
}
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked); // 更新选中状态
notifyDataSetChanged(); // 通知数据变化刷新UI
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
public boolean isInChoiceMode() {
return mChoiceMode; // 检查是否处于选择模式
return mChoiceMode;
}
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear(); // 清除之前的选中状态
mChoiceMode = mode; // 更新模式标志
mSelectedIndex.clear();
mChoiceMode = mode;
mIncludeFolders = false;
}
public void setChoiceMode(boolean mode, boolean includeFolders) {
mSelectedIndex.clear();
mChoiceMode = mode;
mIncludeFolders = includeFolders;
}
public void selectAll(boolean checked) {
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { // 只处理普通笔记,跳过文件夹
setCheckedItem(i, checked); // 设置选中状态
int type = NoteItemData.getNoteType(cursor);
if (type == Notes.TYPE_NOTE || (mIncludeFolders && type == Notes.TYPE_FOLDER)) {
setCheckedItem(i, checked);
}
}
}
}
public HashSet<Long> getSelectedItemIds() {// 获取选中项的ID集合
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Long id = getItemId(position); // 获取选中项的ID
Long id = getItemId(position);
if (id == Notes.ID_ROOT_FOLDER) {
Log.d(TAG, "Wrong item id, should not happen");
} else {
itemSet.add(id); // 添加到集合
itemSet.add(id);
}
}
}
return itemSet;
}
public HashSet<AppWidgetAttribute> getSelectedWidget() {// 获取选中项的小部件属性集合
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
@ -112,10 +121,12 @@ public class NotesListAdapter extends CursorAdapter {//Adapter作为Activity和L
if (c != null) {
AppWidgetAttribute widget = new AppWidgetAttribute();
NoteItemData item = new NoteItemData(mContext, c);
widget.widgetId = item.getWidgetId(); // 获取小部件ID
widget.widgetType = item.getWidgetType(); // 获取小部件类型
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
// Don't close cursor here, only the adapter could close it
/**
* Don't close cursor here, only the adapter could close it
*/
} else {
Log.e(TAG, "Invalid cursor");
return null;
@ -125,7 +136,7 @@ public class NotesListAdapter extends CursorAdapter {//Adapter作为Activity和L
return itemSet;
}
public int getSelectedCount() {// 获取选中项数量
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
return 0;
@ -137,39 +148,39 @@ public class NotesListAdapter extends CursorAdapter {//Adapter作为Activity和L
count++;
}
}
return count; // 返回选中项数量
return count;
}
public boolean isAllSelected() {// 检查是否所有笔记都被选中
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
return (checkedCount != 0 && checkedCount == mNotesCount);
}
public boolean isSelectedItem(final int position) {// 检查指定位置的项是否被选中
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position); // 检查指定位置的项是否被选中
return mSelectedIndex.get(position);
}
@Override
protected void onContentChanged() {
super.onContentChanged();
calcNotesCount(); // 当内容变化时,重新计算笔记数量
calcNotesCount();
}
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
calcNotesCount(); // 当游标变化时,重新计算笔记数量
calcNotesCount();
}
private void calcNotesCount() {// 计算普通笔记的数量
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { // 只统计普通笔记,排除文件夹
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
}
} else {

@ -31,16 +31,13 @@ import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
public class NotesListItem extends LinearLayout {
private ImageView mAlert; // 提醒图标(时钟或通话记录图标)
private TextView mTitle; // 笔记/文件夹标题或内容摘要
private TextView mTime; // 最后修改时间
private TextView mCallName; // 通话记录来电者名称(仅通话记录显示)
private NoteItemData mItemData; // 当前列表项的数据模型
private CheckBox mCheckBox; // 选择模式下的复选框(用于批量操作)
private ImageView mAlert;
private TextView mTitle;
private TextView mTime;
private TextView mCallName;
private NoteItemData mItemData;
private CheckBox mCheckBox;
/**
*
*/
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
@ -51,13 +48,7 @@ public class NotesListItem extends LinearLayout {
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
/**
* UI
* @param data
* @param checked
*/
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 处理选择模式显示
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
@ -66,7 +57,6 @@ public class NotesListItem extends LinearLayout {
}
mItemData = data;
// 处理通话记录文件夹
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
@ -74,18 +64,14 @@ public class NotesListItem extends LinearLayout {
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
}
// 处理回收站文件夹
else if (data.getId() == Notes.ID_TRASH_FOLER) {
} else if (data.getId() == Notes.ID_TRASH_FOLER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.trash_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.trash);
}
// 处理通话记录笔记
else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mAlert.setImageResource(android.R.drawable.ic_menu_delete);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
@ -96,9 +82,14 @@ public class NotesListItem extends LinearLayout {
} else {
mAlert.setVisibility(View.GONE);
}
}
// 处理普通文件夹和笔记
else {
} else if (data.getId() == Notes.ID_TRASH_FOLER) { //为回收站添加图标和显示逻辑
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.trash_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.baseline_restore_from_trash_24);
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
@ -117,20 +108,14 @@ public class NotesListItem extends LinearLayout {
}
}
}
// 设置最后修改时间(相对时间格式)
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 根据数据类型和位置设置背景
setBackground(data);
}
/**
*
*/
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
// 根据笔记在列表中的位置设置不同背景
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
@ -141,14 +126,10 @@ public class NotesListItem extends LinearLayout {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
// 文件夹使用统一背景
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
/**
*
*/
public NoteItemData getItemData() {
return mItemData;
}

@ -0,0 +1,192 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.HashSet;
public class TrashManager {
private static final String TAG = "TrashManager";
private final Context mContext;
private final ContentResolver mResolver;
private final Callback mCallback;
public interface Callback {
void onWidgetsNeedUpdate(HashSet<AppWidgetAttribute> widgets);
void onListChanged();
void onActionModeFinished();
void onRestoreInvalid();
}
public TrashManager(Context context, ContentResolver resolver, Callback callback) {
mContext = context;
mResolver = resolver;
mCallback = callback;
}
public void cleanupExpiredTrash() {
long expireTime = System.currentTimeMillis() - 24L * 60L * 60L * 1000L;
mResolver.delete(Notes.CONTENT_NOTE_URI,
NoteColumns.PARENT_ID + "=? AND " + NoteColumns.MODIFIED_DATE + "<?",
new String[] { String.valueOf(Notes.ID_TRASH_FOLER), String.valueOf(expireTime) });
}
public void batchDelete(final boolean inTrash, final HashSet<Long> ids,
final HashSet<AppWidgetAttribute> widgets, final long originFolderId) {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
if (inTrash) {
if (!DataUtils.batchDeleteNotes(mResolver, ids)) {
Log.e(TAG, "Delete notes error, should not happens");
}
} else {
if (!DataUtils.batchMoveToTrash(mResolver, ids, originFolderId)) {
Log.e(TAG, "Move notes to trash folder error");
}
}
return widgets;
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> resultWidgets) {
if (mCallback != null) {
mCallback.onWidgetsNeedUpdate(resultWidgets);
mCallback.onListChanged();
mCallback.onActionModeFinished();
}
}
}.execute();
}
public void restoreSelected(final HashSet<Long> ids, final HashSet<AppWidgetAttribute> widgets) {
new AsyncTask<Void, Void, Boolean>() {
protected Boolean doInBackground(Void... params) {
boolean hasInvalid = false;
long now = System.currentTimeMillis();
for (long id : ids) {
Cursor cursor = mResolver.query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id),
new String[] { NoteColumns.ORIGIN_PARENT_ID, NoteColumns.TYPE },
null, null, null);
if (cursor == null) {
continue;
}
long originParent = Notes.ID_ROOT_FOLDER;
int type = Notes.TYPE_NOTE;
try {
if (cursor.moveToFirst()) {
originParent = cursor.getLong(0);
type = cursor.getInt(1);
}
} finally {
cursor.close();
}
long targetParent = resolveRestoreParent(originParent);
if (targetParent != originParent) {
hasInvalid = true;
}
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, targetParent);
values.put(NoteColumns.ORIGIN_PARENT_ID, 0);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.MODIFIED_DATE, now);
mResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id),
values, null, null);
if (type == Notes.TYPE_FOLDER) {
restoreNotesForFolder(id, now);
}
}
return hasInvalid;
}
@Override
protected void onPostExecute(Boolean hasInvalid) {
if (mCallback != null) {
if (hasInvalid != null && hasInvalid) {
mCallback.onRestoreInvalid();
}
mCallback.onWidgetsNeedUpdate(widgets);
mCallback.onListChanged();
mCallback.onActionModeFinished();
}
}
}.execute();
}
public HashSet<AppWidgetAttribute> moveFolderToTrash(long folderId, long originFolderId) {
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mResolver, folderId);
DataUtils.moveNotesToTrashForFolder(mResolver, folderId);
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
if (!DataUtils.batchMoveToTrash(mResolver, ids, originFolderId)) {
Log.e(TAG, "Move folder to trash error");
}
return widgets;
}
private void restoreNotesForFolder(long folderId, long now) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, folderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, 0);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.MODIFIED_DATE, now);
mResolver.update(Notes.CONTENT_NOTE_URI, values,
NoteColumns.PARENT_ID + "=? AND " + NoteColumns.ORIGIN_PARENT_ID + "=?",
new String[] { String.valueOf(Notes.ID_TRASH_FOLER), String.valueOf(folderId) });
}
private long resolveRestoreParent(long originParentId) {
if (originParentId == Notes.ID_ROOT_FOLDER || originParentId == Notes.ID_CALL_RECORD_FOLDER) {
return originParentId;
}
if (originParentId <= 0) {
return Notes.ID_ROOT_FOLDER;
}
Cursor cursor = mResolver.query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, originParentId),
new String[] { NoteColumns.ID, NoteColumns.PARENT_ID },
NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.ID_TRASH_FOLER) },
null);
if (cursor == null) {
return Notes.ID_ROOT_FOLDER;
}
try {
if (cursor.moveToFirst()) {
return originParentId;
}
} finally {
cursor.close();
}
return Notes.ID_ROOT_FOLDER;
}
}
Loading…
Cancel
Save