Merge branch 'master' of https://bdgit.educoder.net/p82feo7wg/MiNoteRead into wangjiaqi_branch

# Conflicts:
#	src/Notes-master/res/menu/note_list.xml
#	src/Notes-master/src/net/micode/notes/data/Notes.java
#	src/Notes-master/src/net/micode/notes/model/WorkingNote.java
#	src/Notes-master/src/net/micode/notes/ui/NoteItemData.java
#	src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
pull/15/head
Surponess 2 months ago
commit a4c169eb3a

@ -1,27 +1,20 @@
<?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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.micode.notes"
android:versionCode="1"
android:versionName="0.1" >
<uses-sdk android:minSdkVersion="14" />
<!-- 已移除package属性已注释minSdkVersion可选删除 -->
<!-- <uses-sdk android:minSdkVersion="14" /> -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<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.GET_ACCOUNTS" />
<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
android:icon="@drawable/icon_app"
android:label="@string/app_name" >
@ -42,107 +34,109 @@
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan" >
android:windowSoftInputMode="adjustPan"
android:exported="true" > <!-- 新增 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.NoteEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/NoteTheme" >
android:theme="@style/NoteTheme"
android:exported="true" > <!-- 新增 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
<provider
android:name="net.micode.notes.data.NotesProvider"
android:authorities="micode_notes"
android:multiprocess="true" />
<receiver
android:name=".widget.NoteWidgetProvider_2x"
android:label="@string/app_widget2x2" >
android:label="@string/app_widget2x2"
android:exported="false" > <!-- 新增 -->
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_2x_info" />
</receiver>
<receiver
android:name=".widget.NoteWidgetProvider_4x"
android:label="@string/app_widget4x4" >
android:label="@string/app_widget4x4"
android:exported="false" > <!-- 新增 -->
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_4x_info" />
</receiver>
<receiver android:name=".ui.AlarmInitReceiver" >
<receiver
android:name=".ui.AlarmInitReceiver"
android:exported="true" > <!-- 新增 -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name="net.micode.notes.ui.AlarmReceiver"
android:process=":remote" >
</receiver>
<activity
android:name=".ui.AlarmAlertActivity"
android:label="@string/app_name"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
</activity>
<activity
android:name=".ui.TrashActivity"
android:label="@string/menu_trash"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<activity
android:name=".ui.PasswordInputActivity"
android:label="@string/password_input_title"
android:theme="@style/NoteTheme"
android:exported="false" >
</activity>
<service
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" >
</service>
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.NoteEditActivity" />

@ -0,0 +1,59 @@
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/password_input_title"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="24dp" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password_hint"
android:inputType="textPassword"
android:layout_marginBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<Button
android:id="@+id/btn_return"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/return_button"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/confirm_button" />
</LinearLayout>
</LinearLayout>

@ -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>

@ -101,4 +101,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"/>
<ImageView
android:id="@+id/iv_encrypted_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:layout_marginEnd="24dp"
android:visibility="gone"/>
</FrameLayout>

@ -32,4 +32,7 @@
<item
android:id="@+id/menu_setting"
android:title="@string/menu_setting" />
<item
android:id="@+id/menu_trash"
android:title="@string/menu_trash"/>
</menu>

@ -42,7 +42,12 @@
<string name="menu_sync_cancel">取消同步</string>
<string name="menu_setting">设置</string>
<string name="menu_search">搜索</string>
<string name="menu_trash">回收站</string>
<string name="button_return">返回</string>
<string name="menu_delete">删除</string>
<string name="button_clean_all">清空</string>
<string name="button_recovery">恢复</string>
<string name="menu_success">成功</string>
<string name="menu_move">移动到文件夹</string>
<string name="menu_select_title">选中了 %d 项</string>
<string name="menu_select_none">没有选中项,操作无效</string>
@ -70,6 +75,7 @@
<string name="alert_message_delete_notes">确认要删除所选的 %d 条便签吗?</string>
<string name="alert_message_delete_note">确认要删除该条便签吗?</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>
<!-- export text -->
<string name="error_sdcard_unmounted">SD卡被占用不能操作</string>
@ -119,6 +125,17 @@
<string name="search">便签</string>
<string name="datetime_dialog_ok">设置</string>
<string name="datetime_dialog_cancel">取消</string>
<!-- Password protection -->
<string name="password_input_title">请输入密码</string>
<string name="password_hint">密码</string>
<string name="return_button">返回</string>
<string name="confirm_button">确认</string>
<string name="encrypt_note_dialog_title">加密便签</string>
<string name="encrypt_note_dialog_message">是否将此便签设置为加密便签?</string>
<string name="password_set_dialog_title">设置密码</string>
<string name="password_set_dialog_message">请输入密码</string>
<string name="password_error">密码错误,请重试</string>
<string name="password_empty">密码不能为空</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item>
</plurals>

