Compare commits
	
		
			1 Commits 
		
	
	
		
			main
			...
			Wangce_bra
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 65ead0bc45 | 7 months ago | 
| @ -0,0 +1,287 @@ | |||||||
|  | /* | ||||||
|  |  * 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 { | ||||||
|  |     // 定义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; | ||||||
|  | 
 | ||||||
|  |     // 定义Intent的额外参数
 | ||||||
|  |     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"; | ||||||
|  | 
 | ||||||
|  |     // 定义Widget的类型
 | ||||||
|  |     public static final int TYPE_WIDGET_INVALIDE      = -1; | ||||||
|  |     public static final int TYPE_WIDGET_2X            = 0; | ||||||
|  |     public static final int TYPE_WIDGET_4X            = 1; | ||||||
|  | 
 | ||||||
|  |     // 定义DataConstants类
 | ||||||
|  |     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"); | ||||||
|  | 
 | ||||||
|  |     // 定义NoteColumns接口
 | ||||||
|  |     public interface NoteColumns { | ||||||
|  |         /** | ||||||
|  |          * The unique ID for a row | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String ID = "_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The parent's id for note or folder | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String PARENT_ID = "parent_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Created data for note or folder | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String CREATED_DATE = "created_date"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Latest modified date | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String MODIFIED_DATE = "modified_date"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Alert date | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String ALERTED_DATE = "alert_date"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Folder's name or text content of note | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String SNIPPET = "snippet"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Note's widget id | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String WIDGET_ID = "widget_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Note's widget type | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String WIDGET_TYPE = "widget_type"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Note's background color's id | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         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 | ||||||
|  |          * <P> Type: INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String HAS_ATTACHMENT = "has_attachment"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Folder's count of notes | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String NOTES_COUNT = "notes_count"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The file type: folder or note | ||||||
|  |          * <P> Type: INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String TYPE = "type"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The last sync id | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String SYNC_ID = "sync_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Sign to indicate local modified or not | ||||||
|  |          * <P> Type: INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String LOCAL_MODIFIED = "local_modified"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Original parent id before moving into temporary folder | ||||||
|  |          * <P> Type : INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String ORIGIN_PARENT_ID = "origin_parent_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The gtask id | ||||||
|  |          * <P> Type : TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String GTASK_ID = "gtask_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The version code | ||||||
|  |          * <P> Type : INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String VERSION = "version"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 定义DataColumns接口
 | ||||||
|  |     public interface DataColumns { | ||||||
|  |         /** | ||||||
|  |          * The unique ID for a row | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String ID = "_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The MIME type of the item represented by this row. | ||||||
|  |          * <P> Type: Text </P> | ||||||
|  |          */ | ||||||
|  |         public static final String MIME_TYPE = "mime_type"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * The reference id to note that this data belongs to | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String NOTE_ID = "note_id"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Created data for note or folder | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String CREATED_DATE = "created_date"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Latest modified date | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String MODIFIED_DATE = "modified_date"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Data's content | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String CONTENT = "content"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Generic data column, the meaning is {@link #MIMETYPE} specific, used for | ||||||
|  |          * integer data type | ||||||
|  |          * <P> Type: INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String DATA1 = "data1"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Generic data column, the meaning is {@link #MIMETYPE} specific, used for | ||||||
|  |          * integer data type | ||||||
|  |          * <P> Type: INTEGER </P> | ||||||
|  |          */ | ||||||
|  |         public static final String DATA2 = "data2"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Generic data column, the meaning is {@link #MIMETYPE} specific, used for | ||||||
|  |          * TEXT data type | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String DATA3 = "data3"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Generic data column, the meaning is {@link #MIMETYPE} specific, used for | ||||||
|  |          * TEXT data type | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String DATA4 = "data4"; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Generic data column, the meaning is {@link #MIMETYPE} specific, used for | ||||||
|  |          * TEXT data type | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String DATA5 = "data5"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 定义TextNote类
 | ||||||
|  |     public static final class TextNote implements DataColumns { | ||||||
|  |         /** | ||||||
|  |          * Mode to indicate the text in check list mode or not | ||||||
|  |          * <P> Type: Integer 1:check list mode 0: normal mode </P> | ||||||
|  |          */ | ||||||
|  |         public static final String 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"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 定义CallNote类
 | ||||||
|  |     public static final class CallNote implements DataColumns { | ||||||
|  |         /** | ||||||
|  |          * Call date for this record | ||||||
|  |          * <P> Type: INTEGER (long) </P> | ||||||
|  |          */ | ||||||
|  |         public static final String CALL_DATE = DATA1; | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Phone number for this record | ||||||
|  |          * <P> Type: TEXT </P> | ||||||
|  |          */ | ||||||
|  |         public static final String PHONE_NUMBER = DATA3; | ||||||
|  | 
 | ||||||
|  |         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; | ||||||
|  | 
 | ||||||
|  |         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; | ||||||
|  | 
 | ||||||
|  |         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<Task> mChildren; | ||||||
|  | 
 | ||||||
|  |     public TaskList() { | ||||||
|  |         super(); | ||||||
|  |         mChildren = new ArrayList<Task>(); | ||||||
|  |         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<Task> getChildTaskList() { | ||||||
|  |         return this.mChildren; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setIndex(int index) { | ||||||
|  |         this.mIndex = index; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getIndex() { | ||||||
|  |         return this.mIndex; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,123 @@ | |||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * 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<Void, String, Integer> { | ||||||
|  | 
 | ||||||
|  |     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) { | ||||||
|  |         Notification notification = new Notification(R.drawable.notification, mContext | ||||||
|  |                 .getString(tickerId), System.currentTimeMillis()); | ||||||
|  |         notification.defaults = Notification.DEFAULT_LIGHTS; | ||||||
|  |         notification.flags = Notification.FLAG_AUTO_CANCEL; | ||||||
|  |         PendingIntent pendingIntent; | ||||||
|  |         if (tickerId != R.string.ticker_success) { | ||||||
|  |             pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, | ||||||
|  |                     NotesPreferenceActivity.class), 0); | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, | ||||||
|  |                     NotesListActivity.class), 0); | ||||||
|  |         } | ||||||
|  |         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 | ||||||
|  |                 .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(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<Bundle> 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<Cookie> 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 = ")}</script>"; | ||||||
|  |             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<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>(); | ||||||
|  |             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 = ")}</script>"; | ||||||
|  |             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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<String, TaskList> mGTaskListHashMap; | ||||||
|  | 
 | ||||||
|  |     private HashMap<String, Node> mGTaskHashMap; | ||||||
|  | 
 | ||||||
|  |     private HashMap<String, MetaData> mMetaHashMap; | ||||||
|  | 
 | ||||||
|  |     private TaskList mMetaList; | ||||||
|  | 
 | ||||||
|  |     private HashSet<Long> mLocalDeleteIdMap; | ||||||
|  | 
 | ||||||
|  |     private HashMap<String, Long> mGidToNid; | ||||||
|  | 
 | ||||||
|  |     private HashMap<Long, String> mNidToGid; | ||||||
|  | 
 | ||||||
|  |     private GTaskManager() { | ||||||
|  |         mSyncing = false; | ||||||
|  |         mCancelled = false; | ||||||
|  |         mGTaskListHashMap = new HashMap<String, TaskList>(); | ||||||
|  |         mGTaskHashMap = new HashMap<String, Node>(); | ||||||
|  |         mMetaHashMap = new HashMap<String, MetaData>(); | ||||||
|  |         mMetaList = null; | ||||||
|  |         mLocalDeleteIdMap = new HashSet<Long>(); | ||||||
|  |         mGidToNid = new HashMap<String, Long>(); | ||||||
|  |         mNidToGid = new HashMap<Long, String>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator(); | ||||||
|  |         while (iter.hasNext()) { | ||||||
|  |             Map.Entry<String, Node> 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<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator(); | ||||||
|  |         while (iter.hasNext()) { | ||||||
|  |             Map.Entry<String, TaskList> 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<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator(); | ||||||
|  |             while (iter.hasNext()) { | ||||||
|  |                 Map.Entry<String, TaskList> 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); | ||||||
|  |             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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -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<Long> 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<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); | ||||||
|  |         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<Long> ids, | ||||||
|  |             long folderId) { | ||||||
|  |         if (ids == null) { | ||||||
|  |             Log.d(TAG, "the ids is null"); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>(); | ||||||
|  |         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<AppWidgetAttribute> 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<AppWidgetAttribute> set = null; | ||||||
|  |         if (c != null) { | ||||||
|  |             if (c.moveToFirst()) { | ||||||
|  |                 set = new HashSet<AppWidgetAttribute>(); | ||||||
|  |                 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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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"; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue