From 78120f46e74dc27c3ce109a4fd0f3d53df8cb55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Mon, 3 Apr 2023 18:46:10 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E6=B3=9B=E8=AF=BB=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...米便签开源代码的泛读报告.docx | Bin 0 -> 216977 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/小米便签开源代码的泛读报告.docx diff --git a/doc/小米便签开源代码的泛读报告.docx b/doc/小米便签开源代码的泛读报告.docx new file mode 100644 index 0000000000000000000000000000000000000000..e65da11c6d738cbe69bcd1ae1f0cbca05488ee98 GIT binary patch literal 216977 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%0A5)OgUeLwBKroieCf7uA=| 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≪EvU-7atby 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 Y@T}q3XcKx7<03{5DEdJK%sY(#1!+6>n*aa+ literal 0 HcmV?d00001 From 282f9c849ade4d98f9e276ef883c133c38a21a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Wed, 5 Apr 2023 18:51:15 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E6=BA=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/Contact.java | 73 ++ src/data/Notes.java | 279 +++++ src/data/NotesDatabaseHelper.java | 362 +++++++ src/data/NotesProvider.java | 305 ++++++ src/gtask/data/MetaData.java | 82 ++ src/gtask/data/Node.java | 101 ++ src/gtask/data/SqlData.java | 189 ++++ src/gtask/data/SqlNote.java | 505 +++++++++ src/gtask/data/Task.java | 351 +++++++ src/gtask/data/TaskList.java | 343 +++++++ .../exception/ActionFailureException.java | 33 + .../exception/NetworkFailureException.java | 33 + src/gtask/remote/GTaskASyncTask.java | 129 +++ src/gtask/remote/GTaskClient.java | 585 +++++++++++ src/gtask/remote/GTaskManager.java | 800 +++++++++++++++ src/gtask/remote/GTaskSyncService.java | 128 +++ src/model/Note.java | 253 +++++ src/model/WorkingNote.java | 368 +++++++ src/tool/BackupUtils.java | 344 +++++++ src/tool/DataUtils.java | 295 ++++++ src/tool/GTaskStringUtils.java | 113 +++ src/tool/ResourceParser.java | 181 ++++ src/ui/AlarmAlertActivity.java | 158 +++ src/ui/AlarmInitReceiver.java | 65 ++ src/ui/AlarmReceiver.java | 30 + src/ui/DateTimePicker.java | 485 +++++++++ src/ui/DateTimePickerDialog.java | 90 ++ src/ui/DropdownMenu.java | 61 ++ src/ui/FoldersListAdapter.java | 80 ++ src/ui/NoteEditActivity.java | 873 ++++++++++++++++ src/ui/NoteEditText.java | 217 ++++ src/ui/NoteItemData.java | 224 ++++ src/ui/NotesListActivity.java | 954 ++++++++++++++++++ src/ui/NotesListAdapter.java | 184 ++++ src/ui/NotesListItem.java | 122 +++ src/ui/NotesPreferenceActivity.java | 388 +++++++ src/widget/NoteWidgetProvider.java | 132 +++ src/widget/NoteWidgetProvider_2x.java | 47 + src/widget/NoteWidgetProvider_4x.java | 46 + 39 files changed, 10008 insertions(+) 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/gtask/data/MetaData.java create mode 100644 src/gtask/data/Node.java create mode 100644 src/gtask/data/SqlData.java create mode 100644 src/gtask/data/SqlNote.java create mode 100644 src/gtask/data/Task.java create mode 100644 src/gtask/data/TaskList.java create mode 100644 src/gtask/exception/ActionFailureException.java create mode 100644 src/gtask/exception/NetworkFailureException.java create mode 100644 src/gtask/remote/GTaskASyncTask.java create mode 100644 src/gtask/remote/GTaskClient.java create mode 100644 src/gtask/remote/GTaskManager.java create mode 100644 src/gtask/remote/GTaskSyncService.java create mode 100644 src/model/Note.java create mode 100644 src/model/WorkingNote.java create mode 100644 src/tool/BackupUtils.java create mode 100644 src/tool/DataUtils.java create mode 100644 src/tool/GTaskStringUtils.java create mode 100644 src/tool/ResourceParser.java create mode 100644 src/ui/AlarmAlertActivity.java create mode 100644 src/ui/AlarmInitReceiver.java create mode 100644 src/ui/AlarmReceiver.java create mode 100644 src/ui/DateTimePicker.java create mode 100644 src/ui/DateTimePickerDialog.java create mode 100644 src/ui/DropdownMenu.java create mode 100644 src/ui/FoldersListAdapter.java create mode 100644 src/ui/NoteEditActivity.java create mode 100644 src/ui/NoteEditText.java create mode 100644 src/ui/NoteItemData.java create mode 100644 src/ui/NotesListActivity.java create mode 100644 src/ui/NotesListAdapter.java create mode 100644 src/ui/NotesListItem.java create mode 100644 src/ui/NotesPreferenceActivity.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/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/gtask/data/MetaData.java b/src/gtask/data/MetaData.java new file mode 100644 index 0000000..3a2050b --- /dev/null +++ b/src/gtask/data/MetaData.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class MetaData extends Task { + private final static String TAG = MetaData.class.getSimpleName(); + + private String mRelatedGid = null; + + public void setMeta(String gid, JSONObject metaInfo) { + try { + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); + } catch (JSONException e) { + Log.e(TAG, "failed to put related gid"); + } + setNotes(metaInfo.toString()); + setName(GTaskStringUtils.META_NOTE_NAME); + } + + public String getRelatedGid() { + return mRelatedGid; + } + + @Override + public boolean isWorthSaving() { + return getNotes() != null; + } + + @Override + public void setContentByRemoteJSON(JSONObject js) { + super.setContentByRemoteJSON(js); + if (getNotes() != null) { + try { + JSONObject metaInfo = new JSONObject(getNotes().trim()); + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); + } catch (JSONException e) { + Log.w(TAG, "failed to get related gid"); + mRelatedGid = null; + } + } + } + + @Override + public void setContentByLocalJSON(JSONObject js) { + // this function should not be called + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + } + + @Override + public JSONObject getLocalJSONFromContent() { + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + } + + @Override + public int getSyncAction(Cursor c) { + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + } + +} diff --git a/src/gtask/data/Node.java b/src/gtask/data/Node.java new file mode 100644 index 0000000..63950e0 --- /dev/null +++ b/src/gtask/data/Node.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; + +import org.json.JSONObject; + +public abstract class Node { + public static final int SYNC_ACTION_NONE = 0; + + public static final int SYNC_ACTION_ADD_REMOTE = 1; + + public static final int SYNC_ACTION_ADD_LOCAL = 2; + + public static final int SYNC_ACTION_DEL_REMOTE = 3; + + public static final int SYNC_ACTION_DEL_LOCAL = 4; + + public static final int SYNC_ACTION_UPDATE_REMOTE = 5; + + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; + + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; + + public static final int SYNC_ACTION_ERROR = 8; + + private String mGid; + + private String mName; + + private long mLastModified; + + private boolean mDeleted; + + public Node() { + mGid = null; + mName = ""; + mLastModified = 0; + mDeleted = false; + } + + public abstract JSONObject getCreateAction(int actionId); + + public abstract JSONObject getUpdateAction(int actionId); + + public abstract void setContentByRemoteJSON(JSONObject js); + + public abstract void setContentByLocalJSON(JSONObject js); + + public abstract JSONObject getLocalJSONFromContent(); + + public abstract int getSyncAction(Cursor c); + + public void setGid(String gid) { + this.mGid = gid; + } + + public void setName(String name) { + this.mName = name; + } + + public void setLastModified(long lastModified) { + this.mLastModified = lastModified; + } + + public void setDeleted(boolean deleted) { + this.mDeleted = deleted; + } + + public String getGid() { + return this.mGid; + } + + public String getName() { + return this.mName; + } + + public long getLastModified() { + return this.mLastModified; + } + + public boolean getDeleted() { + return this.mDeleted; + } + +} diff --git a/src/gtask/data/SqlData.java b/src/gtask/data/SqlData.java new file mode 100644 index 0000000..d3ec3be --- /dev/null +++ b/src/gtask/data/SqlData.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper.TABLE; +import net.micode.notes.gtask.exception.ActionFailureException; + +import org.json.JSONException; +import org.json.JSONObject; + + +public class SqlData { + private static final String TAG = SqlData.class.getSimpleName(); + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 + }; + + public static final int DATA_ID_COLUMN = 0; + + public static final int DATA_MIME_TYPE_COLUMN = 1; + + public static final int DATA_CONTENT_COLUMN = 2; + + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mDataId; + + private String mDataMimeType; + + private String mDataContent; + + private long mDataContentData1; + + private String mDataContentData3; + + private ContentValues mDiffDataValues; + + public SqlData(Context context) { + mContentResolver = context.getContentResolver(); + mIsCreate = true; + mDataId = INVALID_ID; + mDataMimeType = DataConstants.NOTE; + mDataContent = ""; + mDataContentData1 = 0; + mDataContentData3 = ""; + mDiffDataValues = new ContentValues(); + } + + public SqlData(Context context, Cursor c) { + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDiffDataValues = new ContentValues(); + } + + private void loadFromCursor(Cursor c) { + mDataId = c.getLong(DATA_ID_COLUMN); + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + mDataContent = c.getString(DATA_CONTENT_COLUMN); + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + } + + public void setContent(JSONObject js) throws JSONException { + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + if (mIsCreate || mDataId != dataId) { + mDiffDataValues.put(DataColumns.ID, dataId); + } + mDataId = dataId; + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE; + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); + } + mDataMimeType = dataMimeType; + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + if (mIsCreate || !mDataContent.equals(dataContent)) { + mDiffDataValues.put(DataColumns.CONTENT, dataContent); + } + mDataContent = dataContent; + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + if (mIsCreate || mDataContentData1 != dataContentData1) { + mDiffDataValues.put(DataColumns.DATA1, dataContentData1); + } + mDataContentData1 = dataContentData1; + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { + mDiffDataValues.put(DataColumns.DATA3, dataContentData3); + } + mDataContentData3 = dataContentData3; + } + + public JSONObject getContent() throws JSONException { + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + JSONObject js = new JSONObject(); + js.put(DataColumns.ID, mDataId); + js.put(DataColumns.MIME_TYPE, mDataMimeType); + js.put(DataColumns.CONTENT, mDataContent); + js.put(DataColumns.DATA1, mDataContentData1); + js.put(DataColumns.DATA3, mDataContentData3); + return js; + } + + public void commit(long noteId, boolean validateVersion, long version) { + + if (mIsCreate) { + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { + mDiffDataValues.remove(DataColumns.ID); + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + } else { + if (mDiffDataValues.size() > 0) { + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + } else { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { + String.valueOf(noteId), String.valueOf(version) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + } + + mDiffDataValues.clear(); + mIsCreate = false; + } + + public long getId() { + return mDataId; + } +} diff --git a/src/gtask/data/SqlNote.java b/src/gtask/data/SqlNote.java new file mode 100644 index 0000000..79a4095 --- /dev/null +++ b/src/gtask/data/SqlNote.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.tool.ResourceParser; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class SqlNote { + private static final String TAG = SqlNote.class.getSimpleName(); + + private static final int INVALID_ID = -99999; + + public static final String[] PROJECTION_NOTE = new String[] { + NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE, + NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, + NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, + NoteColumns.VERSION + }; + + public static final int ID_COLUMN = 0; + + public static final int ALERTED_DATE_COLUMN = 1; + + public static final int BG_COLOR_ID_COLUMN = 2; + + public static final int CREATED_DATE_COLUMN = 3; + + public static final int HAS_ATTACHMENT_COLUMN = 4; + + public static final int MODIFIED_DATE_COLUMN = 5; + + public static final int NOTES_COUNT_COLUMN = 6; + + public static final int PARENT_ID_COLUMN = 7; + + public static final int SNIPPET_COLUMN = 8; + + public static final int TYPE_COLUMN = 9; + + public static final int WIDGET_ID_COLUMN = 10; + + public static final int WIDGET_TYPE_COLUMN = 11; + + public static final int SYNC_ID_COLUMN = 12; + + public static final int LOCAL_MODIFIED_COLUMN = 13; + + public static final int ORIGIN_PARENT_ID_COLUMN = 14; + + public static final int GTASK_ID_COLUMN = 15; + + public static final int VERSION_COLUMN = 16; + + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mIsCreate; + + private long mId; + + private long mAlertDate; + + private int mBgColorId; + + private long mCreatedDate; + + private int mHasAttachment; + + private long mModifiedDate; + + private long mParentId; + + private String mSnippet; + + private int mType; + + private int mWidgetId; + + private int mWidgetType; + + private long mOriginParent; + + private long mVersion; + + private ContentValues mDiffNoteValues; + + private ArrayList mDataList; + + public SqlNote(Context context) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = true; + mId = INVALID_ID; + mAlertDate = 0; + mBgColorId = ResourceParser.getDefaultBgId(context); + mCreatedDate = System.currentTimeMillis(); + mHasAttachment = 0; + mModifiedDate = System.currentTimeMillis(); + mParentId = 0; + mSnippet = ""; + mType = Notes.TYPE_NOTE; + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + mOriginParent = 0; + mVersion = 0; + mDiffNoteValues = new ContentValues(); + mDataList = new ArrayList(); + } + + public SqlNote(Context context, Cursor c) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + } + + public SqlNote(Context context, long id) { + mContext = context; + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(id); + mDataList = new ArrayList(); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + mDiffNoteValues = new ContentValues(); + + } + + private void loadFromCursor(long id) { + Cursor c = null; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(id) + }, null); + if (c != null) { + c.moveToNext(); + loadFromCursor(c); + } else { + Log.w(TAG, "loadFromCursor: cursor = null"); + } + } finally { + if (c != null) + c.close(); + } + } + + private void loadFromCursor(Cursor c) { + mId = c.getLong(ID_COLUMN); + mAlertDate = c.getLong(ALERTED_DATE_COLUMN); + mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = c.getLong(CREATED_DATE_COLUMN); + mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); + mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); + mParentId = c.getLong(PARENT_ID_COLUMN); + mSnippet = c.getString(SNIPPET_COLUMN); + mType = c.getInt(TYPE_COLUMN); + mWidgetId = c.getInt(WIDGET_ID_COLUMN); + mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); + mVersion = c.getLong(VERSION_COLUMN); + } + + private void loadDataContent() { + Cursor c = null; + mDataList.clear(); + try { + c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, + "(note_id=?)", new String[] { + String.valueOf(mId) + }, null); + if (c != null) { + if (c.getCount() == 0) { + Log.w(TAG, "it seems that the note has not data"); + return; + } + while (c.moveToNext()) { + SqlData data = new SqlData(mContext, c); + mDataList.add(data); + } + } else { + Log.w(TAG, "loadDataContent: cursor = null"); + } + } finally { + if (c != null) + c.close(); + } + } + + public boolean setContent(JSONObject js) { + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + Log.w(TAG, "cannot set system folder"); + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + // for folder we can only update the snnipet and type + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : ""; + if (mIsCreate || !mSnippet.equals(snippet)) { + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); + } + mSnippet = snippet; + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE; + if (mIsCreate || mType != type) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + } + mType = type; + } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; + if (mIsCreate || mId != id) { + mDiffNoteValues.put(NoteColumns.ID, id); + } + mId = id; + + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note + .getLong(NoteColumns.ALERTED_DATE) : 0; + if (mIsCreate || mAlertDate != alertDate) { + mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); + } + mAlertDate = alertDate; + + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note + .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext); + if (mIsCreate || mBgColorId != bgColorId) { + mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); + } + mBgColorId = bgColorId; + + long createDate = note.has(NoteColumns.CREATED_DATE) ? note + .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis(); + if (mIsCreate || mCreatedDate != createDate) { + mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); + } + mCreatedDate = createDate; + + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note + .getInt(NoteColumns.HAS_ATTACHMENT) : 0; + if (mIsCreate || mHasAttachment != hasAttachment) { + mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); + } + mHasAttachment = hasAttachment; + + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note + .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); + if (mIsCreate || mModifiedDate != modifiedDate) { + mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); + } + mModifiedDate = modifiedDate; + + long parentId = note.has(NoteColumns.PARENT_ID) ? note + .getLong(NoteColumns.PARENT_ID) : 0; + if (mIsCreate || mParentId != parentId) { + mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); + } + mParentId = parentId; + + String snippet = note.has(NoteColumns.SNIPPET) ? note + .getString(NoteColumns.SNIPPET) : ""; + if (mIsCreate || !mSnippet.equals(snippet)) { + mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); + } + mSnippet = snippet; + + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) + : Notes.TYPE_NOTE; + if (mIsCreate || mType != type) { + mDiffNoteValues.put(NoteColumns.TYPE, type); + } + mType = type; + + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) + : AppWidgetManager.INVALID_APPWIDGET_ID; + if (mIsCreate || mWidgetId != widgetId) { + mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); + } + mWidgetId = widgetId; + + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note + .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; + if (mIsCreate || mWidgetType != widgetType) { + mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); + } + mWidgetType = widgetType; + + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note + .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; + if (mIsCreate || mOriginParent != originParent) { + mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); + } + mOriginParent = originParent; + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + SqlData sqlData = null; + if (data.has(DataColumns.ID)) { + long dataId = data.getLong(DataColumns.ID); + for (SqlData temp : mDataList) { + if (dataId == temp.getId()) { + sqlData = temp; + } + } + } + + if (sqlData == null) { + sqlData = new SqlData(mContext); + mDataList.add(sqlData); + } + + sqlData.setContent(data); + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } + return true; + } + + public JSONObject getContent() { + try { + JSONObject js = new JSONObject(); + + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + + JSONObject note = new JSONObject(); + if (mType == Notes.TYPE_NOTE) { + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.ALERTED_DATE, mAlertDate); + note.put(NoteColumns.BG_COLOR_ID, mBgColorId); + note.put(NoteColumns.CREATED_DATE, mCreatedDate); + note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment); + note.put(NoteColumns.MODIFIED_DATE, mModifiedDate); + note.put(NoteColumns.PARENT_ID, mParentId); + note.put(NoteColumns.SNIPPET, mSnippet); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.WIDGET_ID, mWidgetId); + note.put(NoteColumns.WIDGET_TYPE, mWidgetType); + note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + + JSONArray dataArray = new JSONArray(); + for (SqlData sqlData : mDataList) { + JSONObject data = sqlData.getContent(); + if (data != null) { + dataArray.put(data); + } + } + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + note.put(NoteColumns.ID, mId); + note.put(NoteColumns.TYPE, mType); + note.put(NoteColumns.SNIPPET, mSnippet); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + } + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + return null; + } + + public void setParentId(long id) { + mParentId = id; + mDiffNoteValues.put(NoteColumns.PARENT_ID, id); + } + + public void setGtaskId(String gid) { + mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); + } + + public void setSyncId(long syncId) { + mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); + } + + public void resetLocalModified() { + mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); + } + + public long getId() { + return mId; + } + + public long getParentId() { + return mParentId; + } + + public String getSnippet() { + return mSnippet; + } + + public boolean isNoteType() { + return mType == Notes.TYPE_NOTE; + } + + public void commit(boolean validateVersion) { + if (mIsCreate) { + if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { + mDiffNoteValues.remove(NoteColumns.ID); + } + + Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); + try { + mId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + if (mId == 0) { + throw new IllegalStateException("Create thread id failed"); + } + + if (mType == Notes.TYPE_NOTE) { + for (SqlData sqlData : mDataList) { + sqlData.commit(mId, false, -1); + } + } + } else { + if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { + Log.e(TAG, "No such note"); + throw new IllegalStateException("Try to update note with invalid id"); + } + if (mDiffNoteValues.size() > 0) { + mVersion ++; + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?)", new String[] { + String.valueOf(mId) + }); + } else { + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", + new String[] { + String.valueOf(mId), String.valueOf(mVersion) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + + if (mType == Notes.TYPE_NOTE) { + for (SqlData sqlData : mDataList) { + sqlData.commit(mId, validateVersion, mVersion); + } + } + } + + // refresh local info + loadFromCursor(mId); + if (mType == Notes.TYPE_NOTE) + loadDataContent(); + + mDiffNoteValues.clear(); + mIsCreate = false; + } +} diff --git a/src/gtask/data/Task.java b/src/gtask/data/Task.java new file mode 100644 index 0000000..6a19454 --- /dev/null +++ b/src/gtask/data/Task.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + +public class Task extends Node { + private static final String TAG = Task.class.getSimpleName(); + + private boolean mCompleted; + + private String mNotes; + + private JSONObject mMetaInfo; + + private Task mPriorSibling; + + private TaskList mParent; + + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); + + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); + + // prior_sibling_id + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate task-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); + } + + // completed + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get task content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { + Log.e(TAG, "invalid type"); + return; + } + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + String name = getName(); + try { + if (mMetaInfo == null) { + // new task created from web + if (name == null) { + Log.w(TAG, "the note seems to be an empty one"); + return null; + } + + JSONObject js = new JSONObject(); + JSONObject note = new JSONObject(); + JSONArray dataArray = new JSONArray(); + JSONObject data = new JSONObject(); + data.put(DataColumns.CONTENT, name); + dataArray.put(data); + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + return js; + } else { + // synced task + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + return mMetaInfo; + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public void setMetaInfo(MetaData metaData) { + if (metaData != null && metaData.getNotes() != null) { + try { + mMetaInfo = new JSONObject(metaData.getNotes()); + } catch (JSONException e) { + Log.w(TAG, e.toString()); + mMetaInfo = null; + } + } + } + + public int getSyncAction(Cursor c) { + try { + JSONObject noteInfo = null; + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + } + + if (noteInfo == null) { + Log.w(TAG, "it seems that note meta has been deleted"); + return SYNC_ACTION_UPDATE_REMOTE; + } + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "remote note id seems to be deleted"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + // validate the note id now + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { + Log.w(TAG, "note id doesn't match"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public boolean isWorthSaving() { + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); + } + + public void setCompleted(boolean completed) { + this.mCompleted = completed; + } + + public void setNotes(String notes) { + this.mNotes = notes; + } + + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling; + } + + public void setParent(TaskList parent) { + this.mParent = parent; + } + + public boolean getCompleted() { + return this.mCompleted; + } + + public String getNotes() { + return this.mNotes; + } + + public Task getPriorSibling() { + return this.mPriorSibling; + } + + public TaskList getParent() { + return this.mParent; + } + +} diff --git a/src/gtask/data/TaskList.java b/src/gtask/data/TaskList.java new file mode 100644 index 0000000..4ea21c5 --- /dev/null +++ b/src/gtask/data/TaskList.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + + +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName(); + + private int mIndex; + + private ArrayList mChildren; + + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // index + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-create jsonobject"); + } + + return js; + } + + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // action_type + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + + // action_id + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // id + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + // entity_delta + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to generate tasklist-update jsonobject"); + } + + return js; + } + + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + // id + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + + // last_modified + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + + // name + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("fail to get tasklist content from jsonobject"); + } + } + } + + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); + } + + try { + JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + + if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + String name = folder.getString(NoteColumns.SNIPPET); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); + } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE); + else + Log.e(TAG, "invalid system folder"); + } else { + Log.e(TAG, "error type"); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + public JSONObject getLocalJSONFromContent() { + try { + JSONObject js = new JSONObject(); + JSONObject folder = new JSONObject(); + + String folderName = getName(); + if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), + folderName.length()); + folder.put(NoteColumns.SNIPPET, folderName); + if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + else + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + public int getSyncAction(Cursor c) { + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // there is no local update + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // no update both side + return SYNC_ACTION_NONE; + } else { + // apply remote to local + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + // validate gtask id + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask id doesn't match"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // local modification only + return SYNC_ACTION_UPDATE_REMOTE; + } else { + // for folder conflicts, just apply local modification + return SYNC_ACTION_UPDATE_REMOTE; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + public int getChildTaskCount() { + return mChildren.size(); + } + + public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // need to set prior sibling and parent + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1)); + task.setParent(this); + } + } + return ret; + } + + public boolean addChildTask(Task task, int index) { + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "add child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (task != null && pos == -1) { + mChildren.add(index, task); + + // update the task list + Task preTask = null; + Task afterTask = null; + if (index != 0) + preTask = mChildren.get(index - 1); + if (index != mChildren.size() - 1) + afterTask = mChildren.get(index + 1); + + task.setPriorSibling(preTask); + if (afterTask != null) + afterTask.setPriorSibling(task); + } + + return true; + } + + public boolean removeChildTask(Task task) { + boolean ret = false; + int index = mChildren.indexOf(task); + if (index != -1) { + ret = mChildren.remove(task); + + if (ret) { + // reset prior sibling and parent + task.setPriorSibling(null); + task.setParent(null); + + // update the task list + if (index != mChildren.size()) { + mChildren.get(index).setPriorSibling( + index == 0 ? null : mChildren.get(index - 1)); + } + } + } + return ret; + } + + public boolean moveChildTask(Task task, int index) { + + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "move child task: invalid index"); + return false; + } + + int pos = mChildren.indexOf(task); + if (pos == -1) { + Log.e(TAG, "move child task: the task should in the list"); + return false; + } + + if (pos == index) + return true; + return (removeChildTask(task) && addChildTask(task, index)); + } + + public Task findChildTaskByGid(String gid) { + for (int i = 0; i < mChildren.size(); i++) { + Task t = mChildren.get(i); + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); + } + + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: invalid index"); + return null; + } + return mChildren.get(index); + } + + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) + return task; + } + return null; + } + + public ArrayList getChildTaskList() { + return this.mChildren; + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return this.mIndex; + } +} diff --git a/src/gtask/exception/ActionFailureException.java b/src/gtask/exception/ActionFailureException.java new file mode 100644 index 0000000..15504be --- /dev/null +++ b/src/gtask/exception/ActionFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception; + +public class ActionFailureException extends RuntimeException { + private static final long serialVersionUID = 4425249765923293627L; + + public ActionFailureException() { + super(); + } + + public ActionFailureException(String paramString) { + super(paramString); + } + + public ActionFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); + } +} diff --git a/src/gtask/exception/NetworkFailureException.java b/src/gtask/exception/NetworkFailureException.java new file mode 100644 index 0000000..b08cfb1 --- /dev/null +++ b/src/gtask/exception/NetworkFailureException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.exception; + +public class NetworkFailureException extends Exception { + private static final long serialVersionUID = 2107610287180234136L; + + public NetworkFailureException() { + super(); + } + + public NetworkFailureException(String paramString) { + super(paramString); + } + + public NetworkFailureException(String paramString, Throwable paramThrowable) { + super(paramString, paramThrowable); + } +} diff --git a/src/gtask/remote/GTaskASyncTask.java b/src/gtask/remote/GTaskASyncTask.java new file mode 100644 index 0000000..0e332f3 --- /dev/null +++ b/src/gtask/remote/GTaskASyncTask.java @@ -0,0 +1,129 @@ + +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesListActivity; +import net.micode.notes.ui.NotesPreferenceActivity; + + +public class GTaskASyncTask extends AsyncTask { + + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + + public interface OnCompleteListener { + void onComplete(); + } + + private Context mContext; + + private NotificationManager mNotifiManager; + + private GTaskManager mTaskManager; + + private OnCompleteListener mOnCompleteListener; + + public GTaskASyncTask(Context context, OnCompleteListener listener) { + mContext = context; + mOnCompleteListener = listener; + mNotifiManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + mTaskManager = GTaskManager.getInstance(); + } + + public void cancelSync() { + mTaskManager.cancelSync(); + } + + public void publishProgess(String message) { + publishProgress(new String[] { + message + }); + } + + private void showNotification(int tickerId, String content) { + PendingIntent pendingIntent; + if (tickerId != R.string.ticker_success) { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesPreferenceActivity.class), 0); + + } else { + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, + NotesListActivity.class), 0); + } + + + 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); + } + + + + @Override + protected Integer doInBackground(Void... unused) { + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity + .getSyncAccountName(mContext))); + return mTaskManager.sync(mContext, this); + } + + @Override + protected void onProgressUpdate(String... progress) { + showNotification(R.string.ticker_syncing, progress[0]); + if (mContext instanceof GTaskSyncService) { + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); + } + } + + @Override + protected void onPostExecute(Integer result) { + if (result == GTaskManager.STATE_SUCCESS) { + showNotification(R.string.ticker_success, mContext.getString( + R.string.success_sync_account, mTaskManager.getSyncAccount())); + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); + } else if (result == GTaskManager.STATE_NETWORK_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); + } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { + showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); + } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { + showNotification(R.string.ticker_cancel, mContext + .getString(R.string.error_sync_cancelled)); + } + if (mOnCompleteListener != null) { + new Thread(new Runnable() { + + public void run() { + mOnCompleteListener.onComplete(); + } + }).start(); + } + } +} diff --git a/src/gtask/remote/GTaskClient.java b/src/gtask/remote/GTaskClient.java new file mode 100644 index 0000000..c67dfdf --- /dev/null +++ b/src/gtask/remote/GTaskClient.java @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.app.Activity; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.GTaskStringUtils; +import net.micode.notes.ui.NotesPreferenceActivity; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + + +public class GTaskClient { + private static final String TAG = GTaskClient.class.getSimpleName(); + + private static final String GTASK_URL = "https://mail.google.com/tasks/"; + + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; + + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; + + private static GTaskClient mInstance = null; + + private DefaultHttpClient mHttpClient; + + private String mGetUrl; + + private String mPostUrl; + + private long mClientVersion; + + private boolean mLoggedin; + + private long mLastLoginTime; + + private int mActionId; + + private Account mAccount; + + private JSONArray mUpdateArray; + + private GTaskClient() { + mHttpClient = null; + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + mClientVersion = -1; + mLoggedin = false; + mLastLoginTime = 0; + mActionId = 1; + mAccount = null; + mUpdateArray = null; + } + + public static synchronized GTaskClient getInstance() { + if (mInstance == null) { + mInstance = new GTaskClient(); + } + return mInstance; + } + + public boolean login(Activity activity) { + // we suppose that the cookie would expire after 5 minutes + // then we need to re-login + final long interval = 1000 * 60 * 5; + if (mLastLoginTime + interval < System.currentTimeMillis()) { + mLoggedin = false; + } + + // need to re-login after account switch + if (mLoggedin + && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity + .getSyncAccountName(activity))) { + mLoggedin = false; + } + + if (mLoggedin) { + Log.d(TAG, "already logged in"); + return true; + } + + mLastLoginTime = System.currentTimeMillis(); + String authToken = loginGoogleAccount(activity, false); + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + // login with custom domain if necessary + if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() + .endsWith("googlemail.com"))) { + StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); + int index = mAccount.name.indexOf('@') + 1; + String suffix = mAccount.name.substring(index); + url.append(suffix + "/"); + mGetUrl = url.toString() + "ig"; + mPostUrl = url.toString() + "r/ig"; + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true; + } + } + + // try to login with google official url + if (!mLoggedin) { + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) { + return false; + } + } + + mLoggedin = true; + return true; + } + + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + String authToken; + AccountManager accountManager = AccountManager.get(activity); + Account[] accounts = accountManager.getAccountsByType("com.google"); + + if (accounts.length == 0) { + Log.e(TAG, "there is no available google account"); + return null; + } + + String accountName = NotesPreferenceActivity.getSyncAccountName(activity); + Account account = null; + for (Account a : accounts) { + if (a.name.equals(accountName)) { + account = a; + break; + } + } + if (account != null) { + mAccount = account; + } else { + Log.e(TAG, "unable to get an account with the same name in the settings"); + return null; + } + + // get the token now + AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, + "goanna_mobile", null, activity, null, null); + try { + Bundle authTokenBundle = accountManagerFuture.getResult(); + authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); + if (invalidateToken) { + accountManager.invalidateAuthToken("com.google", authToken); + loginGoogleAccount(activity, false); + } + } catch (Exception e) { + Log.e(TAG, "get auth token failed"); + authToken = null; + } + + return authToken; + } + + private boolean tryToLoginGtask(Activity activity, String authToken) { + if (!loginGtask(authToken)) { + // maybe the auth token is out of date, now let's invalidate the + // token and try again + authToken = loginGoogleAccount(activity, true); + if (authToken == null) { + Log.e(TAG, "login google account failed"); + return false; + } + + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); + return false; + } + } + return true; + } + + private boolean loginGtask(String authToken) { + int timeoutConnection = 10000; + int timeoutSocket = 15000; + HttpParams httpParameters = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); + mHttpClient = new DefaultHttpClient(httpParameters); + BasicCookieStore localBasicCookieStore = new BasicCookieStore(); + mHttpClient.setCookieStore(localBasicCookieStore); + HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); + + // login gtask + try { + String loginUrl = mGetUrl + "?auth=" + authToken; + HttpGet httpGet = new HttpGet(loginUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the cookie now + List cookies = mHttpClient.getCookieStore().getCookies(); + boolean hasAuthCookie = false; + for (Cookie cookie : cookies) { + if (cookie.getName().contains("GTL")) { + hasAuthCookie = true; + } + } + if (!hasAuthCookie) { + Log.w(TAG, "it seems that there is no auth cookie"); + } + + // get the client version + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + mClientVersion = js.getLong("v"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return false; + } catch (Exception e) { + // simply catch all exceptions + Log.e(TAG, "httpget gtask_url failed"); + return false; + } + + return true; + } + + private int getActionId() { + return mActionId++; + } + + private HttpPost createHttpPost() { + HttpPost httpPost = new HttpPost(mPostUrl); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + httpPost.setHeader("AT", "1"); + return httpPost; + } + + private String getResponseContent(HttpEntity entity) throws IOException { + String contentEncoding = null; + if (entity.getContentEncoding() != null) { + contentEncoding = entity.getContentEncoding().getValue(); + Log.d(TAG, "encoding: " + contentEncoding); + } + + InputStream input = entity.getContent(); + if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { + input = new GZIPInputStream(entity.getContent()); + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater); + } + + try { + InputStreamReader isr = new InputStreamReader(input); + BufferedReader br = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); + + while (true) { + String buff = br.readLine(); + if (buff == null) { + return sb.toString(); + } + sb = sb.append(buff); + } + } finally { + input.close(); + } + } + + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + HttpPost httpPost = createHttpPost(); + try { + LinkedList list = new LinkedList(); + list.add(new BasicNameValuePair("r", js.toString())); + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); + httpPost.setEntity(entity); + + // execute the post + HttpResponse response = mHttpClient.execute(httpPost); + String jsString = getResponseContent(response.getEntity()); + return new JSONObject(jsString); + + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("postRequest failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("unable to convert response content to jsonobject"); + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("error occurs when posting request"); + } + } + + public void createTask(Task task) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(task.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // post + JSONObject jsResponse = postRequest(jsPost); + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create task: handing jsonobject failed"); + } + } + + public void createTaskList(TaskList tasklist) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + actionList.put(tasklist.getCreateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + // post + JSONObject jsResponse = postRequest(jsPost); + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( + GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("create tasklist: handing jsonobject failed"); + } + } + + public void commitUpdate() throws NetworkFailureException { + if (mUpdateArray != null) { + try { + JSONObject jsPost = new JSONObject(); + + // action_list + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("commit update: handing jsonobject failed"); + } + } + } + + public void addUpdateNode(Node node) throws NetworkFailureException { + if (node != null) { + // too many update items may result in an error + // set max to 10 items + if (mUpdateArray != null && mUpdateArray.length() > 10) { + commitUpdate(); + } + + if (mUpdateArray == null) + mUpdateArray = new JSONArray(); + mUpdateArray.put(node.getUpdateAction(getActionId())); + } + } + + public void moveTask(Task task, TaskList preParent, TaskList curParent) + throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); + if (preParent == curParent && task.getPriorSibling() != null) { + // put prioring_sibing_id only if moving within the tasklist and + // it is not the first one + action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); + } + action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); + action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); + if (preParent != curParent) { + // put the dest_list only if moving between tasklists + action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); + } + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("move task: handing jsonobject failed"); + } + } + + public void deleteNode(Node node) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + + // action_list + node.setDeleted(true); + actionList.put(node.getUpdateAction(getActionId())); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + postRequest(jsPost); + mUpdateArray = null; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("delete node: handing jsonobject failed"); + } + } + + public JSONArray getTaskLists() throws NetworkFailureException { + if (!mLoggedin) { + Log.e(TAG, "please login first"); + throw new ActionFailureException("not logged in"); + } + + try { + HttpGet httpGet = new HttpGet(mGetUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // get the task list + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); + } catch (ClientProtocolException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (IOException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new NetworkFailureException("gettasklists: httpget failed"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task lists: handing jasonobject failed"); + } + } + + public JSONArray getTaskList(String listGid) throws NetworkFailureException { + commitUpdate(); + try { + JSONObject jsPost = new JSONObject(); + JSONArray actionList = new JSONArray(); + JSONObject action = new JSONObject(); + + // action_list + action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); + action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); + actionList.put(action); + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // client_version + jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + + JSONObject jsResponse = postRequest(jsPost); + return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("get task list: handing jsonobject failed"); + } + } + + public Account getSyncAccount() { + return mAccount; + } + + public void resetUpdateArray() { + mUpdateArray = null; + } +} diff --git a/src/gtask/remote/GTaskManager.java b/src/gtask/remote/GTaskManager.java new file mode 100644 index 0000000..d2b4082 --- /dev/null +++ b/src/gtask/remote/GTaskManager.java @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.data.MetaData; +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.SqlNote; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +public class GTaskManager { + private static final String TAG = GTaskManager.class.getSimpleName(); + + public static final int STATE_SUCCESS = 0; + + public static final int STATE_NETWORK_ERROR = 1; + + public static final int STATE_INTERNAL_ERROR = 2; + + public static final int STATE_SYNC_IN_PROGRESS = 3; + + public static final int STATE_SYNC_CANCELLED = 4; + + private static GTaskManager mInstance = null; + + private Activity mActivity; + + private Context mContext; + + private ContentResolver mContentResolver; + + private boolean mSyncing; + + private boolean mCancelled; + + private HashMap mGTaskListHashMap; + + private HashMap mGTaskHashMap; + + private HashMap mMetaHashMap; + + private TaskList mMetaList; + + private HashSet mLocalDeleteIdMap; + + private HashMap mGidToNid; + + private HashMap mNidToGid; + + private GTaskManager() { + mSyncing = false; + mCancelled = false; + mGTaskListHashMap = new HashMap(); + mGTaskHashMap = new HashMap(); + mMetaHashMap = new HashMap(); + mMetaList = null; + mLocalDeleteIdMap = new HashSet(); + mGidToNid = new HashMap(); + mNidToGid = new HashMap(); + } + + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); + } + return mInstance; + } + + public synchronized void setActivityContext(Activity activity) { + // used for getting authtoken + mActivity = activity; + } + + public int sync(Context context, GTaskASyncTask asyncTask) { + if (mSyncing) { + Log.d(TAG, "Sync is in progress"); + return STATE_SYNC_IN_PROGRESS; + } + mContext = context; + mContentResolver = mContext.getContentResolver(); + mSyncing = true; + mCancelled = false; + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + + try { + GTaskClient client = GTaskClient.getInstance(); + client.resetUpdateArray(); + + // login google task + if (!mCancelled) { + if (!client.login(mActivity)) { + throw new NetworkFailureException("login google task failed"); + } + } + + // get the task list from google + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); + initGTaskList(); + + // do content sync work + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); + syncContent(); + } catch (NetworkFailureException e) { + Log.e(TAG, e.toString()); + return STATE_NETWORK_ERROR; + } catch (ActionFailureException e) { + Log.e(TAG, e.toString()); + return STATE_INTERNAL_ERROR; + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return STATE_INTERNAL_ERROR; + } finally { + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + mSyncing = false; + } + + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; + } + + private void initGTaskList() throws NetworkFailureException { + if (mCancelled) + return; + GTaskClient client = GTaskClient.getInstance(); + try { + JSONArray jsTaskLists = client.getTaskLists(); + + // init meta list first + mMetaList = null; + for (int i = 0; i < jsTaskLists.length(); i++) { + JSONObject object = jsTaskLists.getJSONObject(i); + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + if (name + .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + mMetaList = new TaskList(); + mMetaList.setContentByRemoteJSON(object); + + // load meta data + JSONArray jsMetas = client.getTaskList(gid); + for (int j = 0; j < jsMetas.length(); j++) { + object = (JSONObject) jsMetas.getJSONObject(j); + MetaData metaData = new MetaData(); + metaData.setContentByRemoteJSON(object); + if (metaData.isWorthSaving()) { + mMetaList.addChildTask(metaData); + if (metaData.getGid() != null) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); + } + } + } + } + } + + // create meta list if not existed + if (mMetaList == null) { + mMetaList = new TaskList(); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_META); + GTaskClient.getInstance().createTaskList(mMetaList); + } + + // init task list + for (int i = 0; i < jsTaskLists.length(); i++) { + JSONObject object = jsTaskLists.getJSONObject(i); + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) + && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_META)) { + TaskList tasklist = new TaskList(); + tasklist.setContentByRemoteJSON(object); + mGTaskListHashMap.put(gid, tasklist); + mGTaskHashMap.put(gid, tasklist); + + // load tasks + JSONArray jsTasks = client.getTaskList(gid); + for (int j = 0; j < jsTasks.length(); j++) { + object = (JSONObject) jsTasks.getJSONObject(j); + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + Task task = new Task(); + task.setContentByRemoteJSON(object); + if (task.isWorthSaving()) { + task.setMetaInfo(mMetaHashMap.get(gid)); + tasklist.addChildTask(task); + mGTaskHashMap.put(gid, task); + } + } + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("initGTaskList: handing JSONObject failed"); + } + } + + private void syncContent() throws NetworkFailureException { + int syncType; + Cursor c = null; + String gid; + Node node; + + mLocalDeleteIdMap.clear(); + + if (mCancelled) { + return; + } + + // for local deleted note + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id=?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, null); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); + } + + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + } + } else { + Log.w(TAG, "failed to query trash folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // sync folder first + syncFolder(); + + // for note existing in database + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // local add + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // remote delete + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing note in database"); + } + + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // go through remaining items + Iterator> iter = mGTaskHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + node = entry.getValue(); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + + // mCancelled can be set by another thread, so we neet to check one by + // one + // clear local delete table + if (!mCancelled) { + if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { + throw new ActionFailureException("failed to batch-delete local deleted notes"); + } + } + + // refresh local sync id + if (!mCancelled) { + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); + } + + } + + private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + Node node; + int syncType; + + if (mCancelled) { + return; + } + + // for root folder + try { + c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); + if (c != null) { + c.moveToNext(); + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); + mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); + // for system folder, only update remote name if necessary + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } else { + Log.w(TAG, "failed to query root folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for call-note folder + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + }, null); + if (c != null) { + if (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); + mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // for system folder, only update remote name if + // necessary + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } + } else { + Log.w(TAG, "failed to query call note folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for local existing folders + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + syncType = node.getSyncAction(c); + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // local add + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // remote delete + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // for remote add folders + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + gid = entry.getKey(); + node = entry.getValue(); + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + } + + if (!mCancelled) + GTaskClient.getInstance().commitUpdate(); + } + + private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + MetaData meta; + switch (syncType) { + case Node.SYNC_ACTION_ADD_LOCAL: + addLocalNode(node); + break; + case Node.SYNC_ACTION_ADD_REMOTE: + addRemoteNode(node, c); + break; + case Node.SYNC_ACTION_DEL_LOCAL: + meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + break; + case Node.SYNC_ACTION_DEL_REMOTE: + meta = mMetaHashMap.get(node.getGid()); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + GTaskClient.getInstance().deleteNode(node); + break; + case Node.SYNC_ACTION_UPDATE_LOCAL: + updateLocalNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_REMOTE: + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_CONFLICT: + // merging both modifications maybe a good idea + // right now just use local update simply + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_NONE: + break; + case Node.SYNC_ACTION_ERROR: + default: + throw new ActionFailureException("unkown sync action type"); + } + } + + private void addLocalNode(Node node) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote; + if (node instanceof TaskList) { + if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); + } else if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); + } else { + sqlNote = new SqlNote(mContext); + sqlNote.setContent(node.getLocalJSONFromContent()); + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); + } + } else { + sqlNote = new SqlNote(mContext); + JSONObject js = node.getLocalJSONFromContent(); + try { + if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + if (note.has(NoteColumns.ID)) { + long id = note.getLong(NoteColumns.ID); + if (DataUtils.existInNoteDatabase(mContentResolver, id)) { + // the id is not available, have to create a new one + note.remove(NoteColumns.ID); + } + } + } + + if (js.has(GTaskStringUtils.META_HEAD_DATA)) { + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (data.has(DataColumns.ID)) { + long dataId = data.getLong(DataColumns.ID); + if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { + // the data id is not available, have to create + // a new one + data.remove(DataColumns.ID); + } + } + } + + } + } catch (JSONException e) { + Log.w(TAG, e.toString()); + e.printStackTrace(); + } + sqlNote.setContent(js); + + Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); + if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot add local node"); + } + sqlNote.setParentId(parentId.longValue()); + } + + // create the local node + sqlNote.setGtaskId(node.getGid()); + sqlNote.commit(false); + + // update gid-nid mapping + mGidToNid.put(node.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), node.getGid()); + + // update meta + updateRemoteMeta(node.getGid(), sqlNote); + } + + private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote; + // update the note locally + sqlNote = new SqlNote(mContext, c); + sqlNote.setContent(node.getLocalJSONFromContent()); + + Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) + : new Long(Notes.ID_ROOT_FOLDER); + if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot update local node"); + } + sqlNote.setParentId(parentId.longValue()); + sqlNote.commit(true); + + // update meta info + updateRemoteMeta(node.getGid(), sqlNote); + } + + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + Node n; + + // update remotely + if (sqlNote.isNoteType()) { + Task task = new Task(); + task.setContentByLocalJSON(sqlNote.getContent()); + + String parentGid = mNidToGid.get(sqlNote.getParentId()); + if (parentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); + throw new ActionFailureException("cannot add remote task"); + } + mGTaskListHashMap.get(parentGid).addChildTask(task); + + GTaskClient.getInstance().createTask(task); + n = (Node) task; + + // add meta + updateRemoteMeta(task.getGid(), sqlNote); + } else { + TaskList tasklist = null; + + // we need to skip folder if it has already existed + String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; + if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) + folderName += GTaskStringUtils.FOLDER_DEFAULT; + else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) + folderName += GTaskStringUtils.FOLDER_CALL_NOTE; + else + folderName += sqlNote.getSnippet(); + + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + String gid = entry.getKey(); + TaskList list = entry.getValue(); + + if (list.getName().equals(folderName)) { + tasklist = list; + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + } + break; + } + } + + // no match we can add now + if (tasklist == null) { + tasklist = new TaskList(); + tasklist.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().createTaskList(tasklist); + mGTaskListHashMap.put(tasklist.getGid(), tasklist); + } + n = (Node) tasklist; + } + + // update local note + sqlNote.setGtaskId(n.getGid()); + sqlNote.commit(false); + sqlNote.resetLocalModified(); + sqlNote.commit(true); + + // gid-id mapping + mGidToNid.put(n.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), n.getGid()); + } + + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote = new SqlNote(mContext, c); + + // update remotely + node.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(node); + + // update meta + updateRemoteMeta(node.getGid(), sqlNote); + + // move task if necessary + if (sqlNote.isNoteType()) { + Task task = (Task) node; + TaskList preParentList = task.getParent(); + + String curParentGid = mNidToGid.get(sqlNote.getParentId()); + if (curParentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); + throw new ActionFailureException("cannot update remote task"); + } + TaskList curParentList = mGTaskListHashMap.get(curParentGid); + + if (preParentList != curParentList) { + preParentList.removeChildTask(task); + curParentList.addChildTask(task); + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); + } + } + + // clear local modified flag + sqlNote.resetLocalModified(); + sqlNote.commit(true); + } + + private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + if (sqlNote != null && sqlNote.isNoteType()) { + MetaData metaData = mMetaHashMap.get(gid); + if (metaData != null) { + metaData.setMeta(gid, sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(metaData); + } else { + metaData = new MetaData(); + metaData.setMeta(gid, sqlNote.getContent()); + mMetaList.addChildTask(metaData); + mMetaHashMap.put(gid, metaData); + GTaskClient.getInstance().createTask(metaData); + } + } + } + + private void refreshLocalSyncId() throws NetworkFailureException { + if (mCancelled) { + return; + } + + // get the latest gtask list + mGTaskHashMap.clear(); + mGTaskListHashMap.clear(); + mMetaHashMap.clear(); + initGTaskList(); + + Cursor c = null; + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); + Node node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + ContentValues values = new ContentValues(); + values.put(NoteColumns.SYNC_ID, node.getLastModified()); + mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + c.getLong(SqlNote.ID_COLUMN)), values, null, null); + } else { + Log.e(TAG, "something is missed"); + throw new ActionFailureException( + "some local items don't have gid after sync"); + } + } + } else { + Log.w(TAG, "failed to query local note to refresh sync id"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + } + + public String getSyncAccount() { + return GTaskClient.getInstance().getSyncAccount().name; + } + + public void cancelSync() { + mCancelled = true; + } +} diff --git a/src/gtask/remote/GTaskSyncService.java b/src/gtask/remote/GTaskSyncService.java new file mode 100644 index 0000000..cca36f7 --- /dev/null +++ b/src/gtask/remote/GTaskSyncService.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; + +public class GTaskSyncService extends Service { + public final static String ACTION_STRING_NAME = "sync_action_type"; + + public final static int ACTION_START_SYNC = 0; + + public final static int ACTION_CANCEL_SYNC = 1; + + public final static int ACTION_INVALID = 2; + + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; + + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; + + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; + + private static GTaskASyncTask mSyncTask = null; + + private static String mSyncProgress = ""; + + private void startSync() { + if (mSyncTask == null) { + mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + public void onComplete() { + mSyncTask = null; + sendBroadcast(""); + stopSelf(); + } + }); + sendBroadcast(""); + mSyncTask.execute(); + } + } + + private void cancelSync() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + @Override + public void onCreate() { + mSyncTask = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Bundle bundle = intent.getExtras(); + if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { + switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { + case ACTION_START_SYNC: + startSync(); + break; + case ACTION_CANCEL_SYNC: + cancelSync(); + break; + default: + break; + } + return START_STICKY; + } + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onLowMemory() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); + } + } + + public IBinder onBind(Intent intent) { + return null; + } + + public void sendBroadcast(String msg) { + mSyncProgress = msg; + Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); + intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); + intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); + sendBroadcast(intent); + } + + public static void startSync(Activity activity) { + GTaskManager.getInstance().setActivityContext(activity); + Intent intent = new Intent(activity, GTaskSyncService.class); + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); + activity.startService(intent); + } + + public static void cancelSync(Context context) { + Intent intent = new Intent(context, GTaskSyncService.class); + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); + context.startService(intent); + } + + public static boolean isSyncing() { + return mSyncTask != null; + } + + public static String getProgressString() { + return mSyncProgress; + } +} diff --git a/src/model/Note.java b/src/model/Note.java new file mode 100644 index 0000000..6706cf6 --- /dev/null +++ b/src/model/Note.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + + +public class Note { + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + private static final String TAG = "Note"; + /** + * Create a new note id for adding a new note to databases + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() { + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + } + + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + } + + public long getTextDataId() { + return mNoteData.mTextDataId; + } + + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + } + + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + } + + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData { + private long mTextDataId; + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues; + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + return null; + } + } +} diff --git a/src/model/WorkingNote.java b/src/model/WorkingNote.java new file mode 100644 index 0000000..be081e4 --- /dev/null +++ b/src/model/WorkingNote.java @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + + +public class WorkingNote { + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0; + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * Update widget content if there exist any widget of this note + */ + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE + && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + return true; + } else { + return false; + } + } + + public boolean existInDatabase() { + return mNoteId > 0; + } + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) { + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) { + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + } + + public String getContent() { + return mContent; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() { + return mBgColorId; + } + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() { + return mMode; + } + + public long getNoteId() { + return mNoteId; + } + + public long getFolderId() { + return mFolderId; + } + + public int getWidgetId() { + return mWidgetId; + } + + public int getWidgetType() { + return mWidgetType; + } + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged(); + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set); + + /** + * Call when user create note from widget + */ + void onWidgetChanged(); + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode); + } +} diff --git a/src/tool/BackupUtils.java b/src/tool/BackupUtils.java new file mode 100644 index 0000000..39f6ec4 --- /dev/null +++ b/src/tool/BackupUtils.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.Context; +import android.database.Cursor; +import android.os.Environment; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + + +public class BackupUtils { + private static final String TAG = "BackupUtils"; + // Singleton stuff + private static BackupUtils sInstance; + + public static synchronized BackupUtils getInstance(Context context) { + if (sInstance == null) { + sInstance = new BackupUtils(context); + } + return sInstance; + } + + /** + * Following states are signs to represents backup or restore + * status + */ + // Currently, the sdcard is not mounted + public static final int STATE_SD_CARD_UNMOUONTED = 0; + // The backup file not exist + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // The data is not well formated, may be changed by other programs + public static final int STATE_DATA_DESTROIED = 2; + // Some run-time exception which causes restore or backup fails + public static final int STATE_SYSTEM_ERROR = 3; + // Backup or restore success + public static final int STATE_SUCCESS = 4; + + private TextExport mTextExport; + + private BackupUtils(Context context) { + mTextExport = new TextExport(context); + } + + private static boolean externalStorageAvailable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + public int exportToText() { + return mTextExport.exportToText(); + } + + public String getExportedTextFileName() { + return mTextExport.mFileName; + } + + public String getExportedTextFileDir() { + return mTextExport.mFileDirectory; + } + + private static class TextExport { + private static final String[] NOTE_PROJECTION = { + NoteColumns.ID, + NoteColumns.MODIFIED_DATE, + NoteColumns.SNIPPET, + NoteColumns.TYPE + }; + + private static final int NOTE_COLUMN_ID = 0; + + private static final int NOTE_COLUMN_MODIFIED_DATE = 1; + + private static final int NOTE_COLUMN_SNIPPET = 2; + + private static final String[] DATA_PROJECTION = { + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + private static final int DATA_COLUMN_CONTENT = 0; + + private static final int DATA_COLUMN_MIME_TYPE = 1; + + private static final int DATA_COLUMN_CALL_DATE = 2; + + private static final int DATA_COLUMN_PHONE_NUMBER = 4; + + private final String [] TEXT_FORMAT; + private static final int FORMAT_FOLDER_NAME = 0; + private static final int FORMAT_NOTE_DATE = 1; + private static final int FORMAT_NOTE_CONTENT = 2; + + private Context mContext; + private String mFileName; + private String mFileDirectory; + + public TextExport(Context context) { + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); + mContext = context; + mFileName = ""; + mFileDirectory = ""; + } + + private String getFormat(int id) { + return TEXT_FORMAT[id]; + } + + /** + * Export the folder identified by folder id to text + */ + private void exportFolderToText(String folderId, PrintStream ps) { + // Query notes belong to this folder + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { + folderId + }, null); + + if (notesCursor != null) { + if (notesCursor.moveToFirst()) { + do { + // Print note's last modified date + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = notesCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (notesCursor.moveToNext()); + } + notesCursor.close(); + } + } + + /** + * Export note identified by id to a print stream + */ + private void exportNoteToText(String noteId, PrintStream ps) { + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, + DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { + noteId + }, null); + + if (dataCursor != null) { + if (dataCursor.moveToFirst()) { + do { + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); + if (DataConstants.CALL_NOTE.equals(mimeType)) { + // Print phone number + String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); + long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); + String location = dataCursor.getString(DATA_COLUMN_CONTENT); + + if (!TextUtils.isEmpty(phoneNumber)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + phoneNumber)); + } + // Print call date + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat + .format(mContext.getString(R.string.format_datetime_mdhm), + callDate))); + // Print call attachment location + if (!TextUtils.isEmpty(location)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + location)); + } + } else if (DataConstants.NOTE.equals(mimeType)) { + String content = dataCursor.getString(DATA_COLUMN_CONTENT); + if (!TextUtils.isEmpty(content)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + content)); + } + } + } while (dataCursor.moveToNext()); + } + dataCursor.close(); + } + // print a line separator between note + try { + ps.write(new byte[] { + Character.LINE_SEPARATOR, Character.LETTER_NUMBER + }); + } catch (IOException e) { + Log.e(TAG, e.toString()); + } + } + + /** + * Note will be exported as text which is user readable + */ + public int exportToText() { + if (!externalStorageAvailable()) { + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUONTED; + } + + PrintStream ps = getExportToTextPrintStream(); + if (ps == null) { + Log.e(TAG, "get print stream error"); + return STATE_SYSTEM_ERROR; + } + // First export folder and its notes + Cursor folderCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); + + if (folderCursor != null) { + if (folderCursor.moveToFirst()) { + do { + // Print folder's name + String folderName = ""; + if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + folderName = mContext.getString(R.string.call_record_folder_name); + } else { + folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); + } + if (!TextUtils.isEmpty(folderName)) { + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID); + exportFolderToText(folderId, ps); + } while (folderCursor.moveToNext()); + } + folderCursor.close(); + } + + // Export notes in root's folder + Cursor noteCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + + "=0", null, null); + + if (noteCursor != null) { + if (noteCursor.moveToFirst()) { + do { + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = noteCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (noteCursor.moveToNext()); + } + noteCursor.close(); + } + ps.close(); + + return STATE_SUCCESS; + } + + /** + * Get a print stream pointed to the file {@generateExportedTextFile} + */ + private PrintStream getExportToTextPrintStream() { + File file = generateFileMountedOnSDcard(mContext, R.string.file_path, + R.string.file_name_txt_format); + if (file == null) { + Log.e(TAG, "create file to exported failed"); + return null; + } + mFileName = file.getName(); + mFileDirectory = mContext.getString(R.string.file_path); + PrintStream ps = null; + try { + FileOutputStream fos = new FileOutputStream(file); + ps = new PrintStream(fos); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } catch (NullPointerException e) { + e.printStackTrace(); + return null; + } + return ps; + } + } + + /** + * Generate the text file to store imported data + */ + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); + sb.append(context.getString(filePathResId)); + File filedir = new File(sb.toString()); + sb.append(context.getString( + fileNameFormatResId, + DateFormat.format(context.getString(R.string.format_date_ymd), + System.currentTimeMillis()))); + File file = new File(sb.toString()); + + try { + if (!filedir.exists()) { + filedir.mkdir(); + } + if (!file.exists()) { + file.createNewFile(); + } + return file; + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } +} + + diff --git a/src/tool/DataUtils.java b/src/tool/DataUtils.java new file mode 100644 index 0000000..2a14982 --- /dev/null +++ b/src/tool/DataUtils.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; + +import java.util.ArrayList; +import java.util.HashSet; + + +public class DataUtils { + public static final String TAG = "DataUtils"; + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + if (ids == null) { + Log.d(TAG, "the ids is null"); + return true; + } + if (ids.size() == 0) { + Log.d(TAG, "no id is in the hashset"); + return true; + } + + ArrayList operationList = new ArrayList(); + for (long id : ids) { + if(id == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Don't delete system folder root"); + continue; + } + ContentProviderOperation.Builder builder = ContentProviderOperation + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + operationList.add(builder.build()); + } + try { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.PARENT_ID, desFolderId); + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); + } + + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, + long folderId) { + if (ids == null) { + Log.d(TAG, "the ids is null"); + return true; + } + + ArrayList operationList = new ArrayList(); + for (long id : ids) { + ContentProviderOperation.Builder builder = ContentProviderOperation + .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + builder.withValue(NoteColumns.PARENT_ID, folderId); + builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + operationList.add(builder.build()); + } + + try { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + if (results == null || results.length == 0 || results[0] == null) { + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + return false; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + } + return false; + } + + /** + * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + */ + public static int getUserFolderCount(ContentResolver resolver) { + Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { "COUNT(*)" }, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, + null); + + int count = 0; + if(cursor != null) { + if(cursor.moveToFirst()) { + try { + count = cursor.getInt(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "get folder count failed:" + e.toString()); + } finally { + cursor.close(); + } + } + } + return count; + } + + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, + new String [] {String.valueOf(type)}, + null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), + null, null, null, null); + + boolean exist = false; + if (cursor != null) { + if (cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", + new String[] { name }, null); + boolean exist = false; + if(cursor != null) { + if(cursor.getCount() > 0) { + exist = true; + } + cursor.close(); + } + return exist; + } + + public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, + NoteColumns.PARENT_ID + "=?", + new String[] { String.valueOf(folderId) }, + null); + + HashSet set = null; + if (c != null) { + if (c.moveToFirst()) { + set = new HashSet(); + do { + try { + AppWidgetAttribute widget = new AppWidgetAttribute(); + widget.widgetId = c.getInt(0); + widget.widgetType = c.getInt(1); + set.add(widget); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, e.toString()); + } + } while (c.moveToNext()); + } + c.close(); + } + return set; + } + + public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.PHONE_NUMBER }, + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + null); + + if (cursor != null && cursor.moveToFirst()) { + try { + return cursor.getString(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call number fails " + e.toString()); + } finally { + cursor.close(); + } + } + return ""; + } + + public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + new String [] { CallNote.NOTE_ID }, + CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" + + CallNote.PHONE_NUMBER + ",?)", + new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, + null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + try { + return cursor.getLong(0); + } catch (IndexOutOfBoundsException e) { + Log.e(TAG, "Get call note id fails " + e.toString()); + } + } + cursor.close(); + } + return 0; + } + + public static String getSnippetById(ContentResolver resolver, long noteId) { + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, + new String [] { NoteColumns.SNIPPET }, + NoteColumns.ID + "=?", + new String [] { String.valueOf(noteId)}, + null); + + if (cursor != null) { + String snippet = ""; + if (cursor.moveToFirst()) { + snippet = cursor.getString(0); + } + cursor.close(); + return snippet; + } + throw new IllegalArgumentException("Note is not found with id: " + noteId); + } + + public static String getFormattedSnippet(String snippet) { + if (snippet != null) { + snippet = snippet.trim(); + int index = snippet.indexOf('\n'); + if (index != -1) { + snippet = snippet.substring(0, index); + } + } + return snippet; + } +} diff --git a/src/tool/GTaskStringUtils.java b/src/tool/GTaskStringUtils.java new file mode 100644 index 0000000..666b729 --- /dev/null +++ b/src/tool/GTaskStringUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +public class GTaskStringUtils { + + public final static String GTASK_JSON_ACTION_ID = "action_id"; + + public final static String GTASK_JSON_ACTION_LIST = "action_list"; + + public final static String GTASK_JSON_ACTION_TYPE = "action_type"; + + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; + + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; + + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; + + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; + + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; + + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; + + public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; + + public final static String GTASK_JSON_COMPLETED = "completed"; + + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; + + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; + + public final static String GTASK_JSON_DELETED = "deleted"; + + public final static String GTASK_JSON_DEST_LIST = "dest_list"; + + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; + + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; + + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; + + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; + + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; + + public final static String GTASK_JSON_ID = "id"; + + public final static String GTASK_JSON_INDEX = "index"; + + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; + + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; + + public final static String GTASK_JSON_LIST_ID = "list_id"; + + public final static String GTASK_JSON_LISTS = "lists"; + + public final static String GTASK_JSON_NAME = "name"; + + public final static String GTASK_JSON_NEW_ID = "new_id"; + + public final static String GTASK_JSON_NOTES = "notes"; + + public final static String GTASK_JSON_PARENT_ID = "parent_id"; + + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; + + public final static String GTASK_JSON_RESULTS = "results"; + + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; + + public final static String GTASK_JSON_TASKS = "tasks"; + + public final static String GTASK_JSON_TYPE = "type"; + + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; + + public final static String GTASK_JSON_TYPE_TASK = "TASK"; + + public final static String GTASK_JSON_USER = "user"; + + public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; + + public final static String FOLDER_DEFAULT = "Default"; + + public final static String FOLDER_CALL_NOTE = "Call_Note"; + + public final static String FOLDER_META = "METADATA"; + + public final static String META_HEAD_GTASK_ID = "meta_gid"; + + public final static String META_HEAD_NOTE = "meta_note"; + + public final static String META_HEAD_DATA = "meta_data"; + + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; + +} diff --git a/src/tool/ResourceParser.java b/src/tool/ResourceParser.java new file mode 100644 index 0000000..1ad3ad6 --- /dev/null +++ b/src/tool/ResourceParser.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.tool; + +import android.content.Context; +import android.preference.PreferenceManager; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesPreferenceActivity; + +public class ResourceParser { + + public static final int YELLOW = 0; + public static final int BLUE = 1; + public static final int WHITE = 2; + public static final int GREEN = 3; + public static final int RED = 4; + + public static final int BG_DEFAULT_COLOR = YELLOW; + + public static final int TEXT_SMALL = 0; + public static final int TEXT_MEDIUM = 1; + public static final int TEXT_LARGE = 2; + public static final int TEXT_SUPER = 3; + + public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; + + public static class NoteBgResources { + private final static int [] BG_EDIT_RESOURCES = new int [] { + R.drawable.edit_yellow, + R.drawable.edit_blue, + R.drawable.edit_white, + R.drawable.edit_green, + R.drawable.edit_red + }; + + private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { + R.drawable.edit_title_yellow, + R.drawable.edit_title_blue, + R.drawable.edit_title_white, + R.drawable.edit_title_green, + R.drawable.edit_title_red + }; + + public static int getNoteBgResource(int id) { + return BG_EDIT_RESOURCES[id]; + } + + public static int getNoteTitleBgResource(int id) { + return BG_EDIT_TITLE_RESOURCES[id]; + } + } + + public static int getDefaultBgId(Context context) { + if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( + NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); + } else { + return BG_DEFAULT_COLOR; + } + } + + public static class NoteItemBgResources { + private final static int [] BG_FIRST_RESOURCES = new int [] { + R.drawable.list_yellow_up, + R.drawable.list_blue_up, + R.drawable.list_white_up, + R.drawable.list_green_up, + R.drawable.list_red_up + }; + + private final static int [] BG_NORMAL_RESOURCES = new int [] { + R.drawable.list_yellow_middle, + R.drawable.list_blue_middle, + R.drawable.list_white_middle, + R.drawable.list_green_middle, + R.drawable.list_red_middle + }; + + private final static int [] BG_LAST_RESOURCES = new int [] { + R.drawable.list_yellow_down, + R.drawable.list_blue_down, + R.drawable.list_white_down, + R.drawable.list_green_down, + R.drawable.list_red_down, + }; + + private final static int [] BG_SINGLE_RESOURCES = new int [] { + R.drawable.list_yellow_single, + R.drawable.list_blue_single, + R.drawable.list_white_single, + R.drawable.list_green_single, + R.drawable.list_red_single + }; + + public static int getNoteBgFirstRes(int id) { + return BG_FIRST_RESOURCES[id]; + } + + public static int getNoteBgLastRes(int id) { + return BG_LAST_RESOURCES[id]; + } + + public static int getNoteBgSingleRes(int id) { + return BG_SINGLE_RESOURCES[id]; + } + + public static int getNoteBgNormalRes(int id) { + return BG_NORMAL_RESOURCES[id]; + } + + public static int getFolderBgRes() { + return R.drawable.list_folder; + } + } + + public static class WidgetBgResources { + private final static int [] BG_2X_RESOURCES = new int [] { + R.drawable.widget_2x_yellow, + R.drawable.widget_2x_blue, + R.drawable.widget_2x_white, + R.drawable.widget_2x_green, + R.drawable.widget_2x_red, + }; + + public static int getWidget2xBgResource(int id) { + return BG_2X_RESOURCES[id]; + } + + private final static int [] BG_4X_RESOURCES = new int [] { + R.drawable.widget_4x_yellow, + R.drawable.widget_4x_blue, + R.drawable.widget_4x_white, + R.drawable.widget_4x_green, + R.drawable.widget_4x_red + }; + + public static int getWidget4xBgResource(int id) { + return BG_4X_RESOURCES[id]; + } + } + + public static class TextAppearanceResources { + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper + }; + + public static int getTexAppearanceResource(int id) { + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if (id >= TEXTAPPEARANCE_RESOURCES.length) { + return BG_DEFAULT_FONT_SIZE; + } + return TEXTAPPEARANCE_RESOURCES[id]; + } + + public static int getResourcesSize() { + return TEXTAPPEARANCE_RESOURCES.length; + } + } +} diff --git a/src/ui/AlarmAlertActivity.java b/src/ui/AlarmAlertActivity.java new file mode 100644 index 0000000..85723be --- /dev/null +++ b/src/ui/AlarmAlertActivity.java @@ -0,0 +1,158 @@ +/* + * 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; + } + } +} diff --git a/src/ui/AlarmInitReceiver.java b/src/ui/AlarmInitReceiver.java new file mode 100644 index 0000000..f221202 --- /dev/null +++ b/src/ui/AlarmInitReceiver.java @@ -0,0 +1,65 @@ +/* + * 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.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + + +public class AlarmInitReceiver extends BroadcastReceiver { + + private static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE + }; + + private static final int COLUMN_ID = 0; + private static final int COLUMN_ALERTED_DATE = 1; + + @Override + public void onReceive(Context context, Intent intent) { + long currentDate = System.currentTimeMillis(); + Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + new String[] { String.valueOf(currentDate) }, + null); + + if (c != null) { + if (c.moveToFirst()) { + do { + long alertDate = c.getLong(COLUMN_ALERTED_DATE); + Intent sender = new Intent(context, AlarmReceiver.class); + sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); + AlarmManager alermManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + } while (c.moveToNext()); + } + c.close(); + } + } +} diff --git a/src/ui/AlarmReceiver.java b/src/ui/AlarmReceiver.java new file mode 100644 index 0000000..54e503b --- /dev/null +++ b/src/ui/AlarmReceiver.java @@ -0,0 +1,30 @@ +/* + * 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.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + intent.setClass(context, AlarmAlertActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } +} diff --git a/src/ui/DateTimePicker.java b/src/ui/DateTimePicker.java new file mode 100644 index 0000000..496b0cd --- /dev/null +++ b/src/ui/DateTimePicker.java @@ -0,0 +1,485 @@ +/* + * 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 java.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; + +public class DateTimePicker extends FrameLayout { + + private static final boolean DEFAULT_ENABLE_STATE = true; + + private static final int HOURS_IN_HALF_DAY = 12; + private static final int HOURS_IN_ALL_DAY = 24; + private static final int DAYS_IN_ALL_WEEK = 7; + private static final int DATE_SPINNER_MIN_VAL = 0; + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; + private static final int MINUT_SPINNER_MIN_VAL = 0; + private static final int MINUT_SPINNER_MAX_VAL = 59; + private static final int AMPM_SPINNER_MIN_VAL = 0; + private static final int AMPM_SPINNER_MAX_VAL = 1; + + private final NumberPicker mDateSpinner; + private final NumberPicker mHourSpinner; + private final NumberPicker mMinuteSpinner; + private final NumberPicker mAmPmSpinner; + private Calendar mDate; + + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + + private boolean mIsAm; + + private boolean mIs24HourView; + + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + + private boolean mInitialising; + + private OnDateTimeChangedListener mOnDateTimeChangedListener; + + private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); + updateDateControl(); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + if (!mIs24HourView) { + if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || + oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + mIsAm = !mIsAm; + updateAmPmControl(); + } + } else { + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + mDate.set(Calendar.HOUR_OF_DAY, newHour); + onDateTimeChanged(); + if (isDateChanged) { + setCurrentYear(cal.get(Calendar.YEAR)); + setCurrentMonth(cal.get(Calendar.MONTH)); + setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); + } + } + }; + + private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + offset -= 1; + } + if (offset != 0) { + mDate.add(Calendar.HOUR_OF_DAY, offset); + mHourSpinner.setValue(getCurrentHour()); + updateDateControl(); + int newHour = getCurrentHourOfDay(); + if (newHour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + updateAmPmControl(); + } else { + mIsAm = true; + updateAmPmControl(); + } + } + mDate.set(Calendar.MINUTE, newVal); + onDateTimeChanged(); + } + }; + + private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mIsAm = !mIsAm; + if (mIsAm) { + mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); + } else { + mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); + } + updateAmPmControl(); + onDateTimeChanged(); + } + }; + + public interface OnDateTimeChangedListener { + void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute); + } + + public DateTimePicker(Context context) { + this(context, System.currentTimeMillis()); + } + + public DateTimePicker(Context context, long date) { + this(context, date, DateFormat.is24HourFormat(context)); + } + + public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + mDate = Calendar.getInstance(); + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + + mDateSpinner = (NumberPicker) findViewById(R.id.date); + mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); + mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); + mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + + mHourSpinner = (NumberPicker) findViewById(R.id.hour); + mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); + mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); + mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); + mMinuteSpinner.setOnLongPressUpdateInterval(100); + mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + + String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); + mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); + mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); + mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); + mAmPmSpinner.setDisplayedValues(stringsForAmPm); + mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); + + // update controls to initial state + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + set24HourView(is24HourView); + + // set to current time + setCurrentDate(date); + + setEnabled(isEnabled()); + + // set the content descriptions + mInitialising = false; + } + + @Override + public void setEnabled(boolean enabled) { + if (mIsEnabled == enabled) { + return; + } + super.setEnabled(enabled); + mDateSpinner.setEnabled(enabled); + mMinuteSpinner.setEnabled(enabled); + mHourSpinner.setEnabled(enabled); + mAmPmSpinner.setEnabled(enabled); + mIsEnabled = enabled; + } + + @Override + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * Get the current date in millis + * + * @return the current date in millis + */ + public long getCurrentDateInTimeMillis() { + return mDate.getTimeInMillis(); + } + + /** + * Set the current date + * + * @param date The current date in millis + */ + public void setCurrentDate(long date) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(date); + setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), + cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + } + + /** + * Set the current date + * + * @param year The current year + * @param month The current month + * @param dayOfMonth The current dayOfMonth + * @param hourOfDay The current hourOfDay + * @param minute The current minute + */ + public void setCurrentDate(int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + setCurrentYear(year); + setCurrentMonth(month); + setCurrentDay(dayOfMonth); + setCurrentHour(hourOfDay); + setCurrentMinute(minute); + } + + /** + * Get current year + * + * @return The current year + */ + public int getCurrentYear() { + return mDate.get(Calendar.YEAR); + } + + /** + * Set current year + * + * @param year The current year + */ + public void setCurrentYear(int year) { + if (!mInitialising && year == getCurrentYear()) { + return; + } + mDate.set(Calendar.YEAR, year); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current month in the year + * + * @return The current month in the year + */ + public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); + } + + /** + * Set current month in the year + * + * @param month The month in the year + */ + public void setCurrentMonth(int month) { + if (!mInitialising && month == getCurrentMonth()) { + return; + } + mDate.set(Calendar.MONTH, month); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current day of the month + * + * @return The day of the month + */ + public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); + } + + /** + * Set current day of the month + * + * @param dayOfMonth The day of the month + */ + public void setCurrentDay(int dayOfMonth) { + if (!mInitialising && dayOfMonth == getCurrentDay()) { + return; + } + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); + onDateTimeChanged(); + } + + /** + * Get current hour in 24 hour mode, in the range (0~23) + * @return The current hour in 24 hour mode + */ + public int getCurrentHourOfDay() { + return mDate.get(Calendar.HOUR_OF_DAY); + } + + private int getCurrentHour() { + if (mIs24HourView){ + return getCurrentHourOfDay(); + } else { + int hour = getCurrentHourOfDay(); + if (hour > HOURS_IN_HALF_DAY) { + return hour - HOURS_IN_HALF_DAY; + } else { + return hour == 0 ? HOURS_IN_HALF_DAY : hour; + } + } + } + + /** + * Set current hour in 24 hour mode, in the range (0~23) + * + * @param hourOfDay + */ + public void setCurrentHour(int hourOfDay) { + if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { + return; + } + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + if (!mIs24HourView) { + if (hourOfDay >= HOURS_IN_HALF_DAY) { + mIsAm = false; + if (hourOfDay > HOURS_IN_HALF_DAY) { + hourOfDay -= HOURS_IN_HALF_DAY; + } + } else { + mIsAm = true; + if (hourOfDay == 0) { + hourOfDay = HOURS_IN_HALF_DAY; + } + } + updateAmPmControl(); + } + mHourSpinner.setValue(hourOfDay); + onDateTimeChanged(); + } + + /** + * Get currentMinute + * + * @return The Current Minute + */ + public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); + } + + /** + * Set current minute + */ + public void setCurrentMinute(int minute) { + if (!mInitialising && minute == getCurrentMinute()) { + return; + } + mMinuteSpinner.setValue(minute); + mDate.set(Calendar.MINUTE, minute); + onDateTimeChanged(); + } + + /** + * @return true if this is in 24 hour view else false. + */ + public boolean is24HourView () { + return mIs24HourView; + } + + /** + * Set whether in 24 hour or AM/PM mode. + * + * @param is24HourView True for 24 hour mode. False for AM/PM mode. + */ + public void set24HourView(boolean is24HourView) { + if (mIs24HourView == is24HourView) { + return; + } + mIs24HourView = is24HourView; + mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); + int hour = getCurrentHourOfDay(); + updateHourControl(); + setCurrentHour(hour); + updateAmPmControl(); + } + + private void updateDateControl() { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); + mDateSpinner.setDisplayedValues(null); + for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { + cal.add(Calendar.DAY_OF_YEAR, 1); + mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); + } + mDateSpinner.setDisplayedValues(mDateDisplayValues); + mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); + mDateSpinner.invalidate(); + } + + private void updateAmPmControl() { + if (mIs24HourView) { + mAmPmSpinner.setVisibility(View.GONE); + } else { + int index = mIsAm ? Calendar.AM : Calendar.PM; + mAmPmSpinner.setValue(index); + mAmPmSpinner.setVisibility(View.VISIBLE); + } + } + + private void updateHourControl() { + if (mIs24HourView) { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); + } else { + mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); + mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); + } + } + + /** + * Set the callback that indicates the 'Set' button has been pressed. + * @param callback the callback, if null will do nothing + */ + public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { + mOnDateTimeChangedListener = callback; + } + + private void onDateTimeChanged() { + if (mOnDateTimeChangedListener != null) { + mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), + getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); + } + } +} diff --git a/src/ui/DateTimePickerDialog.java b/src/ui/DateTimePickerDialog.java new file mode 100644 index 0000000..2c47ba4 --- /dev/null +++ b/src/ui/DateTimePickerDialog.java @@ -0,0 +1,90 @@ +/* + * 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 java.util.Calendar; + +import net.micode.notes.R; +import net.micode.notes.ui.DateTimePicker; +import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.text.format.DateFormat; +import android.text.format.DateUtils; + +public class DateTimePickerDialog extends AlertDialog implements OnClickListener { + + private Calendar mDate = Calendar.getInstance(); + private boolean mIs24HourView; + private OnDateTimeSetListener mOnDateTimeSetListener; + private DateTimePicker mDateTimePicker; + + public interface OnDateTimeSetListener { + void OnDateTimeSet(AlertDialog dialog, long date); + } + + public DateTimePickerDialog(Context context, long date) { + super(context); + mDateTimePicker = new DateTimePicker(context); + setView(mDateTimePicker); + mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { + public void onDateTimeChanged(DateTimePicker view, int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + mDate.set(Calendar.YEAR, year); + mDate.set(Calendar.MONTH, month); + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); + mDate.set(Calendar.MINUTE, minute); + updateTitle(mDate.getTimeInMillis()); + } + }); + mDate.setTimeInMillis(date); + mDate.set(Calendar.SECOND, 0); + mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); + setButton(context.getString(R.string.datetime_dialog_ok), this); + setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); + set24HourView(DateFormat.is24HourFormat(this.getContext())); + updateTitle(mDate.getTimeInMillis()); + } + + public void set24HourView(boolean is24HourView) { + mIs24HourView = is24HourView; + } + + public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { + mOnDateTimeSetListener = callBack; + } + + private void updateTitle(long date) { + int flag = + DateUtils.FORMAT_SHOW_YEAR | + DateUtils.FORMAT_SHOW_DATE | + DateUtils.FORMAT_SHOW_TIME; + flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; + setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); + } + + public void onClick(DialogInterface arg0, int arg1) { + if (mOnDateTimeSetListener != null) { + mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); + } + } + +} \ No newline at end of file diff --git a/src/ui/DropdownMenu.java b/src/ui/DropdownMenu.java new file mode 100644 index 0000000..613dc74 --- /dev/null +++ b/src/ui/DropdownMenu.java @@ -0,0 +1,61 @@ +/* + * 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.content.Context; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; + +import net.micode.notes.R; + +public class DropdownMenu { + private Button mButton; + private PopupMenu mPopupMenu; + private Menu mMenu; + + public DropdownMenu(Context context, Button button, int menuId) { + mButton = button; + mButton.setBackgroundResource(R.drawable.dropdown_icon); + mPopupMenu = new PopupMenu(context, mButton); + mMenu = mPopupMenu.getMenu(); + mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + mButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mPopupMenu.show(); + } + }); + } + + public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { + if (mPopupMenu != null) { + mPopupMenu.setOnMenuItemClickListener(listener); + } + } + + public MenuItem findItem(int id) { + return mMenu.findItem(id); + } + + public void setTitle(CharSequence title) { + mButton.setText(title); + } +} diff --git a/src/ui/FoldersListAdapter.java b/src/ui/FoldersListAdapter.java new file mode 100644 index 0000000..96b77da --- /dev/null +++ b/src/ui/FoldersListAdapter.java @@ -0,0 +1,80 @@ +/* + * 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.content.Context; +import android.database.Cursor; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + + +public class FoldersListAdapter extends CursorAdapter { + public static final String [] PROJECTION = { + NoteColumns.ID, + NoteColumns.SNIPPET + }; + + public static final int ID_COLUMN = 0; + public static final int NAME_COLUMN = 1; + + public FoldersListAdapter(Context context, Cursor c) { + super(context, c); + // TODO Auto-generated constructor stub + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return new FolderListItem(context); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + if (view instanceof FolderListItem) { + String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + ((FolderListItem) view).bind(folderName); + } + } + + public String getFolderName(Context context, int position) { + Cursor cursor = (Cursor) getItem(position); + return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context + .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + } + + private class FolderListItem extends LinearLayout { + private TextView mName; + + public FolderListItem(Context context) { + super(context); + inflate(context, R.layout.folder_list_item, this); + mName = (TextView) findViewById(R.id.tv_folder_name); + } + + public void bind(String name) { + mName.setText(name); + } + } + +} diff --git a/src/ui/NoteEditActivity.java b/src/ui/NoteEditActivity.java new file mode 100644 index 0000000..96a9ff8 --- /dev/null +++ b/src/ui/NoteEditActivity.java @@ -0,0 +1,873 @@ +/* + * 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.AlarmManager; +import android.app.AlertDialog; +import android.app.PendingIntent; +import android.app.SearchManager; +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Paint; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.style.BackgroundColorSpan; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.model.WorkingNote.NoteSettingChangedListener; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.tool.ResourceParser.TextAppearanceResources; +import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; +import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class NoteEditActivity extends Activity implements OnClickListener, + NoteSettingChangedListener, OnTextViewChangeListener { + private class HeadViewHolder { + public TextView tvModified; + + public ImageView ivAlertIcon; + + public TextView tvAlertDate; + + public ImageView ibSetBgColor; + } + + private static final Map sBgSelectorBtnsMap = new HashMap(); + static { + sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); + sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); + sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); + sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); + sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); + } + + private static final Map sBgSelectorSelectionMap = new HashMap(); + static { + sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); + sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); + sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); + sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); + sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); + } + + private static final Map sFontSizeBtnsMap = new HashMap(); + static { + sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); + sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); + sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); + sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); + } + + private static final Map sFontSelectorSelectionMap = new HashMap(); + static { + sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); + sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); + } + + private static final String TAG = "NoteEditActivity"; + + private HeadViewHolder mNoteHeaderHolder; + + private View mHeadViewPanel; + + private View mNoteBgColorSelector; + + private View mFontSizeSelector; + + private EditText mNoteEditor; + + private View mNoteEditorPanel; + + private WorkingNote mWorkingNote; + + private SharedPreferences mSharedPrefs; + private int mFontSizeId; + + private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; + + private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; + + public static final String TAG_CHECKED = String.valueOf('\u221A'); + public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); + + private LinearLayout mEditTextList; + + private String mUserQuery; + private Pattern mPattern; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.setContentView(R.layout.note_edit); + + if (savedInstanceState == null && !initActivityState(getIntent())) { + finish(); + return; + } + initResources(); + } + + /** + * Current activity may be killed when the memory is low. Once it is killed, for another time + * user load this activity, we should restore the former state + */ + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); + if (!initActivityState(intent)) { + finish(); + return; + } + Log.d(TAG, "Restoring from killed activity"); + } + } + + private boolean initActivityState(Intent intent) { + /** + * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, + * then jump to the NotesListActivity + */ + mWorkingNote = null; + if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { + long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); + mUserQuery = ""; + + /** + * Starting from the searched result + */ + if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { + noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); + mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); + } + + if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { + Intent jump = new Intent(this, NotesListActivity.class); + startActivity(jump); + showToast(R.string.error_note_not_exist); + finish(); + return false; + } else { + mWorkingNote = WorkingNote.load(this, noteId); + if (mWorkingNote == null) { + Log.e(TAG, "load note failed with note id" + noteId); + finish(); + return false; + } + } + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN + | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { + // New note + long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); + int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, + Notes.TYPE_WIDGET_INVALIDE); + int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, + ResourceParser.getDefaultBgId(this)); + + // Parse call-record note + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); + if (callDate != 0 && phoneNumber != null) { + if (TextUtils.isEmpty(phoneNumber)) { + Log.w(TAG, "The call record number is null"); + } + long noteId = 0; + if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), + phoneNumber, callDate)) > 0) { + mWorkingNote = WorkingNote.load(this, noteId); + if (mWorkingNote == null) { + Log.e(TAG, "load call note failed with note id" + noteId); + finish(); + return false; + } + } else { + mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, + widgetType, bgResId); + mWorkingNote.convertToCallNote(phoneNumber, callDate); + } + } else { + mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, + bgResId); + } + + getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + } else { + Log.e(TAG, "Intent not specified action, should not support"); + finish(); + return false; + } + mWorkingNote.setOnSettingStatusChangedListener(this); + return true; + } + + @Override + protected void onResume() { + super.onResume(); + initNoteScreen(); + } + + private void initNoteScreen() { + mNoteEditor.setTextAppearance(this, TextAppearanceResources + .getTexAppearanceResource(mFontSizeId)); + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + switchToListMode(mWorkingNote.getContent()); + } else { + mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); + mNoteEditor.setSelection(mNoteEditor.getText().length()); + } + for (Integer id : sBgSelectorSelectionMap.keySet()) { + findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); + } + mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + + mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, + mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR)); + + /** + * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker + * is not ready + */ + showAlertHeader(); + } + + private void showAlertHeader() { + if (mWorkingNote.hasClockAlert()) { + long time = System.currentTimeMillis(); + if (time > mWorkingNote.getAlertDate()) { + mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); + } else { + mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( + mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); + } + mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); + mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); + } else { + mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); + mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); + }; + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + initActivityState(intent); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + /** + * For new note without note id, we should firstly save it to + * generate a id. If the editing note is not worth saving, there + * is no id which is equivalent to create new note + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + } + outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); + Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (mNoteBgColorSelector.getVisibility() == View.VISIBLE + && !inRangeOfView(mNoteBgColorSelector, ev)) { + mNoteBgColorSelector.setVisibility(View.GONE); + return true; + } + + if (mFontSizeSelector.getVisibility() == View.VISIBLE + && !inRangeOfView(mFontSizeSelector, ev)) { + mFontSizeSelector.setVisibility(View.GONE); + return true; + } + return super.dispatchTouchEvent(ev); + } + + private boolean inRangeOfView(View view, MotionEvent ev) { + int []location = new int[2]; + view.getLocationOnScreen(location); + int x = location[0]; + int y = location[1]; + if (ev.getX() < x + || ev.getX() > (x + view.getWidth()) + || ev.getY() < y + || ev.getY() > (y + view.getHeight())) { + return false; + } + return true; + } + + private void initResources() { + mHeadViewPanel = findViewById(R.id.note_title); + mNoteHeaderHolder = new HeadViewHolder(); + mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); + mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon); + mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); + mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); + mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); + mNoteEditor = (EditText) findViewById(R.id.note_edit_view); + mNoteEditorPanel = findViewById(R.id.sv_note_edit); + mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); + for (int id : sBgSelectorBtnsMap.keySet()) { + ImageView iv = (ImageView) findViewById(id); + iv.setOnClickListener(this); + } + + mFontSizeSelector = findViewById(R.id.font_size_selector); + for (int id : sFontSizeBtnsMap.keySet()) { + View view = findViewById(id); + view.setOnClickListener(this); + }; + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { + mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; + } + mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); + } + + @Override + protected void onPause() { + super.onPause(); + if(saveNote()) { + Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); + } + clearSettingState(); + } + + private void updateWidget() { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + mWorkingNote.getWidgetId() + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_set_bg_color) { + mNoteBgColorSelector.setVisibility(View.VISIBLE); + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + - View.VISIBLE); + } else if (sBgSelectorBtnsMap.containsKey(id)) { + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + View.GONE); + mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); + mNoteBgColorSelector.setVisibility(View.GONE); + } else if (sFontSizeBtnsMap.containsKey(id)) { + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); + mFontSizeId = sFontSizeBtnsMap.get(id); + mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + getWorkingText(); + switchToListMode(mWorkingNote.getContent()); + } else { + mNoteEditor.setTextAppearance(this, + TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + } + mFontSizeSelector.setVisibility(View.GONE); + } + } + + @Override + public void onBackPressed() { + if(clearSettingState()) { + return; + } + + saveNote(); + super.onBackPressed(); + } + + private boolean clearSettingState() { + if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { + mNoteBgColorSelector.setVisibility(View.GONE); + return true; + } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { + mFontSizeSelector.setVisibility(View.GONE); + return true; + } + return false; + } + + public void onBackgroundColorChanged() { + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( + View.VISIBLE); + mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (isFinishing()) { + return true; + } + clearSettingState(); + menu.clear(); + if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_note_edit, menu); + } else { + getMenuInflater().inflate(R.menu.note_edit, menu); + } + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); + } else { + menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); + } + if (mWorkingNote.hasClockAlert()) { + menu.findItem(R.id.menu_alert).setVisible(false); + } else { + menu.findItem(R.id.menu_delete_remind).setVisible(false); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_new_note: + createNewNote(); + break; + case R.id.menu_delete: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_note)); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + deleteCurrentNote(); + finish(); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + break; + case R.id.menu_font_size: + mFontSizeSelector.setVisibility(View.VISIBLE); + findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); + break; + case R.id.menu_list_mode: + mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? + TextNote.MODE_CHECK_LIST : 0); + break; + case R.id.menu_share: + getWorkingText(); + sendTo(this, mWorkingNote.getContent()); + break; + case R.id.menu_send_to_desktop: + sendToDesktop(); + break; + case R.id.menu_alert: + setReminder(); + break; + case R.id.menu_delete_remind: + mWorkingNote.setAlertDate(0, false); + break; + default: + break; + } + return true; + } + + private void setReminder() { + DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); + d.setOnDateTimeSetListener(new OnDateTimeSetListener() { + public void OnDateTimeSet(AlertDialog dialog, long date) { + mWorkingNote.setAlertDate(date , true); + } + }); + d.show(); + } + + /** + * Share note to apps that support {@link Intent#ACTION_SEND} action + * and {@text/plain} type + */ + private void sendTo(Context context, String info) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, info); + intent.setType("text/plain"); + context.startActivity(intent); + } + + private void createNewNote() { + // Firstly, save current editing notes + saveNote(); + + // For safety, start a new NoteEditActivity + finish(); + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); + startActivity(intent); + } + + private void deleteCurrentNote() { + if (mWorkingNote.existInDatabase()) { + HashSet ids = new HashSet(); + long id = mWorkingNote.getNoteId(); + if (id != Notes.ID_ROOT_FOLDER) { + ids.add(id); + } else { + Log.d(TAG, "Wrong note id, should not happen"); + } + if (!isSyncMode()) { + if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { + Log.e(TAG, "Delete Note error"); + } + } else { + if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + } + mWorkingNote.markDeleted(true); + } + + private boolean isSyncMode() { + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + } + + public void onClockAlertChanged(long date, boolean set) { + /** + * User could set clock to an unsaved note, so before setting the + * alert clock, we should save the note first + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + } + if (mWorkingNote.getNoteId() > 0) { + Intent intent = new Intent(this, AlarmReceiver.class); + intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); + PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); + AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); + showAlertHeader(); + if(!set) { + alarmManager.cancel(pendingIntent); + } else { + alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); + } + } else { + /** + * There is the condition that user has input nothing (the note is + * not worthy saving), we have no note id, remind the user that he + * should input something + */ + Log.e(TAG, "Clock alert setting error"); + showToast(R.string.error_note_empty_for_clock); + } + } + + public void onWidgetChanged() { + updateWidget(); + } + + public void onEditTextDelete(int index, String text) { + int childCount = mEditTextList.getChildCount(); + if (childCount == 1) { + return; + } + + for (int i = index + 1; i < childCount; i++) { + ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) + .setIndex(i - 1); + } + + mEditTextList.removeViewAt(index); + NoteEditText edit = null; + if(index == 0) { + edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( + R.id.et_edit_text); + } else { + edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( + R.id.et_edit_text); + } + int length = edit.length(); + edit.append(text); + edit.requestFocus(); + edit.setSelection(length); + } + + public void onEditTextEnter(int index, String text) { + /** + * Should not happen, check for debug + */ + if(index > mEditTextList.getChildCount()) { + Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); + } + + View view = getListItem(text, index); + mEditTextList.addView(view, index); + NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + edit.requestFocus(); + edit.setSelection(0); + for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { + ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) + .setIndex(i); + } + } + + private void switchToListMode(String text) { + mEditTextList.removeAllViews(); + String[] items = text.split("\n"); + int index = 0; + for (String item : items) { + if(!TextUtils.isEmpty(item)) { + mEditTextList.addView(getListItem(item, index)); + index++; + } + } + mEditTextList.addView(getListItem("", index)); + mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); + + mNoteEditor.setVisibility(View.GONE); + mEditTextList.setVisibility(View.VISIBLE); + } + + private Spannable getHighlightQueryResult(String fullText, String userQuery) { + SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); + if (!TextUtils.isEmpty(userQuery)) { + mPattern = Pattern.compile(userQuery); + Matcher m = mPattern.matcher(fullText); + int start = 0; + while (m.find(start)) { + spannable.setSpan( + new BackgroundColorSpan(this.getResources().getColor( + R.color.user_query_highlight)), m.start(), m.end(), + Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + start = m.end(); + } + } + return spannable; + } + + private View getListItem(String item, int index) { + View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); + final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); + cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } else { + edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); + } + } + }); + + if (item.startsWith(TAG_CHECKED)) { + cb.setChecked(true); + edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + item = item.substring(TAG_CHECKED.length(), item.length()).trim(); + } else if (item.startsWith(TAG_UNCHECKED)) { + cb.setChecked(false); + edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); + item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); + } + + edit.setOnTextViewChangeListener(this); + edit.setIndex(index); + edit.setText(getHighlightQueryResult(item, mUserQuery)); + return view; + } + + public void onTextChange(int index, boolean hasText) { + if (index >= mEditTextList.getChildCount()) { + Log.e(TAG, "Wrong index, should not happen"); + return; + } + if(hasText) { + mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); + } else { + mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); + } + } + + public void onCheckListModeChanged(int oldMode, int newMode) { + if (newMode == TextNote.MODE_CHECK_LIST) { + switchToListMode(mNoteEditor.getText().toString()); + } else { + if (!getWorkingText()) { + mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", + "")); + } + mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); + mEditTextList.setVisibility(View.GONE); + mNoteEditor.setVisibility(View.VISIBLE); + } + } + + private boolean getWorkingText() { + boolean hasChecked = false; + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mEditTextList.getChildCount(); i++) { + View view = mEditTextList.getChildAt(i); + NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + if (!TextUtils.isEmpty(edit.getText())) { + if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { + sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); + hasChecked = true; + } else { + sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); + } + } + } + mWorkingNote.setWorkingText(sb.toString()); + } else { + mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); + } + return hasChecked; + } + + private boolean saveNote() { + getWorkingText(); + boolean saved = mWorkingNote.saveNote(); + if (saved) { + /** + * There are two modes from List view to edit view, open one note, + * create/edit a node. Opening node requires to the original + * position in the list when back from edit view, while creating a + * new node requires to the top of the list. This code + * {@link #RESULT_OK} is used to identify the create/edit state + */ + setResult(RESULT_OK); + } + return saved; + } + + private void sendToDesktop() { + /** + * Before send message to home, we should make sure that current + * editing note is exists in databases. So, for new note, firstly + * save it + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + } + + if (mWorkingNote.getNoteId() > 0) { + Intent sender = new Intent(); + Intent shortcutIntent = new Intent(this, NoteEditActivity.class); + shortcutIntent.setAction(Intent.ACTION_VIEW); + shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); + sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); + sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, + makeShortcutIconTitle(mWorkingNote.getContent())); + sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, + Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); + sender.putExtra("duplicate", true); + sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); + showToast(R.string.info_note_enter_desktop); + sendBroadcast(sender); + } else { + /** + * There is the condition that user has input nothing (the note is + * not worthy saving), we have no note id, remind the user that he + * should input something + */ + Log.e(TAG, "Send to desktop error"); + showToast(R.string.error_note_empty_for_send_to_desktop); + } + } + + private String makeShortcutIconTitle(String content) { + content = content.replace(TAG_CHECKED, ""); + content = content.replace(TAG_UNCHECKED, ""); + return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, + SHORTCUT_ICON_TITLE_MAX_LEN) : content; + } + + private void showToast(int resId) { + showToast(resId, Toast.LENGTH_SHORT); + } + + private void showToast(int resId, int duration) { + Toast.makeText(this, resId, duration).show(); + } +} diff --git a/src/ui/NoteEditText.java b/src/ui/NoteEditText.java new file mode 100644 index 0000000..2afe2a8 --- /dev/null +++ b/src/ui/NoteEditText.java @@ -0,0 +1,217 @@ +/* + * 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.content.Context; +import android.graphics.Rect; +import android.text.Layout; +import android.text.Selection; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.URLSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.widget.EditText; + +import net.micode.notes.R; + +import java.util.HashMap; +import java.util.Map; + +public class NoteEditText extends EditText { + private static final String TAG = "NoteEditText"; + private int mIndex; + private int mSelectionStartBeforeDelete; + + private static final String SCHEME_TEL = "tel:" ; + private static final String SCHEME_HTTP = "http:" ; + private static final String SCHEME_EMAIL = "mailto:" ; + + private static final Map sSchemaActionResMap = new HashMap(); + static { + sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); + sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); + sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); + } + + /** + * Call by the {@link NoteEditActivity} to delete or add edit text + */ + public interface OnTextViewChangeListener { + /** + * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens + * and the text is null + */ + void onEditTextDelete(int index, String text); + + /** + * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} + * happen + */ + void onEditTextEnter(int index, String text); + + /** + * Hide or show item option when text change + */ + void onTextChange(int index, boolean hasText); + } + + private OnTextViewChangeListener mOnTextViewChangeListener; + + public NoteEditText(Context context) { + super(context, null); + mIndex = 0; + } + + public void setIndex(int index) { + mIndex = index; + } + + public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { + mOnTextViewChangeListener = listener; + } + + public NoteEditText(Context context, AttributeSet attrs) { + super(context, attrs, android.R.attr.editTextStyle); + } + + public NoteEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // TODO Auto-generated constructor stub + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + + int x = (int) event.getX(); + int y = (int) event.getY(); + x -= getTotalPaddingLeft(); + y -= getTotalPaddingTop(); + x += getScrollX(); + y += getScrollY(); + + Layout layout = getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + Selection.setSelection(getText(), off); + break; + } + + return super.onTouchEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + return false; + } + break; + case KeyEvent.KEYCODE_DEL: + mSelectionStartBeforeDelete = getSelectionStart(); + break; + default: + break; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch(keyCode) { + case KeyEvent.KEYCODE_DEL: + if (mOnTextViewChangeListener != null) { + if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); + return true; + } + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + case KeyEvent.KEYCODE_ENTER: + if (mOnTextViewChangeListener != null) { + int selectionStart = getSelectionStart(); + String text = getText().subSequence(selectionStart, length()).toString(); + setText(getText().subSequence(0, selectionStart)); + mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); + } else { + Log.d(TAG, "OnTextViewChangeListener was not seted"); + } + break; + default: + break; + } + return super.onKeyUp(keyCode, event); + } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + if (mOnTextViewChangeListener != null) { + if (!focused && TextUtils.isEmpty(getText())) { + mOnTextViewChangeListener.onTextChange(mIndex, false); + } else { + mOnTextViewChangeListener.onTextChange(mIndex, true); + } + } + super.onFocusChanged(focused, direction, previouslyFocusedRect); + } + + @Override + protected void onCreateContextMenu(ContextMenu menu) { + if (getText() instanceof Spanned) { + int selStart = getSelectionStart(); + int selEnd = getSelectionEnd(); + + int min = Math.min(selStart, selEnd); + int max = Math.max(selStart, selEnd); + + final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); + if (urls.length == 1) { + int defaultResId = 0; + for(String schema: sSchemaActionResMap.keySet()) { + if(urls[0].getURL().indexOf(schema) >= 0) { + defaultResId = sSchemaActionResMap.get(schema); + break; + } + } + + if (defaultResId == 0) { + defaultResId = R.string.note_link_other; + } + + menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( + new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // goto a new intent + urls[0].onClick(NoteEditText.this); + return true; + } + }); + } + } + super.onCreateContextMenu(menu); + } +} diff --git a/src/ui/NoteItemData.java b/src/ui/NoteItemData.java new file mode 100644 index 0000000..0f5a878 --- /dev/null +++ b/src/ui/NoteItemData.java @@ -0,0 +1,224 @@ +/* + * 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.content.Context; +import android.database.Cursor; +import android.text.TextUtils; + +import net.micode.notes.data.Contact; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.DataUtils; + + +public class NoteItemData { + static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.CREATED_DATE, + NoteColumns.HAS_ATTACHMENT, + NoteColumns.MODIFIED_DATE, + NoteColumns.NOTES_COUNT, + NoteColumns.PARENT_ID, + NoteColumns.SNIPPET, + NoteColumns.TYPE, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + }; + + private static final int ID_COLUMN = 0; + private static final int ALERTED_DATE_COLUMN = 1; + private static final int BG_COLOR_ID_COLUMN = 2; + private static final int CREATED_DATE_COLUMN = 3; + private static final int HAS_ATTACHMENT_COLUMN = 4; + private static final int MODIFIED_DATE_COLUMN = 5; + private static final int NOTES_COUNT_COLUMN = 6; + private static final int PARENT_ID_COLUMN = 7; + private static final int SNIPPET_COLUMN = 8; + private static final int TYPE_COLUMN = 9; + private static final int WIDGET_ID_COLUMN = 10; + private static final int WIDGET_TYPE_COLUMN = 11; + + private long mId; + private long mAlertDate; + private int mBgColorId; + private long mCreatedDate; + private boolean mHasAttachment; + private long mModifiedDate; + private int mNotesCount; + private long mParentId; + private String mSnippet; + private int mType; + private int mWidgetId; + private int mWidgetType; + private String mName; + private String mPhoneNumber; + + private boolean mIsLastItem; + private boolean mIsFirstItem; + private boolean mIsOnlyOneItem; + private boolean mIsOneNoteFollowingFolder; + private boolean mIsMultiNotesFollowingFolder; + + public NoteItemData(Context context, Cursor cursor) { + mId = cursor.getLong(ID_COLUMN); + mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); + mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); + mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); + mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; + mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); + mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); + mParentId = cursor.getLong(PARENT_ID_COLUMN); + mSnippet = cursor.getString(SNIPPET_COLUMN); + mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( + NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); + mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); + + mPhoneNumber = ""; + if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); + if (!TextUtils.isEmpty(mPhoneNumber)) { + mName = Contact.getContact(context, mPhoneNumber); + if (mName == null) { + mName = mPhoneNumber; + } + } + } + + if (mName == null) { + mName = ""; + } + checkPostion(cursor); + } + + private void checkPostion(Cursor cursor) { + mIsLastItem = cursor.isLast() ? true : false; + mIsFirstItem = cursor.isFirst() ? true : false; + mIsOnlyOneItem = (cursor.getCount() == 1); + mIsMultiNotesFollowingFolder = false; + mIsOneNoteFollowingFolder = false; + + if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { + int position = cursor.getPosition(); + if (cursor.moveToPrevious()) { + if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER + || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { + if (cursor.getCount() > (position + 1)) { + mIsMultiNotesFollowingFolder = true; + } else { + mIsOneNoteFollowingFolder = true; + } + } + if (!cursor.moveToNext()) { + throw new IllegalStateException("cursor move to previous but can't move back"); + } + } + } + } + + public boolean isOneFollowingFolder() { + return mIsOneNoteFollowingFolder; + } + + public boolean isMultiFollowingFolder() { + return mIsMultiNotesFollowingFolder; + } + + public boolean isLast() { + return mIsLastItem; + } + + public String getCallName() { + return mName; + } + + public boolean isFirst() { + return mIsFirstItem; + } + + public boolean isSingle() { + return mIsOnlyOneItem; + } + + public long getId() { + return mId; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getCreatedDate() { + return mCreatedDate; + } + + public boolean hasAttachment() { + return mHasAttachment; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorId() { + return mBgColorId; + } + + public long getParentId() { + return mParentId; + } + + public int getNotesCount() { + return mNotesCount; + } + + public long getFolderId () { + return mParentId; + } + + public int getType() { + return mType; + } + + public int getWidgetType() { + return mWidgetType; + } + + public int getWidgetId() { + return mWidgetId; + } + + public String getSnippet() { + return mSnippet; + } + + public boolean hasAlert() { + return (mAlertDate > 0); + } + + public boolean isCallRecord() { + return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); + } + + public static int getNoteType(Cursor cursor) { + return cursor.getInt(TYPE_COLUMN); + } +} diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java new file mode 100644 index 0000000..e843aec --- /dev/null +++ b/src/ui/NotesListActivity.java @@ -0,0 +1,954 @@ +/* + * 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.app.Dialog; +import android.appwidget.AppWidgetManager; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Display; +import android.view.HapticFeedbackConstants; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; +import android.view.View.OnTouchListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; +import net.micode.notes.model.WorkingNote; +import net.micode.notes.tool.BackupUtils; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser; +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; +import net.micode.notes.widget.NoteWidgetProvider_2x; +import net.micode.notes.widget.NoteWidgetProvider_4x; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashSet; + +public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + + private static final int FOLDER_LIST_QUERY_TOKEN = 1; + + private static final int MENU_FOLDER_DELETE = 0; + + private static final int MENU_FOLDER_VIEW = 1; + + private static final int MENU_FOLDER_CHANGE_NAME = 2; + + private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; + + private enum ListEditState { + NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER + }; + + private ListEditState mState; + + private BackgroundQueryHandler mBackgroundQueryHandler; + + private NotesListAdapter mNotesListAdapter; + + private ListView mNotesListView; + + private Button mAddNewNote; + + private boolean mDispatch; + + private int mOriginY; + + private int mDispatchY; + + private TextView mTitleBar; + + private long mCurrentFolderId; + + private ContentResolver mContentResolver; + + private ModeCallback mModeCallBack; + + private static final String TAG = "NotesListActivity"; + + public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; + + private NoteItemData mFocusNoteDataItem; + + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; + + private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + + NoteColumns.NOTES_COUNT + ">0)"; + + private final static int REQUEST_CODE_OPEN_NODE = 102; + private final static int REQUEST_CODE_NEW_NODE = 103; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.note_list); + initResources(); + + /** + * Insert an introduction when user firstly use this application + */ + setAppInfoFromRawRes(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK + && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { + mNotesListAdapter.changeCursor(null); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + private void setAppInfoFromRawRes() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { + StringBuilder sb = new StringBuilder(); + InputStream in = null; + try { + in = getResources().openRawResource(R.raw.introduction); + if (in != null) { + InputStreamReader isr = new InputStreamReader(in); + BufferedReader br = new BufferedReader(isr); + char [] buf = new char[1024]; + int len = 0; + while ((len = br.read(buf)) > 0) { + sb.append(buf, 0, len); + } + } else { + Log.e(TAG, "Read introduction file error"); + return; + } + } catch (IOException e) { + e.printStackTrace(); + return; + } finally { + if(in != null) { + try { + in.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, + AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, + ResourceParser.RED); + note.setWorkingText(sb.toString()); + if (note.saveNote()) { + sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); + } else { + Log.e(TAG, "Save introduction note error"); + return; + } + } + } + + @Override + protected void onStart() { + super.onStart(); + startAsyncNotesListQuery(); + } + + private void initResources() { + mContentResolver = this.getContentResolver(); + mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mNotesListView = (ListView) findViewById(R.id.notes_list); + mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), + null, false); + mNotesListView.setOnItemClickListener(new OnListItemClickListener()); + mNotesListView.setOnItemLongClickListener(this); + mNotesListAdapter = new NotesListAdapter(this); + mNotesListView.setAdapter(mNotesListAdapter); + mAddNewNote = (Button) findViewById(R.id.btn_new_note); + mAddNewNote.setOnClickListener(this); + mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + mDispatch = false; + mDispatchY = 0; + mOriginY = 0; + mTitleBar = (TextView) findViewById(R.id.tv_title_bar); + mState = ListEditState.NOTE_LIST; + mModeCallBack = new ModeCallback(); + } + + private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { + private DropdownMenu mDropDownMenu; + private ActionMode mActionMode; + private MenuItem mMoveMenu; + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + getMenuInflater().inflate(R.menu.note_list_options, menu); + menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + mMoveMenu = menu.findItem(R.id.move); + if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER + || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this); + } + mActionMode = mode; + mNotesListAdapter.setChoiceMode(true); + mNotesListView.setLongClickable(false); + mAddNewNote.setVisibility(View.GONE); + + View customView = LayoutInflater.from(NotesListActivity.this).inflate( + R.layout.note_list_dropdown_menu, null); + mode.setCustomView(customView); + mDropDownMenu = new DropdownMenu(NotesListActivity.this, + (Button) customView.findViewById(R.id.selection_menu), + R.menu.note_list_dropdown); + mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ + public boolean onMenuItemClick(MenuItem item) { + mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); + updateMenu(); + return true; + } + + }); + return true; + } + + private void updateMenu() { + int selectedCount = mNotesListAdapter.getSelectedCount(); + // Update dropdown menu + String format = getResources().getString(R.string.menu_select_title, selectedCount); + mDropDownMenu.setTitle(format); + MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); + if (item != null) { + if (mNotesListAdapter.isAllSelected()) { + item.setChecked(true); + item.setTitle(R.string.menu_deselect_all); + } else { + item.setChecked(false); + item.setTitle(R.string.menu_select_all); + } + } + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // TODO Auto-generated method stub + return false; + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + // TODO Auto-generated method stub + return false; + } + + public void onDestroyActionMode(ActionMode mode) { + mNotesListAdapter.setChoiceMode(false); + mNotesListView.setLongClickable(true); + mAddNewNote.setVisibility(View.VISIBLE); + } + + public void finishActionMode() { + mActionMode.finish(); + } + + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + mNotesListAdapter.setCheckedItem(position, checked); + updateMenu(); + } + + public boolean onMenuItemClick(MenuItem item) { + if (mNotesListAdapter.getSelectedCount() == 0) { + Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), + Toast.LENGTH_SHORT).show(); + return true; + } + + switch (item.getItemId()) { + case R.id.delete: + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_notes, + mNotesListAdapter.getSelectedCount())); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int which) { + batchDelete(); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + break; + case R.id.move: + startQueryDestinationFolders(); + break; + default: + return false; + } + return true; + } + } + + private class NewNoteOnTouchListener implements OnTouchListener { + + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + Display display = getWindowManager().getDefaultDisplay(); + int screenHeight = display.getHeight(); + int newNoteViewHeight = mAddNewNote.getHeight(); + int start = screenHeight - newNoteViewHeight; + int eventY = start + (int) event.getY(); + /** + * Minus TitleBar's height + */ + if (mState == ListEditState.SUB_FOLDER) { + eventY -= mTitleBar.getHeight(); + start -= mTitleBar.getHeight(); + } + /** + * HACKME:When click the transparent part of "New Note" button, dispatch + * the event to the list view behind this button. The transparent part of + * "New Note" button could be expressed by formula y=-0.12x+94(Unit:pixel) + * and the line top of the button. The coordinate based on left of the "New + * Note" button. The 94 represents maximum height of the transparent part. + * Notice that, if the background of the button changes, the formula should + * also change. This is very bad, just for the UI designer's strong requirement. + */ + if (event.getY() < (event.getX() * (-0.12) + 94)) { + View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 + - mNotesListView.getFooterViewsCount()); + if (view != null && view.getBottom() > start + && (view.getTop() < (start + 94))) { + mOriginY = (int) event.getY(); + mDispatchY = eventY; + event.setLocation(event.getX(), mDispatchY); + mDispatch = true; + return mNotesListView.dispatchTouchEvent(event); + } + } + break; + } + case MotionEvent.ACTION_MOVE: { + if (mDispatch) { + mDispatchY += (int) event.getY() - mOriginY; + event.setLocation(event.getX(), mDispatchY); + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + default: { + if (mDispatch) { + event.setLocation(event.getX(), mDispatchY); + mDispatch = false; + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + } + return false; + } + + }; + + private void startAsyncNotesListQuery() { + String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION + : NORMAL_SELECTION; + mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { + String.valueOf(mCurrentFolderId) + }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); + } + + private final class BackgroundQueryHandler extends AsyncQueryHandler { + public BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + switch (token) { + case FOLDER_NOTE_LIST_QUERY_TOKEN: + mNotesListAdapter.changeCursor(cursor); + break; + case FOLDER_LIST_QUERY_TOKEN: + if (cursor != null && cursor.getCount() > 0) { + showFolderListMenu(cursor); + } else { + Log.e(TAG, "Query folder failed"); + } + break; + default: + return; + } + } + } + + private void showFolderListMenu(Cursor cursor) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(R.string.menu_title_select_folder); + final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + DataUtils.batchMoveToFolder(mContentResolver, + mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); + Toast.makeText( + NotesListActivity.this, + getString(R.string.format_move_notes_to_folder, + mNotesListAdapter.getSelectedCount(), + adapter.getFolderName(NotesListActivity.this, which)), + Toast.LENGTH_SHORT).show(); + mModeCallBack.finishActionMode(); + } + }); + builder.show(); + } + + private void createNewNote() { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); + this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); + } + + private void batchDelete() { + new AsyncTask>() { + protected HashSet doInBackground(Void... unused) { + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + if (!isSyncMode()) { + // if not synced, delete notes directly + if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter + .getSelectedItemIds())) { + } else { + Log.e(TAG, "Delete notes error, should not happens"); + } + } else { + // in sync mode, we'll move the deleted note into the trash + // folder + if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter + .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + Log.e(TAG, "Move notes to trash folder error, should not happens"); + } + } + return widgets; + } + + @Override + protected void onPostExecute(HashSet widgets) { + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + mModeCallBack.finishActionMode(); + } + }.execute(); + } + + private void deleteFolder(long folderId) { + if (folderId == Notes.ID_ROOT_FOLDER) { + Log.e(TAG, "Wrong folder id, should not happen " + folderId); + return; + } + + HashSet ids = new HashSet(); + ids.add(folderId); + HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, + folderId); + if (!isSyncMode()) { + // if not synced, delete folder directly + DataUtils.batchDeleteNotes(mContentResolver, ids); + } else { + // in sync mode, we'll move the deleted folder into the trash folder + DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); + } + if (widgets != null) { + for (AppWidgetAttribute widget : widgets) { + if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + updateWidget(widget.widgetId, widget.widgetType); + } + } + } + } + + private void openNode(NoteItemData data) { + Intent intent = new Intent(this, NoteEditActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(Intent.EXTRA_UID, data.getId()); + this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); + } + + private void openFolder(NoteItemData data) { + mCurrentFolderId = data.getId(); + startAsyncNotesListQuery(); + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mState = ListEditState.CALL_RECORD_FOLDER; + mAddNewNote.setVisibility(View.GONE); + } else { + mState = ListEditState.SUB_FOLDER; + } + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mTitleBar.setText(R.string.call_record_folder_name); + } else { + mTitleBar.setText(data.getSnippet()); + } + mTitleBar.setVisibility(View.VISIBLE); + } + + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_new_note: + createNewNote(); + break; + default: + break; + } + } + + private void showSoftInput() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } + + private void hideSoftInput(View view) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + private void showCreateOrModifyFolderDialog(final boolean create) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); + final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); + showSoftInput(); + if (!create) { + if (mFocusNoteDataItem != null) { + etName.setText(mFocusNoteDataItem.getSnippet()); + builder.setTitle(getString(R.string.menu_folder_change_name)); + } else { + Log.e(TAG, "The long click data item is null"); + return; + } + } else { + etName.setText(""); + builder.setTitle(this.getString(R.string.menu_create_folder)); + } + + builder.setPositiveButton(android.R.string.ok, null); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + hideSoftInput(etName); + } + }); + + final Dialog dialog = builder.setView(view).show(); + final Button positive = (Button)dialog.findViewById(android.R.id.button1); + positive.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + hideSoftInput(etName); + String name = etName.getText().toString(); + if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), + Toast.LENGTH_LONG).show(); + etName.setSelection(0, etName.length()); + return; + } + if (!create) { + if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + + "=?", new String[] { + String.valueOf(mFocusNoteDataItem.getId()) + }); + } + } else if (!TextUtils.isEmpty(name)) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, name); + values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + } + dialog.dismiss(); + } + }); + + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } + /** + * When the name edit text is null, disable the positive button + */ + etName.addTextChangedListener(new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // TODO Auto-generated method stub + + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); + } else { + positive.setEnabled(true); + } + } + + public void afterTextChanged(Editable s) { + // TODO Auto-generated method stub + + } + }); + } + + @Override + public void onBackPressed() { + switch (mState) { + case SUB_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + break; + case CALL_RECORD_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mAddNewNote.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.GONE); + startAsyncNotesListQuery(); + break; + case NOTE_LIST: + super.onBackPressed(); + break; + default: + break; + } + } + + private void updateWidget(int appWidgetId, int appWidgetType) { + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + if (appWidgetType == Notes.TYPE_WIDGET_2X) { + intent.setClass(this, NoteWidgetProvider_2x.class); + } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + intent.setClass(this, NoteWidgetProvider_4x.class); + } else { + Log.e(TAG, "Unspported widget type"); + return; + } + + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + appWidgetId + }); + + sendBroadcast(intent); + setResult(RESULT_OK, intent); + } + + private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + if (mFocusNoteDataItem != null) { + menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); + menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); + menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); + } + } + }; + + @Override + public void onContextMenuClosed(Menu menu) { + if (mNotesListView != null) { + mNotesListView.setOnCreateContextMenuListener(null); + } + super.onContextMenuClosed(menu); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (mFocusNoteDataItem == null) { + Log.e(TAG, "The long click data item is null"); + return false; + } + switch (item.getItemId()) { + case MENU_FOLDER_VIEW: + openFolder(mFocusNoteDataItem); + break; + case MENU_FOLDER_DELETE: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.alert_title_delete)); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setMessage(getString(R.string.alert_message_delete_folder)); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + deleteFolder(mFocusNoteDataItem.getId()); + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + break; + case MENU_FOLDER_CHANGE_NAME: + showCreateOrModifyFolderDialog(false); + break; + default: + break; + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + if (mState == ListEditState.NOTE_LIST) { + getMenuInflater().inflate(R.menu.note_list, menu); + // set sync or sync_cancel + menu.findItem(R.id.menu_sync).setTitle( + GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); + } else if (mState == ListEditState.SUB_FOLDER) { + getMenuInflater().inflate(R.menu.sub_folder, menu); + } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + getMenuInflater().inflate(R.menu.call_record_folder, menu); + } else { + Log.e(TAG, "Wrong state:" + mState); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_new_folder: { + showCreateOrModifyFolderDialog(true); + break; + } + case R.id.menu_export_text: { + exportNoteToText(); + break; + } + case R.id.menu_sync: { + if (isSyncMode()) { + if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + GTaskSyncService.startSync(this); + } else { + GTaskSyncService.cancelSync(this); + } + } else { + startPreferenceActivity(); + } + break; + } + case R.id.menu_setting: { + startPreferenceActivity(); + break; + } + case R.id.menu_new_note: { + createNewNote(); + break; + } + case R.id.menu_search: + onSearchRequested(); + break; + default: + break; + } + return true; + } + + @Override + public boolean onSearchRequested() { + startSearch(null, false, null /* appData */, false); + return true; + } + + private void exportNoteToText() { + final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + new AsyncTask() { + + @Override + protected Integer doInBackground(Void... unused) { + return backup.exportToText(); + } + + @Override + protected void onPostExecute(Integer result) { + if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_unmounted)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SUCCESS) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.success_sdcard_export)); + builder.setMessage(NotesListActivity.this.getString( + R.string.format_exported_file_location, backup + .getExportedTextFileName(), backup.getExportedTextFileDir())); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + builder.setTitle(NotesListActivity.this + .getString(R.string.failed_sdcard_export)); + builder.setMessage(NotesListActivity.this + .getString(R.string.error_sdcard_export)); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } + } + + }.execute(); + } + + private boolean isSyncMode() { + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + } + + private void startPreferenceActivity() { + Activity from = getParent() != null ? getParent() : this; + Intent intent = new Intent(from, NotesPreferenceActivity.class); + from.startActivityIfNeeded(intent, -1); + } + + private class OnListItemClickListener implements OnItemClickListener { + + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + NoteItemData item = ((NotesListItem) view).getItemData(); + if (mNotesListAdapter.isInChoiceMode()) { + if (item.getType() == Notes.TYPE_NOTE) { + position = position - mNotesListView.getHeaderViewsCount(); + mModeCallBack.onItemCheckedStateChanged(null, position, id, + !mNotesListAdapter.isSelectedItem(position)); + } + return; + } + + switch (mState) { + case NOTE_LIST: + if (item.getType() == Notes.TYPE_FOLDER + || item.getType() == Notes.TYPE_SYSTEM) { + openFolder(item); + } else if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in NOTE_LIST"); + } + break; + case SUB_FOLDER: + case CALL_RECORD_FOLDER: + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Log.e(TAG, "Wrong note type in SUB_FOLDER"); + } + break; + default: + break; + } + } + } + + } + + private void startQueryDestinationFolders() { + String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + selection = (mState == ListEditState.NOTE_LIST) ? selection: + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + + mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, + null, + Notes.CONTENT_NOTE_URI, + FoldersListAdapter.PROJECTION, + selection, + new String[] { + String.valueOf(Notes.TYPE_FOLDER), + String.valueOf(Notes.ID_TRASH_FOLER), + String.valueOf(mCurrentFolderId) + }, + NoteColumns.MODIFIED_DATE + " DESC"); + } + + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + if (view instanceof NotesListItem) { + mFocusNoteDataItem = ((NotesListItem) view).getItemData(); + if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + if (mNotesListView.startActionMode(mModeCallBack) != null) { + mModeCallBack.onItemCheckedStateChanged(null, position, id, true); + mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } else { + Log.e(TAG, "startActionMode fails"); + } + } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); + } + } + return false; + } +} diff --git a/src/ui/NotesListAdapter.java b/src/ui/NotesListAdapter.java new file mode 100644 index 0000000..51c9cb9 --- /dev/null +++ b/src/ui/NotesListAdapter.java @@ -0,0 +1,184 @@ +/* + * 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.content.Context; +import android.database.Cursor; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; + +import net.micode.notes.data.Notes; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + + +public class NotesListAdapter extends CursorAdapter { + private static final String TAG = "NotesListAdapter"; + private Context mContext; + private HashMap mSelectedIndex; + private int mNotesCount; + private boolean mChoiceMode; + + public static class AppWidgetAttribute { + public int widgetId; + public int widgetType; + }; + + public NotesListAdapter(Context context) { + super(context, null); + mSelectedIndex = new HashMap(); + mContext = context; + mNotesCount = 0; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return new NotesListItem(context); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + if (view instanceof NotesListItem) { + NoteItemData itemData = new NoteItemData(context, cursor); + ((NotesListItem) view).bind(context, itemData, mChoiceMode, + isSelectedItem(cursor.getPosition())); + } + } + + public void setCheckedItem(final int position, final boolean checked) { + mSelectedIndex.put(position, checked); + notifyDataSetChanged(); + } + + public boolean isInChoiceMode() { + return mChoiceMode; + } + + public void setChoiceMode(boolean mode) { + mSelectedIndex.clear(); + mChoiceMode = mode; + } + + public void selectAll(boolean checked) { + Cursor cursor = getCursor(); + for (int i = 0; i < getCount(); i++) { + if (cursor.moveToPosition(i)) { + if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { + setCheckedItem(i, checked); + } + } + } + } + + public HashSet getSelectedItemIds() { + HashSet itemSet = new HashSet(); + for (Integer position : mSelectedIndex.keySet()) { + if (mSelectedIndex.get(position) == true) { + Long id = getItemId(position); + if (id == Notes.ID_ROOT_FOLDER) { + Log.d(TAG, "Wrong item id, should not happen"); + } else { + itemSet.add(id); + } + } + } + + return itemSet; + } + + public HashSet getSelectedWidget() { + HashSet itemSet = new HashSet(); + for (Integer position : mSelectedIndex.keySet()) { + if (mSelectedIndex.get(position) == true) { + Cursor c = (Cursor) getItem(position); + if (c != null) { + AppWidgetAttribute widget = new AppWidgetAttribute(); + NoteItemData item = new NoteItemData(mContext, c); + widget.widgetId = item.getWidgetId(); + widget.widgetType = item.getWidgetType(); + itemSet.add(widget); + /** + * Don't close cursor here, only the adapter could close it + */ + } else { + Log.e(TAG, "Invalid cursor"); + return null; + } + } + } + return itemSet; + } + + public int getSelectedCount() { + Collection values = mSelectedIndex.values(); + if (null == values) { + return 0; + } + Iterator iter = values.iterator(); + int count = 0; + while (iter.hasNext()) { + if (true == iter.next()) { + count++; + } + } + return count; + } + + public boolean isAllSelected() { + int checkedCount = getSelectedCount(); + return (checkedCount != 0 && checkedCount == mNotesCount); + } + + public boolean isSelectedItem(final int position) { + if (null == mSelectedIndex.get(position)) { + return false; + } + return mSelectedIndex.get(position); + } + + @Override + protected void onContentChanged() { + super.onContentChanged(); + calcNotesCount(); + } + + @Override + public void changeCursor(Cursor cursor) { + super.changeCursor(cursor); + calcNotesCount(); + } + + private void calcNotesCount() { + mNotesCount = 0; + for (int i = 0; i < getCount(); i++) { + Cursor c = (Cursor) getItem(i); + if (c != null) { + if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { + mNotesCount++; + } + } else { + Log.e(TAG, "Invalid cursor"); + return; + } + } + } +} diff --git a/src/ui/NotesListItem.java b/src/ui/NotesListItem.java new file mode 100644 index 0000000..1221e80 --- /dev/null +++ b/src/ui/NotesListItem.java @@ -0,0 +1,122 @@ +/* + * 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.content.Context; +import android.text.format.DateUtils; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.ResourceParser.NoteItemBgResources; + + +public class NotesListItem extends LinearLayout { + private ImageView mAlert; + private TextView mTitle; + private TextView mTime; + private TextView mCallName; + private NoteItemData mItemData; + private CheckBox mCheckBox; + + public NotesListItem(Context context) { + super(context); + inflate(context, R.layout.note_item, this); + mAlert = (ImageView) findViewById(R.id.iv_alert_icon); + mTitle = (TextView) findViewById(R.id.tv_title); + mTime = (TextView) findViewById(R.id.tv_time); + mCallName = (TextView) findViewById(R.id.tv_name); + mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); + } + + public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { + if (choiceMode && data.getType() == Notes.TYPE_NOTE) { + mCheckBox.setVisibility(View.VISIBLE); + mCheckBox.setChecked(checked); + } else { + mCheckBox.setVisibility(View.GONE); + } + + mItemData = data; + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.GONE); + mAlert.setVisibility(View.VISIBLE); + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + mTitle.setText(context.getString(R.string.call_record_folder_name) + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + mAlert.setImageResource(R.drawable.call_record); + } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + mCallName.setVisibility(View.VISIBLE); + mCallName.setText(data.getCallName()); + mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock); + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } else { + mCallName.setVisibility(View.GONE); + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + + if (data.getType() == Notes.TYPE_FOLDER) { + mTitle.setText(data.getSnippet() + + context.getString(R.string.format_folder_files_count, + data.getNotesCount())); + mAlert.setVisibility(View.GONE); + } else { + mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + if (data.hasAlert()) { + mAlert.setImageResource(R.drawable.clock); + mAlert.setVisibility(View.VISIBLE); + } else { + mAlert.setVisibility(View.GONE); + } + } + } + mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + + setBackground(data); + } + + private void setBackground(NoteItemData data) { + int id = data.getBgColorId(); + if (data.getType() == Notes.TYPE_NOTE) { + if (data.isSingle() || data.isOneFollowingFolder()) { + setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); + } else if (data.isLast()) { + setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); + } else if (data.isFirst() || data.isMultiFollowingFolder()) { + setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); + } else { + setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); + } + } else { + setBackgroundResource(NoteItemBgResources.getFolderBgRes()); + } + } + + public NoteItemData getItemData() { + return mItemData; + } +} diff --git a/src/ui/NotesPreferenceActivity.java b/src/ui/NotesPreferenceActivity.java new file mode 100644 index 0000000..07c5f7e --- /dev/null +++ b/src/ui/NotesPreferenceActivity.java @@ -0,0 +1,388 @@ +/* + * 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.accounts.Account; +import android.accounts.AccountManager; +import android.app.ActionBar; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.remote.GTaskSyncService; + + +public class NotesPreferenceActivity extends PreferenceActivity { + public static final String PREFERENCE_NAME = "notes_preferences"; + + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; + + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; + + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; + + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; + + private static final String AUTHORITIES_FILTER_KEY = "authorities"; + + private PreferenceCategory mAccountCategory; + + private GTaskReceiver mReceiver; + + private Account[] mOriAccounts; + + private boolean mHasAddedAccount; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + /* using the app icon for navigation */ + getActionBar().setDisplayHomeAsUpEnabled(true); + + addPreferencesFromResource(R.xml.preferences); + mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); + mReceiver = new GTaskReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); + registerReceiver(mReceiver, filter); + + mOriAccounts = null; + View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); + getListView().addHeaderView(header, null, true); + } + + @Override + protected void onResume() { + super.onResume(); + + // need to set sync account automatically if user has added a new + // account + if (mHasAddedAccount) { + Account[] accounts = getGoogleAccounts(); + if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + for (Account accountNew : accounts) { + boolean found = false; + for (Account accountOld : mOriAccounts) { + if (TextUtils.equals(accountOld.name, accountNew.name)) { + found = true; + break; + } + } + if (!found) { + setSyncAccount(accountNew.name); + break; + } + } + } + } + + refreshUI(); + } + + @Override + protected void onDestroy() { + if (mReceiver != null) { + unregisterReceiver(mReceiver); + } + super.onDestroy(); + } + + private void loadAccountPreference() { + mAccountCategory.removeAll(); + + Preference accountPref = new Preference(this); + final String defaultAccount = getSyncAccountName(this); + accountPref.setTitle(getString(R.string.preferences_account_title)); + accountPref.setSummary(getString(R.string.preferences_account_summary)); + accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + if (!GTaskSyncService.isSyncing()) { + if (TextUtils.isEmpty(defaultAccount)) { + // the first time to set account + showSelectAccountAlertDialog(); + } else { + // if the account has already been set, we need to promp + // user about the risk + showChangeAccountConfirmAlertDialog(); + } + } else { + Toast.makeText(NotesPreferenceActivity.this, + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + .show(); + } + return true; + } + }); + + mAccountCategory.addPreference(accountPref); + } + + private void loadSyncButton() { + Button syncButton = (Button) findViewById(R.id.preference_sync_button); + TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + + // set button state + if (GTaskSyncService.isSyncing()) { + syncButton.setText(getString(R.string.preferences_button_sync_cancel)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.cancelSync(NotesPreferenceActivity.this); + } + }); + } else { + syncButton.setText(getString(R.string.preferences_button_sync_immediately)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.startSync(NotesPreferenceActivity.this); + } + }); + } + syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); + + // set last sync time + if (GTaskSyncService.isSyncing()) { + lastSyncTimeView.setText(GTaskSyncService.getProgressString()); + lastSyncTimeView.setVisibility(View.VISIBLE); + } else { + long lastSyncTime = getLastSyncTime(this); + if (lastSyncTime != 0) { + lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, + DateFormat.format(getString(R.string.preferences_last_sync_time_format), + lastSyncTime))); + lastSyncTimeView.setVisibility(View.VISIBLE); + } else { + lastSyncTimeView.setVisibility(View.GONE); + } + } + } + + private void refreshUI() { + loadAccountPreference(); + loadSyncButton(); + } + + private void showSelectAccountAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); + + dialogBuilder.setCustomTitle(titleView); + dialogBuilder.setPositiveButton(null, null); + + Account[] accounts = getGoogleAccounts(); + String defAccount = getSyncAccountName(this); + + mOriAccounts = accounts; + mHasAddedAccount = false; + + if (accounts.length > 0) { + CharSequence[] items = new CharSequence[accounts.length]; + final CharSequence[] itemMapping = items; + int checkedItem = -1; + int index = 0; + for (Account account : accounts) { + if (TextUtils.equals(account.name, defAccount)) { + checkedItem = index; + } + items[index++] = account.name; + } + dialogBuilder.setSingleChoiceItems(items, checkedItem, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setSyncAccount(itemMapping[which].toString()); + dialog.dismiss(); + refreshUI(); + } + }); + } + + View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); + dialogBuilder.setView(addAccountView); + + final AlertDialog dialog = dialogBuilder.show(); + addAccountView.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + mHasAddedAccount = true; + Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { + "gmail-ls" + }); + startActivityForResult(intent, -1); + dialog.dismiss(); + } + }); + } + + private void showChangeAccountConfirmAlertDialog() { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, + getSyncAccountName(this))); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); + dialogBuilder.setCustomTitle(titleView); + + CharSequence[] menuItemArray = new CharSequence[] { + getString(R.string.preferences_menu_change_account), + getString(R.string.preferences_menu_remove_account), + getString(R.string.preferences_menu_cancel) + }; + dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + showSelectAccountAlertDialog(); + } else if (which == 1) { + removeSyncAccount(); + refreshUI(); + } + } + }); + dialogBuilder.show(); + } + + private Account[] getGoogleAccounts() { + AccountManager accountManager = AccountManager.get(this); + return accountManager.getAccountsByType("com.google"); + } + + private void setSyncAccount(String account) { + if (!getSyncAccountName(this).equals(account)) { + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + if (account != null) { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); + } else { + editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + } + editor.commit(); + + // clean up last sync time + setLastSyncTime(this, 0); + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + + Toast.makeText(NotesPreferenceActivity.this, + getString(R.string.preferences_toast_success_set_accout, account), + Toast.LENGTH_SHORT).show(); + } + } + + private void removeSyncAccount() { + SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { + editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); + } + if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { + editor.remove(PREFERENCE_LAST_SYNC_TIME); + } + editor.commit(); + + // clean up local gtask related info + new Thread(new Runnable() { + public void run() { + ContentValues values = new ContentValues(); + values.put(NoteColumns.GTASK_ID, ""); + values.put(NoteColumns.SYNC_ID, 0); + getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); + } + }).start(); + } + + public static String getSyncAccountName(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); + } + + public static void setLastSyncTime(Context context, long time) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = settings.edit(); + editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + editor.commit(); + } + + public static long getLastSyncTime(Context context) { + SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, + Context.MODE_PRIVATE); + return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); + } + + private class GTaskReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + refreshUI(); + if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + syncStatus.setText(intent + .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); + } + + } + } + + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + Intent intent = new Intent(this, NotesListActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + default: + return false; + } + } +} 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; + } +} From 2ff4c51a38d2738a75f380eba045c5b3464567e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Wed, 5 Apr 2023 19:56:20 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...米便签开源代码的泛读报告.docx | Bin 216977 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/小米便签开源代码的泛读报告.docx diff --git a/doc/小米便签开源代码的泛读报告.docx b/doc/小米便签开源代码的泛读报告.docx deleted file mode 100644 index e65da11c6d738cbe69bcd1ae1f0cbca05488ee98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216977 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%0A5)OgUeLwBKroieCf7uA=| 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≪EvU-7atby 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 Y@T}q3XcKx7<03{5DEdJK%sY(#1!+6>n*aa+ From 19601f2864fb8e7fc1b2866022bdc5028982aa8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Fri, 7 Apr 2023 13:54:01 +0800 Subject: [PATCH 04/16] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/Contact.java | 73 -- src/data/Notes.java | 279 ----- src/data/NotesDatabaseHelper.java | 362 ------- src/data/NotesProvider.java | 305 ------ src/gtask/data/MetaData.java | 82 -- src/gtask/data/Node.java | 101 -- src/gtask/data/SqlData.java | 189 ---- src/gtask/data/SqlNote.java | 505 --------- src/gtask/data/Task.java | 351 ------- src/gtask/data/TaskList.java | 343 ------- .../exception/ActionFailureException.java | 33 - .../exception/NetworkFailureException.java | 33 - src/gtask/remote/GTaskASyncTask.java | 129 --- src/gtask/remote/GTaskClient.java | 585 ----------- src/gtask/remote/GTaskManager.java | 800 --------------- src/gtask/remote/GTaskSyncService.java | 128 --- src/model/Note.java | 253 ----- src/model/WorkingNote.java | 368 ------- src/tool/BackupUtils.java | 344 ------- src/tool/DataUtils.java | 295 ------ src/tool/GTaskStringUtils.java | 113 --- src/tool/ResourceParser.java | 181 ---- src/ui/AlarmAlertActivity.java | 158 --- src/ui/AlarmInitReceiver.java | 65 -- src/ui/AlarmReceiver.java | 30 - src/ui/DateTimePicker.java | 485 --------- src/ui/DateTimePickerDialog.java | 90 -- src/ui/DropdownMenu.java | 61 -- src/ui/FoldersListAdapter.java | 80 -- src/ui/NoteEditActivity.java | 873 ---------------- src/ui/NoteEditText.java | 217 ---- src/ui/NoteItemData.java | 224 ---- src/ui/NotesListActivity.java | 954 ------------------ src/ui/NotesListAdapter.java | 184 ---- src/ui/NotesListItem.java | 122 --- src/ui/NotesPreferenceActivity.java | 388 ------- src/widget/NoteWidgetProvider.java | 132 --- src/widget/NoteWidgetProvider_2x.java | 47 - src/widget/NoteWidgetProvider_4x.java | 46 - 39 files changed, 10008 deletions(-) delete mode 100644 src/data/Contact.java delete mode 100644 src/data/Notes.java delete mode 100644 src/data/NotesDatabaseHelper.java delete mode 100644 src/data/NotesProvider.java delete mode 100644 src/gtask/data/MetaData.java delete mode 100644 src/gtask/data/Node.java delete mode 100644 src/gtask/data/SqlData.java delete mode 100644 src/gtask/data/SqlNote.java delete mode 100644 src/gtask/data/Task.java delete mode 100644 src/gtask/data/TaskList.java delete mode 100644 src/gtask/exception/ActionFailureException.java delete mode 100644 src/gtask/exception/NetworkFailureException.java delete mode 100644 src/gtask/remote/GTaskASyncTask.java delete mode 100644 src/gtask/remote/GTaskClient.java delete mode 100644 src/gtask/remote/GTaskManager.java delete mode 100644 src/gtask/remote/GTaskSyncService.java delete mode 100644 src/model/Note.java delete mode 100644 src/model/WorkingNote.java delete mode 100644 src/tool/BackupUtils.java delete mode 100644 src/tool/DataUtils.java delete mode 100644 src/tool/GTaskStringUtils.java delete mode 100644 src/tool/ResourceParser.java delete mode 100644 src/ui/AlarmAlertActivity.java delete mode 100644 src/ui/AlarmInitReceiver.java delete mode 100644 src/ui/AlarmReceiver.java delete mode 100644 src/ui/DateTimePicker.java delete mode 100644 src/ui/DateTimePickerDialog.java delete mode 100644 src/ui/DropdownMenu.java delete mode 100644 src/ui/FoldersListAdapter.java delete mode 100644 src/ui/NoteEditActivity.java delete mode 100644 src/ui/NoteEditText.java delete mode 100644 src/ui/NoteItemData.java delete mode 100644 src/ui/NotesListActivity.java delete mode 100644 src/ui/NotesListAdapter.java delete mode 100644 src/ui/NotesListItem.java delete mode 100644 src/ui/NotesPreferenceActivity.java delete mode 100644 src/widget/NoteWidgetProvider.java delete mode 100644 src/widget/NoteWidgetProvider_2x.java delete mode 100644 src/widget/NoteWidgetProvider_4x.java diff --git a/src/data/Contact.java b/src/data/Contact.java deleted file mode 100644 index d97ac5d..0000000 --- a/src/data/Contact.java +++ /dev/null @@ -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 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 deleted file mode 100644 index f240604..0000000 --- a/src/data/Notes.java +++ /dev/null @@ -1,279 +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.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 deleted file mode 100644 index ffe5d57..0000000 --- a/src/data/NotesDatabaseHelper.java +++ /dev/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"); - } -} diff --git a/src/data/NotesProvider.java b/src/data/NotesProvider.java deleted file mode 100644 index edb0a60..0000000 --- a/src/data/NotesProvider.java +++ /dev/null @@ -1,305 +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.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/gtask/data/MetaData.java b/src/gtask/data/MetaData.java deleted file mode 100644 index 3a2050b..0000000 --- a/src/gtask/data/MetaData.java +++ /dev/null @@ -1,82 +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.gtask.data; - -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONException; -import org.json.JSONObject; - - -public class MetaData extends Task { - private final static String TAG = MetaData.class.getSimpleName(); - - private String mRelatedGid = null; - - public void setMeta(String gid, JSONObject metaInfo) { - try { - metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); - } catch (JSONException e) { - Log.e(TAG, "failed to put related gid"); - } - setNotes(metaInfo.toString()); - setName(GTaskStringUtils.META_NOTE_NAME); - } - - public String getRelatedGid() { - return mRelatedGid; - } - - @Override - public boolean isWorthSaving() { - return getNotes() != null; - } - - @Override - public void setContentByRemoteJSON(JSONObject js) { - super.setContentByRemoteJSON(js); - if (getNotes() != null) { - try { - JSONObject metaInfo = new JSONObject(getNotes().trim()); - mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); - } catch (JSONException e) { - Log.w(TAG, "failed to get related gid"); - mRelatedGid = null; - } - } - } - - @Override - public void setContentByLocalJSON(JSONObject js) { - // this function should not be called - throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); - } - - @Override - public JSONObject getLocalJSONFromContent() { - throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); - } - - @Override - public int getSyncAction(Cursor c) { - throw new IllegalAccessError("MetaData:getSyncAction should not be called"); - } - -} diff --git a/src/gtask/data/Node.java b/src/gtask/data/Node.java deleted file mode 100644 index 63950e0..0000000 --- a/src/gtask/data/Node.java +++ /dev/null @@ -1,101 +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.gtask.data; - -import android.database.Cursor; - -import org.json.JSONObject; - -public abstract class Node { - public static final int SYNC_ACTION_NONE = 0; - - public static final int SYNC_ACTION_ADD_REMOTE = 1; - - public static final int SYNC_ACTION_ADD_LOCAL = 2; - - public static final int SYNC_ACTION_DEL_REMOTE = 3; - - public static final int SYNC_ACTION_DEL_LOCAL = 4; - - public static final int SYNC_ACTION_UPDATE_REMOTE = 5; - - public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - - public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - - public static final int SYNC_ACTION_ERROR = 8; - - private String mGid; - - private String mName; - - private long mLastModified; - - private boolean mDeleted; - - public Node() { - mGid = null; - mName = ""; - mLastModified = 0; - mDeleted = false; - } - - public abstract JSONObject getCreateAction(int actionId); - - public abstract JSONObject getUpdateAction(int actionId); - - public abstract void setContentByRemoteJSON(JSONObject js); - - public abstract void setContentByLocalJSON(JSONObject js); - - public abstract JSONObject getLocalJSONFromContent(); - - public abstract int getSyncAction(Cursor c); - - public void setGid(String gid) { - this.mGid = gid; - } - - public void setName(String name) { - this.mName = name; - } - - public void setLastModified(long lastModified) { - this.mLastModified = lastModified; - } - - public void setDeleted(boolean deleted) { - this.mDeleted = deleted; - } - - public String getGid() { - return this.mGid; - } - - public String getName() { - return this.mName; - } - - public long getLastModified() { - return this.mLastModified; - } - - public boolean getDeleted() { - return this.mDeleted; - } - -} diff --git a/src/gtask/data/SqlData.java b/src/gtask/data/SqlData.java deleted file mode 100644 index d3ec3be..0000000 --- a/src/gtask/data/SqlData.java +++ /dev/null @@ -1,189 +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.gtask.data; - -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.NotesDatabaseHelper.TABLE; -import net.micode.notes.gtask.exception.ActionFailureException; - -import org.json.JSONException; -import org.json.JSONObject; - - -public class SqlData { - private static final String TAG = SqlData.class.getSimpleName(); - - private static final int INVALID_ID = -99999; - - public static final String[] PROJECTION_DATA = new String[] { - DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, - DataColumns.DATA3 - }; - - public static final int DATA_ID_COLUMN = 0; - - public static final int DATA_MIME_TYPE_COLUMN = 1; - - public static final int DATA_CONTENT_COLUMN = 2; - - public static final int DATA_CONTENT_DATA_1_COLUMN = 3; - - public static final int DATA_CONTENT_DATA_3_COLUMN = 4; - - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mDataId; - - private String mDataMimeType; - - private String mDataContent; - - private long mDataContentData1; - - private String mDataContentData3; - - private ContentValues mDiffDataValues; - - public SqlData(Context context) { - mContentResolver = context.getContentResolver(); - mIsCreate = true; - mDataId = INVALID_ID; - mDataMimeType = DataConstants.NOTE; - mDataContent = ""; - mDataContentData1 = 0; - mDataContentData3 = ""; - mDiffDataValues = new ContentValues(); - } - - public SqlData(Context context, Cursor c) { - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDiffDataValues = new ContentValues(); - } - - private void loadFromCursor(Cursor c) { - mDataId = c.getLong(DATA_ID_COLUMN); - mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); - mDataContent = c.getString(DATA_CONTENT_COLUMN); - mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); - mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); - } - - public void setContent(JSONObject js) throws JSONException { - long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; - if (mIsCreate || mDataId != dataId) { - mDiffDataValues.put(DataColumns.ID, dataId); - } - mDataId = dataId; - - String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) - : DataConstants.NOTE; - if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { - mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); - } - mDataMimeType = dataMimeType; - - String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; - if (mIsCreate || !mDataContent.equals(dataContent)) { - mDiffDataValues.put(DataColumns.CONTENT, dataContent); - } - mDataContent = dataContent; - - long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; - if (mIsCreate || mDataContentData1 != dataContentData1) { - mDiffDataValues.put(DataColumns.DATA1, dataContentData1); - } - mDataContentData1 = dataContentData1; - - String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; - if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { - mDiffDataValues.put(DataColumns.DATA3, dataContentData3); - } - mDataContentData3 = dataContentData3; - } - - public JSONObject getContent() throws JSONException { - if (mIsCreate) { - Log.e(TAG, "it seems that we haven't created this in database yet"); - return null; - } - JSONObject js = new JSONObject(); - js.put(DataColumns.ID, mDataId); - js.put(DataColumns.MIME_TYPE, mDataMimeType); - js.put(DataColumns.CONTENT, mDataContent); - js.put(DataColumns.DATA1, mDataContentData1); - js.put(DataColumns.DATA3, mDataContentData3); - return js; - } - - public void commit(long noteId, boolean validateVersion, long version) { - - if (mIsCreate) { - if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { - mDiffDataValues.remove(DataColumns.ID); - } - - mDiffDataValues.put(DataColumns.NOTE_ID, noteId); - Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); - try { - mDataId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - throw new ActionFailureException("create note failed"); - } - } else { - if (mDiffDataValues.size() > 0) { - int result = 0; - if (!validateVersion) { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); - } else { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, - " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { - String.valueOf(noteId), String.valueOf(version) - }); - } - if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); - } - } - } - - mDiffDataValues.clear(); - mIsCreate = false; - } - - public long getId() { - return mDataId; - } -} diff --git a/src/gtask/data/SqlNote.java b/src/gtask/data/SqlNote.java deleted file mode 100644 index 79a4095..0000000 --- a/src/gtask/data/SqlNote.java +++ /dev/null @@ -1,505 +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.gtask.data; - -import android.appwidget.AppWidgetManager; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.tool.GTaskStringUtils; -import net.micode.notes.tool.ResourceParser; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; - - -public class SqlNote { - private static final String TAG = SqlNote.class.getSimpleName(); - - private static final int INVALID_ID = -99999; - - public static final String[] PROJECTION_NOTE = new String[] { - NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE, - NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, - NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, - NoteColumns.VERSION - }; - - public static final int ID_COLUMN = 0; - - public static final int ALERTED_DATE_COLUMN = 1; - - public static final int BG_COLOR_ID_COLUMN = 2; - - public static final int CREATED_DATE_COLUMN = 3; - - public static final int HAS_ATTACHMENT_COLUMN = 4; - - public static final int MODIFIED_DATE_COLUMN = 5; - - public static final int NOTES_COUNT_COLUMN = 6; - - public static final int PARENT_ID_COLUMN = 7; - - public static final int SNIPPET_COLUMN = 8; - - public static final int TYPE_COLUMN = 9; - - public static final int WIDGET_ID_COLUMN = 10; - - public static final int WIDGET_TYPE_COLUMN = 11; - - public static final int SYNC_ID_COLUMN = 12; - - public static final int LOCAL_MODIFIED_COLUMN = 13; - - public static final int ORIGIN_PARENT_ID_COLUMN = 14; - - public static final int GTASK_ID_COLUMN = 15; - - public static final int VERSION_COLUMN = 16; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mId; - - private long mAlertDate; - - private int mBgColorId; - - private long mCreatedDate; - - private int mHasAttachment; - - private long mModifiedDate; - - private long mParentId; - - private String mSnippet; - - private int mType; - - private int mWidgetId; - - private int mWidgetType; - - private long mOriginParent; - - private long mVersion; - - private ContentValues mDiffNoteValues; - - private ArrayList mDataList; - - public SqlNote(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = true; - mId = INVALID_ID; - mAlertDate = 0; - mBgColorId = ResourceParser.getDefaultBgId(context); - mCreatedDate = System.currentTimeMillis(); - mHasAttachment = 0; - mModifiedDate = System.currentTimeMillis(); - mParentId = 0; - mSnippet = ""; - mType = Notes.TYPE_NOTE; - mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - mOriginParent = 0; - mVersion = 0; - mDiffNoteValues = new ContentValues(); - mDataList = new ArrayList(); - } - - public SqlNote(Context context, Cursor c) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDataList = new ArrayList(); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); - } - - public SqlNote(Context context, long id) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(id); - mDataList = new ArrayList(); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); - - } - - private void loadFromCursor(long id) { - Cursor c = null; - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(id) - }, null); - if (c != null) { - c.moveToNext(); - loadFromCursor(c); - } else { - Log.w(TAG, "loadFromCursor: cursor = null"); - } - } finally { - if (c != null) - c.close(); - } - } - - private void loadFromCursor(Cursor c) { - mId = c.getLong(ID_COLUMN); - mAlertDate = c.getLong(ALERTED_DATE_COLUMN); - mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); - mCreatedDate = c.getLong(CREATED_DATE_COLUMN); - mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); - mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); - mParentId = c.getLong(PARENT_ID_COLUMN); - mSnippet = c.getString(SNIPPET_COLUMN); - mType = c.getInt(TYPE_COLUMN); - mWidgetId = c.getInt(WIDGET_ID_COLUMN); - mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); - mVersion = c.getLong(VERSION_COLUMN); - } - - private void loadDataContent() { - Cursor c = null; - mDataList.clear(); - try { - c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, - "(note_id=?)", new String[] { - String.valueOf(mId) - }, null); - if (c != null) { - if (c.getCount() == 0) { - Log.w(TAG, "it seems that the note has not data"); - return; - } - while (c.moveToNext()) { - SqlData data = new SqlData(mContext, c); - mDataList.add(data); - } - } else { - Log.w(TAG, "loadDataContent: cursor = null"); - } - } finally { - if (c != null) - c.close(); - } - } - - public boolean setContent(JSONObject js) { - try { - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { - Log.w(TAG, "cannot set system folder"); - } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type - String snippet = note.has(NoteColumns.SNIPPET) ? note - .getString(NoteColumns.SNIPPET) : ""; - if (mIsCreate || !mSnippet.equals(snippet)) { - mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); - } - mSnippet = snippet; - - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; - if (mIsCreate || mType != type) { - mDiffNoteValues.put(NoteColumns.TYPE, type); - } - mType = type; - } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; - if (mIsCreate || mId != id) { - mDiffNoteValues.put(NoteColumns.ID, id); - } - mId = id; - - long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note - .getLong(NoteColumns.ALERTED_DATE) : 0; - if (mIsCreate || mAlertDate != alertDate) { - mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); - } - mAlertDate = alertDate; - - int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note - .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext); - if (mIsCreate || mBgColorId != bgColorId) { - mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); - } - mBgColorId = bgColorId; - - long createDate = note.has(NoteColumns.CREATED_DATE) ? note - .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis(); - if (mIsCreate || mCreatedDate != createDate) { - mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); - } - mCreatedDate = createDate; - - int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note - .getInt(NoteColumns.HAS_ATTACHMENT) : 0; - if (mIsCreate || mHasAttachment != hasAttachment) { - mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); - } - mHasAttachment = hasAttachment; - - long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note - .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); - if (mIsCreate || mModifiedDate != modifiedDate) { - mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); - } - mModifiedDate = modifiedDate; - - long parentId = note.has(NoteColumns.PARENT_ID) ? note - .getLong(NoteColumns.PARENT_ID) : 0; - if (mIsCreate || mParentId != parentId) { - mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); - } - mParentId = parentId; - - String snippet = note.has(NoteColumns.SNIPPET) ? note - .getString(NoteColumns.SNIPPET) : ""; - if (mIsCreate || !mSnippet.equals(snippet)) { - mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); - } - mSnippet = snippet; - - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; - if (mIsCreate || mType != type) { - mDiffNoteValues.put(NoteColumns.TYPE, type); - } - mType = type; - - int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) - : AppWidgetManager.INVALID_APPWIDGET_ID; - if (mIsCreate || mWidgetId != widgetId) { - mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); - } - mWidgetId = widgetId; - - int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note - .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; - if (mIsCreate || mWidgetType != widgetType) { - mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); - } - mWidgetType = widgetType; - - long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note - .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; - if (mIsCreate || mOriginParent != originParent) { - mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); - } - mOriginParent = originParent; - - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - SqlData sqlData = null; - if (data.has(DataColumns.ID)) { - long dataId = data.getLong(DataColumns.ID); - for (SqlData temp : mDataList) { - if (dataId == temp.getId()) { - sqlData = temp; - } - } - } - - if (sqlData == null) { - sqlData = new SqlData(mContext); - mDataList.add(sqlData); - } - - sqlData.setContent(data); - } - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return false; - } - return true; - } - - public JSONObject getContent() { - try { - JSONObject js = new JSONObject(); - - if (mIsCreate) { - Log.e(TAG, "it seems that we haven't created this in database yet"); - return null; - } - - JSONObject note = new JSONObject(); - if (mType == Notes.TYPE_NOTE) { - note.put(NoteColumns.ID, mId); - note.put(NoteColumns.ALERTED_DATE, mAlertDate); - note.put(NoteColumns.BG_COLOR_ID, mBgColorId); - note.put(NoteColumns.CREATED_DATE, mCreatedDate); - note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment); - note.put(NoteColumns.MODIFIED_DATE, mModifiedDate); - note.put(NoteColumns.PARENT_ID, mParentId); - note.put(NoteColumns.SNIPPET, mSnippet); - note.put(NoteColumns.TYPE, mType); - note.put(NoteColumns.WIDGET_ID, mWidgetId); - note.put(NoteColumns.WIDGET_TYPE, mWidgetType); - note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); - js.put(GTaskStringUtils.META_HEAD_NOTE, note); - - JSONArray dataArray = new JSONArray(); - for (SqlData sqlData : mDataList) { - JSONObject data = sqlData.getContent(); - if (data != null) { - dataArray.put(data); - } - } - js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); - } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { - note.put(NoteColumns.ID, mId); - note.put(NoteColumns.TYPE, mType); - note.put(NoteColumns.SNIPPET, mSnippet); - js.put(GTaskStringUtils.META_HEAD_NOTE, note); - } - - return js; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - return null; - } - - public void setParentId(long id) { - mParentId = id; - mDiffNoteValues.put(NoteColumns.PARENT_ID, id); - } - - public void setGtaskId(String gid) { - mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); - } - - public void setSyncId(long syncId) { - mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); - } - - public void resetLocalModified() { - mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); - } - - public long getId() { - return mId; - } - - public long getParentId() { - return mParentId; - } - - public String getSnippet() { - return mSnippet; - } - - public boolean isNoteType() { - return mType == Notes.TYPE_NOTE; - } - - public void commit(boolean validateVersion) { - if (mIsCreate) { - if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { - mDiffNoteValues.remove(NoteColumns.ID); - } - - Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); - try { - mId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - throw new ActionFailureException("create note failed"); - } - if (mId == 0) { - throw new IllegalStateException("Create thread id failed"); - } - - if (mType == Notes.TYPE_NOTE) { - for (SqlData sqlData : mDataList) { - sqlData.commit(mId, false, -1); - } - } - } else { - if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { - Log.e(TAG, "No such note"); - throw new IllegalStateException("Try to update note with invalid id"); - } - if (mDiffNoteValues.size() > 0) { - mVersion ++; - int result = 0; - if (!validateVersion) { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?)", new String[] { - String.valueOf(mId) - }); - } else { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", - new String[] { - String.valueOf(mId), String.valueOf(mVersion) - }); - } - if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); - } - } - - if (mType == Notes.TYPE_NOTE) { - for (SqlData sqlData : mDataList) { - sqlData.commit(mId, validateVersion, mVersion); - } - } - } - - // refresh local info - loadFromCursor(mId); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - - mDiffNoteValues.clear(); - mIsCreate = false; - } -} diff --git a/src/gtask/data/Task.java b/src/gtask/data/Task.java deleted file mode 100644 index 6a19454..0000000 --- a/src/gtask/data/Task.java +++ /dev/null @@ -1,351 +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.gtask.data; - -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - - -public class Task extends Node { - private static final String TAG = Task.class.getSimpleName(); - - private boolean mCompleted; - - private String mNotes; - - private JSONObject mMetaInfo; - - private Task mPriorSibling; - - private TaskList mParent; - - public Task() { - super(); - mCompleted = false; - mNotes = null; - mPriorSibling = null; - mParent = null; - mMetaInfo = null; - } - - public JSONObject getCreateAction(int actionId) { - JSONObject js = new JSONObject(); - - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // index - js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); - entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, - GTaskStringUtils.GTASK_JSON_TYPE_TASK); - if (getNotes() != null) { - entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); - } - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - // parent_id - js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); - - // dest_parent_type - js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, - GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - - // list_id - js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); - - // prior_sibling_id - if (mPriorSibling != null) { - js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); - } - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate task-create jsonobject"); - } - - return js; - } - - public JSONObject getUpdateAction(int actionId) { - JSONObject js = new JSONObject(); - - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // id - js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - if (getNotes() != null) { - entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); - } - entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate task-update jsonobject"); - } - - return js; - } - - public void setContentByRemoteJSON(JSONObject js) { - if (js != null) { - try { - // id - if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { - setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); - } - - // last_modified - if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { - setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); - } - - // name - if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { - setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); - } - - // notes - if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { - setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); - } - - // deleted - if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { - setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); - } - - // completed - if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { - setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to get task content from jsonobject"); - } - } - } - - public void setContentByLocalJSON(JSONObject js) { - if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) - || !js.has(GTaskStringUtils.META_HEAD_DATA)) { - Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); - } - - try { - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - - if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { - Log.e(TAG, "invalid type"); - return; - } - - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { - setName(data.getString(DataColumns.CONTENT)); - break; - } - } - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - } - - public JSONObject getLocalJSONFromContent() { - String name = getName(); - try { - if (mMetaInfo == null) { - // new task created from web - if (name == null) { - Log.w(TAG, "the note seems to be an empty one"); - return null; - } - - JSONObject js = new JSONObject(); - JSONObject note = new JSONObject(); - JSONArray dataArray = new JSONArray(); - JSONObject data = new JSONObject(); - data.put(DataColumns.CONTENT, name); - dataArray.put(data); - js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); - note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); - js.put(GTaskStringUtils.META_HEAD_NOTE, note); - return js; - } else { - // synced task - JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { - data.put(DataColumns.CONTENT, getName()); - break; - } - } - - note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); - return mMetaInfo; - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return null; - } - } - - public void setMetaInfo(MetaData metaData) { - if (metaData != null && metaData.getNotes() != null) { - try { - mMetaInfo = new JSONObject(metaData.getNotes()); - } catch (JSONException e) { - Log.w(TAG, e.toString()); - mMetaInfo = null; - } - } - } - - public int getSyncAction(Cursor c) { - try { - JSONObject noteInfo = null; - if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { - noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - } - - if (noteInfo == null) { - Log.w(TAG, "it seems that note meta has been deleted"); - return SYNC_ACTION_UPDATE_REMOTE; - } - - if (!noteInfo.has(NoteColumns.ID)) { - Log.w(TAG, "remote note id seems to be deleted"); - return SYNC_ACTION_UPDATE_LOCAL; - } - - // validate the note id now - if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { - Log.w(TAG, "note id doesn't match"); - return SYNC_ACTION_UPDATE_LOCAL; - } - - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // there is no local update - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // no update both side - return SYNC_ACTION_NONE; - } else { - // apply remote to local - return SYNC_ACTION_UPDATE_LOCAL; - } - } else { - // validate gtask id - if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { - Log.e(TAG, "gtask id doesn't match"); - return SYNC_ACTION_ERROR; - } - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only - return SYNC_ACTION_UPDATE_REMOTE; - } else { - return SYNC_ACTION_UPDATE_CONFLICT; - } - } - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - - return SYNC_ACTION_ERROR; - } - - public boolean isWorthSaving() { - return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) - || (getNotes() != null && getNotes().trim().length() > 0); - } - - public void setCompleted(boolean completed) { - this.mCompleted = completed; - } - - public void setNotes(String notes) { - this.mNotes = notes; - } - - public void setPriorSibling(Task priorSibling) { - this.mPriorSibling = priorSibling; - } - - public void setParent(TaskList parent) { - this.mParent = parent; - } - - public boolean getCompleted() { - return this.mCompleted; - } - - public String getNotes() { - return this.mNotes; - } - - public Task getPriorSibling() { - return this.mPriorSibling; - } - - public TaskList getParent() { - return this.mParent; - } - -} diff --git a/src/gtask/data/TaskList.java b/src/gtask/data/TaskList.java deleted file mode 100644 index 4ea21c5..0000000 --- a/src/gtask/data/TaskList.java +++ /dev/null @@ -1,343 +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.gtask.data; - -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; - - -public class TaskList extends Node { - private static final String TAG = TaskList.class.getSimpleName(); - - private int mIndex; - - private ArrayList mChildren; - - public TaskList() { - super(); - mChildren = new ArrayList(); - mIndex = 1; - } - - public JSONObject getCreateAction(int actionId) { - JSONObject js = new JSONObject(); - - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // index - js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); - entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, - GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate tasklist-create jsonobject"); - } - - return js; - } - - public JSONObject getUpdateAction(int actionId) { - JSONObject js = new JSONObject(); - - try { - // action_type - js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - - // action_id - js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - - // id - js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - - // entity_delta - JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); - js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to generate tasklist-update jsonobject"); - } - - return js; - } - - public void setContentByRemoteJSON(JSONObject js) { - if (js != null) { - try { - // id - if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { - setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); - } - - // last_modified - if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { - setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); - } - - // name - if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { - setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); - } - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("fail to get tasklist content from jsonobject"); - } - } - } - - public void setContentByLocalJSON(JSONObject js) { - if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { - Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); - } - - try { - JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - - if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - String name = folder.getString(NoteColumns.SNIPPET); - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); - } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { - if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); - else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) - setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE); - else - Log.e(TAG, "invalid system folder"); - } else { - Log.e(TAG, "error type"); - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - } - - public JSONObject getLocalJSONFromContent() { - try { - JSONObject js = new JSONObject(); - JSONObject folder = new JSONObject(); - - String folderName = getName(); - if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) - folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), - folderName.length()); - folder.put(NoteColumns.SNIPPET, folderName); - if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) - || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) - folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - else - folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - - js.put(GTaskStringUtils.META_HEAD_NOTE, folder); - - return js; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return null; - } - } - - public int getSyncAction(Cursor c) { - try { - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // there is no local update - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // no update both side - return SYNC_ACTION_NONE; - } else { - // apply remote to local - return SYNC_ACTION_UPDATE_LOCAL; - } - } else { - // validate gtask id - if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { - Log.e(TAG, "gtask id doesn't match"); - return SYNC_ACTION_ERROR; - } - if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only - return SYNC_ACTION_UPDATE_REMOTE; - } else { - // for folder conflicts, just apply local modification - return SYNC_ACTION_UPDATE_REMOTE; - } - } - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - - return SYNC_ACTION_ERROR; - } - - public int getChildTaskCount() { - return mChildren.size(); - } - - public boolean addChildTask(Task task) { - boolean ret = false; - if (task != null && !mChildren.contains(task)) { - ret = mChildren.add(task); - if (ret) { - // need to set prior sibling and parent - task.setPriorSibling(mChildren.isEmpty() ? null : mChildren - .get(mChildren.size() - 1)); - task.setParent(this); - } - } - return ret; - } - - public boolean addChildTask(Task task, int index) { - if (index < 0 || index > mChildren.size()) { - Log.e(TAG, "add child task: invalid index"); - return false; - } - - int pos = mChildren.indexOf(task); - if (task != null && pos == -1) { - mChildren.add(index, task); - - // update the task list - Task preTask = null; - Task afterTask = null; - if (index != 0) - preTask = mChildren.get(index - 1); - if (index != mChildren.size() - 1) - afterTask = mChildren.get(index + 1); - - task.setPriorSibling(preTask); - if (afterTask != null) - afterTask.setPriorSibling(task); - } - - return true; - } - - public boolean removeChildTask(Task task) { - boolean ret = false; - int index = mChildren.indexOf(task); - if (index != -1) { - ret = mChildren.remove(task); - - if (ret) { - // reset prior sibling and parent - task.setPriorSibling(null); - task.setParent(null); - - // update the task list - if (index != mChildren.size()) { - mChildren.get(index).setPriorSibling( - index == 0 ? null : mChildren.get(index - 1)); - } - } - } - return ret; - } - - public boolean moveChildTask(Task task, int index) { - - if (index < 0 || index >= mChildren.size()) { - Log.e(TAG, "move child task: invalid index"); - return false; - } - - int pos = mChildren.indexOf(task); - if (pos == -1) { - Log.e(TAG, "move child task: the task should in the list"); - return false; - } - - if (pos == index) - return true; - return (removeChildTask(task) && addChildTask(task, index)); - } - - public Task findChildTaskByGid(String gid) { - for (int i = 0; i < mChildren.size(); i++) { - Task t = mChildren.get(i); - if (t.getGid().equals(gid)) { - return t; - } - } - return null; - } - - public int getChildTaskIndex(Task task) { - return mChildren.indexOf(task); - } - - public Task getChildTaskByIndex(int index) { - if (index < 0 || index >= mChildren.size()) { - Log.e(TAG, "getTaskByIndex: invalid index"); - return null; - } - return mChildren.get(index); - } - - public Task getChilTaskByGid(String gid) { - for (Task task : mChildren) { - if (task.getGid().equals(gid)) - return task; - } - return null; - } - - public ArrayList getChildTaskList() { - return this.mChildren; - } - - public void setIndex(int index) { - this.mIndex = index; - } - - public int getIndex() { - return this.mIndex; - } -} diff --git a/src/gtask/exception/ActionFailureException.java b/src/gtask/exception/ActionFailureException.java deleted file mode 100644 index 15504be..0000000 --- a/src/gtask/exception/ActionFailureException.java +++ /dev/null @@ -1,33 +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.gtask.exception; - -public class ActionFailureException extends RuntimeException { - private static final long serialVersionUID = 4425249765923293627L; - - public ActionFailureException() { - super(); - } - - public ActionFailureException(String paramString) { - super(paramString); - } - - public ActionFailureException(String paramString, Throwable paramThrowable) { - super(paramString, paramThrowable); - } -} diff --git a/src/gtask/exception/NetworkFailureException.java b/src/gtask/exception/NetworkFailureException.java deleted file mode 100644 index b08cfb1..0000000 --- a/src/gtask/exception/NetworkFailureException.java +++ /dev/null @@ -1,33 +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.gtask.exception; - -public class NetworkFailureException extends Exception { - private static final long serialVersionUID = 2107610287180234136L; - - public NetworkFailureException() { - super(); - } - - public NetworkFailureException(String paramString) { - super(paramString); - } - - public NetworkFailureException(String paramString, Throwable paramThrowable) { - super(paramString, paramThrowable); - } -} diff --git a/src/gtask/remote/GTaskASyncTask.java b/src/gtask/remote/GTaskASyncTask.java deleted file mode 100644 index 0e332f3..0000000 --- a/src/gtask/remote/GTaskASyncTask.java +++ /dev/null @@ -1,129 +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.gtask.remote; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesListActivity; -import net.micode.notes.ui.NotesPreferenceActivity; - - -public class GTaskASyncTask extends AsyncTask { - - private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - - public interface OnCompleteListener { - void onComplete(); - } - - private Context mContext; - - private NotificationManager mNotifiManager; - - private GTaskManager mTaskManager; - - private OnCompleteListener mOnCompleteListener; - - public GTaskASyncTask(Context context, OnCompleteListener listener) { - mContext = context; - mOnCompleteListener = listener; - mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - mTaskManager = GTaskManager.getInstance(); - } - - public void cancelSync() { - mTaskManager.cancelSync(); - } - - public void publishProgess(String message) { - publishProgress(new String[] { - message - }); - } - - private void showNotification(int tickerId, String content) { - PendingIntent pendingIntent; - if (tickerId != R.string.ticker_success) { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesPreferenceActivity.class), 0); - - } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), 0); - } - - - 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); - } - - - - @Override - protected Integer doInBackground(Void... unused) { - publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); - return mTaskManager.sync(mContext, this); - } - - @Override - protected void onProgressUpdate(String... progress) { - showNotification(R.string.ticker_syncing, progress[0]); - if (mContext instanceof GTaskSyncService) { - ((GTaskSyncService) mContext).sendBroadcast(progress[0]); - } - } - - @Override - protected void onPostExecute(Integer result) { - if (result == GTaskManager.STATE_SUCCESS) { - showNotification(R.string.ticker_success, mContext.getString( - R.string.success_sync_account, mTaskManager.getSyncAccount())); - NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); - } else if (result == GTaskManager.STATE_NETWORK_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); - } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); - } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { - showNotification(R.string.ticker_cancel, mContext - .getString(R.string.error_sync_cancelled)); - } - if (mOnCompleteListener != null) { - new Thread(new Runnable() { - - public void run() { - mOnCompleteListener.onComplete(); - } - }).start(); - } - } -} diff --git a/src/gtask/remote/GTaskClient.java b/src/gtask/remote/GTaskClient.java deleted file mode 100644 index c67dfdf..0000000 --- a/src/gtask/remote/GTaskClient.java +++ /dev/null @@ -1,585 +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.gtask.remote; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerFuture; -import android.app.Activity; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.gtask.data.Node; -import net.micode.notes.gtask.data.Task; -import net.micode.notes.gtask.data.TaskList; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.gtask.exception.NetworkFailureException; -import net.micode.notes.tool.GTaskStringUtils; -import net.micode.notes.ui.NotesPreferenceActivity; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.LinkedList; -import java.util.List; -import java.util.zip.GZIPInputStream; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; - - -public class GTaskClient { - private static final String TAG = GTaskClient.class.getSimpleName(); - - private static final String GTASK_URL = "https://mail.google.com/tasks/"; - - private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - - private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - - private static GTaskClient mInstance = null; - - private DefaultHttpClient mHttpClient; - - private String mGetUrl; - - private String mPostUrl; - - private long mClientVersion; - - private boolean mLoggedin; - - private long mLastLoginTime; - - private int mActionId; - - private Account mAccount; - - private JSONArray mUpdateArray; - - private GTaskClient() { - mHttpClient = null; - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - mClientVersion = -1; - mLoggedin = false; - mLastLoginTime = 0; - mActionId = 1; - mAccount = null; - mUpdateArray = null; - } - - public static synchronized GTaskClient getInstance() { - if (mInstance == null) { - mInstance = new GTaskClient(); - } - return mInstance; - } - - public boolean login(Activity activity) { - // we suppose that the cookie would expire after 5 minutes - // then we need to re-login - final long interval = 1000 * 60 * 5; - if (mLastLoginTime + interval < System.currentTimeMillis()) { - mLoggedin = false; - } - - // need to re-login after account switch - if (mLoggedin - && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity - .getSyncAccountName(activity))) { - mLoggedin = false; - } - - if (mLoggedin) { - Log.d(TAG, "already logged in"); - return true; - } - - mLastLoginTime = System.currentTimeMillis(); - String authToken = loginGoogleAccount(activity, false); - if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; - } - - // login with custom domain if necessary - if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() - .endsWith("googlemail.com"))) { - StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); - int index = mAccount.name.indexOf('@') + 1; - String suffix = mAccount.name.substring(index); - url.append(suffix + "/"); - mGetUrl = url.toString() + "ig"; - mPostUrl = url.toString() + "r/ig"; - - if (tryToLoginGtask(activity, authToken)) { - mLoggedin = true; - } - } - - // try to login with google official url - if (!mLoggedin) { - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - if (!tryToLoginGtask(activity, authToken)) { - return false; - } - } - - mLoggedin = true; - return true; - } - - private String loginGoogleAccount(Activity activity, boolean invalidateToken) { - String authToken; - AccountManager accountManager = AccountManager.get(activity); - Account[] accounts = accountManager.getAccountsByType("com.google"); - - if (accounts.length == 0) { - Log.e(TAG, "there is no available google account"); - return null; - } - - String accountName = NotesPreferenceActivity.getSyncAccountName(activity); - Account account = null; - for (Account a : accounts) { - if (a.name.equals(accountName)) { - account = a; - break; - } - } - if (account != null) { - mAccount = account; - } else { - Log.e(TAG, "unable to get an account with the same name in the settings"); - return null; - } - - // get the token now - AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, - "goanna_mobile", null, activity, null, null); - try { - Bundle authTokenBundle = accountManagerFuture.getResult(); - authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); - if (invalidateToken) { - accountManager.invalidateAuthToken("com.google", authToken); - loginGoogleAccount(activity, false); - } - } catch (Exception e) { - Log.e(TAG, "get auth token failed"); - authToken = null; - } - - return authToken; - } - - private boolean tryToLoginGtask(Activity activity, String authToken) { - if (!loginGtask(authToken)) { - // maybe the auth token is out of date, now let's invalidate the - // token and try again - authToken = loginGoogleAccount(activity, true); - if (authToken == null) { - Log.e(TAG, "login google account failed"); - return false; - } - - if (!loginGtask(authToken)) { - Log.e(TAG, "login gtask failed"); - return false; - } - } - return true; - } - - private boolean loginGtask(String authToken) { - int timeoutConnection = 10000; - int timeoutSocket = 15000; - HttpParams httpParameters = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); - HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); - mHttpClient = new DefaultHttpClient(httpParameters); - BasicCookieStore localBasicCookieStore = new BasicCookieStore(); - mHttpClient.setCookieStore(localBasicCookieStore); - HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - - // login gtask - try { - String loginUrl = mGetUrl + "?auth=" + authToken; - HttpGet httpGet = new HttpGet(loginUrl); - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the cookie now - List cookies = mHttpClient.getCookieStore().getCookies(); - boolean hasAuthCookie = false; - for (Cookie cookie : cookies) { - if (cookie.getName().contains("GTL")) { - hasAuthCookie = true; - } - } - if (!hasAuthCookie) { - Log.w(TAG, "it seems that there is no auth cookie"); - } - - // get the client version - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - int begin = resString.indexOf(jsBegin); - int end = resString.lastIndexOf(jsEnd); - String jsString = null; - if (begin != -1 && end != -1 && begin < end) { - jsString = resString.substring(begin + jsBegin.length(), end); - } - JSONObject js = new JSONObject(jsString); - mClientVersion = js.getLong("v"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return false; - } catch (Exception e) { - // simply catch all exceptions - Log.e(TAG, "httpget gtask_url failed"); - return false; - } - - return true; - } - - private int getActionId() { - return mActionId++; - } - - private HttpPost createHttpPost() { - HttpPost httpPost = new HttpPost(mPostUrl); - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - httpPost.setHeader("AT", "1"); - return httpPost; - } - - private String getResponseContent(HttpEntity entity) throws IOException { - String contentEncoding = null; - if (entity.getContentEncoding() != null) { - contentEncoding = entity.getContentEncoding().getValue(); - Log.d(TAG, "encoding: " + contentEncoding); - } - - InputStream input = entity.getContent(); - if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { - input = new GZIPInputStream(entity.getContent()); - } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { - Inflater inflater = new Inflater(true); - input = new InflaterInputStream(entity.getContent(), inflater); - } - - try { - InputStreamReader isr = new InputStreamReader(input); - BufferedReader br = new BufferedReader(isr); - StringBuilder sb = new StringBuilder(); - - while (true) { - String buff = br.readLine(); - if (buff == null) { - return sb.toString(); - } - sb = sb.append(buff); - } - } finally { - input.close(); - } - } - - private JSONObject postRequest(JSONObject js) throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); - } - - HttpPost httpPost = createHttpPost(); - try { - LinkedList list = new LinkedList(); - list.add(new BasicNameValuePair("r", js.toString())); - UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); - httpPost.setEntity(entity); - - // execute the post - HttpResponse response = mHttpClient.execute(httpPost); - String jsString = getResponseContent(response.getEntity()); - return new JSONObject(jsString); - - } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("postRequest failed"); - } catch (IOException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("postRequest failed"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("unable to convert response content to jsonobject"); - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("error occurs when posting request"); - } - } - - public void createTask(Task task) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(task.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // post - JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("create task: handing jsonobject failed"); - } - } - - public void createTaskList(TaskList tasklist) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - actionList.put(tasklist.getCreateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - // post - JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); - tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("create tasklist: handing jsonobject failed"); - } - } - - public void commitUpdate() throws NetworkFailureException { - if (mUpdateArray != null) { - try { - JSONObject jsPost = new JSONObject(); - - // action_list - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - mUpdateArray = null; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("commit update: handing jsonobject failed"); - } - } - } - - public void addUpdateNode(Node node) throws NetworkFailureException { - if (node != null) { - // too many update items may result in an error - // set max to 10 items - if (mUpdateArray != null && mUpdateArray.length() > 10) { - commitUpdate(); - } - - if (mUpdateArray == null) - mUpdateArray = new JSONArray(); - mUpdateArray.put(node.getUpdateAction(getActionId())); - } - } - - public void moveTask(Task task, TaskList preParent, TaskList curParent) - throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); - - // action_list - action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); - if (preParent == curParent && task.getPriorSibling() != null) { - // put prioring_sibing_id only if moving within the tasklist and - // it is not the first one - action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); - } - action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); - action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); - if (preParent != curParent) { - // put the dest_list only if moving between tasklists - action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); - } - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("move task: handing jsonobject failed"); - } - } - - public void deleteNode(Node node) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - - // action_list - node.setDeleted(true); - actionList.put(node.getUpdateAction(getActionId())); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - postRequest(jsPost); - mUpdateArray = null; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("delete node: handing jsonobject failed"); - } - } - - public JSONArray getTaskLists() throws NetworkFailureException { - if (!mLoggedin) { - Log.e(TAG, "please login first"); - throw new ActionFailureException("not logged in"); - } - - try { - HttpGet httpGet = new HttpGet(mGetUrl); - HttpResponse response = null; - response = mHttpClient.execute(httpGet); - - // get the task list - String resString = getResponseContent(response.getEntity()); - String jsBegin = "_setup("; - String jsEnd = ")}"; - int begin = resString.indexOf(jsBegin); - int end = resString.lastIndexOf(jsEnd); - String jsString = null; - if (begin != -1 && end != -1 && begin < end) { - jsString = resString.substring(begin + jsBegin.length(), end); - } - JSONObject js = new JSONObject(jsString); - return js.getJSONObject("t").getJSONArray(GTaskStringUtils.GTASK_JSON_LISTS); - } catch (ClientProtocolException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (IOException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new NetworkFailureException("gettasklists: httpget failed"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("get task lists: handing jasonobject failed"); - } - } - - public JSONArray getTaskList(String listGid) throws NetworkFailureException { - commitUpdate(); - try { - JSONObject jsPost = new JSONObject(); - JSONArray actionList = new JSONArray(); - JSONObject action = new JSONObject(); - - // action_list - action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, - GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); - action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version - jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - - JSONObject jsResponse = postRequest(jsPost); - return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("get task list: handing jsonobject failed"); - } - } - - public Account getSyncAccount() { - return mAccount; - } - - public void resetUpdateArray() { - mUpdateArray = null; - } -} diff --git a/src/gtask/remote/GTaskManager.java b/src/gtask/remote/GTaskManager.java deleted file mode 100644 index d2b4082..0000000 --- a/src/gtask/remote/GTaskManager.java +++ /dev/null @@ -1,800 +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.gtask.remote; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.data.MetaData; -import net.micode.notes.gtask.data.Node; -import net.micode.notes.gtask.data.SqlNote; -import net.micode.notes.gtask.data.Task; -import net.micode.notes.gtask.data.TaskList; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.gtask.exception.NetworkFailureException; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; - - -public class GTaskManager { - private static final String TAG = GTaskManager.class.getSimpleName(); - - public static final int STATE_SUCCESS = 0; - - public static final int STATE_NETWORK_ERROR = 1; - - public static final int STATE_INTERNAL_ERROR = 2; - - public static final int STATE_SYNC_IN_PROGRESS = 3; - - public static final int STATE_SYNC_CANCELLED = 4; - - private static GTaskManager mInstance = null; - - private Activity mActivity; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mSyncing; - - private boolean mCancelled; - - private HashMap mGTaskListHashMap; - - private HashMap mGTaskHashMap; - - private HashMap mMetaHashMap; - - private TaskList mMetaList; - - private HashSet mLocalDeleteIdMap; - - private HashMap mGidToNid; - - private HashMap mNidToGid; - - private GTaskManager() { - mSyncing = false; - mCancelled = false; - mGTaskListHashMap = new HashMap(); - mGTaskHashMap = new HashMap(); - mMetaHashMap = new HashMap(); - mMetaList = null; - mLocalDeleteIdMap = new HashSet(); - mGidToNid = new HashMap(); - mNidToGid = new HashMap(); - } - - public static synchronized GTaskManager getInstance() { - if (mInstance == null) { - mInstance = new GTaskManager(); - } - return mInstance; - } - - public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken - mActivity = activity; - } - - public int sync(Context context, GTaskASyncTask asyncTask) { - if (mSyncing) { - Log.d(TAG, "Sync is in progress"); - return STATE_SYNC_IN_PROGRESS; - } - mContext = context; - mContentResolver = mContext.getContentResolver(); - mSyncing = true; - mCancelled = false; - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - - try { - GTaskClient client = GTaskClient.getInstance(); - client.resetUpdateArray(); - - // login google task - if (!mCancelled) { - if (!client.login(mActivity)) { - throw new NetworkFailureException("login google task failed"); - } - } - - // get the task list from google - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); - initGTaskList(); - - // do content sync work - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); - syncContent(); - } catch (NetworkFailureException e) { - Log.e(TAG, e.toString()); - return STATE_NETWORK_ERROR; - } catch (ActionFailureException e) { - Log.e(TAG, e.toString()); - return STATE_INTERNAL_ERROR; - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return STATE_INTERNAL_ERROR; - } finally { - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - mSyncing = false; - } - - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; - } - - private void initGTaskList() throws NetworkFailureException { - if (mCancelled) - return; - GTaskClient client = GTaskClient.getInstance(); - try { - JSONArray jsTaskLists = client.getTaskLists(); - - // init meta list first - mMetaList = null; - for (int i = 0; i < jsTaskLists.length(); i++) { - JSONObject object = jsTaskLists.getJSONObject(i); - String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - - if (name - .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { - mMetaList = new TaskList(); - mMetaList.setContentByRemoteJSON(object); - - // load meta data - JSONArray jsMetas = client.getTaskList(gid); - for (int j = 0; j < jsMetas.length(); j++) { - object = (JSONObject) jsMetas.getJSONObject(j); - MetaData metaData = new MetaData(); - metaData.setContentByRemoteJSON(object); - if (metaData.isWorthSaving()) { - mMetaList.addChildTask(metaData); - if (metaData.getGid() != null) { - mMetaHashMap.put(metaData.getRelatedGid(), metaData); - } - } - } - } - } - - // create meta list if not existed - if (mMetaList == null) { - mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META); - GTaskClient.getInstance().createTaskList(mMetaList); - } - - // init task list - for (int i = 0; i < jsTaskLists.length(); i++) { - JSONObject object = jsTaskLists.getJSONObject(i); - String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - - if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) - && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META)) { - TaskList tasklist = new TaskList(); - tasklist.setContentByRemoteJSON(object); - mGTaskListHashMap.put(gid, tasklist); - mGTaskHashMap.put(gid, tasklist); - - // load tasks - JSONArray jsTasks = client.getTaskList(gid); - for (int j = 0; j < jsTasks.length(); j++) { - object = (JSONObject) jsTasks.getJSONObject(j); - gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - Task task = new Task(); - task.setContentByRemoteJSON(object); - if (task.isWorthSaving()) { - task.setMetaInfo(mMetaHashMap.get(gid)); - tasklist.addChildTask(task); - mGTaskHashMap.put(gid, task); - } - } - } - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("initGTaskList: handing JSONObject failed"); - } - } - - private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; - - mLocalDeleteIdMap.clear(); - - if (mCancelled) { - return; - } - - // for local deleted note - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type<>? AND parent_id=?)", new String[] { - String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) - }, null); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); - } - - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); - } - } else { - Log.w(TAG, "failed to query trash folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // sync folder first - syncFolder(); - - // for note existing in database - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type=? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); - mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c); - } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // remote delete - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } - } - doContentSync(syncType, node, c); - } - } else { - Log.w(TAG, "failed to query existing note in database"); - } - - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // go through remaining items - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - node = entry.getValue(); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - - // mCancelled can be set by another thread, so we neet to check one by - // one - // clear local delete table - if (!mCancelled) { - if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { - throw new ActionFailureException("failed to batch-delete local deleted notes"); - } - } - - // refresh local sync id - if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); - } - - } - - private void syncFolder() throws NetworkFailureException { - Cursor c = null; - String gid; - Node node; - int syncType; - - if (mCancelled) { - return; - } - - // for root folder - try { - c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); - if (c != null) { - c.moveToNext(); - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); - mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // for system folder, only update remote name if necessary - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } else { - Log.w(TAG, "failed to query root folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // for call-note folder - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(Notes.ID_CALL_RECORD_FOLDER) - }, null); - if (c != null) { - if (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); - mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // for system folder, only update remote name if - // necessary - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } - } else { - Log.w(TAG, "failed to query call note folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // for local existing folders - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type=? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); - mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c); - } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // remote delete - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } - } - doContentSync(syncType, node, c); - } - } else { - Log.w(TAG, "failed to query existing folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // for remote add folders - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - gid = entry.getKey(); - node = entry.getValue(); - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - } - - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); - } - - private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - MetaData meta; - switch (syncType) { - case Node.SYNC_ACTION_ADD_LOCAL: - addLocalNode(node); - break; - case Node.SYNC_ACTION_ADD_REMOTE: - addRemoteNode(node, c); - break; - case Node.SYNC_ACTION_DEL_LOCAL: - meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); - if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); - } - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); - break; - case Node.SYNC_ACTION_DEL_REMOTE: - meta = mMetaHashMap.get(node.getGid()); - if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); - } - GTaskClient.getInstance().deleteNode(node); - break; - case Node.SYNC_ACTION_UPDATE_LOCAL: - updateLocalNode(node, c); - break; - case Node.SYNC_ACTION_UPDATE_REMOTE: - updateRemoteNode(node, c); - break; - case Node.SYNC_ACTION_UPDATE_CONFLICT: - // merging both modifications maybe a good idea - // right now just use local update simply - updateRemoteNode(node, c); - break; - case Node.SYNC_ACTION_NONE: - break; - case Node.SYNC_ACTION_ERROR: - default: - throw new ActionFailureException("unkown sync action type"); - } - } - - private void addLocalNode(Node node) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote; - if (node instanceof TaskList) { - if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { - sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); - } else if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { - sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); - } else { - sqlNote = new SqlNote(mContext); - sqlNote.setContent(node.getLocalJSONFromContent()); - sqlNote.setParentId(Notes.ID_ROOT_FOLDER); - } - } else { - sqlNote = new SqlNote(mContext); - JSONObject js = node.getLocalJSONFromContent(); - try { - if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - if (note.has(NoteColumns.ID)) { - long id = note.getLong(NoteColumns.ID); - if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // the id is not available, have to create a new one - note.remove(NoteColumns.ID); - } - } - } - - if (js.has(GTaskStringUtils.META_HEAD_DATA)) { - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - if (data.has(DataColumns.ID)) { - long dataId = data.getLong(DataColumns.ID); - if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // the data id is not available, have to create - // a new one - data.remove(DataColumns.ID); - } - } - } - - } - } catch (JSONException e) { - Log.w(TAG, e.toString()); - e.printStackTrace(); - } - sqlNote.setContent(js); - - Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot add local node"); - } - sqlNote.setParentId(parentId.longValue()); - } - - // create the local node - sqlNote.setGtaskId(node.getGid()); - sqlNote.commit(false); - - // update gid-nid mapping - mGidToNid.put(node.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), node.getGid()); - - // update meta - updateRemoteMeta(node.getGid(), sqlNote); - } - - private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote; - // update the note locally - sqlNote = new SqlNote(mContext, c); - sqlNote.setContent(node.getLocalJSONFromContent()); - - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) - : new Long(Notes.ID_ROOT_FOLDER); - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot update local node"); - } - sqlNote.setParentId(parentId.longValue()); - sqlNote.commit(true); - - // update meta info - updateRemoteMeta(node.getGid(), sqlNote); - } - - private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; - - // update remotely - if (sqlNote.isNoteType()) { - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); - - String parentGid = mNidToGid.get(sqlNote.getParentId()); - if (parentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot add remote task"); - } - mGTaskListHashMap.get(parentGid).addChildTask(task); - - GTaskClient.getInstance().createTask(task); - n = (Node) task; - - // add meta - updateRemoteMeta(task.getGid(), sqlNote); - } else { - TaskList tasklist = null; - - // we need to skip folder if it has already existed - String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; - if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) - folderName += GTaskStringUtils.FOLDER_DEFAULT; - else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) - folderName += GTaskStringUtils.FOLDER_CALL_NOTE; - else - folderName += sqlNote.getSnippet(); - - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - String gid = entry.getKey(); - TaskList list = entry.getValue(); - - if (list.getName().equals(folderName)) { - tasklist = list; - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - } - break; - } - } - - // no match we can add now - if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); - mGTaskListHashMap.put(tasklist.getGid(), tasklist); - } - n = (Node) tasklist; - } - - // update local note - sqlNote.setGtaskId(n.getGid()); - sqlNote.commit(false); - sqlNote.resetLocalModified(); - sqlNote.commit(true); - - // gid-id mapping - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); - } - - private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; - } - - SqlNote sqlNote = new SqlNote(mContext, c); - - // update remotely - node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); - - // update meta - updateRemoteMeta(node.getGid(), sqlNote); - - // move task if necessary - if (sqlNote.isNoteType()) { - Task task = (Task) node; - TaskList preParentList = task.getParent(); - - String curParentGid = mNidToGid.get(sqlNote.getParentId()); - if (curParentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot update remote task"); - } - TaskList curParentList = mGTaskListHashMap.get(curParentGid); - - if (preParentList != curParentList) { - preParentList.removeChildTask(task); - curParentList.addChildTask(task); - GTaskClient.getInstance().moveTask(task, preParentList, curParentList); - } - } - - // clear local modified flag - sqlNote.resetLocalModified(); - sqlNote.commit(true); - } - - private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { - if (sqlNote != null && sqlNote.isNoteType()) { - MetaData metaData = mMetaHashMap.get(gid); - if (metaData != null) { - metaData.setMeta(gid, sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(metaData); - } else { - metaData = new MetaData(); - metaData.setMeta(gid, sqlNote.getContent()); - mMetaList.addChildTask(metaData); - mMetaHashMap.put(gid, metaData); - GTaskClient.getInstance().createTask(metaData); - } - } - } - - private void refreshLocalSyncId() throws NetworkFailureException { - if (mCancelled) { - return; - } - - // get the latest gtask list - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - initGTaskList(); - - Cursor c = null; - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type<>? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - Node node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - ContentValues values = new ContentValues(); - values.put(NoteColumns.SYNC_ID, node.getLastModified()); - mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - c.getLong(SqlNote.ID_COLUMN)), values, null, null); - } else { - Log.e(TAG, "something is missed"); - throw new ActionFailureException( - "some local items don't have gid after sync"); - } - } - } else { - Log.w(TAG, "failed to query local note to refresh sync id"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - } - - public String getSyncAccount() { - return GTaskClient.getInstance().getSyncAccount().name; - } - - public void cancelSync() { - mCancelled = true; - } -} diff --git a/src/gtask/remote/GTaskSyncService.java b/src/gtask/remote/GTaskSyncService.java deleted file mode 100644 index cca36f7..0000000 --- a/src/gtask/remote/GTaskSyncService.java +++ /dev/null @@ -1,128 +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.gtask.remote; - -import android.app.Activity; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; - -public class GTaskSyncService extends Service { - public final static String ACTION_STRING_NAME = "sync_action_type"; - - public final static int ACTION_START_SYNC = 0; - - public final static int ACTION_CANCEL_SYNC = 1; - - public final static int ACTION_INVALID = 2; - - public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - - public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - - public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - - private static GTaskASyncTask mSyncTask = null; - - private static String mSyncProgress = ""; - - private void startSync() { - if (mSyncTask == null) { - mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { - public void onComplete() { - mSyncTask = null; - sendBroadcast(""); - stopSelf(); - } - }); - sendBroadcast(""); - mSyncTask.execute(); - } - } - - private void cancelSync() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } - } - - @Override - public void onCreate() { - mSyncTask = null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { - switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { - case ACTION_START_SYNC: - startSync(); - break; - case ACTION_CANCEL_SYNC: - cancelSync(); - break; - default: - break; - } - return START_STICKY; - } - return super.onStartCommand(intent, flags, startId); - } - - @Override - public void onLowMemory() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } - } - - public IBinder onBind(Intent intent) { - return null; - } - - public void sendBroadcast(String msg) { - mSyncProgress = msg; - Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); - intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); - intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); - sendBroadcast(intent); - } - - public static void startSync(Activity activity) { - GTaskManager.getInstance().setActivityContext(activity); - Intent intent = new Intent(activity, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); - activity.startService(intent); - } - - public static void cancelSync(Context context) { - Intent intent = new Intent(context, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); - context.startService(intent); - } - - public static boolean isSyncing() { - return mSyncTask != null; - } - - public static String getProgressString() { - return mSyncProgress; - } -} diff --git a/src/model/Note.java b/src/model/Note.java deleted file mode 100644 index 6706cf6..0000000 --- a/src/model/Note.java +++ /dev/null @@ -1,253 +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.model; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.net.Uri; -import android.os.RemoteException; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; - -import java.util.ArrayList; - - -public class Note { - private ContentValues mNoteDiffValues; - private NoteData mNoteData; - private static final String TAG = "Note"; - /** - * Create a new note id for adding a new note to databases - */ - public static synchronized long getNewNoteId(Context context, long folderId) { - // Create a new note in the database - ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis(); - values.put(NoteColumns.CREATED_DATE, createdTime); - values.put(NoteColumns.MODIFIED_DATE, createdTime); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - values.put(NoteColumns.PARENT_ID, folderId); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - - long noteId = 0; - try { - noteId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - noteId = 0; - } - if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId); - } - return noteId; - } - - public Note() { - mNoteDiffValues = new ContentValues(); - mNoteData = new NoteData(); - } - - public void setNoteValue(String key, String value) { - mNoteDiffValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - public void setTextData(String key, String value) { - mNoteData.setTextData(key, value); - } - - public void setTextDataId(long id) { - mNoteData.setTextDataId(id); - } - - public long getTextDataId() { - return mNoteData.mTextDataId; - } - - public void setCallDataId(long id) { - mNoteData.setCallDataId(id); - } - - public void setCallData(String key, String value) { - mNoteData.setCallData(key, value); - } - - public boolean isLocalModified() { - return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); - } - - public boolean syncNote(Context context, long noteId) { - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - if (!isLocalModified()) { - return true; - } - - /** - * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and - * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the - * note data info - */ - if (context.getContentResolver().update( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, - null) == 0) { - Log.e(TAG, "Update note error, should not happen"); - // Do not return, fall through - } - mNoteDiffValues.clear(); - - if (mNoteData.isLocalModified() - && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { - return false; - } - - return true; - } - - private class NoteData { - private long mTextDataId; - - private ContentValues mTextDataValues; - - private long mCallDataId; - - private ContentValues mCallDataValues; - - private static final String TAG = "NoteData"; - - public NoteData() { - mTextDataValues = new ContentValues(); - mCallDataValues = new ContentValues(); - mTextDataId = 0; - mCallDataId = 0; - } - - boolean isLocalModified() { - return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; - } - - void setTextDataId(long id) { - if(id <= 0) { - throw new IllegalArgumentException("Text data id should larger than 0"); - } - mTextDataId = id; - } - - void setCallDataId(long id) { - if (id <= 0) { - throw new IllegalArgumentException("Call data id should larger than 0"); - } - mCallDataId = id; - } - - void setCallData(String key, String value) { - mCallDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - void setTextData(String key, String value) { - mTextDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - Uri pushIntoContentResolver(Context context, long noteId) { - /** - * Check for safety - */ - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - ArrayList operationList = new ArrayList(); - ContentProviderOperation.Builder builder = null; - - if(mTextDataValues.size() > 0) { - mTextDataValues.put(DataColumns.NOTE_ID, noteId); - if (mTextDataId == 0) { - mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mTextDataValues); - try { - setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new text data fail with noteId" + noteId); - mTextDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mTextDataId)); - builder.withValues(mTextDataValues); - operationList.add(builder.build()); - } - mTextDataValues.clear(); - } - - if(mCallDataValues.size() > 0) { - mCallDataValues.put(DataColumns.NOTE_ID, noteId); - if (mCallDataId == 0) { - mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mCallDataValues); - try { - setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new call data fail with noteId" + noteId); - mCallDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mCallDataId)); - builder.withValues(mCallDataValues); - operationList.add(builder.build()); - } - mCallDataValues.clear(); - } - - if (operationList.size() > 0) { - try { - ContentProviderResult[] results = context.getContentResolver().applyBatch( - Notes.AUTHORITY, operationList); - return (results == null || results.length == 0 || results[0] == null) ? null - : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } - } - return null; - } - } -} diff --git a/src/model/WorkingNote.java b/src/model/WorkingNote.java deleted file mode 100644 index be081e4..0000000 --- a/src/model/WorkingNote.java +++ /dev/null @@ -1,368 +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.model; - -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.tool.ResourceParser.NoteBgResources; - - -public class WorkingNote { - // Note for the working note - private Note mNote; - // Note Id - private long mNoteId; - // Note content - private String mContent; - // Note mode - private int mMode; - - private long mAlertDate; - - private long mModifiedDate; - - private int mBgColorId; - - private int mWidgetId; - - private int mWidgetType; - - private long mFolderId; - - private Context mContext; - - private static final String TAG = "WorkingNote"; - - private boolean mIsDeleted; - - private NoteSettingChangedListener mNoteSettingStatusListener; - - public static final String[] DATA_PROJECTION = new String[] { - DataColumns.ID, - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - }; - - public static final String[] NOTE_PROJECTION = new String[] { - NoteColumns.PARENT_ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - NoteColumns.MODIFIED_DATE - }; - - private static final int DATA_ID_COLUMN = 0; - - private static final int DATA_CONTENT_COLUMN = 1; - - private static final int DATA_MIME_TYPE_COLUMN = 2; - - private static final int DATA_MODE_COLUMN = 3; - - private static final int NOTE_PARENT_ID_COLUMN = 0; - - private static final int NOTE_ALERTED_DATE_COLUMN = 1; - - private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - - private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - - private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - - // New note construct - private WorkingNote(Context context, long folderId) { - mContext = context; - mAlertDate = 0; - mModifiedDate = System.currentTimeMillis(); - mFolderId = folderId; - mNote = new Note(); - mNoteId = 0; - mIsDeleted = false; - mMode = 0; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - } - - // Existing note construct - private WorkingNote(Context context, long noteId, long folderId) { - mContext = context; - mNoteId = noteId; - mFolderId = folderId; - mIsDeleted = false; - mNote = new Note(); - loadNote(); - } - - private void loadNote() { - Cursor cursor = mContext.getContentResolver().query( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, - null, null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); - mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); - mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); - mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); - mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); - } - cursor.close(); - } else { - Log.e(TAG, "No note with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note with id " + mNoteId); - } - loadNoteData(); - } - - private void loadNoteData() { - Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, - DataColumns.NOTE_ID + "=?", new String[] { - String.valueOf(mNoteId) - }, null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - do { - String type = cursor.getString(DATA_MIME_TYPE_COLUMN); - if (DataConstants.NOTE.equals(type)) { - mContent = cursor.getString(DATA_CONTENT_COLUMN); - mMode = cursor.getInt(DATA_MODE_COLUMN); - mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); - } else if (DataConstants.CALL_NOTE.equals(type)) { - mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); - } else { - Log.d(TAG, "Wrong note type with type:" + type); - } - } while (cursor.moveToNext()); - } - cursor.close(); - } else { - Log.e(TAG, "No data with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); - } - } - - public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, - int widgetType, int defaultBgColorId) { - WorkingNote note = new WorkingNote(context, folderId); - note.setBgColorId(defaultBgColorId); - note.setWidgetId(widgetId); - note.setWidgetType(widgetType); - return note; - } - - public static WorkingNote load(Context context, long id) { - return new WorkingNote(context, id, 0); - } - - public synchronized boolean saveNote() { - if (isWorthSaving()) { - if (!existInDatabase()) { - if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { - Log.e(TAG, "Create new note fail with id:" + mNoteId); - return false; - } - } - - mNote.syncNote(mContext, mNoteId); - - /** - * Update widget content if there exist any widget of this note - */ - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE - && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - return true; - } else { - return false; - } - } - - public boolean existInDatabase() { - return mNoteId > 0; - } - - private boolean isWorthSaving() { - if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) - || (existInDatabase() && !mNote.isLocalModified())) { - return false; - } else { - return true; - } - } - - public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { - mNoteSettingStatusListener = l; - } - - public void setAlertDate(long date, boolean set) { - if (date != mAlertDate) { - mAlertDate = date; - mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); - } - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onClockAlertChanged(date, set); - } - } - - public void markDeleted(boolean mark) { - mIsDeleted = mark; - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - } - - public void setBgColorId(int id) { - if (id != mBgColorId) { - mBgColorId = id; - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onBackgroundColorChanged(); - } - mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); - } - } - - public void setCheckListMode(int mode) { - if (mMode != mode) { - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); - } - mMode = mode; - mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); - } - } - - public void setWidgetType(int type) { - if (type != mWidgetType) { - mWidgetType = type; - mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); - } - } - - public void setWidgetId(int id) { - if (id != mWidgetId) { - mWidgetId = id; - mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); - } - } - - public void setWorkingText(String text) { - if (!TextUtils.equals(mContent, text)) { - mContent = text; - mNote.setTextData(DataColumns.CONTENT, mContent); - } - } - - public void convertToCallNote(String phoneNumber, long callDate) { - mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); - mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); - mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); - } - - public boolean hasClockAlert() { - return (mAlertDate > 0 ? true : false); - } - - public String getContent() { - return mContent; - } - - public long getAlertDate() { - return mAlertDate; - } - - public long getModifiedDate() { - return mModifiedDate; - } - - public int getBgColorResId() { - return NoteBgResources.getNoteBgResource(mBgColorId); - } - - public int getBgColorId() { - return mBgColorId; - } - - public int getTitleBgResId() { - return NoteBgResources.getNoteTitleBgResource(mBgColorId); - } - - public int getCheckListMode() { - return mMode; - } - - public long getNoteId() { - return mNoteId; - } - - public long getFolderId() { - return mFolderId; - } - - public int getWidgetId() { - return mWidgetId; - } - - public int getWidgetType() { - return mWidgetType; - } - - public interface NoteSettingChangedListener { - /** - * Called when the background color of current note has just changed - */ - void onBackgroundColorChanged(); - - /** - * Called when user set clock - */ - void onClockAlertChanged(long date, boolean set); - - /** - * Call when user create note from widget - */ - void onWidgetChanged(); - - /** - * Call when switch between check list mode and normal mode - * @param oldMode is previous mode before change - * @param newMode is new mode - */ - void onCheckListModeChanged(int oldMode, int newMode); - } -} diff --git a/src/tool/BackupUtils.java b/src/tool/BackupUtils.java deleted file mode 100644 index 39f6ec4..0000000 --- a/src/tool/BackupUtils.java +++ /dev/null @@ -1,344 +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.tool; - -import android.content.Context; -import android.database.Cursor; -import android.os.Environment; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.util.Log; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - - -public class BackupUtils { - private static final String TAG = "BackupUtils"; - // Singleton stuff - private static BackupUtils sInstance; - - public static synchronized BackupUtils getInstance(Context context) { - if (sInstance == null) { - sInstance = new BackupUtils(context); - } - return sInstance; - } - - /** - * Following states are signs to represents backup or restore - * status - */ - // Currently, the sdcard is not mounted - public static final int STATE_SD_CARD_UNMOUONTED = 0; - // The backup file not exist - public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; - // The data is not well formated, may be changed by other programs - public static final int STATE_DATA_DESTROIED = 2; - // Some run-time exception which causes restore or backup fails - public static final int STATE_SYSTEM_ERROR = 3; - // Backup or restore success - public static final int STATE_SUCCESS = 4; - - private TextExport mTextExport; - - private BackupUtils(Context context) { - mTextExport = new TextExport(context); - } - - private static boolean externalStorageAvailable() { - return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); - } - - public int exportToText() { - return mTextExport.exportToText(); - } - - public String getExportedTextFileName() { - return mTextExport.mFileName; - } - - public String getExportedTextFileDir() { - return mTextExport.mFileDirectory; - } - - private static class TextExport { - private static final String[] NOTE_PROJECTION = { - NoteColumns.ID, - NoteColumns.MODIFIED_DATE, - NoteColumns.SNIPPET, - NoteColumns.TYPE - }; - - private static final int NOTE_COLUMN_ID = 0; - - private static final int NOTE_COLUMN_MODIFIED_DATE = 1; - - private static final int NOTE_COLUMN_SNIPPET = 2; - - private static final String[] DATA_PROJECTION = { - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - }; - - private static final int DATA_COLUMN_CONTENT = 0; - - private static final int DATA_COLUMN_MIME_TYPE = 1; - - private static final int DATA_COLUMN_CALL_DATE = 2; - - private static final int DATA_COLUMN_PHONE_NUMBER = 4; - - private final String [] TEXT_FORMAT; - private static final int FORMAT_FOLDER_NAME = 0; - private static final int FORMAT_NOTE_DATE = 1; - private static final int FORMAT_NOTE_CONTENT = 2; - - private Context mContext; - private String mFileName; - private String mFileDirectory; - - public TextExport(Context context) { - TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); - mContext = context; - mFileName = ""; - mFileDirectory = ""; - } - - private String getFormat(int id) { - return TEXT_FORMAT[id]; - } - - /** - * Export the folder identified by folder id to text - */ - private void exportFolderToText(String folderId, PrintStream ps) { - // Query notes belong to this folder - Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { - folderId - }, null); - - if (notesCursor != null) { - if (notesCursor.moveToFirst()) { - do { - // Print note's last modified date - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note - String noteId = notesCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (notesCursor.moveToNext()); - } - notesCursor.close(); - } - } - - /** - * Export note identified by id to a print stream - */ - private void exportNoteToText(String noteId, PrintStream ps) { - Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, - DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { - noteId - }, null); - - if (dataCursor != null) { - if (dataCursor.moveToFirst()) { - do { - String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); - if (DataConstants.CALL_NOTE.equals(mimeType)) { - // Print phone number - String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); - long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); - String location = dataCursor.getString(DATA_COLUMN_CONTENT); - - if (!TextUtils.isEmpty(phoneNumber)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - phoneNumber)); - } - // Print call date - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat - .format(mContext.getString(R.string.format_datetime_mdhm), - callDate))); - // Print call attachment location - if (!TextUtils.isEmpty(location)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - location)); - } - } else if (DataConstants.NOTE.equals(mimeType)) { - String content = dataCursor.getString(DATA_COLUMN_CONTENT); - if (!TextUtils.isEmpty(content)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - content)); - } - } - } while (dataCursor.moveToNext()); - } - dataCursor.close(); - } - // print a line separator between note - try { - ps.write(new byte[] { - Character.LINE_SEPARATOR, Character.LETTER_NUMBER - }); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } - } - - /** - * Note will be exported as text which is user readable - */ - public int exportToText() { - if (!externalStorageAvailable()) { - Log.d(TAG, "Media was not mounted"); - return STATE_SD_CARD_UNMOUONTED; - } - - PrintStream ps = getExportToTextPrintStream(); - if (ps == null) { - Log.e(TAG, "get print stream error"); - return STATE_SYSTEM_ERROR; - } - // First export folder and its notes - Cursor folderCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, - "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " - + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); - - if (folderCursor != null) { - if (folderCursor.moveToFirst()) { - do { - // Print folder's name - String folderName = ""; - if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { - folderName = mContext.getString(R.string.call_record_folder_name); - } else { - folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); - } - if (!TextUtils.isEmpty(folderName)) { - ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); - } - String folderId = folderCursor.getString(NOTE_COLUMN_ID); - exportFolderToText(folderId, ps); - } while (folderCursor.moveToNext()); - } - folderCursor.close(); - } - - // Export notes in root's folder - Cursor noteCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, - NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID - + "=0", null, null); - - if (noteCursor != null) { - if (noteCursor.moveToFirst()) { - do { - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note - String noteId = noteCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (noteCursor.moveToNext()); - } - noteCursor.close(); - } - ps.close(); - - return STATE_SUCCESS; - } - - /** - * Get a print stream pointed to the file {@generateExportedTextFile} - */ - private PrintStream getExportToTextPrintStream() { - File file = generateFileMountedOnSDcard(mContext, R.string.file_path, - R.string.file_name_txt_format); - if (file == null) { - Log.e(TAG, "create file to exported failed"); - return null; - } - mFileName = file.getName(); - mFileDirectory = mContext.getString(R.string.file_path); - PrintStream ps = null; - try { - FileOutputStream fos = new FileOutputStream(file); - ps = new PrintStream(fos); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } catch (NullPointerException e) { - e.printStackTrace(); - return null; - } - return ps; - } - } - - /** - * Generate the text file to store imported data - */ - private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { - StringBuilder sb = new StringBuilder(); - sb.append(Environment.getExternalStorageDirectory()); - sb.append(context.getString(filePathResId)); - File filedir = new File(sb.toString()); - sb.append(context.getString( - fileNameFormatResId, - DateFormat.format(context.getString(R.string.format_date_ymd), - System.currentTimeMillis()))); - File file = new File(sb.toString()); - - try { - if (!filedir.exists()) { - filedir.mkdir(); - } - if (!file.exists()) { - file.createNewFile(); - } - return file; - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; - } -} - - diff --git a/src/tool/DataUtils.java b/src/tool/DataUtils.java deleted file mode 100644 index 2a14982..0000000 --- a/src/tool/DataUtils.java +++ /dev/null @@ -1,295 +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.tool; - -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.os.RemoteException; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; - -import java.util.ArrayList; -import java.util.HashSet; - - -public class DataUtils { - public static final String TAG = "DataUtils"; - public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { - if (ids == null) { - Log.d(TAG, "the ids is null"); - return true; - } - if (ids.size() == 0) { - Log.d(TAG, "no id is in the hashset"); - return true; - } - - ArrayList operationList = new ArrayList(); - for (long id : ids) { - if(id == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "Don't delete system folder root"); - continue; - } - ContentProviderOperation.Builder builder = ContentProviderOperation - .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - operationList.add(builder.build()); - } - try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } - return false; - } - - public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.PARENT_ID, desFolderId); - values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); - } - - public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, - long folderId) { - if (ids == null) { - Log.d(TAG, "the ids is null"); - return true; - } - - ArrayList operationList = new ArrayList(); - for (long id : ids) { - ContentProviderOperation.Builder builder = ContentProviderOperation - .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - builder.withValue(NoteColumns.PARENT_ID, folderId); - builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); - operationList.add(builder.build()); - } - - try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } - return false; - } - - /** - * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} - */ - public static int getUserFolderCount(ContentResolver resolver) { - Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { "COUNT(*)" }, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, - null); - - int count = 0; - if(cursor != null) { - if(cursor.moveToFirst()) { - try { - count = cursor.getInt(0); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "get folder count failed:" + e.toString()); - } finally { - cursor.close(); - } - } - } - return count; - } - - public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), - null, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String [] {String.valueOf(type)}, - null); - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), - null, null, null, null); - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), - null, null, null, null); - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, - NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", - new String[] { name }, null); - boolean exist = false; - if(cursor != null) { - if(cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, - NoteColumns.PARENT_ID + "=?", - new String[] { String.valueOf(folderId) }, - null); - - HashSet set = null; - if (c != null) { - if (c.moveToFirst()) { - set = new HashSet(); - do { - try { - AppWidgetAttribute widget = new AppWidgetAttribute(); - widget.widgetId = c.getInt(0); - widget.widgetType = c.getInt(1); - set.add(widget); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, e.toString()); - } - } while (c.moveToNext()); - } - c.close(); - } - return set; - } - - public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.PHONE_NUMBER }, - CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", - new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, - null); - - if (cursor != null && cursor.moveToFirst()) { - try { - return cursor.getString(0); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call number fails " + e.toString()); - } finally { - cursor.close(); - } - } - return ""; - } - - public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.NOTE_ID }, - CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", - new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, - null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - try { - return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call note id fails " + e.toString()); - } - } - cursor.close(); - } - return 0; - } - - public static String getSnippetById(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, - new String [] { NoteColumns.SNIPPET }, - NoteColumns.ID + "=?", - new String [] { String.valueOf(noteId)}, - null); - - if (cursor != null) { - String snippet = ""; - if (cursor.moveToFirst()) { - snippet = cursor.getString(0); - } - cursor.close(); - return snippet; - } - throw new IllegalArgumentException("Note is not found with id: " + noteId); - } - - public static String getFormattedSnippet(String snippet) { - if (snippet != null) { - snippet = snippet.trim(); - int index = snippet.indexOf('\n'); - if (index != -1) { - snippet = snippet.substring(0, index); - } - } - return snippet; - } -} diff --git a/src/tool/GTaskStringUtils.java b/src/tool/GTaskStringUtils.java deleted file mode 100644 index 666b729..0000000 --- a/src/tool/GTaskStringUtils.java +++ /dev/null @@ -1,113 +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.tool; - -public class GTaskStringUtils { - - public final static String GTASK_JSON_ACTION_ID = "action_id"; - - public final static String GTASK_JSON_ACTION_LIST = "action_list"; - - public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - - public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - - public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - - public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - - public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - - public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - - public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - - public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - - public final static String GTASK_JSON_COMPLETED = "completed"; - - public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - - public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - - public final static String GTASK_JSON_DELETED = "deleted"; - - public final static String GTASK_JSON_DEST_LIST = "dest_list"; - - public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - - public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - - public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - - public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - - public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - - public final static String GTASK_JSON_ID = "id"; - - public final static String GTASK_JSON_INDEX = "index"; - - public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - - public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - - public final static String GTASK_JSON_LIST_ID = "list_id"; - - public final static String GTASK_JSON_LISTS = "lists"; - - public final static String GTASK_JSON_NAME = "name"; - - public final static String GTASK_JSON_NEW_ID = "new_id"; - - public final static String GTASK_JSON_NOTES = "notes"; - - public final static String GTASK_JSON_PARENT_ID = "parent_id"; - - public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - - public final static String GTASK_JSON_RESULTS = "results"; - - public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - - public final static String GTASK_JSON_TASKS = "tasks"; - - public final static String GTASK_JSON_TYPE = "type"; - - public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - - public final static String GTASK_JSON_TYPE_TASK = "TASK"; - - public final static String GTASK_JSON_USER = "user"; - - public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - - public final static String FOLDER_DEFAULT = "Default"; - - public final static String FOLDER_CALL_NOTE = "Call_Note"; - - public final static String FOLDER_META = "METADATA"; - - public final static String META_HEAD_GTASK_ID = "meta_gid"; - - public final static String META_HEAD_NOTE = "meta_note"; - - public final static String META_HEAD_DATA = "meta_data"; - - public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; - -} diff --git a/src/tool/ResourceParser.java b/src/tool/ResourceParser.java deleted file mode 100644 index 1ad3ad6..0000000 --- a/src/tool/ResourceParser.java +++ /dev/null @@ -1,181 +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.tool; - -import android.content.Context; -import android.preference.PreferenceManager; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesPreferenceActivity; - -public class ResourceParser { - - public static final int YELLOW = 0; - public static final int BLUE = 1; - public static final int WHITE = 2; - public static final int GREEN = 3; - public static final int RED = 4; - - public static final int BG_DEFAULT_COLOR = YELLOW; - - public static final int TEXT_SMALL = 0; - public static final int TEXT_MEDIUM = 1; - public static final int TEXT_LARGE = 2; - public static final int TEXT_SUPER = 3; - - public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; - - public static class NoteBgResources { - private final static int [] BG_EDIT_RESOURCES = new int [] { - R.drawable.edit_yellow, - R.drawable.edit_blue, - R.drawable.edit_white, - R.drawable.edit_green, - R.drawable.edit_red - }; - - private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { - R.drawable.edit_title_yellow, - R.drawable.edit_title_blue, - R.drawable.edit_title_white, - R.drawable.edit_title_green, - R.drawable.edit_title_red - }; - - public static int getNoteBgResource(int id) { - return BG_EDIT_RESOURCES[id]; - } - - public static int getNoteTitleBgResource(int id) { - return BG_EDIT_TITLE_RESOURCES[id]; - } - } - - public static int getDefaultBgId(Context context) { - if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { - return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); - } else { - return BG_DEFAULT_COLOR; - } - } - - public static class NoteItemBgResources { - private final static int [] BG_FIRST_RESOURCES = new int [] { - R.drawable.list_yellow_up, - R.drawable.list_blue_up, - R.drawable.list_white_up, - R.drawable.list_green_up, - R.drawable.list_red_up - }; - - private final static int [] BG_NORMAL_RESOURCES = new int [] { - R.drawable.list_yellow_middle, - R.drawable.list_blue_middle, - R.drawable.list_white_middle, - R.drawable.list_green_middle, - R.drawable.list_red_middle - }; - - private final static int [] BG_LAST_RESOURCES = new int [] { - R.drawable.list_yellow_down, - R.drawable.list_blue_down, - R.drawable.list_white_down, - R.drawable.list_green_down, - R.drawable.list_red_down, - }; - - private final static int [] BG_SINGLE_RESOURCES = new int [] { - R.drawable.list_yellow_single, - R.drawable.list_blue_single, - R.drawable.list_white_single, - R.drawable.list_green_single, - R.drawable.list_red_single - }; - - public static int getNoteBgFirstRes(int id) { - return BG_FIRST_RESOURCES[id]; - } - - public static int getNoteBgLastRes(int id) { - return BG_LAST_RESOURCES[id]; - } - - public static int getNoteBgSingleRes(int id) { - return BG_SINGLE_RESOURCES[id]; - } - - public static int getNoteBgNormalRes(int id) { - return BG_NORMAL_RESOURCES[id]; - } - - public static int getFolderBgRes() { - return R.drawable.list_folder; - } - } - - public static class WidgetBgResources { - private final static int [] BG_2X_RESOURCES = new int [] { - R.drawable.widget_2x_yellow, - R.drawable.widget_2x_blue, - R.drawable.widget_2x_white, - R.drawable.widget_2x_green, - R.drawable.widget_2x_red, - }; - - public static int getWidget2xBgResource(int id) { - return BG_2X_RESOURCES[id]; - } - - private final static int [] BG_4X_RESOURCES = new int [] { - R.drawable.widget_4x_yellow, - R.drawable.widget_4x_blue, - R.drawable.widget_4x_white, - R.drawable.widget_4x_green, - R.drawable.widget_4x_red - }; - - public static int getWidget4xBgResource(int id) { - return BG_4X_RESOURCES[id]; - } - } - - public static class TextAppearanceResources { - private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper - }; - - public static int getTexAppearanceResource(int id) { - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ - if (id >= TEXTAPPEARANCE_RESOURCES.length) { - return BG_DEFAULT_FONT_SIZE; - } - return TEXTAPPEARANCE_RESOURCES[id]; - } - - public static int getResourcesSize() { - return TEXTAPPEARANCE_RESOURCES.length; - } - } -} diff --git a/src/ui/AlarmAlertActivity.java b/src/ui/AlarmAlertActivity.java deleted file mode 100644 index 85723be..0000000 --- a/src/ui/AlarmAlertActivity.java +++ /dev/null @@ -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; - } - } -} diff --git a/src/ui/AlarmInitReceiver.java b/src/ui/AlarmInitReceiver.java deleted file mode 100644 index f221202..0000000 --- a/src/ui/AlarmInitReceiver.java +++ /dev/null @@ -1,65 +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.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; - - -public class AlarmInitReceiver extends BroadcastReceiver { - - private static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE - }; - - private static final int COLUMN_ID = 0; - private static final int COLUMN_ALERTED_DATE = 1; - - @Override - public void onReceive(Context context, Intent intent) { - long currentDate = System.currentTimeMillis(); - Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, - PROJECTION, - NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, - new String[] { String.valueOf(currentDate) }, - null); - - if (c != null) { - if (c.moveToFirst()) { - do { - long alertDate = c.getLong(COLUMN_ALERTED_DATE); - Intent sender = new Intent(context, AlarmReceiver.class); - sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); - AlarmManager alermManager = (AlarmManager) context - .getSystemService(Context.ALARM_SERVICE); - alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); - } while (c.moveToNext()); - } - c.close(); - } - } -} diff --git a/src/ui/AlarmReceiver.java b/src/ui/AlarmReceiver.java deleted file mode 100644 index 54e503b..0000000 --- a/src/ui/AlarmReceiver.java +++ /dev/null @@ -1,30 +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.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class AlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - intent.setClass(context, AlarmAlertActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } -} diff --git a/src/ui/DateTimePicker.java b/src/ui/DateTimePicker.java deleted file mode 100644 index 496b0cd..0000000 --- a/src/ui/DateTimePicker.java +++ /dev/null @@ -1,485 +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 java.text.DateFormatSymbols; -import java.util.Calendar; - -import net.micode.notes.R; - - -import android.content.Context; -import android.text.format.DateFormat; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.NumberPicker; - -public class DateTimePicker extends FrameLayout { - - private static final boolean DEFAULT_ENABLE_STATE = true; - - private static final int HOURS_IN_HALF_DAY = 12; - private static final int HOURS_IN_ALL_DAY = 24; - private static final int DAYS_IN_ALL_WEEK = 7; - private static final int DATE_SPINNER_MIN_VAL = 0; - private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; - private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; - private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; - private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; - private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; - private static final int MINUT_SPINNER_MIN_VAL = 0; - private static final int MINUT_SPINNER_MAX_VAL = 59; - private static final int AMPM_SPINNER_MIN_VAL = 0; - private static final int AMPM_SPINNER_MAX_VAL = 1; - - private final NumberPicker mDateSpinner; - private final NumberPicker mHourSpinner; - private final NumberPicker mMinuteSpinner; - private final NumberPicker mAmPmSpinner; - private Calendar mDate; - - private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; - - private boolean mIsAm; - - private boolean mIs24HourView; - - private boolean mIsEnabled = DEFAULT_ENABLE_STATE; - - private boolean mInitialising; - - private OnDateTimeChangedListener mOnDateTimeChangedListener; - - private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); - updateDateControl(); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - boolean isDateChanged = false; - Calendar cal = Calendar.getInstance(); - if (!mIs24HourView) { - if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || - oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - mIsAm = !mIsAm; - updateAmPmControl(); - } - } else { - if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - } - int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); - mDate.set(Calendar.HOUR_OF_DAY, newHour); - onDateTimeChanged(); - if (isDateChanged) { - setCurrentYear(cal.get(Calendar.YEAR)); - setCurrentMonth(cal.get(Calendar.MONTH)); - setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); - } - } - }; - - private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int minValue = mMinuteSpinner.getMinValue(); - int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; - if (oldVal == maxValue && newVal == minValue) { - offset += 1; - } else if (oldVal == minValue && newVal == maxValue) { - offset -= 1; - } - if (offset != 0) { - mDate.add(Calendar.HOUR_OF_DAY, offset); - mHourSpinner.setValue(getCurrentHour()); - updateDateControl(); - int newHour = getCurrentHourOfDay(); - if (newHour >= HOURS_IN_HALF_DAY) { - mIsAm = false; - updateAmPmControl(); - } else { - mIsAm = true; - updateAmPmControl(); - } - } - mDate.set(Calendar.MINUTE, newVal); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mIsAm = !mIsAm; - if (mIsAm) { - mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); - } else { - mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); - } - updateAmPmControl(); - onDateTimeChanged(); - } - }; - - public interface OnDateTimeChangedListener { - void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute); - } - - public DateTimePicker(Context context) { - this(context, System.currentTimeMillis()); - } - - public DateTimePicker(Context context, long date) { - this(context, date, DateFormat.is24HourFormat(context)); - } - - public DateTimePicker(Context context, long date, boolean is24HourView) { - super(context); - mDate = Calendar.getInstance(); - mInitialising = true; - mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; - inflate(context, R.layout.datetime_picker, this); - - mDateSpinner = (NumberPicker) findViewById(R.id.date); - mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); - mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); - mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); - - mHourSpinner = (NumberPicker) findViewById(R.id.hour); - mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); - mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); - mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); - mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); - mMinuteSpinner.setOnLongPressUpdateInterval(100); - mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); - - String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); - mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); - mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); - mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); - mAmPmSpinner.setDisplayedValues(stringsForAmPm); - mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); - - // update controls to initial state - updateDateControl(); - updateHourControl(); - updateAmPmControl(); - - set24HourView(is24HourView); - - // set to current time - setCurrentDate(date); - - setEnabled(isEnabled()); - - // set the content descriptions - mInitialising = false; - } - - @Override - public void setEnabled(boolean enabled) { - if (mIsEnabled == enabled) { - return; - } - super.setEnabled(enabled); - mDateSpinner.setEnabled(enabled); - mMinuteSpinner.setEnabled(enabled); - mHourSpinner.setEnabled(enabled); - mAmPmSpinner.setEnabled(enabled); - mIsEnabled = enabled; - } - - @Override - public boolean isEnabled() { - return mIsEnabled; - } - - /** - * Get the current date in millis - * - * @return the current date in millis - */ - public long getCurrentDateInTimeMillis() { - return mDate.getTimeInMillis(); - } - - /** - * Set the current date - * - * @param date The current date in millis - */ - public void setCurrentDate(long date) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(date); - setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); - } - - /** - * Set the current date - * - * @param year The current year - * @param month The current month - * @param dayOfMonth The current dayOfMonth - * @param hourOfDay The current hourOfDay - * @param minute The current minute - */ - public void setCurrentDate(int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - setCurrentYear(year); - setCurrentMonth(month); - setCurrentDay(dayOfMonth); - setCurrentHour(hourOfDay); - setCurrentMinute(minute); - } - - /** - * Get current year - * - * @return The current year - */ - public int getCurrentYear() { - return mDate.get(Calendar.YEAR); - } - - /** - * Set current year - * - * @param year The current year - */ - public void setCurrentYear(int year) { - if (!mInitialising && year == getCurrentYear()) { - return; - } - mDate.set(Calendar.YEAR, year); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current month in the year - * - * @return The current month in the year - */ - public int getCurrentMonth() { - return mDate.get(Calendar.MONTH); - } - - /** - * Set current month in the year - * - * @param month The month in the year - */ - public void setCurrentMonth(int month) { - if (!mInitialising && month == getCurrentMonth()) { - return; - } - mDate.set(Calendar.MONTH, month); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current day of the month - * - * @return The day of the month - */ - public int getCurrentDay() { - return mDate.get(Calendar.DAY_OF_MONTH); - } - - /** - * Set current day of the month - * - * @param dayOfMonth The day of the month - */ - public void setCurrentDay(int dayOfMonth) { - if (!mInitialising && dayOfMonth == getCurrentDay()) { - return; - } - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current hour in 24 hour mode, in the range (0~23) - * @return The current hour in 24 hour mode - */ - public int getCurrentHourOfDay() { - return mDate.get(Calendar.HOUR_OF_DAY); - } - - private int getCurrentHour() { - if (mIs24HourView){ - return getCurrentHourOfDay(); - } else { - int hour = getCurrentHourOfDay(); - if (hour > HOURS_IN_HALF_DAY) { - return hour - HOURS_IN_HALF_DAY; - } else { - return hour == 0 ? HOURS_IN_HALF_DAY : hour; - } - } - } - - /** - * Set current hour in 24 hour mode, in the range (0~23) - * - * @param hourOfDay - */ - public void setCurrentHour(int hourOfDay) { - if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { - return; - } - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - if (!mIs24HourView) { - if (hourOfDay >= HOURS_IN_HALF_DAY) { - mIsAm = false; - if (hourOfDay > HOURS_IN_HALF_DAY) { - hourOfDay -= HOURS_IN_HALF_DAY; - } - } else { - mIsAm = true; - if (hourOfDay == 0) { - hourOfDay = HOURS_IN_HALF_DAY; - } - } - updateAmPmControl(); - } - mHourSpinner.setValue(hourOfDay); - onDateTimeChanged(); - } - - /** - * Get currentMinute - * - * @return The Current Minute - */ - public int getCurrentMinute() { - return mDate.get(Calendar.MINUTE); - } - - /** - * Set current minute - */ - public void setCurrentMinute(int minute) { - if (!mInitialising && minute == getCurrentMinute()) { - return; - } - mMinuteSpinner.setValue(minute); - mDate.set(Calendar.MINUTE, minute); - onDateTimeChanged(); - } - - /** - * @return true if this is in 24 hour view else false. - */ - public boolean is24HourView () { - return mIs24HourView; - } - - /** - * Set whether in 24 hour or AM/PM mode. - * - * @param is24HourView True for 24 hour mode. False for AM/PM mode. - */ - public void set24HourView(boolean is24HourView) { - if (mIs24HourView == is24HourView) { - return; - } - mIs24HourView = is24HourView; - mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); - int hour = getCurrentHourOfDay(); - updateHourControl(); - setCurrentHour(hour); - updateAmPmControl(); - } - - private void updateDateControl() { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); - mDateSpinner.setDisplayedValues(null); - for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { - cal.add(Calendar.DAY_OF_YEAR, 1); - mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); - } - mDateSpinner.setDisplayedValues(mDateDisplayValues); - mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); - mDateSpinner.invalidate(); - } - - private void updateAmPmControl() { - if (mIs24HourView) { - mAmPmSpinner.setVisibility(View.GONE); - } else { - int index = mIsAm ? Calendar.AM : Calendar.PM; - mAmPmSpinner.setValue(index); - mAmPmSpinner.setVisibility(View.VISIBLE); - } - } - - private void updateHourControl() { - if (mIs24HourView) { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); - } else { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); - } - } - - /** - * Set the callback that indicates the 'Set' button has been pressed. - * @param callback the callback, if null will do nothing - */ - public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { - mOnDateTimeChangedListener = callback; - } - - private void onDateTimeChanged() { - if (mOnDateTimeChangedListener != null) { - mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), - getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); - } - } -} diff --git a/src/ui/DateTimePickerDialog.java b/src/ui/DateTimePickerDialog.java deleted file mode 100644 index 2c47ba4..0000000 --- a/src/ui/DateTimePickerDialog.java +++ /dev/null @@ -1,90 +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 java.util.Calendar; - -import net.micode.notes.R; -import net.micode.notes.ui.DateTimePicker; -import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.text.format.DateFormat; -import android.text.format.DateUtils; - -public class DateTimePickerDialog extends AlertDialog implements OnClickListener { - - private Calendar mDate = Calendar.getInstance(); - private boolean mIs24HourView; - private OnDateTimeSetListener mOnDateTimeSetListener; - private DateTimePicker mDateTimePicker; - - public interface OnDateTimeSetListener { - void OnDateTimeSet(AlertDialog dialog, long date); - } - - public DateTimePickerDialog(Context context, long date) { - super(context); - mDateTimePicker = new DateTimePicker(context); - setView(mDateTimePicker); - mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { - public void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - mDate.set(Calendar.YEAR, year); - mDate.set(Calendar.MONTH, month); - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - mDate.set(Calendar.MINUTE, minute); - updateTitle(mDate.getTimeInMillis()); - } - }); - mDate.setTimeInMillis(date); - mDate.set(Calendar.SECOND, 0); - mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); - setButton(context.getString(R.string.datetime_dialog_ok), this); - setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); - set24HourView(DateFormat.is24HourFormat(this.getContext())); - updateTitle(mDate.getTimeInMillis()); - } - - public void set24HourView(boolean is24HourView) { - mIs24HourView = is24HourView; - } - - public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { - mOnDateTimeSetListener = callBack; - } - - private void updateTitle(long date) { - int flag = - DateUtils.FORMAT_SHOW_YEAR | - DateUtils.FORMAT_SHOW_DATE | - DateUtils.FORMAT_SHOW_TIME; - flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; - setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); - } - - public void onClick(DialogInterface arg0, int arg1) { - if (mOnDateTimeSetListener != null) { - mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); - } - } - -} \ No newline at end of file diff --git a/src/ui/DropdownMenu.java b/src/ui/DropdownMenu.java deleted file mode 100644 index 613dc74..0000000 --- a/src/ui/DropdownMenu.java +++ /dev/null @@ -1,61 +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.content.Context; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; - -import net.micode.notes.R; - -public class DropdownMenu { - private Button mButton; - private PopupMenu mPopupMenu; - private Menu mMenu; - - public DropdownMenu(Context context, Button button, int menuId) { - mButton = button; - mButton.setBackgroundResource(R.drawable.dropdown_icon); - mPopupMenu = new PopupMenu(context, mButton); - mMenu = mPopupMenu.getMenu(); - mPopupMenu.getMenuInflater().inflate(menuId, mMenu); - mButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mPopupMenu.show(); - } - }); - } - - public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { - if (mPopupMenu != null) { - mPopupMenu.setOnMenuItemClickListener(listener); - } - } - - public MenuItem findItem(int id) { - return mMenu.findItem(id); - } - - public void setTitle(CharSequence title) { - mButton.setText(title); - } -} diff --git a/src/ui/FoldersListAdapter.java b/src/ui/FoldersListAdapter.java deleted file mode 100644 index 96b77da..0000000 --- a/src/ui/FoldersListAdapter.java +++ /dev/null @@ -1,80 +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.content.Context; -import android.database.Cursor; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorAdapter; -import android.widget.LinearLayout; -import android.widget.TextView; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; - - -public class FoldersListAdapter extends CursorAdapter { - public static final String [] PROJECTION = { - NoteColumns.ID, - NoteColumns.SNIPPET - }; - - public static final int ID_COLUMN = 0; - public static final int NAME_COLUMN = 1; - - public FoldersListAdapter(Context context, Cursor c) { - super(context, c); - // TODO Auto-generated constructor stub - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return new FolderListItem(context); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof FolderListItem) { - String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context - .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); - ((FolderListItem) view).bind(folderName); - } - } - - public String getFolderName(Context context, int position) { - Cursor cursor = (Cursor) getItem(position); - return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context - .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); - } - - private class FolderListItem extends LinearLayout { - private TextView mName; - - public FolderListItem(Context context) { - super(context); - inflate(context, R.layout.folder_list_item, this); - mName = (TextView) findViewById(R.id.tv_folder_name); - } - - public void bind(String name) { - mName.setText(name); - } - } - -} diff --git a/src/ui/NoteEditActivity.java b/src/ui/NoteEditActivity.java deleted file mode 100644 index 96a9ff8..0000000 --- a/src/ui/NoteEditActivity.java +++ /dev/null @@ -1,873 +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.AlarmManager; -import android.app.AlertDialog; -import android.app.PendingIntent; -import android.app.SearchManager; -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Paint; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.style.BackgroundColorSpan; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.model.WorkingNote; -import net.micode.notes.model.WorkingNote.NoteSettingChangedListener; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser; -import net.micode.notes.tool.ResourceParser.TextAppearanceResources; -import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; -import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; -import net.micode.notes.widget.NoteWidgetProvider_2x; -import net.micode.notes.widget.NoteWidgetProvider_4x; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public class NoteEditActivity extends Activity implements OnClickListener, - NoteSettingChangedListener, OnTextViewChangeListener { - private class HeadViewHolder { - public TextView tvModified; - - public ImageView ivAlertIcon; - - public TextView tvAlertDate; - - public ImageView ibSetBgColor; - } - - private static final Map sBgSelectorBtnsMap = new HashMap(); - static { - sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); - sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); - sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); - sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); - sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); - } - - private static final Map sBgSelectorSelectionMap = new HashMap(); - static { - sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); - sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); - sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); - sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); - sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); - } - - private static final Map sFontSizeBtnsMap = new HashMap(); - static { - sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); - sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); - sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); - sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); - } - - private static final Map sFontSelectorSelectionMap = new HashMap(); - static { - sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); - } - - private static final String TAG = "NoteEditActivity"; - - private HeadViewHolder mNoteHeaderHolder; - - private View mHeadViewPanel; - - private View mNoteBgColorSelector; - - private View mFontSizeSelector; - - private EditText mNoteEditor; - - private View mNoteEditorPanel; - - private WorkingNote mWorkingNote; - - private SharedPreferences mSharedPrefs; - private int mFontSizeId; - - private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; - - private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; - - public static final String TAG_CHECKED = String.valueOf('\u221A'); - public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); - - private LinearLayout mEditTextList; - - private String mUserQuery; - private Pattern mPattern; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.setContentView(R.layout.note_edit); - - if (savedInstanceState == null && !initActivityState(getIntent())) { - finish(); - return; - } - initResources(); - } - - /** - * Current activity may be killed when the memory is low. Once it is killed, for another time - * user load this activity, we should restore the former state - */ - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); - if (!initActivityState(intent)) { - finish(); - return; - } - Log.d(TAG, "Restoring from killed activity"); - } - } - - private boolean initActivityState(Intent intent) { - /** - * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, - * then jump to the NotesListActivity - */ - mWorkingNote = null; - if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { - long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); - mUserQuery = ""; - - /** - * Starting from the searched result - */ - if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { - noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); - mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); - } - - if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { - Intent jump = new Intent(this, NotesListActivity.class); - startActivity(jump); - showToast(R.string.error_note_not_exist); - finish(); - return false; - } else { - mWorkingNote = WorkingNote.load(this, noteId); - if (mWorkingNote == null) { - Log.e(TAG, "load note failed with note id" + noteId); - finish(); - return false; - } - } - getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN - | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { - // New note - long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); - int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID); - int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, - Notes.TYPE_WIDGET_INVALIDE); - int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, - ResourceParser.getDefaultBgId(this)); - - // Parse call-record note - String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); - long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); - if (callDate != 0 && phoneNumber != null) { - if (TextUtils.isEmpty(phoneNumber)) { - Log.w(TAG, "The call record number is null"); - } - long noteId = 0; - if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), - phoneNumber, callDate)) > 0) { - mWorkingNote = WorkingNote.load(this, noteId); - if (mWorkingNote == null) { - Log.e(TAG, "load call note failed with note id" + noteId); - finish(); - return false; - } - } else { - mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, - widgetType, bgResId); - mWorkingNote.convertToCallNote(phoneNumber, callDate); - } - } else { - mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, - bgResId); - } - - getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } else { - Log.e(TAG, "Intent not specified action, should not support"); - finish(); - return false; - } - mWorkingNote.setOnSettingStatusChangedListener(this); - return true; - } - - @Override - protected void onResume() { - super.onResume(); - initNoteScreen(); - } - - private void initNoteScreen() { - mNoteEditor.setTextAppearance(this, TextAppearanceResources - .getTexAppearanceResource(mFontSizeId)); - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - switchToListMode(mWorkingNote.getContent()); - } else { - mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); - mNoteEditor.setSelection(mNoteEditor.getText().length()); - } - for (Integer id : sBgSelectorSelectionMap.keySet()) { - findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); - } - mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - - mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, - mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME - | DateUtils.FORMAT_SHOW_YEAR)); - - /** - * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker - * is not ready - */ - showAlertHeader(); - } - - private void showAlertHeader() { - if (mWorkingNote.hasClockAlert()) { - long time = System.currentTimeMillis(); - if (time > mWorkingNote.getAlertDate()) { - mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); - } else { - mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( - mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); - } - mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); - mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); - } else { - mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); - mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); - }; - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - initActivityState(intent); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - /** - * For new note without note id, we should firstly save it to - * generate a id. If the editing note is not worth saving, there - * is no id which is equivalent to create new note - */ - if (!mWorkingNote.existInDatabase()) { - saveNote(); - } - outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); - Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (mNoteBgColorSelector.getVisibility() == View.VISIBLE - && !inRangeOfView(mNoteBgColorSelector, ev)) { - mNoteBgColorSelector.setVisibility(View.GONE); - return true; - } - - if (mFontSizeSelector.getVisibility() == View.VISIBLE - && !inRangeOfView(mFontSizeSelector, ev)) { - mFontSizeSelector.setVisibility(View.GONE); - return true; - } - return super.dispatchTouchEvent(ev); - } - - private boolean inRangeOfView(View view, MotionEvent ev) { - int []location = new int[2]; - view.getLocationOnScreen(location); - int x = location[0]; - int y = location[1]; - if (ev.getX() < x - || ev.getX() > (x + view.getWidth()) - || ev.getY() < y - || ev.getY() > (y + view.getHeight())) { - return false; - } - return true; - } - - private void initResources() { - mHeadViewPanel = findViewById(R.id.note_title); - mNoteHeaderHolder = new HeadViewHolder(); - mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); - mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon); - mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); - mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); - mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); - mNoteEditor = (EditText) findViewById(R.id.note_edit_view); - mNoteEditorPanel = findViewById(R.id.sv_note_edit); - mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); - for (int id : sBgSelectorBtnsMap.keySet()) { - ImageView iv = (ImageView) findViewById(id); - iv.setOnClickListener(this); - } - - mFontSizeSelector = findViewById(R.id.font_size_selector); - for (int id : sFontSizeBtnsMap.keySet()) { - View view = findViewById(id); - view.setOnClickListener(this); - }; - mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ - if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { - mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; - } - mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); - } - - @Override - protected void onPause() { - super.onPause(); - if(saveNote()) { - Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); - } - clearSettingState(); - } - - private void updateWidget() { - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { - intent.setClass(this, NoteWidgetProvider_2x.class); - } else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) { - intent.setClass(this, NoteWidgetProvider_4x.class); - } else { - Log.e(TAG, "Unspported widget type"); - return; - } - - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - mWorkingNote.getWidgetId() - }); - - sendBroadcast(intent); - setResult(RESULT_OK, intent); - } - - public void onClick(View v) { - int id = v.getId(); - if (id == R.id.btn_set_bg_color) { - mNoteBgColorSelector.setVisibility(View.VISIBLE); - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - - View.VISIBLE); - } else if (sBgSelectorBtnsMap.containsKey(id)) { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.GONE); - mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); - mNoteBgColorSelector.setVisibility(View.GONE); - } else if (sFontSizeBtnsMap.containsKey(id)) { - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); - mFontSizeId = sFontSizeBtnsMap.get(id); - mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - getWorkingText(); - switchToListMode(mWorkingNote.getContent()); - } else { - mNoteEditor.setTextAppearance(this, - TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); - } - mFontSizeSelector.setVisibility(View.GONE); - } - } - - @Override - public void onBackPressed() { - if(clearSettingState()) { - return; - } - - saveNote(); - super.onBackPressed(); - } - - private boolean clearSettingState() { - if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { - mNoteBgColorSelector.setVisibility(View.GONE); - return true; - } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { - mFontSizeSelector.setVisibility(View.GONE); - return true; - } - return false; - } - - public void onBackgroundColorChanged() { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.VISIBLE); - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - if (isFinishing()) { - return true; - } - clearSettingState(); - menu.clear(); - if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { - getMenuInflater().inflate(R.menu.call_note_edit, menu); - } else { - getMenuInflater().inflate(R.menu.note_edit, menu); - } - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); - } else { - menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); - } - if (mWorkingNote.hasClockAlert()) { - menu.findItem(R.id.menu_alert).setVisible(false); - } else { - menu.findItem(R.id.menu_delete_remind).setVisible(false); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_new_note: - createNewNote(); - break; - case R.id.menu_delete: - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.alert_title_delete)); - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setMessage(getString(R.string.alert_message_delete_note)); - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - deleteCurrentNote(); - finish(); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - break; - case R.id.menu_font_size: - mFontSizeSelector.setVisibility(View.VISIBLE); - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); - break; - case R.id.menu_list_mode: - mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? - TextNote.MODE_CHECK_LIST : 0); - break; - case R.id.menu_share: - getWorkingText(); - sendTo(this, mWorkingNote.getContent()); - break; - case R.id.menu_send_to_desktop: - sendToDesktop(); - break; - case R.id.menu_alert: - setReminder(); - break; - case R.id.menu_delete_remind: - mWorkingNote.setAlertDate(0, false); - break; - default: - break; - } - return true; - } - - private void setReminder() { - DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); - d.setOnDateTimeSetListener(new OnDateTimeSetListener() { - public void OnDateTimeSet(AlertDialog dialog, long date) { - mWorkingNote.setAlertDate(date , true); - } - }); - d.show(); - } - - /** - * Share note to apps that support {@link Intent#ACTION_SEND} action - * and {@text/plain} type - */ - private void sendTo(Context context, String info) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_TEXT, info); - intent.setType("text/plain"); - context.startActivity(intent); - } - - private void createNewNote() { - // Firstly, save current editing notes - saveNote(); - - // For safety, start a new NoteEditActivity - finish(); - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); - startActivity(intent); - } - - private void deleteCurrentNote() { - if (mWorkingNote.existInDatabase()) { - HashSet ids = new HashSet(); - long id = mWorkingNote.getNoteId(); - if (id != Notes.ID_ROOT_FOLDER) { - ids.add(id); - } else { - Log.d(TAG, "Wrong note id, should not happen"); - } - if (!isSyncMode()) { - if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { - Log.e(TAG, "Delete Note error"); - } - } else { - if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { - Log.e(TAG, "Move notes to trash folder error, should not happens"); - } - } - } - mWorkingNote.markDeleted(true); - } - - private boolean isSyncMode() { - return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; - } - - public void onClockAlertChanged(long date, boolean set) { - /** - * User could set clock to an unsaved note, so before setting the - * alert clock, we should save the note first - */ - if (!mWorkingNote.existInDatabase()) { - saveNote(); - } - if (mWorkingNote.getNoteId() > 0) { - Intent intent = new Intent(this, AlarmReceiver.class); - intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); - PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); - AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); - showAlertHeader(); - if(!set) { - alarmManager.cancel(pendingIntent); - } else { - alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); - } - } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ - Log.e(TAG, "Clock alert setting error"); - showToast(R.string.error_note_empty_for_clock); - } - } - - public void onWidgetChanged() { - updateWidget(); - } - - public void onEditTextDelete(int index, String text) { - int childCount = mEditTextList.getChildCount(); - if (childCount == 1) { - return; - } - - for (int i = index + 1; i < childCount; i++) { - ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) - .setIndex(i - 1); - } - - mEditTextList.removeViewAt(index); - NoteEditText edit = null; - if(index == 0) { - edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( - R.id.et_edit_text); - } else { - edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( - R.id.et_edit_text); - } - int length = edit.length(); - edit.append(text); - edit.requestFocus(); - edit.setSelection(length); - } - - public void onEditTextEnter(int index, String text) { - /** - * Should not happen, check for debug - */ - if(index > mEditTextList.getChildCount()) { - Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); - } - - View view = getListItem(text, index); - mEditTextList.addView(view, index); - NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - edit.requestFocus(); - edit.setSelection(0); - for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { - ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) - .setIndex(i); - } - } - - private void switchToListMode(String text) { - mEditTextList.removeAllViews(); - String[] items = text.split("\n"); - int index = 0; - for (String item : items) { - if(!TextUtils.isEmpty(item)) { - mEditTextList.addView(getListItem(item, index)); - index++; - } - } - mEditTextList.addView(getListItem("", index)); - mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); - - mNoteEditor.setVisibility(View.GONE); - mEditTextList.setVisibility(View.VISIBLE); - } - - private Spannable getHighlightQueryResult(String fullText, String userQuery) { - SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); - if (!TextUtils.isEmpty(userQuery)) { - mPattern = Pattern.compile(userQuery); - Matcher m = mPattern.matcher(fullText); - int start = 0; - while (m.find(start)) { - spannable.setSpan( - new BackgroundColorSpan(this.getResources().getColor( - R.color.user_query_highlight)), m.start(), m.end(), - Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - start = m.end(); - } - } - return spannable; - } - - private View getListItem(String item, int index) { - View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); - final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); - CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); - cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } else { - edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); - } - } - }); - - if (item.startsWith(TAG_CHECKED)) { - cb.setChecked(true); - edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - item = item.substring(TAG_CHECKED.length(), item.length()).trim(); - } else if (item.startsWith(TAG_UNCHECKED)) { - cb.setChecked(false); - edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); - item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); - } - - edit.setOnTextViewChangeListener(this); - edit.setIndex(index); - edit.setText(getHighlightQueryResult(item, mUserQuery)); - return view; - } - - public void onTextChange(int index, boolean hasText) { - if (index >= mEditTextList.getChildCount()) { - Log.e(TAG, "Wrong index, should not happen"); - return; - } - if(hasText) { - mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); - } else { - mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); - } - } - - public void onCheckListModeChanged(int oldMode, int newMode) { - if (newMode == TextNote.MODE_CHECK_LIST) { - switchToListMode(mNoteEditor.getText().toString()); - } else { - if (!getWorkingText()) { - mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", - "")); - } - mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); - mEditTextList.setVisibility(View.GONE); - mNoteEditor.setVisibility(View.VISIBLE); - } - } - - private boolean getWorkingText() { - boolean hasChecked = false; - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < mEditTextList.getChildCount(); i++) { - View view = mEditTextList.getChildAt(i); - NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - if (!TextUtils.isEmpty(edit.getText())) { - if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { - sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); - hasChecked = true; - } else { - sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); - } - } - } - mWorkingNote.setWorkingText(sb.toString()); - } else { - mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); - } - return hasChecked; - } - - private boolean saveNote() { - getWorkingText(); - boolean saved = mWorkingNote.saveNote(); - if (saved) { - /** - * There are two modes from List view to edit view, open one note, - * create/edit a node. Opening node requires to the original - * position in the list when back from edit view, while creating a - * new node requires to the top of the list. This code - * {@link #RESULT_OK} is used to identify the create/edit state - */ - setResult(RESULT_OK); - } - return saved; - } - - private void sendToDesktop() { - /** - * Before send message to home, we should make sure that current - * editing note is exists in databases. So, for new note, firstly - * save it - */ - if (!mWorkingNote.existInDatabase()) { - saveNote(); - } - - if (mWorkingNote.getNoteId() > 0) { - Intent sender = new Intent(); - Intent shortcutIntent = new Intent(this, NoteEditActivity.class); - shortcutIntent.setAction(Intent.ACTION_VIEW); - shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); - sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, - makeShortcutIconTitle(mWorkingNote.getContent())); - sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); - sender.putExtra("duplicate", true); - sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - showToast(R.string.info_note_enter_desktop); - sendBroadcast(sender); - } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ - Log.e(TAG, "Send to desktop error"); - showToast(R.string.error_note_empty_for_send_to_desktop); - } - } - - private String makeShortcutIconTitle(String content) { - content = content.replace(TAG_CHECKED, ""); - content = content.replace(TAG_UNCHECKED, ""); - return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, - SHORTCUT_ICON_TITLE_MAX_LEN) : content; - } - - private void showToast(int resId) { - showToast(resId, Toast.LENGTH_SHORT); - } - - private void showToast(int resId, int duration) { - Toast.makeText(this, resId, duration).show(); - } -} diff --git a/src/ui/NoteEditText.java b/src/ui/NoteEditText.java deleted file mode 100644 index 2afe2a8..0000000 --- a/src/ui/NoteEditText.java +++ /dev/null @@ -1,217 +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.content.Context; -import android.graphics.Rect; -import android.text.Layout; -import android.text.Selection; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.style.URLSpan; -import android.util.AttributeSet; -import android.util.Log; -import android.view.ContextMenu; -import android.view.KeyEvent; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.MotionEvent; -import android.widget.EditText; - -import net.micode.notes.R; - -import java.util.HashMap; -import java.util.Map; - -public class NoteEditText extends EditText { - private static final String TAG = "NoteEditText"; - private int mIndex; - private int mSelectionStartBeforeDelete; - - private static final String SCHEME_TEL = "tel:" ; - private static final String SCHEME_HTTP = "http:" ; - private static final String SCHEME_EMAIL = "mailto:" ; - - private static final Map sSchemaActionResMap = new HashMap(); - static { - sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); - sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); - sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); - } - - /** - * Call by the {@link NoteEditActivity} to delete or add edit text - */ - public interface OnTextViewChangeListener { - /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null - */ - void onEditTextDelete(int index, String text); - - /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen - */ - void onEditTextEnter(int index, String text); - - /** - * Hide or show item option when text change - */ - void onTextChange(int index, boolean hasText); - } - - private OnTextViewChangeListener mOnTextViewChangeListener; - - public NoteEditText(Context context) { - super(context, null); - mIndex = 0; - } - - public void setIndex(int index) { - mIndex = index; - } - - public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { - mOnTextViewChangeListener = listener; - } - - public NoteEditText(Context context, AttributeSet attrs) { - super(context, attrs, android.R.attr.editTextStyle); - } - - public NoteEditText(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - // TODO Auto-generated constructor stub - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - - int x = (int) event.getX(); - int y = (int) event.getY(); - x -= getTotalPaddingLeft(); - y -= getTotalPaddingTop(); - x += getScrollX(); - y += getScrollY(); - - Layout layout = getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - Selection.setSelection(getText(), off); - break; - } - - return super.onTouchEvent(event); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - if (mOnTextViewChangeListener != null) { - return false; - } - break; - case KeyEvent.KEYCODE_DEL: - mSelectionStartBeforeDelete = getSelectionStart(); - break; - default: - break; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - switch(keyCode) { - case KeyEvent.KEYCODE_DEL: - if (mOnTextViewChangeListener != null) { - if (0 == mSelectionStartBeforeDelete && mIndex != 0) { - mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); - return true; - } - } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); - } - break; - case KeyEvent.KEYCODE_ENTER: - if (mOnTextViewChangeListener != null) { - int selectionStart = getSelectionStart(); - String text = getText().subSequence(selectionStart, length()).toString(); - setText(getText().subSequence(0, selectionStart)); - mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); - } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); - } - break; - default: - break; - } - return super.onKeyUp(keyCode, event); - } - - @Override - protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { - if (mOnTextViewChangeListener != null) { - if (!focused && TextUtils.isEmpty(getText())) { - mOnTextViewChangeListener.onTextChange(mIndex, false); - } else { - mOnTextViewChangeListener.onTextChange(mIndex, true); - } - } - super.onFocusChanged(focused, direction, previouslyFocusedRect); - } - - @Override - protected void onCreateContextMenu(ContextMenu menu) { - if (getText() instanceof Spanned) { - int selStart = getSelectionStart(); - int selEnd = getSelectionEnd(); - - int min = Math.min(selStart, selEnd); - int max = Math.max(selStart, selEnd); - - final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); - if (urls.length == 1) { - int defaultResId = 0; - for(String schema: sSchemaActionResMap.keySet()) { - if(urls[0].getURL().indexOf(schema) >= 0) { - defaultResId = sSchemaActionResMap.get(schema); - break; - } - } - - if (defaultResId == 0) { - defaultResId = R.string.note_link_other; - } - - menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( - new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - // goto a new intent - urls[0].onClick(NoteEditText.this); - return true; - } - }); - } - } - super.onCreateContextMenu(menu); - } -} diff --git a/src/ui/NoteItemData.java b/src/ui/NoteItemData.java deleted file mode 100644 index 0f5a878..0000000 --- a/src/ui/NoteItemData.java +++ /dev/null @@ -1,224 +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.content.Context; -import android.database.Cursor; -import android.text.TextUtils; - -import net.micode.notes.data.Contact; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.tool.DataUtils; - - -public class NoteItemData { - static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, - NoteColumns.HAS_ATTACHMENT, - NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, - NoteColumns.PARENT_ID, - NoteColumns.SNIPPET, - NoteColumns.TYPE, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - }; - - private static final int ID_COLUMN = 0; - private static final int ALERTED_DATE_COLUMN = 1; - private static final int BG_COLOR_ID_COLUMN = 2; - private static final int CREATED_DATE_COLUMN = 3; - private static final int HAS_ATTACHMENT_COLUMN = 4; - private static final int MODIFIED_DATE_COLUMN = 5; - private static final int NOTES_COUNT_COLUMN = 6; - private static final int PARENT_ID_COLUMN = 7; - private static final int SNIPPET_COLUMN = 8; - private static final int TYPE_COLUMN = 9; - private static final int WIDGET_ID_COLUMN = 10; - private static final int WIDGET_TYPE_COLUMN = 11; - - private long mId; - private long mAlertDate; - private int mBgColorId; - private long mCreatedDate; - private boolean mHasAttachment; - private long mModifiedDate; - private int mNotesCount; - private long mParentId; - private String mSnippet; - private int mType; - private int mWidgetId; - private int mWidgetType; - private String mName; - private String mPhoneNumber; - - private boolean mIsLastItem; - private boolean mIsFirstItem; - private boolean mIsOnlyOneItem; - private boolean mIsOneNoteFollowingFolder; - private boolean mIsMultiNotesFollowingFolder; - - public NoteItemData(Context context, Cursor cursor) { - mId = cursor.getLong(ID_COLUMN); - mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); - mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); - mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); - mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; - mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); - mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); - mParentId = cursor.getLong(PARENT_ID_COLUMN); - mSnippet = cursor.getString(SNIPPET_COLUMN); - mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( - NoteEditActivity.TAG_UNCHECKED, ""); - mType = cursor.getInt(TYPE_COLUMN); - mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); - - mPhoneNumber = ""; - if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { - mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); - if (!TextUtils.isEmpty(mPhoneNumber)) { - mName = Contact.getContact(context, mPhoneNumber); - if (mName == null) { - mName = mPhoneNumber; - } - } - } - - if (mName == null) { - mName = ""; - } - checkPostion(cursor); - } - - private void checkPostion(Cursor cursor) { - mIsLastItem = cursor.isLast() ? true : false; - mIsFirstItem = cursor.isFirst() ? true : false; - mIsOnlyOneItem = (cursor.getCount() == 1); - mIsMultiNotesFollowingFolder = false; - mIsOneNoteFollowingFolder = false; - - if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { - int position = cursor.getPosition(); - if (cursor.moveToPrevious()) { - if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER - || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { - if (cursor.getCount() > (position + 1)) { - mIsMultiNotesFollowingFolder = true; - } else { - mIsOneNoteFollowingFolder = true; - } - } - if (!cursor.moveToNext()) { - throw new IllegalStateException("cursor move to previous but can't move back"); - } - } - } - } - - public boolean isOneFollowingFolder() { - return mIsOneNoteFollowingFolder; - } - - public boolean isMultiFollowingFolder() { - return mIsMultiNotesFollowingFolder; - } - - public boolean isLast() { - return mIsLastItem; - } - - public String getCallName() { - return mName; - } - - public boolean isFirst() { - return mIsFirstItem; - } - - public boolean isSingle() { - return mIsOnlyOneItem; - } - - public long getId() { - return mId; - } - - public long getAlertDate() { - return mAlertDate; - } - - public long getCreatedDate() { - return mCreatedDate; - } - - public boolean hasAttachment() { - return mHasAttachment; - } - - public long getModifiedDate() { - return mModifiedDate; - } - - public int getBgColorId() { - return mBgColorId; - } - - public long getParentId() { - return mParentId; - } - - public int getNotesCount() { - return mNotesCount; - } - - public long getFolderId () { - return mParentId; - } - - public int getType() { - return mType; - } - - public int getWidgetType() { - return mWidgetType; - } - - public int getWidgetId() { - return mWidgetId; - } - - public String getSnippet() { - return mSnippet; - } - - public boolean hasAlert() { - return (mAlertDate > 0); - } - - public boolean isCallRecord() { - return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); - } - - public static int getNoteType(Cursor cursor) { - return cursor.getInt(TYPE_COLUMN); - } -} diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java deleted file mode 100644 index e843aec..0000000 --- a/src/ui/NotesListActivity.java +++ /dev/null @@ -1,954 +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.app.Dialog; -import android.appwidget.AppWidgetManager; -import android.content.AsyncQueryHandler; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.AsyncTask; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Log; -import android.view.ActionMode; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Display; -import android.view.HapticFeedbackConstants; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; -import android.view.View.OnTouchListener; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.PopupMenu; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.remote.GTaskSyncService; -import net.micode.notes.model.WorkingNote; -import net.micode.notes.tool.BackupUtils; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser; -import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; -import net.micode.notes.widget.NoteWidgetProvider_2x; -import net.micode.notes.widget.NoteWidgetProvider_4x; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashSet; - -public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; - - private static final int FOLDER_LIST_QUERY_TOKEN = 1; - - private static final int MENU_FOLDER_DELETE = 0; - - private static final int MENU_FOLDER_VIEW = 1; - - private static final int MENU_FOLDER_CHANGE_NAME = 2; - - private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; - - private enum ListEditState { - NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER - }; - - private ListEditState mState; - - private BackgroundQueryHandler mBackgroundQueryHandler; - - private NotesListAdapter mNotesListAdapter; - - private ListView mNotesListView; - - private Button mAddNewNote; - - private boolean mDispatch; - - private int mOriginY; - - private int mDispatchY; - - private TextView mTitleBar; - - private long mCurrentFolderId; - - private ContentResolver mContentResolver; - - private ModeCallback mModeCallBack; - - private static final String TAG = "NotesListActivity"; - - public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; - - private NoteItemData mFocusNoteDataItem; - - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; - - private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" - + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " - + NoteColumns.NOTES_COUNT + ">0)"; - - private final static int REQUEST_CODE_OPEN_NODE = 102; - private final static int REQUEST_CODE_NEW_NODE = 103; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.note_list); - initResources(); - - /** - * Insert an introduction when user firstly use this application - */ - setAppInfoFromRawRes(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == RESULT_OK - && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { - mNotesListAdapter.changeCursor(null); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - private void setAppInfoFromRawRes() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { - StringBuilder sb = new StringBuilder(); - InputStream in = null; - try { - in = getResources().openRawResource(R.raw.introduction); - if (in != null) { - InputStreamReader isr = new InputStreamReader(in); - BufferedReader br = new BufferedReader(isr); - char [] buf = new char[1024]; - int len = 0; - while ((len = br.read(buf)) > 0) { - sb.append(buf, 0, len); - } - } else { - Log.e(TAG, "Read introduction file error"); - return; - } - } catch (IOException e) { - e.printStackTrace(); - return; - } finally { - if(in != null) { - try { - in.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, - AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, - ResourceParser.RED); - note.setWorkingText(sb.toString()); - if (note.saveNote()) { - sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); - } else { - Log.e(TAG, "Save introduction note error"); - return; - } - } - } - - @Override - protected void onStart() { - super.onStart(); - startAsyncNotesListQuery(); - } - - private void initResources() { - mContentResolver = this.getContentResolver(); - mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mNotesListView = (ListView) findViewById(R.id.notes_list); - mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), - null, false); - mNotesListView.setOnItemClickListener(new OnListItemClickListener()); - mNotesListView.setOnItemLongClickListener(this); - mNotesListAdapter = new NotesListAdapter(this); - mNotesListView.setAdapter(mNotesListAdapter); - mAddNewNote = (Button) findViewById(R.id.btn_new_note); - mAddNewNote.setOnClickListener(this); - mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); - mDispatch = false; - mDispatchY = 0; - mOriginY = 0; - mTitleBar = (TextView) findViewById(R.id.tv_title_bar); - mState = ListEditState.NOTE_LIST; - mModeCallBack = new ModeCallback(); - } - - private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { - private DropdownMenu mDropDownMenu; - private ActionMode mActionMode; - private MenuItem mMoveMenu; - - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - getMenuInflater().inflate(R.menu.note_list_options, menu); - menu.findItem(R.id.delete).setOnMenuItemClickListener(this); - mMoveMenu = menu.findItem(R.id.move); - if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER - || DataUtils.getUserFolderCount(mContentResolver) == 0) { - mMoveMenu.setVisible(false); - } else { - mMoveMenu.setVisible(true); - mMoveMenu.setOnMenuItemClickListener(this); - } - mActionMode = mode; - mNotesListAdapter.setChoiceMode(true); - mNotesListView.setLongClickable(false); - mAddNewNote.setVisibility(View.GONE); - - View customView = LayoutInflater.from(NotesListActivity.this).inflate( - R.layout.note_list_dropdown_menu, null); - mode.setCustomView(customView); - mDropDownMenu = new DropdownMenu(NotesListActivity.this, - (Button) customView.findViewById(R.id.selection_menu), - R.menu.note_list_dropdown); - mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ - public boolean onMenuItemClick(MenuItem item) { - mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); - updateMenu(); - return true; - } - - }); - return true; - } - - private void updateMenu() { - int selectedCount = mNotesListAdapter.getSelectedCount(); - // Update dropdown menu - String format = getResources().getString(R.string.menu_select_title, selectedCount); - mDropDownMenu.setTitle(format); - MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); - if (item != null) { - if (mNotesListAdapter.isAllSelected()) { - item.setChecked(true); - item.setTitle(R.string.menu_deselect_all); - } else { - item.setChecked(false); - item.setTitle(R.string.menu_select_all); - } - } - } - - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - // TODO Auto-generated method stub - return false; - } - - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // TODO Auto-generated method stub - return false; - } - - public void onDestroyActionMode(ActionMode mode) { - mNotesListAdapter.setChoiceMode(false); - mNotesListView.setLongClickable(true); - mAddNewNote.setVisibility(View.VISIBLE); - } - - public void finishActionMode() { - mActionMode.finish(); - } - - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - mNotesListAdapter.setCheckedItem(position, checked); - updateMenu(); - } - - public boolean onMenuItemClick(MenuItem item) { - if (mNotesListAdapter.getSelectedCount() == 0) { - Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), - Toast.LENGTH_SHORT).show(); - return true; - } - - switch (item.getItemId()) { - case R.id.delete: - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(getString(R.string.alert_title_delete)); - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setMessage(getString(R.string.alert_message_delete_notes, - mNotesListAdapter.getSelectedCount())); - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - batchDelete(); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - break; - case R.id.move: - startQueryDestinationFolders(); - break; - default: - return false; - } - return true; - } - } - - private class NewNoteOnTouchListener implements OnTouchListener { - - public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: { - Display display = getWindowManager().getDefaultDisplay(); - int screenHeight = display.getHeight(); - int newNoteViewHeight = mAddNewNote.getHeight(); - int start = screenHeight - newNoteViewHeight; - int eventY = start + (int) event.getY(); - /** - * Minus TitleBar's height - */ - if (mState == ListEditState.SUB_FOLDER) { - eventY -= mTitleBar.getHeight(); - start -= mTitleBar.getHeight(); - } - /** - * HACKME:When click the transparent part of "New Note" button, dispatch - * the event to the list view behind this button. The transparent part of - * "New Note" button could be expressed by formula y=-0.12x+94(Unit:pixel) - * and the line top of the button. The coordinate based on left of the "New - * Note" button. The 94 represents maximum height of the transparent part. - * Notice that, if the background of the button changes, the formula should - * also change. This is very bad, just for the UI designer's strong requirement. - */ - if (event.getY() < (event.getX() * (-0.12) + 94)) { - View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - - mNotesListView.getFooterViewsCount()); - if (view != null && view.getBottom() > start - && (view.getTop() < (start + 94))) { - mOriginY = (int) event.getY(); - mDispatchY = eventY; - event.setLocation(event.getX(), mDispatchY); - mDispatch = true; - return mNotesListView.dispatchTouchEvent(event); - } - } - break; - } - case MotionEvent.ACTION_MOVE: { - if (mDispatch) { - mDispatchY += (int) event.getY() - mOriginY; - event.setLocation(event.getX(), mDispatchY); - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - default: { - if (mDispatch) { - event.setLocation(event.getX(), mDispatchY); - mDispatch = false; - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - } - return false; - } - - }; - - private void startAsyncNotesListQuery() { - String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION - : NORMAL_SELECTION; - mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, - Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { - String.valueOf(mCurrentFolderId) - }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); - } - - private final class BackgroundQueryHandler extends AsyncQueryHandler { - public BackgroundQueryHandler(ContentResolver contentResolver) { - super(contentResolver); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - switch (token) { - case FOLDER_NOTE_LIST_QUERY_TOKEN: - mNotesListAdapter.changeCursor(cursor); - break; - case FOLDER_LIST_QUERY_TOKEN: - if (cursor != null && cursor.getCount() > 0) { - showFolderListMenu(cursor); - } else { - Log.e(TAG, "Query folder failed"); - } - break; - default: - return; - } - } - } - - private void showFolderListMenu(Cursor cursor) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(R.string.menu_title_select_folder); - final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); - builder.setAdapter(adapter, new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - DataUtils.batchMoveToFolder(mContentResolver, - mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); - Toast.makeText( - NotesListActivity.this, - getString(R.string.format_move_notes_to_folder, - mNotesListAdapter.getSelectedCount(), - adapter.getFolderName(NotesListActivity.this, which)), - Toast.LENGTH_SHORT).show(); - mModeCallBack.finishActionMode(); - } - }); - builder.show(); - } - - private void createNewNote() { - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); - this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); - } - - private void batchDelete() { - new AsyncTask>() { - protected HashSet doInBackground(Void... unused) { - HashSet widgets = mNotesListAdapter.getSelectedWidget(); - if (!isSyncMode()) { - // if not synced, delete notes directly - if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter - .getSelectedItemIds())) { - } else { - Log.e(TAG, "Delete notes error, should not happens"); - } - } else { - // in sync mode, we'll move the deleted note into the trash - // folder - if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter - .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { - Log.e(TAG, "Move notes to trash folder error, should not happens"); - } - } - return widgets; - } - - @Override - protected void onPostExecute(HashSet widgets) { - if (widgets != null) { - for (AppWidgetAttribute widget : widgets) { - if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { - updateWidget(widget.widgetId, widget.widgetType); - } - } - } - mModeCallBack.finishActionMode(); - } - }.execute(); - } - - private void deleteFolder(long folderId) { - if (folderId == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "Wrong folder id, should not happen " + folderId); - return; - } - - HashSet ids = new HashSet(); - ids.add(folderId); - HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, - folderId); - if (!isSyncMode()) { - // if not synced, delete folder directly - DataUtils.batchDeleteNotes(mContentResolver, ids); - } else { - // in sync mode, we'll move the deleted folder into the trash folder - DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); - } - if (widgets != null) { - for (AppWidgetAttribute widget : widgets) { - if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { - updateWidget(widget.widgetId, widget.widgetType); - } - } - } - } - - private void openNode(NoteItemData data) { - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, data.getId()); - this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); - } - - private void openFolder(NoteItemData data) { - mCurrentFolderId = data.getId(); - startAsyncNotesListQuery(); - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mState = ListEditState.CALL_RECORD_FOLDER; - mAddNewNote.setVisibility(View.GONE); - } else { - mState = ListEditState.SUB_FOLDER; - } - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mTitleBar.setText(R.string.call_record_folder_name); - } else { - mTitleBar.setText(data.getSnippet()); - } - mTitleBar.setVisibility(View.VISIBLE); - } - - public void onClick(View v) { - switch (v.getId()) { - case R.id.btn_new_note: - createNewNote(); - break; - default: - break; - } - } - - private void showSoftInput() { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (inputMethodManager != null) { - inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } - - private void hideSoftInput(View view) { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - private void showCreateOrModifyFolderDialog(final boolean create) { - final AlertDialog.Builder builder = new AlertDialog.Builder(this); - View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); - final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); - showSoftInput(); - if (!create) { - if (mFocusNoteDataItem != null) { - etName.setText(mFocusNoteDataItem.getSnippet()); - builder.setTitle(getString(R.string.menu_folder_change_name)); - } else { - Log.e(TAG, "The long click data item is null"); - return; - } - } else { - etName.setText(""); - builder.setTitle(this.getString(R.string.menu_create_folder)); - } - - builder.setPositiveButton(android.R.string.ok, null); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - hideSoftInput(etName); - } - }); - - final Dialog dialog = builder.setView(view).show(); - final Button positive = (Button)dialog.findViewById(android.R.id.button1); - positive.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - hideSoftInput(etName); - String name = etName.getText().toString(); - if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { - Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), - Toast.LENGTH_LONG).show(); - etName.setSelection(0, etName.length()); - return; - } - if (!create) { - if (!TextUtils.isEmpty(name)) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.SNIPPET, name); - values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID - + "=?", new String[] { - String.valueOf(mFocusNoteDataItem.getId()) - }); - } - } else if (!TextUtils.isEmpty(name)) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.SNIPPET, name); - values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); - } - dialog.dismiss(); - } - }); - - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); - } - /** - * When the name edit text is null, disable the positive button - */ - etName.addTextChangedListener(new TextWatcher() { - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // TODO Auto-generated method stub - - } - - public void onTextChanged(CharSequence s, int start, int before, int count) { - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); - } else { - positive.setEnabled(true); - } - } - - public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub - - } - }); - } - - @Override - public void onBackPressed() { - switch (mState) { - case SUB_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - startAsyncNotesListQuery(); - mTitleBar.setVisibility(View.GONE); - break; - case CALL_RECORD_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - mAddNewNote.setVisibility(View.VISIBLE); - mTitleBar.setVisibility(View.GONE); - startAsyncNotesListQuery(); - break; - case NOTE_LIST: - super.onBackPressed(); - break; - default: - break; - } - } - - private void updateWidget(int appWidgetId, int appWidgetType) { - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - if (appWidgetType == Notes.TYPE_WIDGET_2X) { - intent.setClass(this, NoteWidgetProvider_2x.class); - } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { - intent.setClass(this, NoteWidgetProvider_4x.class); - } else { - Log.e(TAG, "Unspported widget type"); - return; - } - - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - appWidgetId - }); - - sendBroadcast(intent); - setResult(RESULT_OK, intent); - } - - private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - if (mFocusNoteDataItem != null) { - menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); - menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); - menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); - menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); - } - } - }; - - @Override - public void onContextMenuClosed(Menu menu) { - if (mNotesListView != null) { - mNotesListView.setOnCreateContextMenuListener(null); - } - super.onContextMenuClosed(menu); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (mFocusNoteDataItem == null) { - Log.e(TAG, "The long click data item is null"); - return false; - } - switch (item.getItemId()) { - case MENU_FOLDER_VIEW: - openFolder(mFocusNoteDataItem); - break; - case MENU_FOLDER_DELETE: - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.alert_title_delete)); - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setMessage(getString(R.string.alert_message_delete_folder)); - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - deleteFolder(mFocusNoteDataItem.getId()); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - break; - case MENU_FOLDER_CHANGE_NAME: - showCreateOrModifyFolderDialog(false); - break; - default: - break; - } - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menu.clear(); - if (mState == ListEditState.NOTE_LIST) { - getMenuInflater().inflate(R.menu.note_list, menu); - // set sync or sync_cancel - menu.findItem(R.id.menu_sync).setTitle( - GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); - } else if (mState == ListEditState.SUB_FOLDER) { - getMenuInflater().inflate(R.menu.sub_folder, menu); - } else if (mState == ListEditState.CALL_RECORD_FOLDER) { - getMenuInflater().inflate(R.menu.call_record_folder, menu); - } else { - Log.e(TAG, "Wrong state:" + mState); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_new_folder: { - showCreateOrModifyFolderDialog(true); - break; - } - case R.id.menu_export_text: { - exportNoteToText(); - break; - } - case R.id.menu_sync: { - if (isSyncMode()) { - if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { - GTaskSyncService.startSync(this); - } else { - GTaskSyncService.cancelSync(this); - } - } else { - startPreferenceActivity(); - } - break; - } - case R.id.menu_setting: { - startPreferenceActivity(); - break; - } - case R.id.menu_new_note: { - createNewNote(); - break; - } - case R.id.menu_search: - onSearchRequested(); - break; - default: - break; - } - return true; - } - - @Override - public boolean onSearchRequested() { - startSearch(null, false, null /* appData */, false); - return true; - } - - private void exportNoteToText() { - final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); - new AsyncTask() { - - @Override - protected Integer doInBackground(Void... unused) { - return backup.exportToText(); - } - - @Override - protected void onPostExecute(Integer result) { - if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.failed_sdcard_export)); - builder.setMessage(NotesListActivity.this - .getString(R.string.error_sdcard_unmounted)); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } else if (result == BackupUtils.STATE_SUCCESS) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.success_sdcard_export)); - builder.setMessage(NotesListActivity.this.getString( - R.string.format_exported_file_location, backup - .getExportedTextFileName(), backup.getExportedTextFileDir())); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.failed_sdcard_export)); - builder.setMessage(NotesListActivity.this - .getString(R.string.error_sdcard_export)); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } - } - - }.execute(); - } - - private boolean isSyncMode() { - return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; - } - - private void startPreferenceActivity() { - Activity from = getParent() != null ? getParent() : this; - Intent intent = new Intent(from, NotesPreferenceActivity.class); - from.startActivityIfNeeded(intent, -1); - } - - private class OnListItemClickListener implements OnItemClickListener { - - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (view instanceof NotesListItem) { - NoteItemData item = ((NotesListItem) view).getItemData(); - if (mNotesListAdapter.isInChoiceMode()) { - if (item.getType() == Notes.TYPE_NOTE) { - position = position - mNotesListView.getHeaderViewsCount(); - mModeCallBack.onItemCheckedStateChanged(null, position, id, - !mNotesListAdapter.isSelectedItem(position)); - } - return; - } - - switch (mState) { - case NOTE_LIST: - if (item.getType() == Notes.TYPE_FOLDER - || item.getType() == Notes.TYPE_SYSTEM) { - openFolder(item); - } else if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in NOTE_LIST"); - } - break; - case SUB_FOLDER: - case CALL_RECORD_FOLDER: - if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in SUB_FOLDER"); - } - break; - default: - break; - } - } - } - - } - - private void startQueryDestinationFolders() { - String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; - selection = (mState == ListEditState.NOTE_LIST) ? selection: - "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; - - mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, - null, - Notes.CONTENT_NOTE_URI, - FoldersListAdapter.PROJECTION, - selection, - new String[] { - String.valueOf(Notes.TYPE_FOLDER), - String.valueOf(Notes.ID_TRASH_FOLER), - String.valueOf(mCurrentFolderId) - }, - NoteColumns.MODIFIED_DATE + " DESC"); - } - - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - if (view instanceof NotesListItem) { - mFocusNoteDataItem = ((NotesListItem) view).getItemData(); - if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { - if (mNotesListView.startActionMode(mModeCallBack) != null) { - mModeCallBack.onItemCheckedStateChanged(null, position, id, true); - mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } else { - Log.e(TAG, "startActionMode fails"); - } - } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { - mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); - } - } - return false; - } -} diff --git a/src/ui/NotesListAdapter.java b/src/ui/NotesListAdapter.java deleted file mode 100644 index 51c9cb9..0000000 --- a/src/ui/NotesListAdapter.java +++ /dev/null @@ -1,184 +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.content.Context; -import android.database.Cursor; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorAdapter; - -import net.micode.notes.data.Notes; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; - - -public class NotesListAdapter extends CursorAdapter { - private static final String TAG = "NotesListAdapter"; - private Context mContext; - private HashMap mSelectedIndex; - private int mNotesCount; - private boolean mChoiceMode; - - public static class AppWidgetAttribute { - public int widgetId; - public int widgetType; - }; - - public NotesListAdapter(Context context) { - super(context, null); - mSelectedIndex = new HashMap(); - mContext = context; - mNotesCount = 0; - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return new NotesListItem(context); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof NotesListItem) { - NoteItemData itemData = new NoteItemData(context, cursor); - ((NotesListItem) view).bind(context, itemData, mChoiceMode, - isSelectedItem(cursor.getPosition())); - } - } - - public void setCheckedItem(final int position, final boolean checked) { - mSelectedIndex.put(position, checked); - notifyDataSetChanged(); - } - - public boolean isInChoiceMode() { - return mChoiceMode; - } - - public void setChoiceMode(boolean mode) { - mSelectedIndex.clear(); - mChoiceMode = mode; - } - - public void selectAll(boolean checked) { - Cursor cursor = getCursor(); - for (int i = 0; i < getCount(); i++) { - if (cursor.moveToPosition(i)) { - if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { - setCheckedItem(i, checked); - } - } - } - } - - public HashSet getSelectedItemIds() { - HashSet itemSet = new HashSet(); - for (Integer position : mSelectedIndex.keySet()) { - if (mSelectedIndex.get(position) == true) { - Long id = getItemId(position); - if (id == Notes.ID_ROOT_FOLDER) { - Log.d(TAG, "Wrong item id, should not happen"); - } else { - itemSet.add(id); - } - } - } - - return itemSet; - } - - public HashSet getSelectedWidget() { - HashSet itemSet = new HashSet(); - for (Integer position : mSelectedIndex.keySet()) { - if (mSelectedIndex.get(position) == true) { - Cursor c = (Cursor) getItem(position); - if (c != null) { - AppWidgetAttribute widget = new AppWidgetAttribute(); - NoteItemData item = new NoteItemData(mContext, c); - widget.widgetId = item.getWidgetId(); - widget.widgetType = item.getWidgetType(); - itemSet.add(widget); - /** - * Don't close cursor here, only the adapter could close it - */ - } else { - Log.e(TAG, "Invalid cursor"); - return null; - } - } - } - return itemSet; - } - - public int getSelectedCount() { - Collection values = mSelectedIndex.values(); - if (null == values) { - return 0; - } - Iterator iter = values.iterator(); - int count = 0; - while (iter.hasNext()) { - if (true == iter.next()) { - count++; - } - } - return count; - } - - public boolean isAllSelected() { - int checkedCount = getSelectedCount(); - return (checkedCount != 0 && checkedCount == mNotesCount); - } - - public boolean isSelectedItem(final int position) { - if (null == mSelectedIndex.get(position)) { - return false; - } - return mSelectedIndex.get(position); - } - - @Override - protected void onContentChanged() { - super.onContentChanged(); - calcNotesCount(); - } - - @Override - public void changeCursor(Cursor cursor) { - super.changeCursor(cursor); - calcNotesCount(); - } - - private void calcNotesCount() { - mNotesCount = 0; - for (int i = 0; i < getCount(); i++) { - Cursor c = (Cursor) getItem(i); - if (c != null) { - if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { - mNotesCount++; - } - } else { - Log.e(TAG, "Invalid cursor"); - return; - } - } - } -} diff --git a/src/ui/NotesListItem.java b/src/ui/NotesListItem.java deleted file mode 100644 index 1221e80..0000000 --- a/src/ui/NotesListItem.java +++ /dev/null @@ -1,122 +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.content.Context; -import android.text.format.DateUtils; -import android.view.View; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - - -public class NotesListItem extends LinearLayout { - private ImageView mAlert; - private TextView mTitle; - private TextView mTime; - private TextView mCallName; - private NoteItemData mItemData; - private CheckBox mCheckBox; - - public NotesListItem(Context context) { - super(context); - inflate(context, R.layout.note_item, this); - mAlert = (ImageView) findViewById(R.id.iv_alert_icon); - mTitle = (TextView) findViewById(R.id.tv_title); - mTime = (TextView) findViewById(R.id.tv_time); - mCallName = (TextView) findViewById(R.id.tv_name); - mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); - } - - public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { - if (choiceMode && data.getType() == Notes.TYPE_NOTE) { - mCheckBox.setVisibility(View.VISIBLE); - mCheckBox.setChecked(checked); - } else { - mCheckBox.setVisibility(View.GONE); - } - - mItemData = data; - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mCallName.setVisibility(View.GONE); - mAlert.setVisibility(View.VISIBLE); - mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - mTitle.setText(context.getString(R.string.call_record_folder_name) - + context.getString(R.string.format_folder_files_count, data.getNotesCount())); - mAlert.setImageResource(R.drawable.call_record); - } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { - mCallName.setVisibility(View.VISIBLE); - mCallName.setText(data.getCallName()); - mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); - mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); - if (data.hasAlert()) { - mAlert.setImageResource(R.drawable.clock); - mAlert.setVisibility(View.VISIBLE); - } else { - mAlert.setVisibility(View.GONE); - } - } else { - mCallName.setVisibility(View.GONE); - mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - - if (data.getType() == Notes.TYPE_FOLDER) { - mTitle.setText(data.getSnippet() - + context.getString(R.string.format_folder_files_count, - data.getNotesCount())); - mAlert.setVisibility(View.GONE); - } else { - mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); - if (data.hasAlert()) { - mAlert.setImageResource(R.drawable.clock); - mAlert.setVisibility(View.VISIBLE); - } else { - mAlert.setVisibility(View.GONE); - } - } - } - mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); - - setBackground(data); - } - - private void setBackground(NoteItemData data) { - int id = data.getBgColorId(); - if (data.getType() == Notes.TYPE_NOTE) { - if (data.isSingle() || data.isOneFollowingFolder()) { - setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); - } else if (data.isLast()) { - setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); - } else if (data.isFirst() || data.isMultiFollowingFolder()) { - setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); - } else { - setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); - } - } else { - setBackgroundResource(NoteItemBgResources.getFolderBgRes()); - } - } - - public NoteItemData getItemData() { - return mItemData; - } -} diff --git a/src/ui/NotesPreferenceActivity.java b/src/ui/NotesPreferenceActivity.java deleted file mode 100644 index 07c5f7e..0000000 --- a/src/ui/NotesPreferenceActivity.java +++ /dev/null @@ -1,388 +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.accounts.Account; -import android.accounts.AccountManager; -import android.app.ActionBar; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.ContentValues; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.remote.GTaskSyncService; - - -public class NotesPreferenceActivity extends PreferenceActivity { - public static final String PREFERENCE_NAME = "notes_preferences"; - - public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - - public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - - public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; - - private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - - private static final String AUTHORITIES_FILTER_KEY = "authorities"; - - private PreferenceCategory mAccountCategory; - - private GTaskReceiver mReceiver; - - private Account[] mOriAccounts; - - private boolean mHasAddedAccount; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - /* using the app icon for navigation */ - getActionBar().setDisplayHomeAsUpEnabled(true); - - addPreferencesFromResource(R.xml.preferences); - mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); - mReceiver = new GTaskReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); - registerReceiver(mReceiver, filter); - - mOriAccounts = null; - View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); - getListView().addHeaderView(header, null, true); - } - - @Override - protected void onResume() { - super.onResume(); - - // need to set sync account automatically if user has added a new - // account - if (mHasAddedAccount) { - Account[] accounts = getGoogleAccounts(); - if (mOriAccounts != null && accounts.length > mOriAccounts.length) { - for (Account accountNew : accounts) { - boolean found = false; - for (Account accountOld : mOriAccounts) { - if (TextUtils.equals(accountOld.name, accountNew.name)) { - found = true; - break; - } - } - if (!found) { - setSyncAccount(accountNew.name); - break; - } - } - } - } - - refreshUI(); - } - - @Override - protected void onDestroy() { - if (mReceiver != null) { - unregisterReceiver(mReceiver); - } - super.onDestroy(); - } - - private void loadAccountPreference() { - mAccountCategory.removeAll(); - - Preference accountPref = new Preference(this); - final String defaultAccount = getSyncAccountName(this); - accountPref.setTitle(getString(R.string.preferences_account_title)); - accountPref.setSummary(getString(R.string.preferences_account_summary)); - accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - if (!GTaskSyncService.isSyncing()) { - if (TextUtils.isEmpty(defaultAccount)) { - // the first time to set account - showSelectAccountAlertDialog(); - } else { - // if the account has already been set, we need to promp - // user about the risk - showChangeAccountConfirmAlertDialog(); - } - } else { - Toast.makeText(NotesPreferenceActivity.this, - R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) - .show(); - } - return true; - } - }); - - mAccountCategory.addPreference(accountPref); - } - - private void loadSyncButton() { - Button syncButton = (Button) findViewById(R.id.preference_sync_button); - TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - - // set button state - if (GTaskSyncService.isSyncing()) { - syncButton.setText(getString(R.string.preferences_button_sync_cancel)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - GTaskSyncService.cancelSync(NotesPreferenceActivity.this); - } - }); - } else { - syncButton.setText(getString(R.string.preferences_button_sync_immediately)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - GTaskSyncService.startSync(NotesPreferenceActivity.this); - } - }); - } - syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); - - // set last sync time - if (GTaskSyncService.isSyncing()) { - lastSyncTimeView.setText(GTaskSyncService.getProgressString()); - lastSyncTimeView.setVisibility(View.VISIBLE); - } else { - long lastSyncTime = getLastSyncTime(this); - if (lastSyncTime != 0) { - lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, - DateFormat.format(getString(R.string.preferences_last_sync_time_format), - lastSyncTime))); - lastSyncTimeView.setVisibility(View.VISIBLE); - } else { - lastSyncTimeView.setVisibility(View.GONE); - } - } - } - - private void refreshUI() { - loadAccountPreference(); - loadSyncButton(); - } - - private void showSelectAccountAlertDialog() { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - - View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); - TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); - titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); - TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); - subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - - dialogBuilder.setCustomTitle(titleView); - dialogBuilder.setPositiveButton(null, null); - - Account[] accounts = getGoogleAccounts(); - String defAccount = getSyncAccountName(this); - - mOriAccounts = accounts; - mHasAddedAccount = false; - - if (accounts.length > 0) { - CharSequence[] items = new CharSequence[accounts.length]; - final CharSequence[] itemMapping = items; - int checkedItem = -1; - int index = 0; - for (Account account : accounts) { - if (TextUtils.equals(account.name, defAccount)) { - checkedItem = index; - } - items[index++] = account.name; - } - dialogBuilder.setSingleChoiceItems(items, checkedItem, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setSyncAccount(itemMapping[which].toString()); - dialog.dismiss(); - refreshUI(); - } - }); - } - - View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); - dialogBuilder.setView(addAccountView); - - final AlertDialog dialog = dialogBuilder.show(); - addAccountView.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mHasAddedAccount = true; - Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); - intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { - "gmail-ls" - }); - startActivityForResult(intent, -1); - dialog.dismiss(); - } - }); - } - - private void showChangeAccountConfirmAlertDialog() { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - - View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); - TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); - titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, - getSyncAccountName(this))); - TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); - subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); - dialogBuilder.setCustomTitle(titleView); - - CharSequence[] menuItemArray = new CharSequence[] { - getString(R.string.preferences_menu_change_account), - getString(R.string.preferences_menu_remove_account), - getString(R.string.preferences_menu_cancel) - }; - dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - if (which == 0) { - showSelectAccountAlertDialog(); - } else if (which == 1) { - removeSyncAccount(); - refreshUI(); - } - } - }); - dialogBuilder.show(); - } - - private Account[] getGoogleAccounts() { - AccountManager accountManager = AccountManager.get(this); - return accountManager.getAccountsByType("com.google"); - } - - private void setSyncAccount(String account) { - if (!getSyncAccountName(this).equals(account)) { - SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = settings.edit(); - if (account != null) { - editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); - } else { - editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); - } - editor.commit(); - - // clean up last sync time - setLastSyncTime(this, 0); - - // clean up local gtask related info - new Thread(new Runnable() { - public void run() { - ContentValues values = new ContentValues(); - values.put(NoteColumns.GTASK_ID, ""); - values.put(NoteColumns.SYNC_ID, 0); - getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); - } - }).start(); - - Toast.makeText(NotesPreferenceActivity.this, - getString(R.string.preferences_toast_success_set_accout, account), - Toast.LENGTH_SHORT).show(); - } - } - - private void removeSyncAccount() { - SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = settings.edit(); - if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { - editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); - } - if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { - editor.remove(PREFERENCE_LAST_SYNC_TIME); - } - editor.commit(); - - // clean up local gtask related info - new Thread(new Runnable() { - public void run() { - ContentValues values = new ContentValues(); - values.put(NoteColumns.GTASK_ID, ""); - values.put(NoteColumns.SYNC_ID, 0); - getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); - } - }).start(); - } - - public static String getSyncAccountName(Context context) { - SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, - Context.MODE_PRIVATE); - return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); - } - - public static void setLastSyncTime(Context context, long time) { - SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, - Context.MODE_PRIVATE); - SharedPreferences.Editor editor = settings.edit(); - editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); - editor.commit(); - } - - public static long getLastSyncTime(Context context) { - SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, - Context.MODE_PRIVATE); - return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); - } - - private class GTaskReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - refreshUI(); - if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { - TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - syncStatus.setText(intent - .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); - } - - } - } - - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - Intent intent = new Intent(this, NotesListActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - default: - return false; - } - } -} diff --git a/src/widget/NoteWidgetProvider.java b/src/widget/NoteWidgetProvider.java deleted file mode 100644 index ec6f819..0000000 --- a/src/widget/NoteWidgetProvider.java +++ /dev/null @@ -1,132 +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.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 deleted file mode 100644 index adcb2f7..0000000 --- a/src/widget/NoteWidgetProvider_2x.java +++ /dev/null @@ -1,47 +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.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 deleted file mode 100644 index c12a02e..0000000 --- a/src/widget/NoteWidgetProvider_4x.java +++ /dev/null @@ -1,46 +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.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; - } -} From ff2ee1e025f1ac6162b19c48c0f7834ce33654ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Fri, 7 Apr 2023 14:39:59 +0800 Subject: [PATCH 05/16] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/210340080 徐海麓 泛读报告.doc | Bin 10240 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/210340080 徐海麓 泛读报告.doc diff --git a/doc/210340080 徐海麓 泛读报告.doc b/doc/210340080 徐海麓 泛读报告.doc deleted file mode 100644 index 6a7c058af8f7446d506e71a7a5c40db32f518348..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeI1eQZ`&9mmhT&-0Y0Ewp8n!U`J?>=`Q?l-p8X#>*(Q8&F(ZC{h&0TMM>>mI-CT zrb)A*VrD{(8j0E57BP}6F~!ZyzZ}M8QxkQ@Y#A{oU9!dL)IH9BByn6n-*cZ^?mAjp z-h|oT&F7w%d*1Fjzw0K<+UybI#8-Igxk=zpzqdjEhI%bH48f zk;dR1l^duBKBTRQ>=?#zHQpM}F}O*{g*?cIu`mt_pb$_2GahaNXS9$3{xah_(p%N4-XHLI(BR8iLzXbL)X-wvu*>$q$l9}r z`lF*hXoZmnnpa`{&GubeKh}y!@*%yY+4?nahx`7Tv*nt^h`K?2r!i4K$`+_B&4115 zq2|Ay3(fxtPz)2H1SY{`xEV@e3QPqJ`>ikyQZOB6z-^F*+hHc$0c9`??u5HwHq3!? zmbc0voZ!Yb=BMXBx|x4J!vJ>W&2F|w1%a!*G^J|5N&m#`}dP}%05*q z;?&%JTczg26o-Va%9AcNqXkWr*}$mnX0%#p*EY1Wg4%^=-74I9=;bzas{$RHht4iU z?`qKJO5*RfzAED9)8;v7Wd*)Ud>(EMsDwq}5nE%m_Iz8^u1l4{#$WyJzW$rnpDdqJ z^v&-u{jsU9`bl#J>2BmEJF*F6Q?`Tbj&xe%Al=c}AA(Q@j&<1A z16}2pgsv)99TE@J4$TFm`-|LE?h#Nqz1gthq*H4dy#~f`Kbq3Y>}etAO5AqzrR!KRRBOX>uGePs|Qlx;MP~WR>Wpjp8MjeU%s*g&fmfE}E zQU%*J;`LYdF|4l{O*}xK9^~GI4s?vLMN@hIsI3jl9mJ(orQRMm0C{ruWud{CW=sm(e4g;6rzRL`el^L!y(0*B! zKVx>=$oLelX4)IHM_*P>HV5v+dNw#JXYR z0ONiot=$dt;@5dYT{Bi6N~1E`1DDYQ3wFKuc&R^*|GRPg$CR5*o6DMyJp7MuA3v^` zQTfF8085en%X1(Jc`z1=pcJNpz5~y`{}*G<{NcBs`2M(#UNBixH`VN@D#o5sDJt(Hl>O|i+neX+_$*HrW@y1@5_V^WVj{jP}o+-2X~$?oc0 zARAgY?v;db7CAZkM=H0CRVmr5)(QTLgWNOYh3wBbChim(epYRI7oSvKXkYTltSkRO zh`Oqx`mUwuokJPUUuHCYnD1h4*tgMi3-~J)BayIuVcyUkNg3Bm+OX}X*?4&k`XHlC z7?iRIQs6-~G{Sax2)dyM2Fn+g8@egdUP?Lt@A){Zss5+$>dYqC$6{z))3WEQyMq1Z z9qCvs9ji!dr@4mJw1F*_#yZEW!`)ivvfP%KwX0n-aAn=L+8$G~yBMo>D6zJGGoi=C z&6u|1$5ZU;>bxw5seQ`Y{)iCd2>^P64#sd>VNsq>+GsORory$F70VMy0lGaZHqVNo2oZ*jHB~e z>v3ay15Z4RNfP6p-*>mBQ(t#%G4=a3x!thuvKw~RdPvI`rhBN~Eljws8;`khzudh2 zXxf}+kHCu0dLT~?Tf%$Ugx|^bdpqGd@gCqa?W(k`(Q8l1JG;I^q0sU7Mpc$CkYC!yb^|M#y6&2*ph-?8a=Y;=`1uf)_?z zwlH|pIVhdYGPsz7&gC33e#x9PFPc}~U%3-gC8?WJx1?sK7R+!w*Nb@xFW1ZW#(9O_ zcyFRN$#Z(3~6?w&8iI=2(OZ?6LTmCzj-Kyl6XA&>4;Je*Aw7C}YpAQ3_ymShoXq+Oy5jhlId$Rd->sBoKrnhH%U;}Pb|D^q4OZn)$f3n1(&Lm7L#tJm0|c m!#2F0Glu&qXCvr&9VOSBM>w-<=S+AV=fum5UrmFrL-{ZC3i{*# From 0ceb2126daa472dca5099643880774fcb40081ca Mon Sep 17 00:00:00 2001 From: sxg <2363882464@qq.com> Date: Fri, 14 Apr 2023 15:03:15 +0800 Subject: [PATCH 06/16] 1 --- src/model/Note.java | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/model/Note.java b/src/model/Note.java index 6706cf6..5042eca 100644 --- a/src/model/Note.java +++ b/src/model/Note.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.OperationApplicationException; import android.net.Uri; import android.os.RemoteException; -import android.util.Log; +import android.util.Log;//输出日志 import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.CallNote; @@ -39,28 +39,28 @@ public class Note { private NoteData mNoteData; private static final String TAG = "Note"; /** - * Create a new note id for adding a new note to databases + * 为数据库添加的新便签创建一个新ID */ public static synchronized long getNewNoteId(Context context, long folderId) { - // Create a new note in the database + //在数据库中创建新便签 ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis(); - values.put(NoteColumns.CREATED_DATE, createdTime); - values.put(NoteColumns.MODIFIED_DATE, createdTime); - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + long createdTime = System.currentTimeMillis();//便签创建时间 + values.put(NoteColumns.CREATED_DATE, createdTime);//创建时间 + values.put(NoteColumns.MODIFIED_DATE, createdTime);//修改时间 + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);//便签类型 values.put(NoteColumns.LOCAL_MODIFIED, 1); - values.put(NoteColumns.PARENT_ID, folderId); + values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - + //ContentResolver用于外部应用(比如输入法等软件)对ContentProvider中的数据进行增删查改等操作 long noteId = 0; try { noteId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); noteId = 0; - } + }//采用 try catch 语句来捕获输入便签ID异常并处理 if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId); + throw new IllegalStateException("Wrong note id:" + noteId);//非法状态警告:"ID错误" } return noteId; } @@ -68,7 +68,7 @@ public class Note { public Note() { mNoteDiffValues = new ContentValues(); mNoteData = new NoteData(); - } + }//定义两个变量来存储新便签数据,mNoteDiffValues:便签属性、mNoteData:便签数据 public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); From fa0483372f5e82acb8015fed32759697a1060506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Thu, 20 Apr 2023 22:14:22 +0800 Subject: [PATCH 07/16] 1 --- ...米便签开源代码的泛读报告.docx | Bin 216950 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/小米便签开源代码的泛读报告.docx diff --git a/doc/小米便签开源代码的泛读报告.docx b/doc/小米便签开源代码的泛读报告.docx deleted file mode 100644 index 78ab019e85790ab2cc59d0d6109c3949c13b7982..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216950 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 Sfy6iu{$+!r&(*-Z!}wo5E8T|x From 3ea740f2f054b83d574d19ec72396929ae2c0011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Thu, 20 Apr 2023 22:16:31 +0800 Subject: [PATCH 08/16] 11 --- src/model/Note.java | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/model/Note.java b/src/model/Note.java index 5042eca..6706cf6 100644 --- a/src/model/Note.java +++ b/src/model/Note.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.OperationApplicationException; import android.net.Uri; import android.os.RemoteException; -import android.util.Log;//输出日志 +import android.util.Log; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.CallNote; @@ -39,28 +39,28 @@ public class Note { private NoteData mNoteData; private static final String TAG = "Note"; /** - * 为数据库添加的新便签创建一个新ID + * Create a new note id for adding a new note to databases */ public static synchronized long getNewNoteId(Context context, long folderId) { - //在数据库中创建新便签 + // Create a new note in the database ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis();//便签创建时间 - values.put(NoteColumns.CREATED_DATE, createdTime);//创建时间 - values.put(NoteColumns.MODIFIED_DATE, createdTime);//修改时间 - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);//便签类型 + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); values.put(NoteColumns.LOCAL_MODIFIED, 1); - values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格中 + values.put(NoteColumns.PARENT_ID, folderId); Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - //ContentResolver用于外部应用(比如输入法等软件)对ContentProvider中的数据进行增删查改等操作 + long noteId = 0; try { noteId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); noteId = 0; - }//采用 try catch 语句来捕获输入便签ID异常并处理 + } if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId);//非法状态警告:"ID错误" + throw new IllegalStateException("Wrong note id:" + noteId); } return noteId; } @@ -68,7 +68,7 @@ public class Note { public Note() { mNoteDiffValues = new ContentValues(); mNoteData = new NoteData(); - }//定义两个变量来存储新便签数据,mNoteDiffValues:便签属性、mNoteData:便签数据 + } public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); From ecdfc10b33493e8b98212276896f2b4e006229a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Thu, 20 Apr 2023 22:17:45 +0800 Subject: [PATCH 09/16] =?UTF-8?q?=E6=B3=9B=E8=AF=BB=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/210340068 潘孝峰小组.docx | Bin 405651 -> 405533 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/210340068 潘孝峰小组.docx b/doc/210340068 潘孝峰小组.docx index 496b7b7b330892305f81f5588edbc3afc043e516..68ed02d1c1dafcdd7e03d5e3b7830ead30a40ec6 100644 GIT binary patch delta 17140 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 z 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 delta 17300 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 From 5f72a84cd57d90b77abc3279779414a582b2b741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Thu, 20 Apr 2023 22:18:11 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=E6=BA=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Note.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/model/Note.java b/src/model/Note.java index 6706cf6..f291329 100644 --- a/src/model/Note.java +++ b/src/model/Note.java @@ -251,3 +251,4 @@ public class Note { } } } + \ No newline at end of file From fe7ca3fa8b92acc67b8d3c9b501a0b772374fe53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BD=98=E5=AD=9D=E5=B3=B0?= <2557830190@qq.com> Date: Fri, 21 Apr 2023 12:57:33 +0800 Subject: [PATCH 11/16] 1 --- two/model/Note.java | 266 ------------------ two/model/WorkingNote.java | 377 ------------------------- two/tool/BackupUtils.java | 351 ----------------------- two/tool/DataUtils.java | 303 -------------------- two/tool/GTaskStringUtils.java | 113 -------- two/tool/ResourceParser.java | 183 ------------ two/ui/AlarmAlertActivity.java | 177 ------------ two/ui/AlarmInitReceiver.java | 66 ----- two/ui/AlarmReceiver.java | 30 -- two/ui/DateTimePicker.java | 490 --------------------------------- 10 files changed, 2356 deletions(-) delete mode 100644 two/model/Note.java delete mode 100644 two/model/WorkingNote.java delete mode 100644 two/tool/BackupUtils.java delete mode 100644 two/tool/DataUtils.java delete mode 100644 two/tool/GTaskStringUtils.java delete mode 100644 two/tool/ResourceParser.java delete mode 100644 two/ui/AlarmAlertActivity.java delete mode 100644 two/ui/AlarmInitReceiver.java delete mode 100644 two/ui/AlarmReceiver.java delete mode 100644 two/ui/DateTimePicker.java diff --git a/two/model/Note.java b/two/model/Note.java deleted file mode 100644 index a1fc706..0000000 --- a/two/model/Note.java +++ /dev/null @@ -1,266 +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.model; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.net.Uri; -import android.os.RemoteException; -import android.util.Log;//输出日志 - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; - -import java.rmi.RemoteException; -import java.util.ArrayList; - - -public class Note { - private ContentValues mNoteDiffValues; - private NoteData mNoteData; - private static final String TAG = "Note"; - //为数据库添加的新便签创建一个新ID - - public static synchronized long getNewNoteId(Context context, long folderId) { - //在数据库中创建新便签 - ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis();//便签创建时间 - values.put(NoteColumns.CREATED_DATE, createdTime);//创建时间 - values.put(NoteColumns.MODIFIED_DATE, createdTime);//修改时间 - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);//便签类型 - values.put(NoteColumns.LOCAL_MODIFIED, 1); - values.put(NoteColumns.PARENT_ID, folderId);//将数据写入数据库表格中 - Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - //ContentResolver用于外部应用(比如输入法等软件)对ContentProvider中的数据进行增删查改等操作 - long noteId = 0; - try { - noteId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - noteId = 0; - }//采用 try catch 语句来捕获输入便签ID异常并处理 - if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId);//非法状态警告:"ID错误" - } - return noteId; - } - - public Note() { - mNoteDiffValues = new ContentValues(); - mNoteData = new NoteData(); - }//定义两个变量来存储新便签数据,mNoteDiffValues:便签属性、mNoteData:便签数据 - - public void setNoteValue(String key, String value) {//设置便签种类 - mNoteDiffValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - }//新建标签属性的数据 - - public void setTextData(String key, String value) { - mNoteData.setTextData(key, value); - } - //便签文本内容数据 - - public void setTextDataId(long id) { - mNoteData.setTextDataId(id); - } - //文本数据ID - - public long getTextDataId() { - return mNoteData.mTextDataId; - } - //获取文本数据ID - - public void setCallDataId(long id) { - mNoteData.setCallDataId(id); - } - //电话号码数据ID - - public void setCallData(String key, String value) { - mNoteData.setCallData(key, value); - } - //获取电话号码数据ID - - public boolean isLocalModified() { - return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); - }//判断便签是否有修改 - /** - * Boolean变量存储为16位的数值形式,但只能是True或False。 - * 当其他的数值类型转换为Boolean类型时,0变False,其他值变True。当Boolean值转换为其他的数据类型时,False变0,而True变-1 - */ - - public boolean syncNote(Context context, long noteId) { - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - }//修改后的便签ID错误判断;IllegalArgumentException:不合法的参数异常 - - if (!isLocalModified()) { - return true; - } - - /** - * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and - * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the - * note data info - */ - if (context.getContentResolver().update( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, - null) == 0) { - Log.e(TAG, "Update note error, should not happen"); - // Do not return, fall through - } - mNoteDiffValues.clear(); - - if (mNoteData.isLocalModified() - && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { - return false; - } - - return true; - }//判断数据有没有同步 - - private class NoteData {//定义便签内容的数据类:文本数据:ContentValues类;电话号码:ContentValues类 - private long mTextDataId; - - private ContentValues mTextDataValues; - /** - * Contentvalues类只能存储基本类型的数据 - * Contentvalues类中有很多用于存放数据的Put方法,但是这些put方法只会接收基本数据类型的参数,因此存储的时候也就只能存储基本数据类型。 - */ - private long mCallDataId; - - private ContentValues mCallDataValues; - - private static final String TAG = "NoteData"; - - public NoteData() { - mTextDataValues = new ContentValues(); - mCallDataValues = new ContentValues(); - mTextDataId = 0; - mCallDataId = 0; - } - - boolean isLocalModified() {//实现修改便签的函数 - return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; - } - - void setTextDataId(long id) { - if(id <= 0) { - throw new IllegalArgumentException("Text data id should larger than 0"); - } - mTextDataId = id; - } - - void setCallDataId(long id) { - if (id <= 0) { - throw new IllegalArgumentException("Call data id should larger than 0"); - } - mCallDataId = id; - } - - void setCallData(String key, String value) { - mCallDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - void setTextData(String key, String value) { - mTextDataValues.put(key, value); - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - Uri pushIntoContentResolver(Context context, long noteId) {//将修改的便签数据通过Uri存储到数据库 - /** - * Check for safety - */ - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - ArrayList operationList = new ArrayList(); - ContentProviderOperation.Builder builder = null; - //数据库操作 - - if(mTextDataValues.size() > 0) { - mTextDataValues.put(DataColumns.NOTE_ID, noteId); - if (mTextDataId == 0) { - mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mTextDataValues); - try { - setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new text data fail with noteId" + noteId); - mTextDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mTextDataId)); - builder.withValues(mTextDataValues); - operationList.add(builder.build()); - } - mTextDataValues.clear(); - }//将便签文本数据存储至DataColumns - - if(mCallDataValues.size() > 0) { - mCallDataValues.put(DataColumns.NOTE_ID, noteId); - if (mCallDataId == 0) { - mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mCallDataValues); - try { - setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new call data fail with noteId" + noteId); - mCallDataValues.clear(); - return null; - } - } else { - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mCallDataId)); - builder.withValues(mCallDataValues); - operationList.add(builder.build()); - } - mCallDataValues.clear(); - }//将便签电话号码数据存储至DataColumns - - if (operationList.size() > 0) { - try { - ContentProviderResult[] results = context.getContentResolver().applyBatch( - Notes.AUTHORITY, operationList); - return (results == null || results.length == 0 || results[0] == null) ? null - : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } - }//存储异常处理办法 - return null; - } - } -} diff --git a/two/model/WorkingNote.java b/two/model/WorkingNote.java deleted file mode 100644 index 894a7d1..0000000 --- a/two/model/WorkingNote.java +++ /dev/null @@ -1,377 +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.model; - -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Log; -/** - android.util.Log常用的方法有以下5个:Log.v() Log.d() Log.i() Log.w() 以及 Log.e() 。根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR。 - Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v("",""); - Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择. - Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息 - Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。 - Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。 - */ - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.tool.ResourceParser.NoteBgResources; - - -public class WorkingNote { - // Note for the working note - private Note mNote; - // Note Id - private long mNoteId; - // Note content - private String mContent; - // Note mode - private int mMode; - - private long mAlertDate; - - private long mModifiedDate; - - private int mBgColorId; - - private int mWidgetId; - - private int mWidgetType; - - private long mFolderId; - - private Context mContext; - - private static final String TAG = "WorkingNote"; - - private boolean mIsDeleted; - - private NoteSettingChangedListener mNoteSettingStatusListener; - - public static final String[] DATA_PROJECTION = new String[] { - DataColumns.ID, - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - };//新建字符串数组DATA_PROJECTION - - public static final String[] NOTE_PROJECTION = new String[] { - NoteColumns.PARENT_ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - NoteColumns.MODIFIED_DATE - };//新建字符串数组NOTE_PROJECTION - - private static final int DATA_ID_COLUMN = 0; - - private static final int DATA_CONTENT_COLUMN = 1; - - private static final int DATA_MIME_TYPE_COLUMN = 2; - - private static final int DATA_MODE_COLUMN = 3; - - private static final int NOTE_PARENT_ID_COLUMN = 0; - - private static final int NOTE_ALERTED_DATE_COLUMN = 1; - - private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - - private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - - private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - - // New note construct - private WorkingNote(Context context, long folderId) { - mContext = context; - mAlertDate = 0; - mModifiedDate = System.currentTimeMillis(); - mFolderId = folderId; - mNote = new Note(); - mNoteId = 0; - mIsDeleted = false; - mMode = 0; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - } - - // Existing note construct - private WorkingNote(Context context, long noteId, long folderId) {//构造函数 - mContext = context; - mNoteId = noteId; - mFolderId = folderId; - mIsDeleted = false; - mNote = new Note(); - loadNote(); - } - - private void loadNote() {//加载便签调用query函数找到第一个项目 - Cursor cursor = mContext.getContentResolver().query( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, - null, null); - //query是一个查询参数类,封装了查询条件,分页,排序等功能 - - if (cursor != null) { - if (cursor.moveToFirst()) { - mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); - mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); - mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); - mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); - mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); - }//便签存在时储存信息 - cursor.close(); - } else { - Log.e(TAG, "No note with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note with id " + mNoteId); - }//便签不存在时报错 - loadNoteData(); - } - - private void loadNoteData() {//加载便签数据 - Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, - DataColumns.NOTE_ID + "=?", new String[] { - String.valueOf(mNoteId) - }, null); - - if (cursor != null) {//便签存在 - if (cursor.moveToFirst()) { - do { - String type = cursor.getString(DATA_MIME_TYPE_COLUMN); - if (DataConstants.NOTE.equals(type)) { - mContent = cursor.getString(DATA_CONTENT_COLUMN); - mMode = cursor.getInt(DATA_MODE_COLUMN); - mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); - } else if (DataConstants.CALL_NOTE.equals(type)) { - mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); - } else { - Log.d(TAG, "Wrong note type with type:" + type); - } - } while (cursor.moveToNext()); - }//do-while循环查阅所有信息,直到cursor.moveToNext()值为空 - cursor.close(); - } else { - Log.e(TAG, "No data with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); - } - } - - public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, - int widgetType, int defaultBgColorId) { - WorkingNote note = new WorkingNote(context, folderId); - note.setBgColorId(defaultBgColorId); - note.setWidgetId(widgetId); - note.setWidgetType(widgetType); - return note; - }//创建一个空便签并传输参数:文本,文件夹名称,窗口样式,背景颜色 - - public static WorkingNote load(Context context, long id) { - return new WorkingNote(context, id, 0); - } - - public synchronized boolean saveNote() { - if (isWorthSaving()) {//调用isWorthSaving类 - if (!existInDatabase()) { - if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { - Log.e(TAG, "Create new note fail with id:" + mNoteId); - return false; - } - } - - mNote.syncNote(mContext, mNoteId); - - /** - * Update widget content if there exist any widget of this note - */ - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE - && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - return true; - } else { - return false; - } - } - - public boolean existInDatabase() { - return mNoteId > 0; - } - - private boolean isWorthSaving() {//判断便签是否能保存 - if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) - || (existInDatabase() && !mNote.isLocalModified())) {//被删除、内容为空、已经保存过了 - return false; - } else { - return true; - } - } - - public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {//设置便签的设置列表 - mNoteSettingStatusListener = l; - } - - public void setAlertDate(long date, boolean set) {//设置警报数据 - if (date != mAlertDate) { - mAlertDate = date; - mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); - } - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onClockAlertChanged(date, set); - } - } - - public void markDeleted(boolean mark) {//设置删除标记 - mIsDeleted = mark; - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged();//调用mNoteSettingStatusListener - } - } - - public void setBgColorId(int id) {//shezhi背景颜色 - if (id != mBgColorId) { - mBgColorId = id; - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onBackgroundColorChanged(); - } - mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); - } - } - - public void setCheckListMode(int mode) {//设置检查列表模式 - if (mMode != mode) { - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); - } - mMode = mode; - mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); - } - } - - public void setWidgetType(int type) {//设置窗口小部件样式 - if (type != mWidgetType) { - mWidgetType = type; - mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); - } - } - - public void setWidgetId(int id) {//设置窗口小部件ID - if (id != mWidgetId) { - mWidgetId = id; - mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); - } - } - - public void setWorkingText(String text) {//设置WorkingText - if (!TextUtils.equals(mContent, text)) { - mContent = text; - mNote.setTextData(DataColumns.CONTENT, mContent); - } - } - - public void convertToCallNote(String phoneNumber, long callDate) {//转变电话号码的信息:phoneNumber,callDate - mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); - mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); - mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); - } - - public boolean hasClockAlert() {//判断是否有时钟 - return (mAlertDate > 0 ? true : false); - } - - public String getContent() {//获取便签内容 - return mContent; - } - - public long getAlertDate() {//获取警报数据 - return mAlertDate; - } - - public long getModifiedDate() {//获取修改后的数据 - return mModifiedDate; - } - - public int getBgColorResId() {//获取背景颜色ID来源 - return NoteBgResources.getNoteBgResource(mBgColorId); - } - - public int getBgColorId() {//获取背景颜色ID - return mBgColorId; - } - - public int getTitleBgResId() {//获取标题颜色ID - return NoteBgResources.getNoteTitleBgResource(mBgColorId); - } - - public int getCheckListMode() {//获取检查列表模式 - return mMode; - } - - public long getNoteId() {//获取便签ID - return mNoteId; - } - - public long getFolderId() {//获取文件夹ID - return mFolderId; - } - - public int getWidgetId() {//获取窗口小部件ID - return mWidgetId; - } - - public int getWidgetType() {//获取窗口小部件样式 - return mWidgetType; - } - - public interface NoteSettingChangedListener {//创建接口NoteSettingChangedListener,监视便签更改 - /** - * Called when the background color of current note has just changed - */ - void onBackgroundColorChanged(); - - /** - * Called when user set clock - */ - void onClockAlertChanged(long date, boolean set); - - /** - * Call when user create note from widget - */ - void onWidgetChanged(); - - /** - * Call when switch between check list mode and normal mode - * @param oldMode is previous mode before change - * @param newMode is new mode - */ - void onCheckListModeChanged(int oldMode, int newMode); - } -} diff --git a/two/tool/BackupUtils.java b/two/tool/BackupUtils.java deleted file mode 100644 index ca7c9d9..0000000 --- a/two/tool/BackupUtils.java +++ /dev/null @@ -1,351 +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.tool; - -import android.content.Context; -import android.database.Cursor; -import android.os.Environment; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.util.Log; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - - -public class BackupUtils { - private static final String TAG = "BackupUtils"; - // Singleton stuff - private static BackupUtils sInstance; - - public static synchronized BackupUtils getInstance(Context context) { - /** - * synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: - *   1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; - *   2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; - *   3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; - *   4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。 - */ - if (sInstance == null) {//如果当前备份不存在,则新建一个 - sInstance = new BackupUtils(context); - } - return sInstance; - } - - /** - * Following states are signs to represents backup or restore - * status - */ - // Currently, the sdcard is not mounted - public static final int STATE_SD_CARD_UNMOUONTED = 0; - // The backup file not exist 备份文件夹不存在 - public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; - // The data is not well formated, may be changed by other programs 数据未被格式化,可能被其他应用修改 - public static final int STATE_DATA_DESTROIED = 2; - // Some run-time exception which causes restore or backup fails 访问超时 - public static final int STATE_SYSTEM_ERROR = 3; - // Backup or restore success 成功储存 - public static final int STATE_SUCCESS = 4; - - private TextExport mTextExport; - - private BackupUtils(Context context) {//初始化函数 - mTextExport = new TextExport(context); - } - - private static boolean externalStorageAvailable() {//检查外部存储功能是否可用 - return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); - } - - public int exportToText() { - return mTextExport.exportToText(); - } - - public String getExportedTextFileName() { - return mTextExport.mFileName; - } - - public String getExportedTextFileDir() { - return mTextExport.mFileDirectory; - } - - private static class TextExport { - private static final String[] NOTE_PROJECTION = { - NoteColumns.ID, - NoteColumns.MODIFIED_DATE, - NoteColumns.SNIPPET, - NoteColumns.TYPE - }; - - private static final int NOTE_COLUMN_ID = 0; - - private static final int NOTE_COLUMN_MODIFIED_DATE = 1; - - private static final int NOTE_COLUMN_SNIPPET = 2; - - private static final String[] DATA_PROJECTION = { - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - }; - - private static final int DATA_COLUMN_CONTENT = 0; - - private static final int DATA_COLUMN_MIME_TYPE = 1; - - private static final int DATA_COLUMN_CALL_DATE = 2; - - private static final int DATA_COLUMN_PHONE_NUMBER = 4; - - private final String [] TEXT_FORMAT; - private static final int FORMAT_FOLDER_NAME = 0; - private static final int FORMAT_NOTE_DATE = 1; - private static final int FORMAT_NOTE_CONTENT = 2; - - private Context mContext; - private String mFileName; - private String mFileDirectory; - - public TextExport(Context context) { - TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); - mContext = context; - mFileName = ""; - mFileDirectory = ""; - } - - private String getFormat(int id) {//获取文本的组成部分 - return TEXT_FORMAT[id]; - } - - /** - * Export the folder identified by folder id to text - */ - private void exportFolderToText(String folderId, PrintStream ps) { - // Query notes belong to this folder 通过查询亲代ID是文件夹ID的Note来选出制定ID文件夹下的Note - Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { - folderId - }, null); - - if (notesCursor != null) { - if (notesCursor.moveToFirst()) { - do { - // Print note's last modified date 里面保存了这份Note的最后修改日期 - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note - String noteId = notesCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps);//将便签导出成text文档 - } while (notesCursor.moveToNext()); - } - notesCursor.close(); - } - } - - /** - * Export note identified by id to a print stream - */ - private void exportNoteToText(String noteId, PrintStream ps) { - Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, - DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { - noteId - }, null); - - if (dataCursor != null) {//利用光标来扫描内容,区别为callnote和note两种,靠ps.printline输出 - if (dataCursor.moveToFirst()) { - do { - String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); - if (DataConstants.CALL_NOTE.equals(mimeType)) { - // Print phone number - String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); - long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); - String location = dataCursor.getString(DATA_COLUMN_CONTENT); - - if (!TextUtils.isEmpty(phoneNumber)) {//判断电话号码是否为空字符 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - phoneNumber)); - } - // Print call date 输出拨号日期 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat - .format(mContext.getString(R.string.format_datetime_mdhm), - callDate))); - // Print call attachment location 输出电话号码附件位置 - if (!TextUtils.isEmpty(location)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - location)); - } - } else if (DataConstants.NOTE.equals(mimeType)) { - String content = dataCursor.getString(DATA_COLUMN_CONTENT); - if (!TextUtils.isEmpty(content)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - content)); - } - } - } while (dataCursor.moveToNext()); - } - dataCursor.close(); - } - // print a line separator between note - try { - ps.write(new byte[] { - Character.LINE_SEPARATOR, Character.LETTER_NUMBER - }); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } - } - - /** - * Note will be exported as text which is user readable - */ - public int exportToText() {//新建函数,调用exportFolder和exportNote - if (!externalStorageAvailable()) { - Log.d(TAG, "Media was not mounted"); - return STATE_SD_CARD_UNMOUONTED; - } - - PrintStream ps = getExportToTextPrintStream(); - if (ps == null) { - Log.e(TAG, "get print stream error"); - return STATE_SYSTEM_ERROR; - } - // First export folder and its notes 导出文件夹 - Cursor folderCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, - "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " - + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); - - if (folderCursor != null) { - if (folderCursor.moveToFirst()) { - do { - // Print folder's name 输出文件夹名称 - String folderName = ""; - if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { - folderName = mContext.getString(R.string.call_record_folder_name); - } else { - folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); - } - if (!TextUtils.isEmpty(folderName)) { - ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); - } - String folderId = folderCursor.getString(NOTE_COLUMN_ID); - exportFolderToText(folderId, ps); - } while (folderCursor.moveToNext()); - } - folderCursor.close(); - } - - // Export notes in root's folder 导出根目录里的便签 - Cursor noteCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, - NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID - + "=0", null, null); - - if (noteCursor != null) { - if (noteCursor.moveToFirst()) { - do { - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note - String noteId = noteCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (noteCursor.moveToNext()); - } - noteCursor.close(); - } - ps.close(); - - return STATE_SUCCESS; - } - - /** - * Get a print stream pointed to the file {@generateExportedTextFile} - */ - private PrintStream getExportToTextPrintStream() { - File file = generateFileMountedOnSDcard(mContext, R.string.file_path, - R.string.file_name_txt_format); - if (file == null) { - Log.e(TAG, "create file to exported failed"); - return null; - } - mFileName = file.getName(); - mFileDirectory = mContext.getString(R.string.file_path); - PrintStream ps = null; - try { - FileOutputStream fos = new FileOutputStream(file); - ps = new PrintStream(fos);//将ps输出到特定的文件,目的就是导出到文件 - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } catch (NullPointerException e) { - e.printStackTrace(); - return null; - } - return ps; - } - } - - /** - * Generate the text file to store imported data - */ - private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { - StringBuilder sb = new StringBuilder(); - sb.append(Environment.getExternalStorageDirectory());//外部存储路径 - sb.append(context.getString(filePathResId));//文件存储路径 - File filedir = new File(sb.toString());//存储路径信息 - sb.append(context.getString( - fileNameFormatResId, - DateFormat.format(context.getString(R.string.format_date_ymd), - System.currentTimeMillis()))); - File file = new File(sb.toString()); - - try {//若文件不存在,就新建一个 - if (!filedir.exists()) { - filedir.mkdir(); - } - if (!file.exists()) { - file.createNewFile(); - } - return file; - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; - } -} - - diff --git a/two/tool/DataUtils.java b/two/tool/DataUtils.java deleted file mode 100644 index 80ef312..0000000 --- a/two/tool/DataUtils.java +++ /dev/null @@ -1,303 +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.tool; - -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.os.RemoteException; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; - -import java.util.ArrayList; -import java.util.HashSet; - - -public class DataUtils { - public static final String TAG = "DataUtils"; - public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) {//删除多个笔记 - if (ids == null) { - Log.d(TAG, "the ids is null"); - return true; - } - if (ids.size() == 0) { - Log.d(TAG, "no id is in the hashset"); - return true; - } - - ArrayList operationList = new ArrayList();//任务列表 - for (long id : ids) { - if(id == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "Don't delete system folder root"); - continue; - }//如果是根目录,则不删除 - ContentProviderOperation.Builder builder = ContentProviderOperation - .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));//用newDelete实现删除功能 - operationList.add(builder.build()); - } - /** - * 对于这种批量访问提供程序的操作 - * 例如插入大量行或者通过同一个方法调用在多个表中插入行,或者通常以事务(原子操作)的形式跨进程边界执行一组操作 - * 通常可以使用ContentProviderOperation来执行 - */ - try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - //数据库事务,数据库事务是由一组数据库操作序列组成,事务作为一个整体被执行 - if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } - return false; - } - - public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.PARENT_ID, desFolderId); - values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); - //更新需要移动的便签,然后用update实现 - } - - public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, - long folderId) { - if (ids == null) { - Log.d(TAG, "the ids is null"); - return true; - } - - ArrayList operationList = new ArrayList(); - for (long id : ids) { - ContentProviderOperation.Builder builder = ContentProviderOperation - .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));//使用withAppendedId为Uri加上ID - builder.withValue(NoteColumns.PARENT_ID, folderId); - builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); - operationList.add(builder.build()); - }//将IDS里包含的每一列的数据逐个加入到operationList里面,等待最后的批量处理 - - try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - //applyBatch一次性处理一个操作列表 - if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } - return false; - } - - /** - * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} - */ - public static int getUserFolderCount(ContentResolver resolver) { - Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { "COUNT(*)" }, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, - null);//筛选条件:源文件不为trash folder - - int count = 0; - if(cursor != null) { - if(cursor.moveToFirst()) { - try { - count = cursor.getInt(0); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "get folder count failed:" + e.toString()); - } finally { - cursor.close(); - } - } - } - return count; - } - - public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),//通过withAppendedId为Uri加上ID - null, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String [] {String.valueOf(type)}, - null);//查询条件:type适宜,且不属于无用文件夹 - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) {//用getCount判断cursor值是否为空 - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), - null, null, null, null); - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), - null, null, null, null); - - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, - NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", - new String[] { name }, null);//通过文件名字来查询文件是否存在 - boolean exist = false; - if(cursor != null) { - if(cursor.getCount() > 0) { - exist = true; - } - cursor.close(); - } - return exist; - } - - public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, - NoteColumns.PARENT_ID + "=?", - new String[] { String.valueOf(folderId) }, - null);//查询条件:父ID是传入的文件夹ID - - HashSet set = null; - if (c != null) { - if (c.moveToFirst()) { - set = new HashSet(); - do { - try { - AppWidgetAttribute widget = new AppWidgetAttribute(); - widget.widgetId = c.getInt(0);//0对应NoteColumns.WIDGET_ID - widget.widgetType = c.getInt(1);//1对应NoteColumns.WIDGET_TYPE - set.add(widget); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, e.toString()); - } - } while (c.moveToNext());//查询下一条项目 - } - c.close(); - } - return set; - } - - public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.PHONE_NUMBER }, - CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", - new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, - null);//通过数据库查询,查询条件是callDate和phoneNumber匹配传入参数的值 - - if (cursor != null && cursor.moveToFirst()) { - try { - return cursor.getString(0);//CallNote.NOTE_ID - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call number fails " + e.toString()); - } finally { - cursor.close(); - } - } - return ""; - } - - public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.NOTE_ID }, - CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", - new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, - null);//查询条件:便签ID - - if (cursor != null) { - if (cursor.moveToFirst()) { - try { - return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call note id fails " + e.toString()); - } - } - cursor.close(); - } - return 0; - } - - public static String getSnippetById(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, - new String [] { NoteColumns.SNIPPET }, - NoteColumns.ID + "=?", - new String [] { String.valueOf(noteId)}, - null); - - if (cursor != null) { - String snippet = ""; - if (cursor.moveToFirst()) { - snippet = cursor.getString(0); - } - cursor.close(); - return snippet; - } - throw new IllegalArgumentException("Note is not found with id: " + noteId); - } - - public static String getFormattedSnippet(String snippet) {//对字符串进行格式化处理,去掉字符串两头的空格和换行符 - if (snippet != null) { - snippet = snippet.trim(); - int index = snippet.indexOf('\n'); - if (index != -1) { - snippet = snippet.substring(0, index); - } - } - return snippet; - } -} diff --git a/two/tool/GTaskStringUtils.java b/two/tool/GTaskStringUtils.java deleted file mode 100644 index 344998c..0000000 --- a/two/tool/GTaskStringUtils.java +++ /dev/null @@ -1,113 +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.tool; - -public class GTaskStringUtils {//定义大量变量,为jsonObject提供Key,把这些定义全部写到一个类里,方便查看管理 - - public final static String GTASK_JSON_ACTION_ID = "action_id"; - - public final static String GTASK_JSON_ACTION_LIST = "action_list"; - - public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - - public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - - public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - - public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - - public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - - public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - - public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - - public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - - public final static String GTASK_JSON_COMPLETED = "completed"; - - public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - - public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - - public final static String GTASK_JSON_DELETED = "deleted"; - - public final static String GTASK_JSON_DEST_LIST = "dest_list"; - - public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - - public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - - public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - - public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - - public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - - public final static String GTASK_JSON_ID = "id"; - - public final static String GTASK_JSON_INDEX = "index"; - - public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - - public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - - public final static String GTASK_JSON_LIST_ID = "list_id"; - - public final static String GTASK_JSON_LISTS = "lists"; - - public final static String GTASK_JSON_NAME = "name"; - - public final static String GTASK_JSON_NEW_ID = "new_id"; - - public final static String GTASK_JSON_NOTES = "notes"; - - public final static String GTASK_JSON_PARENT_ID = "parent_id"; - - public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - - public final static String GTASK_JSON_RESULTS = "results"; - - public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - - public final static String GTASK_JSON_TASKS = "tasks"; - - public final static String GTASK_JSON_TYPE = "type"; - - public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - - public final static String GTASK_JSON_TYPE_TASK = "TASK"; - - public final static String GTASK_JSON_USER = "user"; - - public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - - public final static String FOLDER_DEFAULT = "Default"; - - public final static String FOLDER_CALL_NOTE = "Call_Note"; - - public final static String FOLDER_META = "METADATA"; - - public final static String META_HEAD_GTASK_ID = "meta_gid"; - - public final static String META_HEAD_NOTE = "meta_note"; - - public final static String META_HEAD_DATA = "meta_data"; - - public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; - -} diff --git a/two/tool/ResourceParser.java b/two/tool/ResourceParser.java deleted file mode 100644 index 57c5265..0000000 --- a/two/tool/ResourceParser.java +++ /dev/null @@ -1,183 +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.tool; - -import android.content.Context; -import android.preference.PreferenceManager; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesPreferenceActivity; - -public class ResourceParser { - - //背景颜色ID设置 - public static final int YELLOW = 0; - public static final int BLUE = 1; - public static final int WHITE = 2; - public static final int GREEN = 3; - public static final int RED = 4; - - public static final int BG_DEFAULT_COLOR = YELLOW;//默认背景颜色 - - //文本字体大小ID设置 - public static final int TEXT_SMALL = 0; - public static final int TEXT_MEDIUM = 1; - public static final int TEXT_LARGE = 2; - public static final int TEXT_SUPER = 3; - - public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;//默认文本字体大小 - - public static class NoteBgResources { - private final static int [] BG_EDIT_RESOURCES = new int [] { - R.drawable.edit_yellow, - R.drawable.edit_blue, - R.drawable.edit_white, - R.drawable.edit_green, - R.drawable.edit_red - }; - - private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {//标题颜色 - R.drawable.edit_title_yellow, - R.drawable.edit_title_blue, - R.drawable.edit_title_white, - R.drawable.edit_title_green, - R.drawable.edit_title_red - }; - - public static int getNoteBgResource(int id) { - return BG_EDIT_RESOURCES[id]; - }//获取并返回背景颜色ID - - public static int getNoteTitleBgResource(int id) { - return BG_EDIT_TITLE_RESOURCES[id]; - }//获取并返回标题颜色ID - } - - public static int getDefaultBgId(Context context) {//获取默认的背景颜色 - if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { - return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); - } else { - return BG_DEFAULT_COLOR; - } - } - - public static class NoteItemBgResources { - private final static int [] BG_FIRST_RESOURCES = new int [] { - R.drawable.list_yellow_up, - R.drawable.list_blue_up, - R.drawable.list_white_up, - R.drawable.list_green_up, - R.drawable.list_red_up - }; - - private final static int [] BG_NORMAL_RESOURCES = new int [] { - R.drawable.list_yellow_middle, - R.drawable.list_blue_middle, - R.drawable.list_white_middle, - R.drawable.list_green_middle, - R.drawable.list_red_middle - }; - - private final static int [] BG_LAST_RESOURCES = new int [] { - R.drawable.list_yellow_down, - R.drawable.list_blue_down, - R.drawable.list_white_down, - R.drawable.list_green_down, - R.drawable.list_red_down, - }; - - private final static int [] BG_SINGLE_RESOURCES = new int [] { - R.drawable.list_yellow_single, - R.drawable.list_blue_single, - R.drawable.list_white_single, - R.drawable.list_green_single, - R.drawable.list_red_single - }; - - public static int getNoteBgFirstRes(int id) { - return BG_FIRST_RESOURCES[id]; - } - - public static int getNoteBgLastRes(int id) { - return BG_LAST_RESOURCES[id]; - } - - public static int getNoteBgSingleRes(int id) { - return BG_SINGLE_RESOURCES[id]; - } - - public static int getNoteBgNormalRes(int id) { - return BG_NORMAL_RESOURCES[id]; - } - - public static int getFolderBgRes() { - return R.drawable.list_folder; - } - } - - public static class WidgetBgResources { - private final static int [] BG_2X_RESOURCES = new int [] { - R.drawable.widget_2x_yellow, - R.drawable.widget_2x_blue, - R.drawable.widget_2x_white, - R.drawable.widget_2x_green, - R.drawable.widget_2x_red, - }; - - public static int getWidget2xBgResource(int id) { - return BG_2X_RESOURCES[id]; - } - - private final static int [] BG_4X_RESOURCES = new int [] { - R.drawable.widget_4x_yellow, - R.drawable.widget_4x_blue, - R.drawable.widget_4x_white, - R.drawable.widget_4x_green, - R.drawable.widget_4x_red - }; - - public static int getWidget4xBgResource(int id) { - return BG_4X_RESOURCES[id]; - } - } - - public static class TextAppearanceResources { - private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper - }; - - public static int getTexAppearanceResource(int id) {//容错函数,防止输入的ID大于资源总量,若如此,则自动返回默认的设置结果 - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ - if (id >= TEXTAPPEARANCE_RESOURCES.length) { - return BG_DEFAULT_FONT_SIZE; - } - return TEXTAPPEARANCE_RESOURCES[id]; - } - - public static int getResourcesSize() { - return TEXTAPPEARANCE_RESOURCES.length; - } - } -} diff --git a/two/ui/AlarmAlertActivity.java b/two/ui/AlarmAlertActivity.java deleted file mode 100644 index 59cc2fa..0000000 --- a/two/ui/AlarmAlertActivity.java +++ /dev/null @@ -1,177 +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;//便签内容在数据库存储的ID - private String mSnippet;//闹钟出现的内容 - private static final int SNIPPET_PREW_MAX_LEN = 60; - MediaPlayer mPlayer; - - @Override - protected void onCreate(Bundle savedInstanceState) { - /** - * Bundle在Android开发中非常常见,它的作用主要时用于传递数据。 - * Bundle传递的数据包括:string、int、boolean、byte、float、long、double等基本类型或它们对应的数组,也可以是对象或对象数组。 - * 当Bundle传递的是对象或对象数组时,必须实现Serialiable或Parcelable接口。 - */ - - 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);//根据ID从数据库中获取标签的内容 - 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() {//判断屏幕是否锁屏,调用系统函数判断,最后返回值是boolean值(Ture或False) - 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();//开始播放 - /** - * MediaPlayer 类是媒体框架最重要的组成部分之一。 - * 此类的对象能够获取、解码以及播放音频和视频,而且只需极少量设置。它支持多种不同的媒体源 - * 比如本地资源、内部 URI,例如您可能从内容解析器那获取的 URI、外部网址(流式传输) - */ - } 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); - } - /** - * AlertDialog可以在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertDialog一般是用于提示一些非常重要的内容或者警告信息。 - * 1.创建构造器AlertDialog.Builder的对象; - * 2.通过构造器对象调用setTitle、setMessage、setIcon等方法构造对话框的标题、信息和图标等内容; - * 3.根据需要调用setPositive/Negative/NeutralButton()方法设置正面按钮、负面按钮和中立按钮; - * 4.调用构造器对象的create方法创建AlertDialog对象; - * 5.AlertDialog对象调用show方法,让对话框在界面上显示。 - */ - - public void onClick(DialogInterface dialog, int which) { - switch (which) {//选择click后下一步的操作 - case DialogInterface.BUTTON_NEGATIVE://取消 - Intent intent = new Intent(this, NoteEditActivity.class);//两个类间的数据传输 - intent.setAction(Intent.ACTION_VIEW);//动作属性 - intent.putExtra(Intent.EXTRA_UID, mNoteId);//实现key-value对,EXTRA_UID为key,mNoteId为键 - startActivity(intent);//开始 - break; - default: - break; - } - } - - public void onDismiss(DialogInterface dialog) { - stopAlarmSound();//暂停闹钟声音 - finish(); - } - - private void stopAlarmSound() { - if (mPlayer != null) { - mPlayer.stop();//停止播放 - mPlayer.release();//释放MediaPlayer对象相关的资源 - mPlayer = null; - } - } -} diff --git a/two/ui/AlarmInitReceiver.java b/two/ui/AlarmInitReceiver.java deleted file mode 100644 index 7ba989c..0000000 --- a/two/ui/AlarmInitReceiver.java +++ /dev/null @@ -1,66 +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.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; - - -public class AlarmInitReceiver extends BroadcastReceiver { - - private static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE - }; - //调用便签ID和闹钟时间 - private static final int COLUMN_ID = 0; - private static final int COLUMN_ALERTED_DATE = 1; - - @Override - public void onReceive(Context context, Intent intent) { - long currentDate = System.currentTimeMillis(); - Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, - PROJECTION, - NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, - new String[] { String.valueOf(currentDate) },//将currentDate转化为字符串变量 - null); - //通过查找数据库中的标签内容,找到和当前系统时间相等的标签 - - if (c != null) { - if (c.moveToFirst()) { - do { - long alertDate = c.getLong(COLUMN_ALERTED_DATE); - Intent sender = new Intent(context, AlarmReceiver.class); - sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); - PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); - AlarmManager alermManager = (AlarmManager) context - .getSystemService(Context.ALARM_SERVICE); - alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); - } while (c.moveToNext()); - } - c.close(); - }//根据数据库里的闹钟时间创建一个闹钟 - } -} diff --git a/two/ui/AlarmReceiver.java b/two/ui/AlarmReceiver.java deleted file mode 100644 index 67a16d5..0000000 --- a/two/ui/AlarmReceiver.java +++ /dev/null @@ -1,30 +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.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class AlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - intent.setClass(context, AlarmAlertActivity.class);//启动AlarmAlertActivity - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//activity要存在于activity的栈中,而非activity的途径启动activity时必然不存在一个activity的栈,所以要新建一个栈装入启动的activity - context.startActivity(intent); - } -}//这是实现alarm这个功能最接近用户层的包,基于AlarmAlertActivity和AlarmInitReceiver \ No newline at end of file diff --git a/two/ui/DateTimePicker.java b/two/ui/DateTimePicker.java deleted file mode 100644 index 813e324..0000000 --- a/two/ui/DateTimePicker.java +++ /dev/null @@ -1,490 +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 java.text.DateFormatSymbols; -import java.util.Calendar; - -import net.micode.notes.R; - - -import android.content.Context; -import android.text.format.DateFormat; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.NumberPicker; - -public class DateTimePicker extends FrameLayout { - //初始化控件 - private static final boolean DEFAULT_ENABLE_STATE = true; - - private static final int HOURS_IN_HALF_DAY = 12; - private static final int HOURS_IN_ALL_DAY = 24; - private static final int DAYS_IN_ALL_WEEK = 7; - private static final int DATE_SPINNER_MIN_VAL = 0; - private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; - private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; - private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; - private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; - private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; - private static final int MINUT_SPINNER_MIN_VAL = 0; - private static final int MINUT_SPINNER_MAX_VAL = 59; - private static final int AMPM_SPINNER_MIN_VAL = 0; - private static final int AMPM_SPINNER_MAX_VAL = 1; - - //设置闹钟时需要选择的变量 - private final NumberPicker mDateSpinner; - private final NumberPicker mHourSpinner; - private final NumberPicker mMinuteSpinner; - private final NumberPicker mAmPmSpinner; - private Calendar mDate;//定义了Calendar类型的变量mDate,用于操作时间 - //Calendar 类是一个抽象类,它为特定瞬间与 YEAR、MONTH、DAY_OF—MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法。 - - private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; - - private boolean mIsAm; - - private boolean mIs24HourView; - - private boolean mIsEnabled = DEFAULT_ENABLE_STATE; - - private boolean mInitialising; - - private OnDateTimeChangedListener mOnDateTimeChangedListener; - - private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); - updateDateControl(); - onDateTimeChanged(); - } - };//OnValueChangeListener,这是时间改变监视器,这里主要是对日期的监视 - //将现在日期的值传递给mDate;updateDateControl是同步操作 - - private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {//监视Hour - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - boolean isDateChanged = false; - Calendar cal = Calendar.getInstance(); - if (!mIs24HourView) { - if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true;//选择12小时制时,晚上12点之后日期的更改 - } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - }//选择12小时制时,凌晨12点之后对日期的更改 - if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || - oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - mIsAm = !mIsAm; - updateAmPmControl(); - }//选择12小时制时,中午12点之后对AM和PM的更改 - } else { - if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true;//选择24小时制时,晚上12点之后日期的更改 - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true;//选择24小时制时,凌晨12点之后对日期的更改 - } - } - int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);//通过数字选择器给newHour赋值 - mDate.set(Calendar.HOUR_OF_DAY, newHour);//通过set将Hour值传给mDate - onDateTimeChanged(); - if (isDateChanged) { - setCurrentYear(cal.get(Calendar.YEAR)); - setCurrentMonth(cal.get(Calendar.MONTH)); - setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); - } - } - }; - - private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {//这里是对分钟改变的监视 - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int minValue = mMinuteSpinner.getMinValue(); - int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0;//设置offset,作为小时改变的一个记录数据 - if (oldVal == maxValue && newVal == minValue) { - offset += 1; - } else if (oldVal == minValue && newVal == maxValue) { - offset -= 1; - }//如果原来值为59,新值为0,则offset加1 - //如果原来值为0,新值为59,则offset减1 - - if (offset != 0) { - mDate.add(Calendar.HOUR_OF_DAY, offset); - mHourSpinner.setValue(getCurrentHour()); - updateDateControl(); - int newHour = getCurrentHourOfDay(); - if (newHour >= HOURS_IN_HALF_DAY) { - mIsAm = false; - updateAmPmControl(); - } else { - mIsAm = true; - updateAmPmControl(); - } - } - mDate.set(Calendar.MINUTE, newVal); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {//对AM和PM的监视 - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mIsAm = !mIsAm; - if (mIsAm) { - mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); - } else { - mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); - } - updateAmPmControl(); - onDateTimeChanged(); - } - }; - - public interface OnDateTimeChangedListener { - void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute); - } - - public DateTimePicker(Context context) { - this(context, System.currentTimeMillis()); - }//获取当前的系统时间 - - public DateTimePicker(Context context, long date) { - this(context, date, DateFormat.is24HourFormat(context)); - }//DateFormat上面函数的得到的数据 - - public DateTimePicker(Context context, long date, boolean is24HourView) { - super(context);//获取系统时间 - mDate = Calendar.getInstance(); - mInitialising = true; - mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; - inflate(context, R.layout.datetime_picker, this); - - mDateSpinner = (NumberPicker) findViewById(R.id.date); - mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); - mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); - mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); - - mHourSpinner = (NumberPicker) findViewById(R.id.hour); - mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); - mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); - mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); - mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); - mMinuteSpinner.setOnLongPressUpdateInterval(100); - mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); - - String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); - mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); - mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); - mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); - mAmPmSpinner.setDisplayedValues(stringsForAmPm); - mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); - - // update controls to initial state - updateDateControl(); - updateHourControl(); - updateAmPmControl(); - - set24HourView(is24HourView); - - // set to current time - setCurrentDate(date); - - setEnabled(isEnabled()); - - // set the content descriptions - mInitialising = false; - } - - @Override - public void setEnabled(boolean enabled) { - if (mIsEnabled == enabled) { - return; - } - super.setEnabled(enabled); - mDateSpinner.setEnabled(enabled); - mMinuteSpinner.setEnabled(enabled); - mHourSpinner.setEnabled(enabled); - mAmPmSpinner.setEnabled(enabled); - mIsEnabled = enabled; - } - - @Override - public boolean isEnabled() { - return mIsEnabled; - } - - /** - * Get the current date in millis - * - * @return the current date in millis - */ - public long getCurrentDateInTimeMillis() { - return mDate.getTimeInMillis(); - }//获取当前的秒数 - - /** - * Set the current date - * - * @param date The current date in millis - */ - public void setCurrentDate(long date) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(date); - setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); - }//设置当前的日期 - - /** - * Set the current date - * - * @param year The current year - * @param month The current month - * @param dayOfMonth The current dayOfMonth - * @param hourOfDay The current hourOfDay - * @param minute The current minute - */ - public void setCurrentDate(int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - setCurrentYear(year); - setCurrentMonth(month); - setCurrentDay(dayOfMonth); - setCurrentHour(hourOfDay); - setCurrentMinute(minute); - }//设置当前的时间:年月日时分 - - /** - * Get current year - * - * @return The current year - */ - public int getCurrentYear() { - return mDate.get(Calendar.YEAR); - } - - /** - * Set current year - * - * @param year The current year - */ - public void setCurrentYear(int year) { - if (!mInitialising && year == getCurrentYear()) { - return; - } - mDate.set(Calendar.YEAR, year); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current month in the year - * - * @return The current month in the year - */ - public int getCurrentMonth() { - return mDate.get(Calendar.MONTH); - } - - /** - * Set current month in the year - * - * @param month The month in the year - */ - public void setCurrentMonth(int month) { - if (!mInitialising && month == getCurrentMonth()) { - return; - } - mDate.set(Calendar.MONTH, month); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current day of the month - * - * @return The day of the month - */ - public int getCurrentDay() { - return mDate.get(Calendar.DAY_OF_MONTH); - } - - /** - * Set current day of the month - * - * @param dayOfMonth The day of the month - */ - public void setCurrentDay(int dayOfMonth) { - if (!mInitialising && dayOfMonth == getCurrentDay()) { - return; - } - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current hour in 24 hour mode, in the range (0~23) - * @return The current hour in 24 hour mode - */ - public int getCurrentHourOfDay() { - return mDate.get(Calendar.HOUR_OF_DAY); - } - - private int getCurrentHour() { - if (mIs24HourView){ - return getCurrentHourOfDay(); - } else { - int hour = getCurrentHourOfDay(); - if (hour > HOURS_IN_HALF_DAY) { - return hour - HOURS_IN_HALF_DAY; - } else { - return hour == 0 ? HOURS_IN_HALF_DAY : hour; - } - } - } - - /** - * Set current hour in 24 hour mode, in the range (0~23) - * - * @param hourOfDay - */ - public void setCurrentHour(int hourOfDay) { - if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { - return; - } - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - if (!mIs24HourView) { - if (hourOfDay >= HOURS_IN_HALF_DAY) { - mIsAm = false; - if (hourOfDay > HOURS_IN_HALF_DAY) { - hourOfDay -= HOURS_IN_HALF_DAY; - } - } else { - mIsAm = true; - if (hourOfDay == 0) { - hourOfDay = HOURS_IN_HALF_DAY; - } - } - updateAmPmControl(); - } - mHourSpinner.setValue(hourOfDay); - onDateTimeChanged(); - } - - /** - * Get currentMinute - * - * @return The Current Minute - */ - public int getCurrentMinute() { - return mDate.get(Calendar.MINUTE); - } - - /** - * Set current minute - */ - public void setCurrentMinute(int minute) { - if (!mInitialising && minute == getCurrentMinute()) { - return; - } - mMinuteSpinner.setValue(minute); - mDate.set(Calendar.MINUTE, minute); - onDateTimeChanged(); - } - - /** - * @return true if this is in 24 hour view else false. - */ - public boolean is24HourView () { - return mIs24HourView; - } - - /** - * Set whether in 24 hour or AM/PM mode. - * - * @param is24HourView True for 24 hour mode. False for AM/PM mode. - */ - public void set24HourView(boolean is24HourView) { - if (mIs24HourView == is24HourView) { - return; - } - mIs24HourView = is24HourView; - mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); - int hour = getCurrentHourOfDay(); - updateHourControl(); - setCurrentHour(hour); - updateAmPmControl(); - } - - private void updateDateControl() { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); - mDateSpinner.setDisplayedValues(null); - for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { - cal.add(Calendar.DAY_OF_YEAR, 1); - mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); - } - mDateSpinner.setDisplayedValues(mDateDisplayValues); - mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); - mDateSpinner.invalidate(); - }//确定周几 - - private void updateAmPmControl() { - if (mIs24HourView) { - mAmPmSpinner.setVisibility(View.GONE); - } else { - int index = mIsAm ? Calendar.AM : Calendar.PM; - mAmPmSpinner.setValue(index); - mAmPmSpinner.setVisibility(View.VISIBLE); - } - }//确定AM和PM - - private void updateHourControl() { - if (mIs24HourView) { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); - } else { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); - } - }//计算小时 - - /** - * Set the callback that indicates the 'Set' button has been pressed. - * @param callback the callback, if null will do nothing - */ - public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { - mOnDateTimeChangedListener = callback; - } - - private void onDateTimeChanged() { - if (mOnDateTimeChangedListener != null) { - mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), - getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); - } - } -} From 25d62e9ec2485b2c83259d484d36f099f2ec0500 Mon Sep 17 00:00:00 2001 From: sxg <2363882464@qq.com> Date: Thu, 27 Apr 2023 23:50:16 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E5=88=86=E6=9E=90=E7=B1=BB=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/首页背景分析类图.png | Bin 0 -> 27430 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 model/首页背景分析类图.png diff --git a/model/首页背景分析类图.png b/model/首页背景分析类图.png new file mode 100644 index 0000000000000000000000000000000000000000..24feaa200bc044add6d681758adac394baf13daf GIT binary patch literal 27430 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%0DD_ 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 Date: Fri, 28 Apr 2023 11:13:25 +0800 Subject: [PATCH 13/16] 1 --- .../{ => 分析类图}/首页背景分析类图.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename model/{ => 分析类图}/首页背景分析类图.png (100%) diff --git a/model/首页背景分析类图.png b/model/分析类图/首页背景分析类图.png similarity index 100% rename from model/首页背景分析类图.png rename to model/分析类图/首页背景分析类图.png From 5c054d69185d3191f1a15610acaa6fe4863c67d5 Mon Sep 17 00:00:00 2001 From: sxg <2363882464@qq.com> Date: Fri, 28 Apr 2023 14:16:41 +0800 Subject: [PATCH 14/16] 1 --- src/model/Note.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/model/Note.java b/src/model/Note.java index f291329..6706cf6 100644 --- a/src/model/Note.java +++ b/src/model/Note.java @@ -251,4 +251,3 @@ public class Note { } } } - \ No newline at end of file From fb47e16e930199bb71d3e4387ceac6b053a65624 Mon Sep 17 00:00:00 2001 From: sxg <2363882464@qq.com> Date: Fri, 28 Apr 2023 14:20:58 +0800 Subject: [PATCH 15/16] 1 --- src/gtask/remote/GTaskASyncTask.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/gtask/remote/GTaskASyncTask.java b/src/gtask/remote/GTaskASyncTask.java index 0e332f3..b3b61e7 100644 --- a/src/gtask/remote/GTaskASyncTask.java +++ b/src/gtask/remote/GTaskASyncTask.java @@ -64,6 +64,10 @@ public class GTaskASyncTask extends AsyncTask { } 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, @@ -73,21 +77,11 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } - - - 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(); + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, + pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } - - @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity From c82f21ffb1a8e9ebc5166bfef0c57af32aa6a384 Mon Sep 17 00:00:00 2001 From: sxg <2363882464@qq.com> Date: Fri, 28 Apr 2023 14:27:15 +0800 Subject: [PATCH 16/16] 1 --- ...页背景分析类图.png => 首页背景.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename model/分析类图/{首页背景分析类图.png => 首页背景.png} (100%) diff --git a/model/分析类图/首页背景分析类图.png b/model/分析类图/首页背景.png similarity index 100% rename from model/分析类图/首页背景分析类图.png rename to model/分析类图/首页背景.png