Compare commits

..

1 Commits

Author SHA1 Message Date
朱雪婷 65e0c1c5dd test
3 days ago

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

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

@ -1,150 +0,0 @@
<?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>

@ -1,190 +0,0 @@
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

@ -1,23 +0,0 @@
[中文]
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

@ -1,73 +0,0 @@
/*
* 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.data;
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 java.util.HashMap;
public class Contact {
private static HashMap<String, String> sContactCache;
private static final String TAG = "Contact";
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN "
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
public static String getContact(Context context, String phoneNumber) {
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
if (cursor != null && cursor.moveToFirst()) {
try {
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;
}
}
}

@ -1,362 +0,0 @@
/*
* 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.data;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db";
private static final int DB_VERSION = 4;
public interface TABLE {
public static final String NOTE = "note";
public static final String DATA = "data";
}
private static final String TAG = "NotesDatabaseHelper";
private static NotesDatabaseHelper mInstance;
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
DataColumns.MIME_TYPE + " TEXT NOT NULL," +
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_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.DATA1 + " INTEGER," +
DataColumns.DATA2 + " INTEGER," +
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
/**
* Increase folder's note count when move note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
* Decrease folder's note count when move note from folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_update " +
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";
/**
* Increase folder's note count when insert new note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
" AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
* Decrease folder's note count when delete note from the folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";
/**
* Update note's content when insert data with type {@link DataConstants#NOTE}
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA +
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" +
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Delete datas belong to note which has been deleted
*/
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Delete notes belong to folder which has been deleted
*/
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Move notes belong to folder which has been moved to trash folder
*/
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
" AFTER UPDATE ON " + TABLE.NOTE +
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db);
createSystemFolder(db);
Log.d(TAG, "note table has been created");
}
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
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 folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}
private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues();
/**
* call record foler for call notes
*/
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* root folder which is default folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
Log.d(TAG, "data table has been created");
}
private void reCreateDataTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
}
return mInstance;
}
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
createDataTable(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
boolean skipV2 = false;
if (oldVersion == 1) {
upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++;
}
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db);
reCreateTriggers = true;
oldVersion++;
}
if (oldVersion == 3) {
upgradeToV4(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
}
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);
createNoteTable(db);
createDataTable(db);
}
private void upgradeToV3(SQLiteDatabase db) {
// drop unused triggers
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
// add a column for gtask id
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''");
// add a trash system folder
ContentValues values = new ContentValues();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
}

@ -1,158 +0,0 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn()) {
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
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)
: mSnippet;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
}
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
} else {
finish();
}
}
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
private void playAlarmSound() {
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.app_name);
dialog.setMessage(mSnippet);
dialog.setPositiveButton(R.string.notealert_ok, this);
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
dialog.show().setOnDismissListener(this);
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
break;
default:
break;
}
}
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
}
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
}