@ -48,6 +48,11 @@
<string name="menu_search">Search</string>
<string name="menu_delete">Delete</string>
<string name="menu_move">Move to folder</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_select_title">%d selected</string>
<string name="menu_select_none">Nothing selected, the operation is invalid</string>
<string name="menu_select_all">Select all</string>
@ -72,6 +77,7 @@
<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_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_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>
@ -126,6 +132,17 @@
<string name="search">Notes</string>
<string name="datetime_dialog_ok">set</string>
<string name="datetime_dialog_cancel">cancel</string>
<!-- Password protection -->
<string name="password_input_title">Enter your password please</string>
<string name="password_hint">password</string>
<string name="return_button">Return</string>
<string name="confirm_button">Confirm</string>
<string name="encrypt_note_dialog_title">Encrypted note</string>
<string name="encrypt_note_dialog_message">Would you like to set the note as an encrypted note?</string>
<string name="password_set_dialog_title">Set password</string>
<string name="password_set_dialog_message">Enter password please</string>
<string name="password_error">Error, try again</string>
<string name="password_empty">The password can not be empty</string>
<plurals name="search_results_title">
<item quantity="one"><xliff:g id="number" example="1">%1$s</xliff:g> result for \"<xliff:g id="search" example="???">%2$s</xliff:g>\"</item>
<!-- Case of 0 or 2 or more results. -->

@ -59,10 +59,14 @@
</style>
<style name="NoteTheme" parent="@android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">@style/NoteActionBarStyle</item>
<item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
<item name="android:displayOptions">showHome|showTitle|useLogo</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
</style>
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid">
<item name="android:visibility">visible</item>
<item name="android:displayOptions" />
<item name="android:visibility">gone</item>
</style>
</resources>

