Compare commits

..

3 Commits

Author SHA1 Message Date
徐洁 013f4b22b7 remote
2 years ago
徐洁 19caaa2e17 初步解读和注释
2 years ago
徐洁 ada68e2749 xujie
2 years ago

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.

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

@ -1,36 +1,15 @@
/*
* 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.gtask.remote; package net.micode.notes.gtask.remote;
import android.app.Notification; /*GTask
import android.app.NotificationManager; *
import android.app.PendingIntent; * private void showNotification(int tickerId, String content)
import android.content.Context; * protected Integer doInBackground(Void... unused) 线
import android.content.Intent; * protected void onProgressUpdate(String... progress) 使 线
import android.os.AsyncTask; * protected void onPostExecute(Integer result) Handler UI使doInBackground UI
*/
import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
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;
public interface OnCompleteListener { public interface OnCompleteListener {
@ -57,22 +36,21 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
mTaskManager.cancelSync(); mTaskManager.cancelSync();
} }
public void publishProgess(String message) { public void publishProgess(String message) { // 发布进度单位系统将会调用onProgressUpdate()方法更新这些值
publishProgress(new String[] { publishProgress(new String[] {
message message
}); });
} }
//向用户提示当前同步的状态
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());
notification.defaults = Notification.DEFAULT_LIGHTS; notification.defaults = Notification.DEFAULT_LIGHTS; // 调用系统自带灯光
notification.flags = Notification.FLAG_AUTO_CANCEL; notification.flags = Notification.FLAG_AUTO_CANCEL; // 点击清除按钮或点击通知后会自动消失
PendingIntent pendingIntent; PendingIntent pendingIntent; //一个描述了想要启动一个Activity、Broadcast或是Service的意图
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); //如果同步不成功那么从系统取得一个用于启动一个NotesPreferenceActivity的PendingIntent对象
} else { } else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
@ -80,31 +58,30 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
} }
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);//通过NotificationManager对象的notify方法来执行一个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))); //利用getString,将把 NotesPreferenceActivity.getSyncAccountName(mContext))的字符串内容传进sync_progress_login中
return mTaskManager.sync(mContext, this); //进行后台同步具体操作 return mTaskManager.sync(mContext, this); //进行后台同步具体操作
} }
@Override @Override
protected void onProgressUpdate(String... progress) { protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]); showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) { if (mContext instanceof GTaskSyncService) { //instanceof 判断mContext是否是GTaskSyncService的实例
((GTaskSyncService) mContext).sendBroadcast(progress[0]); ((GTaskSyncService) mContext).sendBroadcast(progress[0]);
} }
} }
@Override @Override
protected void onPostExecute(Integer result) { //用于在执行完后台任务后更新UI,显示结果 protected void onPostExecute(Integer result) { //用于在执行完后台任务后更新UI,显示结果
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()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); //设置最新同步的时间
} else if (result == GTaskManager.STATE_NETWORK_ERROR) { } else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) { } else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
@ -112,11 +89,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() { //完成后的操作使用onComplete()将所有值都重新初始化,相当于完成一次操作
mOnCompleteListener.onComplete(); mOnCompleteListener.onComplete();
} }
}).start(); }).start();

@ -1,70 +1,13 @@
/*
* 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.gtask.remote; package net.micode.notes.gtask.remote;
import android.accounts.Account; /*
import android.accounts.AccountManager; * GTASKGTASK
import android.accounts.AccountManagerFuture; * 使accountManager JSONObject HttpParams authToken Gid
import android.app.Activity; */
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import net.micode.notes.ui.NotesPreferenceActivity;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
public class GTaskClient { public class GTaskClient {
private static final String TAG = GTaskClient.class.getSimpleName(); private static final String TAG = GTaskClient.class.getSimpleName();
private static final String GTASK_URL = "https://mail.google.com/tasks/"; private static final String GTASK_URL = "https://mail.google.com/tasks/"; //这个是指定的URL
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
@ -102,6 +45,10 @@ public class GTaskClient {
mUpdateArray = null; mUpdateArray = null;
} }
/*
* 使 getInstance()
* mInstance
*/
public static synchronized GTaskClient getInstance() { public static synchronized GTaskClient getInstance() {
if (mInstance == null) { if (mInstance == null) {
mInstance = new GTaskClient(); mInstance = new GTaskClient();
@ -109,42 +56,50 @@ public class GTaskClient {
return mInstance; return mInstance;
} }
/*Activity
*
* 使URL使URL
* truefalse
*/
public boolean login(Activity activity) { public boolean login(Activity activity) {
// we suppose that the cookie would expire after 5 minutes // we suppose that the cookie would expire after 5 minutes
// then we need to re-login // then we need to re-login
//判断距离最后一次登录操作是否超过5分钟
final long interval = 1000 * 60 * 5; final long interval = 1000 * 60 * 5;
if (mLastLoginTime + interval < System.currentTimeMillis()) { if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false; mLoggedin = false;
} }
// need to re-login after account switch // need to re-login after account switch 重新登录操作
if (mLoggedin if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) { .getSyncAccountName(activity))) {
mLoggedin = false; mLoggedin = false;
} }
//如果没超过时间,则不需要重新登录
if (mLoggedin) { if (mLoggedin) {
Log.d(TAG, "already logged in"); Log.d(TAG, "already logged in");
return true; return true;
} }
mLastLoginTime = System.currentTimeMillis(); mLastLoginTime = System.currentTimeMillis();//更新最后登录时间,改为系统当前的时间
String authToken = loginGoogleAccount(activity, false); String authToken = loginGoogleAccount(activity, false);//判断是否登录到谷歌账户
if (authToken == null) { if (authToken == null) {
Log.e(TAG, "login google account failed"); Log.e(TAG, "login google account failed");
return false; return false;
} }
// login with custom domain if necessary // login with custom domain if necessary
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() //尝试使用用户自己的域名登录
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() //将用户账号名改为统一格式(小写)后判断是否为一个谷歌账号地址
.endsWith("googlemail.com"))) { .endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
int index = mAccount.name.indexOf('@') + 1; int index = mAccount.name.indexOf('@') + 1;
String suffix = mAccount.name.substring(index); String suffix = mAccount.name.substring(index);
url.append(suffix + "/"); url.append(suffix + "/");
mGetUrl = url.toString() + "ig"; mGetUrl = url.toString() + "ig"; //设置用户对应的getUrl
mPostUrl = url.toString() + "r/ig"; mPostUrl = url.toString() + "r/ig"; //设置用户对应的postUrl
if (tryToLoginGtask(activity, authToken)) { if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true; mLoggedin = true;
@ -152,6 +107,7 @@ public class GTaskClient {
} }
// try to login with google official url // try to login with google official url
//如果用户账户无法登录则使用谷歌官方的URI进行登录
if (!mLoggedin) { if (!mLoggedin) {
mGetUrl = GTASK_GET_URL; mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL; mPostUrl = GTASK_POST_URL;
@ -164,10 +120,15 @@ public class GTaskClient {
return true; return true;
} }
/*
* 使
* 使AccountManager
*
*/
private String loginGoogleAccount(Activity activity, boolean invalidateToken) { private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken; String authToken; //令牌,是登录操作保证安全性的一个方法
AccountManager accountManager = AccountManager.get(activity); AccountManager accountManager = AccountManager.get(activity);//AccountManager这个类给用户提供了集中注册账号的接口
Account[] accounts = accountManager.getAccountsByType("com.google"); Account[] accounts = accountManager.getAccountsByType("com.google");//获取全部以com.google结尾的account
if (accounts.length == 0) { if (accounts.length == 0) {
Log.e(TAG, "there is no available google account"); Log.e(TAG, "there is no available google account");
@ -176,6 +137,7 @@ public class GTaskClient {
String accountName = NotesPreferenceActivity.getSyncAccountName(activity); String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null; Account account = null;
//遍历获得的accounts信息寻找已经记录过的账户信息
for (Account a : accounts) { for (Account a : accounts) {
if (a.name.equals(accountName)) { if (a.name.equals(accountName)) {
account = a; account = a;
@ -190,11 +152,13 @@ public class GTaskClient {
} }
// get the token now // get the token now
//获取选中账号的令牌
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account, AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null); "goanna_mobile", null, activity, null, null);
try { try {
Bundle authTokenBundle = accountManagerFuture.getResult(); Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
//如果是invalidateToken那么需要调用invalidateAuthToken(String, String)方法废除这个无效token
if (invalidateToken) { if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken); accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false); loginGoogleAccount(activity, false);
@ -207,10 +171,12 @@ public class GTaskClient {
return authToken; return authToken;
} }
//尝试登陆Gtask这只是一个预先判断令牌是否是有效以及是否能登上GTask的方法,而不是具体实现登陆的方法
private boolean tryToLoginGtask(Activity activity, String authToken) { private boolean tryToLoginGtask(Activity activity, String authToken) {
if (!loginGtask(authToken)) { if (!loginGtask(authToken)) {
// maybe the auth token is out of date, now let's invalidate the // maybe the auth token is out of authTokedate, now let's invalidate the
// token and try again // token and try again
//删除过一个无效的authToken申请一个新的后再次尝试登陆
authToken = loginGoogleAccount(activity, true); authToken = loginGoogleAccount(activity, true);
if (authToken == null) { if (authToken == null) {
Log.e(TAG, "login google account failed"); Log.e(TAG, "login google account failed");
@ -225,25 +191,27 @@ public class GTaskClient {
return true; return true;
} }
//实现登录GTask的具体操作
private boolean loginGtask(String authToken) { private boolean loginGtask(String authToken) {
int timeoutConnection = 10000; int timeoutConnection = 10000;
int timeoutSocket = 15000; int timeoutSocket = 15000; //socket是一种通信连接实现数据的交换的端口
HttpParams httpParameters = new BasicHttpParams(); HttpParams httpParameters = new BasicHttpParams(); //实例化一个新的HTTP参数类
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);//设置连接超时时间
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);//设置设置端口超时时间
mHttpClient = new DefaultHttpClient(httpParameters); mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore(); BasicCookieStore localBasicCookieStore = new BasicCookieStore(); //设置本地cookie
mHttpClient.setCookieStore(localBasicCookieStore); mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// login gtask // login gtask
try { try {
String loginUrl = mGetUrl + "?auth=" + authToken; String loginUrl = mGetUrl + "?auth=" + authToken; //设置登录的url
HttpGet httpGet = new HttpGet(loginUrl); HttpGet httpGet = new HttpGet(loginUrl); //通过登录的uri实例化网页上资源的查找
HttpResponse response = null; HttpResponse response = null;
response = mHttpClient.execute(httpGet); response = mHttpClient.execute(httpGet);
// get the cookie now // get the cookie now
//获取CookieStore里存放的cookie,看如果存有“GTL(不知道什么意思)”则说明有验证成功的有效的cookie
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies(); List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false; boolean hasAuthCookie = false;
for (Cookie cookie : cookies) { for (Cookie cookie : cookies) {
@ -256,6 +224,7 @@ public class GTaskClient {
} }
// get the client version // get the client version
//获取client的内容具体操作是在返回的Content中截取从_setup(开始到)}</script>中间的字符串内容也就是gtask_url的内容
String resString = getResponseContent(response.getEntity()); String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup("; String jsBegin = "_setup(";
String jsEnd = ")}</script>"; String jsEnd = ")}</script>";
@ -284,6 +253,10 @@ public class GTaskClient {
return mActionId++; return mActionId++;
} }
/*
* 使HttpPost
* httpPost
*/
private HttpPost createHttpPost() { private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl); HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -291,24 +264,28 @@ public class GTaskClient {
return httpPost; return httpPost;
} }
/*URL
* 使getContentEncoding()
*
*/
private String getResponseContent(HttpEntity entity) throws IOException { private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null; String contentEncoding = null;
if (entity.getContentEncoding() != null) { if (entity.getContentEncoding() != null) {//通过URL得到HttpEntity对象如果不为空则使用getContent方法创建一个流将数据从网络都过来
contentEncoding = entity.getContentEncoding().getValue(); contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding); Log.d(TAG, "encoding: " + contentEncoding);
} }
InputStream input = entity.getContent(); InputStream input = entity.getContent();
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {//GZIP是使用DEFLATE进行压缩数据的另一个压缩库
input = new GZIPInputStream(entity.getContent()); input = new GZIPInputStream(entity.getContent());
} else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) {//DEFLATE是一个无专利的压缩算法它可以实现无损数据压缩
Inflater inflater = new Inflater(true); Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater); input = new InflaterInputStream(entity.getContent(), inflater);
} }
try { try {
InputStreamReader isr = new InputStreamReader(input); InputStreamReader isr = new InputStreamReader(input);
BufferedReader br = new BufferedReader(isr); BufferedReader br = new BufferedReader(isr);//是一个包装类,它可以包装字符流,将字符流放入缓存里,先把字符读到缓存里,到缓存满了时候,再读入内存,是为了提供读的效率而设计的
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
while (true) { while (true) {
@ -323,20 +300,28 @@ public class GTaskClient {
} }
} }
/*JSON
* jsonjs
* UrlEncodedFormEntity entityhttpPost.setEntity(entity)jshttpPost
* 使getResponseContent
* json
*/
private JSONObject postRequest(JSONObject js) throws NetworkFailureException { private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) { if (!mLoggedin) {//未登录
Log.e(TAG, "please login first"); Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in"); throw new ActionFailureException("not logged in");
} }
//实例化一个httpPost的对象用来向服务器传输数据在这里就是发送请求而请求的内容在js里
HttpPost httpPost = createHttpPost(); HttpPost httpPost = createHttpPost();
try { try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>(); LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString())); list.add(new BasicNameValuePair("r", js.toString()));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); //UrlEncodedFormEntity()的形式比较单一,是普通的键值对
httpPost.setEntity(entity); httpPost.setEntity(entity);
// execute the post // execute the post
//执行这个请求
HttpResponse response = mHttpClient.execute(httpPost); HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity()); String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString); return new JSONObject(jsString);
@ -360,6 +345,12 @@ public class GTaskClient {
} }
} }
/*
* .gtask.data.TaskTask
* jsonTask,jsPost
* postRequest
* 使task.setGidtasknew_ID
*/
public void createTask(Task task) throws NetworkFailureException { public void createTask(Task task) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
@ -386,6 +377,9 @@ public class GTaskClient {
} }
} }
/*
* createTasktasklistgid
*/
public void createTaskList(TaskList tasklist) throws NetworkFailureException { public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
@ -412,6 +406,11 @@ public class GTaskClient {
} }
} }
/*
*
* 使JSONObject使jsPost.putPutUpdateArrayClientVersion
* 使postRequestjspost,
*/
public void commitUpdate() throws NetworkFailureException { public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) { if (mUpdateArray != null) {
try { try {
@ -433,6 +432,10 @@ public class GTaskClient {
} }
} }
/*
*
* commitUpdate()
*/
public void addUpdateNode(Node node) throws NetworkFailureException { public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) { if (node != null) {
// too many update items may result in an error // too many update items may result in an error
@ -447,6 +450,12 @@ public class GTaskClient {
} }
} }
/*
* task,tasktask
* getGidtaskgid
* JSONObject.put(String name, Object value)task
* postRequest
*/
public void moveTask(Task task, TaskList preParent, TaskList curParent) public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException { throws NetworkFailureException {
commitUpdate(); commitUpdate();
@ -463,15 +472,17 @@ public class GTaskClient {
if (preParent == curParent && task.getPriorSibling() != null) { if (preParent == curParent && task.getPriorSibling() != null) {
// put prioring_sibing_id only if moving within the tasklist and // put prioring_sibing_id only if moving within the tasklist and
// it is not the first one // it is not the first one
//设置优先级ID只有当移动是发生在文件中
action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());
} }
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); //设置移动前所属列表
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); //设置当前所属列表
if (preParent != curParent) { if (preParent != curParent) {
// put the dest_list only if moving between tasklists // put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
} }
actionList.put(action); actionList.put(action);
//最后将ACTION_LIST加入到jsPost中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version // client_version
@ -486,6 +497,11 @@ public class GTaskClient {
} }
} }
/*
*
* JSON
* 使postRequest
*/
public void deleteNode(Node node) throws NetworkFailureException { public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
@ -494,7 +510,7 @@ public class GTaskClient {
// action_list // action_list
node.setDeleted(true); node.setDeleted(true);
actionList.put(node.getUpdateAction(getActionId())); actionList.put(node.getUpdateAction(getActionId())); //这里会获取到删除操作的ID加入到actionLiast中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version // client_version
@ -509,6 +525,11 @@ public class GTaskClient {
} }
} }
/*
*
* GetURI使getResponseContent
* "_setup(")}</script>GTASK_JSON_LISTS
*/
public JSONArray getTaskLists() throws NetworkFailureException { public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) { if (!mLoggedin) {
Log.e(TAG, "please login first"); Log.e(TAG, "please login first");
@ -521,6 +542,7 @@ public class GTaskClient {
response = mHttpClient.execute(httpGet); response = mHttpClient.execute(httpGet);
// get the task list // get the task list
//筛选工作把筛选出的字符串放入jsString
String resString = getResponseContent(response.getEntity()); String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup("; String jsBegin = "_setup(";
String jsEnd = ")}</script>"; String jsEnd = ")}</script>";
@ -531,6 +553,7 @@ public class GTaskClient {
jsString = resString.substring(begin + jsBegin.length(), end); jsString = resString.substring(begin + jsBegin.length(), end);
} }
JSONObject js = new JSONObject(jsString); JSONObject js = new JSONObject(jsString);
//获取GTASK_JSON_LISTS
return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS);
} catch (ClientProtocolException e) { } catch (ClientProtocolException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
@ -547,6 +570,9 @@ public class GTaskClient {
} }
} }
/*
* TASKListgid,
*/
public JSONArray getTaskList(String listGid) throws NetworkFailureException { public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
@ -558,7 +584,7 @@ public class GTaskClient {
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); //这里设置为传入的listGid
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action); actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
@ -579,6 +605,7 @@ public class GTaskClient {
return mAccount; return mAccount;
} }
//重置更新的内容
public void resetUpdateArray() { public void resetUpdateArray() {
mUpdateArray = null; mUpdateArray = null;
} }

