针对回收站功能疑修 #13

Merged
p5vjqsb6e merged 1 commits from luogang_branch into master 2 months ago

@ -1,27 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) <!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.micode.notes"
android:versionCode="1" android:versionCode="1"
android:versionName="0.1" > android:versionName="0.1" >
<!-- 已移除package属性已注释minSdkVersion可选删除 -->
<uses-sdk android:minSdkVersion="14" /> <!-- <uses-sdk android:minSdkVersion="14" /> -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -30,8 +23,7 @@
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:icon="@drawable/icon_app" android:icon="@drawable/icon_app"
android:label="@string/app_name" > android:label="@string/app_name" >
@ -42,107 +34,103 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/NoteTheme" android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow" android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan" > android:windowSoftInputMode="adjustPan"
android:exported="true" > <!-- 新增 -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".ui.NoteEditActivity" android:name=".ui.NoteEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/NoteTheme" > android:theme="@style/NoteTheme"
android:exported="true" > <!-- 新增 -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" /> <data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" /> <data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.INSERT_OR_EDIT" /> <action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" /> <data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" /> <data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEARCH" /> <action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.searchable" android:name="android.app.searchable"
android:resource="@xml/searchable" /> android:resource="@xml/searchable" />
</activity> </activity>
<provider <provider
android:name="net.micode.notes.data.NotesProvider" android:name="net.micode.notes.data.NotesProvider"
android:authorities="micode_notes" android:authorities="micode_notes"
android:multiprocess="true" /> android:multiprocess="true" />
<receiver <receiver
android:name=".widget.NoteWidgetProvider_2x" android:name=".widget.NoteWidgetProvider_2x"
android:label="@string/app_widget2x2" > android:label="@string/app_widget2x2"
android:exported="false" > <!-- 新增 -->
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" /> <action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" /> <action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_2x_info" /> android:resource="@xml/widget_2x_info" />
</receiver> </receiver>
<receiver <receiver
android:name=".widget.NoteWidgetProvider_4x" android:name=".widget.NoteWidgetProvider_4x"
android:label="@string/app_widget4x4" > android:label="@string/app_widget4x4"
android:exported="false" > <!-- 新增 -->
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" /> <action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" /> <action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_4x_info" /> android:resource="@xml/widget_4x_info" />
</receiver> </receiver>
<receiver
<receiver android:name=".ui.AlarmInitReceiver" > android:name=".ui.AlarmInitReceiver"
android:exported="true" > <!-- 新增 -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver <receiver
android:name="net.micode.notes.ui.AlarmReceiver" android:name="net.micode.notes.ui.AlarmReceiver"
android:process=":remote" > android:process=":remote" >
</receiver> </receiver>
<activity <activity
android:name=".ui.AlarmAlertActivity" android:name=".ui.AlarmAlertActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" > android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity> </activity>
<activity <activity
android:name="net.micode.notes.ui.NotesPreferenceActivity" android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title" android:label="@string/preferences_title"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" > android:theme="@android:style/Theme.Holo.Light" >
</activity> </activity>
<activity
android:name=".ui.TrashActivity"
android:label="@string/menu_trash"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<service <service
android:name="net.micode.notes.gtask.remote.GTaskSyncService" android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" > android:exported="false" >
</service> </service>
<meta-data <meta-data
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" /> android:value=".ui.NoteEditActivity" />

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="8dp">
<Button
android:id="@+id/btn_clean_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_clean_all"
android:minWidth="120dp"
android:padding="16dp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
android:id="@+id/btn_return"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_return"
android:minWidth="120dp"
android:padding="16dp" />
</LinearLayout>
<ListView
android:id="@+id/trash_list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:cacheColorHint="@null"
android:listSelector="@android:color/transparent"
android:divider="@null"
android:fadingEdge="@null" />
</LinearLayout>
</FrameLayout>

@ -36,4 +36,8 @@
<item <item
android:id="@+id/menu_search" android:id="@+id/menu_search"
android:title="@string/menu_search"/> android:title="@string/menu_search"/>
<item
android:id="@+id/menu_trash"
android:title="@string/menu_trash"/>
</menu> </menu>

@ -42,6 +42,11 @@
<string name="menu_sync_cancel">取消同步</string> <string name="menu_sync_cancel">取消同步</string>
<string name="menu_setting">设置</string> <string name="menu_setting">设置</string>
<string name="menu_search">搜索</string> <string name="menu_search">搜索</string>
<string name="menu_trash">回收站</string>
<string name="button_return">返回</string>
<string name="button_clean_all">清空</string>
<string name="button_recovery">恢复</string>
<string name="menu_success">成功</string>
<string name="menu_delete">删除</string> <string name="menu_delete">删除</string>
<string name="menu_move">移动到文件夹</string> <string name="menu_move">移动到文件夹</string>
<string name="menu_select_title">选中了 %d 项</string> <string name="menu_select_title">选中了 %d 项</string>
@ -70,6 +75,7 @@
<string name="alert_message_delete_notes">确认要删除所选的 %d 条便签吗?</string> <string name="alert_message_delete_notes">确认要删除所选的 %d 条便签吗?</string>
<string name="alert_message_delete_note">确认要删除该条便签吗?</string> <string name="alert_message_delete_note">确认要删除该条便签吗?</string>
<string name="alert_message_delete_folder">确认删除文件夹及所包含的便签吗?</string> <string name="alert_message_delete_folder">确认删除文件夹及所包含的便签吗?</string>
<string name="alert_message_clear_all_trash">确认清空回收站中的所有便签吗?此操作无法撤销。</string>
<string name="format_move_notes_to_folder">已将所选 %1$d 条便签移到 %2$s 文件夹</string> <string name="format_move_notes_to_folder">已将所选 %1$d 条便签移到 %2$s 文件夹</string>
<!-- export text --> <!-- export text -->
<string name="error_sdcard_unmounted">SD卡被占用不能操作</string> <string name="error_sdcard_unmounted">SD卡被占用不能操作</string>

@ -46,6 +46,11 @@
<string name="menu_sync_cancel">Cancel syncing</string> <string name="menu_sync_cancel">Cancel syncing</string>
<string name="menu_setting">Settings</string> <string name="menu_setting">Settings</string>
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
<string name="menu_trash">Trash</string>
<string name="button_return">Return</string>
<string name="button_clean_all">Clean All</string>
<string name="button_recovery">Recovery</string>
<string name="menu_success">Success</string>
<string name="menu_delete">Delete</string> <string name="menu_delete">Delete</string>
<string name="menu_move">Move to folder</string> <string name="menu_move">Move to folder</string>
<string name="menu_select_title">%d selected</string> <string name="menu_select_title">%d selected</string>
@ -72,6 +77,7 @@
<string name="info_note_enter_desktop">Note added to home</string> <string name="info_note_enter_desktop">Note added to home</string>
<string name="alert_message_delete_folder">Confirm to delete folder and its notes?</string> <string name="alert_message_delete_folder">Confirm to delete folder and its notes?</string>
<string name="alert_title_delete">Delete selected notes</string> <string name="alert_title_delete">Delete selected notes</string>
<string name="alert_message_clear_all_trash">Confirm to clear all notes from trash? This action cannot be undone.</string>
<string name="alert_message_delete_notes">Confirm to delete the selected %d notes?</string> <string name="alert_message_delete_notes">Confirm to delete the selected %d notes?</string>
<string name="alert_message_delete_note">Confirm to delete this note?</string> <string name="alert_message_delete_note">Confirm to delete this note?</string>
<string name="format_move_notes_to_folder">Have moved selected %1$d notes to %2$s folder</string> <string name="format_move_notes_to_folder">Have moved selected %1$d notes to %2$s folder</string>