@ -0,0 +1,16 @@
.
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

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

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,53 @@
plugins {
alias(libs.plugins.android.application)
}
android {
packaging {
resources.excludes.add("META-INF/DEPENDENCIES");
resources.excludes.add("META-INF/NOTICE");
resources.excludes.add("META-INF/LICENSE");
resources.excludes.add("META-INF/LICENSE.txt");
resources.excludes.add("META-INF/NOTICE.txt");
}
namespace = "net.micode.notes"
compileSdk = 34
defaultConfig {
applicationId = "net.micode.notes"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
implementation(files("D:\\Code\\AndroidCode\\Notes-master\\httpcomponents-client-4.5.14-bin\\lib\\httpclient-osgi-4.5.14.jar"))
implementation(files("D:\\Code\\AndroidCode\\Notes-master\\httpcomponents-client-4.5.14-bin\\lib\\httpclient-win-4.5.14.jar"))
implementation(files("D:\\Code\\AndroidCode\\Notes-master\\httpcomponents-client-4.5.14-bin\\lib\\httpcore-4.4.16.jar"))
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -46,7 +46,7 @@
android:theme="@style/NoteTheme"
android:exported="true">
<intent-filter tools:ignore="AppLinkUrlError">
<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" />

@ -24,6 +24,7 @@ import android.telephony.PhoneNumberUtils;
import android.util.Log;
import java.util.HashMap;
//change
public class Contact {//联系人
private static HashMap<String, String> sContactCache;
@ -35,40 +36,42 @@ public class Contact {//联系人
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
// 获取联系人
//获取联系人
public static String getContact(Context context, String phoneNumber) {
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 查找HashMap中是否已有phoneNumber信息
//查找HashMap中是否已有PhoneNumber信息
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
// 查找数据库中phoneNumber的信息
//查找数据库中PhoneNumber信息
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
// 判定查询结果
// moveToFirst()返回第一条
//判定查询结果
//moveToFirst()返回第一条
if (cursor != null && cursor.moveToFirst()) {
try {
// 找到相关信息
//找到相关信息
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;

@ -17,9 +17,11 @@
package net.micode.notes.data;
import android.net.Uri;
//NOTES类中定义了很多变量这些变量大多是int型和string型
public class Notes {
public static final String AUTHORITY = "micode_notes";
public static final String TAG = "Notes";
//以下三个常量对NoteColumns.TYPE的值进行设置时会用到
public static final int TYPE_NOTE = 0;
public static final int TYPE_FOLDER = 1;
public static final int TYPE_SYSTEM = 2;
@ -55,12 +57,13 @@ public class Notes {
* Uri to query all notes and folders
*/
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
//定义查询便签和文件夹的指针
/**
* Uri to query data
*/
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
//定义查找数据的指针
//定义NoteColums的常量,用于后面创建数据库的表头
public interface NoteColumns {
/**
* The unique ID for a row
@ -165,8 +168,8 @@ public class Notes {
* <P> Type : INTEGER (long) </P>
*/
public static final String VERSION = "version";
}
}//这些常量主要是定义便签的属性的
//定义DataColums的常量用于后面创建数据库的表头
public interface DataColumns {
/**
* The unique ID for a row
@ -240,6 +243,7 @@ public class Notes {
*/
public static final String DATA5 = "data5";
}
//主要是定义存储便签内容数据的
public static final class TextNote implements DataColumns {
/**
@ -255,7 +259,7 @@ public class Notes {
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 {
/**
@ -275,5 +279,5 @@ public class Notes {
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");
}
}//电话内容的数据结构
}

@ -15,54 +15,53 @@
*/
package net.micode.notes.data;
import android.content.ContentValues;//就是用于保存一些数据string boolean byte double float int long short ...)信息,这些信息可以被数据库操作时使用。
import android.content.Context;//加载和访问资源。android中主要是这两个功能但是这里具体不清楚
import android.database.sqlite.SQLiteDatabase;//主要提供了对应于添加、删除、更新、查询的操作方法: insert()、delete()、update()和query()。配合content.values
import android.database.sqlite.SQLiteOpenHelper;//用来管理数据的创建和版本更新
import android.util.Log;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
//数据库操作用SQLOpenhelper,对一些note和文件进行数据库的操作比如删除文件后将文件里的note也相应删除
public class NotesDatabaseHelper extends SQLiteOpenHelper {
//定义数据库的名称,这是一个常量,不会发生变化
private static final String DB_NAME = "note.db";
//定义数据库的版本号,用于管理数据库的升级
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 DATA = "data";
}
//定义数据表的名称
private static final String TAG = "NotesDatabaseHelper";
//单例模式的实现,确保只有一个实例存在
private static NotesDatabaseHelper mInstance;
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";//数据库中需要存储的项目的名称,就相当于创建一个表格的表头的内容。
NoteColumns.ID + " INTEGER PRIMARY KEY," + //笔记的唯一标识符
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +//记得ID
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +//提醒日期
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +//背景颜色
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +//创建日期
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +//是否有附件
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +//修改日期
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +//子笔记数量
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +//笔记摘要
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + //笔记类型
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + //小部件ID
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + //小部件类型
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + //同步ID
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + //本地修改状态
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + //原始笔记UD
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + //Google任务ID
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + //版本号
")"; //数据库中需要存储的项目的名称,就相当于创建一个表格的表头的内容
private static Notes.DataColumns DataColumns;
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
@ -77,10 +76,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";//和上面的功能一样,主要是存储的项目不同
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";//存储便签编号的一个数据表格
"CREATE INDEX IF NOT EXISTS note_id_index ON " + //IF NOT EXISTS子句确保只有当索引不存在时才创建避免重复创建索引
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; //存储便签编号的一个数据表格
/**
* Increase folder's note count when move note to the folder
@ -92,7 +90,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";//在文件夹中移入一个Note之后需要更改的数据的表格
" END"; //在文件夹中移入一个Note之后需要更改的数据的表格
/**
* Decrease folder's note count when move note from folder
@ -105,7 +103,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";//在文件夹中移出一个Note之后需要更改的数据的表格。
" END"; //在文件夹中移出一个Note之后需要更改的数据的表格。
/**
* Increase folder's note count when insert new note to the folder
@ -117,7 +115,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";//在文件夹中插入一个Note之后需要更改的数据的表格
" END"; //在文件夹中插入一个Note之后需要更改的数据的表格
/**
* Decrease folder's note count when delete note from the folder
@ -130,7 +128,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";//在文件夹中删除一个Note之后需要更改的数据的表格
" END"; //在文件夹中删除一个Note之后需要更改的数据的表格
/**
* Update note's content when insert data with type {@link DataConstants#NOTE}
@ -143,7 +141,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";//在文件夹中对一个Note导入新的数据之后需要更改的数据的表格
" END";//在文件夹中对一个Note导入新的数据之后需要更改的数据的表格
/**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
@ -156,7 +154,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";//Note数据被修改后需要更改的数据的表格
" END";//Note数据被修改后需要更改的数据的表格
/**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
@ -180,8 +178,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" BEGIN" +
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";//删除已删除的便签的数据后需要更改的数据的表格。
" END";//删除已删除的便签的数据后需要更改的数据的表格
/**
* Delete notes belong to folder which has been deleted
@ -192,7 +189,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" BEGIN" +
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";//删除已删除的文件夹的便签后需要更改的数据的表格
" END";//删除已删除的文件夹的便签后需要更改的数据的表格
/**
* Move notes belong to folder which has been moved to trash folder
@ -205,18 +202,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";//还原垃圾桶中便签后需要更改的数据的表格
" END";//还原垃圾桶中便签后需要更改的数据的表格
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}//构造函数,传入数据库的名称和版本
}
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db);
createSystemFolder(db);
Log.d(TAG, "note table has been created");
}//创建表格(用来存储标签属性)
} //创建表格(用来存储标签属性)
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
@ -234,50 +231,66 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}//execSQL是数据库操作的API主要是更改行为的SQL语句
} //execSQL是数据库操作的API主要是更改行为的SQL语句
//在这里主要是用来重新创建上述定义的表格用的,先删除原来有的数据库的触发器再重新创建新的数据库
//创建系统文件夹,在数据库中创建用于存储系统文件夹信息的记录
private void createSystemFolder(SQLiteDatabase db) {
//初始化ContentValues对象用于存储待插入数据库的键值对数据
ContentValues values = new ContentValues();
/**
* call record foler for call notes
*/
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
//创建一个新的笔记项,并将其类型指定为系统类型
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);//设置笔记项的ID为预定义的呼叫记录文件夹ID
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);//设置笔记项的类型为系统类型
db.insert(TABLE.NOTE, null, values);//将笔记项插入到数据库的NOTE表中
/**
* root folder which is default folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);//这里的NoteColumns.ID表示记录的标识字段Notes.ID_ROOT_FOLDER是根文件夹的特定标识值
//设置当前记录的类型为系统类型
//这里的NoteColumns.TYPE表示记录的类型字段Notes.TYPE_SYSTEM是系统类型的具体值
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
//向数据库中的NOTE表插入一条记录
//使用了之前准备好的values对象包含了要插入的数据
db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*/
//清除现有的值,并为移动笔记到临时文件夹做准备
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
//在数据库的NOTE表中插入一条新的记录这条记录代表一个临时文件夹中的系统类型笔记
db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*/
//清空values对象以避免数据冗余或冲突
values.clear();
//设置新笔记的ID为回收站文件夹IDTYPE为系统类型
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
//将新笔记的信息插入到NOTE表中
db.insert(TABLE.NOTE, null, values);
}//创建几个系统文件夹
//此方法主要用于在数据库中创建数据表包括执行创建表的SQL语句、
// 创建相关的触发器以及为提高查询效率创建索引
public void createDataTable(SQLiteDatabase db) {
//执行创建数据表的SQL语句
db.execSQL(CREATE_DATA_TABLE_SQL);
//重新创建与数据表相关的触发器
reCreateDataTableTriggers(db);
//执行创建数据表上note_id索引的SQL语句以提高查询效率
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
//记录日志,表明数据表已成功创建
Log.d(TAG, "data table has been created");
}//创建表格(用来存储标签内容
}//创建表格(用来存储标签内容
private void reCreateDataTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
@ -293,16 +306,16 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
}
return mInstance;
}//上网查是为解决同一时刻只能有一个线程执行.
return mInstance;//上网查是为解决同一时刻只能有一个线程执行.
//在写程序库代码时,有时有一个类需要被所有的其它类使用,
//但又要求这个类只能被实例化一次,是个服务类,定义一次,其它类使用同一个这个类的实例
}
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
createDataTable(db);
}//实现两个表格(上面创建的两个表格)
} ////实现两个表格(上面创建的两个表格)
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
@ -360,7 +373,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
}//更新到V3版本
private void upgradeToV4(SQLiteDatabase db) {
//在NOTE表中添加VERSION列初始值为0不可为空
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}//更新到V4版本
}
}

