diff --git a/src/main/java/net/micode/notes/tool/DataUtils.java b/src/main/java/net/micode/notes/tool/DataUtils.java new file mode 100644 index 0000000..42b64f7 --- /dev/null +++ b/src/main/java/net/micode/notes/tool/DataUtils.java @@ -0,0 +1,519 @@ +/* + * 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.tool; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; + +import java.util.ArrayList; +import java.util.HashSet; + + +/** + * DataUtils - 数据工具类 + *
+ * 提供便签数据的各种操作方法,包括批量删除、移动、查询、验证等 + * 所有方法均为静态方法,方便直接调用 + *
+ * + * @author MiCode Open Source Community + * @version 1.0 + */ +public class DataUtils { + public static final String TAG = "DataUtils"; // 日志标签 + + /** + * 批量删除便签 + *+ * 通过ContentProvider批量删除指定ID的便签 + *
+ * + * @param resolver ContentResolver对象 + * @param ids 要删除的便签ID集合 + * @return 删除是否成功 + */ + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet+ * 更新便签的父文件夹ID,并标记为本地修改 + *
+ * + * @param resolver ContentResolver对象 + * @param id 便签ID + * @param srcFolderId 源文件夹ID + * @param desFolderId 目标文件夹ID + */ + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.PARENT_ID, desFolderId); + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); + } + + /** + * 批量移动便签到指定文件夹 + *+ * 通过ContentProvider批量更新便签的父文件夹ID + *
+ * + * @param resolver ContentResolver对象 + * @param ids 要移动的便签ID集合 + * @param folderId 目标文件夹ID + * @return 移动是否成功 + */ + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet+ * 获取除系统文件夹外的所有文件夹数量 + *
+ * + * @param resolver ContentResolver对象 + * @return 用户文件夹数量 + */ + public static int getUserFolderCount(ContentResolver resolver) { + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, + null); + + int count = 0; + if(cursor != null) { + if(cursor.moveToFirst()) { + try { + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get folder count failed:" + e.toString()); + } finally { + cursor.close(); + } + } + } + return count; + } + + /** + * 检查便签是否在数据库中可见 + *+ * 检查指定类型的便签是否存在于数据库中且不在回收站 + *
+ * + * @param resolver ContentResolver对象 + * @param noteId 便签ID + * @param type 便签类型 + * @return 是否可见 + */ + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, + new String [] {String.valueOf(type)}, + null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + /** + * 检查便签是否存在于数据库中 + *+ * 检查指定ID的便签是否存在于数据库中 + *
+ * + * @param resolver ContentResolver对象 + * @param noteId 便签ID + * @return 是否存在 + */ + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + /** + * 检查数据是否存在于数据库中 + *+ * 检查指定ID的数据是否存在于数据库中 + *
+ * + * @param resolver ContentResolver对象 + * @param dataId 数据ID + * @return 是否存在 + */ + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + /** + * 检查可见文件夹名称是否存在 + *+ * 检查除回收站外的文件夹中是否已存在指定名称的文件夹 + *
+ * + * @param resolver ContentResolver对象 + * @param name 文件夹名称 + * @return 是否存在 + */ + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", + new String[] { name }, null); + boolean exist = false; + if(cursor != null) { + if(cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + /** + * 获取文件夹下的便签小部件 + *+ * 查询指定文件夹下所有便签关联的小部件信息 + *
+ * + * @param resolver ContentResolver对象 + * @param folderId 文件夹ID + * @return 小部件属性集合 + */ + public static HashSet+ * 查询指定通话便签的电话号码 + *
+ * + * @param resolver ContentResolver对象 + * @param noteId 便签ID + * @return 电话号码,不存在则返回空字符串 + */ + public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.PHONE_NUMBER }, + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + null); + + if (cursor != null && cursor.moveToFirst()) { + try { + return cursor.getString(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call number fails " + e.toString()); + } finally { + cursor.close(); + } + } + return ""; + } + + /** + * 根据电话号码和通话日期获取便签ID + *+ * 查询指定电话号码和通话日期对应的通话便签ID + *
+ * + * @param resolver ContentResolver对象 + * @param phoneNumber 电话号码 + * @param callDate 通话日期 + * @return 便签ID,不存在则返回0 + */ + public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.NOTE_ID }, + CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + + CallNote.PHONE_NUMBER + ",?)", + new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, + null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + try { + return cursor.getLong(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call note id fails " + e.toString()); + } + } + cursor.close(); + } + return 0; + } + + /** + * 根据便签ID获取摘要 + *+ * 查询指定便签的摘要内容 + *
+ * + * @param resolver ContentResolver对象 + * @param noteId 便签ID + * @return 便签摘要 + * @throws IllegalArgumentException 当便签不存在时抛出 + */ + public static String getSnippetById(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, + new String [] { NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String [] { String.valueOf(noteId)}, + null); + + if (cursor != null) { + String snippet = ""; + if (cursor.moveToFirst()) { + snippet = cursor.getString(0); + } + cursor.close(); + return snippet; + } + throw new IllegalArgumentException("Note is not found with id: " + noteId); + } + + /** + * 格式化摘要 + *+ * 去除摘要前后空格,并只保留第一行 + *
+ * + * @param snippet 原始摘要 + * @return 格式化后的摘要 + */ + public static String getFormattedSnippet(String snippet) { + if (snippet != null) { + snippet = snippet.trim(); + int index = snippet.indexOf('\n'); + if (index != -1) { + snippet = snippet.substring(0, index); + } + } + return snippet; + } + + /** + * 获取指定文件夹中的便签数量 + *+ * 查询指定文件夹中包含的便签数量 + *
+ * + * @param resolver ContentResolver对象 + * @param folderId 文件夹ID + * @return 便签数量 + */ + public static int getNoteCount(ContentResolver resolver, long folderId) { + Cursor cursor = resolver.query( + Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(folderId) }, + null + ); + + int count = 0; + if (cursor != null) { + if (cursor.moveToFirst()) { + try { + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get note count failed:" + e.toString()); + } finally { + cursor.close(); + } + } + } + return count; + } + + /** + * 获取未分类的便签数量 + *+ * 查询直接在根文件夹中的便签数量,即未分类的便签 + *
+ * + * @param resolver ContentResolver对象 + * @return 未分类的便签数量 + */ + public static int getUncategorizedNoteCount(ContentResolver resolver) { + // 未分类的便签就是直接在根文件夹中的便签 + return getNoteCount(resolver, Notes.ID_ROOT_FOLDER); + } +} diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 57d7f3a..8a4de46 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -833,6 +833,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, saveNote(); super.onBackPressed(); + // 添加返回动画效果 + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); } /** diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 6ffe5f4..ac51489 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -1117,6 +1117,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt intent.setAction(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, data.getId()); this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); + // 添加小米便签特色打开动画 + overridePendingTransition(R.anim.xiaomi_note_open_enter, R.anim.xiaomi_note_open_exit); } } @@ -1620,42 +1622,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } }); - - // 设置用户文件夹列表长按事件,实现删除文件夹功能 - mUserFolderListView.setOnItemLongClickListener(new OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) { - SidebarAdapter.SidebarItem item = (SidebarAdapter.SidebarItem) mUserFolderAdapter.getItem(position); - if (item != null) { - final long folderId = item.getCategoryId(); - if (folderId == Notes.ID_ROOT_FOLDER) { - // 根文件夹不能删除 - Toast.makeText(NotesListActivity.this, "根文件夹不能删除", Toast.LENGTH_SHORT).show(); - return true; - } - - // 显示确认删除对话框 - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle("确认删除"); - builder.setMessage("确定要删除文件夹 \"" + item.getTitle() + "\" 吗?"); - builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // 删除文件夹 - deleteFolder(folderId); - // 重新加载侧边栏数据 - loadSidebarData(); - // 显示删除成功提示 - Toast.makeText(NotesListActivity.this, "文件夹已删除", Toast.LENGTH_SHORT).show(); - } - }); - builder.setNegativeButton("取消", null); - builder.show(); - return true; - } - return false; - } - }); // 设置添加文件夹按钮点击事件 mAddFolderButton.setOnClickListener(new OnClickListener() { diff --git a/src/main/res/anim/note_open_enter.xml b/src/main/res/anim/note_open_enter.xml new file mode 100644 index 0000000..9ace166 --- /dev/null +++ b/src/main/res/anim/note_open_enter.xml @@ -0,0 +1,16 @@ + +