@ -1,119 +1,73 @@
/*
* 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.gtask.remote; package net.micode.notes.gtask.remote;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
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.NoteColumns;
import net.micode.notes.gtask.data.MetaData;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.SqlNote;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
public class GTaskManager { public class GTaskManager {
private static final String TAG = GTaskManager.class.getSimpleName(); private static final String TAG = GTaskManager.class.getSimpleName();
public static final int STATE_SUCCESS = 0; public static final int STATE_SUCCESS = 0;
public static final int STATE_NETWORK_ERROR = 1; public static final int STATE_NETWORK_ERROR = 1;
public static final int STATE_INTERNAL_ERROR = 2; public static final int STATE_INTERNAL_ERROR = 2;
public static final int STATE_SYNC_IN_PROGRESS = 3; public static final int STATE_SYNC_IN_PROGRESS = 3;
public static final int STATE_SYNC_CANCELLED = 4; public static final int STATE_SYNC_CANCELLED = 4;
private static GTaskManager mInstance = null; private static GTaskManager mInstance = null;
private Activity mActivity; private Activity mActivity;
private Context mContext; private Context mContext;
private ContentResolver mContentResolver; private ContentResolver mContentResolver;
private boolean mSyncing; private boolean mSyncing;
private boolean mCancelled; private boolean mCancelled;
private HashMap<String, TaskList> mGTaskListHashMap; private HashMap<String, TaskList> mGTaskListHashMap;
private HashMap<String, Node> mGTaskHashMap; private HashMap<String, Node> mGTaskHashMap;
private HashMap<String, MetaData> mMetaHashMap; private HashMap<String, MetaData> mMetaHashMap;
private TaskList mMetaList; private TaskList mMetaList;
private HashSet<Long> mLocalDeleteIdMap; private HashSet<Long> mLocalDeleteIdMap;
private HashMap<String, Long> mGidToNid; private HashMap<String, Long> mGidToNid;
private HashMap<Long, String> mNidToGid; private HashMap<Long, String> mNidToGid;
private GTaskManager() { private GTaskManager() { //对象初始化函数
mSyncing = false; mSyncing = false; //正在同步,flase代表未执行
mCancelled = false; mCancelled = false; //全局标识flase代表可以执行
mGTaskListHashMap = new HashMap<String, TaskList>(); mGTaskListHashMap = new HashMap<String, TaskList>(); //<>代表Java的泛型,就是创建一个用类型作为参数的类。
mGTaskHashMap = new HashMap<String, Node>(); mGTaskHashMap = new HashMap<String, Node>();
mMetaHashMap = new HashMap<String, MetaData>(); mMetaHashMap = new HashMap<String, MetaData>();
mMetaList = null; mMetaList = null;
mLocalDeleteIdMap = new HashSet<Long>(); mLocalDeleteIdMap = new HashSet<Long>();
mGidToNid = new HashMap<String, Long>(); mGidToNid = new HashMap<String, Long>(); //GoogleID to NodeID??
mNidToGid = new HashMap<Long, String>(); mNidToGid = new HashMap<Long, String>(); //NodeID to GoogleID???通过hashmap散列表建立映射
} }
public static synchronized GTaskManager getInstance() { /**
* synchronized线
*
* @author TTS
* @return GtaskManger
*/
public static synchronized GTaskManager getInstance() { //可能运行在多线程环境下,使用语言级同步--synchronized
if (mInstance == null) { if (mInstance == null) {
mInstance = new GTaskManager(); mInstance = new GTaskManager();
} }
return mInstance; return mInstance;
} }
/**
* synchronized线
* @author TTS
* @param activity
*/
public synchronized void setActivityContext(Activity activity) { public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken // used for getting auth token
mActivity = activity; mActivity = activity;
} }
public int sync(Context context, GTaskASyncTask asyncTask) { /**
*
*
* @author TTS
* @param context-----
* @param asyncTask-------
* @return int
*/
public int sync(Context context, GTaskASyncTask asyncTask) { //核心函数
if (mSyncing) { if (mSyncing) {
Log.d(TAG, "Sync is in progress"); Log.d(TAG, "Sync is in progress"); //创建日志文件调试信息debug
return STATE_SYNC_IN_PROGRESS; return STATE_SYNC_IN_PROGRESS;
} }
mContext = context; mContext = context;
@ -128,8 +82,8 @@ public class GTaskManager {
mNidToGid.clear(); mNidToGid.clear();
try { try {
GTaskClient client = GTaskClient.getInstance(); GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例,client--客户机
client.resetUpdateArray(); client.resetUpdateArray(); //JSONArray类型reset即置为NULL
// login google task // login google task
if (!mCancelled) { if (!mCancelled) {
@ -140,15 +94,15 @@ public class GTaskManager {
// get the task list from google // get the task list from google
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList(); initGTaskList(); //获取Google上的JSONtasklist转为本地TaskList
// do content sync work // do content sync work
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
syncContent(); syncContent();
} catch (NetworkFailureException e) { } catch (NetworkFailureException e) { //分为两种异常,此类异常为网络异常
Log.e(TAG, e.toString()); Log.e(TAG, e.toString()); //创建日志文件调试信息error
return STATE_NETWORK_ERROR; return STATE_NETWORK_ERROR;
} catch (ActionFailureException e) { } catch (ActionFailureException e) { //此类异常为操作异常
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
return STATE_INTERNAL_ERROR; return STATE_INTERNAL_ERROR;
} catch (Exception e) { } catch (Exception e) {
@ -168,32 +122,41 @@ public class GTaskManager {
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
} }
/**
*GtaskListGoogleJSONtasklistTaskList
*mMetaListmGTaskListHashMapmGTaskHashMap
*@author TTS
*@exception NetworkFailureException
*@return void
*/
private void initGTaskList() throws NetworkFailureException { private void initGTaskList() throws NetworkFailureException {
if (mCancelled) if (mCancelled)
return; return;
GTaskClient client = GTaskClient.getInstance(); GTaskClient client = GTaskClient.getInstance(); //getInstance即为创建一个实例client应指远端客户机
try { try {
JSONArray jsTaskLists = client.getTaskLists(); //Json对象是Name Value对(即子元素)的无序集合相当于一个Map对象。JsonObject类是bantouyan-json库对Json对象的抽象提供操纵Json对象的各种方法。
//其格式为{"key1":value1,"key2",value2....};key 必须是字符串。
//因为ajax请求不刷新页面但配合js可以实现局部刷新因此json常常被用来作为异步请求的返回对象使用。
JSONArray jsTaskLists = client.getTaskLists(); //原注释为get task list------lists
// init meta list first // init meta list first
mMetaList = null; mMetaList = null; //TaskList类型
for (int i = 0; i < jsTaskLists.length(); i++) { for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i); JSONObject object = jsTaskLists.getJSONObject(i); //JSONObject与JSONArray一个为对象一个为数组。此处取出单个JASONObject
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {
.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); //MetaList意为元表,Tasklist类型此处为初始化
mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); //将JSON中部分数据复制到自己定义的对象中相对应的数据name->mname...
mMetaList.setContentByRemoteJSON(object);
// load meta data // load meta data
JSONArray jsMetas = client.getTaskList(gid); JSONArray jsMetas = client.getTaskList(gid); //原注释为get action_list------list
for (int j = 0; j < jsMetas.length(); j++) { for (int j = 0; j < jsMetas.length(); j++) {
object = (JSONObject) jsMetas.getJSONObject(j); object = (JSONObject) jsMetas.getJSONObject(j);
MetaData metaData = new MetaData(); MetaData metaData = new MetaData(); //继承自Node
metaData.setContentByRemoteJSON(object); metaData.setContentByRemoteJSON(object);
if (metaData.isWorthSaving()) { if (metaData.isWorthSaving()) { //if not worth to savemetadata将不加入mMetaList
mMetaList.addChildTask(metaData); mMetaList.addChildTask(metaData);
if (metaData.getGid() != null) { if (metaData.getGid() != null) {
mMetaHashMap.put(metaData.getRelatedGid(), metaData); mMetaHashMap.put(metaData.getRelatedGid(), metaData);
@ -214,16 +177,16 @@ public class GTaskManager {
// init task list // init task list
for (int i = 0; i < jsTaskLists.length(); i++) { for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i); JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); //通过getString函数传入本地某个标志数据的名称获取其在远端的名称。
String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);
if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
&& !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_META)) { + GTaskStringUtils.FOLDER_META)) {
TaskList tasklist = new TaskList(); TaskList tasklist = new TaskList(); //继承自Node
tasklist.setContentByRemoteJSON(object); tasklist.setContentByRemoteJSON(object);
mGTaskListHashMap.put(gid, tasklist); mGTaskListHashMap.put(gid, tasklist);
mGTaskHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); //为什么加两遍???
// load tasks // load tasks
JSONArray jsTasks = client.getTaskList(gid); JSONArray jsTasks = client.getTaskList(gid);
@ -247,13 +210,18 @@ public class GTaskManager {
} }
} }
private void syncContent() throws NetworkFailureException { /**
*
* @throws NetworkFailureException
* @return
*/
private void syncContent() throws NetworkFailureException { //本地内容同步操作
int syncType; int syncType;
Cursor c = null; Cursor c = null; //数据库指针
String gid; String gid; //GoogleID??
Node node; Node node; //Node包含Sync_Action的不同类型
mLocalDeleteIdMap.clear(); mLocalDeleteIdMap.clear(); //HashSet<Long>类型
if (mCancelled) { if (mCancelled) {
return; return;
@ -301,8 +269,8 @@ public class GTaskManager {
node = mGTaskHashMap.get(gid); node = mGTaskHashMap.get(gid);
if (node != null) { if (node != null) {
mGTaskHashMap.remove(gid); mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); //通过hashmap建立联系
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); //通过hashmap建立联系
syncType = node.getSyncAction(c); syncType = node.getSyncAction(c);
} else { } else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
@ -327,14 +295,14 @@ public class GTaskManager {
} }
// go through remaining items // go through remaining items
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator(); Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator(); //Iterator迭代器
while (iter.hasNext()) { while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next(); Map.Entry<String, Node> entry = iter.next();
node = entry.getValue(); node = entry.getValue();
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
} }
// mCancelled can be set by another thread, so we neet to check one by // mCancelled can be set by another thread, so we neet to check one by //thread----线程
// one // one
// clear local delete table // clear local delete table
if (!mCancelled) { if (!mCancelled) {
@ -351,6 +319,11 @@ public class GTaskManager {
} }
/**
*
* @author TTS
* @throws NetworkFailureException
*/
private void syncFolder() throws NetworkFailureException { private void syncFolder() throws NetworkFailureException {
Cursor c = null; Cursor c = null;
String gid; String gid;
@ -394,7 +367,7 @@ public class GTaskManager {
try { try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",
new String[] { new String[] {
String.valueOf(Notes.ID_CALL_RECORD_FOLDER) String.valueOf(Notes.ID_CALL_RECORD_FOLDER)
}, null); }, null);
if (c != null) { if (c != null) {
if (c.moveToNext()) { if (c.moveToNext()) {
@ -476,6 +449,14 @@ public class GTaskManager {
GTaskClient.getInstance().commitUpdate(); GTaskClient.getInstance().commitUpdate();
} }
/**
* syncTypeaddLocalNodeaddRemoteNodedeleteNodeupdateLocalNodeupdateRemoteNode
* @author TTS
* @param syncType
* @param node
* @param c
* @throws NetworkFailureException
*/
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
@ -522,6 +503,12 @@ public class GTaskManager {
} }
} }
/**
* Node
* @author TTS
* @param node
* @throws NetworkFailureException
*/
private void addLocalNode(Node node) throws NetworkFailureException { private void addLocalNode(Node node) throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
@ -596,6 +583,15 @@ public class GTaskManager {
updateRemoteMeta(node.getGid(), sqlNote); updateRemoteMeta(node.getGid(), sqlNote);
} }
/**
* updatenode
* @author TTS
* @param node
* ----
* @param c
* ----Cursor
* @throws NetworkFailureException
*/
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
@ -619,12 +615,22 @@ public class GTaskManager {
updateRemoteMeta(node.getGid(), sqlNote); updateRemoteMeta(node.getGid(), sqlNote);
} }
/**
* Node
* updateRemoteMeta
* @author TTS
* @param node
* ----
* @param c
* --Cursor
* @throws NetworkFailureException
*/
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
} }
SqlNote sqlNote = new SqlNote(mContext, c); SqlNote sqlNote = new SqlNote(mContext, c); //从本地mContext中获取内容
Node n; Node n;
// update remotely // update remotely
@ -634,11 +640,12 @@ public class GTaskManager {
String parentGid = mNidToGid.get(sqlNote.getParentId()); String parentGid = mNidToGid.get(sqlNote.getParentId());
if (parentGid == null) { if (parentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist"); Log.e(TAG, "cannot find task's parent tasklist"); //调试信息
throw new ActionFailureException("cannot add remote task"); throw new ActionFailureException("cannot add remote task");
} }
mGTaskListHashMap.get(parentGid).addChildTask(task); mGTaskListHashMap.get(parentGid).addChildTask(task); //在本地生成的GTaskList中增加子结点
//登录远程服务器创建Task
GTaskClient.getInstance().createTask(task); GTaskClient.getInstance().createTask(task);
n = (Node) task; n = (Node) task;
@ -656,6 +663,7 @@ public class GTaskManager {
else else
folderName += sqlNote.getSnippet(); folderName += sqlNote.getSnippet();
//iterator迭代器通过统一的接口迭代所有的map元素
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator(); Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next(); Map.Entry<String, TaskList> entry = iter.next();
@ -687,11 +695,20 @@ public class GTaskManager {
sqlNote.resetLocalModified(); sqlNote.resetLocalModified();
sqlNote.commit(true); sqlNote.commit(true);
// gid-id mapping // gid-id mapping //创建id间的映射
mGidToNid.put(n.getGid(), sqlNote.getId()); mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid()); mNidToGid.put(sqlNote.getId(), n.getGid());
} }
/**
* Nodemeta(updateRemoteMeta)
* @author TTS
* @param node
* ----
* @param c
* --Cursor
* @throws NetworkFailureException
*/
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
@ -701,7 +718,7 @@ public class GTaskManager {
// update remotely // update remotely
node.setContentByLocalJSON(sqlNote.getContent()); node.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(node); GTaskClient.getInstance().addUpdateNode(node); //GTaskClient用途为从本地登陆远端服务器
// update meta // update meta
updateRemoteMeta(node.getGid(), sqlNote); updateRemoteMeta(node.getGid(), sqlNote);
@ -710,15 +727,19 @@ public class GTaskManager {
if (sqlNote.isNoteType()) { if (sqlNote.isNoteType()) {
Task task = (Task) node; Task task = (Task) node;
TaskList preParentList = task.getParent(); TaskList preParentList = task.getParent();
//preParentList为通过node获取的父节点列表
String curParentGid = mNidToGid.get(sqlNote.getParentId()); String curParentGid = mNidToGid.get(sqlNote.getParentId());
//curParentGid为通过光标在数据库中找到sqlNote的mParentId再通过mNidToGid由long类型转为String类型的Gid
if (curParentGid == null) { if (curParentGid == null) {
Log.e(TAG, "cannot find task's parent tasklist"); Log.e(TAG, "cannot find task's parent tasklist");
throw new ActionFailureException("cannot update remote task"); throw new ActionFailureException("cannot update remote task");
} }
TaskList curParentList = mGTaskListHashMap.get(curParentGid); TaskList curParentList = mGTaskListHashMap.get(curParentGid);
//通过HashMap找到对应Gid的TaskList
if (preParentList != curParentList) { if (preParentList != curParentList) { //?????????????
preParentList.removeChildTask(task); preParentList.removeChildTask(task);
curParentList.addChildTask(task); curParentList.addChildTask(task);
GTaskClient.getInstance().moveTask(task, preParentList, curParentList); GTaskClient.getInstance().moveTask(task, preParentList, curParentList);
@ -727,9 +748,19 @@ public class GTaskManager {
// clear local modified flag // clear local modified flag
sqlNote.resetLocalModified(); sqlNote.resetLocalModified();
//commit到本地数据库
sqlNote.commit(true); sqlNote.commit(true);
} }
/**
* meta meta----------
* @author TTS
* @param gid
* ---GoogleIDString
* @param sqlNote
* ---使SqlNote
* @throws NetworkFailureException
*/
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) { if (sqlNote != null && sqlNote.isNoteType()) {
MetaData metaData = mMetaHashMap.get(gid); MetaData metaData = mMetaHashMap.get(gid);
@ -746,12 +777,18 @@ public class GTaskManager {
} }
} }
/**
* syncID
* @author TTS
* @return void
* @throws NetworkFailureException
*/
private void refreshLocalSyncId() throws NetworkFailureException { private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) { if (mCancelled) {
return; return;
} }
// get the latest gtask list // get the latest gtask list //获取最近的最晚的gtask list
mGTaskHashMap.clear(); mGTaskHashMap.clear();
mGTaskListHashMap.clear(); mGTaskListHashMap.clear();
mMetaHashMap.clear(); mMetaHashMap.clear();
@ -762,16 +799,16 @@ public class GTaskManager {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id<>?)", new String[] { "(type<>? AND parent_id<>?)", new String[] {
String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
}, NoteColumns.TYPE + " DESC"); }, NoteColumns.TYPE + " DESC"); //query语句五个参数NoteColumns.TYPE + " DESC"-----为按类型递减顺序返回查询结果。new String[] {String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)}------为选择参数。"(type<>? AND parent_id<>?)"-------指明返回行过滤器。SqlNote.PROJECTION_NOTE--------应返回的数据列的名字。Notes.CONTENT_NOTE_URI--------contentProvider包含所有数据集所对应的uri
if (c != null) { if (c != null) {
while (c.moveToNext()) { while (c.moveToNext()) {
String gid = c.getString(SqlNote.GTASK_ID_COLUMN); String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid); Node node = mGTaskHashMap.get(gid);
if (node != null) { if (node != null) {
mGTaskHashMap.remove(gid); mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues(); ContentValues values = new ContentValues(); //在ContentValues中创建键值对。准备通过contentResolver写入数据
values.put(NoteColumns.SYNC_ID, node.getLastModified()); values.put(NoteColumns.SYNC_ID, node.getLastModified());
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, //进行批量更改选择参数为NULL应该可以用insert替换参数分别为表名和需要更新的value对象。
c.getLong(SqlNote.ID_COLUMN)), values, null, null); c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else { } else {
Log.e(TAG, "something is missed"); Log.e(TAG, "something is missed");
@ -790,10 +827,19 @@ public class GTaskManager {
} }
} }
/**
* ,mAccount.name
* @author TTS
* @return String
*/
public String getSyncAccount() { public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name; return GTaskClient.getInstance().getSyncAccount().name;
} }
/**
* mCancelledtrue
* @author TTS
*/
public void cancelSync() { public void cancelSync() {
mCancelled = true; mCancelled = true;
} }

@ -1,27 +1,20 @@
/*
* 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.gtask.remote; package net.micode.notes.gtask.remote;
import android.app.Activity; /*
import android.app.Service; * Service
import android.content.Context; *
import android.content.Intent; * private void startSync()
import android.os.Bundle; * private void cancelSync()
import android.os.IBinder; * public void onCreate()
* public int onStartCommand(Intent intent, int flags, int startId) serviceserviceservice
* public void onLowMemory() serviceservice
* public IBinder onBind()
* public void sendBroadcast(String msg)
* public static void startSync(Activity activity)
* public static void cancelSync(Context context)
* public static boolean isSyncing()
* public static String getProgressString()
*/
public class GTaskSyncService extends Service { public class GTaskSyncService extends Service {
public final static String ACTION_STRING_NAME = "sync_action_type"; public final static String ACTION_STRING_NAME = "sync_action_type";
@ -53,11 +46,11 @@ 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();
@ -65,7 +58,7 @@ public class GTaskSyncService extends Service {
} }
@Override @Override
public void onCreate() { public void onCreate() { //初始化一个service
mSyncTask = null; mSyncTask = null;
} }
@ -74,7 +67,7 @@ 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;
@ -84,12 +77,11 @@ public class GTaskSyncService extends Service {
default: default:
break; break;
} }
return START_STICKY; return START_STICKY; //等待新的intent来是这个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) {
@ -97,40 +89,35 @@ public class GTaskSyncService extends Service {
} }
} }
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) { //不知道干吗用的
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
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); //附加INTENT中的相应参数的值
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) {//执行一个serviceservice的内容里的同步动作就是开始同步
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);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
activity.startService(intent); activity.startService(intent);
} }
//执行取消同步的操作 public static void cancelSync(Context context) {//执行一个serviceservice的内容里的同步动作就是取消同步
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,21 +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.model; package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
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.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
import android.content.ContentProviderOperation;//批量的更新、插入、删除数据。
import android.content.ContentProviderResult;//操作的结果
import android.content.ContentUris;//用于添加和获取Uri后面的ID
import android.content.ContentValues;//一种用来存储基本数据类型数据的存储机制
import android.content.Context;//需要用该类来弄清楚调用者的实例
import android.content.OperationApplicationException;//操作应用程序容错
import android.net.Uri;//表示待操作的数据
import android.os.RemoteException;//远程容错
import android.util.Log;//输出日志,比如说出错、警告等
public class Note { public class Note {
// private ContentValues mNoteDiffValues; private ContentValues mNoteDiffValues;
ContentValues mNoteDiffValues;//
private NoteData mNoteData; private NoteData mNoteData;
private static final String TAG = "Note"; private static final String TAG = "Note";
/** /**
* Create a new note id for adding a new note to databases * Create a new note id for adding a new note to databases
*/ */
@ -27,17 +49,16 @@ public class Note {
values.put(NoteColumns.MODIFIED_DATE, createdTime); values.put(NoteColumns.MODIFIED_DATE, createdTime);
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1); values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格 values.put(NoteColumns.PARENT_ID, folderId);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
//ContentResolver()主要是实现外部应用对ContentProvider中的数据
//进行添加、删除、修改和查询操作
long noteId = 0; long noteId = 0;
try { try {
noteId = Long.valueOf(uri.getPathSegments().get(1)); noteId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString()); Log.e(TAG, "Get note id error :" + e.toString());
noteId = 0; noteId = 0;
}//try-catch异常处理 }
if (noteId == -1) { if (noteId == -1) {
throw new IllegalStateException("Wrong note id:" + noteId); throw new IllegalStateException("Wrong note id:" + noteId);
} }
@ -47,37 +68,37 @@ public class Note {
public Note() { public Note() {
mNoteDiffValues = new ContentValues(); mNoteDiffValues = new ContentValues();
mNoteData = new NoteData(); mNoteData = new NoteData();
}//定义两个变量用来存储便签的数据,一个是存储便签属性、一个是存储便签内容 }
public void setNoteValue(String key, String value) { public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value); mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}//设置数据库表格的标签属性数据 }
public void setTextData(String key, String value) { public void setTextData(String key, String value) {
mNoteData.setTextData(key, value); mNoteData.setTextData(key, value);
}//设置数据库表格的标签文本内容的数据 }
public void setTextDataId(long id) { public void setTextDataId(long id) {
mNoteData.setTextDataId(id); mNoteData.setTextDataId(id);
}//设置文本数据的ID }
public long getTextDataId() { public long getTextDataId() {
return mNoteData.mTextDataId; return mNoteData.mTextDataId;
}//得到文本数据的ID }
public void setCallDataId(long id) { public void setCallDataId(long id) {
mNoteData.setCallDataId(id); mNoteData.setCallDataId(id);
}//设置电话号码数据的ID }
public void setCallData(String key, String value) { public void setCallData(String key, String value) {
mNoteData.setCallData(key, value); mNoteData.setCallData(key, value);
}//得到电话号码数据的ID }
public boolean isLocalModified() { public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}//判断是否是本地修改 }
public boolean syncNote(Context context, long noteId) { public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) { if (noteId <= 0) {
@ -107,16 +128,16 @@ public class Note {
} }
return true; return true;
}//判断数据是否同步 }
private class NoteData {//定义一个基本的便签内容的数据类,主要包含文本数据和电话号码数据 private class NoteData {
private long mTextDataId; private long mTextDataId;
private ContentValues mTextDataValues;//文本数据 private ContentValues mTextDataValues;
private long mCallDataId; private long mCallDataId;
private ContentValues mCallDataValues;//电话号码数据 private ContentValues mCallDataValues;
private static final String TAG = "NoteData"; private static final String TAG = "NoteData";
@ -126,7 +147,7 @@ public class Note {
mTextDataId = 0; mTextDataId = 0;
mCallDataId = 0; mCallDataId = 0;
} }
//下面是上述几个函数的具体实现
boolean isLocalModified() { boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
} }
@ -156,17 +177,17 @@ public class Note {
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
} }
//下面函数的作用是将新的数据通过Uri的操作存储到数据库
Uri pushIntoContentResolver(Context context, long noteId) { Uri pushIntoContentResolver(Context context, long noteId) {
/** /**
* Check for safety * Check for safety
*/ */
if (noteId <= 0) { if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId); throw new IllegalArgumentException("Wrong note id:" + noteId);
}//判断数据是否合法 }
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;//数据库的操作列表 ContentProviderOperation.Builder builder = null;
if(mTextDataValues.size() > 0) { if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId); mTextDataValues.put(DataColumns.NOTE_ID, noteId);
@ -188,7 +209,7 @@ public class Note {
operationList.add(builder.build()); operationList.add(builder.build());
} }
mTextDataValues.clear(); mTextDataValues.clear();
}//把文本数据存入DataColumns }
if(mCallDataValues.size() > 0) { if(mCallDataValues.size() > 0) {
mCallDataValues.put(DataColumns.NOTE_ID, noteId); mCallDataValues.put(DataColumns.NOTE_ID, noteId);
@ -210,7 +231,7 @@ public class Note {
operationList.add(builder.build()); operationList.add(builder.build());
} }
mCallDataValues.clear(); mCallDataValues.clear();
}//把电话号码数据存入DataColumns }
if (operationList.size() > 0) { if (operationList.size() > 0) {
try { try {
@ -225,7 +246,7 @@ public class Note {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
return null; return null;
} }
}//存储过程中的异常处理 }
return null; return null;
} }
} }

@ -1,5 +1,37 @@
/*
* 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.model; package net.micode.notes.model;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.CallNote;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
public class WorkingNote { public class WorkingNote {
// Note for the working note // Note for the working note
private Note mNote; private Note mNote;
@ -11,11 +43,17 @@ public class WorkingNote {
private int mMode; private int mMode;
private long mAlertDate; private long mAlertDate;
private long mModifiedDate; private long mModifiedDate;
private int mBgColorId; private int mBgColorId;
private int mWidgetId; private int mWidgetId;
private int mWidgetType; private int mWidgetType;
private long mFolderId; private long mFolderId;
private Context mContext; private Context mContext;
private static final String TAG = "WorkingNote"; private static final String TAG = "WorkingNote";
@ -24,7 +62,6 @@ public class WorkingNote {
private NoteSettingChangedListener mNoteSettingStatusListener; private NoteSettingChangedListener mNoteSettingStatusListener;
// 声明 DATA_PROJECTION字符串数组
public static final String[] DATA_PROJECTION = new String[] { public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID, DataColumns.ID,
DataColumns.CONTENT, DataColumns.CONTENT,
@ -35,7 +72,6 @@ public class WorkingNote {
DataColumns.DATA4, DataColumns.DATA4,
}; };
// 声明 NOTE_PROJECTION字符串数组
public static final String[] NOTE_PROJECTION = new String[] { public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID, NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE, NoteColumns.ALERTED_DATE,
@ -46,18 +82,27 @@ public class WorkingNote {
}; };
private static final int DATA_ID_COLUMN = 0; private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1; private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2; private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3; private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0; private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1; private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2; private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
private static final int NOTE_WIDGET_ID_COLUMN = 3; private static final int NOTE_WIDGET_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4; private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5; private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
// New note construct // New note construct
public WorkingNote(Context context, long folderId) { private WorkingNote(Context context, long folderId) {
mContext = context; mContext = context;
mAlertDate = 0; mAlertDate = 0;
mModifiedDate = System.currentTimeMillis(); mModifiedDate = System.currentTimeMillis();
@ -69,7 +114,6 @@ public class WorkingNote {
mWidgetType = Notes.TYPE_WIDGET_INVALIDE; mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
} }
// WorkingNote的构造函数
// Existing note construct // Existing note construct
private WorkingNote(Context context, long noteId, long folderId) { private WorkingNote(Context context, long noteId, long folderId) {
mContext = context; mContext = context;
@ -80,14 +124,11 @@ public class WorkingNote {
loadNote(); loadNote();
} }
// 加载Note
// 通过数据库调用query函数找到第一个条目
private void loadNote() { private void loadNote() {
Cursor cursor = mContext.getContentResolver().query( Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null); null, null);
// 若存在,储存相应信息
if (cursor != null) { if (cursor != null) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
@ -98,7 +139,6 @@ public class WorkingNote {
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
} }
cursor.close(); cursor.close();
// 若不存在,报错
} else { } else {
Log.e(TAG, "No note with id:" + mNoteId); Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId); throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
@ -106,7 +146,6 @@ public class WorkingNote {
loadNoteData(); loadNoteData();
} }
// 加载NoteData
private void loadNoteData() { private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] { DataColumns.NOTE_ID + "=?", new String[] {
@ -114,8 +153,7 @@ public class WorkingNote {
}, null); }, null);
if (cursor != null) { if (cursor != null) {
// 查到信息不为空 if (cursor.moveToFirst()) {
if (cursor.moveToFirst()) { // 查看第一项是否存在
do { do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN); String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) { if (DataConstants.NOTE.equals(type)) {
@ -127,7 +165,7 @@ public class WorkingNote {
} else { } else {
Log.d(TAG, "Wrong note type with type:" + type); Log.d(TAG, "Wrong note type with type:" + type);
} }
} while (cursor.moveToNext());//查阅所有项,直到为空 } while (cursor.moveToNext());
} }
cursor.close(); cursor.close();
} else { } else {
@ -136,12 +174,9 @@ public class WorkingNote {
} }
} }
// 创建空的Note
// 传参context文件夹idwidget背景颜色
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) { int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId); WorkingNote note = new WorkingNote(context, folderId);
// 设定相关属性
note.setBgColorId(defaultBgColorId); note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId); note.setWidgetId(widgetId);
note.setWidgetType(widgetType); note.setWidgetType(widgetType);
@ -152,10 +187,9 @@ public class WorkingNote {
return new WorkingNote(context, id, 0); return new WorkingNote(context, id, 0);
} }
// 保存Note
public synchronized boolean saveNote() { public synchronized boolean saveNote() {
if (isWorthSaving()) { //是否值得保存 if (isWorthSaving()) {
if (!existInDatabase()) { // 是否存在数据库中 if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId); Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false; return false;
@ -178,14 +212,11 @@ public class WorkingNote {
} }
} }
// 是否在数据库中存在
public boolean existInDatabase() { public boolean existInDatabase() {
return mNoteId > 0; return mNoteId > 0;
} }
// 是否值得保存
private boolean isWorthSaving() { private boolean isWorthSaving() {
// 被删除,或(不在数据库中 内容为空),或 本地已保存过
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) { || (existInDatabase() && !mNote.isLocalModified())) {
return false; return false;
@ -194,14 +225,10 @@ public class WorkingNote {
} }
} }
// 设置mNoteSettingStatusListener
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l; mNoteSettingStatusListener = l;
} }
// 设置AlertDate
// 若 mAlertDate与data不同则更改mAlertDate并设定NoteValue
public void setAlertDate(long date, boolean set) { public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) { if (date != mAlertDate) {
mAlertDate = date; mAlertDate = date;
@ -212,20 +239,16 @@ public class WorkingNote {
} }
} }
// 设定删除标记
public void markDeleted(boolean mark) { public void markDeleted(boolean mark) {
// 设定标记
mIsDeleted = mark; mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged(); mNoteSettingStatusListener.onWidgetChanged();
// 调用mNoteSettingStatusListener的 onWidgetChanged方法
} }
} }
// 设定背景颜色
public void setBgColorId(int id) { public void setBgColorId(int id) {
if (id != mBgColorId) { //设定条件 id != mBgColorId if (id != mBgColorId) {
mBgColorId = id; mBgColorId = id;
if (mNoteSettingStatusListener != null) { if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged(); mNoteSettingStatusListener.onBackgroundColorChanged();
@ -234,10 +257,8 @@ public class WorkingNote {
} }
} }
// 设定检查列表模式
// 参数mode
public void setCheckListMode(int mode) { public void setCheckListMode(int mode) {
if (mMode != mode) { //设定条件 mMode != mode if (mMode != mode) {
if (mNoteSettingStatusListener != null) { if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
} }
@ -246,108 +267,81 @@ public class WorkingNote {
} }
} }
// 设定WidgetType
// 参数type
public void setWidgetType(int type) { public void setWidgetType(int type) {
if (type != mWidgetType) {//设定条件 type != mWidgetType if (type != mWidgetType) {
mWidgetType = type; mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
// 调用Note的setNoteValue方法更改WidgetType
} }
} }
// 设定WidgetId
// 参数id
public void setWidgetId(int id) { public void setWidgetId(int id) {
if (id != mWidgetId) {//设定条件 id != mWidgetId if (id != mWidgetId) {
mWidgetId = id; mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
// 调用Note的setNoteValue方法更改WidgetId
} }
} }
// 设定WorkingTex
// 参数更改的text
public void setWorkingText(String text) { public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {//设定条件 mContent, text内容不同 if (!TextUtils.equals(mContent, text)) {
mContent = text; mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent); mNote.setTextData(DataColumns.CONTENT, mContent);
// 调用Note的setTextData方法更改WorkingText
} }
} }
// 转变mNote的CallData及CallNote信息
// 参数String phoneNumber, long callDate
public void convertToCallNote(String phoneNumber, long callDate) { public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
} }
// 判断是否有时钟题型
public boolean hasClockAlert() { public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false); return (mAlertDate > 0 ? true : false);
} }
// 获取Content
public String getContent() { public String getContent() {
return mContent; return mContent;
} }
// 获取AlertDate
public long getAlertDate() { public long getAlertDate() {
return mAlertDate; return mAlertDate;
} }
// 获取ModifiedDate
public long getModifiedDate() { public long getModifiedDate() {
return mModifiedDate; return mModifiedDate;
} }
// 获取背景颜色来源id
public int getBgColorResId() { public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId); return NoteBgResources.getNoteBgResource(mBgColorId);
} }
// 获取背景颜色id
public int getBgColorId() { public int getBgColorId() {
return mBgColorId; return mBgColorId;
} }
// 获取标题背景颜色id
public int getTitleBgResId() { public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId); return NoteBgResources.getNoteTitleBgResource(mBgColorId);
} }
// 获取CheckListMode
public int getCheckListMode() { public int getCheckListMode() {
return mMode; return mMode;
} }
// 获取便签id
public long getNoteId() { public long getNoteId() {
return mNoteId; return mNoteId;
} }
// 获取文件夹id
public long getFolderId() { public long getFolderId() {
return mFolderId; return mFolderId;
} }
// 获取WidgetId
public int getWidgetId() { public int getWidgetId() {
return mWidgetId; return mWidgetId;
} }
// 获取WidgetType
public int getWidgetType() { public int getWidgetType() {
return mWidgetType; return mWidgetType;
} }
// 创建接口 NoteSettingChangedListener,便签更新监视
// 为NoteEditActivity提供接口
// 提供函数有
public interface NoteSettingChangedListener { public interface NoteSettingChangedListener {
/** /**
* Called when the background color of current note has just changed * Called when the background color of current note has just changed

@ -14,168 +14,145 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener; import android.content.DialogInterface.OnDismissListener;
import android.content.Intent; import android.content.Intent;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.PowerManager; import android.os.PowerManager;
import android.provider.Settings; import android.provider.Settings;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.DataUtils;
import java.io.IOException; import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId; //文本在数据库存储中的ID号 public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private String mSnippet; //闹钟提示时出现的文本片段 private long mNoteId;
private static final int SNIPPET_PREW_MAX_LEN = 60; private String mSnippet;
MediaPlayer mPlayer; private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@Override
//当闹钟创建时需要做的初始化 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
//Bundle类型的数据与Map类型的数据相似都是以key-value的形式存储数据的 requestWindowFeature(Window.FEATURE_NO_TITLE);
//onsaveInstanceState方法是用来保存Activity的状态的
//能从onCreate的参数savedInsanceState中获得状态数据 final Window win = getWindow();
requestWindowFeature(Window.FEATURE_NO_TITLE); win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
//界面显示——无标题
if (!isScreenOn()) {
final Window win = getWindow(); win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
if (!isScreenOn()) { //在手机锁屏后如果到了闹钟提示时间,点亮屏幕 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON }
//保持窗体点亮
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON Intent intent = getIntent();
//将窗体点亮
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON try {
//允许窗体点亮时锁屏 mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
} mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
Intent intent = getIntent(); : mSnippet;
} catch (IllegalArgumentException e) {
try { e.printStackTrace();
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); //获得所有的ID return;
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); }
//根据ID从数据库中获取标签的内容
//getContentResolver是实现数据共享实例存储 mPlayer = new MediaPlayer();
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) showActionDialog();
: mSnippet; playAlarmSound();
//判断标签片段是否达到符合长度 } else {
} catch (IllegalArgumentException e) { finish();
e.printStackTrace(); //e.printStackTrace()函数功能是抛出异常, 还将显示出更深的调用信息 }
return; }
}
private boolean isScreenOn() {
mPlayer = new MediaPlayer(); //媒体控制模块 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { //如果ID和type_note在数据库中可见 return pm.isScreenOn();
showActionDialog(); }
//调用内部函数弹出对话框
playAlarmSound(); private void playAlarmSound() {
//调用内部函数函数闹钟音乐 Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
} else {
finish(); int silentModeStreams = Settings.System.getInt(getContentResolver(),
//完成闹钟动作 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
}
} if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
private boolean isScreenOn() { } else {
//判断屏幕是否锁屏,调用系统函数判断 mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); }
return pm.isScreenOn(); try {
} mPlayer.setDataSource(this, url);
mPlayer.prepare();
private void playAlarmSound() { mPlayer.setLooping(true);
//播放闹钟音乐 mPlayer.start();
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); } catch (IllegalArgumentException e) {
//调用系统的铃声管理URI得到闹钟提示音 // TODO Auto-generated catch block
int silentModeStreams = Settings.System.getInt(getContentResolver(), e.printStackTrace();
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); //系统中设置音频流格式的值 } catch (SecurityException e) {
// TODO Auto-generated catch block
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { e.printStackTrace();
//铃声模式是否影响闹钟音量,是则音频流模式设置跟随系统设置,不是则设为默认 } catch (IllegalStateException e) {
mPlayer.setAudioStreamType(silentModeStreams); // TODO Auto-generated catch block
} else { e.printStackTrace();
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } catch (IOException e) {
} // TODO Auto-generated catch block
try { e.printStackTrace();
mPlayer.setDataSource(this, url); }
//设置多媒体数据来源 根据 Uri }
mPlayer.prepare();
//准备同步 private void showActionDialog() {
mPlayer.setLooping(true); AlertDialog.Builder dialog = new AlertDialog.Builder(this);
//设置是否循环播放 dialog.setTitle(R.string.app_name);
mPlayer.start(); dialog.setMessage(mSnippet);
//开始 dialog.setPositiveButton(R.string.notealert_ok, this);
} catch (IllegalArgumentException e) { if (isScreenOn()) {
e.printStackTrace(); dialog.setNegativeButton(R.string.notealert_enter, this);
} catch (SecurityException e) { }
e.printStackTrace(); dialog.show().setOnDismissListener(this);
} catch (IllegalStateException e) { }
e.printStackTrace();
} catch (IOException e) { public void onClick(DialogInterface dialog, int which) {
e.printStackTrace(); switch (which) {
} case DialogInterface.BUTTON_NEGATIVE:
} Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
private void showActionDialog() { intent.putExtra(Intent.EXTRA_UID, mNoteId);
AlertDialog.Builder dialog = new AlertDialog.Builder(this); startActivity(intent);
//新建一个dialog break;
//注意AlertDialog的构造方法全部是Protected的所以不能直接通过new一个AlertDialog来创建出一个AlertDialog default:
//要调用AlertDialog.Builder中的create() break;
dialog.setTitle(R.string.app_name); }
dialog.setMessage(mSnippet); }
//初始化标题,内容
dialog.setPositiveButton(R.string.notealert_ok, this); public void onDismiss(DialogInterface dialog) {
//给对话框添加"Yes"按钮 stopAlarmSound();
if (isScreenOn()) { //如果亮屏,则对话框添加"No"按钮 finish();
dialog.setNegativeButton(R.string.notealert_enter, this); }
}
dialog.show().setOnDismissListener(this); private void stopAlarmSound() {
} if (mPlayer != null) {
mPlayer.stop();
public void onClick(DialogInterface dialog, int which) { mPlayer.release();
switch (which) { mPlayer = null;
//用which来选择click后下一步的操作 }
case DialogInterface.BUTTON_NEGATIVE: //如果是“取消” }
Intent intent = new Intent(this, NoteEditActivity.class); //实现两个类间的数据传输 }
intent.setAction(Intent.ACTION_VIEW); //设置动作属性
intent.putExtra(Intent.EXTRA_UID, mNoteId); //实现key-value对 XTRA_UID为keymNoteId为键
startActivity(intent); //开始动作
break;
default: //如果是“确定”
break;
}
}
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
//停止闹钟声音
finish();
}
private void stopAlarmSound() {
if (mPlayer != null) { //存在媒体对象
mPlayer.stop(); //停止播放
mPlayer.release(); //释放MediaPlayer对象
mPlayer = null;
}
}
}

@ -14,57 +14,52 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
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;
public class AlarmInitReceiver extends BroadcastReceiver { public class AlarmInitReceiver extends BroadcastReceiver {
private static final String [] PROJECTION = new String [] { private static final String [] PROJECTION = new String [] {
NoteColumns.ID, NoteColumns.ID,
NoteColumns.ALERTED_DATE NoteColumns.ALERTED_DATE
}; };
//对数据库的操作调用标签ID和闹钟时间
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override private static final int COLUMN_ID = 0;
public void onReceive(Context context, Intent intent) { //用于接受和处理系统广播 private static final int COLUMN_ALERTED_DATE = 1;
long currentDate = System.currentTimeMillis();
//获取一个当前的毫秒数,即当前时间
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
//将long变量currentDate转化为字符串
null);
//通过查找数据库中的标签内容,找到晚于当前时间的标签
if (c != null) { @Override
if (c.moveToFirst()) { public void onReceive(Context context, Intent intent) {
do { long currentDate = System.currentTimeMillis();
long alertDate = c.getLong(COLUMN_ALERTED_DATE); Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
Intent sender = new Intent(context, AlarmReceiver.class); PROJECTION,
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); //获得一个可广播的intent对象 new String[] { String.valueOf(currentDate) },
AlarmManager alermManager = (AlarmManager) context //设置闹钟 null);
.getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); if (c != null) {
} while (c.moveToNext()); if (c.moveToFirst()) {
} do {
c.close(); long alertDate = c.getLong(COLUMN_ALERTED_DATE);
} Intent sender = new Intent(context, AlarmReceiver.class);
//查询结果不为空,就遍历结果集并为每个标签创建一个闹钟 sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
//这里就是根据数据库里的闹钟时间创建一个闹钟机制 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
} AlarmManager alermManager = (AlarmManager) context
} .getSystemService(Context.ALARM_SERVICE);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
c.close();
}
}
}

@ -14,24 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, AlarmAlertActivity.class);
//启动AlarmAlertActivity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//确保新启动的Activity存在于一个新的任务栈中而不是当前的Activity栈内
//activity要存在于activity的栈中而非activity的途径启动activity时必然不存在一个activity的栈
//所以要新起一个栈装入启动的activity
context.startActivity(intent);
}
}
//用于捕获闹钟触发的事件并在接收到广播时启动相应的Activity来处理闹钟提醒
//作用还需要深究但是对于setClass和addFlags的
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
intent.setClass(context, AlarmAlertActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}

@ -14,473 +14,472 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.Calendar; import java.util.Calendar;
import net.micode.notes.R; import net.micode.notes.R;
import android.content.Context; import android.content.Context;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.NumberPicker; import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout { public class DateTimePicker extends FrameLayout {
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24; private static final int HOURS_IN_HALF_DAY = 12;
private static final int DAYS_IN_ALL_WEEK = 7; private static final int HOURS_IN_ALL_DAY = 24;
private static final int DATE_SPINNER_MIN_VAL = 0; private static final int DAYS_IN_ALL_WEEK = 7;
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; private static final int DATE_SPINNER_MIN_VAL = 0;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
private static final int MINUT_SPINNER_MIN_VAL = 0; private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
private static final int MINUT_SPINNER_MAX_VAL = 59; private static final int MINUT_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MIN_VAL = 0; private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MAX_VAL = 1; private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner; private final NumberPicker mDateSpinner;
private final NumberPicker mMinuteSpinner; private final NumberPicker mHourSpinner;
private final NumberPicker mAmPmSpinner; private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate; private Calendar mDate;
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm; private boolean mIsAm;
private boolean mIs24HourView; private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising; private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener; private OnDateTimeChangedListener mOnDateTimeChangedListener;
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override @Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl(); updateDateControl();
onDateTimeChanged(); onDateTimeChanged();
} }
}; };
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override @Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false; boolean isDateChanged = false;
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
if (!mIs24HourView) { if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true; isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1); cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true; isDateChanged = true;
} }
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm; mIsAm = !mIsAm;
updateAmPmControl(); updateAmPmControl();
} }
} else { } else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true; isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1); cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true; isDateChanged = true;
} }
} }
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour); mDate.set(Calendar.HOUR_OF_DAY, newHour);
onDateTimeChanged(); onDateTimeChanged();
if (isDateChanged) { if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR)); setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH)); setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
} }
} }
}; };
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override @Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue(); int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue(); int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0; int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
if (oldVal == maxValue && newVal == minValue) { offset += 1;
offset += 1; } else if (oldVal == minValue && newVal == maxValue) {
} else if (oldVal == minValue && newVal == maxValue) { offset -= 1;
offset -= 1; }
} if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
if (offset != 0) { mHourSpinner.setValue(getCurrentHour());
mDate.add(Calendar.HOUR_OF_DAY, offset); updateDateControl();
mHourSpinner.setValue(getCurrentHour()); int newHour = getCurrentHourOfDay();
updateDateControl(); if (newHour >= HOURS_IN_HALF_DAY) {
int newHour = getCurrentHourOfDay(); mIsAm = false;
if (newHour >= HOURS_IN_HALF_DAY) { updateAmPmControl();
mIsAm = false; } else {
updateAmPmControl(); mIsAm = true;
} else { updateAmPmControl();
mIsAm = true; }
updateAmPmControl(); }
} mDate.set(Calendar.MINUTE, newVal);
} onDateTimeChanged();
mDate.set(Calendar.MINUTE, newVal); }
onDateTimeChanged(); };
}
}; private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
@Override if (mIsAm) {
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
mIsAm = !mIsAm; } else {
if (mIsAm) { mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); }
} else { updateAmPmControl();
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); onDateTimeChanged();
} }
updateAmPmControl(); };
onDateTimeChanged();
} public interface OnDateTimeChangedListener {
}; void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
public interface OnDateTimeChangedListener { }
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute); public DateTimePicker(Context context) {
} this(context, System.currentTimeMillis());
}
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis()); public DateTimePicker(Context context, long date) {
} this(context, date, DateFormat.is24HourFormat(context));
}
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context)); public DateTimePicker(Context context, long date, boolean is24HourView) {
} super(context);
public DateTimePicker(Context context, long date, boolean is24HourView) { mDate = Calendar.getInstance();
super(context); mInitialising = true;
mDate = Calendar.getInstance(); mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
mInitialising = true; inflate(context, R.layout.datetime_picker, this);
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this); mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner = (NumberPicker) findViewById(R.id.date); mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner = (NumberPicker) findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnLongPressUpdateInterval(100); mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setDisplayedValues(stringsForAmPm); mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
updateDateControl();
updateDateControl(); updateHourControl();
updateHourControl(); updateAmPmControl();
updateAmPmControl();
set24HourView(is24HourView);
set24HourView(is24HourView);
// set to current time
setCurrentDate(date);
setCurrentDate(date);
setEnabled(isEnabled());
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
mInitialising = false; }
}
@Override
@Override public void setEnabled(boolean enabled) {
public void setEnabled(boolean enabled) { if (mIsEnabled == enabled) {
if (mIsEnabled == enabled) { return;
return; }
} super.setEnabled(enabled);
super.setEnabled(enabled); mDateSpinner.setEnabled(enabled);
mDateSpinner.setEnabled(enabled); mMinuteSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled); mHourSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled); mAmPmSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled); mIsEnabled = enabled;
mIsEnabled = enabled; }
}
@Override
@Override public boolean isEnabled() {
public boolean isEnabled() { return mIsEnabled;
return mIsEnabled; }
}
/**
/** * Get the current date in millis
* Get the current date in millis *
* * @return the current date in millis
* @return the current date in millis */
*/ public long getCurrentDateInTimeMillis() {
public long getCurrentDateInTimeMillis() { return mDate.getTimeInMillis();
return mDate.getTimeInMillis(); }
}
/**
/** * Set the current date
* Set the current date *
* * @param date The current date in millis
* @param date The current date in millis */
*/ public void setCurrentDate(long date) {
public void setCurrentDate(long date) { Calendar cal = Calendar.getInstance();
Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(date);
cal.setTimeInMillis(date); setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); }
}
/**
/** * Set the current date
* Set the current date *
* * @param year The current year
* @param year The current year * @param month The current month
* @param month The current month * @param dayOfMonth The current dayOfMonth
* @param dayOfMonth The current dayOfMonth * @param hourOfDay The current hourOfDay
* @param hourOfDay The current hourOfDay * @param minute The current minute
* @param minute The current minute */
*/ public void setCurrentDate(int year, int month,
public void setCurrentDate(int year, int month, int dayOfMonth, int hourOfDay, int minute) {
int dayOfMonth, int hourOfDay, int minute) { setCurrentYear(year);
setCurrentYear(year); setCurrentMonth(month);
setCurrentMonth(month); setCurrentDay(dayOfMonth);
setCurrentDay(dayOfMonth); setCurrentHour(hourOfDay);
setCurrentHour(hourOfDay); setCurrentMinute(minute);
setCurrentMinute(minute); }
}
/**
/** * Get current year
* Get current year *
* * @return The current year
* @return The current year */
*/ public int getCurrentYear() {
public int getCurrentYear() { return mDate.get(Calendar.YEAR);
return mDate.get(Calendar.YEAR); }
}
/**
/** * Set current year
* Set current year *
* * @param year The current year
* @param year The current year */
*/ public void setCurrentYear(int year) {
public void setCurrentYear(int year) { if (!mInitialising && year == getCurrentYear()) {
if (!mInitialising && year == getCurrentYear()) { return;
return; }
} mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.YEAR, year); updateDateControl();
updateDateControl(); onDateTimeChanged();
onDateTimeChanged(); }
}
/**
/** * Get current month in the year
* Get current month in the year *
* * @return The current month in the year
* @return The current month in the year */
*/ public int getCurrentMonth() {
public int getCurrentMonth() { return mDate.get(Calendar.MONTH);
return mDate.get(Calendar.MONTH); }
}
/**
/** * Set current month in the year
* Set current month in the year *
* * @param month The month in the year
* @param month The month in the year */
*/ public void setCurrentMonth(int month) {
public void setCurrentMonth(int month) { if (!mInitialising && month == getCurrentMonth()) {
if (!mInitialising && month == getCurrentMonth()) { return;
return; }
} mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.MONTH, month); updateDateControl();
updateDateControl(); onDateTimeChanged();
onDateTimeChanged(); }
}
/**
/** * Get current day of the month
* Get current day of the month *
* * @return The day of the month
* @return The day of the month */
*/ public int getCurrentDay() {
public int getCurrentDay() { return mDate.get(Calendar.DAY_OF_MONTH);
return mDate.get(Calendar.DAY_OF_MONTH); }
}
/**
/** * Set current day of the month
* Set current day of the month *
* * @param dayOfMonth The day of the month
* @param dayOfMonth The day of the month */
*/ public void setCurrentDay(int dayOfMonth) {
public void setCurrentDay(int dayOfMonth) { if (!mInitialising && dayOfMonth == getCurrentDay()) {
if (!mInitialising && dayOfMonth == getCurrentDay()) { return;
return; }
} mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); updateDateControl();
updateDateControl(); onDateTimeChanged();
onDateTimeChanged(); }
}
/**
/** * Get current hour in 24 hour mode, in the range (0~23)
* Get current hour in 24 hour mode, in the range (0~23) * @return The current hour in 24 hour mode
* @return The current hour in 24 hour mode */
*/ public int getCurrentHourOfDay() {
public int getCurrentHourOfDay() { return mDate.get(Calendar.HOUR_OF_DAY);
return mDate.get(Calendar.HOUR_OF_DAY); }
}
private int getCurrentHour() {
private int getCurrentHour() { if (mIs24HourView){
if (mIs24HourView){ return getCurrentHourOfDay();
return getCurrentHourOfDay(); } else {
} else { int hour = getCurrentHourOfDay();
int hour = getCurrentHourOfDay(); if (hour > HOURS_IN_HALF_DAY) {
if (hour > HOURS_IN_HALF_DAY) { return hour - HOURS_IN_HALF_DAY;
return hour - HOURS_IN_HALF_DAY; } else {
} else { return hour == 0 ? HOURS_IN_HALF_DAY : hour;
return hour == 0 ? HOURS_IN_HALF_DAY : hour; }
} }
} }
}
/**
/** * Set current hour in 24 hour mode, in the range (0~23)
* Set current hour in 24 hour mode, in the range (0~23) *
* * @param hourOfDay
* @param hourOfDay */
*/ public void setCurrentHour(int hourOfDay) {
public void setCurrentHour(int hourOfDay) { if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { return;
return; }
} mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); if (!mIs24HourView) {
if (!mIs24HourView) { if (hourOfDay >= HOURS_IN_HALF_DAY) {
if (hourOfDay >= HOURS_IN_HALF_DAY) { mIsAm = false;
mIsAm = false; if (hourOfDay > HOURS_IN_HALF_DAY) {
if (hourOfDay > HOURS_IN_HALF_DAY) { hourOfDay -= HOURS_IN_HALF_DAY;
hourOfDay -= HOURS_IN_HALF_DAY; }
} } else {
} else { mIsAm = true;
mIsAm = true; if (hourOfDay == 0) {
if (hourOfDay == 0) { hourOfDay = HOURS_IN_HALF_DAY;
hourOfDay = HOURS_IN_HALF_DAY; }
} }
} updateAmPmControl();
updateAmPmControl(); }
} mHourSpinner.setValue(hourOfDay);
mHourSpinner.setValue(hourOfDay); onDateTimeChanged();
onDateTimeChanged(); }
}
/**
/** * Get currentMinute
* Get currentMinute *
* * @return The Current Minute
* @return The Current Minute */
*/ public int getCurrentMinute() {
public int getCurrentMinute() { return mDate.get(Calendar.MINUTE);
return mDate.get(Calendar.MINUTE); }
}
/**
/** * Set current minute
* Set current minute */
*/ public void setCurrentMinute(int minute) {
public void setCurrentMinute(int minute) { if (!mInitialising && minute == getCurrentMinute()) {
if (!mInitialising && minute == getCurrentMinute()) { return;
return; }
} mMinuteSpinner.setValue(minute);
mMinuteSpinner.setValue(minute); mDate.set(Calendar.MINUTE, minute);
mDate.set(Calendar.MINUTE, minute); onDateTimeChanged();
onDateTimeChanged(); }
}
/**
/** * @return true if this is in 24 hour view else false.
* @return true if this is in 24 hour view else false. */
*/ public boolean is24HourView () {
public boolean is24HourView () { return mIs24HourView;
return mIs24HourView; }
}
/**
/** * Set whether in 24 hour or AM/PM mode.
* Set whether in 24 hour or AM/PM mode. *
* * @param is24HourView True for 24 hour mode. False for AM/PM mode.
* @param is24HourView True for 24 hour mode. False for AM/PM mode. */
*/ public void set24HourView(boolean is24HourView) {
public void set24HourView(boolean is24HourView) { if (mIs24HourView == is24HourView) {
if (mIs24HourView == is24HourView) { return;
return; }
} mIs24HourView = is24HourView;
mIs24HourView = is24HourView; mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); int hour = getCurrentHourOfDay();
int hour = getCurrentHourOfDay(); updateHourControl();
updateHourControl(); setCurrentHour(hour);
setCurrentHour(hour); updateAmPmControl();
updateAmPmControl(); }
}
private void updateDateControl() {
private void updateDateControl() { Calendar cal = Calendar.getInstance();
Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); mDateSpinner.setDisplayedValues(null);
mDateSpinner.setDisplayedValues(null); for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { cal.add(Calendar.DAY_OF_YEAR, 1);
cal.add(Calendar.DAY_OF_YEAR, 1); mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); }
} mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setDisplayedValues(mDateDisplayValues); mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); mDateSpinner.invalidate();
mDateSpinner.invalidate(); }
}
private void updateAmPmControl() {
private void updateAmPmControl() { if (mIs24HourView) {
if (mIs24HourView) { mAmPmSpinner.setVisibility(View.GONE);
mAmPmSpinner.setVisibility(View.GONE); } else {
} else { int index = mIsAm ? Calendar.AM : Calendar.PM;
int index = mIsAm ? Calendar.AM : Calendar.PM; mAmPmSpinner.setValue(index);
mAmPmSpinner.setValue(index); mAmPmSpinner.setVisibility(View.VISIBLE);
mAmPmSpinner.setVisibility(View.VISIBLE); }
} }
}
private void updateHourControl() {
private void updateHourControl() { if (mIs24HourView) {
if (mIs24HourView) { mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); } else {
} else { mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); }
} }
}
/**
/** * Set the callback that indicates the 'Set' button has been pressed.
* Set the callback that indicates the 'Set' button has been pressed. * @param callback the callback, if null will do nothing
* @param callback the callback, if null will do nothing */
*/ public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { mOnDateTimeChangedListener = callback;
mOnDateTimeChangedListener = callback; }
}
private void onDateTimeChanged() {
private void onDateTimeChanged() { if (mOnDateTimeChangedListener != null) {
if (mOnDateTimeChangedListener != null) { mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); }
} }
} }
}