@ -16,32 +16,47 @@
package net.micode.notes.data;
//导入Android系统提供的搜索管理器用于处理搜索相关功能
import android.app.SearchManager;
//导入Android内容提供者类用于管理数据的访问
import android.content.ContentProvider;
//导入Android内容URI工具类用于创建和处理URI
import android.content.ContentUris;
//导入Android内容值类用于存储要插入或更新的数据
import android.content.ContentValues;
//导入Android意图类用于启动活动、广播等
import android.content.Intent;
//导入Android URI匹配器用于匹配URI以确定操作类型
import android.content.UriMatcher;
//导入Android数据库游标用于查询数据库后获取数据
import android.database.Cursor;
//导入Android SQLite数据库类用于操作SQLite数据库
import android.database.sqlite.SQLiteDatabase;
//导入Android网络URI类用于表示网络路径
import android.net.Uri;
//导入Android文本工具类用于处理文本相关操作
import android.text.TextUtils;
//导入Android日志工具类用于输出日志信息
import android.util.Log;
//导入笔记应用所需的资源类包含应用所需的各种资源ID
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;
public class NotesProvider extends ContentProvider {
//UriMatcher用于匹配传入的URI以便确定如何处理请求
private static final UriMatcher mMatcher;
// NotesDatabaseHelper是与数据库交互的助手类用于执行数据库操作
private NotesDatabaseHelper mHelper;
//TAG常量用于日志记录方便在日志输出时标识来源
private static final String TAG = "NotesProvider";
//以下常量定义了不同的URI匹配码每个代码对应一种特定的URI请求类型
private static final int URI_NOTE = 1;
private static final int URI_NOTE_ITEM = 2;
private static final int URI_DATA = 3;
@ -51,12 +66,17 @@ public class NotesProvider extends ContentProvider {
private static final int URI_SEARCH_SUGGEST = 6;
static {
//初始化UriMatcher对象用于后续匹配不同的URI
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//为Notes实体的各个操作添加URI匹配规则
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
//为Data实体的各个操作添加URI匹配规则
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
//添加用于搜索操作的URI匹配规则
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
//添加用于搜索建议操作的URI匹配规则支持模糊查询
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
@ -66,13 +86,19 @@ public class NotesProvider extends ContentProvider {
* we will trim '\n' and white space in order to show more information.
*/
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
//将ID列别名设置为SUGGEST_COLUMN_INTENT_EXTRA_DATA用于搜索建议的额外数据
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
//使用TRIM和REPLACE函数处理SNIPPET列移除换行符用于搜索建议的文本显示
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
//再次处理SNIPPET列用于搜索建议的第二个文本显示
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
//将一个drawable资源作为图标用于搜索建议
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
//设置搜索建议的意图行为为ACTION_VIEW
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
//设置搜索建议的意图数据类型为Notes.TextNote.CONTENT_TYPE
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
//定义一个用于搜索笔记片段的查询字符串
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
@ -81,23 +107,32 @@ public class NotesProvider extends ContentProvider {
@Override
public boolean onCreate() {
//初始化NotesDatabaseHelper实例用于后续的数据库操作
mHelper = NotesDatabaseHelper.getInstance(getContext());
//表示该方法已成功创建返回true
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// 初始化游标为null
Cursor c = null;
//获取可读的数据库实例
SQLiteDatabase db = mHelper.getReadableDatabase();
//初始化ID为null
String id = null;
// 根据URI匹配类型执行不同的查询操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
//查询整个NOTE表
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
//获取URI路径中的ID查询特定的NOTE项
id = uri.getPathSegments().get(1);
//查询整个DATA表
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
@ -106,74 +141,102 @@ public class NotesProvider extends ContentProvider {
sortOrder);
break;
case URI_DATA_ITEM:
//获取URI路径中的ID查询特定的DATA项
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:
//对于搜索和搜索建议的查询限制使用sortOrder和projection参数
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
//初始化searchString为null用于存储后续可能提取的搜索字符串
String searchString = null;
//检查uri是否匹配预定义的URI模式以确定是否为搜索建议请求
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
//如果uri的路径段数大于1说明包含足够的路径信息
if (uri.getPathSegments().size() > 1) {
//从路径段中提取搜索字符串
searchString = uri.getPathSegments().get(1);
}
} else {
//如果不是搜索建议请求,尝试从查询参数中获取搜索字符串
searchString = uri.getQueryParameter("pattern");
}
if (TextUtils.isEmpty(searchString)) {
return null;
}
} //# 检查搜索字符串是否为空
//# 如果搜索字符串为空则返回null表示没有找到匹配的项目
//尝试执行SQL查询以搜索笔记片段
try {
//格式化搜索字符串以便在SQL查询中使用
searchString = String.format("%%%s%%", searchString);
//执行SQL查询
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
//捕获异常并记录错误信息
Log.e(TAG, "got exception: " + ex.toString());
}
break;
//如果URI不匹配任何已知的情况则抛出异常
default:
//抛出IllegalArgumentException指出URI未知
throw new IllegalArgumentException("Unknown URI " + uri);
}
//如果c不为空则设置通知URI以便在内容发生变化时通知
if (c != null) {
//使用getContext().getContentResolver()获取内容解析器与uri关联通知
c.setNotificationUri(getContext().getContentResolver(), uri);
}
//返回对象c以便进一步处理或使用
return c;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
//获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
//初始化数据ID和笔记ID
long dataId = 0, noteId = 0, insertedId = 0;
//根据URI匹配类型执行不同的插入操作
switch (mMatcher.match(uri)) {
case URI_NOTE:
//插入笔记同时更新插入ID和笔记ID
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA:
//检查数据是否包含笔记ID如果没有则记录日志
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);
//插入数据同时更新插入ID和数据ID
break;
//默认情况下如果URI不匹配任何已知的情况则抛出异常
default:
//抛出IllegalArgumentException异常指出URI未知
throw new IllegalArgumentException("Unknown URI " + uri);
}
// Notify the note uri
// 如果noteId大于0则通知内容提供者有笔记内容发生变化
if (noteId > 0) {
//通过ContentUris.withAppendedId方法构建特定笔记的URI
// 使用notifyChange方法通知内容提供者指定URI的数据发生了改变
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// Notify the data uri
//如果数据ID大于0则通知内容提供者数据已更改
if (dataId > 0) {
//通知注册监听指定数据URI的组件数据已更改以便它们可以重新查询数据
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
@ -183,119 +246,196 @@ public class NotesProvider extends ContentProvider {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//初始化删除的行数为0
int count = 0;
//初始化ID为null用于可能的单个数据项的删除
String id = null;
//获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
//初始化deleteData标志为false用于判断是否需要删除数据
boolean deleteData = false;
switch (mMatcher.match(uri)) {
//根据URI匹配结果执行相应的操作
case URI_NOTE:
//当匹配到URI_NOTE时更新selection以限定删除条件
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
//执行删除操作仅删除ID大于0的笔记记录
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
//获取URI路径中的第二段作为笔记ID
id = uri.getPathSegments().get(1);
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
//将路径段中的ID转换为长整型
long noteId = Long.valueOf(id);
//如果noteId的值小于等于0则退出循环
if (noteId <= 0) {
break;
}
//执行数据库中的数据删除操作
//# 这里使用了delete方法从指定表中删除符合条件的记录
//# 参数 TABLE.NOTE 指定了要操作的数据库表
//# NoteColumns.ID + "=" + id + parseSelection(selection) 构造了删除条件即指定要删除的记录的ID
//# selection 和 selectionArgs 进一步细化了删除条件,确保只删除符合特定要求的记录
//# 该方法没有返回值,但会根据删除的记录数更新数据库状态
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
//结束删除操作
break;
//处理URI_DATA情况
case URI_DATA:
//执行数据表中的数据删除操作
count = db.delete(TABLE.DATA, selection, selectionArgs);
//标记已删除数据
deleteData = true;
break;
//处理URI_DATA_ITEM情况
case URI_DATA_ITEM:
//获取路径段中的ID
id = uri.getPathSegments().get(1);
// 执行数据删除操作
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
////标记数据已删除
deleteData = true;
break;
//如果URI不匹配任何已知的情况则抛出异常
default:
//抛出IllegalArgumentException指出URI未知
throw new IllegalArgumentException("Unknown URI " + uri);
}
//如果计数大于0表明有需要处理的数据
if (count > 0) {
//如果deleteData标志为真则需要删除数据
if (deleteData) {
//通知内容提供者数据已更改,以便其他观察者可以响应这个更改
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
//通知内容解析器ContentResolver数据已更改
getContext().getContentResolver().notifyChange(uri, null);
}
//返回计数结果
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 初始化更新计数为0
int count = 0;
// 初始化ID为null用于后续可能的单条目更新
String id = null;
// 获取可写的数据库实例
SQLiteDatabase db = mHelper.getWritableDatabase();
// 标记是否需要更新数据
boolean updateData = false;
// 根据URI匹配操作类型
switch (mMatcher.match(uri)) {
case URI_NOTE:
//处理URI为URI_NOTE的情况主要进行以下操作
// 1. 调用increaseNoteVersion函数将笔记版本号增加-1可能是为了表示版本回退或删除
// 此处不涉及函数调用的详细逻辑,但表明了对笔记版本号的操作意图。
// 2. 使用从内容提供者传入的selection和selectionArgs参数来更新数据库中的笔记内容。
// 这里的db.update方法调用说明了更新操作是在NOTE表上进行更新的条件和新值由selection和values参数指定。
// 最后将更新的行数赋值给count变量以便后续处理或响应。
// """
increaseNoteVersion(-1, selection, selectionArgs);
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
//处理URI为NOTE_ITEM的情况
case URI_NOTE_ITEM:
//获取URI路径中的ID段
id = uri.getPathSegments().get(1);
//调用方法增加笔记版本参数包括笔记ID、选择条件和选择条件参数
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
//更新数据库中的笔记记录,设置更新的值、条件和条件参数
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
//处理URI_DATA情况
case URI_DATA:
//更新数据库中的数据
count = db.update(TABLE.DATA, values, selection, selectionArgs);
//设置更新标志为true
updateData = true;
//结束switch-case语句
break;
//处理URI_DATA_ITEM情况
case URI_DATA_ITEM:
//获取路径段中的ID
id = uri.getPathSegments().get(1);
//根据ID更新数据库中的数据项
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
//设置更新标志为true
updateData = true;
//结束处理
break;
//默认情况下如果URI不匹配任何已知的情况则抛出异常
default:
//抛出IllegalArgumentException异常指出URI未知
throw new IllegalArgumentException("Unknown URI " + uri);
}
//如果存在要更新的数据
if (count > 0) {
//如果updateData标志为真则通知数据集发生变化以便刷新显示笔记列表的界面
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
//通知数据集发生变化,以便刷新显示特定笔记的界面
getContext().getContentResolver().notifyChange(uri, null);
}
//返回更新的笔记数量
return count;
}
//根据提供的选择条件字符串生成附加的SQL查询条件。
// * 如果选择条件字符串不为空,则将其包裹在" AND ("和")"之间以适应SQL查询的语法结构
// * 否则,返回空字符串,表示不添加任何查询条件。
// * @param selection SQL查询的选择条件字符串可能为空。
// * @return 返回格式化后的查询条件字符串用于拼接SQL查询语句。
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
//@param id 笔记的唯一标识符,用于定位要更新的笔记记录
// selection SQL查询的WHERE子句用于指定更新条件
// selectionArgs 替换WHERE子句中问号占位符的实际值数组
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
//初始化一个StringBuilder对象用于构建SQL语句
StringBuilder sql = new StringBuilder(120);
//构建UPDATE SQL语句的起始部分
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
//如果id大于0或者selection不为空则在SQL语句中添加WHERE子句
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
//如果id大于0则构建一个SQL查询条件指定笔记的ID等于给定的id值
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
//如果selection不为空则进行查询条件的处理
if (!TextUtils.isEmpty(selection)) {
//根据id是否大于0来决定是否解析selection以适应不同的查询条件构建需求
String selectString = id > 0 ? parseSelection(selection) : selection;
//遍历selectionArgs中的每个参数依次替换查询条件中的问号占位符
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
// 将处理后的查询条件字符串append到sql StringBuilder中以构建最终的SQL查询语句
sql.append(selectString);
}
//执行SQL语句
//# 该方法用于执行非查询操作的SQL语句如CREATE TABLE、INSERT、UPDATE、DELETE等
//# 通过getWritableDatabase()方法获取SQLiteDatabase对象该对象用于对数据库进行读写操作
//# 使用execSQL()方法执行SQL语句该方法不返回查询结果适用于不需要返回结果的操作
mHelper.getWritableDatabase().execSQL(sql.toString());
}
//重写getType方法
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub

@ -64,21 +64,22 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS;
notification.flags = Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0);
NotesPreferenceActivity.class), PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0);
NotesListActivity.class), PendingIntent.FLAG_IMMUTABLE);
}
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
Notification.Builder builder = new Notification.Builder(mContext)
.setAutoCancel(true)
.setContentTitle(mContext.getString(R.string.app_name))
.setContentText(content)
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setOngoing(true);
Notification notification=builder.getNotification();
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}