@ -19,44 +19,51 @@ package net.micode.notes.data;
import android.net.Uri;
/**
*
*
*
*
*
*
*/
public class Notes {
// ContentProvider的授权标识
// ContentProvider的授权标识
public static final String AUTHORITY = "micode_notes";
// 日志标签
// 日志标签
public static final String TAG = "Notes";
// 0类型普通笔记
// 0类型普通笔记
public static final int TYPE_NOTE = 0;
// 1类型文件夹
// 1类型文件夹
public static final int TYPE_FOLDER = 1;
// 2类型系统文件夹
// 2类型系统文件夹
public static final int TYPE_SYSTEM = 2;
// 3类型清单
public static final int TYPE_CHECKLIST = 3;
// 系统文件夹的 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; // 回收站文件夹
// 系统文件夹的 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
* 1.
* 2. ID
* 3. ID
* 4.
* 5. ID
* 6.
* 7.
* Intent Extra Android Intent
* 1.
* 2. ID
* 3. ID
* 4.
* 5. ID
* 6.
* Following IDs are system folders' identifiers
* {@link Notes#ID_ROOT_FOLDER } is default folder
* {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder
* {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records
* 7.
*/
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_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
@ -65,176 +72,262 @@ public class Notes {
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
public static final String INTENT_EXTRA_NOTE_TYPE = "net.micode.notes.note_type";
// 无效的小部件类型 -1
public static final int TYPE_WIDGET_INVALIDE = -1;
// 2x2小部件类型 0
public static final int TYPE_WIDGET_2X = 0;
// 4x4小部件类型 1
public static final int TYPE_WIDGET_4X = 1;
/**
*
*/
public static class DataConstants {
// 普通笔记的MIME类型
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
// 通话记录笔记的MIME类型
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");
// 查询数据表的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 interface NoteColumns {
public static final Uri CONTENT_TRASH_URI = Uri.parse("content://" + AUTHORITY + "/trash");
// 行的唯一ID
public interface NoteColumns {
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
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";
// 笔记或文件夹的创建日期
/**
* Created data for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String CREATED_DATE = "created_date";
// 最后修改日期
/**
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static final String MODIFIED_DATE = "modified_date";
// 提醒日期
/**
* Alert date
* <P> Type: INTEGER (long) </P>
*/
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";
// 笔记的小部件ID
/**
* Note's widget id
* <P> Type: INTEGER (long) </P>
*/
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";
// 笔记的背景颜色ID
/**
* Note's background color's id
* <P> Type: INTEGER (long) </P>
*/
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";
// 文件夹中的笔记数量
/**
* Folder's count of notes
* <P> Type: INTEGER (long) </P>
*/
public static final String NOTES_COUNT = "notes_count";
// 文件类型:文件夹或笔记
/**
* The file type: folder or note
* <P> Type: INTEGER </P>
*/
public static final String TYPE = "type";
// 最后一次同步的ID
/**
* The last sync id
* <P> Type: INTEGER (long) </P>
*/
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";
// 移动到临时文件夹之前的原始父ID
/**
* Original parent id before moving into temporary folder
* <P> Type : INTEGER </P>
*/
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";
// 版本号
/**
* The version code
* <P> Type : INTEGER (long) </P>
*/
public static final String VERSION = "version";
/**
* Whether the note is encrypted
* <P> Type : INTEGER (0 = not encrypted, 1 = encrypted) </P>
*/
public static final String IS_ENCRYPTED = "is_encrypted";
}
public interface TrashColumns extends NoteColumns {
/**
* Deleted date for trash note
* <P> Type: INTEGER (long) </P>
*/
public static final String DELETED_DATE = "deleted_date";
}
/**
* DataColumns - 便(data)
* 便
* 使MIME_TYPE便便
* DataColumns - 便(data)
* 便
* 使MIME_TYPE便便
*/
public interface DataColumns {
// 行的唯一ID
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
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";
// 该数据所属笔记的引用ID
/**
* The reference id to note that this data belongs to
* <P> Type: INTEGER (long) </P>
*/
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";
// 最后修改日期
/**
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static final String MODIFIED_DATE = "modified_date";
// 数据内容
/**
* Data's content
* <P> Type: TEXT </P>
*/
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";
// 通用数据列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";
// 通用数据列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";
// 通用数据列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";
// 通用数据列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";
}
/**
* TextNote - 便
* 便URI
*/
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 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";
// 文本笔记的单项MIME类型
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");
}
/**
* CallNote - 便
* 便URI
*/
public static final class CallNote implements DataColumns {
// 通话日期
/**
* Call date for this record
* <P> Type: INTEGER (long) </P>
*/
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 CONTACT_NAME = DATA4;
// 通话记录笔记的目录MIME类型
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";
// 通话记录笔记的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
}
}

@ -26,31 +26,25 @@ import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
/**
*
*
*/
public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db"; // 数据库名称
private static final int DB_VERSION = 4; // 数据库版本
;
/**
*
*/
private static final String DB_NAME = "note.db";
private static final int DB_VERSION = 6;
public interface TABLE {
/** 笔记表名 */
public static final String NOTE = "note";
/** 数据表名 */
public static final String DATA = "data";
}
private static final String TAG = "NotesDatabaseHelper"; // 日志标签
private static NotesDatabaseHelper mInstance; // 单例实例
public static final String TRASH = "trash_note";
public static final String ENCRYPTED_NOTE_PASSWORD = "encrypted_note_password";
}
/** 数据库帮助类的单例实例 */
private static final String TAG = "NotesDatabaseHelper";
private static NotesDatabaseHelper mInstance;
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
@ -70,10 +64,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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" +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0" +
")";
// 创建笔记数据表的SQL语句
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
@ -89,18 +83,54 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";
// 创建笔记数据表中note_id列的索引
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
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," +
NoteColumns.IS_ENCRYPTED + " INTEGER NOT NULL DEFAULT 0," +
"deleted_date INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)" +
")";
private static final String CREATE_ENCRYPTED_NOTE_PASSWORD_TABLE_SQL =
"CREATE TABLE " + TABLE.ENCRYPTED_NOTE_PASSWORD + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
"password TEXT NOT NULL," +
"FOREIGN KEY(" + NoteColumns.ID + ") REFERENCES " + TABLE.NOTE + "(" + NoteColumns.ID + ") ON DELETE CASCADE" +
")";
/**
*
* 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 =
"CREATE TRIGGER decrease_folder_count_on_update " +
@ -113,7 +143,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
*
* Increase folder's note count when insert new note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
@ -125,7 +155,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
*
* Decrease folder's note count when delete note from the folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
@ -137,7 +167,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" 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 =
"CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA +
@ -148,7 +180,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" 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 =
"CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA +
@ -159,7 +193,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" 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 =
"CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA +
@ -171,7 +207,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
*
* Delete datas belong to note which has been deleted
*/
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
@ -182,7 +218,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
*
* Delete notes belong to folder which has been deleted
*/
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
@ -193,7 +229,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
*
* Move notes belong to folder which has been moved to trash folder
*/
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
@ -205,31 +241,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
*
* @param context
*/
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
/**
*
* @param db
*/
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL); // 创建笔记表结构
reCreateNoteTableTriggers(db); // 重建笔记表触发器
createSystemFolder(db); // 创建系统文件夹
db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db);
createSystemFolder(db);
Log.d(TAG, "note table has been created");
}
/**
*
* @param db
*/
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
// 删除现有触发器
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_delete");
@ -238,7 +261,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
// 创建新触发器
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_DELETE_TRIGGER);
@ -248,171 +270,172 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}
/**
*
* @param db
*/
private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues();
/**
*
* call record foler for call notes
*/
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
// 创建根文件夹(默认文件夹)
/**
* root folder which is default folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
// 创建临时文件夹(用于移动笔记)
/**
* temporary folder which is used for moving note
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
// 创建回收站文件夹
/**
* create trash folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
/**
*
* @param db
*/
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL); // 创建笔记数据表结构
reCreateDataTableTriggers(db); // 重建笔记数据表触发器
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); // 创建note_id列索引
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
Log.d(TAG, "data table has been created");
}
/**
*
* @param db
*/
public void createTrashTable(SQLiteDatabase db) {
db.execSQL(CREATE_TRASH_TABLE_SQL);
Log.d(TAG, "trash_note table has been created");
}
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_update");
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_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
/**
* NotesDatabaseHelper
* @param context
* @return NotesDatabaseHelper
*/
static synchronized NotesDatabaseHelper getInstance(Context context) {
public static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
}
return mInstance;
}
/** 创建数据库:依次创建 note 表与 data 表 */
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db); // 创建笔记表
createDataTable(db); // 创建笔记数据表
createNoteTable(db);
createDataTable(db);
createTrashTable(db);
createEncryptedNotePasswordTable(db);
}
/**
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false; // 是否需要重建触发器
boolean skipV2 = false; // 是否跳过版本2的升级
boolean reCreateTriggers = false;
boolean skipV2 = false;
// 从版本1升级
if (oldVersion == 1) {
upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++;
}
// 从版本2升级跳过已在版本1升级中处理的情况
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db);
reCreateTriggers = true;
oldVersion++;
}
// 从版本3升级
if (oldVersion == 3) {
upgradeToV4(db);
oldVersion++;
}
// 如果需要,重建触发器
if (oldVersion == 4) {
upgradeToV5(db);
oldVersion++;
}
if (oldVersion == 5) {
upgradeToV6(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
}
// 检查升级是否成功
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) {
// 删除旧表
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
// 创建新表
createNoteTable(db);
createDataTable(db);
}
/**
* 3
* @param 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_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
// 添加gtask_id列用于同步
// add a column for gtask id
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''");
// 添加回收站系统文件夹
// add a trash system folder
ContentValues values = new ContentValues();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
/**
* 4
* @param db
*/
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
private void upgradeToV5(SQLiteDatabase db) {
createTrashTable(db);
Log.d(TAG, "Upgraded to version 5: created trash_note table");
}
private void upgradeToV6(SQLiteDatabase db) {
// 添加is_encrypted字段到note表
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.IS_ENCRYPTED
+ " INTEGER NOT NULL DEFAULT 0");
// 添加is_encrypted字段到trash_note表如果表已存在
try {
db.execSQL("ALTER TABLE " + TABLE.TRASH + " ADD COLUMN " + NoteColumns.IS_ENCRYPTED
+ " INTEGER NOT NULL DEFAULT 0");
} catch (Exception e) {
// 如果表不存在或字段已存在,忽略错误
Log.d(TAG, "trash_note table may not exist or column already exists: " + e.getMessage());
}
// 创建加密便签密码表
createEncryptedNotePasswordTable(db);
Log.d(TAG, "Upgraded to version 6: added is_encrypted column and encrypted_note_password table");
}
public void createEncryptedNotePasswordTable(SQLiteDatabase db) {
db.execSQL(CREATE_ENCRYPTED_NOTE_PASSWORD_TABLE_SQL);
Log.d(TAG, "encrypted_note_password table has been created");
}
}

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

