Compare commits

..

No commits in common. 'master' and 'yuqiuyang_branch' have entirely different histories.

3
.idea/.gitignore vendored

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/class_16.iml" filepath="$PROJECT_DIR$/class_16.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

9
doc/.gitignore vendored

@ -0,0 +1,9 @@
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
project.properties
.settings/
.classpath
.project

@ -0,0 +1,150 @@
<?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" />
<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" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<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" />
<application
android:icon="@drawable/icon_app"
android:label="@string/app_name" >
<activity
android:name=".ui.NotesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan" >
<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" >
<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" >
<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" >
<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" >
<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>
<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" />
</application>
</manifest>

@ -0,0 +1,190 @@
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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

@ -0,0 +1,23 @@
[中文]
1. MiCode便签是小米便签的社区开源版由MIUI团队(www.miui.com) 发起并贡献第一批代码遵循NOTICE文件所描述的开源协议
今后为MiCode社区(www.micode.net) 拥有,并由社区发布和维护。
2. Bug反馈和跟踪请访问Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. 功能建议和综合讨论请访问MiCode,
http://micode.net/forum.php?mod=forumdisplay&fid=38
[English]
1. MiCode Notes is open source edition of XM notepad, it's first initiated and sponsored by MIUI team (www.miui.com).
It's opened under license described by NOTICE file. It's owned by the MiCode community (www.micode.net). In future,
the MiCode community will release and maintain this project.
2. Regarding issue tracking, please visit Github,
https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open
3. Regarding feature request and general discussion, please visit Micode forum,
http://micode.net/forum.php?mod=forumdisplay&fid=38

Binary file not shown.

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<config>
<source_groups>
<source_group_cea3fbad-c603-4bb6-903f-3c088d0b778a>
<java_standard>15</java_standard>
<name>Java Source Group</name>
<source_extensions>
<source_extension>.java</source_extension>
</source_extensions>
<source_paths>
<source_path>data</source_path>
<source_path>gtask</source_path>
<source_path>model</source_path>
<source_path>tool</source_path>
<source_path>ui</source_path>
<source_path>widget</source_path>
</source_paths>
<status>enabled</status>
<type>Java Source Group</type>
<use_jre_system_library>1</use_jre_system_library>
</source_group_cea3fbad-c603-4bb6-903f-3c088d0b778a>
</source_groups>
<version>8</version>
</config>

@ -14,72 +14,60 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.data; package net.micode.notes.data;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import android.database.Cursor; import java.util.HashMap;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import java.util.HashMap; public class Contact {
//change private static HashMap<String, String> sContactCache;
public class Contact { //联系人 private static final String TAG = "Contact";
private static HashMap<String, String> sContactCache;
private static final String TAG = "Contact";
// 定义字符串CALLER_ID_SELECTION private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN "
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
+ Phone.NUMBER public static String getContact(Context context, String phoneNumber) {
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" if(sContactCache == null) {
+ " AND " + Data.RAW_CONTACT_ID + " IN " sContactCache = new HashMap<String, String>();
+ "(SELECT raw_contact_id " }
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
// 获取联系人 if(sContactCache.containsKey(phoneNumber)) {
public static String getContact(Context context, String phoneNumber) { return sContactCache.get(phoneNumber);
if(sContactCache == null) { }
sContactCache = new HashMap<String, String>();
}
// 查找HashMap中是否已有phoneNumber信息 String selection = CALLER_ID_SELECTION.replace("+",
if(sContactCache.containsKey(phoneNumber)) { PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
return sContactCache.get(phoneNumber); Cursor cursor = context.getContentResolver().query(
} Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
String selection = CALLER_ID_SELECTION.replace("+", if (cursor != null && cursor.moveToFirst()) {
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); try {
// 查找数据库中phoneNumber的信息 String name = cursor.getString(0);
Cursor cursor = context.getContentResolver().query( sContactCache.put(phoneNumber, name);
Data.CONTENT_URI, return name;
new String [] { Phone.DISPLAY_NAME }, } catch (IndexOutOfBoundsException e) {
selection, Log.e(TAG, " Cursor get string error " + e.toString());
new String[] { phoneNumber }, return null;
null); } finally {
cursor.close();
// 判定查询结果 }
// moveToFirst()返回第一条 } else {
if (cursor != null && cursor.moveToFirst()) { Log.d(TAG, "No contact matched with number:" + phoneNumber);
try { return null;
// 找到相关信息 }
String name = cursor.getString(0); }
sContactCache.put(phoneNumber, name); }
return name;
// 异常
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, " Cursor get string error " + e.toString());
return null;
} finally {
cursor.close();
}
// 未找到相关信息
} else {
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
}
}
}