@ -17,43 +17,24 @@
package net.micode.notes.data; package net.micode.notes.data;
import android.net.Uri; import android.net.Uri;
/**
*
*
*
*/
public class Notes { public class Notes {
// ContentProvider的授权标识
public static final String AUTHORITY = "micode_notes"; public static final String AUTHORITY = "micode_notes";
// 日志标签
public static final String TAG = "Notes"; public static final String TAG = "Notes";
// 0类型普通笔记
public static final int TYPE_NOTE = 0; public static final int TYPE_NOTE = 0;
// 1类型文件夹
public static final int TYPE_FOLDER = 1; public static final int TYPE_FOLDER = 1;
// 2类型系统文件夹
public static final int TYPE_SYSTEM = 2; public static final int TYPE_SYSTEM = 2;
// 系统文件夹的 ID 定义。
// #ID_ROOT_FOLDER 默认根文件夹
// #ID_TEMPARAY_FOLDER 临时文件夹,移动笔记时的中转
// #ID_CALL_RECORD_FOLDER 通话记录专用文件夹
// #ID_TRASH_FOLER 回收站
public static final int ID_ROOT_FOLDER = 0; // 默认根文件夹
public static final int ID_TEMPARAY_FOLDER = -1; // 临时文件夹(用于无归属的笔记)
public static final int ID_CALL_RECORD_FOLDER = -2; // 通话记录文件夹
public static final int ID_TRASH_FOLER = -3; // 回收站文件夹
/** /**
* Intent Extra Android Intent * Following IDs are system folders' identifiers
* 1. * {@link Notes#ID_ROOT_FOLDER } is default folder
* 2. ID * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder
* 3. ID * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records
* 4.
* 5. ID
* 6.
*/ */
public static final int ID_ROOT_FOLDER = 0;
public static final int ID_TEMPARAY_FOLDER = -1;
public static final int ID_CALL_RECORD_FOLDER = -2;
public static final int ID_TRASH_FOLER = -3;
public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";
public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
@ -61,176 +42,251 @@ public class Notes {
public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
// 无效的小部件类型 -1
public static final int TYPE_WIDGET_INVALIDE = -1; public static final int TYPE_WIDGET_INVALIDE = -1;
// 2x2小部件类型 0
public static final int TYPE_WIDGET_2X = 0; public static final int TYPE_WIDGET_2X = 0;
// 4x4小部件类型 1
public static final int TYPE_WIDGET_4X = 1; public static final int TYPE_WIDGET_4X = 1;
/**
*
*/
public static class DataConstants { public static class DataConstants {
// 普通笔记的MIME类型
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
// 通话记录笔记的MIME类型
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
} }
// URI 是 ContentProvider 的标准访问入口,用于统一访问应用的数据。 /**
// 查询所有笔记和文件夹的URI * Uri to query all notes and folders
*/
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
// 查询数据表的URI
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/** /**
* NoteColumns - 便(notes) * Uri to query data
* 便
*/ */
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/** /**
* * Uri to query trash notes
*/ */
public static final Uri CONTENT_TRASH_URI = Uri.parse("content://" + AUTHORITY + "/trash");
public interface NoteColumns { public interface NoteColumns {
/**
// 行的唯一ID * The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static final String ID = "_id"; public static final String ID = "_id";
// 笔记或文件夹的父ID /**
* The parent's id for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String PARENT_ID = "parent_id"; public static final String PARENT_ID = "parent_id";
// 笔记或文件夹的创建日期 /**
* Created data for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String CREATED_DATE = "created_date"; public static final String CREATED_DATE = "created_date";
// 最后修改日期 /**
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static final String MODIFIED_DATE = "modified_date"; public static final String MODIFIED_DATE = "modified_date";
// 提醒日期
/**
* Alert date
* <P> Type: INTEGER (long) </P>
*/
public static final String ALERTED_DATE = "alert_date"; public static final String ALERTED_DATE = "alert_date";
// 文件夹名称或笔记的文本内容摘要 /**
* Folder's name or text content of note
* <P> Type: TEXT </P>
*/
public static final String SNIPPET = "snippet"; public static final String SNIPPET = "snippet";
// 笔记的小部件ID /**
* Note's widget id
* <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_ID = "widget_id"; public static final String WIDGET_ID = "widget_id";
// 笔记的小部件类型 /**
* Note's widget type
* <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_TYPE = "widget_type"; public static final String WIDGET_TYPE = "widget_type";
// 笔记的背景颜色ID /**
* Note's background color's id
* <P> Type: INTEGER (long) </P>
*/
public static final String BG_COLOR_ID = "bg_color_id"; public static final String BG_COLOR_ID = "bg_color_id";
// 是否有附件;对于文本笔记没有附件,多媒体笔记至少有一个附件 /**
* For text note, it doesn't has attachment, for multi-media
* note, it has at least one attachment
* <P> Type: INTEGER </P>
*/
public static final String HAS_ATTACHMENT = "has_attachment"; public static final String HAS_ATTACHMENT = "has_attachment";
// 文件夹中的笔记数量 /**
* Folder's count of notes
* <P> Type: INTEGER (long) </P>
*/
public static final String NOTES_COUNT = "notes_count"; public static final String NOTES_COUNT = "notes_count";
// 文件类型:文件夹或笔记 /**
* The file type: folder or note
* <P> Type: INTEGER </P>
*/
public static final String TYPE = "type"; public static final String TYPE = "type";
// 最后一次同步的ID /**
* The last sync id
* <P> Type: INTEGER (long) </P>
*/
public static final String SYNC_ID = "sync_id"; public static final String SYNC_ID = "sync_id";
// 标记是否在本地被修改 /**
* Sign to indicate local modified or not
* <P> Type: INTEGER </P>
*/
public static final String LOCAL_MODIFIED = "local_modified"; public static final String LOCAL_MODIFIED = "local_modified";
// 移动到临时文件夹之前的原始父ID /**
* Original parent id before moving into temporary folder
* <P> Type : INTEGER </P>
*/
public static final String ORIGIN_PARENT_ID = "origin_parent_id"; public static final String ORIGIN_PARENT_ID = "origin_parent_id";
// Google Task的ID /**
* The gtask id
* <P> Type : TEXT </P>
*/
public static final String GTASK_ID = "gtask_id"; public static final String GTASK_ID = "gtask_id";
// 版本号 /**
* The version code
* <P> Type : INTEGER (long) </P>
*/
public static final String VERSION = "version"; public static final String VERSION = "version";
} }
/** public interface TrashColumns extends NoteColumns {
* DataColumns - 便(data) /**
* 便 * Deleted date for trash note
* 使MIME_TYPE便便 * <P> Type: INTEGER (long) </P>
*/ */
public static final String DELETED_DATE = "deleted_date";
}
public interface DataColumns { public interface DataColumns {
// 行的唯一ID /**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static final String ID = "_id"; public static final String ID = "_id";
// 该行数据项的MIME类型 /**
* The MIME type of the item represented by this row.
* <P> Type: Text </P>
*/
public static final String MIME_TYPE = "mime_type"; public static final String MIME_TYPE = "mime_type";
// 该数据所属笔记的引用ID /**
* The reference id to note that this data belongs to
* <P> Type: INTEGER (long) </P>
*/
public static final String NOTE_ID = "note_id"; public static final String NOTE_ID = "note_id";
// 笔记或文件夹的创建日期 /**
* Created data for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String CREATED_DATE = "created_date"; public static final String CREATED_DATE = "created_date";
// 最后修改日期 /**
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static final String MODIFIED_DATE = "modified_date"; public static final String MODIFIED_DATE = "modified_date";
// 数据内容 /**
* Data's content
* <P> Type: TEXT </P>
*/
public static final String CONTENT = "content"; public static final String CONTENT = "content";
// 通用数据列1
/**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* integer data type
* <P> Type: INTEGER </P>
*/
public static final String DATA1 = "data1"; public static final String DATA1 = "data1";
// 通用数据列2 /**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* integer data type
* <P> Type: INTEGER </P>
*/
public static final String DATA2 = "data2"; public static final String DATA2 = "data2";
// 通用数据列3 /**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static final String DATA3 = "data3"; public static final String DATA3 = "data3";
// 通用数据列4 /**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static final String DATA4 = "data4"; public static final String DATA4 = "data4";
// 通用数据列5 /**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static final String DATA5 = "data5"; public static final String DATA5 = "data5";
} }
/**
* TextNote - 便
* 便URI
*/
public static final class TextNote implements DataColumns { public static final class TextNote implements DataColumns {
// 模式标识:文本是否为清单模式 /**
* Mode to indicate the text in check list mode or not
* <P> Type: Integer 1:check list mode 0: normal mode </P>
*/
public static final String MODE = DATA1; public static final String MODE = DATA1;
// 清单模式常量
public static final int MODE_CHECK_LIST = 1; public static final int MODE_CHECK_LIST = 1;
// 普通模式常量
public static final int MODE_NORMAL = 0;
// 文本笔记的目录MIME类型
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";
// 文本笔记的单项MIME类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";
// 文本笔记的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");
} }
/**
* CallNote - 便
* 便URI
*/
public static final class CallNote implements DataColumns { public static final class CallNote implements DataColumns {
// 通话日期 /**
* Call date for this record
* <P> Type: INTEGER (long) </P>
*/
public static final String CALL_DATE = DATA1; public static final String CALL_DATE = DATA1;
// 电话号码 /**
* Phone number for this record
* <P> Type: TEXT </P>
*/
public static final String PHONE_NUMBER = DATA3; public static final String PHONE_NUMBER = DATA3;
// 联系人姓名
public static final String CONTACT_NAME = DATA4;
// 通话记录笔记的目录MIME类型
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";
// 通话记录笔记的单项MIME类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";
// 通话记录笔记的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
} }
} }