@ -14,77 +14,77 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import java.util.Calendar; import java.util.Calendar;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker; import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener { public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance(); private Calendar mDate = Calendar.getInstance();
private boolean mIs24HourView; private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener; private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker; private DateTimePicker mDateTimePicker;
public interface OnDateTimeSetListener { public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date); void OnDateTimeSet(AlertDialog dialog, long date);
} }
public DateTimePickerDialog(Context context, long date) { public DateTimePickerDialog(Context context, long date) {
super(context); super(context);
mDateTimePicker = new DateTimePicker(context); mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker); setView(mDateTimePicker);
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month, public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) { int dayOfMonth, int hourOfDay, int minute) {
mDate.set(Calendar.YEAR, year); mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month); mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute); mDate.set(Calendar.MINUTE, minute);
updateTitle(mDate.getTimeInMillis()); updateTitle(mDate.getTimeInMillis());
} }
}); });
mDate.setTimeInMillis(date); mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0); mDate.set(Calendar.SECOND, 0);
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
setButton(context.getString(R.string.datetime_dialog_ok), this); setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
set24HourView(DateFormat.is24HourFormat(this.getContext())); set24HourView(DateFormat.is24HourFormat(this.getContext()));
updateTitle(mDate.getTimeInMillis()); updateTitle(mDate.getTimeInMillis());
} }
public void set24HourView(boolean is24HourView) { public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView; mIs24HourView = is24HourView;
} }
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack; mOnDateTimeSetListener = callBack;
} }
private void updateTitle(long date) { private void updateTitle(long date) {
int flag = int flag =
DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME; DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
} }
public void onClick(DialogInterface arg0, int arg1) { public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) { if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
} }
} }
} }