@ -56,6 +56,7 @@ public class WorkingNote {
private static final String TAG = "WorkingNote"; // 日志标签
private boolean mIsDeleted; // 是否已删除
private NoteSettingChangedListener mNoteSettingStatusListener; // 笔记设置变化监听器
private String mPassword; // 密码(仅在创建时使用)
private int mNoteType; // 笔记类型(普通笔记/清单)
@ -243,6 +244,12 @@ public class WorkingNote {
mNote.syncNote(mContext, mNoteId);
// 如果设置了密码,保存密码
if (mPassword != null && !mPassword.isEmpty()) {
net.micode.notes.tool.PasswordUtils.savePassword(mContext, mNoteId, mPassword);
mPassword = null; // 清除密码,避免重复保存
}
// 如果该笔记存在小部件,更新小部件内容
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
@ -266,15 +273,26 @@ public class WorkingNote {
/**
*
*
* 使
* @return truefalse
*/
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
if (mIsDeleted) {
return false;
} else {
}
// 如果设置了密码,说明用户想要创建加密便签,即使内容为空也应该保存
if (mPassword != null && !mPassword.isEmpty()) {
return true;
}
// 如果便签不存在于数据库中且内容为空,且没有本地修改,不值得保存
if (!existInDatabase() && TextUtils.isEmpty(mContent) && !mNote.isLocalModified()) {
return false;
}
// 如果便签存在于数据库中但未被修改,不值得保存
if (existInDatabase() && !mNote.isLocalModified()) {
return false;
}
return true;
}
/**
@ -480,6 +498,38 @@ public class WorkingNote {
return mWidgetType;
}
/**
* 便
* @param encrypted
*/
public void setEncrypted(boolean encrypted) {
mNote.setNoteValue(NoteColumns.IS_ENCRYPTED, encrypted ? "1" : "0");
}
/**
* 使
* @param password
*/
public void setPassword(String password) {
mPassword = password;
}
/**
* 便
* @return
*/
public boolean isEncrypted() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId),
new String[]{NoteColumns.IS_ENCRYPTED}, null, null, null);
boolean encrypted = false;
if (cursor != null && cursor.moveToFirst()) {
encrypted = cursor.getInt(0) == 1;
cursor.close();
}
return encrypted;
}
/**
*
*

@ -29,75 +29,95 @@ import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TrashColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
/**
*
* <p></p>
* <p>使// note data </p>
*/
/**
* -
*
*/
public class DataUtils {
/**
*
*/
public static final String TAG = "DataUtils";
/**
*
*
* @param resolver ContentResolver
* @param ids ID
* @return truefalse
* Move notes to trash table instead of directly deleting them
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
public static boolean batchMoveToTrash(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null || ids.size() == 0) {
Log.d(TAG, "the ids is null or empty");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
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());
}
Cursor cursor = null;
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER || id <= 0) {
Log.e(TAG, "Don't move system folder root or invalid id: " + id);
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;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (Exception e) {
Log.e(TAG, String.format("Move to trash failed: %s", e.toString()));
return false;
} finally {
if (cursor != null) {
cursor.close();
}
}
return false;
}
/**
*
* @param resolver
* @param id ID
* @param srcFolderId ID
* @param desFolderId ID
*/
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
// Instead of directly deleting, move to trash
return batchMoveToTrash(resolver, ids);
}
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
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);
}
/**
*
* @param resolver
* @param ids ID
* @param folderId ID
* @return
*/
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
if (ids == null) {
@ -145,9 +158,7 @@ public class DataUtils {
}
/**
*
* @param resolver
* @return
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
@ -171,13 +182,6 @@ public class DataUtils {
return count;
}
/**
* ID
* @param resolver
* @param noteId ID
* @param type
* @return
*/
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
@ -195,12 +199,6 @@ public class DataUtils {
return exist;
}
/**
* ID
* @param resolver
* @param noteId ID
* @return
*/
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
@ -215,12 +213,6 @@ public class DataUtils {
return exist;
}
/**
* ID
* @param resolver
* @param dataId ID
* @return
*/
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
@ -235,12 +227,6 @@ public class DataUtils {
return exist;
}
/**
*
* @param resolver
* @param name
* @return
*/
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
@ -257,12 +243,6 @@ public class DataUtils {
return exist;
}
/**
*
* @param resolver
* @param folderId ID
* @return
*/
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
@ -290,13 +270,6 @@ public class DataUtils {
return set;
}
/**
* ID
*
* @param resolver
* @param noteId ID
* @return
*/
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
@ -317,13 +290,93 @@ public class DataUtils {
}
/**
* ID
*
* @param resolver
* @param phoneNumber
* @param callDate
* @return ID
* Restore note from trash table back to note table
*/
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) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
@ -345,13 +398,6 @@ public class DataUtils {
return 0;
}
/**
* ID
* @param resolver
* @param noteId ID
* @return
* @throws IllegalArgumentException
*/
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
@ -370,12 +416,6 @@ public class DataUtils {
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
/**
*
*
* @param snippet
* @return
*/
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();

@ -0,0 +1,145 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper;
/**
*
* 便
*/
public class PasswordUtils {
private static final String TAG = "PasswordUtils";
/**
* 便
* @param context
* @param noteId 便ID
* @param password
* @return
*/
public static boolean savePassword(Context context, long noteId, String password) {
if (noteId <= 0 || password == null || password.isEmpty()) {
Log.e(TAG, "Invalid noteId or password");
return false;
}
SQLiteDatabase db = NotesDatabaseHelper.getInstance(context).getWritableDatabase();
ContentValues values = new ContentValues();
values.put(NoteColumns.ID, noteId);
values.put("password", password);
// 先删除旧密码(如果存在)
db.delete(NotesDatabaseHelper.TABLE.ENCRYPTED_NOTE_PASSWORD,
NoteColumns.ID + "=?", new String[]{String.valueOf(noteId)});
// 插入新密码
long result = db.insert(NotesDatabaseHelper.TABLE.ENCRYPTED_NOTE_PASSWORD, null, values);
return result != -1;
}
/**
* 便
* @param context
* @param noteId 便ID
* @return null
*/
public static String getPassword(Context context, long noteId) {
if (noteId <= 0) {
return null;
}
SQLiteDatabase db = NotesDatabaseHelper.getInstance(context).getReadableDatabase();
Cursor cursor = db.query(NotesDatabaseHelper.TABLE.ENCRYPTED_NOTE_PASSWORD,
new String[]{"password"},
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)},
null, null, null);
String password = null;
if (cursor != null && cursor.moveToFirst()) {
password = cursor.getString(0);
cursor.close();
}
return password;
}
/**
*
* @param context
* @param noteId 便ID
* @param inputPassword
* @return
*/
public static boolean verifyPassword(Context context, long noteId, String inputPassword) {
String savedPassword = getPassword(context, noteId);
if (savedPassword == null) {
return false;
}
return savedPassword.equals(inputPassword);
}
/**
* 便
* @param context
* @param noteId 便ID
* @return
*/
public static boolean deletePassword(Context context, long noteId) {
if (noteId <= 0) {
return false;
}
SQLiteDatabase db = NotesDatabaseHelper.getInstance(context).getWritableDatabase();
int result = db.delete(NotesDatabaseHelper.TABLE.ENCRYPTED_NOTE_PASSWORD,
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)});
return result > 0;
}
/**
* 便
* @param context
* @param noteId 便ID
* @return
*/
public static boolean isNoteEncrypted(Context context, long noteId) {
if (noteId <= 0) {
return false;
}
Cursor cursor = context.getContentResolver().query(
android.content.ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
new String[]{NoteColumns.IS_ENCRYPTED},
null, null, null);
boolean isEncrypted = false;
if (cursor != null && cursor.moveToFirst()) {
isEncrypted = cursor.getInt(0) == 1;
cursor.close();
}
return isEncrypted;
}
}