@ -26,31 +26,23 @@ import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
/**
*
*
*/
public class NotesDatabaseHelper extends SQLiteOpenHelper { public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db"; // 数据库名称 private static final String DB_NAME = "note.db";
private static final int DB_VERSION = 4; // 数据库版本
; private static final int DB_VERSION = 5;
/**
*
*/
public interface TABLE { public interface TABLE {
/** 笔记表名 */
public static final String NOTE = "note"; public static final String NOTE = "note";
/** 数据表名 */
public static final String DATA = "data"; public static final String DATA = "data";
}
private static final String TAG = "NotesDatabaseHelper"; // 日志标签 public static final String TRASH = "trash_note";
private static NotesDatabaseHelper mInstance; // 单例实例 }
/** 数据库帮助类的单例实例 */ private static final String TAG = "NotesDatabaseHelper";
private static NotesDatabaseHelper mInstance;
private static final String CREATE_NOTE_TABLE_SQL = private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" + "CREATE TABLE " + TABLE.NOTE + "(" +
@ -73,7 +65,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")"; ")";
// 创建笔记数据表的SQL语句
private static final String CREATE_DATA_TABLE_SQL = private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" + "CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," + DataColumns.ID + " INTEGER PRIMARY KEY," +
@ -89,18 +80,46 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")"; ")";
// 创建笔记数据表中note_id列的索引
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " + "CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
private static final String CREATE_TRASH_TABLE_SQL =
"CREATE TABLE " + TABLE.TRASH + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," +
"deleted_date INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)" +
")";
/** /**
* * Increase folder's note count when move note to the folder
*/ */
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER ="CREATE TRIGGER increase_folder_count_on_update "+" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +" BEGIN " +" UPDATE " + TABLE.NOTE +" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +" END"; private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/** /**
* * Decrease folder's note count when move note from folder
*/ */
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_update " + "CREATE TRIGGER decrease_folder_count_on_update " +
@ -113,7 +132,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END"; " END";
/** /**
* * Increase folder's note count when insert new note to the folder
*/ */
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " + "CREATE TRIGGER increase_folder_count_on_insert " +
@ -125,7 +144,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END"; " END";
/** /**
* * Decrease folder's note count when delete note from the folder
*/ */
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " + "CREATE TRIGGER decrease_folder_count_on_delete " +
@ -137,7 +156,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" AND " + NoteColumns.NOTES_COUNT + ">0;" + " AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END"; " END";
/**插入 TEXT_NOTE 类型的数据行时,用数据内容刷新 note.snippet */ /**
* Update note's content when insert data with type {@link DataConstants#NOTE}
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " + "CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA + " AFTER INSERT ON " + TABLE.DATA +
@ -148,7 +169,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END"; " END";
/**更新 TEXT_NOTE 数据行时,同步更新 note.snippet */ /**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " + "CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA + " AFTER UPDATE ON " + TABLE.DATA +
@ -159,7 +182,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END"; " END";
/** 删除 TEXT_NOTE 数据行时,清空 note.snippet */ /**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " + "CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA + " AFTER delete ON " + TABLE.DATA +
@ -171,7 +196,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END"; " END";
/** /**
* * Delete datas belong to note which has been deleted
*/ */
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " + "CREATE TRIGGER delete_data_on_delete " +
@ -182,7 +207,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END"; " END";
/** /**
* * Delete notes belong to folder which has been deleted
*/ */
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " + "CREATE TRIGGER folder_delete_notes_on_delete " +
@ -193,7 +218,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END"; " END";
/** /**
* * Move notes belong to folder which has been moved to trash folder
*/ */
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " + "CREATE TRIGGER folder_move_notes_on_trash " +
@ -205,31 +230,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END"; " END";
/**
*
* @param context
*/
public NotesDatabaseHelper(Context context) { public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION); super(context, DB_NAME, null, DB_VERSION);
} }
/**
*
* @param db
*/
public void createNoteTable(SQLiteDatabase db) { public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL); // 创建笔记表结构 db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db); // 重建笔记表触发器 reCreateNoteTableTriggers(db);
createSystemFolder(db); // 创建系统文件夹 createSystemFolder(db);
Log.d(TAG, "note table has been created"); Log.d(TAG, "note table has been created");
} }
/**
*
* @param db
*/
private void reCreateNoteTableTriggers(SQLiteDatabase db) { private void reCreateNoteTableTriggers(SQLiteDatabase db) {
// 删除现有触发器
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
@ -238,7 +250,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
// 创建新触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
@ -248,71 +259,63 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
} }
/**
*
* @param db
*/
private void createSystemFolder(SQLiteDatabase db) { private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
/** /**
* * call record foler for call notes
*/ */
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
// 创建根文件夹(默认文件夹) /**
* root folder which is default folder
*/
values.clear(); values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
// 创建临时文件夹(用于移动笔记) /**
* temporary folder which is used for moving note
*/
values.clear(); values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
// 创建回收站文件夹 /**
* create trash folder
*/
values.clear(); values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
} }
/**
*
* @param db
*/
public void createDataTable(SQLiteDatabase db) { public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL); // 创建笔记数据表结构 db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db); // 重建笔记数据表触发器 reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); // 创建note_id列索引 db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
Log.d(TAG, "data table has been created"); Log.d(TAG, "data table has been created");
} }
/** public void createTrashTable(SQLiteDatabase db) {
* db.execSQL(CREATE_TRASH_TABLE_SQL);
* @param db Log.d(TAG, "trash_note table has been created");
*/ }
private void reCreateDataTableTriggers(SQLiteDatabase db) { private void reCreateDataTableTriggers(SQLiteDatabase db) {
// 删除现有触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
// 创建新触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
} }
/**
* NotesDatabaseHelper
* @param context
* @return NotesDatabaseHelper
*/
static synchronized NotesDatabaseHelper getInstance(Context context) { static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) { if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context); mInstance = new NotesDatabaseHelper(context);
@ -320,99 +323,80 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
return mInstance; return mInstance;
} }
/** 创建数据库:依次创建 note 表与 data 表 */
@Override @Override
public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
createNoteTable(db); // 创建笔记表 createDataTable(db);
createDataTable(db); // 创建笔记数据表 createTrashTable(db);
} }
/**
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
boolean reCreateTriggers = false; // 是否需要重建触发器 boolean skipV2 = false;
boolean skipV2 = false; // 是否跳过版本2的升级
// 从版本1升级
if (oldVersion == 1) { if (oldVersion == 1) {
upgradeToV2(db); upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++; oldVersion++;
} }
// 从版本2升级跳过已在版本1升级中处理的情况
if (oldVersion == 2 && !skipV2) { if (oldVersion == 2 && !skipV2) {
upgradeToV3(db); upgradeToV3(db);
reCreateTriggers = true; reCreateTriggers = true;
oldVersion++; oldVersion++;
} }
// 从版本3升级
if (oldVersion == 3) { if (oldVersion == 3) {
upgradeToV4(db); upgradeToV4(db);
oldVersion++; oldVersion++;
} }
// 如果需要,重建触发器 if (oldVersion == 4) {
upgradeToV5(db);
oldVersion++;
}
if (reCreateTriggers) { if (reCreateTriggers) {
reCreateNoteTableTriggers(db); reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db); reCreateDataTableTriggers(db);
} }
// 检查升级是否成功
if (oldVersion != newVersion) { if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion); throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ "fails");
} }
} }
/**
* 2
* @param db
*/
private void upgradeToV2(SQLiteDatabase db) { private void upgradeToV2(SQLiteDatabase db) {
// 删除旧表
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
// 创建新表
createNoteTable(db); createNoteTable(db);
createDataTable(db); createDataTable(db);
} }
/**
* 3
* @param db
*/
private void upgradeToV3(SQLiteDatabase db) { private void upgradeToV3(SQLiteDatabase db) {
// 删除不再使用的触发器 // drop unused triggers
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
// add a column for gtask id
// 添加gtask_id列用于同步
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''"); + " TEXT NOT NULL DEFAULT ''");
// add a trash system folder
// 添加回收站系统文件夹
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
} }
/**
* 4
* @param db
*/
private void upgradeToV4(SQLiteDatabase db) { private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0"); + " INTEGER NOT NULL DEFAULT 0");
} }
private void upgradeToV5(SQLiteDatabase db) {
createTrashTable(db);
Log.d(TAG, "Upgraded to version 5: created trash_note table");
}
} }