@ -14,48 +14,48 @@
* 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.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R; import net.micode.notes.R;
public class DropdownMenu { public class DropdownMenu {
private Button mButton; private Button mButton;
private PopupMenu mPopupMenu; private PopupMenu mPopupMenu;
private Menu mMenu; private Menu mMenu;
public DropdownMenu(Context context, Button button, int menuId) { public DropdownMenu(Context context, Button button, int menuId) {
mButton = button; mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon); mButton.setBackgroundResource(R.drawable.dropdown_icon);
mPopupMenu = new PopupMenu(context, mButton); mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu(); mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
mButton.setOnClickListener(new OnClickListener() { mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
mPopupMenu.show(); mPopupMenu.show();
} }
}); });
} }
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) { if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener); mPopupMenu.setOnMenuItemClickListener(listener);
} }
} }
public MenuItem findItem(int id) { public MenuItem findItem(int id) {
return mMenu.findItem(id); return mMenu.findItem(id);
} }
public void setTitle(CharSequence title) { public void setTitle(CharSequence title) {
mButton.setText(title); mButton.setText(title);
} }
} }

@ -14,66 +14,67 @@
* 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.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import net.micode.notes.R; import net.micode.notes.R;
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;
public class FoldersListAdapter extends CursorAdapter { public class FoldersListAdapter extends CursorAdapter {
public static final String [] PROJECTION = { public static final String [] PROJECTION = {
NoteColumns.ID, NoteColumns.ID,
NoteColumns.SNIPPET NoteColumns.SNIPPET
}; };
public static final int ID_COLUMN = 0; public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1; public static final int NAME_COLUMN = 1;
public FoldersListAdapter(Context context, Cursor c) { public FoldersListAdapter(Context context, Cursor c) {
super(context, c); super(context, c);
} // TODO Auto-generated constructor stub
}
@Override @Override
public View newView(Context context, Cursor cursor, ViewGroup parent) { public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context); return new FolderListItem(context);
} }
@Override @Override
public void bindView(View view, Context context, Cursor cursor) { public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) { if (view instanceof FolderListItem) {
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName); ((FolderListItem) view).bind(folderName);
} }
} }
public String getFolderName(Context context, int position) { public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position); Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
} }
private class FolderListItem extends LinearLayout { private class FolderListItem extends LinearLayout {
private TextView mName; private TextView mName;
public FolderListItem(Context context) { public FolderListItem(Context context) {
super(context); super(context);
inflate(context, R.layout.folder_list_item, this); inflate(context, R.layout.folder_list_item, this);
mName = (TextView) findViewById(R.id.tv_folder_name); mName = (TextView) findViewById(R.id.tv_folder_name);
} }
public void bind(String name) { public void bind(String name) {
mName.setText(name); mName.setText(name);
} }
} }
} }

