From f05b0f1270103d65858b804ed5d77a5b78368e4a Mon Sep 17 00:00:00 2001 From: lijiangtao <198586914@qq.com> Date: Wed, 14 May 2025 23:18:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BA=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...米便签开源代码的泛读报告.docx | Bin 0 -> 17284 bytes src/data/Contact.java | 73 ++++ src/data/Notes.java | 279 ++++++++++++++ src/data/NotesDatabaseHelper.java | 362 ++++++++++++++++++ src/data/NotesProvider.java | 305 +++++++++++++++ src/widget/NoteWidgetProvider.java | 132 +++++++ src/widget/NoteWidgetProvider_2x.java | 47 +++ src/widget/NoteWidgetProvider_4x.java | 46 +++ 8 files changed, 1244 insertions(+) create mode 100644 doc/小米便签开源代码的泛读报告.docx create mode 100644 src/data/Contact.java create mode 100644 src/data/Notes.java create mode 100644 src/data/NotesDatabaseHelper.java create mode 100644 src/data/NotesProvider.java create mode 100644 src/widget/NoteWidgetProvider.java create mode 100644 src/widget/NoteWidgetProvider_2x.java create mode 100644 src/widget/NoteWidgetProvider_4x.java diff --git a/doc/小米便签开源代码的泛读报告.docx b/doc/小米便签开源代码的泛读报告.docx new file mode 100644 index 0000000000000000000000000000000000000000..743021a2862767f943e6d3dc90249c740462e541 GIT binary patch literal 17284 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 F{{hocj4c2F literal 0 HcmV?d00001 diff --git a/src/data/Contact.java b/src/data/Contact.java new file mode 100644 index 0000000..d97ac5d --- /dev/null +++ b/src/data/Contact.java @@ -0,0 +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.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 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(); + } + + 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; + } + } +} diff --git a/src/data/Notes.java b/src/data/Notes.java new file mode 100644 index 0000000..f240604 --- /dev/null +++ b/src/data/Notes.java @@ -0,0 +1,279 @@ +/* + * 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.net.Uri; +public class Notes { + public static final String AUTHORITY = "micode_notes"; + public static final String TAG = "Notes"; + public static final int TYPE_NOTE = 0; + public static final int TYPE_FOLDER = 1; + public static final int TYPE_SYSTEM = 2; + + /** + * Following IDs are system folders' identifiers + * {@link Notes#ID_ROOT_FOLDER } is default folder + * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder + * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + */ + public static final int ID_ROOT_FOLDER = 0; + public static final int ID_TEMPARAY_FOLDER = -1; + public static final int ID_CALL_RECORD_FOLDER = -2; + public static final int ID_TRASH_FOLER = -3; + + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; + public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; + public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; + public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; + public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; + + public static final int TYPE_WIDGET_INVALIDE = -1; + public static final int TYPE_WIDGET_2X = 0; + public static final int TYPE_WIDGET_4X = 1; + + public static class DataConstants { + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; + } + + /** + * 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"); + + public interface NoteColumns { + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The parent's id for note or folder + *

Type: INTEGER (long)

+ */ + public static final String PARENT_ID = "parent_id"; + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date"; + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date"; + + + /** + * Alert date + *

Type: INTEGER (long)

+ */ + public static final String ALERTED_DATE = "alert_date"; + + /** + * Folder's name or text content of note + *

Type: TEXT

+ */ + public static final String SNIPPET = "snippet"; + + /** + * Note's widget id + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_ID = "widget_id"; + + /** + * Note's widget type + *

Type: INTEGER (long)

+ */ + public static final String WIDGET_TYPE = "widget_type"; + + /** + * Note's background color's id + *

Type: INTEGER (long)

+ */ + public static final String BG_COLOR_ID = "bg_color_id"; + + /** + * For text note, it doesn't has attachment, for multi-media + * note, it has at least one attachment + *

Type: INTEGER

+ */ + public static final String HAS_ATTACHMENT = "has_attachment"; + + /** + * Folder's count of notes + *

Type: INTEGER (long)

+ */ + public static final String NOTES_COUNT = "notes_count"; + + /** + * The file type: folder or note + *

Type: INTEGER

+ */ + public static final String TYPE = "type"; + + /** + * The last sync id + *

Type: INTEGER (long)

+ */ + public static final String SYNC_ID = "sync_id"; + + /** + * Sign to indicate local modified or not + *

Type: INTEGER

+ */ + public static final String LOCAL_MODIFIED = "local_modified"; + + /** + * Original parent id before moving into temporary folder + *

Type : INTEGER

+ */ + public static final String ORIGIN_PARENT_ID = "origin_parent_id"; + + /** + * The gtask id + *

Type : TEXT

+ */ + public static final String GTASK_ID = "gtask_id"; + + /** + * The version code + *

Type : INTEGER (long)

+ */ + public static final String VERSION = "version"; + } + + public interface DataColumns { + /** + * The unique ID for a row + *

Type: INTEGER (long)

+ */ + public static final String ID = "_id"; + + /** + * The MIME type of the item represented by this row. + *

Type: Text

+ */ + public static final String MIME_TYPE = "mime_type"; + + /** + * The reference id to note that this data belongs to + *

Type: INTEGER (long)

+ */ + public static final String NOTE_ID = "note_id"; + + /** + * Created data for note or folder + *

Type: INTEGER (long)

+ */ + public static final String CREATED_DATE = "created_date"; + + /** + * Latest modified date + *

Type: INTEGER (long)

+ */ + public static final String MODIFIED_DATE = "modified_date"; + + /** + * Data's content + *

Type: TEXT

+ */ + public static final String CONTENT = "content"; + + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA1 = "data1"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * integer data type + *

Type: INTEGER

+ */ + public static final String DATA2 = "data2"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA3 = "data3"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA4 = "data4"; + + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * TEXT data type + *

Type: TEXT

+ */ + public static final String DATA5 = "data5"; + } + + public static final class TextNote implements DataColumns { + /** + * Mode to indicate the text in check list mode or not + *

Type: Integer 1:check list mode 0: normal mode

+ */ + public static final String MODE = DATA1; + + 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 + *

Type: INTEGER (long)

+ */ + public static final String CALL_DATE = DATA1; + + /** + * Phone number for this record + *

Type: TEXT

+ */ + 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"); + } +} diff --git a/src/data/NotesDatabaseHelper.java b/src/data/NotesDatabaseHelper.java new file mode 100644 index 0000000..ffe5d57 --- /dev/null +++ b/src/data/NotesDatabaseHelper.java @@ -0,0 +1,362 @@ +/* + * 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"); + } +} diff --git a/src/data/NotesProvider.java b/src/data/NotesProvider.java new file mode 100644 index 0000000..edb0a60 --- /dev/null +++ b/src/data/NotesProvider.java @@ -0,0 +1,305 @@ +/* + * 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.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 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 { + private static final UriMatcher mMatcher; + + private NotesDatabaseHelper mHelper; + + private static final String TAG = "NotesProvider"; + + private static final int URI_NOTE = 1; + private static final int URI_NOTE_ITEM = 2; + private static final int URI_DATA = 3; + private static final int URI_DATA_ITEM = 4; + + private static final int URI_SEARCH = 5; + private static final int URI_SEARCH_SUGGEST = 6; + + static { + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + 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); + } + + /** + * x'0A' represents the '\n' character in sqlite. For title and content in the search result, + * we will trim '\n' and white space in order to show more information. + */ + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "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; + + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + @Override + public boolean onCreate() { + mHelper = NotesDatabaseHelper.getInstance(getContext()); + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + Cursor c = null; + SQLiteDatabase db = mHelper.getReadableDatabase(); + String id = null; + 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) { + 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: + throw new IllegalArgumentException("Unknown URI " + uri); + } + if (c != null) { + c.setNotificationUri(getContext().getContentResolver(), uri); + } + return c; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + SQLiteDatabase db = mHelper.getWritableDatabase(); + long dataId = 0, noteId = 0, insertedId = 0; + switch (mMatcher.match(uri)) { + case URI_NOTE: + 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); + } + + // Notify the data uri + if (dataId > 0) { + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); + } + + return ContentUris.withAppendedId(uri, insertedId); + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + 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 + 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 (updateData) { + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + } + getContext().getContentResolver().notifyChange(uri, null); + } + return count; + } + + private String parseSelection(String selection) { + return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); + } + + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { + StringBuilder sql = new StringBuilder(120); + sql.append("UPDATE "); + sql.append(TABLE.NOTE); + sql.append(" SET "); + sql.append(NoteColumns.VERSION); + sql.append("=" + NoteColumns.VERSION + "+1 "); + + if (id > 0 || !TextUtils.isEmpty(selection)) { + sql.append(" WHERE "); + } + if (id > 0) { + sql.append(NoteColumns.ID + "=" + String.valueOf(id)); + } + if (!TextUtils.isEmpty(selection)) { + String selectString = id > 0 ? parseSelection(selection) : selection; + for (String args : selectionArgs) { + selectString = selectString.replaceFirst("\\?", args); + } + sql.append(selectString); + } + + mHelper.getWritableDatabase().execSQL(sql.toString()); + } + + @Override + public String getType(Uri uri) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/widget/NoteWidgetProvider.java b/src/widget/NoteWidgetProvider.java new file mode 100644 index 0000000..ec6f819 --- /dev/null +++ b/src/widget/NoteWidgetProvider.java @@ -0,0 +1,132 @@ +/* + * 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.widget; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.util.Log; +import android.widget.RemoteViews; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NoteEditActivity; +import net.micode.notes.ui.NotesListActivity; + +public abstract class NoteWidgetProvider extends AppWidgetProvider { + public static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.BG_COLOR_ID, + NoteColumns.SNIPPET + }; + + public static final int COLUMN_ID = 0; + public static final int COLUMN_BG_COLOR_ID = 1; + public static final int COLUMN_SNIPPET = 2; + + private static final String TAG = "NoteWidgetProvider"; + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + for (int i = 0; i < appWidgetIds.length; i++) { + context.getContentResolver().update(Notes.CONTENT_NOTE_URI, + values, + NoteColumns.WIDGET_ID + "=?", + new String[] { String.valueOf(appWidgetIds[i])}); + } + } + + private Cursor getNoteWidgetInfo(Context context, int widgetId) { + return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, + null); + } + + protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + update(context, appWidgetManager, appWidgetIds, false); + } + + private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, + boolean privacyMode) { + for (int i = 0; i < appWidgetIds.length; i++) { + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { + int bgId = ResourceParser.getDefaultBgId(context); + String snippet = ""; + Intent intent = new Intent(context, NoteEditActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); + + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); + if (c != null && c.moveToFirst()) { + if (c.getCount() > 1) { + Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); + c.close(); + return; + } + snippet = c.getString(COLUMN_SNIPPET); + bgId = c.getInt(COLUMN_BG_COLOR_ID); + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); + intent.setAction(Intent.ACTION_VIEW); + } else { + snippet = context.getResources().getString(R.string.widget_havenot_content); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + } + + if (c != null) { + c.close(); + } + + RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); + rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); + /** + * Generate the pending intent to start host for the widget + */ + PendingIntent pendingIntent = null; + if (privacyMode) { + rv.setTextViewText(R.id.widget_text, + context.getString(R.string.widget_under_visit_mode)); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( + context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); + } else { + rv.setTextViewText(R.id.widget_text, snippet); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, + PendingIntent.FLAG_UPDATE_CURRENT); + } + + rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + appWidgetManager.updateAppWidget(appWidgetIds[i], rv); + } + } + } + + protected abstract int getBgResourceId(int bgId); + + protected abstract int getLayoutId(); + + protected abstract int getWidgetType(); +} diff --git a/src/widget/NoteWidgetProvider_2x.java b/src/widget/NoteWidgetProvider_2x.java new file mode 100644 index 0000000..adcb2f7 --- /dev/null +++ b/src/widget/NoteWidgetProvider_2x.java @@ -0,0 +1,47 @@ +/* + * 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.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + + +public class NoteWidgetProvider_2x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + @Override + protected int getLayoutId() { + return R.layout.widget_2x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_2X; + } +} diff --git a/src/widget/NoteWidgetProvider_4x.java b/src/widget/NoteWidgetProvider_4x.java new file mode 100644 index 0000000..c12a02e --- /dev/null +++ b/src/widget/NoteWidgetProvider_4x.java @@ -0,0 +1,46 @@ +/* + * 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.widget; + +import android.appwidget.AppWidgetManager; +import android.content.Context; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.ResourceParser; + + +public class NoteWidgetProvider_4x extends NoteWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.update(context, appWidgetManager, appWidgetIds); + } + + protected int getLayoutId() { + return R.layout.widget_4x; + } + + @Override + protected int getBgResourceId(int bgId) { + return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); + } + + @Override + protected int getWidgetType() { + return Notes.TYPE_WIDGET_4X; + } +}