@ -35,58 +35,39 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE; import net.micode.notes.data.NotesDatabaseHelper.TABLE;
/**
*
* CRUDContentProvider
*
*/
public class NotesProvider extends ContentProvider { public class NotesProvider extends ContentProvider {
private static final UriMatcher mMatcher;
private static final UriMatcher mMatcher; // URI匹配器用于解析不同类型的URI请求
// 数据库帮助类实例,负责获取读写数据库
private NotesDatabaseHelper mHelper; private NotesDatabaseHelper mHelper;
private static final String TAG = "NotesProvider"; // 日志标签 private static final String TAG = "NotesProvider";
// URI匹配码笔记列表
private static final int URI_NOTE = 1; private static final int URI_NOTE = 1;
// URI匹配码单个笔记
private static final int URI_NOTE_ITEM = 2; private static final int URI_NOTE_ITEM = 2;
// URI匹配码数据列表
private static final int URI_DATA = 3; private static final int URI_DATA = 3;
// URI匹配码单个数据项
private static final int URI_DATA_ITEM = 4; private static final int URI_DATA_ITEM = 4;
// URI匹配码搜索
private static final int URI_SEARCH = 5; private static final int URI_SEARCH = 5;
// URI匹配码搜索建议
private static final int URI_SEARCH_SUGGEST = 6; private static final int URI_SEARCH_SUGGEST = 6;
private static final int URI_TRASH = 7;
private static final int URI_TRASH_ITEM = 8;
/**
* - URI
*/
static { static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 笔记集合URI
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 单个笔记URI带ID
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
// 笔记数据集合URI
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 单个笔记数据URI带ID
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
// 搜索URI
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 搜索建议URI两种形式
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, "trash", URI_TRASH);
mMatcher.addURI(Notes.AUTHORITY, "trash/#", URI_TRASH_ITEM);
} }
/** /**
* * x'0A' represents the '\n' character in sqlite. For title and content in the search result,
* x'0A' SQLite 便 * we will trim '\n' and white space in order to show more information.
*/ */
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
@ -96,35 +77,18 @@ public class NotesProvider extends ContentProvider {
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
/**
*
*
*/
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE + " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
/**
*
*
*/
@Override @Override
public boolean onCreate() { public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext()); mHelper = NotesDatabaseHelper.getInstance(getContext());
return true; return true;
} }
/**
* -
* @param uri URI
* @param projection
* @param selection
* @param selectionArgs
* @param sortOrder
* @return
*/
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) { String sortOrder) {
@ -132,27 +96,35 @@ public class NotesProvider extends ContentProvider {
SQLiteDatabase db = mHelper.getReadableDatabase(); SQLiteDatabase db = mHelper.getReadableDatabase();
String id = null; String id = null;
switch (mMatcher.match(uri)) { switch (mMatcher.match(uri)) {
case URI_NOTE: // 查询笔记集合 case URI_NOTE:
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder); sortOrder);
break; break;
case URI_NOTE_ITEM:
case URI_NOTE_ITEM: // 查询单个笔记
id = uri.getPathSegments().get(1); id = uri.getPathSegments().get(1);
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder); + parseSelection(selection), selectionArgs, null, null, sortOrder);
break; break;
case URI_DATA: // 查询笔记数据集合 case URI_DATA:
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder); sortOrder);
break; break;
case URI_DATA_ITEM: // 查询单个笔记数据 case URI_DATA_ITEM:
id = uri.getPathSegments().get(1); id = uri.getPathSegments().get(1);
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder); + parseSelection(selection), selectionArgs, null, null, sortOrder);
break; break;
case URI_SEARCH: // 搜索请求 case URI_TRASH:
case URI_SEARCH_SUGGEST: // 搜索建议请求 c = db.query(TABLE.TRASH, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_TRASH_ITEM:
id = uri.getPathSegments().get(1);
c = db.query(TABLE.TRASH, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
if (sortOrder != null || projection != null) { if (sortOrder != null || projection != null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
@ -183,27 +155,20 @@ public class NotesProvider extends ContentProvider {
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
} }
if (c != null) { if (c != null) {
// 通知游标所关联的 URI用于后续数据变更时自动刷新
c.setNotificationUri(getContext().getContentResolver(), uri); c.setNotificationUri(getContext().getContentResolver(), uri);
} }
return c; return c;
} }
/**
* -
* @param uri URI
* @param values
* @return URI
*/
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase(); SQLiteDatabase db = mHelper.getWritableDatabase();
long dataId = 0, noteId = 0, insertedId = 0; long dataId = 0, noteId = 0, insertedId = 0;
switch (mMatcher.match(uri)) { switch (mMatcher.match(uri)) {
case URI_NOTE: // 插入笔记 case URI_NOTE:
insertedId = noteId = db.insert(TABLE.NOTE, null, values); insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break; break;
case URI_DATA: // 插入笔记数据 case URI_DATA:
if (values.containsKey(DataColumns.NOTE_ID)) { if (values.containsKey(DataColumns.NOTE_ID)) {
noteId = values.getAsLong(DataColumns.NOTE_ID); noteId = values.getAsLong(DataColumns.NOTE_ID);
} else { } else {
@ -211,31 +176,32 @@ public class NotesProvider extends ContentProvider {
} }
insertedId = dataId = db.insert(TABLE.DATA, null, values); insertedId = dataId = db.insert(TABLE.DATA, null, values);
break; break;
case URI_TRASH:
insertedId = noteId = db.insert(TABLE.TRASH, null, values);
break;
default: default:
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
} }
// 通知笔记 URI 变化noteId>0 时) // Notify the note uri
if (noteId > 0) { if (noteId > 0 && mMatcher.match(uri) != URI_TRASH) {
getContext().getContentResolver().notifyChange( getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
} }
// 通知数据 URI 变化dataId>0 时) // Notify the data uri
if (dataId > 0) { if (dataId > 0) {
getContext().getContentResolver().notifyChange( getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
} }
// Notify the trash uri
if (mMatcher.match(uri) == URI_TRASH) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_TRASH_URI, null);
}
return ContentUris.withAppendedId(uri, insertedId); return ContentUris.withAppendedId(uri, insertedId);
} }
/**
* -
* @param uri URI
* @param selection
* @param selectionArgs
* @return
*/
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0; int count = 0;
@ -252,7 +218,6 @@ public class NotesProvider extends ContentProvider {
/** /**
* ID that smaller than 0 is system folder which is not allowed to * ID that smaller than 0 is system folder which is not allowed to
* trash * trash
* ID0
*/ */
long noteId = Long.valueOf(id); long noteId = Long.valueOf(id);
if (noteId <= 0) { if (noteId <= 0) {
@ -271,50 +236,60 @@ public class NotesProvider extends ContentProvider {
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true; deleteData = true;
break; break;
case URI_TRASH:
if (TextUtils.isEmpty(selection)) {
selection = NoteColumns.ID + ">0";
} else {
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
}
count = db.delete(TABLE.TRASH, selection, selectionArgs);
break;
case URI_TRASH_ITEM:
id = uri.getPathSegments().get(1);
long trashId = Long.valueOf(id);
if (trashId <= 0) {
break;
}
count = db.delete(TABLE.TRASH,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
default: default:
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
} }
if (count > 0) { if (count > 0) {
if (deleteData) { if (deleteData) {
// 数据行删除会影响笔记摘要,通知笔记 URI
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
} }
if (mMatcher.match(uri) == URI_TRASH || mMatcher.match(uri) == URI_TRASH_ITEM) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_TRASH_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null); getContext().getContentResolver().notifyChange(uri, null);
} }
return count; return count;
} }
/**
*
* @param uri URI
* @param values
* @param selection
* @param selectionArgs
* @return
*/
@Override @Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0; int count = 0;
String id = null; String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase(); SQLiteDatabase db = mHelper.getWritableDatabase();
boolean updateData = false; // 是否更新了数据内容 boolean updateData = false;
switch (mMatcher.match(uri)) { switch (mMatcher.match(uri)) {
case URI_NOTE: // 更新所有笔记 case URI_NOTE:
increaseNoteVersion(-1, selection, selectionArgs); // 增加笔记版本号 increaseNoteVersion(-1, selection, selectionArgs);
count = db.update(TABLE.NOTE, values, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break; break;
case URI_NOTE_ITEM: // 更新单个笔记 case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1); id = uri.getPathSegments().get(1);
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); // 增加笔记版本号 increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs); + parseSelection(selection), selectionArgs);
break; break;
case URI_DATA: // 更新所有笔记数据 case URI_DATA:
count = db.update(TABLE.DATA, values, selection, selectionArgs); count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true; updateData = true;
break; break;
case URI_DATA_ITEM: // 更新单个笔记数据 case URI_DATA_ITEM:
id = uri.getPathSegments().get(1); id = uri.getPathSegments().get(1);
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs); + parseSelection(selection), selectionArgs);
@ -326,32 +301,17 @@ public class NotesProvider extends ContentProvider {
if (count > 0) { if (count > 0) {
if (updateData) { if (updateData) {
// 数据行更新影响笔记摘要,通知笔记 URI
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
} }
// 通知URI的观察者数据已更改
getContext().getContentResolver().notifyChange(uri, null); getContext().getContentResolver().notifyChange(uri, null);
} }
return count; return count;
} }
/**
* WHERE selection AND (...)
*
* @param selection
* @return
*/
private String parseSelection(String selection) { private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : ""); return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
} }
/**
*
*
* @param id ID0ID使 selection
* @param selection
* @param selectionArgs
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120); StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE "); sql.append("UPDATE ");
@ -377,11 +337,6 @@ public class NotesProvider extends ContentProvider {
mHelper.getWritableDatabase().execSQL(sql.toString()); mHelper.getWritableDatabase().execSQL(sql.toString());
} }
/**
* URIMIME
* @param uri URI
* @return MIME - null
*/
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
// TODO Auto-generated method stub // TODO Auto-generated method stub