@ -74,8 +74,6 @@ import java.util.regex.Pattern;
public class NoteEditActivity extends Activity implements OnClickListener, public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener { NoteSettingChangedListener, OnTextViewChangeListener {
//该类主要针对标签编辑
//继承了系统内部和监听有关的类
private class HeadViewHolder { private class HeadViewHolder {
public TextView tvModified; public TextView tvModified;
@ -88,13 +86,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>(); private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static { static {
//使用Map实现数据存储
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
//put函数把指定值和指定键相连
} }
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>(); private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
@ -127,27 +123,19 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private HeadViewHolder mNoteHeaderHolder; private HeadViewHolder mNoteHeaderHolder;
private View mHeadViewPanel; private View mHeadViewPanel;
//私有化界面操作mHeadViewPanel对表头的操作
private View mNoteBgColorSelector; private View mNoteBgColorSelector;
//私有化界面操作mNoteBgColorSelector对背景颜色的操作
private View mFontSizeSelector; private View mFontSizeSelector;
//私有化界面操作mFontSizeSelector对标签字体的操作
private EditText mNoteEditor; private EditText mNoteEditor;
private View mNoteEditorPanel; private View mNoteEditorPanel;
//私有化界面操作mNoteEditorPanel文本编辑的控制板
private WorkingNote mWorkingNote; private WorkingNote mWorkingNote;
//初始化模板WorkingNote
private SharedPreferences mSharedPrefs; private SharedPreferences mSharedPrefs;
//私有化SharedPreferences的数据存储方式
//它的本质是基于XML文件存储key-value键值对数据
private int mFontSizeId; private int mFontSizeId;
//用于操作字体的大小
private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
@ -157,7 +145,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
private LinearLayout mEditTextList; private LinearLayout mEditTextList;
//线性布局
private String mUserQuery; private String mUserQuery;
private Pattern mPattern; private Pattern mPattern;
@ -166,7 +153,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit); this.setContentView(R.layout.note_edit);
//访问数据库
if (savedInstanceState == null && !initActivityState(getIntent())) { if (savedInstanceState == null && !initActivityState(getIntent())) {
finish(); finish();
return; return;
@ -189,7 +176,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return; return;
} }
Log.d(TAG, "Restoring from killed activity"); Log.d(TAG, "Restoring from killed activity");
}//为防止内存不足时程序的终止,在此处保存现场 }
} }
private boolean initActivityState(Intent intent) { private boolean initActivityState(Intent intent) {
@ -201,40 +188,34 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = ""; mUserQuery = "";
//如果用户实例化标签时系统并未给出标签ID
/** /**
* Starting from the searched result * Starting from the searched result
*/ */
//根据键值查找ID
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
} }
//如果ID在数据库中未找到
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class); Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);//程序将跳转到上面声明的Intent jump startActivity(jump);
showToast(R.string.error_note_not_exist); showToast(R.string.error_note_not_exist);
finish(); finish();
return false; return false;
} //如果ID在数据库中找到 } else {
else {
mWorkingNote = WorkingNote.load(this, noteId); mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) { if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);//打印出红色的错误信息 Log.e(TAG, "load note failed with note id" + noteId);
finish(); finish();
return false; return false;
} }
} }
//setSoftInputMode软键盘输入模式
getWindow().setSoftInputMode( getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// New note // New note
// intent.getAction()
// 大多用于broadcast时给intent设置一个action
// 用户可以通过receive intent通过getAction得到的字符串来决定操作
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID); AppWidgetManager.INVALID_APPWIDGET_ID);
@ -243,7 +224,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this)); ResourceParser.getDefaultBgId(this));
// intent.getIntLong、StringExtra是对各变量的语法分析
// Parse call-record note // Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
@ -260,17 +240,15 @@ public class NoteEditActivity extends Activity implements OnClickListener,
finish(); finish();
return false; return false;
} }
} //将电话号码与手机的号码簿相关 } else {
else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId); widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate); mWorkingNote.convertToCallNote(phoneNumber, callDate);
} }
} } else {
else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId); bgResId);
}//创建一个新的WorkingNote }
getWindow().setSoftInputMode( getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
@ -291,10 +269,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
private void initNoteScreen() { private void initNoteScreen() {
//界面初始化
mNoteEditor.setTextAppearance(this, TextAppearanceResources mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId)); .getTexAppearanceResource(mFontSizeId));
//设置外观
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent()); switchToListMode(mWorkingNote.getContent());
} else { } else {
@ -318,18 +294,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/ */
showAlertHeader(); showAlertHeader();
} }
//设置闹钟的显示
private void showAlertHeader() { private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) { if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
if (time > mWorkingNote.getAlertDate()) { //如果系统时间大于闹钟设置时间,闹钟失效 if (time > mWorkingNote.getAlertDate()) {
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else { } else {
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
} }
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);//显示闹钟开启的图标 mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else { } else {
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
@ -350,8 +326,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* generate a id. If the editing note is not worth saving, there * generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note * is no id which is equivalent to create new note
*/ */
//在创建新的标签时,先在数据库中匹配
//如果不存在,那么在数据库中存储
if (!mWorkingNote.existInDatabase()) { if (!mWorkingNote.existInDatabase()) {
saveNote(); saveNote();
} }
@ -360,23 +334,21 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
@Override @Override
//MotionEvent是对屏幕触控的传递机制
public boolean dispatchTouchEvent(MotionEvent ev) { public boolean dispatchTouchEvent(MotionEvent ev) {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) { && !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE); mNoteBgColorSelector.setVisibility(View.GONE);
return true; return true;
}//颜色选择器在屏幕上可见 }
if (mFontSizeSelector.getVisibility() == View.VISIBLE if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) { && !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE); mFontSizeSelector.setVisibility(View.GONE);
return true; return true;
}//字体大小选择器在屏幕上可见 }
return super.dispatchTouchEvent(ev); return super.dispatchTouchEvent(ev);
} }
//对屏幕触控的坐标进行操作
private boolean inRangeOfView(View view, MotionEvent ev) { private boolean inRangeOfView(View view, MotionEvent ev) {
int []location = new int[2]; int []location = new int[2];
view.getLocationOnScreen(location); view.getLocationOnScreen(location);
@ -385,7 +357,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (ev.getX() < x if (ev.getX() < x
|| ev.getX() > (x + view.getWidth()) || ev.getX() > (x + view.getWidth())
|| ev.getY() < y || ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {//如果触控的位置超出了给定的范围返回false || ev.getY() > (y + view.getHeight())) {
return false; return false;
} }
return true; return true;
@ -405,13 +377,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
for (int id : sBgSelectorBtnsMap.keySet()) { for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id); ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this); iv.setOnClickListener(this);
}//初始化标签属性内容 }
mFontSizeSelector = findViewById(R.id.font_size_selector); mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) { for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id); View view = findViewById(id);
view.setOnClickListener(this); view.setOnClickListener(this);
};//选择字体大小 };
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/** /**
@ -434,7 +406,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
clearSettingState(); clearSettingState();
} }
//和桌面小工具的同步
private void updateWidget() { private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
@ -510,7 +481,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
@Override @Override
//准备选择菜单
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) { if (isFinishing()) {
return true; return true;
@ -519,7 +489,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
menu.clear(); menu.clear();
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu); getMenuInflater().inflate(R.menu.call_note_edit, menu);
// MenuInflater用来实例化Menu目录下的Menu布局文件
} else { } else {
getMenuInflater().inflate(R.menu.note_edit, menu); getMenuInflater().inflate(R.menu.note_edit, menu);
} }
@ -537,67 +506,45 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
@Override @Override
//动态改变菜单选项内容
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
//根据菜单id来编辑相关项目
case R.id.menu_new_note: case R.id.menu_new_note:
//创建便签
createNewNote(); createNewNote();
break; break;
case R.id.menu_delete: case R.id.menu_delete:
//删除便签
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
//创建关于删除操作的对话框
builder.setTitle(getString(R.string.alert_title_delete)); builder.setTitle(getString(R.string.alert_title_delete));
// 设置标签的标题为alert_title_delete
builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setIcon(android.R.drawable.ic_dialog_alert);
//设置对话框图标
builder.setMessage(getString(R.string.alert_message_delete_note)); builder.setMessage(getString(R.string.alert_message_delete_note));
//设置对话框内容
builder.setPositiveButton(android.R.string.ok, builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener()//建立按键监听器 new DialogInterface.OnClickListener() {
{
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
// 点击所触发事件
// 删除当前便签
deleteCurrentNote(); deleteCurrentNote();
finish(); finish();
} }
}); });
builder.setNegativeButton(android.R.string.cancel, null); builder.setNegativeButton(android.R.string.cancel, null);
//添加否定按钮
builder.show(); builder.show();
//显示对话框
break; break;
case R.id.menu_font_size: case R.id.menu_font_size:
//字体大小的编辑
mFontSizeSelector.setVisibility(View.VISIBLE); mFontSizeSelector.setVisibility(View.VISIBLE);
// 将字体选择器置为可见
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
// 通过id找到相应的大小
break; break;
case R.id.menu_list_mode: case R.id.menu_list_mode:
//选择列表模式
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0); TextNote.MODE_CHECK_LIST : 0);
break; break;
case R.id.menu_share: case R.id.menu_share:
//菜单共享
getWorkingText(); getWorkingText();
sendTo(this, mWorkingNote.getContent()); sendTo(this, mWorkingNote.getContent());
// 用SendTo函数将运行文本发送到遍历的本文内
break; break;
case R.id.menu_send_to_desktop: case R.id.menu_send_to_desktop:
//发送到桌面
sendToDesktop(); sendToDesktop();
break; break;
case R.id.menu_alert: case R.id.menu_alert:
//创建提醒器
setReminder(); setReminder();
break; break;
case R.id.menu_delete_remind: case R.id.menu_delete_remind:
//删除日期提醒
mWorkingNote.setAlertDate(0, false); mWorkingNote.setAlertDate(0, false);
break; break;
default: default:
@ -606,17 +553,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return true; return true;
} }
//建立事件提醒器
private void setReminder() { private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
// 建立修改时间日期的对话框
d.setOnDateTimeSetListener(new OnDateTimeSetListener() { d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) { public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true); mWorkingNote.setAlertDate(date , true);
//选择提醒的日期
} }
}); });
//建立时间日期的监听器
d.show(); d.show();
} }
@ -624,112 +567,85 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* Share note to apps that support {@link Intent#ACTION_SEND} action * Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type * and {@text/plain} type
*/ */
//共享便签
private void sendTo(Context context, String info) { private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND); Intent intent = new Intent(Intent.ACTION_SEND);
//建立intent链接选项
intent.putExtra(Intent.EXTRA_TEXT, info); intent.putExtra(Intent.EXTRA_TEXT, info);
//将需要传递的便签信息放入text文件中
intent.setType("text/plain"); intent.setType("text/plain");
//编辑连接器的类型
context.startActivity(intent); context.startActivity(intent);
//在acti中进行链接
} }
//创建一个新的便签
private void createNewNote() { private void createNewNote() {
// Firstly, save current editing notes // Firstly, save current editing notes
//保存当前便签
saveNote(); saveNote();
// For safety, start a new NoteEditActivity // For safety, start a new NoteEditActivity
finish(); finish();
Intent intent = new Intent(this, NoteEditActivity.class); Intent intent = new Intent(this, NoteEditActivity.class);
//设置链接器
intent.setAction(Intent.ACTION_INSERT_OR_EDIT); intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
//该活动定义为创建或编辑
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
//将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中
startActivity(intent); startActivity(intent);
//开始activity并链接
} }
//删除当前便签
private void deleteCurrentNote() { private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) //假如当前运行的便签内存有数据 if (mWorkingNote.existInDatabase()) {
{
HashSet<Long> ids = new HashSet<Long>(); HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId(); long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) { if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);//如果不是头文件夹建立一个hash表把便签id存起来 ids.add(id);
} else { } else {
Log.d(TAG, "Wrong note id, should not happen");//否则报错 Log.d(TAG, "Wrong note id, should not happen");
} }
if (!isSyncMode()) { if (!isSyncMode()) {
//非同步模式
//删除操作
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error"); Log.e(TAG, "Delete Note error");
} }
} else { } else {
//同步模式
//移动至垃圾文件夹的操作
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens"); Log.e(TAG, "Move notes to trash folder error, should not happens");
} }
} }
} }
mWorkingNote.markDeleted(true); mWorkingNote.markDeleted(true);
//将这些标签的删除标记置为true
} }
//判断是否为同步模式 private boolean isSyncMode() {
private boolean isSyncMode() {//查看NotesPreferenceActivity中同步名称是否为空
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
} }
//设置提醒时间
public void onClockAlertChanged(long date, boolean set) { public void onClockAlertChanged(long date, boolean set) {
/** /**
* User could set clock to an unsaved note, so before setting the * User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first * alert clock, we should save the note first
*/ */
if (!mWorkingNote.existInDatabase()) { if (!mWorkingNote.existInDatabase()) {
//首先保存已有的便签
saveNote(); saveNote();
} }
if (mWorkingNote.getNoteId() > 0) { if (mWorkingNote.getNoteId() > 0) {
Intent intent = new Intent(this, AlarmReceiver.class); Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
//若有有运行的便签就是建立一个链接器将标签id都存在uri中
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
//设置提醒管理器
showAlertHeader(); showAlertHeader();
if(!set) { if(!set) {
alarmManager.cancel(pendingIntent); alarmManager.cancel(pendingIntent);
} else { } else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
} }
//如果用户设置了时间,就通过提醒管理器设置一个监听事项
} else { } else {
/** /**
* There is the condition that user has input nothing (the note is * There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he * not worthy saving), we have no note id, remind the user that he
* should input something * should input something
*/ */
//没有运行的便签就报错
Log.e(TAG, "Clock alert setting error"); Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock); showToast(R.string.error_note_empty_for_clock);
} }
} }
//Widget发生改变的所触发的事件
public void onWidgetChanged() { public void onWidgetChanged() {
updateWidget(); updateWidget();
}//更新Widget }
//删除编辑文本框所触发的事件
public void onEditTextDelete(int index, String text) { public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount(); int childCount = mEditTextList.getChildCount();
if (childCount == 1) { if (childCount == 1) {
@ -738,11 +654,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
for (int i = index + 1; i < childCount; i++) { for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);//通过id把编辑框存在便签编辑框中 .setIndex(i - 1);
} }
mEditTextList.removeViewAt(index); mEditTextList.removeViewAt(index);
//删除特定位置的视图
NoteEditText edit = null; NoteEditText edit = null;
if(index == 0) { if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
@ -750,14 +665,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} else { } else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text); R.id.et_edit_text);
}//通过id把编辑框存在空的NoteEditText中 }
int length = edit.length(); int length = edit.length();
edit.append(text); edit.append(text);
edit.requestFocus();//请求优先级 edit.requestFocus();
edit.setSelection(length);//定位到length位置处的条目 edit.setSelection(length);
} }
//进入编辑文本框所触发的事件
public void onEditTextEnter(int index, String text) { public void onEditTextEnter(int index, String text) {
/** /**
* Should not happen, check for debug * Should not happen, check for debug
@ -768,66 +682,53 @@ public class NoteEditActivity extends Activity implements OnClickListener,
View view = getListItem(text, index); View view = getListItem(text, index);
mEditTextList.addView(view, index); mEditTextList.addView(view, index);
//建立一个新的视图并添加到编辑文本框内
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.requestFocus();//请求优先操作 edit.requestFocus();
edit.setSelection(0);//定位到起始位置 edit.setSelection(0);
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);//遍历子文本框并设置对应对下标 .setIndex(i);
} }
} }
//切换至列表模式
private void switchToListMode(String text) { private void switchToListMode(String text) {
mEditTextList.removeAllViews(); mEditTextList.removeAllViews();
String[] items = text.split("\n"); String[] items = text.split("\n");
int index = 0; int index = 0;
//清空所有视图,初始化下标
for (String item : items) { for (String item : items) {
if(!TextUtils.isEmpty(item)) { if(!TextUtils.isEmpty(item)) {
mEditTextList.addView(getListItem(item, index)); mEditTextList.addView(getListItem(item, index));
index++; index++;
//遍历所有文本单元并添加到文本框中
} }
} }
mEditTextList.addView(getListItem("", index)); mEditTextList.addView(getListItem("", index));
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
mNoteEditor.setVisibility(View.GONE);//便签编辑器不可见 mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);//将文本编辑框置为可见 mEditTextList.setVisibility(View.VISIBLE);
} }
//获取高亮效果的反馈情况
private Spannable getHighlightQueryResult(String fullText, String userQuery) { private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
//新建效果选项
if (!TextUtils.isEmpty(userQuery)) { if (!TextUtils.isEmpty(userQuery)) {
mPattern = Pattern.compile(userQuery); mPattern = Pattern.compile(userQuery);
//将用户的询问进行解析
Matcher m = mPattern.matcher(fullText); Matcher m = mPattern.matcher(fullText);
//建立一个状态机检查Pattern并进行匹配
int start = 0; int start = 0;
while (m.find(start)) { while (m.find(start)) {
spannable.setSpan( spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor( new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(), R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE); Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景颜色
start = m.end(); start = m.end();
//跟新起始位置
} }
} }
return spannable; return spannable;
} }
//获取列表项
private View getListItem(String item, int index) { private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
//创建一个视图
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
//创建一个文本编辑框并设置可见性
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@ -838,10 +739,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
} }
}); });
//建立一个打钩框并设置监听器
if (item.startsWith(TAG_CHECKED)) { if (item.startsWith(TAG_CHECKED)) {
//选择勾选
cb.setChecked(true); cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim(); item = item.substring(TAG_CHECKED.length(), item.length()).trim();
@ -854,79 +753,61 @@ public class NoteEditActivity extends Activity implements OnClickListener,
edit.setOnTextViewChangeListener(this); edit.setOnTextViewChangeListener(this);
edit.setIndex(index); edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery)); edit.setText(getHighlightQueryResult(item, mUserQuery));
//运行编辑框的监听器对该行为作出反应,并设置下标及文本内容
return view; return view;
} }
//便签内容发生改变所触发的事件
public void onTextChange(int index, boolean hasText) { public void onTextChange(int index, boolean hasText) {
if (index >= mEditTextList.getChildCount()) { if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");//越界报错 Log.e(TAG, "Wrong index, should not happen");
return; return;
} }
if(hasText) { if(hasText) {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else { } else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
//如果内容不为空则将其子编辑框可见性置为可见,否则不可见
} }
} }
//检查模式和列表模式的切换
public void onCheckListModeChanged(int oldMode, int newMode) { public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) { if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString()); switchToListMode(mNoteEditor.getText().toString());
//检查模式切换到列表模式
} else { } else {
if (!getWorkingText()) { if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
"")); ""));
} }
//若是获取到文本就改变其检查标记
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE); mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE); mNoteEditor.setVisibility(View.VISIBLE);
//修改文本编辑器的内容和可见性
} }
} }
//设置勾选选项表并返回是否勾选的标记
private boolean getWorkingText() { private boolean getWorkingText() {
boolean hasChecked = false; boolean hasChecked = false;
//初始化check标记,默认为false
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 若模式为CHECK_LIST
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
//创建可变字符串
for (int i = 0; i < mEditTextList.getChildCount(); i++) { for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i); View view = mEditTextList.getChildAt(i);
//遍历所有子编辑框的视图
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {//若文本不为空 if (!TextUtils.isEmpty(edit.getText())) {
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
//该选项框已打钩
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;//扩展字符串为已打钩并把标记置true hasChecked = true;
} else { } else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
//扩展字符串添加未打钩
} }
} }
} }
mWorkingNote.setWorkingText(sb.toString()); mWorkingNote.setWorkingText(sb.toString());
//利用编辑好的字符串设置运行便签的内容
} else { } else {
mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
// 若不是该模式直接用编辑器中的内容设置运行中标签的内容
} }
return hasChecked; return hasChecked;
} }
//保存便签
private boolean saveNote() { private boolean saveNote() {
getWorkingText(); getWorkingText();
boolean saved = mWorkingNote.saveNote(); boolean saved = mWorkingNote.saveNote();
//运行 getWorkingText()之后保存
if (saved) { if (saved) {
/** /**
* There are two modes from List view to edit view, open one note, * There are two modes from List view to edit view, open one note,
@ -935,13 +816,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* new node requires to the top of the list. This code * new node requires to the top of the list. This code
* {@link #RESULT_OK} is used to identify the create/edit state * {@link #RESULT_OK} is used to identify the create/edit state
*/ */
//RESULT_OK是为了识别保存的情况:一是创建后保存,二是修改后保存
setResult(RESULT_OK); setResult(RESULT_OK);
} }
return saved; return saved;
} }
//将便签发送至桌面
private void sendToDesktop() { private void sendToDesktop() {
/** /**
* Before send message to home, we should make sure that current * Before send message to home, we should make sure that current
@ -949,15 +828,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* save it * save it
*/ */
if (!mWorkingNote.existInDatabase()) { if (!mWorkingNote.existInDatabase()) {
saveNote();//若不存在数据(新标签)就保存 saveNote();
} }
if (mWorkingNote.getNoteId() > 0) { if (mWorkingNote.getNoteId() > 0) {
Intent sender = new Intent(); Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class); Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
//建立发送到桌面的连接器
shortcutIntent.setAction(Intent.ACTION_VIEW); shortcutIntent.setAction(Intent.ACTION_VIEW);
//链接为一个视图
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
@ -965,12 +842,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
sender.putExtra("duplicate", true); sender.putExtra("duplicate", true);
//将便签的相关信息都添加到要发送的文件里
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
//设置sneder的行为是发送
showToast(R.string.info_note_enter_desktop); showToast(R.string.info_note_enter_desktop);
sendBroadcast(sender); sendBroadcast(sender);
//显示到桌面
} else { } else {
/** /**
* There is the condition that user has input nothing (the note is * There is the condition that user has input nothing (the note is
@ -982,21 +856,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
} }
//编辑小图标的标题
private String makeShortcutIconTitle(String content) { private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, ""); content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, ""); content = content.replace(TAG_UNCHECKED, "");
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content; SHORTCUT_ICON_TITLE_MAX_LEN) : content;
//直接设置为content中的内容并返回有勾选和未勾选2种
} }
//显示提示的视图(根据下标显示对应的提示)
private void showToast(int resId) { private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT); showToast(resId, Toast.LENGTH_SHORT);
} }
//持续显示提示的视图(根据下标和持续的时间duration编辑提示视图并显示)
private void showToast(int resId, int duration) { private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show(); Toast.makeText(this, resId, duration).show();
} }

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

@ -41,7 +41,7 @@ public class NoteItemData {
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;
@ -75,9 +75,8 @@ public class NoteItemData {
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);
@ -93,11 +92,10 @@ public class NoteItemData {
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)) {//mphonenumber里有符合字符串则用contart功能连接 if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber); mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) { if (mName == null) {
mName = mPhoneNumber; mName = mPhoneNumber;
@ -110,35 +108,32 @@ public class NoteItemData {
} }
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) {//若光标满足系统或note格式 || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) { if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;//若是数据行数大于但前位置+1则设置成正确 mIsMultiNotesFollowingFolder = true;
} else { } else {
mIsOneNoteFollowingFolder = true;//否则单一文件夹标记为true mIsOneNoteFollowingFolder = 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() { public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder; return mIsOneNoteFollowingFolder;
} }
@ -219,7 +214,6 @@ public class NoteItemData {
return (mAlertDate > 0); return (mAlertDate > 0);
} }
//若数据父id为保存至文件夹模式的id且满足电话号码单元不为空则isCallRecord为true
public boolean isCallRecord() { public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
} }

@ -77,12 +77,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.HashSet; import java.util.HashSet;
//主界面,一进入就是这个界面
/** public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
* @author k
*
*/
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { //没有用特定的标签加注释。。。感觉没有什么用
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
private static final int FOLDER_LIST_QUERY_TOKEN = 1; private static final int FOLDER_LIST_QUERY_TOKEN = 1;
@ -93,7 +89,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private static final int MENU_FOLDER_CHANGE_NAME = 2; private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; //单行超过80个字符 private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
private enum ListEditState { private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
@ -140,13 +136,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private final static int REQUEST_CODE_NEW_NODE = 103; private final static int REQUEST_CODE_NEW_NODE = 103;
@Override @Override
// 创建类 protected void onCreate(Bundle savedInstanceState) {
protected void onCreate(final Bundle savedInstanceState) { //需要是final类型 根据程序上下文环境Java关键字final有“这是无法改变的”或者“终态的”含义它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变设计或效率。 super.onCreate(savedInstanceState);
// final类不能被继承没有子类final类中的方法默认是final的。
//final方法不能被子类的方法覆盖但可以被继承。
//final成员变量表示常量只能被赋值一次赋值后值不再改变。
//final不能用于修饰构造方法。
super.onCreate(savedInstanceState); // 调用父类的onCreate函数
setContentView(R.layout.note_list); setContentView(R.layout.note_list);
initResources(); initResources();
@ -157,32 +148,26 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
@Override @Override
// 返回一些子模块完成的数据交给主Activity处理
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 结果值 和 要求值 符合要求 if (resultCode == RESULT_OK
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null); mNotesListAdapter.changeCursor(null);
} else { } else {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
// 调用 Activity 的onActivityResult
} }
} }
private void setAppInfoFromRawRes() { private void setAppInfoFromRawRes() {
// Android平台给我们提供了一个SharedPreferences类它是一个轻量级的存储类特别适合用于保存软件配置参数。
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
InputStream in = null; InputStream in = null;
try { try {
// 把资源文件放到应用程序的/raw/raw下那么就可以在应用中使用getResources获取资源后, in = getResources().openRawResource(R.raw.introduction);
// 以openRawResource方法不带后缀的资源文件名打开这个文件。
in = getResources().openRawResource(R.raw.introduction);
if (in != null) { if (in != null) {
InputStreamReader isr = new InputStreamReader(in); InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr); BufferedReader br = new BufferedReader(isr);
char [] buf = new char[1024]; // 自行定义的数值,使用者不知道有什么意义 char [] buf = new char[1024];
int len = 0; int len = 0;
while ((len = br.read(buf)) > 0) { while ((len = br.read(buf)) > 0) {
sb.append(buf, 0, len); sb.append(buf, 0, len);
@ -195,7 +180,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
e.printStackTrace(); e.printStackTrace();
return; return;
} finally { } finally {
if (in != null) { if(in != null) {
try { try {
in.close(); in.close();
} catch (IOException e) { } catch (IOException e) {
@ -205,13 +190,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
// 创建空的WorkingNote
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED); ResourceParser.RED);
note.setWorkingText(sb.toString()); note.setWorkingText(sb.toString());
if (note.saveNote()) { if (note.saveNote()) {
// 更新保存note的信息
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit();
} else { } else {
Log.e(TAG, "Save introduction note error"); Log.e(TAG, "Save introduction note error");
@ -226,21 +209,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
startAsyncNotesListQuery(); startAsyncNotesListQuery();
} }
// 初始化资源
private void initResources() { private void initResources() {
mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西 mContentResolver = this.getContentResolver();
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());
mCurrentFolderId = Notes.ID_ROOT_FOLDER; mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mNotesListView = (ListView) findViewById(R.id.notes_list);
// findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名
mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView作为Item的容器
mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null),
null, false); null, false);
mNotesListView.setOnItemClickListener(new OnListItemClickListener()); mNotesListView.setOnItemClickListener(new OnListItemClickListener());
mNotesListView.setOnItemLongClickListener(this); mNotesListView.setOnItemLongClickListener(this);
mNotesListAdapter = new NotesListAdapter(this); mNotesListAdapter = new NotesListAdapter(this);
mNotesListView.setAdapter(mNotesListAdapter); mNotesListView.setAdapter(mNotesListAdapter);
mAddNewNote = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮 mAddNewNote = (Button) findViewById(R.id.btn_new_note);
mAddNewNote.setOnClickListener(this); mAddNewNote.setOnClickListener(this);
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());
mDispatch = false; mDispatch = false;
@ -251,7 +231,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback(); mModeCallBack = new ModeCallback();
} }
// 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu; private DropdownMenu mDropDownMenu;
private ActionMode mActionMode; private ActionMode mActionMode;
@ -280,7 +259,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
(Button) customView.findViewById(R.id.selection_menu), (Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown); R.menu.note_list_dropdown);
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(final MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
updateMenu(); updateMenu();
return true; return true;
@ -290,12 +269,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true; return true;
} }
// 更新菜单
private void updateMenu() { private void updateMenu() {
int selectedCount = mNotesListAdapter.getSelectedCount(); int selectedCount = mNotesListAdapter.getSelectedCount();
// Update dropdown menu // Update dropdown menu
String format = getResources().getString(R.string.menu_select_title, selectedCount); String format = getResources().getString(R.string.menu_select_title, selectedCount);
mDropDownMenu.setTitle(format); // 更改标题 mDropDownMenu.setTitle(format);
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);
if (item != null) { if (item != null) {
if (mNotesListAdapter.isAllSelected()) { if (mNotesListAdapter.isAllSelected()) {
@ -388,7 +366,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
/** /**
* HACKME:When click the transparent part of "New Note" button, dispatch * HACKME:When click the transparent part of "New Note" button, dispatch
* the event to the list view behind this button. The transparent part of * the event to the list view behind this button. The transparent part of
* "New Note" button could be expressed by formula y=-0.12x+94nit:pixel<EFBFBD> * "New Note" button could be expressed by formula y=-0.12x+94Unit:pixel
* and the line top of the button. The coordinate based on left of the "New * and the line top of the button. The coordinate based on left of the "New
* Note" button. The 94 represents maximum height of the transparent part. * Note" button. The 94 represents maximum height of the transparent part.
* Notice that, if the background of the button changes, the formula should * Notice that, if the background of the button changes, the formula should
@ -686,12 +664,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}); });
} }
/* (non-Javadoc)
* @see android.app.Activity#onBackPressed()
*
*/
@Override @Override
public void onBackPressed() { switch (mState) { public void onBackPressed() {
switch (mState) {
case SUB_FOLDER: case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER; mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST; mState = ListEditState.NOTE_LIST;
@ -713,11 +688,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
} }
/**
* @param appWidgetId
* @param appWidgetType
* widgetintent
*/
private void updateWidget(int appWidgetId, int appWidgetType) { private void updateWidget(int appWidgetId, int appWidgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (appWidgetType == Notes.TYPE_WIDGET_2X) { if (appWidgetType == Notes.TYPE_WIDGET_2X) {
@ -737,9 +707,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
setResult(RESULT_OK, intent); setResult(RESULT_OK, intent);
} }
/**
*
*/
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mFocusNoteDataItem != null) { if (mFocusNoteDataItem != null) {
@ -759,10 +726,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
super.onContextMenuClosed(menu); super.onContextMenuClosed(menu);
} }
/* (non-Javadoc)
* @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
* menu
*/
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) { if (mFocusNoteDataItem == null) {
@ -771,10 +734,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
switch (item.getItemId()) { switch (item.getItemId()) {
case MENU_FOLDER_VIEW: case MENU_FOLDER_VIEW:
openFolder(mFocusNoteDataItem);//打开对应文件 openFolder(mFocusNoteDataItem);
break; break;
case MENU_FOLDER_DELETE: case MENU_FOLDER_DELETE:
AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置确认是否删除的对话框 AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete)); builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_folder)); builder.setMessage(getString(R.string.alert_message_delete_folder));
@ -785,7 +748,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
}); });
builder.setNegativeButton(android.R.string.cancel, null); builder.setNegativeButton(android.R.string.cancel, null);
builder.show();//显示对话框 builder.show();
break; break;
case MENU_FOLDER_CHANGE_NAME: case MENU_FOLDER_CHANGE_NAME:
showCreateOrModifyFolderDialog(false); showCreateOrModifyFolderDialog(false);
@ -855,19 +818,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
return true; return true;
} }
/* (non-Javadoc)
* @see android.app.Activity#onSearchRequested()
* startSearch
*/
@Override @Override
public boolean onSearchRequested() { public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false); startSearch(null, false, null /* appData */, false);
return true; return true;
} }
/**
* 便
*/
private void exportNoteToText() { private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() { new AsyncTask<Void, Void, Integer>() {
@ -910,27 +866,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}.execute(); }.execute();
} }
/**
* @return
*
*/
private boolean isSyncMode() { private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
} }
/**
* PreferenceActivity
*/
private void startPreferenceActivity() { private void startPreferenceActivity() {
Activity from = getParent() != null ? getParent() : this; Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class); Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1); from.startActivityIfNeeded(intent, -1);
} }
/**
* @author k
* 便
*/
private class OnListItemClickListener implements OnItemClickListener { private class OnListItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@ -972,9 +917,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
} }
/**
*
*/
private void startQueryDestinationFolders() { private void startQueryDestinationFolders() {
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection: selection = (mState == ListEditState.NOTE_LIST) ? selection:
@ -993,12 +935,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
NoteColumns.MODIFIED_DATE + " DESC"); NoteColumns.MODIFIED_DATE + " DESC");
} }
/* (non-Javadoc)
* @see android.widget.AdapterView.OnItemLongClickListener#onItemLongClick(android.widget.AdapterView, android.view.View, int, long)
*
* 便ActionModeContextMenu
* ActionMOdeContextMenu
*/
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (view instanceof NotesListItem) { if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData(); mFocusNoteDataItem = ((NotesListItem) view).getItemData();

@ -14,189 +14,171 @@
* 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.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes;
import java.util.Collection;
import java.util.Collection; import java.util.HashMap;
import java.util.HashMap; import java.util.HashSet;
import java.util.HashSet; import java.util.Iterator;
import java.util.Iterator;
public class NotesListAdapter extends CursorAdapter {
public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter";
private static final String TAG = "NotesListAdapter"; private Context mContext;
private Context mContext; private HashMap<Integer, Boolean> mSelectedIndex;
private HashMap<Integer, Boolean> mSelectedIndex; private int mNotesCount;
private int mNotesCount; private boolean mChoiceMode;
private boolean mChoiceMode;
public static class AppWidgetAttribute {
public static class AppWidgetAttribute { public int widgetId;
public int widgetId; public int widgetType;
public int widgetType; };
};
public NotesListAdapter(Context context) {
super(context, null);
public NotesListAdapter(Context context) { mSelectedIndex = new HashMap<Integer, Boolean>();
super(context, null); mContext = context;
mSelectedIndex = new HashMap<Integer, Boolean>(); mNotesCount = 0;
mContext = context; }
mNotesCount = 0;
} @Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
@Override return new NotesListItem(context);
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context); @Override
} public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor);
@Override ((NotesListItem) view).bind(context, itemData, mChoiceMode,
public void bindView(View view, Context context, Cursor cursor) { isSelectedItem(cursor.getPosition()));
if (view instanceof NotesListItem) { }
}
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode, public void setCheckedItem(final int position, final boolean checked) {
isSelectedItem(cursor.getPosition())); mSelectedIndex.put(position, checked);
notifyDataSetChanged();
} }
}
public boolean isInChoiceMode() {
return mChoiceMode;
public void setCheckedItem(final int position, final boolean checked) { }
mSelectedIndex.put(position, checked);
public void setChoiceMode(boolean mode) {
notifyDataSetChanged(); mSelectedIndex.clear();
mChoiceMode = mode;
} }
public void selectAll(boolean checked) {
public boolean isInChoiceMode() { Cursor cursor = getCursor();
return mChoiceMode; for (int i = 0; i < getCount(); i++) {
} if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
setCheckedItem(i, checked);
public void setChoiceMode(boolean mode) { }
mSelectedIndex.clear(); }
mChoiceMode = mode; }
} }
public HashSet<Long> getSelectedItemIds() {
public void selectAll(boolean checked) { HashSet<Long> itemSet = new HashSet<Long>();
Cursor cursor = getCursor(); for (Integer position : mSelectedIndex.keySet()) {
for (int i = 0; i < getCount(); i++) { if (mSelectedIndex.get(position) == true) {
if (cursor.moveToPosition(i)) { Long id = getItemId(position);
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { if (id == Notes.ID_ROOT_FOLDER) {
setCheckedItem(i, checked); Log.d(TAG, "Wrong item id, should not happen");
} } else {
} itemSet.add(id);
} }
} }
}
public HashSet<Long> getSelectedItemIds() { return itemSet;
HashSet<Long> itemSet = new HashSet<Long>(); }
for (Integer position : mSelectedIndex.keySet()) { public HashSet<AppWidgetAttribute> getSelectedWidget() {
if (mSelectedIndex.get(position) == true) { HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
Long id = getItemId(position); for (Integer position : mSelectedIndex.keySet()) {
if (id == Notes.ID_ROOT_FOLDER) { if (mSelectedIndex.get(position) == true) {
Log.d(TAG, "Wrong item id, should not happen"); Cursor c = (Cursor) getItem(position);
} else { if (c != null) {
itemSet.add(id); AppWidgetAttribute widget = new AppWidgetAttribute();
} NoteItemData item = new NoteItemData(mContext, c);
} widget.widgetId = item.getWidgetId();
} widget.widgetType = item.getWidgetType();
itemSet.add(widget);
return itemSet; /**
} * Don't close cursor here, only the adapter could close it
*/
} else {
public HashSet<AppWidgetAttribute> getSelectedWidget() { Log.e(TAG, "Invalid cursor");
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>(); return null;
for (Integer position : mSelectedIndex.keySet()) { }
if (mSelectedIndex.get(position) == true) { }
Cursor c = (Cursor) getItem(position); }
if (c != null) { return itemSet;
AppWidgetAttribute widget = new AppWidgetAttribute(); }
NoteItemData item = new NoteItemData(mContext, c);
widget.widgetId = item.getWidgetId(); public int getSelectedCount() {
widget.widgetType = item.getWidgetType(); Collection<Boolean> values = mSelectedIndex.values();
itemSet.add(widget); if (null == values) {
} else { return 0;
Log.e(TAG, "Invalid cursor"); }
return null; Iterator<Boolean> iter = values.iterator();
} int count = 0;
} while (iter.hasNext()) {
} if (true == iter.next()) {
return itemSet; count++;
} }
}
public int getSelectedCount() { return count;
Collection<Boolean> values = mSelectedIndex.values(); }
if (null == values) {
return 0; public boolean isAllSelected() {
} int checkedCount = getSelectedCount();
Iterator<Boolean> iter = values.iterator(); return (checkedCount != 0 && checkedCount == mNotesCount);
int count = 0; }
while (iter.hasNext()) {
if (true == iter.next()) { public boolean isSelectedItem(final int position) {
count++; if (null == mSelectedIndex.get(position)) {
} return false;
} }
return count; return mSelectedIndex.get(position);
} }
@Override
public boolean isAllSelected() { protected void onContentChanged() {
int checkedCount = getSelectedCount(); super.onContentChanged();
return (checkedCount != 0 && checkedCount == mNotesCount); calcNotesCount();
} }
@Override
public boolean isSelectedItem(final int position) { public void changeCursor(Cursor cursor) {
if (null == mSelectedIndex.get(position)) { super.changeCursor(cursor);
return false; calcNotesCount();
} }
return mSelectedIndex.get(position);
} private void calcNotesCount() {
mNotesCount = 0;
@Override for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);
if (c != null) {
protected void onContentChanged() { if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
super.onContentChanged(); mNotesCount++;
}
calcNotesCount(); } else {
} Log.e(TAG, "Invalid cursor");
return;
@Override }
public void changeCursor(Cursor cursor) { }
super.changeCursor(cursor); }
calcNotesCount(); }
}
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
}
} else {
Log.e(TAG, "Invalid cursor");
return;
}
}
}
}