@ -286,6 +286,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
}
// 处理加密便签
boolean isEncrypted = intent.getBooleanExtra("is_encrypted", false);
if (isEncrypted) {
String password = intent.getStringExtra("password");
if (password != null && !password.isEmpty()) {
mWorkingNote.setEncrypted(true);
// 保存密码(需要在便签保存后)
mWorkingNote.setPassword(password);
}
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);

@ -36,18 +36,7 @@ import net.micode.notes.tool.DataUtils;
public class NoteItemData {
/** 数据库查询投影数组 */
static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 笔记ID
NoteColumns.ALERTED_DATE, // 提醒日期
NoteColumns.BG_COLOR_ID, // 背景颜色ID
NoteColumns.CREATED_DATE, // 创建日期
NoteColumns.HAS_ATTACHMENT, // 是否有附件
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.NOTES_COUNT, // 笔记数量
NoteColumns.PARENT_ID, // 父文件夹ID
NoteColumns.SNIPPET, // 笔记摘要
NoteColumns.TYPE, // 笔记类型
NoteColumns.WIDGET_ID, // 小部件ID
NoteColumns.WIDGET_TYPE, // 小部件类型
NoteColumns.IS_ENCRYPTED, // 是否加密
};
/** 笔记ID列索引 */
@ -74,6 +63,8 @@ public class NoteItemData {
private static final int WIDGET_ID_COLUMN = 10;
/** 小部件类型列索引 */
private static final int WIDGET_TYPE_COLUMN = 11;
/** 是否加密列索引 */
private static final int IS_ENCRYPTED_COLUMN = 12;
/** 笔记ID */
private long mId;
@ -99,6 +90,8 @@ public class NoteItemData {
private int mWidgetId;
/** 小部件类型 */
private int mWidgetType;
/** 是否加密 */
private boolean mIsEncrypted;
/** 联系人姓名(用于通话记录) */
private String mName;
/** 电话号码(用于通话记录) */
@ -149,6 +142,7 @@ public class NoteItemData {
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mIsEncrypted = cursor.getInt(IS_ENCRYPTED_COLUMN) == 1;
mPhoneNumber = "";
// 如果是通话记录文件夹中的笔记,获取电话号码和联系人姓名
@ -401,4 +395,12 @@ public class NoteItemData {
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
/**
*
* @return truefalse
*/
public boolean isEncrypted() {
return mIsEncrypted;
}
}

@ -90,61 +90,65 @@ import java.lang.reflect.Field;
*
*/
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
// 异步查询处理的标记常量
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; // 查询笔记列表的标记
private static final int FOLDER_LIST_QUERY_TOKEN = 1; // 查询文件夹列表的标记
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
// 文件夹上下文菜单的菜单项ID
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 FOLDER_LIST_QUERY_TOKEN = 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 String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
/**
*
* NOTE_LIST SUB_FOLDER CALL_RECORD_FOLDER
*/
private enum ListEditState {
NOTE_LIST, // 笔记列表状态
SUB_FOLDER, // 子文件夹状态
CALL_RECORD_FOLDER // 通话记录文件夹状态
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
// 成员变量
private ListEditState mState; // 当前列表编辑状态
private BackgroundQueryHandler mBackgroundQueryHandler; // 异步查询处理器
private NotesListAdapter mNotesListAdapter; // 笔记列表适配器
private ListView mNotesListView; // 笔记列表视图
private Button mAddNewNote; // 新建笔记按钮
private boolean mDispatch; // 是否分发触摸事件给列表视图
private int mOriginY; // 原始触摸Y坐标
private int mDispatchY; // 分发触摸Y坐标
private TextView mTitleBar; // 标题栏
private long mCurrentFolderId; // 当前文件夹ID
private ContentResolver mContentResolver; // 内容解析器
private ModeCallback mModeCallBack; // 多选模式回调
private ListEditState mState;
private boolean mIsChecklistMode; // 是否为清单模式OMO
private Button mNoteModeButton; // 笔记模式按钮OMO
private Button mChecklistModeButton; // 清单模式按钮OMO
private ChecklistManager mChecklistManager; // 清单管理器OMO
private static final String TAG = "NotesListActivity"; // 日志标签
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;// 笔记列表滚动速率
private BackgroundQueryHandler mBackgroundQueryHandler;
private NotesListAdapter mNotesListAdapter;
private ListView mNotesListView;
private Button mAddNewNote;
private boolean mDispatch;
private int mOriginY;
private int mDispatchY;
private TextView mTitleBar;
private long mCurrentFolderId;
private ContentResolver mContentResolver;
private ModeCallback mModeCallBack;
private static final String TAG = "NotesListActivity";
private NoteItemData mFocusNoteDataItem; // 当前焦点的笔记数据项
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 NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; // 普通文件夹查询条件
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ 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_NEW_NODE = 103; // 新建笔记的请求码
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -179,20 +183,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 处理从NoteEditActivity返回的结果
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
// 如果是打开或新建笔记的请求返回成功,刷新列表
mNotesListAdapter.changeCursor(null);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
/**
*
* 使
*/
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
@ -439,12 +437,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
int start = screenHeight - newNoteViewHeight;
int eventY = start + (int) event.getY();
// 子文件夹状态需减去标题栏高度
/**
* Minus TitleBar's height
*/
if (mState == ListEditState.SUB_FOLDER) {
eventY -= 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)) {
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount());
@ -578,6 +588,66 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*
*/
private void createNewNote() {
// 弹出对话框询问是否加密
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.encrypt_note_dialog_title);
builder.setMessage(R.string.encrypt_note_dialog_message);
builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户选择加密,弹出密码输入对话框
showPasswordInputDialog();
}
});
builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户选择不加密,直接创建便签
createNoteWithoutEncryption();
}
});
builder.show();
}
private void showPasswordInputDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etPassword = (EditText) view.findViewById(R.id.et_foler_name);
etPassword.setHint(R.string.password_hint);
etPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
builder.setTitle(R.string.password_set_dialog_title);
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 取消,直接创建不加密的便签
createNoteWithoutEncryption();
}
});
final Dialog dialog = builder.create();
dialog.show();
final Button positive = (Button) dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String password = etPassword.getText().toString();
if (TextUtils.isEmpty(password)) {
Toast.makeText(NotesListActivity.this,
R.string.password_empty, Toast.LENGTH_SHORT).show();
return;
}
dialog.dismiss();
// 创建加密便签
createNoteWithEncryption(password);
}
});
}
private void createNoteWithoutEncryption() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
@ -603,6 +673,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*
*
*/
private void createNoteWithEncryption(String password) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId);
intent.putExtra("is_encrypted", true);
intent.putExtra("password", password);
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE);
}
private void batchDelete() {
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
@ -676,10 +755,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* /
*/
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
// 检查是否为加密便签
// 使用PasswordUtils检查因为数据库中的值可能更准确
boolean isEncrypted = net.micode.notes.tool.PasswordUtils.isNoteEncrypted(this, data.getId());
if (isEncrypted) {
// 跳转到密码输入界面
Intent intent = new Intent(this, PasswordInputActivity.class);
intent.putExtra(PasswordInputActivity.EXTRA_NOTE_ID, data.getId());
startActivity(intent);
} else {
// 普通便签,直接打开
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
}
/**
@ -1078,6 +1168,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
case R.id.menu_search:
onSearchRequested();
break;
case R.id.menu_trash: {
Intent intent = new Intent(this, TrashActivity.class);
startActivity(intent);
break;
}
default:
break;
}

@ -49,6 +49,8 @@ import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
public class NotesListItem extends LinearLayout {
/** 提醒图标 */
private ImageView mAlert;
/** 加密图标 */
private ImageView mEncryptedIcon;
/** 笔记标题文本视图 */
private TextView mTitle;
/** 时间文本视图 */
@ -113,6 +115,7 @@ public class NotesListItem extends LinearLayout {
inflate(context, R.layout.note_item, this);
// 获取视图组件
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mEncryptedIcon = (ImageView) findViewById(R.id.iv_encrypted_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
@ -308,6 +311,14 @@ public class NotesListItem extends LinearLayout {
// 设置修改时间
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 显示加密图标
if (data.isEncrypted()) {
mEncryptedIcon.setVisibility(View.VISIBLE);
mEncryptedIcon.setImageResource(android.R.drawable.ic_lock_lock);
} else {
mEncryptedIcon.setVisibility(View.GONE);
}
// 设置背景
setBackground(data);
}

@ -0,0 +1,101 @@
/*
* 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.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.tool.PasswordUtils;
/**
* Activity
* 便
*/
public class PasswordInputActivity extends Activity {
private static final String TAG = "PasswordInputActivity";
public static final String EXTRA_NOTE_ID = "note_id";
private EditText mPasswordEditText;
private Button mReturnButton;
private Button mConfirmButton;
private long mNoteId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_password_input);
mNoteId = getIntent().getLongExtra(EXTRA_NOTE_ID, 0);
if (mNoteId <= 0) {
Toast.makeText(this, R.string.error_note_not_exist, Toast.LENGTH_SHORT).show();
finish();
return;
}
initViews();
}
private void initViews() {
mPasswordEditText = (EditText) findViewById(R.id.et_password);
mReturnButton = (Button) findViewById(R.id.btn_return);
mConfirmButton = (Button) findViewById(R.id.btn_confirm);
mReturnButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 返回主界面
finish();
}
});
mConfirmButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String password = mPasswordEditText.getText().toString();
if (TextUtils.isEmpty(password)) {
Toast.makeText(PasswordInputActivity.this,
R.string.password_empty, Toast.LENGTH_SHORT).show();
return;
}
// 验证密码
if (PasswordUtils.verifyPassword(PasswordInputActivity.this, mNoteId, password)) {
// 密码正确,跳转到便签编辑界面
Intent intent = new Intent(PasswordInputActivity.this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
finish();
} else {
// 密码错误
Toast.makeText(PasswordInputActivity.this,
R.string.password_error, Toast.LENGTH_SHORT).show();
mPasswordEditText.setText("");
}
}
});
}
}

@ -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