@ -14,306 +14,266 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.data; package net.micode.notes.data;
import android.content.ContentUris; import android.net.Uri;
import android.net.Uri; public class Notes {
// Notes 类中定义了很多常量这些常量大多是int型和string型 public static final String AUTHORITY = "micode_notes";
public class Notes { public static final String TAG = "Notes";
public static final String AUTHORITY = "micode_notes"; public static final int TYPE_NOTE = 0;
public static final String TAG = "Notes"; public static final int TYPE_FOLDER = 1;
public static final int TYPE_SYSTEM = 2;
//以下三个常量对NoteColumns.TYPE的值进行设置时会用到
public static final int TYPE_NOTE = 0; /**
public static final int TYPE_FOLDER = 1; * Following IDs are system folders' identifiers
public static final int TYPE_SYSTEM = 2; * {@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
* Following IDs are system folders' identifiers */
* {@link Notes#ID_ROOT_FOLDER } is default folder public static final int ID_ROOT_FOLDER = 0;
* {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder public static final int ID_TEMPARAY_FOLDER = -1;
* {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records public static final int ID_CALL_RECORD_FOLDER = -2;
*/ public static final int ID_TRASH_FOLER = -3;
public static final int ID_ROOT_FOLDER = 0;
public static final int ID_TEMPARAY_FOLDER = -1; public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";
public static final int ID_CALL_RECORD_FOLDER = -2; public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final int ID_TRASH_FOLER = -3; public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";
public static final String INTENT_EXTRA_ALERT_DATE = 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";
"net.micode.notes.alert_date";
public static final String INTENT_EXTRA_BACKGROUND_ID = public static final int TYPE_WIDGET_INVALIDE = -1;
public static final int TYPE_WIDGET_2X = 0;
"net.micode.notes.background_color_id"; public static final int TYPE_WIDGET_4X = 1;
public static final String INTENT_EXTRA_WIDGET_ID =
public static class DataConstants {
"net.micode.notes.widget_id"; public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
public static final String INTENT_EXTRA_WIDGET_TYPE = public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
}
"net.micode.notes.widget_type";
public static final String INTENT_EXTRA_FOLDER_ID = /**
* Uri to query all notes and folders
"net.micode.notes.folder_id"; */
public static final String INTENT_EXTRA_CALL_DATE = public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
"net.micode.notes.call_date"; /**
* Uri to query data
public static final int TYPE_WIDGET_INVALIDE = -1; */
public static final int TYPE_WIDGET_2X = 0; public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
public static final int TYPE_WIDGET_4X = 1;
public interface NoteColumns {
public static class DataConstants { /**
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; * The unique ID for a row
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; * <P> Type: INTEGER (long) </P>
} */
public static final String ID = "_id";
/**
* Uri to query all notes and folders /**
*/ * The parent's id for note or folder
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + * <P> Type: INTEGER (long) </P>
*/
AUTHORITY + "/note");//定义查询便签和文件夹的指针。 public static final String PARENT_ID = "parent_id";
// public static final Uri my_URI = ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI , 10); /**
* Created data for note or folder
/** * <P> Type: INTEGER (long) </P>
* Uri to query data */
*/ public static final String CREATED_DATE = "created_date";
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" +
/**
AUTHORITY + "/data");//定义查找数据的指针。 * Latest modified date
* <P> Type: INTEGER (long) </P>
// 定义NoteColumns的常量,用于后面创建数据库的表头 */
public interface NoteColumns { public static final String MODIFIED_DATE = "modified_date";
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P> /**
*/ * Alert date
public static final String ID = "_id"; * <P> Type: INTEGER (long) </P>
*/
/** public static final String ALERTED_DATE = "alert_date";
* The parent's id for note or folder
* <P> Type: INTEGER (long) </P> /**
*/ * Folder's name or text content of note
public static final String PARENT_ID = "parent_id";//为什么会有parent_id * <P> Type: TEXT </P>
*/
/** public static final String SNIPPET = "snippet";
* Created data for note or folder
* <P> Type: INTEGER (long) </P> /**
*/ * Note's widget id
public static final String CREATED_DATE = "created_date"; * <P> Type: INTEGER (long) </P>
*/
/** public static final String WIDGET_ID = "widget_id";
* Latest modified date
* <P> Type: INTEGER (long) </P> /**
*/ * Note's widget type
public static final String MODIFIED_DATE = "modified_date"; * <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_TYPE = "widget_type";
/**
* Alert date /**
* <P> Type: INTEGER (long) </P> * Note's background color's id
*/ * <P> Type: INTEGER (long) </P>
public static final String ALERTED_DATE = "alert_date"; */
public static final String BG_COLOR_ID = "bg_color_id";
/**
* Folder's name or text content of note /**
* <P> Type: TEXT </P> * For text note, it doesn't has attachment, for multi-media
*/ * note, it has at least one attachment
public static final String SNIPPET = "snippet"; * <P> Type: INTEGER </P>
*/
/** public static final String HAS_ATTACHMENT = "has_attachment";
* Note's widget id
* <P> Type: INTEGER (long) </P> /**
*/ * Folder's count of notes
public static final String WIDGET_ID = "widget_id"; * <P> Type: INTEGER (long) </P>
*/
/** public static final String NOTES_COUNT = "notes_count";
* Note's widget type
* <P> Type: INTEGER (long) </P> /**
*/ * The file type: folder or note
public static final String WIDGET_TYPE = "widget_type"; * <P> Type: INTEGER </P>
*/
/** public static final String TYPE = "type";
* Note's background color's id
* <P> Type: INTEGER (long) </P> /**
*/ * The last sync id
public static final String BG_COLOR_ID = "bg_color_id"; * <P> Type: INTEGER (long) </P>
*/
/** public static final String SYNC_ID = "sync_id";
* For text note, it doesn't has attachment, for multi-media
* note, it has at least one attachment /**
* <P> Type: INTEGER </P> * Sign to indicate local modified or not
*/ * <P> Type: INTEGER </P>
public static final String HAS_ATTACHMENT = "has_attachment"; */
public static final String LOCAL_MODIFIED = "local_modified";
/**
* Folder's count of notes /**
* <P> Type: INTEGER (long) </P> * Original parent id before moving into temporary folder
*/ * <P> Type : INTEGER </P>
public static final String NOTES_COUNT = "notes_count"; */
public static final String ORIGIN_PARENT_ID = "origin_parent_id";
/**
* The file type: folder or note /**
* <P> Type: INTEGER </P> * The gtask id
*/ * <P> Type : TEXT </P>
public static final String TYPE = "type"; */
public static final String GTASK_ID = "gtask_id";
/**
* The last sync id /**
* <P> Type: INTEGER (long) </P> * The version code
*/ * <P> Type : INTEGER (long) </P>
public static final String SYNC_ID = "sync_id";//同步 */
public static final String VERSION = "version";
/** }
* Sign to indicate local modified or not
* <P> Type: INTEGER </P> public interface DataColumns {
*/ /**
public static final String LOCAL_MODIFIED = "local_modified"; * The unique ID for a row
* <P> Type: INTEGER (long) </P>
/** */
* Original parent id before moving into temporary folder public static final String ID = "_id";
* <P> Type : INTEGER </P>
*/ /**
public static final String ORIGIN_PARENT_ID = "origin_parent_id"; * The MIME type of the item represented by this row.
* <P> Type: Text </P>
/** */
* The gtask id public static final String MIME_TYPE = "mime_type";
* <P> Type : TEXT </P>
*/ /**
public static final String GTASK_ID = "gtask_id"; * The reference id to note that this data belongs to
* <P> Type: INTEGER (long) </P>
/** */
* The version code public static final String NOTE_ID = "note_id";
* <P> Type : INTEGER (long) </P>
*/ /**
public static final String VERSION = "version"; * Created data for note or folder
}//这些常量主要是定义便签的属性的。 * <P> Type: INTEGER (long) </P>
*/
// 定义DataColumns的常量,用于后面创建数据库的表头 public static final String CREATED_DATE = "created_date";
public interface DataColumns {
/** /**
* The unique ID for a row * Latest modified date
* <P> Type: INTEGER (long) </P> * <P> Type: INTEGER (long) </P>
*/ */
public static final String ID = "_id"; public static final String MODIFIED_DATE = "modified_date";
/** /**
* The MIME type of the item represented by this row. * Data's content
* <P> Type: Text </P> * <P> Type: TEXT </P>
*/ */
public static final String MIME_TYPE = "mime_type"; public static final String CONTENT = "content";
/**
* The reference id to note that this data belongs to /**
* <P> Type: INTEGER (long) </P> * Generic data column, the meaning is {@link #MIMETYPE} specific, used for
*/ * integer data type
public static final String NOTE_ID = "note_id"; * <P> Type: INTEGER </P>
*/
/** public static final String DATA1 = "data1";
* Created data for note or folder
* <P> Type: INTEGER (long) </P> /**
*/ * Generic data column, the meaning is {@link #MIMETYPE} specific, used for
public static final String CREATED_DATE = "created_date"; * integer data type
* <P> Type: INTEGER </P>
/** */
* Latest modified date public static final String DATA2 = "data2";
* <P> Type: INTEGER (long) </P>
*/ /**
public static final String MODIFIED_DATE = "modified_date"; * Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
/** * <P> Type: TEXT </P>
* Data's content */
* <P> Type: TEXT </P> public static final String DATA3 = "data3";
*/
public static final String CONTENT = "content"; /**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
/** * <P> Type: TEXT </P>
* Generic data column, the meaning is {@link #MIMETYPE} specific, */
used for public static final String DATA4 = "data4";
* integer data type
* <P> Type: INTEGER </P> /**
*/ * Generic data column, the meaning is {@link #MIMETYPE} specific, used for
public static final String DATA1 = "data1"; * TEXT data type
* <P> Type: TEXT </P>
/** */
* Generic data column, the meaning is {@link #MIMETYPE} specific, public static final String DATA5 = "data5";
used for }
* integer data type
* <P> Type: INTEGER </P> public static final class TextNote implements DataColumns {
*/ /**
public static final String DATA2 = "data2"; * Mode to indicate the text in check list mode or not
* <P> Type: Integer 1:check list mode 0: normal mode </P>
/** */
* Generic data column, the meaning is {@link #MIMETYPE} specific, public static final String MODE = DATA1;
used for
* TEXT data type public static final int MODE_CHECK_LIST = 1;
* <P> Type: TEXT </P>
*/ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";
public static final String DATA3 = "data3";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";
/**
* Generic data column, the meaning is {@link #MIMETYPE} specific, public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");
used for }
* TEXT data type
* <P> Type: TEXT </P> public static final class CallNote implements DataColumns {
*/ /**
public static final String DATA4 = "data4"; * Call date for this record
* <P> Type: INTEGER (long) </P>
/** */
* Generic data column, the meaning is {@link #MIMETYPE} specific, public static final String CALL_DATE = DATA1;
used for
* TEXT data type /**
* <P> Type: TEXT </P> * Phone number for this record
*/ * <P> Type: TEXT </P>
public static final String DATA5 = "data5"; */
}//主要是定义存储便签内容数据的 public static final String PHONE_NUMBER = DATA3;
public static final class TextNote implements DataColumns {
/** public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";
* 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 CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";
*/
public static final String MODE = DATA1; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
}
public static final int MODE_CHECK_LIST = 1; }
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/text_note";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/text_note";
public static final Uri CONTENT_URI = Uri.parse("content://" +
AUTHORITY + "/text_note");
}//文本内容的数据结构
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 CONTENT_TYPE =
"vnd.android.cursor.dir/call_note";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/call_note";
public static final Uri CONTENT_URI = Uri.parse("content://" +
AUTHORITY + "/call_note");
}//电话内容的数据结构
}

@ -14,352 +14,349 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.data; package net.micode.notes.data;
import android.content.ContentValues;//就是用于保存一些数据string boolean byte double float int long short ...)信息,这些信息可以被数据库操作时使用。 import android.content.ContentValues;
import android.content.Context;//加载和访问资源。android中主要是这两个功能但是这里具体不清楚 import android.content.Context;
import android.database.sqlite.SQLiteDatabase;//主要提供了对应于添加、删除、更新、查询的操作方法: insert()、delete()、update()和query()。配合content.values import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;//用来管理数据的创建和版本更新 import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; import android.util.Log;
import net.micode.notes.data.Notes.DataColumns; 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;
//数据库操作用SQLOpenhelper,对一些note和文件进行数据库的操作比如删除文件后将文件里的note也相应删除
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 = 4;
public interface TABLE { //接口分成note和data在后面的程序里分别使用过 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"; private static final String TAG = "NotesDatabaseHelper";
private static NotesDatabaseHelper mInstance; 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 + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," + NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " 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.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";//数据库中需要存储的项目的名称,就相当于创建一个表格的表头的内容。 ")";
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," +
DataColumns.MIME_TYPE + " TEXT NOT NULL," + DataColumns.MIME_TYPE + " TEXT NOT NULL," +
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA1 + " INTEGER," + DataColumns.DATA1 + " INTEGER," +
DataColumns.DATA2 + " INTEGER," + DataColumns.DATA2 + " INTEGER," +
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";//和上面的功能一样,主要是存储的项目不同 ")";
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 + ");";
/** /**
* Increase folder's note count when move note to the folder * Increase folder's note count when move note to the folder
*/ */
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+ "CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " + " BEGIN " +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";//在文件夹中移入一个Note之后需要更改的数据的表格。 " END";
/** /**
* Decrease folder's note count when move note from folder * 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 " +
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " + " BEGIN " +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";//在文件夹中移出一个Note之后需要更改的数据的表格。 " END";
/** /**
* Increase folder's note count when insert new note to the folder * 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 " +
" AFTER INSERT ON " + TABLE.NOTE + " AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " + " BEGIN " +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";//在文件夹中插入一个Note之后需要更改的数据的表格。 " END";
/** /**
* Decrease folder's note count when delete note from the folder * 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 " +
" AFTER DELETE ON " + TABLE.NOTE + " AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " + " BEGIN " +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" + " AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";//在文件夹中删除一个Note之后需要更改的数据的表格。 " END";
/** /**
* Update note's content when insert data with type {@link DataConstants#NOTE} * 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 +
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" + " BEGIN" +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";//在文件夹中对一个Note导入新的数据之后需要更改的数据的表格。 " END";
/** /**
* Update note's content when data with {@link DataConstants#NOTE} type has changed * 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 +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" + " BEGIN" +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";//Note数据被修改后需要更改的数据的表格。 " END";
/** /**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted * 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 +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" + " BEGIN" +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" + " SET " + NoteColumns.SNIPPET + "=''" +
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" END";//Note数据被删除后需要更改的数据的表格。 " END";
/** /**
* Delete datas belong to note which has been deleted * 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 " +
" AFTER DELETE ON " + TABLE.NOTE + " AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" + " BEGIN" +
" DELETE FROM " + TABLE.DATA + " DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";//删除已删除的便签的数据后需要更改的数据的表格。 " END";
/** /**
* Delete notes belong to folder which has been deleted * 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 " +
" AFTER DELETE ON " + TABLE.NOTE + " AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" + " BEGIN" +
" DELETE FROM " + TABLE.NOTE + " DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";//删除已删除的文件夹的便签后需要更改的数据的表格。 " END";
/** /**
* Move notes belong to folder which has been moved to trash folder * 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 " +
" AFTER UPDATE ON " + TABLE.NOTE + " AFTER UPDATE ON " + TABLE.NOTE +
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" + " BEGIN" +
" UPDATE " + TABLE.NOTE + " UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";//还原垃圾桶中便签后需要更改的数据的表格。 " END";
public NotesDatabaseHelper(Context context) { public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION); super(context, DB_NAME, null, DB_VERSION);
}//构造函数,传入数据库的名称和版本 }
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");
}//创建表格(用来存储标签属性) }
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");
db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");
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);
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}//execSQL是数据库操作的API主要是更改行为的SQL语句。 }
//在这里主要是用来重新创建上述定义的表格用的,先删除原来有的数据库的触发器再重新创建新的数据库
private void createSystemFolder(SQLiteDatabase db) {
private void createSystemFolder(SQLiteDatabase db) { ContentValues values = new ContentValues();
ContentValues values = new ContentValues();
/**
/** * call record foler for call notes
* 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
* 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
* 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
* 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); }
}//创建几个系统文件夹
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);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); Log.d(TAG, "data table has been created");
Log.d(TAG, "data 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); }
}//同上面的execSQL
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); }
} return mInstance;
return mInstance; }
}//上网查是为解决同一时刻只能有一个线程执行.
//在写程序库代码时,有时有一个类需要被所有的其它类使用, @Override
//但又要求这个类只能被实例化一次,是个服务类,定义一次,其它类使用同一个这个类的实例 public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
@Override createDataTable(db);
public void onCreate(SQLiteDatabase db) { }
createNoteTable(db);
createDataTable(db); @Override
}//实现两个表格(上面创建的两个表格) public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
@Override boolean skipV2 = false;
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false; if (oldVersion == 1) {
boolean skipV2 = false; upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
if (oldVersion == 1) { oldVersion++;
upgradeToV2(db); }
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++; if (oldVersion == 2 && !skipV2) {
} upgradeToV3(db);
reCreateTriggers = true;
if (oldVersion == 2 && !skipV2) { oldVersion++;
upgradeToV3(db); }
reCreateTriggers = true;
oldVersion++; if (oldVersion == 3) {
} upgradeToV4(db);
oldVersion++;
if (oldVersion == 3) { }
upgradeToV4(db);
oldVersion++; if (reCreateTriggers) {
} reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
if (reCreateTriggers) { }
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db); if (oldVersion != newVersion) {
} throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ "fails");
if (oldVersion != newVersion) { }
throw new IllegalStateException("Upgrade notes database to version " + newVersion }
+ "fails");
} private void upgradeToV2(SQLiteDatabase db) {
}//数据库版本的更新(数据库内容的更改) db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
private void upgradeToV2(SQLiteDatabase db) { createNoteTable(db);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); createDataTable(db);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); }
createNoteTable(db);
createDataTable(db); private void upgradeToV3(SQLiteDatabase db) {
}//更新到V2版本 // drop unused triggers
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
private void upgradeToV3(SQLiteDatabase db) { db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
// drop unused triggers db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); // add a column for gtask id
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); + " TEXT NOT NULL DEFAULT ''");
// add a column for gtask id // add a trash system folder
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID ContentValues values = new ContentValues();
+ " TEXT NOT NULL DEFAULT ''"); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
// add a trash system folder values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
ContentValues values = new ContentValues(); db.insert(TABLE.NOTE, null, values);
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); }
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); private void upgradeToV4(SQLiteDatabase db) {
}//更新到V3版本 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
private void upgradeToV4(SQLiteDatabase db) { }
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION }
+ " INTEGER NOT NULL DEFAULT 0");
}//更新到V4版本,但是不知道V2、V3、V4是什么意思
}

@ -14,323 +14,292 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.data; package net.micode.notes.data;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.R; import android.app.SearchManager;
import net.micode.notes.data.Notes.DataColumns; import android.content.ContentProvider;
import net.micode.notes.data.Notes.NoteColumns; import android.content.ContentUris;
import net.micode.notes.data.NotesDatabaseHelper.TABLE; import android.content.ContentValues;
//为存储和获取数据提供接口。可以在不同的应用程序之间共享数据 import android.content.Intent;
//ContentProvider提供的方法 import android.content.UriMatcher;
//query查询 import android.database.Cursor;
//insert插入 import android.database.sqlite.SQLiteDatabase;
//update更新 import android.net.Uri;
//delete删除 import android.text.TextUtils;
//getType得到数据类型 import android.util.Log;
public class NotesProvider extends ContentProvider {
// UriMatcher用于匹配Uri
private static final UriMatcher mMatcher;
private NotesDatabaseHelper mHelper; import net.micode.notes.R;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
private static final String TAG = "NotesProvider";
private static final int URI_NOTE = 1; public class NotesProvider extends ContentProvider {
private static final int URI_NOTE_ITEM = 2; private static final UriMatcher mMatcher;
private static final int URI_DATA = 3;
private static final int URI_DATA_ITEM = 4;
private static final int URI_SEARCH = 5; private NotesDatabaseHelper mHelper;
private static final int URI_SEARCH_SUGGEST = 6;
static { private static final String TAG = "NotesProvider";
// 创建UriMatcher时调用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_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
/** private static final int URI_NOTE = 1;
* x'0A' represents the '\n' character in sqlite. For title and content in the search result, private static final int URI_NOTE_ITEM = 2;
* we will trim '\n' and white space in order to show more information. private static final int URI_DATA = 3;
*/ private static final int URI_DATA_ITEM = 4;
// 声明 NOTES_SEARCH_PROJECTION
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
// 声明NOTES_SNIPPET_SEARCH_QUERY
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 private static final int URI_SEARCH = 5;
// Context只有在onCreate()中才被初始化 private static final int URI_SEARCH_SUGGEST = 6;
// 对mHelper进行实例化
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
@Override static {
// 查询uri在数据库中对应的位置 mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
String sortOrder) { mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
Cursor c = null; mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
// 获取可读数据库 mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
SQLiteDatabase db = mHelper.getReadableDatabase(); mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
String id = null; mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
// 匹配查找uri mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
switch (mMatcher.match(uri)) { }
// 对于不同的匹配值,在数据库中查找相应的条目
case URI_NOTE:
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
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:
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
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:
if (sortOrder != null || projection != null) {
// 不合法的参数异常
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
String searchString = null; /**
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { * x'0A' represents the '\n' character in sqlite. For title and content in the search result,
if (uri.getPathSegments().size() > 1) { * we will trim '\n' and white space in order to show more information.
// getPathSegments()方法得到一个String的List */
// 在uri.getPathSegments().get(1)为第2个元素 private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
searchString = uri.getPathSegments().get(1); + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
} + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
} else { + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
searchString = uri.getQueryParameter("pattern"); + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
} + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
if (TextUtils.isEmpty(searchString)) { private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
return null; + " FROM " + TABLE.NOTE
} + " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
try { @Override
searchString = String.format("%%%s%%", searchString); public boolean onCreate() {
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, mHelper = NotesDatabaseHelper.getInstance(getContext());
new String[] { searchString }); return true;
} catch (IllegalStateException ex) { }
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default:
// 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
@Override @Override
// 插入一个uri public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
public Uri insert(Uri uri, ContentValues values) { String sortOrder) {
// 获得可写的数据库 Cursor c = null;
SQLiteDatabase db = mHelper.getWritableDatabase(); SQLiteDatabase db = mHelper.getReadableDatabase();
long dataId = 0, noteId = 0, insertedId = 0; 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,
insertedId = noteId = db.insert(TABLE.NOTE, null, values); sortOrder);
break; break;
// 如果存在查找NOTE_ID case URI_NOTE_ITEM:
case URI_DATA: id = uri.getPathSegments().get(1);
if (values.containsKey(DataColumns.NOTE_ID)) { c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
noteId = values.getAsLong(DataColumns.NOTE_ID); + parseSelection(selection), selectionArgs, null, null, sortOrder);
} else { break;
Log.d(TAG, "Wrong data format without note id:" + values.toString()); case URI_DATA:
} c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
insertedId = dataId = db.insert(TABLE.DATA, null, values); sortOrder);
break; break;
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:
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
String searchString = null;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
} else {
searchString = uri.getQueryParameter("pattern");
}
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
searchString = String.format("%%%s%%", searchString);
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default: default:
throw new IllegalArgumentException("Unknown URI " + uri); throw new IllegalArgumentException("Unknown URI " + uri);
} }
// Notify the note uri if (c != null) {
// notifyChange获得一个ContextResolver对象并且更新里面的内容 c.setNotificationUri(getContext().getContentResolver(), uri);
if (noteId > 0) { }
getContext().getContentResolver().notifyChange( return c;
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); }
}
// Notify the data uri @Override
if (dataId > 0) { public Uri insert(Uri uri, ContentValues values) {
getContext().getContentResolver().notifyChange( SQLiteDatabase db = mHelper.getWritableDatabase();
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); long dataId = 0, noteId = 0, insertedId = 0;
} switch (mMatcher.match(uri)) {
case URI_NOTE:
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA:
if (values.containsKey(DataColumns.NOTE_ID)) {
noteId = values.getAsLong(DataColumns.NOTE_ID);
} else {
Log.d(TAG, "Wrong data format without note id:" + values.toString());
}
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// Notify the note uri
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 返回插入的uri的路径 // Notify the data uri
return ContentUris.withAppendedId(uri, insertedId); if (dataId > 0) {
} getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
@Override return ContentUris.withAppendedId(uri, insertedId);
// 删除一个uri }
public int delete(Uri uri, String selection, String[] selectionArgs) {
//Uri代表要操作的数据Android上可用的每种资源 -包括 图像、视频片段、音频资源等都可以用Uri来表示。
int count = 0;
String id = null;
// 获得可写的数据库
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean deleteData = false;
switch (mMatcher.match(uri)) {
case URI_NOTE:
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
}
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case URI_DATA:
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override @Override
// 更新一个uri public int delete(Uri uri, 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 deleteData = false;
boolean updateData = false; switch (mMatcher.match(uri)) {
switch (mMatcher.match(uri)) { case URI_NOTE:
case URI_NOTE: selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
increaseNoteVersion(-1, selection, selectionArgs); count = db.delete(TABLE.NOTE, 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); * ID that smaller than 0 is system folder which is not allowed to
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id * trash
+ parseSelection(selection), selectionArgs); */
break; long noteId = Long.valueOf(id);
case URI_DATA: if (noteId <= 0) {
count = db.update(TABLE.DATA, values, selection, selectionArgs); break;
updateData = true; }
break; count = db.delete(TABLE.NOTE,
case URI_DATA_ITEM: NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
id = uri.getPathSegments().get(1); break;
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id case URI_DATA:
+ parseSelection(selection), selectionArgs); count = db.delete(TABLE.DATA, selection, selectionArgs);
updateData = true; deleteData = true;
break; break;
default: case URI_DATA_ITEM:
throw new IllegalArgumentException("Unknown URI " + uri); id = uri.getPathSegments().get(1);
} count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@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;
switch (mMatcher.match(uri)) {
case URI_NOTE:
increaseNoteVersion(-1, selection, selectionArgs);
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
case URI_DATA:
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) { if (count > 0) {
if (updateData) { if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
} }
getContext().getContentResolver().notifyChange(uri, null); getContext().getContentResolver().notifyChange(uri, null);
} }
return count; return count;
} }
// 将字符串解析成规定格式 private String parseSelection(String selection) {
private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); }
}
//增加一个noteVersion 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 "); sql.append(TABLE.NOTE);
sql.append(TABLE.NOTE); sql.append(" SET ");
sql.append(" SET "); sql.append(NoteColumns.VERSION);
sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 ");
sql.append("=" + NoteColumns.VERSION + "+1 ");
if (id > 0 || !TextUtils.isEmpty(selection)) { if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE "); sql.append(" WHERE ");
} }
if (id > 0) { if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id)); sql.append(NoteColumns.ID + "=" + String.valueOf(id));
} }
if (!TextUtils.isEmpty(selection)) { if (!TextUtils.isEmpty(selection)) {
String selectString = id > 0 ? parseSelection(selection) : selection; String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) { for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args); selectString = selectString.replaceFirst("\\?", args);
} }
sql.append(selectString); sql.append(selectString);
} }
// execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句 mHelper.getWritableDatabase().execSQL(sql.toString());
mHelper.getWritableDatabase().execSQL(sql.toString()); }
}
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
} }