@ -14,118 +14,109 @@
* 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.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources; import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
public class NotesListItem extends LinearLayout {
public class NotesListItem extends LinearLayout { private ImageView mAlert;
private ImageView mAlert; private TextView mTitle;
private TextView mTitle; private TextView mTime;
private TextView mTime; private TextView mCallName;
private TextView mCallName; private NoteItemData mItemData;
private NoteItemData mItemData; private CheckBox mCheckBox;
private CheckBox mCheckBox;
public NotesListItem(Context context) {
super(context);
public NotesListItem(Context context) { inflate(context, R.layout.note_item, this);
super(context); mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
inflate(context, R.layout.note_item, this); mTitle = (TextView) findViewById(R.id.tv_title);
mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTime = (TextView) findViewById(R.id.tv_time);
mTitle = (TextView) findViewById(R.id.tv_title); mCallName = (TextView) findViewById(R.id.tv_name);
mTime = (TextView) findViewById(R.id.tv_time); mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
mCallName = (TextView) findViewById(R.id.tv_name); }
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
} public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { mCheckBox.setVisibility(View.VISIBLE);
if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setChecked(checked);
mCheckBox.setVisibility(View.VISIBLE); } else {
mCheckBox.setChecked(checked); mCheckBox.setVisibility(View.GONE);
} else { }
mCheckBox.setVisibility(View.GONE);
} mItemData = data;
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mItemData = data; mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mCallName.setVisibility(View.GONE); mTitle.setText(context.getString(R.string.call_record_folder_name)
mAlert.setVisibility(View.VISIBLE); + context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mTitle.setText(context.getString(R.string.call_record_folder_name) mCallName.setText(data.getCallName());
+ context.getString(R.string.format_folder_files_count, data.getNotesCount())); mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mAlert.setImageResource(R.drawable.call_record); mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { if (data.hasAlert()) {
mCallName.setVisibility(View.VISIBLE); mAlert.setImageResource(R.drawable.clock);
mCallName.setText(data.getCallName()); mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); } else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); mAlert.setVisibility(View.GONE);
}
if (data.hasAlert()) { } else {
mAlert.setImageResource(R.drawable.clock); mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
} else {
mAlert.setVisibility(View.GONE); if (data.getType() == Notes.TYPE_FOLDER) {
} mTitle.setText(data.getSnippet()
} else { + context.getString(R.string.format_folder_files_count,
mCallName.setVisibility(View.GONE); data.getNotesCount()));
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); mAlert.setVisibility(View.GONE);
} else {
if (data.getType() == Notes.TYPE_FOLDER) { mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
mTitle.setText(data.getSnippet() if (data.hasAlert()) {
+ context.getString(R.string.format_folder_files_count, mAlert.setImageResource(R.drawable.clock);
data.getNotesCount())); mAlert.setVisibility(View.VISIBLE);
mAlert.setVisibility(View.GONE); } else {
} else { mAlert.setVisibility(View.GONE);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); }
if (data.hasAlert()) { }
mAlert.setImageResource(R.drawable.clock); }
mAlert.setVisibility(View.VISIBLE); mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
} else {
mAlert.setVisibility(View.GONE); setBackground(data);
} }
}
} private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackground(data); setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} } else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
private void setBackground(NoteItemData data) { } else if (data.isFirst() || data.isMultiFollowingFolder()) {
int id = data.getBgColorId(); setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
if (data.getType() == Notes.TYPE_NOTE) { setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
if (data.isSingle() || data.isOneFollowingFolder()) { } else {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); setBackgroundResource(NoteItemBgResources.getFolderBgRes());
} else if (data.isLast()) { }
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); }
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); public NoteItemData getItemData() {
} else { return mItemData;
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); }
} }
} else {
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
public NoteItemData getItemData() {
return mItemData;
}
}