@ -15,7 +15,7 @@
*/
package net.micode.notes.ui;
//导入Android平台提供的各种功能模块用于构建和管理应用程序的活动、对话框、意图等功能
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@ -32,7 +32,7 @@ import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
//导入自定义的资源文件和数据处理工具类
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
@ -47,6 +47,11 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
MediaPlayer mPlayer;
@Override
//在活动创建时被调用,用于初始化活动
//调用父类的onCreate方法进行基本初始化
//移除窗口标题
//配置窗口在锁定状态下显示
//根据屏幕状态,添加额外的窗口标志以确保应用在特定场景下的可见性和屏幕行为
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
@ -64,94 +69,144 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
Intent intent = getIntent();
try {
//从intent中提取笔记ID
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
//根据ID获取笔记摘要
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)
: mSnippet;
} catch (IllegalArgumentException e) {
//捕获非法参数异常并打印堆栈跟踪
e.printStackTrace();
//异常情况下返回
return;
}
//初始化MediaPlayer对象用于播放音频
mPlayer = new MediaPlayer();
// 检查数据库中是否存在可见的笔记
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
//如果存在,显示操作对话框并播放提醒声音
showActionDialog();
playAlarmSound();
} else {
//如果不存在,结束当前活动或进程
finish();
}
}
//检查屏幕是否处于开启状态
//通过访问系统服务中的PowerManager来获取屏幕的状态信息
// 此方法用于判断当前设备的屏幕是否点亮,以便在需要时采取相应操作
private boolean isScreenOn() {
//获取PowerManager实例以管理电源相关操作
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
//使用PowerManager的isScreenOn方法检查屏幕状态并返回
return pm.isScreenOn();
}
//本方法首先获取系统默认的报警铃声URI然后根据系统设置的静音模式
//选择合适的音频流类型播放报警声音
private void playAlarmSound() {
//获取系统默认的报警铃声URI
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
//获取系统静音模式下受影响的音频流类型
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
//判断静音模式是否影响报警音频流
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
//如果影响,则使用系统设置的静音模式对应的音频流类型
mPlayer.setAudioStreamType(silentModeStreams);
} else {
//如果不影响,则直接使用报警音频流类型
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
//设置媒体播放器的数据源
mPlayer.setDataSource(this, url);
//准备媒体播放器
mPlayer.prepare();
//设置循环播放
mPlayer.setLooping(true);
//开始播放
mPlayer.start();
} catch (IllegalArgumentException e) {
//当参数无效时,打印异常信息
// TODO Auto-generated catch block
//当没有权限访问指定的数据源时,打印异常信息
e.printStackTrace();
} catch (SecurityException e) {
//当媒体播放器处于不适合的操作状态时,打印异常信息
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
//当数据源不可访问或发生I/O错误时打印异常信息
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//用于创建并显示一个操作对话框该对话框包含一个标题、一条消息内容、一个确定按钮,
// 并根据屏幕状态决定是否显示一个取消按钮对话框的样式和内容是根据当前应用的主题和提供的
// 字符串资源来定制的
private void showActionDialog() {
//创建AlertDialog实例用于构建对话框界面
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
//设置对话框标题为应用名称
dialog.setTitle(R.string.app_name);
//设置对话框的消息内容
dialog.setMessage(mSnippet);
//设置确定按钮点击时将执行当前类中定义的onClick方法
dialog.setPositiveButton(R.string.notealert_ok, this);
//根据屏幕状态决定是否设置取消按钮,以适应不同的使用场景
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
// 显示对话框并设置关闭监听器当对话框关闭时将执行当前类中定义的onDismiss方法
dialog.show().setOnDismissListener(this);
}
//处理对话框的点击事件
// dialog 接口对话框对象,用于识别和操作对话框
// which 点击的按钮标识,用于判断用户点击的是哪个按钮
public void onClick(DialogInterface dialog, int which) {
//根据点击的按钮标识进行相应的操作
switch (which) {
//当用户点击的是负向按钮时
case DialogInterface.BUTTON_NEGATIVE:
// 创建一个意图用于启动NoteEditActivity
Intent intent = new Intent(this, NoteEditActivity.class);
//设置意图的动作,表示查看操作
intent.setAction(Intent.ACTION_VIEW);
//设置意图的动作,表示查看操作
intent.putExtra(Intent.EXTRA_UID, mNoteId);
//使用意图启动活动
startActivity(intent);
//对于其他按钮点击情况,不执行任何操作
break;
default:
break;
}
}
// 当对话框被关闭时调用的方法
// 该方法负责停止报警声音并结束当前活动
// dialog 接口对话框对象,表示哪个对话框被关闭
public void onDismiss(DialogInterface dialog) {
//停止报警声音
stopAlarmSound();
//结束当前活动
finish();
}
//停止报警声音的方法
// 该方法检查媒体播放器是否不为空,然后停止播放并释放资源
private void stopAlarmSound() {
//检查媒体播放器是否不为空
if (mPlayer != null) {
//停止媒体播放器的播放
mPlayer.stop();
//释放媒体播放器的资源
mPlayer.release();
//将媒体播放器设置为null表示不再使用
mPlayer = null;
}
}

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save