@ -16,17 +16,12 @@
package net.micode.notes.gtask.data; package net.micode.notes.gtask.data;
//cursor类用于在Android中进行数据库查询操作的游标类
import android.database.Cursor; import android.database.Cursor;
//log类用于在Android应用程序中输出日志的类
import android.util.Log; import android.util.Log;
//GTaskStringUtils用于处理相关的字符串操作
import net.micode.notes.tool.GTaskStringUtils; import net.micode.notes.tool.GTaskStringUtils;
//JSONException类用于JSON解析过程中可能出现的异常类
import org.json.JSONException; import org.json.JSONException;
//JSONObject类用于在Java中创建和操作JSON对象的类
import org.json.JSONObject; import org.json.JSONObject;
@ -35,37 +30,29 @@ public class MetaData extends Task {
private String mRelatedGid = null; private String mRelatedGid = null;
//此函数用于设置元数据信息首先接受两个参数gid字符串类型和metainfogid是Goole任务的IDmetainfo是包含元数据信息的JSON对象。
public void setMeta(String gid, JSONObject metaInfo) { public void setMeta(String gid, JSONObject metaInfo) {
//在try-catch块中首先使用put方法将gid添加到metainfo中如果添加失败会抛出JSONException异常然后在catch块中通过日志输出错误信息。
try { try {
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "failed to put related gid"); Log.e(TAG, "failed to put related gid");
} }
//然后将metainfo转换为字符串并调用setNotes方法进行设置。
setNotes(metaInfo.toString()); setNotes(metaInfo.toString());
setName(GTaskStringUtils.META_NOTE_NAME); setName(GTaskStringUtils.META_NOTE_NAME);
} }
//获取相关联的gid
public String getRelatedGid() { public String getRelatedGid() {
return mRelatedGid; return mRelatedGid;
} }
//判断当前数据是否为空,若为空则返回真值并保存。
@Override @Override
public boolean isWorthSaving() { public boolean isWorthSaving() {
return getNotes() != null; return getNotes() != null;
} }
//这段代码是对note类中setContentByRemoteJSON的重写作用是通过远程JSON数据设置笔记里的内容
@Override @Override
public void setContentByRemoteJSON(JSONObject js) { public void setContentByRemoteJSON(JSONObject js) {
super.setContentByRemoteJSON(js); super.setContentByRemoteJSON(js);
if (getNotes() != null) { if (getNotes() != null) {
//在 try-catch 块中尝试从JSON对象中获取相关的任务 ID并将其赋值给成员变量mRelatedGid如果获取失败或者注释不是有效的 JSON 格式,则将 mRelatedGid 设为 null。
try { try {
JSONObject metaInfo = new JSONObject(getNotes().trim()); JSONObject metaInfo = new JSONObject(getNotes().trim());
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
@ -76,20 +63,17 @@ public class MetaData extends Task {
} }
} }
//函数作用是使用本地JSON数据对象设置元数据内容
@Override @Override
public void setContentByLocalJSON(JSONObject js) { public void setContentByLocalJSON(JSONObject js) {
// this function should not be called // this function should not be called
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
} }
//函数作用是从元数据内容中获取JSON对象
@Override @Override
public JSONObject getLocalJSONFromContent() { public JSONObject getLocalJSONFromContent() {
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
} }
//函数作用是获取实时动作状态
@Override @Override
public int getSyncAction(Cursor c) { public int getSyncAction(Cursor c) {
throw new IllegalAccessError("MetaData:getSyncAction should not be called"); throw new IllegalAccessError("MetaData:getSyncAction should not be called");

@ -20,31 +20,30 @@ import android.database.Cursor;
import org.json.JSONObject; import org.json.JSONObject;
//下面是一些同步实时操作的基础数据类型,定义了指示相关操作的常量。
public abstract class Node { public abstract class Node {
public static final int SYNC_ACTION_NONE = 0;//本地和云端都无可更新内容(即本地和云端内容一致) public static final int SYNC_ACTION_NONE = 0;
public static final int SYNC_ACTION_ADD_REMOTE = 1;//在云端增加内容 public static final int SYNC_ACTION_ADD_REMOTE = 1;
public static final int SYNC_ACTION_ADD_LOCAL = 2;//在本地增加内容 public static final int SYNC_ACTION_ADD_LOCAL = 2;
public static final int SYNC_ACTION_DEL_REMOTE = 3;//在云端删除内容 public static final int SYNC_ACTION_DEL_REMOTE = 3;
public static final int SYNC_ACTION_DEL_LOCAL = 4;//在本地删除内容 public static final int SYNC_ACTION_DEL_LOCAL = 4;
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;//将本地内容更新到云端 public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;//将云端内容更新到本地 public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;//同步出现冲突 public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
public static final int SYNC_ACTION_ERROR = 8;//同步出现错误 public static final int SYNC_ACTION_ERROR = 8;
private String mGid; private String mGid;
private String mName; private String mName;
private long mLastModified;//记录最后一次修改时间 private long mLastModified;
private boolean mDeleted; private boolean mDeleted;
@ -55,60 +54,46 @@ public abstract class Node {
mDeleted = false; mDeleted = false;
} }
//根据操作ID获取用于创建节点的 JSON 对象。
public abstract JSONObject getCreateAction(int actionId); public abstract JSONObject getCreateAction(int actionId);
//根据操作ID获取用于更新节点的 JSON 对象。
public abstract JSONObject getUpdateAction(int actionId); public abstract JSONObject getUpdateAction(int actionId);
//根据云端JSON数据设置节点的内容。
public abstract void setContentByRemoteJSON(JSONObject js); public abstract void setContentByRemoteJSON(JSONObject js);
//根据本地JSON数据设置节点的内容。
public abstract void setContentByLocalJSON(JSONObject js); public abstract void setContentByLocalJSON(JSONObject js);
//根据节点的内容生成本地 JSON 对象。
public abstract JSONObject getLocalJSONFromContent(); public abstract JSONObject getLocalJSONFromContent();
//根据游标获取同步操作类型。
public abstract int getSyncAction(Cursor c); public abstract int getSyncAction(Cursor c);
//用于记录用于标识节点的ID
public void setGid(String gid) { public void setGid(String gid) {
this.mGid = gid; this.mGid = gid;
} }
//用于记录节点名称
public void setName(String name) { public void setName(String name) {
this.mName = name; this.mName = name;
} }
//用于记录节点最后的修改时间
public void setLastModified(long lastModified) { public void setLastModified(long lastModified) {
this.mLastModified = lastModified; this.mLastModified = lastModified;
} }
//用于表示节点是否删除
public void setDeleted(boolean deleted) { public void setDeleted(boolean deleted) {
this.mDeleted = deleted; this.mDeleted = deleted;
} }
//返回用于标识节点的ID
public String getGid() { public String getGid() {
return this.mGid; return this.mGid;
} }
//用于返回节点名称
public String getName() { public String getName() {
return this.mName; return this.mName;
} }
//返回节点最后的修改时间
public long getLastModified() { public long getLastModified() {
return this.mLastModified; return this.mLastModified;
} }
//判断节点是否删除
public boolean getDeleted() { public boolean getDeleted() {
return this.mDeleted; return this.mDeleted;
} }

@ -35,7 +35,6 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
//函数用于处理便签数据的数据库操作得到类的简写名称并将其存入字符串TAG中
public class SqlData { public class SqlData {
private static final String TAG = SqlData.class.getSimpleName(); private static final String TAG = SqlData.class.getSimpleName();
@ -58,7 +57,6 @@ public class SqlData {
private ContentResolver mContentResolver; private ContentResolver mContentResolver;
//判断是否直接用content生成是则为true否则为false
private boolean mIsCreate; private boolean mIsCreate;
private long mDataId; private long mDataId;
@ -73,7 +71,6 @@ public class SqlData {
private ContentValues mDiffDataValues; private ContentValues mDiffDataValues;
//函数用于数据的初始化
public SqlData(Context context) { public SqlData(Context context) {
mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver();
mIsCreate = true; mIsCreate = true;
@ -85,7 +82,6 @@ public class SqlData {
mDiffDataValues = new ContentValues(); mDiffDataValues = new ContentValues();
} }
//函数用于数据的初始化
public SqlData(Context context, Cursor c) { public SqlData(Context context, Cursor c) {
mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver();
mIsCreate = false; mIsCreate = false;
@ -93,7 +89,6 @@ public class SqlData {
mDiffDataValues = new ContentValues(); mDiffDataValues = new ContentValues();
} }
//函数用于加载数据
private void loadFromCursor(Cursor c) { private void loadFromCursor(Cursor c) {
mDataId = c.getLong(DATA_ID_COLUMN); mDataId = c.getLong(DATA_ID_COLUMN);
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
@ -102,9 +97,7 @@ public class SqlData {
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
} }
//函数用于设置共享的数据,并提供异常抛出与处理机制
public void setContent(JSONObject js) throws JSONException { public void setContent(JSONObject js) throws JSONException {
//如果传入的JSONObject对象中有DataColumns.ID这一项则设置否则设为INVALID_ID
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
if (mIsCreate || mDataId != dataId) { if (mIsCreate || mDataId != dataId) {
mDiffDataValues.put(DataColumns.ID, dataId); mDiffDataValues.put(DataColumns.ID, dataId);
@ -137,13 +130,11 @@ public class SqlData {
mDataContentData3 = dataContentData3; mDataContentData3 = dataContentData3;
} }
//函数用于获取共享的数据,并提供异常抛出与处理机制
public JSONObject getContent() throws JSONException { public JSONObject getContent() throws JSONException {
if (mIsCreate) { if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet"); Log.e(TAG, "it seems that we haven't created this in database yet");
return null; return null;
} }
//创建JSONObject对象并将相关数据放入其中并返回。
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
js.put(DataColumns.ID, mDataId); js.put(DataColumns.ID, mDataId);
js.put(DataColumns.MIME_TYPE, mDataMimeType); js.put(DataColumns.MIME_TYPE, mDataMimeType);
@ -153,7 +144,6 @@ public class SqlData {
return js; return js;
} }
//函数用于把当前所做的修改保存到数据库
public void commit(long noteId, boolean validateVersion, long version) { public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) { if (mIsCreate) {
@ -193,7 +183,6 @@ public class SqlData {
mIsCreate = false; mIsCreate = false;
} }
//函数用于获取当前id
public long getId() { public long getId() {
return mDataId; return mDataId;
} }

@ -37,13 +37,12 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
//函数用于将得到的类的简写名称存入字符串TAG中
public class SqlNote { public class SqlNote {
private static final String TAG = SqlNote.class.getSimpleName(); private static final String TAG = SqlNote.class.getSimpleName();
private static final int INVALID_ID = -99999; private static final int INVALID_ID = -99999;
//集合了interface NoteColumns中的17个SF常量
public static final String[] PROJECTION_NOTE = new String[] { public static final String[] PROJECTION_NOTE = new String[] {
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
@ -87,7 +86,6 @@ public class SqlNote {
public static final int VERSION_COLUMN = 16; public static final int VERSION_COLUMN = 16;
//下面定义了17个内部变量其中12个可以在content中获得5个需要初始化为0或new
private Context mContext; private Context mContext;
private ContentResolver mContentResolver; private ContentResolver mContentResolver;
@ -124,7 +122,6 @@ public class SqlNote {
private ArrayList<SqlData> mDataList; private ArrayList<SqlData> mDataList;
//构造函数,用于对所有的变量进行初始化
public SqlNote(Context context) { public SqlNote(Context context) {
mContext = context; mContext = context;
mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver();
@ -146,13 +143,10 @@ public class SqlNote {
mDataList = new ArrayList<SqlData>(); mDataList = new ArrayList<SqlData>();
} }
//构造函数
public SqlNote(Context context, Cursor c) { public SqlNote(Context context, Cursor c) {
mContext = context; mContext = context;
//获取内容解析器对象
mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver();
mIsCreate = false; mIsCreate = false;
//从传入的Cursor对象中加载便签数据
loadFromCursor(c); loadFromCursor(c);
mDataList = new ArrayList<SqlData>(); mDataList = new ArrayList<SqlData>();
if (mType == Notes.TYPE_NOTE) if (mType == Notes.TYPE_NOTE)
@ -160,7 +154,6 @@ public class SqlNote {
mDiffNoteValues = new ContentValues(); mDiffNoteValues = new ContentValues();
} }
//构造函数,用于标示构造方式
public SqlNote(Context context, long id) { public SqlNote(Context context, long id) {
mContext = context; mContext = context;
mContentResolver = context.getContentResolver(); mContentResolver = context.getContentResolver();
@ -170,18 +163,16 @@ public class SqlNote {
if (mType == Notes.TYPE_NOTE) if (mType == Notes.TYPE_NOTE)
loadDataContent(); loadDataContent();
mDiffNoteValues = new ContentValues(); mDiffNoteValues = new ContentValues();
} }
//函数作用于从数据库中加载指定ID的便签数据
private void loadFromCursor(long id) { private void loadFromCursor(long id) {
Cursor c = null; Cursor c = null;
//通过ID获得对应的ContentResolver中的cursor
try { try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
new String[] { new String[] {
String.valueOf(id) String.valueOf(id)
}, null); }, null);
//然后加载数据进行初始化
if (c != null) { if (c != null) {
c.moveToNext(); c.moveToNext();
loadFromCursor(c); loadFromCursor(c);
@ -194,7 +185,6 @@ public class SqlNote {
} }
} }
//函数用于从传入的Cursor对象中加载便签数据
private void loadFromCursor(Cursor c) { private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN); mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
@ -210,12 +200,9 @@ public class SqlNote {
mVersion = c.getLong(VERSION_COLUMN); mVersion = c.getLong(VERSION_COLUMN);
} }
//函数用于从数据库中加载与特定便签相关的数据内容
private void loadDataContent() { private void loadDataContent() {
Cursor c = null; Cursor c = null;
//清空mDatalist中的数据以便重新加载新的数据
mDataList.clear(); mDataList.clear();
//使用内容解析器对象mContentResolver调用query()方法查询数据库查询结果将赋值给Cursor对象c再通过检查c是否为空来判断查询是否成功。如果不为空表示查询到了结果。然后通过循环遍历c的每一行数据创建SqlData对象并将其加入mDataList中。
try { try {
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[] { "(note_id=?)", new String[] {
@ -239,16 +226,13 @@ public class SqlNote {
} }
} }
//函数用于设置便签的内容接收一个JSONObject对象作为参数并根据该对象中的数据更新便签的属性。
public boolean setContent(JSONObject js) { public boolean setContent(JSONObject js) {
try { try {
//从JSONObject对象中获取名为note的子对象该子对象包含了便签的各个属性。接着根据便签的类型系统、文件夹、笔记进行不同的处理逻辑。
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
//如果类型是系统,则打印警告信息
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder"); Log.w(TAG, "cannot set system folder");
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 如果类型是文件夹则更新摘要和类型属性。如果是新建笔记或者摘要有变化则将新的摘要值存入mDiffNoteValues集合并更新mSnippet的值。同样的如果是新建便签或者类型有变化则将新的类型值存入mDiffNoteValues集合并更新mType的值。 // for folder we can only update the snnipet and type
String snippet = note.has(NoteColumns.SNIPPET) ? note String snippet = note.has(NoteColumns.SNIPPET) ? note
.getString(NoteColumns.SNIPPET) : ""; .getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) { if (mIsCreate || !mSnippet.equals(snippet)) {
@ -262,8 +246,7 @@ public class SqlNote {
mDiffNoteValues.put(NoteColumns.TYPE, type); mDiffNoteValues.put(NoteColumns.TYPE, type);
} }
mType = type; mType = type;
} //如果类型是笔记,则根据笔记的各个属性更新相应的值 } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
if (mIsCreate || mId != id) { if (mIsCreate || mId != id) {
@ -348,7 +331,6 @@ public class SqlNote {
} }
mOriginParent = originParent; mOriginParent = originParent;
//最后遍历dataArray数组该数组包含了便签的内容数据。对于每个数据对象如果存在ID属性就在mDataList列表中查找对应的SqlData对象如果找到则使用该对象否则创建一个新的SqlData对象并添加到mDataList列表中。然后调用sqlData.setContent(data)将数据对象传递给SqlData对象进行处理。
for (int i = 0; i < dataArray.length(); i++) { for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i); JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null; SqlData sqlData = null;
@ -361,7 +343,7 @@ public class SqlNote {
} }
} }
if (sqlData == null) { if (sqlData == null) {
sqlData = new SqlData(mContext); sqlData = new SqlData(mContext);
mDataList.add(sqlData); mDataList.add(sqlData);
} }
@ -377,19 +359,16 @@ public class SqlNote {
return true; return true;
} }
//函数用于获取便签的内容
public JSONObject getContent() { public JSONObject getContent() {
try { try {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
//如果mIsCreate属性为true表示该便签还未在数据库中创建此时打印警告信息并返回null
if (mIsCreate) { if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet"); Log.e(TAG, "it seems that we haven't created this in database yet");
return null; return null;
} }
JSONObject note = new JSONObject(); JSONObject note = new JSONObject();
//如果便签类型是笔记将便签的各个属性添加到note对象中然后将note对象添加到js对象中。同时创建一个名为dataArray的JSONArray数组并遍历mDataList列表中的每个SqlData对象。对于每个SqlData对象调用getContent获取其内容数据并将数据对象添加到dataArray数组中。
if (mType == Notes.TYPE_NOTE) { if (mType == Notes.TYPE_NOTE) {
note.put(NoteColumns.ID, mId); note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate); note.put(NoteColumns.ALERTED_DATE, mAlertDate);
@ -413,7 +392,7 @@ public class SqlNote {
} }
} }
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} //如果类型是文件夹和系统将ID、类型和摘要属性添加到note对象中然后将note对象添加到js对象中。 else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
note.put(NoteColumns.ID, mId); note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType); note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet); note.put(NoteColumns.SNIPPET, mSnippet);
@ -428,56 +407,45 @@ public class SqlNote {
return null; return null;
} }
//函数用于给当前ID设置父ID
public void setParentId(long id) { public void setParentId(long id) {
mParentId = id; mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id); mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
} }
//函数用于给当前ID设置GTASK_ID,
public void setGtaskId(String gid) { public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
} }
//函数用于给当前ID设置同步ID
public void setSyncId(long syncId) { public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
} }
//函数用于初始化本地修改,即撤销所有当前修改
public void resetLocalModified() { public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
} }
//函数用于获得当前ID
public long getId() { public long getId() {
return mId; return mId;
} }
//函数用于获得当前ID的父ID
public long getParentId() { public long getParentId() {
return mParentId; return mParentId;
} }
//函数用于显示当前便签的摘要
public String getSnippet() { public String getSnippet() {
return mSnippet; return mSnippet;
} }
//函数用于判断是否为笔记属性
public boolean isNoteType() { public boolean isNoteType() {
return mType == Notes.TYPE_NOTE; return mType == Notes.TYPE_NOTE;
} }
//函数用于把对当前便签所做的修改保存到数据库中
public void commit(boolean validateVersion) { public void commit(boolean validateVersion) {
if (mIsCreate) { if (mIsCreate) {
//如果便签的ID为无效值INVALID_ID且mDiffNoteValues集合中包含键为NoteColumns.ID的键值对则将该键值对从集合中移除
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
mDiffNoteValues.remove(NoteColumns.ID); mDiffNoteValues.remove(NoteColumns.ID);
} }
//调用ContentResolver的insert将便签的内容插入到数据库中
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try { try {
mId = Long.valueOf(uri.getPathSegments().get(1)); mId = Long.valueOf(uri.getPathSegments().get(1));
@ -489,14 +457,12 @@ public class SqlNote {
throw new IllegalStateException("Create thread id failed"); throw new IllegalStateException("Create thread id failed");
} }
//如果便签类型为笔记则遍历mDataList列表中的每个SqlData对象并调用commit将对应的数据保存到数据库中
if (mType == Notes.TYPE_NOTE) { if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) { for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1); sqlData.commit(mId, false, -1);
} }
} }
} else { } else {
//如果便签的ID无效或为根文件夹或通话记录文件夹则抛出IllegalStateException异常提示正在尝试使用无效ID更新笔记
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note"); Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id"); throw new IllegalStateException("Try to update note with invalid id");
@ -504,7 +470,6 @@ public class SqlNote {
if (mDiffNoteValues.size() > 0) { if (mDiffNoteValues.size() > 0) {
mVersion ++; mVersion ++;
int result = 0; int result = 0;
//如果validateVersion值为false则直接调用update更新内容到数据库中否则先查询数据库中该便签的版本号如果查询得到的版本号小于等于mVersion则更新笔记到数据库中否则抛出异常提示提交失败
if (!validateVersion) { if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] { + NoteColumns.ID + "=?)", new String[] {
@ -522,7 +487,6 @@ public class SqlNote {
} }
} }
//如果便签类型为笔记则遍历mDataList列表中的每个SqlData对象调用commit将对应的数据保存到数据库中
if (mType == Notes.TYPE_NOTE) { if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) { for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion); sqlData.commit(mId, validateVersion, mVersion);
@ -531,12 +495,10 @@ public class SqlNote {
} }
// refresh local info // refresh local info
//调用loadFromCursor重新加载从数据库中获取的数据并调用loadDataContent重新加载便签的附加数据以确保对便签修改的完整性和正确性
loadFromCursor(mId); loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE) if (mType == Notes.TYPE_NOTE)
loadDataContent(); loadDataContent();
//最后清空mDiffNoteValues集合将mIsCreate属性设置为false表示提交操作已完成
mDiffNoteValues.clear(); mDiffNoteValues.clear();
mIsCreate = false; mIsCreate = false;
} }

@ -33,7 +33,7 @@ import java.util.ArrayList;
public class TaskList extends Node { public class TaskList extends Node {
private static final String TAG = TaskList.class.getSimpleName(); private static final String TAG = TaskList.class.getSimpleName();
private int mIndex;//当前TaskList的指针 private int mIndex;
private ArrayList<Task> mChildren; private ArrayList<Task> mChildren;
@ -43,7 +43,6 @@ public class TaskList extends Node {
mIndex = 1; mIndex = 1;
} }
//生成并返回一个半酣了一定数据的JSONObject实体
public JSONObject getCreateAction(int actionId) { public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
@ -75,7 +74,6 @@ public class TaskList extends Node {
return js; return js;
} }
//更新JSONObject实体
public JSONObject getUpdateAction(int actionId) { public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
@ -105,7 +103,6 @@ public class TaskList extends Node {
return js; return js;
} }
//通过远程JSON对象实现远程同步
public void setContentByRemoteJSON(JSONObject js) { public void setContentByRemoteJSON(JSONObject js) {
if (js != null) { if (js != null) {
try { try {
@ -132,7 +129,6 @@ public class TaskList extends Node {
} }
} }
//通过本地JSON对象实现本地同步
public void setContentByLocalJSON(JSONObject js) { public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
@ -161,7 +157,6 @@ public class TaskList extends Node {
} }
} }
//将本地内容转换为JSONObject
public JSONObject getLocalJSONFromContent() { public JSONObject getLocalJSONFromContent() {
try { try {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
@ -188,7 +183,6 @@ public class TaskList extends Node {
} }
} }
//提供同步任务,确定同步操作的类型。
public int getSyncAction(Cursor c) { public int getSyncAction(Cursor c) {
try { try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
@ -226,7 +220,6 @@ public class TaskList extends Node {
return mChildren.size(); return mChildren.size();
} }
//在当前任务表末尾添加新的任务
public boolean addChildTask(Task task) { public boolean addChildTask(Task task) {
boolean ret = false; boolean ret = false;
if (task != null && !mChildren.contains(task)) { if (task != null && !mChildren.contains(task)) {
@ -241,7 +234,6 @@ public class TaskList extends Node {
return ret; return ret;
} }
//在当前任务表的指定位置添加新的任务
public boolean addChildTask(Task task, int index) { public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) { if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index"); Log.e(TAG, "add child task: invalid index");
@ -268,7 +260,6 @@ public class TaskList extends Node {
return true; return true;
} }
//删除任务表中的一个任务
public boolean removeChildTask(Task task) { public boolean removeChildTask(Task task) {
boolean ret = false; boolean ret = false;
int index = mChildren.indexOf(task); int index = mChildren.indexOf(task);
@ -290,7 +281,6 @@ public class TaskList extends Node {
return ret; return ret;
} }
//移动任务表中的一个任务
public boolean moveChildTask(Task task, int index) { public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) { if (index < 0 || index >= mChildren.size()) {
@ -309,7 +299,6 @@ public class TaskList extends Node {
return (removeChildTask(task) && addChildTask(task, index)); return (removeChildTask(task) && addChildTask(task, index));
} }
//寻找一个任务
public Task findChildTaskByGid(String gid) { public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) { for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i); Task t = mChildren.get(i);
@ -320,12 +309,10 @@ public class TaskList extends Node {
return null; return null;
} }
//返指定index的任务
public int getChildTaskIndex(Task task) { public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task); return mChildren.indexOf(task);
} }
//返回指定index的任务
public Task getChildTaskByIndex(int index) { public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) { if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index"); Log.e(TAG, "getTaskByIndex: invalid index");
@ -334,7 +321,6 @@ public class TaskList extends Node {
return mChildren.get(index); return mChildren.get(index);
} }
//返回指定gid的任务
public Task getChilTaskByGid(String gid) { public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) { for (Task task : mChildren) {
if (task.getGid().equals(gid)) if (task.getGid().equals(gid))

@ -28,7 +28,7 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity; import net.micode.notes.ui.NotesPreferenceActivity;
//异步操作类实现GTask的异步操作过程
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> { public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
@ -63,7 +63,6 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}); });
} }
//向用户提示当前同步的状态
private void showNotification(int tickerId, String content) { private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis()); .getString(tickerId), System.currentTimeMillis());
@ -72,23 +71,22 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
PendingIntent pendingIntent; PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) { if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0); //如果同步不成功那么从系统取得一个用于启动一个NotesPreferenceActivity的PendingIntent对象 NotesPreferenceActivity.class), 0);
} else { } else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0); //如果同步成功那么从系统取得一个用于启动一个NotesListActivity的PendingIntent对象 NotesListActivity.class), 0);
} }
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent); pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
} }
//完成任务的主要工作
@Override @Override
protected Integer doInBackground(Void... unused) { protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));//利用getString,将把 NotesPreferenceActivity.getSyncAccountName(mContext))的字符串内容传进sync_progress_login中 .getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this); //进行后台同步具体操作 return mTaskManager.sync(mContext, this);
} }
@Override @Override
@ -100,7 +98,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
} }
@Override @Override
protected void onPostExecute(Integer result) { //用于在执行完后台任务后更新UI,显示结果 protected void onPostExecute(Integer result) {
if (result == GTaskManager.STATE_SUCCESS) { if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString( showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount())); R.string.success_sync_account, mTaskManager.getSyncAccount()));
@ -112,11 +110,11 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) { } else if (result == GTaskManager.STATE_SYNC_CANCELLED) {
showNotification(R.string.ticker_cancel, mContext showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled)); .getString(R.string.error_sync_cancelled));
}//几种不同情况下的结果显示 }
if (mOnCompleteListener != null) { if (mOnCompleteListener != null) {
new Thread(new Runnable() { new Thread(new Runnable() {
public void run() {//完成后的操作使用onComplete()将所有值都重新初始化,相当于完成一次操作 public void run() {
mOnCompleteListener.onComplete(); mOnCompleteListener.onComplete();
} }
}).start(); }).start();

@ -42,7 +42,6 @@ public class GTaskSyncService extends Service {
private static String mSyncProgress = ""; private static String mSyncProgress = "";
//开始一个同步的工作
private void startSync() { private void startSync() {
if (mSyncTask == null) { if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
@ -53,11 +52,10 @@ public class GTaskSyncService extends Service {
} }
}); });
sendBroadcast(""); sendBroadcast("");
mSyncTask.execute(); ////这个函数让任务是以单线程队列方式或线程池队列方式运行 mSyncTask.execute();
} }
} }
//取消同步
private void cancelSync() { private void cancelSync() {
if (mSyncTask != null) { if (mSyncTask != null) {
mSyncTask.cancelSync(); mSyncTask.cancelSync();
@ -74,7 +72,6 @@ public class GTaskSyncService extends Service {
Bundle bundle = intent.getExtras(); Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
//两种情况,开始同步或者取消同步
case ACTION_START_SYNC: case ACTION_START_SYNC:
startSync(); startSync();
break; break;
@ -89,7 +86,6 @@ public class GTaskSyncService extends Service {
return super.onStartCommand(intent, flags, startId); return super.onStartCommand(intent, flags, startId);
} }
//在没有内存的情况下如果存在service则结束掉service
@Override @Override
public void onLowMemory() { public void onLowMemory() {
if (mSyncTask != null) { if (mSyncTask != null) {
@ -101,16 +97,14 @@ public class GTaskSyncService extends Service {
return null; return null;
} }
//发送同步的相关通知
public void sendBroadcast(String msg) { public void sendBroadcast(String msg) {
mSyncProgress = msg; mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); //创建一个新的Intent Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent); sendBroadcast(intent);
} }
//执行开始同步的操作
public static void startSync(Activity activity) { public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity); GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class); Intent intent = new Intent(activity, GTaskSyncService.class);
@ -118,19 +112,16 @@ public class GTaskSyncService extends Service {
activity.startService(intent); activity.startService(intent);
} }
//执行取消同步的操作
public static void cancelSync(Context context) { public static void cancelSync(Context context) {
Intent intent = new Intent(context, GTaskSyncService.class); Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
context.startService(intent); context.startService(intent);
} }
//判断是否在进行同步
public static boolean isSyncing() { public static boolean isSyncing() {
return mSyncTask != null; return mSyncTask != null;
} }
//获取当前进度的信息
public static String getProgressString() { public static String getProgressString() {
return mSyncProgress; return mSyncProgress;
} }

@ -1,16 +1,48 @@
/*
* 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; package net.micode.notes.tool;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class BackupUtils { public class BackupUtils {
private static final String TAG = "BackupUtils"; private static final String TAG = "BackupUtils";
// Singleton stuff // Singleton stuff
private static BackupUtils sInstance; //类里面为什么可以定义自身类的对象? private static BackupUtils sInstance;
public static synchronized BackupUtils getInstance(Context context) { public static synchronized BackupUtils getInstance(Context context) {
//ynchronized 关键字,代表这个方法加锁,相当于不管哪一个线程例如线程A
//运行到这个方法时,都要检查有没有其它线程B或者C、 D等正在用这个方法(或者该类的其他同步方法)有的话要等正在使用synchronized方法的线程B或者C 、D运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。
//它包括两种用法synchronized 方法和 synchronized 块。
if (sInstance == null) { if (sInstance == null) {
//如果当前备份不存在,则新声明一个
sInstance = new BackupUtils(context); sInstance = new BackupUtils(context);
} }
return sInstance; return sInstance;
@ -20,24 +52,24 @@ public class BackupUtils {
* Following states are signs to represents backup or restore * Following states are signs to represents backup or restore
* status * status
*/ */
// Currently, the sdcard is not mounted SD卡没有被装入手机 // Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0; public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist 备份文件夹不存在 // The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs 数据已被破坏,可能被修改 // The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2; public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails 超时异常 // Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3; public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success 成功存储 // Backup or restore success
public static final int STATE_SUCCESS = 4; public static final int STATE_SUCCESS = 4;
private TextExport mTextExport; private TextExport mTextExport;
private BackupUtils(Context context) { //初始化函数 private BackupUtils(Context context) {
mTextExport = new TextExport(context); mTextExport = new TextExport(context);
} }
private static boolean externalStorageAvailable() { //外部存储功能是否可用 private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
} }
@ -96,11 +128,11 @@ public class BackupUtils {
public TextExport(Context context) { public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context; mContext = context;
mFileName = ""; //为什么为空? mFileName = "";
mFileDirectory = ""; mFileDirectory = "";
} }
private String getFormat(int id) { //获取文本的组成部分 private String getFormat(int id) {
return TEXT_FORMAT[id]; return TEXT_FORMAT[id];
} }
@ -108,22 +140,22 @@ public class BackupUtils {
* Export the folder identified by folder id to text * Export the folder identified by folder id to text
*/ */
private void exportFolderToText(String folderId, PrintStream ps) { private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder 通过查询parent id是文件夹id的note来选出制定ID文件夹下的Note // Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId folderId
}, null); }, null);
if (notesCursor != null) { if (notesCursor != null) {
if (notesCursor.moveToFirst()) { if (notesCursor.moveToFirst()) {
do { do {
// Print note's last modified date ps里面保存有这份note的日期 // Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm), mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note // Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID); String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps); //将文件导出到text exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext()); } while (notesCursor.moveToNext());
} }
notesCursor.close(); notesCursor.close();
@ -136,10 +168,10 @@ public class BackupUtils {
private void exportNoteToText(String noteId, PrintStream ps) { private void exportNoteToText(String noteId, PrintStream ps) {
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId noteId
}, null); }, null);
if (dataCursor != null) { //利用光标来扫描内容区别为callnote和note两种靠ps.printline输出 if (dataCursor != null) {
if (dataCursor.moveToFirst()) { if (dataCursor.moveToFirst()) {
do { do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
@ -149,7 +181,7 @@ public class BackupUtils {
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT); String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) { //判断是否为空字符 if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber)); phoneNumber));
} }
@ -186,7 +218,7 @@ public class BackupUtils {
/** /**
* Note will be exported as text which is user readable * Note will be exported as text which is user readable
*/ */
public int exportToText() { //总函数调用上面的exportFolder和exportNote public int exportToText() {
if (!externalStorageAvailable()) { if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted"); Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED; return STATE_SD_CARD_UNMOUONTED;
@ -197,7 +229,7 @@ public class BackupUtils {
Log.e(TAG, "get print stream error"); Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR; return STATE_SYSTEM_ERROR;
} }
// First export folder and its notes 导出文件夹,就是导出里面包含的便签 // First export folder and its notes
Cursor folderCursor = mContext.getContentResolver().query( Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI, Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NOTE_PROJECTION,
@ -225,7 +257,7 @@ public class BackupUtils {
folderCursor.close(); folderCursor.close();
} }
// Export notes in root's folder 将根目录里的便签导出(由于不属于任何文件夹,因此无法通过文件夹导出来实现这一部分便签的导出) // Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query( Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI, Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NOTE_PROJECTION,
@ -265,7 +297,7 @@ public class BackupUtils {
PrintStream ps = null; PrintStream ps = null;
try { try {
FileOutputStream fos = new FileOutputStream(file); FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos); //将ps输出流输出到特定的文件目的就是导出到文件而不是直接输出 ps = new PrintStream(fos);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
@ -282,16 +314,16 @@ public class BackupUtils {
*/ */
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory()); //外部SD卡的存储路径 sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId)); //文件的存储路径 sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString()); //filedir应该就是用来存储路径信息 File filedir = new File(sb.toString());
sb.append(context.getString( sb.append(context.getString(
fileNameFormatResId, fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd), DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis()))); System.currentTimeMillis())));
File file = new File(sb.toString()); File file = new File(sb.toString());
try { //如果这些文件不存在,则新建 try {
if (!filedir.exists()) { if (!filedir.exists()) {
filedir.mkdir(); filedir.mkdir();
} }
@ -304,8 +336,9 @@ public class BackupUtils {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
// try catch 异常处理
return null; return null;
} }
} }

@ -1,8 +1,43 @@
/*
* 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; package net.micode.notes.tool;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
public class DataUtils { public class DataUtils {
public static final String TAG = "DataUtils"; public static final String TAG = "DataUtils";
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) { //直接删除多个笔记 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) { if (ids == null) {
Log.d(TAG, "the ids is null"); Log.d(TAG, "the ids is null");
return true; return true;
@ -12,19 +47,18 @@ public class DataUtils {
return true; return true;
} }
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); //提供一个任务列表 ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) { for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) { if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root"); Log.e(TAG, "Don't delete system folder root");
continue; continue;
} //如果发现是根文件夹,则不删除 }
ContentProviderOperation.Builder builder = ContentProviderOperation ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); //用newDelete实现删除功能 .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build()); // operationList.add(builder.build());
} }
try { try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);//主机名或叫Authority用于唯一标识这个ContentProvider外部调用者可以根据这个标识来找到它。 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
//数据库事务,数据库事务是由一组数据库操作序列组成,事务作为一个整体被执行
if (results == null || results.length == 0 || results[0] == null) { if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString()); Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false; return false;
@ -43,11 +77,11 @@ public class DataUtils {
values.put(NoteColumns.PARENT_ID, desFolderId); values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1); values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); //对需要移动的便签进行数据更新然后用update实现 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
} }
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) {
Log.d(TAG, "the ids is null"); Log.d(TAG, "the ids is null");
return true; return true;
@ -56,14 +90,14 @@ public class DataUtils {
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) { for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); //通过withAppendedId方法为该Uri加上ID .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId); builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build()); operationList.add(builder.build());
}//将ids里包含的每一列的数据逐次加入到operationList中等待最后的批量处理 }
try { try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); //applyBatch一次性处理一个操作列表 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) { if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString()); Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false; return false;
@ -85,7 +119,7 @@ public class DataUtils {
new String[] { "COUNT(*)" }, new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null); //筛选条件源文件不为trash folder null);
int count = 0; int count = 0;
if(cursor != null) { if(cursor != null) {
@ -103,15 +137,15 @@ public class DataUtils {
} }
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), //通过withAppendedId方法为该Uri加上ID Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)}, new String [] {String.valueOf(type)},
null); //查询条件type符合且不属于垃圾文件夹 null);
boolean exist = false; boolean exist = false;
if (cursor != null) { if (cursor != null) {
if (cursor.getCount() > 0) {//用getcount函数判断cursor是否为空 if (cursor.getCount() > 0) {
exist = true; exist = true;
} }
cursor.close(); cursor.close();
@ -150,10 +184,9 @@ public class DataUtils {
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 +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?", " AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null); new String[] { name }, null);
//通过名字查询文件是否存在
boolean exist = false; boolean exist = false;
if(cursor != null) { if(cursor != null) {
if(cursor.getCount() > 0) { if(cursor.getCount() > 0) {
@ -169,7 +202,7 @@ public class DataUtils {
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?", NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) }, new String[] { String.valueOf(folderId) },
null); //查询条件父ID是传入的folderId; null);
HashSet<AppWidgetAttribute> set = null; HashSet<AppWidgetAttribute> set = null;
if (c != null) { if (c != null) {
@ -178,13 +211,13 @@ public class DataUtils {
do { do {
try { try {
AppWidgetAttribute widget = new AppWidgetAttribute(); AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0); //0对应的NoteColumns.WIDGET_ID widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1); //1对应的NoteColumns.WIDGET_TYPE widget.widgetType = c.getInt(1);
set.add(widget); set.add(widget);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
} }
} while (c.moveToNext()); //查询下一条 } while (c.moveToNext());
} }
c.close(); c.close();
} }
@ -214,15 +247,14 @@ public class DataUtils {
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 },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)", + CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null); null);
//通过数据库操作查询条件是callDate和phoneNumber匹配传入参数的值
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
try { try {
return cursor.getLong(0); //0对应的CallNote.NOTE_ID return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString()); Log.e(TAG, "Get call note id fails " + e.toString());
} }
@ -237,7 +269,7 @@ public class DataUtils {
new String [] { NoteColumns.SNIPPET }, new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?", NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)}, new String [] { String.valueOf(noteId)},
null);//查询条件noteId null);
if (cursor != null) { if (cursor != null) {
String snippet = ""; String snippet = "";
@ -249,7 +281,8 @@ public class DataUtils {
} }
throw new IllegalArgumentException("Note is not found with id: " + noteId); throw new IllegalArgumentException("Note is not found with id: " + noteId);
} }
public static String getFormattedSnippet(String snippet) { //对字符串进行格式处理,将字符串两头的空格去掉,同时将换行符去掉
public static String getFormattedSnippet(String snippet) {
if (snippet != null) { if (snippet != null) {
snippet = snippet.trim(); snippet = snippet.trim();
int index = snippet.indexOf('\n'); int index = snippet.indexOf('\n');
@ -259,5 +292,4 @@ public class DataUtils {
} }
return snippet; return snippet;
} }
} }

@ -1,7 +1,21 @@
//简介定义了很多的静态字符串目的就是为了提供jsonObject中相应字符串的"key"。把这些静态的定义单独写到了一个类里面,这是非常好的编程规范 /*
* 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; package net.micode.notes.tool;
//这个类就是定义了一堆static string实际就是为jsonObject提供Key把这些定义全部写到一个类里方便查看管理是一个非常好的编程习惯
public class GTaskStringUtils { public class GTaskStringUtils {
public final static String GTASK_JSON_ACTION_ID = "action_id"; public final static String GTASK_JSON_ACTION_ID = "action_id";

@ -1,20 +1,27 @@
package net.micode.notes.tool; /*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
/*使 *
* R.java * Licensed under the Apache License, Version 2.0 (the "License");
* R.id * you may not use this file except in compliance with the License.
* R.drawable 使 * You may obtain a copy of the License at
* R.layout
* R.menu
* R.String
* R.style 使
* idgetXXX
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* @BG_DEFAULT_COLOR *
* BG_DEFAULT_FONT_SIZE * 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.Context;
import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
public class ResourceParser { public class ResourceParser {
public static final int YELLOW = 0; public static final int YELLOW = 0;
@ -34,19 +41,19 @@ public class ResourceParser {
public static class NoteBgResources { public static class NoteBgResources {
private final static int [] BG_EDIT_RESOURCES = new int [] { private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow, R.drawable.edit_yellow,
R.drawable.edit_blue, R.drawable.edit_blue,
R.drawable.edit_white, R.drawable.edit_white,
R.drawable.edit_green, R.drawable.edit_green,
R.drawable.edit_red R.drawable.edit_red
}; };
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow, R.drawable.edit_title_yellow,
R.drawable.edit_title_blue, R.drawable.edit_title_blue,
R.drawable.edit_title_white, R.drawable.edit_title_white,
R.drawable.edit_title_green, R.drawable.edit_title_green,
R.drawable.edit_title_red R.drawable.edit_title_red
}; };
public static int getNoteBgResource(int id) { public static int getNoteBgResource(int id) {
@ -57,7 +64,7 @@ public class ResourceParser {
return BG_EDIT_TITLE_RESOURCES[id]; return BG_EDIT_TITLE_RESOURCES[id];
} }
} }
//直接获取默认的背景颜色。看不太懂这个PREFERENCE_SET_BG_COLOR_KEY是个final string,也就是说getBoolean肯定执行else为什么要这么写
public static int getDefaultBgId(Context context) { public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
@ -69,35 +76,35 @@ public class ResourceParser {
public static class NoteItemBgResources { public static class NoteItemBgResources {
private final static int [] BG_FIRST_RESOURCES = new int [] { private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up, R.drawable.list_yellow_up,
R.drawable.list_blue_up, R.drawable.list_blue_up,
R.drawable.list_white_up, R.drawable.list_white_up,
R.drawable.list_green_up, R.drawable.list_green_up,
R.drawable.list_red_up R.drawable.list_red_up
}; };
private final static int [] BG_NORMAL_RESOURCES = new int [] { private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle, R.drawable.list_yellow_middle,
R.drawable.list_blue_middle, R.drawable.list_blue_middle,
R.drawable.list_white_middle, R.drawable.list_white_middle,
R.drawable.list_green_middle, R.drawable.list_green_middle,
R.drawable.list_red_middle R.drawable.list_red_middle
}; };
private final static int [] BG_LAST_RESOURCES = new int [] { private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down, R.drawable.list_yellow_down,
R.drawable.list_blue_down, R.drawable.list_blue_down,
R.drawable.list_white_down, R.drawable.list_white_down,
R.drawable.list_green_down, R.drawable.list_green_down,
R.drawable.list_red_down, R.drawable.list_red_down,
}; };
private final static int [] BG_SINGLE_RESOURCES = new int [] { private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single, R.drawable.list_yellow_single,
R.drawable.list_blue_single, R.drawable.list_blue_single,
R.drawable.list_white_single, R.drawable.list_white_single,
R.drawable.list_green_single, R.drawable.list_green_single,
R.drawable.list_red_single R.drawable.list_red_single
}; };
public static int getNoteBgFirstRes(int id) { public static int getNoteBgFirstRes(int id) {
@ -123,11 +130,11 @@ public class ResourceParser {
public static class WidgetBgResources { public static class WidgetBgResources {
private final static int [] BG_2X_RESOURCES = new int [] { private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow, R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue, R.drawable.widget_2x_blue,
R.drawable.widget_2x_white, R.drawable.widget_2x_white,
R.drawable.widget_2x_green, R.drawable.widget_2x_green,
R.drawable.widget_2x_red, R.drawable.widget_2x_red,
}; };
public static int getWidget2xBgResource(int id) { public static int getWidget2xBgResource(int id) {
@ -135,11 +142,11 @@ public class ResourceParser {
} }
private final static int [] BG_4X_RESOURCES = new int [] { private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow, R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue, R.drawable.widget_4x_blue,
R.drawable.widget_4x_white, R.drawable.widget_4x_white,
R.drawable.widget_4x_green, R.drawable.widget_4x_green,
R.drawable.widget_4x_red R.drawable.widget_4x_red
}; };
public static int getWidget4xBgResource(int id) { public static int getWidget4xBgResource(int id) {
@ -149,13 +156,12 @@ public class ResourceParser {
public static class TextAppearanceResources { public static class TextAppearanceResources {
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal, R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium, R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge, R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper R.style.TextAppearanceSuper
}; };
//这里有一个容错的函数防止输入的id大于资源总量若如此则自动返回默认的设置结果
public static int getTexAppearanceResource(int id) { public static int getTexAppearanceResource(int id) {
/** /**
* HACKME: Fix bug of store the resource id in shared preference. * HACKME: Fix bug of store the resource id in shared preference.

File diff suppressed because it is too large Load Diff

@ -14,273 +14,202 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.text.Layout; import android.text.Layout;
import android.text.Selection; import android.text.Selection;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener; import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.EditText; import android.widget.EditText;
import net.micode.notes.R; import net.micode.notes.R;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
//继承edittext设置便签设置文本框 public class NoteEditText extends EditText {
public class NoteEditText extends EditText { private static final String TAG = "NoteEditText";
private static final String TAG = "NoteEditText"; private int mIndex;
private int mIndex; private int mSelectionStartBeforeDelete;
private int mSelectionStartBeforeDelete;
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_TEL = "tel:" ; private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_HTTP = "http:" ; private static final String SCHEME_EMAIL = "mailto:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
///建立一个字符和整数的hash表用于链接电话网站还有邮箱 static {
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>(); sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
static { sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); }
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
} public interface OnTextViewChangeListener {
/**
/** * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* Call by the {@link NoteEditActivity} to delete or add edit text * and the text is null
*/ */
//在NoteEditActivity中删除或添加文本的操作可以看做是一个文本是否被变的标记英文注释已说明的很清楚 void onEditTextDelete(int index, String text);
public interface OnTextViewChangeListener {
/** /**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* and the text is null * happen
*/ */
//处理删除按键时的操作 void onEditTextEnter(int index, String text);
void onEditTextDelete(int index, String text);
/**
/** * Hide or show item option when text change
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} */
* happen void onTextChange(int index, boolean hasText);
*/ }
//处理进入按键时的操作
void onEditTextEnter(int index, String text); private OnTextViewChangeListener mOnTextViewChangeListener;
/** public NoteEditText(Context context) {
* Hide or show item option when text change super(context, null);
*/ mIndex = 0;
void onTextChange(int index, boolean hasText); }
}
public void setIndex(int index) {
private OnTextViewChangeListener mOnTextViewChangeListener; mIndex = index;
}
//根据context设置文本
public NoteEditText(Context context) {
super(context, null);//用super引用父类变量 public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mIndex = 0; mOnTextViewChangeListener = listener;
} }
//设置当前光标 public NoteEditText(Context context, AttributeSet attrs) {
public void setIndex(int index) { super(context, attrs, android.R.attr.editTextStyle);
mIndex = index; }
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
//初始化文本修改标记 super(context, attrs, defStyle);
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { // TODO Auto-generated construct or stub
mOnTextViewChangeListener = listener; }
}
public boolean onTouchEvent(MotionEvent event) {
//AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性 switch (event.getAction()) {
//初始化便签 case MotionEvent.ACTION_DOWN:
public NoteEditText(Context context, AttributeSet attrs) { int x = (int) event.getX();
super(context, attrs, android.R.attr.editTextStyle); int y = (int) event.getY();
} x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
// 根据defstyle自动初始化 x += getScrollX();
public NoteEditText(Context context, AttributeSet attrs, int defStyle) { y += getScrollY();
super(context, attrs, defStyle);
// TODO Auto-generated construct or stub Layout layout = getLayout();
} int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
@Override
//view里的函数处理手机屏幕的所有事件 Selection.setSelection(getText(), off);
/*event break;
*/ }
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) { return super.onTouchEvent(event);
//重写了需要处理屏幕被按下的事件 }
case MotionEvent.ACTION_DOWN:
//跟新当前坐标值 @Override
int x = (int) event.getX();
int y = (int) event.getY(); public boolean onKeyDown(int keyCode, KeyEvent event) {
x -= getTotalPaddingLeft(); switch (keyCode) {
y -= getTotalPaddingTop(); case KeyEvent.KEYCODE_ENTER:
x += getScrollX(); if (mOnTextViewChangeListener != null) {
y += getScrollY(); return false;
}
//用布局控件layout根据x,y的新值设置新的位置 break;
Layout layout = getLayout(); case KeyEvent.KEYCODE_DEL:
int line = layout.getLineForVertical(y); mSelectionStartBeforeDelete = getSelectionStart();
int off = layout.getOffsetForHorizontal(line, x); break;
default:
//更新光标新的位置 break;
Selection.setSelection(getText(), off); }
break; return super.onKeyDown(keyCode, event);
} }
return super.onTouchEvent(event); @Override
} public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
@Override case KeyEvent.KEYCODE_DEL:
/* if (mOnTextViewChangeListener != null) {
* if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
* mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
*/ return true;
public boolean onKeyDown(int keyCode, KeyEvent event) { }
switch (keyCode) { } else {
//根据按键的 Unicode 编码值来处理 Log.d(TAG, "OnTextViewChangeListener was not seted");
case KeyEvent.KEYCODE_ENTER: }
//“进入”按键 break;
if (mOnTextViewChangeListener != null) { case KeyEvent.KEYCODE_ENTER:
return false; if (mOnTextViewChangeListener != null) {
} int selectionStart = getSelectionStart();
break; String text = getText().subSequence(selectionStart, length()).toString();
case KeyEvent.KEYCODE_DEL: setText(getText().subSequence(0, selectionStart));
//“删除”按键 mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
mSelectionStartBeforeDelete = getSelectionStart(); } else {
break; Log.d(TAG, "OnTextViewChangeListener was not seted");
default: }
break; break;
} default:
//继续执行父类的其他点击事件 break;
return super.onKeyDown(keyCode, event); }
} return super.onKeyUp(keyCode, event);
}
@Override
/* @Override
* protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
* if (mOnTextViewChangeListener != null) {
*/ if (!focused && TextUtils.isEmpty(getText())) {
public boolean onKeyUp(int keyCode, KeyEvent event) { mOnTextViewChangeListener.onTextChange(mIndex, false);
switch(keyCode) { } else {
//根据按键的 Unicode 编码值来处理有删除和进入2种操作 mOnTextViewChangeListener.onTextChange(mIndex, true);
case KeyEvent.KEYCODE_DEL: }
if (mOnTextViewChangeListener != null) { }
//若是被修改过 super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (0 == mSelectionStartBeforeDelete && mIndex != 0) { }
//若之前有被修改并且文档不为空
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); @Override
//利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除 protected void onCreateContextMenu(ContextMenu menu) {
return true; if (getText() instanceof Spanned) {
} int selStart = getSelectionStart();
} else { int selEnd = getSelectionEnd();
Log.d(TAG, "OnTextViewChangeListener was not seted");
//其他情况报错,文档的改动监听器并没有建立 int min = Math.min(selStart, selEnd);
} int max = Math.max(selStart, selEnd);
break;
case KeyEvent.KEYCODE_ENTER: final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
//同上也是分为监听器是否建立2种情况 if (urls.length == 1) {
if (mOnTextViewChangeListener != null) { int defaultResId = 0;
int selectionStart = getSelectionStart(); for(String schema: sSchemaActionResMap.keySet()) {
//获取当前位置 if(urls[0].getURL().indexOf(schema) >= 0) {
String text = getText().subSequence(selectionStart, length()).toString(); defaultResId = sSchemaActionResMap.get(schema);
//获取当前文本 break;
setText(getText().subSequence(0, selectionStart)); }
//根据获取的文本设置当前文本 }
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
//当{@link KeyEvent#KEYCODE_ENTER}添加新文本 if (defaultResId == 0) {
} else { defaultResId = R.string.note_link_other;
Log.d(TAG, "OnTextViewChangeListener was not seted"); }
//其他情况报错,文档的改动监听器并没有建立
} menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
break; new OnMenuItemClickListener() {
default: public boolean onMenuItemClick(MenuItem item) {
break; // goto a new intent
} urls[0].onClick(NoteEditText.this);
//继续执行父类的其他按键弹起的事件 return true;
return super.onKeyUp(keyCode, event); }
} });
}
@Override }
/* super.onCreateContextMenu(menu);
* }
* }
* focusedViewFocusedtruefalse
direction
RectViewnull
*/
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
//若监听器已经建立
if (!focused && TextUtils.isEmpty(getText())) {
//获取到焦点并且文本不为空
mOnTextViewChangeListener.onTextChange(mIndex, false);
//mOnTextViewChangeListener子函数置false隐藏事件选项
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
//mOnTextViewChangeListener子函数置true显示事件选项
}
}
//继续执行父类的其他焦点变化的事件
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
/*
*
*
*/
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
//有文本存在
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
//获取文本开始和结尾位置
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
//获取开始到结尾的最大值和最小值
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
//设置url的信息的范围值
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
//获取计划表中所有的key值
if(urls[0].getURL().indexOf(schema) >= 0) {
//若url可以添加则在添加后将defaultResId置为key所映射的值
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
//defaultResId == 0则说明url并没有添加任何东西所以置为连接其他SchemaActionResMap的值
defaultResId = R.string.note_link_other;
}
//建立菜单
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
//新建按键监听器
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
//根据相应的文本设置菜单的按键
return true;
}
});
}
}
//继续执行父类的其他菜单创建的事件
super.onCreateContextMenu(menu);
}
}