@ -14,409 +14,375 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.ui; package net.micode.notes.ui;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.app.ActionBar; import android.app.ActionBar;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import net.micode.notes.R; import net.micode.notes.R;
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.gtask.remote.GTaskSyncService; import net.micode.notes.gtask.remote.GTaskSyncService;
public class NotesPreferenceActivity extends PreferenceActivity { public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private static final String AUTHORITIES_FILTER_KEY = "authorities"; private static final String AUTHORITIES_FILTER_KEY = "authorities";
private PreferenceCategory mAccountCategory; private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver; private GTaskReceiver mReceiver;
private Account[] mOriAccounts; private Account[] mOriAccounts;
private boolean mHasAddedAccount; private boolean mHasAddedAccount;
@Override
@Override protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
protected void onCreate(Bundle icicle) {
/* using the app icon for navigation */
super.onCreate(icicle); getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true); addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
addPreferencesFromResource(R.xml.preferences); mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
mReceiver = new GTaskReceiver(); registerReceiver(mReceiver, filter);
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); mOriAccounts = null;
registerReceiver(mReceiver, filter); View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
mOriAccounts = null; }
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true); @Override
} protected void onResume() {
super.onResume();
@Override
// need to set sync account automatically if user has added a new
protected void onResume() { // account
if (mHasAddedAccount) {
super.onResume(); Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
if (mHasAddedAccount) { for (Account accountNew : accounts) {
boolean found = false;
Account[] accounts = getGoogleAccounts(); for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
if (mOriAccounts != null && accounts.length > mOriAccounts.length) { found = true;
for (Account accountNew : accounts) { break;
boolean found = false; }
for (Account accountOld : mOriAccounts) { }
if (TextUtils.equals(accountOld.name, accountNew.name)) { if (!found) {
found = true; setSyncAccount(accountNew.name);
break; break;
} }
} }
if (!found) { }
setSyncAccount(accountNew.name); }
break;
} refreshUI();
} }
}
} @Override
protected void onDestroy() {
refreshUI(); if (mReceiver != null) {
unregisterReceiver(mReceiver);
} }
super.onDestroy();
@Override }
protected void onDestroy() { private void loadAccountPreference() {
if (mReceiver != null) { mAccountCategory.removeAll();
unregisterReceiver(mReceiver);
} Preference accountPref = new Preference(this);
super.onDestroy(); final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
} accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
private void loadAccountPreference() { public boolean onPreferenceClick(Preference preference) {
mAccountCategory.removeAll(); if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
Preference accountPref = new Preference(this); // the first time to set account
showSelectAccountAlertDialog();
final String defaultAccount = getSyncAccountName(this); } else {
accountPref.setTitle(getString(R.string.preferences_account_title)); // if the account has already been set, we need to promp
accountPref.setSummary(getString(R.string.preferences_account_summary)); // user about the risk
showChangeAccountConfirmAlertDialog();
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { }
public boolean onPreferenceClick(Preference preference) { } else {
Toast.makeText(NotesPreferenceActivity.this,
if (!GTaskSyncService.isSyncing()) { R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
if (TextUtils.isEmpty(defaultAccount)) { .show();
showSelectAccountAlertDialog(); }
} else { return true;
showChangeAccountConfirmAlertDialog(); }
} });
} else {
Toast.makeText(NotesPreferenceActivity.this, mAccountCategory.addPreference(accountPref);
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) }
.show();
} private void loadSyncButton() {
return true; Button syncButton = (Button) findViewById(R.id.preference_sync_button);
} TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
});
// set button state
mAccountCategory.addPreference(accountPref); if (GTaskSyncService.isSyncing()) {
} syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
private void loadSyncButton() { public void onClick(View v) {
Button syncButton = (Button) findViewById(R.id.preference_sync_button); GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); }
if (GTaskSyncService.isSyncing()) { });
} else {
syncButton.setText(getString(R.string.preferences_button_sync_cancel)); syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() { syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this); GTaskSyncService.startSync(NotesPreferenceActivity.this);
} }
}); });
}
} else { syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() { // set last sync time
public void onClick(View v) { if (GTaskSyncService.isSyncing()) {
GTaskSyncService.startSync(NotesPreferenceActivity.this); lastSyncTimeView.setText(GTaskSyncService.getProgressString());
} lastSyncTimeView.setVisibility(View.VISIBLE);
}); } else {
long lastSyncTime = getLastSyncTime(this);
} if (lastSyncTime != 0) {
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
if (GTaskSyncService.isSyncing()) { lastSyncTime)));
lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE);
lastSyncTimeView.setVisibility(View.VISIBLE); } else {
lastSyncTimeView.setVisibility(View.GONE);
} else { }
}
long lastSyncTime = getLastSyncTime(this); }
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, private void refreshUI() {
DateFormat.format(getString(R.string.preferences_last_sync_time_format), loadAccountPreference();
lastSyncTime))); loadSyncButton();
lastSyncTimeView.setVisibility(View.VISIBLE); }
} else { private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
lastSyncTimeView.setVisibility(View.GONE);
} View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
} TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
} titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
private void refreshUI() { subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
loadAccountPreference();
loadSyncButton(); dialogBuilder.setCustomTitle(titleView);
} dialogBuilder.setPositiveButton(null, null);
private void showSelectAccountAlertDialog() { Account[] accounts = getGoogleAccounts();
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); mHasAddedAccount = false;
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); if (accounts.length > 0) {
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); CharSequence[] items = new CharSequence[accounts.length];
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); final CharSequence[] itemMapping = items;
int checkedItem = -1;
dialogBuilder.setCustomTitle(titleView); int index = 0;
dialogBuilder.setPositiveButton(null, null); for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
Account[] accounts = getGoogleAccounts(); checkedItem = index;
String defAccount = getSyncAccountName(this); }
items[index++] = account.name;
mOriAccounts = accounts; }
mHasAddedAccount = false; dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
if (accounts.length > 0) { public void onClick(DialogInterface dialog, int which) {
CharSequence[] items = new CharSequence[accounts.length]; setSyncAccount(itemMapping[which].toString());
final CharSequence[] itemMapping = items; dialog.dismiss();
int checkedItem = -1; refreshUI();
int index = 0; }
for (Account account : accounts) { });
if (TextUtils.equals(account.name, defAccount)) { }
checkedItem = index;
} View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
items[index++] = account.name; dialogBuilder.setView(addAccountView);
}
dialogBuilder.setSingleChoiceItems(items, checkedItem, final AlertDialog dialog = dialogBuilder.show();
new DialogInterface.OnClickListener() { addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(View v) {
setSyncAccount(itemMapping[which].toString()); mHasAddedAccount = true;
dialog.dismiss(); Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
refreshUI(); intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
} "gmail-ls"
}); });
} startActivityForResult(intent, -1);
dialog.dismiss();
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); }
dialogBuilder.setView(addAccountView); });
}
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() { private void showChangeAccountConfirmAlertDialog() {
public void onClick(View v) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
"gmail-ls" titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
}); getSyncAccountName(this)));
startActivityForResult(intent, -1); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialog.dismiss(); dialogBuilder.setCustomTitle(titleView);
}
}); CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
} getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
private void showChangeAccountConfirmAlertDialog() { };
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); if (which == 0) {
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); showSelectAccountAlertDialog();
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, } else if (which == 1) {
getSyncAccountName(this))); removeSyncAccount();
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); refreshUI();
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); }
}
dialogBuilder.setCustomTitle(titleView); });
dialogBuilder.show();
CharSequence[] menuItemArray = new CharSequence[] { }
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account), private Account[] getGoogleAccounts() {
getString(R.string.preferences_menu_cancel) AccountManager accountManager = AccountManager.get(this);
}; return accountManager.getAccountsByType("com.google");
}
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
if (which == 0) { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
showSelectAccountAlertDialog(); SharedPreferences.Editor editor = settings.edit();
} else if (which == 1) { if (account != null) {
removeSyncAccount(); editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
refreshUI(); } else {
} editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
} }
}); editor.commit();
dialogBuilder.show();
// clean up last sync time
} setLastSyncTime(this, 0);
// clean up local gtask related info
private Account[] getGoogleAccounts() { new Thread(new Runnable() {
AccountManager accountManager = AccountManager.get(this); public void run() {
return accountManager.getAccountsByType("com.google"); ContentValues values = new ContentValues();
} values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
private void setSyncAccount(String account) { }
if (!getSyncAccountName(this).equals(account)) { }).start();
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit(); Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
if (account != null) { Toast.LENGTH_SHORT).show();
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); }
} else { }
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
} private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.commit(); if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
setLastSyncTime(this, 0); }
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
new Thread(new Runnable() { editor.remove(PREFERENCE_LAST_SYNC_TIME);
public void run() { }
ContentValues values = new ContentValues(); editor.commit();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0); // clean up local gtask related info
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); new Thread(new Runnable() {
} public void run() {
}).start(); ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
Toast.makeText(NotesPreferenceActivity.this, getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
getString(R.string.preferences_toast_success_set_accout, account), }
Toast.LENGTH_SHORT).show(); }).start();
}
}
} public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
private void removeSyncAccount() { Context.MODE_PRIVATE);
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
SharedPreferences.Editor editor = settings.edit(); }
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { public static void setLastSyncTime(Context context, long time) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
} Context.MODE_PRIVATE);
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { SharedPreferences.Editor editor = settings.edit();
editor.remove(PREFERENCE_LAST_SYNC_TIME); editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
} editor.commit();
editor.commit(); }
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
new Thread(new Runnable() { Context.MODE_PRIVATE);
public void run() { return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
ContentValues values = new ContentValues(); }
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0); private class GTaskReceiver extends BroadcastReceiver {
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
} @Override
}).start(); public void onReceive(Context context, Intent intent) {
refreshUI();
} if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
public static String getSyncAccountName(Context context) { .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, }
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); }
} }
public boolean onOptionsItemSelected(MenuItem item) {
public static void setLastSyncTime(Context context, long time) { switch (item.getItemId()) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, case android.R.id.home:
Context.MODE_PRIVATE); Intent intent = new Intent(this, NotesListActivity.class);
SharedPreferences.Editor editor = settings.edit(); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); return true;
editor.commit(); default:
return false;
} }
}
public static long getLastSyncTime(Context context) { }
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}