@ -29,75 +29,95 @@ import android.util.Log;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote; import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TrashColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
/**
*
* <p></p>
* <p>使// note data </p>
*/
/**
* -
*
*/
public class DataUtils { public class DataUtils {
/**
*
*/
public static final String TAG = "DataUtils"; public static final String TAG = "DataUtils";
/** /**
* * Move notes to trash table instead of directly deleting them
*
* @param resolver ContentResolver
* @param ids ID
* @return truefalse
*/ */
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) { public static boolean batchMoveToTrash(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) { if (ids == null || ids.size() == 0) {
Log.d(TAG, "the ids is null"); Log.d(TAG, "the ids is null or empty");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true; return true;
} }
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); Cursor cursor = null;
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try { try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); for (long id : ids) {
if (results == null || results.length == 0 || results[0] == null) { if(id == Notes.ID_ROOT_FOLDER || id <= 0) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString()); Log.e(TAG, "Don't move system folder root or invalid id: " + id);
return false; continue;
}
// Query the note from note table
cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id),
null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
// Read all columns from the note
ContentValues trashValues = new ContentValues();
// Copy all columns from note to trash
for (String column : cursor.getColumnNames()) {
int columnIndex = cursor.getColumnIndex(column);
if (columnIndex >= 0) {
int type = cursor.getType(columnIndex);
switch (type) {
case android.database.Cursor.FIELD_TYPE_NULL:
break;
case android.database.Cursor.FIELD_TYPE_INTEGER:
trashValues.put(column, cursor.getLong(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_FLOAT:
trashValues.put(column, cursor.getDouble(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_STRING:
trashValues.put(column, cursor.getString(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_BLOB:
trashValues.put(column, cursor.getBlob(columnIndex));
break;
}
}
}
// Set deleted_date to current time
trashValues.put("deleted_date", System.currentTimeMillis());
// Insert into trash table
resolver.insert(Notes.CONTENT_TRASH_URI, trashValues);
// Delete from note table
resolver.delete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), null, null);
}
if (cursor != null) {
cursor.close();
cursor = null;
}
} }
return true; return true;
} catch (RemoteException e) { } catch (Exception e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); Log.e(TAG, String.format("Move to trash failed: %s", e.toString()));
} catch (OperationApplicationException e) { return false;
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } finally {
if (cursor != null) {
cursor.close();
}
} }
return false;
} }
/** public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
* // Instead of directly deleting, move to trash
* @param resolver return batchMoveToTrash(resolver, ids);
* @param id ID }
* @param srcFolderId ID
* @param desFolderId ID
*/
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId); values.put(NoteColumns.PARENT_ID, desFolderId);
@ -106,13 +126,6 @@ public class DataUtils {
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
} }
/**
*
* @param resolver
* @param ids ID
* @param folderId ID
* @return
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) { long folderId) {
if (ids == null) { if (ids == null) {
@ -145,9 +158,7 @@ public class DataUtils {
} }
/** /**
* * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
* @param resolver
* @return
*/ */
public static int getUserFolderCount(ContentResolver resolver) { public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
@ -171,13 +182,6 @@ public class DataUtils {
return count; return count;
} }
/**
* ID
* @param resolver
* @param noteId ID
* @param type
* @return
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null,
@ -195,12 +199,6 @@ public class DataUtils {
return exist; return exist;
} }
/**
* ID
* @param resolver
* @param noteId ID
* @return
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null); null, null, null, null);
@ -215,12 +213,6 @@ public class DataUtils {
return exist; return exist;
} }
/**
* ID
* @param resolver
* @param dataId ID
* @return
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null); null, null, null, null);
@ -235,12 +227,6 @@ public class DataUtils {
return exist; return exist;
} }
/**
*
* @param resolver
* @param name
* @return
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
@ -257,12 +243,6 @@ public class DataUtils {
return exist; return exist;
} }
/**
*
* @param resolver
* @param folderId ID
* @return
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) { public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
@ -290,13 +270,6 @@ public class DataUtils {
return set; return set;
} }
/**
* ID
*
* @param resolver
* @param noteId ID
* @return
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER }, new String [] { CallNote.PHONE_NUMBER },
@ -317,13 +290,93 @@ public class DataUtils {
} }
/** /**
* ID * Restore note from trash table back to note table
*
* @param resolver
* @param phoneNumber
* @param callDate
* @return ID
*/ */
public static boolean restoreNoteFromTrash(ContentResolver resolver, long trashId) {
Cursor cursor = null;
try {
// Query the note from trash table
cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_TRASH_URI, trashId),
null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
// Read all columns from the trash note
ContentValues noteValues = new ContentValues();
// Copy all columns from trash to note (except deleted_date)
for (String column : cursor.getColumnNames()) {
if ("deleted_date".equals(column)) {
continue; // Skip deleted_date column
}
int columnIndex = cursor.getColumnIndex(column);
if (columnIndex >= 0) {
int type = cursor.getType(columnIndex);
switch (type) {
case android.database.Cursor.FIELD_TYPE_NULL:
break;
case android.database.Cursor.FIELD_TYPE_INTEGER:
noteValues.put(column, cursor.getLong(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_FLOAT:
noteValues.put(column, cursor.getDouble(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_STRING:
noteValues.put(column, cursor.getString(columnIndex));
break;
case android.database.Cursor.FIELD_TYPE_BLOB:
noteValues.put(column, cursor.getBlob(columnIndex));
break;
}
}
}
// Insert back to note table
resolver.insert(Notes.CONTENT_NOTE_URI, noteValues);
// Delete from trash table
resolver.delete(ContentUris.withAppendedId(Notes.CONTENT_TRASH_URI, trashId), null, null);
return true;
}
return false;
} catch (Exception e) {
Log.e(TAG, String.format("Restore from trash failed: %s", e.toString()));
return false;
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Permanently delete note from trash table
*/
public static boolean permanentlyDeleteFromTrash(ContentResolver resolver, long trashId) {
try {
int count = resolver.delete(ContentUris.withAppendedId(Notes.CONTENT_TRASH_URI, trashId), null, null);
return count > 0;
} catch (Exception e) {
Log.e(TAG, String.format("Permanently delete from trash failed: %s", e.toString()));
return false;
}
}
/**
* Clear all notes from trash table
*/
public static boolean clearAllTrash(ContentResolver resolver) {
try {
int count = resolver.delete(Notes.CONTENT_TRASH_URI, null, null);
Log.d(TAG, "Cleared " + count + " notes from trash");
// Return true even if count is 0, as it might mean the trash was already empty
return true;
} catch (Exception e) {
Log.e(TAG, String.format("Clear all trash failed: %s", e.toString()));
return false;
}
}
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID }, new String [] { CallNote.NOTE_ID },
@ -345,13 +398,6 @@ public class DataUtils {
return 0; return 0;
} }
/**
* ID
* @param resolver
* @param noteId ID
* @return
* @throws IllegalArgumentException
*/
public static String getSnippetById(ContentResolver resolver, long noteId) { public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET }, new String [] { NoteColumns.SNIPPET },
@ -370,12 +416,6 @@ public class DataUtils {
throw new IllegalArgumentException("Note is not found with id: " + noteId); throw new IllegalArgumentException("Note is not found with id: " + noteId);
} }
/**
*
*
* @param snippet
* @return
*/
public static String getFormattedSnippet(String snippet) { public static String getFormattedSnippet(String snippet) {
if (snippet != null) { if (snippet != null) {
snippet = snippet.trim(); snippet = snippet.trim();

@ -78,90 +78,85 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.HashSet; import java.util.HashSet;
/**
* -
*
*
*/
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
// 异步查询处理的标记常量 private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; // 查询笔记列表的标记
private static final int FOLDER_LIST_QUERY_TOKEN = 1; // 查询文件夹列表的标记 private static final int FOLDER_LIST_QUERY_TOKEN = 1;
private static final int MENU_FOLDER_DELETE = 0;
// 文件夹上下文菜单的菜单项ID private static final int MENU_FOLDER_VIEW = 1;
private static final int MENU_FOLDER_DELETE = 0; // 删除文件夹
private static final int MENU_FOLDER_VIEW = 1; // 查看文件夹 private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final int MENU_FOLDER_CHANGE_NAME = 2; // 修改文件夹名称
// 用于存储应用介绍是否已添加的偏好设置键
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
/**
*
* NOTE_LIST SUB_FOLDER CALL_RECORD_FOLDER
*/
private enum ListEditState { private enum ListEditState {
NOTE_LIST, // 笔记列表状态 NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
SUB_FOLDER, // 子文件夹状态
CALL_RECORD_FOLDER // 通话记录文件夹状态
}; };
// 成员变量 private ListEditState mState;
private ListEditState mState; // 当前列表编辑状态
private BackgroundQueryHandler mBackgroundQueryHandler; // 异步查询处理器 private BackgroundQueryHandler mBackgroundQueryHandler;
private NotesListAdapter mNotesListAdapter; // 笔记列表适配器
private ListView mNotesListView; // 笔记列表视图 private NotesListAdapter mNotesListAdapter;
private Button mAddNewNote; // 新建笔记按钮
private boolean mDispatch; // 是否分发触摸事件给列表视图 private ListView mNotesListView;
private int mOriginY; // 原始触摸Y坐标
private int mDispatchY; // 分发触摸Y坐标 private Button mAddNewNote;
private TextView mTitleBar; // 标题栏
private long mCurrentFolderId; // 当前文件夹ID private boolean mDispatch;
private ContentResolver mContentResolver; // 内容解析器
private ModeCallback mModeCallBack; // 多选模式回调 private int mOriginY;
private static final String TAG = "NotesListActivity"; // 日志标签 private int mDispatchY;
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;// 笔记列表滚动速率
private TextView mTitleBar;
private NoteItemData mFocusNoteDataItem; // 当前焦点的笔记数据项
private long mCurrentFolderId;
// 查询条件常量
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; // 普通文件夹查询条件 private ContentResolver mContentResolver;
private ModeCallback mModeCallBack;
private static final String TAG = "NotesListActivity";
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
private NoteItemData mFocusNoteDataItem;
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)"; // 根文件夹查询条件(显示非系统文件夹和非空通话记录文件夹) + NoteColumns.NOTES_COUNT + ">0)";
// 请求码常量 private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_OPEN_NODE = 102; // 打开笔记的请求码 private final static int REQUEST_CODE_NEW_NODE = 103;
private final static int REQUEST_CODE_NEW_NODE = 103; // 新建笔记的请求码
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.note_list); // 设置布局 setContentView(R.layout.note_list);
initResources(); // 初始化资源 initResources();
// 首次进入时插入“介绍”笔记 /**
setAppInfoFromRawRes(); // 设置应用信息(首次使用时添加介绍笔记) * Insert an introduction when user firstly use this application
*/
setAppInfoFromRawRes();
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 处理从NoteEditActivity返回的结果
if (resultCode == RESULT_OK if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
// 如果是打开或新建笔记的请求返回成功,刷新列表
mNotesListAdapter.changeCursor(null); mNotesListAdapter.changeCursor(null);
} else { } else {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
} }
} }
/**
*
* 使
*/
private void setAppInfoFromRawRes() { private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
@ -208,20 +203,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
*
*/
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
startAsyncNotesListQuery(); startAsyncNotesListQuery();
} }
/**
*
*
*/
private void initResources() { private void initResources() {
mContentResolver = this.getContentResolver(); mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
@ -244,19 +231,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback(); mModeCallBack = new ModeCallback();
} }
/**
* -
*
*/
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu; private DropdownMenu mDropDownMenu;
private ActionMode mActionMode; private ActionMode mActionMode;
private MenuItem mMoveMenu; private MenuItem mMoveMenu;
/**
*
*
*/
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
getMenuInflater().inflate(R.menu.note_list_options, menu); getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this); menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
@ -290,10 +269,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true; return true;
} }
/**
*
*
*/
private void updateMenu() { private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount(); int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu // Update dropdown menu
@ -321,37 +296,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return false; return false;
} }
/**
*
*
*/
public void onDestroyActionMode(ActionMode mode) { public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false); mNotesListAdapter.setChoiceMode(false);
mNotesListView.setLongClickable(true); mNotesListView.setLongClickable(true);
mAddNewNote.setVisibility(View.VISIBLE); mAddNewNote.setVisibility(View.VISIBLE);
} }
/**
*
*/
public void finishActionMode() { public void finishActionMode() {
mActionMode.finish(); mActionMode.finish();
} }
/**
*
*
*/
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) { boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked); mNotesListAdapter.setCheckedItem(position, checked);
updateMenu(); updateMenu();
} }
/**
*
*
*/
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) { if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
@ -361,7 +321,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.delete: case R.id.delete:
// 显示删除确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete)); builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setIcon(android.R.drawable.ic_dialog_alert);
@ -371,14 +330,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int which) { int which) {
batchDelete(); // 执行批量删除 batchDelete();
} }
}); });
builder.setNegativeButton(android.R.string.cancel, null); builder.setNegativeButton(android.R.string.cancel, null);
builder.show(); builder.show();
break; break;
case R.id.move: case R.id.move:
// 查询目标文件夹列表
startQueryDestinationFolders(); startQueryDestinationFolders();
break; break;
default: default:
@ -388,10 +346,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
*
*/
private class NewNoteOnTouchListener implements OnTouchListener { private class NewNoteOnTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
@ -402,13 +356,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
int newNoteViewHeight = mAddNewNote.getHeight(); int newNoteViewHeight = mAddNewNote.getHeight();
int start = screenHeight - newNoteViewHeight; int start = screenHeight - newNoteViewHeight;
int eventY = start + (int) event.getY(); int eventY = start + (int) event.getY();
// 子文件夹状态需减去标题栏高度 /**
* Minus TitleBar's height
*/
if (mState == ListEditState.SUB_FOLDER) { if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight(); eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight(); start -= mTitleBar.getHeight();
} }
// HACKME: 点击“新建”按钮透明区时,将事件派发给下方列表。 /**
// 透明区近似线性公式 y = -0.12x + 94px背景改动需同步更新该公式。 * HACKME:When click the transparent part of "New Note" button, dispatch
* the event to the list view behind this button. The transparent part of
* "New Note" button could be expressed by formula y=-0.12x+94Unit:pixel
* and the line top of the button. The coordinate based on left of the "New
* Note" button. The 94 represents maximum height of the transparent part.
* Notice that, if the background of the button changes, the formula should
* also change. This is very bad, just for the UI designer's strong requirement.
*/
if (event.getY() < (event.getX() * (-0.12) + 94)) { if (event.getY() < (event.getX() * (-0.12) + 94)) {
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount()); - mNotesListView.getFooterViewsCount());
@ -445,10 +408,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}; };
/**
*
* ID
*/
private void startAsyncNotesListQuery() { private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION; : NORMAL_SELECTION;
@ -458,27 +417,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
} }
/**
* -
*/
private final class BackgroundQueryHandler extends AsyncQueryHandler { private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) { public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver); super(contentResolver);
} }
/**
*
*
*/
@Override @Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) { protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) { switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN: case FOLDER_NOTE_LIST_QUERY_TOKEN:
// 查询笔记列表完成,更新适配器
mNotesListAdapter.changeCursor(cursor); mNotesListAdapter.changeCursor(cursor);
break; break;
case FOLDER_LIST_QUERY_TOKEN: case FOLDER_LIST_QUERY_TOKEN:
// 查询文件夹列表完成,显示文件夹菜单
if (cursor != null && cursor.getCount() > 0) { if (cursor != null && cursor.getCount() > 0) {
showFolderListMenu(cursor); showFolderListMenu(cursor);
} else { } else {
@ -491,10 +441,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
*
*/
private void showFolderListMenu(Cursor cursor) { private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(R.string.menu_title_select_folder); builder.setTitle(R.string.menu_title_select_folder);
@ -516,10 +462,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
builder.show(); builder.show();
} }
/**
*
*
*/
private void createNewNote() { private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class); Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT); intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
@ -527,10 +469,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
} }
/**
*
*
*/
private void batchDelete() { private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() { new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) { protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
@ -568,10 +506,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute(); }.execute();
} }
/**
*
*
*/
private void deleteFolder(long folderId) { private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) { if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId); Log.e(TAG, "Wrong folder id, should not happen " + folderId);
@ -599,10 +533,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
* /
*/
private void openNode(NoteItemData data) { private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class); Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW); intent.setAction(Intent.ACTION_VIEW);
@ -610,10 +540,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
} }
/**
*
* ID
*/
private void openFolder(NoteItemData data) { private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId(); mCurrentFolderId = data.getId();
startAsyncNotesListQuery(); startAsyncNotesListQuery();
@ -631,10 +557,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mTitleBar.setVisibility(View.VISIBLE); mTitleBar.setVisibility(View.VISIBLE);
} }
/**
*
*
*/
public void onClick(View v) { public void onClick(View v) {
switch (v.getId()) { switch (v.getId()) {
case R.id.btn_new_note: case R.id.btn_new_note:
@ -645,9 +567,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
*/
private void showSoftInput() { private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) { if (inputMethodManager != null) {
@ -655,18 +574,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
*/
private void hideSoftInput(View view) { private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
} }
/**
*
* @param create
*/
private void showCreateOrModifyFolderDialog(final boolean create) { private void showCreateOrModifyFolderDialog(final boolean create) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
@ -752,10 +664,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}); });
} }
/**
*
* 退
*/
@Override @Override
public void onBackPressed() { public void onBackPressed() {
switch (mState) { switch (mState) {
@ -780,10 +688,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
*
* 广
*/
private void updateWidget(int appWidgetId, int appWidgetType) { private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) { if (appWidgetType == Notes.TYPE_WIDGET_2X) {
@ -803,10 +707,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
setResult(RESULT_OK, intent); setResult(RESULT_OK, intent);
} }
/**
*
*
*/
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) { if (mFocusNoteDataItem != null) {
@ -818,10 +718,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
}; };
/**
*
*
*/
@Override @Override
public void onContextMenuClosed(Menu menu) { public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) { if (mNotesListView != null) {
@ -830,10 +726,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
super.onContextMenuClosed(menu); super.onContextMenuClosed(menu);
} }
/**
*
*
*/
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) { if (mFocusNoteDataItem == null) {
@ -868,10 +760,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true; return true;
} }
/**
*
*
*/
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear(); menu.clear();
@ -890,10 +778,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true; return true;
} }
/**
*
*
*/
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
@ -928,6 +812,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
case R.id.menu_search: case R.id.menu_search:
onSearchRequested(); onSearchRequested();
break; break;
case R.id.menu_trash: {
Intent intent = new Intent(this, TrashActivity.class);
startActivity(intent);
break;
}
default: default:
break; break;
} }