@ -14,217 +14,214 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.text.TextUtils; import android.text.TextUtils;
import net.micode.notes.data.Contact; import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.DataUtils;
public class NoteItemData { public class NoteItemData {
static final String [] PROJECTION = new String [] { static final String [] PROJECTION = new String [] {
NoteColumns.ID, NoteColumns.ID,
NoteColumns.ALERTED_DATE, NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.CREATED_DATE,
NoteColumns.HAS_ATTACHMENT, NoteColumns.HAS_ATTACHMENT,
NoteColumns.MODIFIED_DATE, NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT, NoteColumns.NOTES_COUNT,
NoteColumns.PARENT_ID, NoteColumns.PARENT_ID,
NoteColumns.SNIPPET, NoteColumns.SNIPPET,
NoteColumns.TYPE, NoteColumns.TYPE,
NoteColumns.WIDGET_ID, NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE, NoteColumns.WIDGET_TYPE,
}; };
//常量标记和数据就不一一标记了,意义翻译基本就知道
private static final int ID_COLUMN = 0; private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1; private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2; private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3; private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4; private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5; private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6; private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7; private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8; private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9; private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10; private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11; private static final int WIDGET_TYPE_COLUMN = 11;
private long mId; private long mId;
private long mAlertDate; private long mAlertDate;
private int mBgColorId; private int mBgColorId;
private long mCreatedDate; private long mCreatedDate;
private boolean mHasAttachment; private boolean mHasAttachment;
private long mModifiedDate; private long mModifiedDate;
private int mNotesCount; private int mNotesCount;
private long mParentId; private long mParentId;
private String mSnippet; private String mSnippet;
private int mType; private int mType;
private int mWidgetId; private int mWidgetId;
private int mWidgetType; private int mWidgetType;
private String mName; private String mName;
private String mPhoneNumber; private String mPhoneNumber;
private boolean mIsLastItem; private boolean mIsLastItem;
private boolean mIsFirstItem; private boolean mIsFirstItem;
private boolean mIsOnlyOneItem; private boolean mIsOnlyOneItem;
private boolean mIsOneNoteFollowingFolder; private boolean mIsOneNoteFollowingFolder;
private boolean mIsMultiNotesFollowingFolder; private boolean mIsMultiNotesFollowingFolder;
//初始化NoteItemData主要利用光标cursor获取的东西 public NoteItemData(Context context, Cursor cursor) {
public NoteItemData(Context context, Cursor cursor) {
//getxxx为转换格式 mId = cursor.getLong(ID_COLUMN);
mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); mParentId = cursor.getLong(PARENT_ID_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN); mSnippet = cursor.getString(SNIPPET_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN); mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( NoteEditActivity.TAG_UNCHECKED, "");
NoteEditActivity.TAG_UNCHECKED, ""); mType = cursor.getInt(TYPE_COLUMN);
mType = cursor.getInt(TYPE_COLUMN); mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
//初始化电话号码的信息 mPhoneNumber = "";
mPhoneNumber = ""; if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) {
if (!TextUtils.isEmpty(mPhoneNumber)) {//mphonenumber里有符合字符串则用contart功能连接 mName = Contact.getContact(context, mPhoneNumber);
mName = Contact.getContact(context, mPhoneNumber); if (mName == null) {
if (mName == null) { mName = mPhoneNumber;
mName = mPhoneNumber; }
} }
} }
}
if (mName == null) {
if (mName == null) { mName = "";
mName = ""; }
} checkPostion(cursor);
checkPostion(cursor); }
}
///根据鼠标的位置设置标记,和位置 private void checkPostion(Cursor cursor) {
private void checkPostion(Cursor cursor) {
//初始化几个标记cursor具体功能笔记中已提到不一一叙述 mIsLastItem = cursor.isLast() ? true : false;
mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false; mIsOnlyOneItem = (cursor.getCount() == 1);
mIsOnlyOneItem = (cursor.getCount() == 1);
//初始化“多重子文件”“单一子文件”2个标记 mIsMultiNotesFollowingFolder = false;
mIsMultiNotesFollowingFolder = false; mIsOneNoteFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
//主要是设置上诉2标记 if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {//若是note格式并且不是第一个元素 int position = cursor.getPosition();
int position = cursor.getPosition(); if (cursor.moveToPrevious()) {
if (cursor.moveToPrevious()) {//获取光标位置后看上一行 if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {//若光标满足系统或note格式 if (cursor.getCount() > (position + 1)) {
if (cursor.getCount() > (position + 1)) { mIsMultiNotesFollowingFolder = true;
mIsMultiNotesFollowingFolder = true;//若是数据行数大于但前位置+1则设置成正确 } else {
} else { mIsOneNoteFollowingFolder = true;
mIsOneNoteFollowingFolder = true;//否则单一文件夹标记为true }
} }
} if (!cursor.moveToNext()) {
if (!cursor.moveToNext()) {//若不能再往下走则报错 throw new IllegalStateException("cursor move to previous but can't move back");
throw new IllegalStateException("cursor move to previous but can't move back"); }
} }
} }
} }
} public boolean isOneFollowingFolder() {
///以下都是获取标记没什么好说的,不过倒数第二个需要说明下,很具体看下面 return mIsOneNoteFollowingFolder;
public boolean isOneFollowingFolder() { }
return mIsOneNoteFollowingFolder;
} public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
public boolean isMultiFollowingFolder() { }
return mIsMultiNotesFollowingFolder;
} public boolean isLast() {
return mIsLastItem;
public boolean isLast() { }
return mIsLastItem;
} public String getCallName() {
return mName;
public String getCallName() { }
return mName;
} public boolean isFirst() {
return mIsFirstItem;
public boolean isFirst() { }
return mIsFirstItem;
} public boolean isSingle() {
return mIsOnlyOneItem;
public boolean isSingle() { }
return mIsOnlyOneItem;
} public long getId() {
return mId;
public long getId() { }
return mId;
} public long getAlertDate() {
return mAlertDate;
public long getAlertDate() { }
return mAlertDate;
} public long getCreatedDate() {
return mCreatedDate;
public long getCreatedDate() { }
return mCreatedDate;
} public boolean hasAttachment() {
return mHasAttachment;
public boolean hasAttachment() { }
return mHasAttachment;
} public long getModifiedDate() {
return mModifiedDate;
public long getModifiedDate() { }
return mModifiedDate;
} public int getBgColorId() {
return mBgColorId;
public int getBgColorId() { }
return mBgColorId;
} public long getParentId() {
return mParentId;
public long getParentId() { }
return mParentId;
} public int getNotesCount() {
return mNotesCount;
public int getNotesCount() { }
return mNotesCount;
} public long getFolderId () {
return mParentId;
public long getFolderId () { }
return mParentId;
} public int getType() {
return mType;
public int getType() { }
return mType;
} public int getWidgetType() {
return mWidgetType;
public int getWidgetType() { }
return mWidgetType;
} public int getWidgetId() {
return mWidgetId;
public int getWidgetId() { }
return mWidgetId;
} public String getSnippet() {
return mSnippet;
public String getSnippet() { }
return mSnippet;
} public boolean hasAlert() {
return (mAlertDate > 0);
public boolean hasAlert() { }
return (mAlertDate > 0);
} public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
//若数据父id为保存至文件夹模式的id且满足电话号码单元不为空则isCallRecord为true }
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); public static int getNoteType(Cursor cursor) {
} return cursor.getInt(TYPE_COLUMN);
}
public static int getNoteType(Cursor cursor) { }
return cursor.getInt(TYPE_COLUMN);
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save