@ -46,7 +46,6 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
private static final String TAG = "NoteWidgetProvider"; private static final String TAG = "NoteWidgetProvider";
@Override @Override
//小组件被删除时被调用
public void onDeleted(Context context, int[] appWidgetIds) { public void onDeleted(Context context, int[] appWidgetIds) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
@ -58,8 +57,6 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
} }
} }
//用于查询与给定小组件ID相关的笔记信息并返回一个Cursor对象
private Cursor getNoteWidgetInfo(Context context, int widgetId) { private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION, PROJECTION,
@ -72,8 +69,6 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
update(context, appWidgetManager, appWidgetIds, false); update(context, appWidgetManager, appWidgetIds, false);
} }
//根据查询结果设置小组件的背景资源、文本内容和点击意图并通过AppWidgetManager的updateAppWidget方法更新小组件的视图
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) { boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) { for (int i = 0; i < appWidgetIds.length; i++) {
@ -111,7 +106,6 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
/** /**
* Generate the pending intent to start host for the widget * Generate the pending intent to start host for the widget
*/ */
//根据小组件ID查询对应的笔记信息并根据查询结果设置相应的视图内容。如果查询结果为空则显示默认的提示文本和插入/编辑操作的意图
PendingIntent pendingIntent = null; PendingIntent pendingIntent = null;
if (privacyMode) { if (privacyMode) {
rv.setTextViewText(R.id.widget_text, rv.setTextViewText(R.id.widget_text,

@ -23,10 +23,9 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser; import net.micode.notes.tool.ResourceParser;
//NoteWidgetProvider_2x类继承自NoteWidgetProvider并覆盖了其中的一些方法
public class NoteWidgetProvider_2x extends NoteWidgetProvider { public class NoteWidgetProvider_2x extends NoteWidgetProvider {
@Override @Override
//小组件需要更新时被调用。它调用父类的update方法并传入相应的参数。
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds); super.update(context, appWidgetManager, appWidgetIds);
} }
@ -37,13 +36,11 @@ public class NoteWidgetProvider_2x extends NoteWidgetProvider {
} }
@Override @Override
//根据给定的背景ID返回相应的背景资源ID。这里调用了ResourceParser类的静态方法getWidget2xBgResource来获取2x小组件的背景资源ID。
protected int getBgResourceId(int bgId) { protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
} }
@Override @Override
//返回小组件的类型
protected int getWidgetType() { protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X; return Notes.TYPE_WIDGET_2X;
} }

@ -23,7 +23,7 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser; import net.micode.notes.tool.ResourceParser;
//跟Provider_2x一模一样类名字不同
public class NoteWidgetProvider_4x extends NoteWidgetProvider { public class NoteWidgetProvider_4x extends NoteWidgetProvider {
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

Loading…
Cancel
Save