@ -0,0 +1,203 @@
/*
* 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.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
public class TrashActivity extends Activity implements OnClickListener {
private static final String TAG = "TrashActivity";
private static final int TRASH_LIST_QUERY_TOKEN = 0;
private Button mReturnButton;
private Button mCleanAllButton;
private ListView mTrashListView;
private NotesListAdapter mTrashListAdapter;
private ContentResolver mContentResolver;
private BackgroundQueryHandler mBackgroundQueryHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trash);
initResources();
}
@Override
protected void onStart() {
super.onStart();
startAsyncTrashListQuery();
}
private void initResources() {
mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(mContentResolver);
mReturnButton = (Button) findViewById(R.id.btn_return);
if (mReturnButton != null) {
mReturnButton.setOnClickListener(this);
}
mCleanAllButton = (Button) findViewById(R.id.btn_clean_all);
if (mCleanAllButton != null) {
mCleanAllButton.setOnClickListener(this);
}
mTrashListView = (ListView) findViewById(R.id.trash_list);
if (mTrashListView != null) {
mTrashListAdapter = new NotesListAdapter(this);
mTrashListView.setAdapter(mTrashListAdapter);
mTrashListView.setOnItemClickListener(new OnTrashItemClickListener());
}
}
private void startAsyncTrashListQuery() {
mBackgroundQueryHandler.startQuery(TRASH_LIST_QUERY_TOKEN, null,
Notes.CONTENT_TRASH_URI, NoteItemData.PROJECTION, null, null,
NoteColumns.MODIFIED_DATE + " DESC");
}
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case TRASH_LIST_QUERY_TOKEN:
mTrashListAdapter.changeCursor(cursor);
break;
default:
break;
}
}
}
private class OnTrashItemClickListener implements OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) {
NoteItemData item = ((NotesListItem) view).getItemData();
showTrashItemDialog(item);
}
}
}
private void showTrashItemDialog(final NoteItemData item) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(item.getSnippet());
builder.setItems(new String[] {
getString(R.string.menu_delete),
getString(R.string.button_recovery)
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0: // Delete - 彻底删除
permanentlyDeleteNote(item.getId());
break;
case 1: // Recovery - 恢复
recoverNote(item.getId());
break;
}
}
});
builder.show();
}
private void permanentlyDeleteNote(long noteId) {
if (DataUtils.permanentlyDeleteFromTrash(mContentResolver, noteId)) {
Toast.makeText(this, getString(R.string.menu_delete) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list
} else {
Toast.makeText(this, getString(R.string.menu_delete) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
private void recoverNote(long noteId) {
if (DataUtils.restoreNoteFromTrash(mContentResolver, noteId)) {
Toast.makeText(this, getString(R.string.button_recovery) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list
} else {
Toast.makeText(this, getString(R.string.button_recovery) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
private void showClearAllTrashDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.button_clean_all));
builder.setMessage(getString(R.string.alert_message_clear_all_trash));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
clearAllTrash();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
private void clearAllTrash() {
if (DataUtils.clearAllTrash(mContentResolver)) {
Toast.makeText(this, getString(R.string.button_clean_all) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list to clear UI
} else {
Toast.makeText(this, getString(R.string.button_clean_all) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_return:
finish();
break;
case R.id.btn_clean_all:
showClearAllTrashDialog();
break;
default:
break;
}
}
}
Loading…
Cancel
Save