Merge pull request '请求' (#2) from ganshihao_branch into main
commit
47bb83488d
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "net.micode.notes.test",
|
||||
"variantName": "debugAndroidTest",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 0,
|
||||
"versionName": "",
|
||||
"outputFile": "app-debug-androidTest.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File"
|
||||
}
|
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "net.micode.notes",
|
||||
"variantName": "debug",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "0.1",
|
||||
"outputFile": "app-debug.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File"
|
||||
}
|
@ -0,0 +1,242 @@
|
||||
-- Merging decision tree log ---
|
||||
manifest
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:18:1-152:12
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:18:1-152:12
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:18:1-152:12
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:18:1-152:12
|
||||
package
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
android:versionName
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:20:5-30
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
xmlns:android
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:18:11-69
|
||||
android:versionCode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:19:5-28
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
uses-sdk
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:22:5-44
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:22:5-44
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:22:5-44
|
||||
android:targetSdkVersion
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
android:minSdkVersion
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:22:15-41
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
uses-permission#android.permission.WRITE_EXTERNAL_STORAGE
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:24:5-81
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:24:22-78
|
||||
uses-permission#com.android.launcher.permission.INSTALL_SHORTCUT
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:25:5-88
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:25:22-85
|
||||
uses-permission#android.permission.INTERNET
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:26:5-67
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:26:22-64
|
||||
uses-permission#android.permission.READ_CONTACTS
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:27:5-72
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:27:22-69
|
||||
uses-permission#android.permission.MANAGE_ACCOUNTS
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:28:5-74
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:28:22-71
|
||||
uses-permission#android.permission.AUTHENTICATE_ACCOUNTS
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:29:5-80
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:29:22-77
|
||||
uses-permission#android.permission.GET_ACCOUNTS
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:30:5-71
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:30:22-68
|
||||
uses-permission#android.permission.USE_CREDENTIALS
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:31:5-74
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:31:22-71
|
||||
uses-permission#android.permission.RECEIVE_BOOT_COMPLETED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:32:5-81
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:32:22-78
|
||||
application
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:34:5-151:19
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml:34:5-151:19
|
||||
android:extractNativeLibs
|
||||
INJECTED from D:\Notes-master3\app\src\main\AndroidManifest.xml
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:36:9-41
|
||||
android:icon
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:35:9-42
|
||||
activity#net.micode.notes.ui.NotesListActivity
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:37:9-50:20
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:40:13-45
|
||||
android:launchMode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:41:13-43
|
||||
android:windowSoftInputMode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:44:13-52
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:45:13-36
|
||||
android:uiOptions
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:43:13-57
|
||||
android:configChanges
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:39:13-74
|
||||
android:theme
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:42:13-45
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:38:13-49
|
||||
intent-filter#action:name:android.intent.action.MAIN+category:name:android.intent.category.LAUNCHER
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:46:13-49:29
|
||||
action#android.intent.action.MAIN
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:47:17-69
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:47:25-66
|
||||
category#android.intent.category.LAUNCHER
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:48:17-77
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:48:27-74
|
||||
activity#net.micode.notes.ui.NoteEditActivity
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:52:9-81:20
|
||||
android:launchMode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:55:13-43
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:57:13-36
|
||||
android:configChanges
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:54:13-74
|
||||
android:theme
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:56:13-45
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:53:13-48
|
||||
intent-filter#action:name:android.intent.action.VIEW+category:name:android.intent.category.DEFAULT+data:host:com.example.notes.provider+data:host:com.example.notes.provider+data:mimeType:vnd.android.cursor.item/call_note+data:mimeType:vnd.android.cursor.item/text_note+data:path:/notes+data:path:/notes+data:scheme:content+data:scheme:content
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:58:13-63:29
|
||||
action#android.intent.action.VIEW
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:59:17-69
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:59:25-66
|
||||
category#android.intent.category.DEFAULT
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:60:17-76
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:60:27-73
|
||||
data
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:61:17-167
|
||||
android:path
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:61:143-164
|
||||
android:host
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:61:101-142
|
||||
android:scheme
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:61:76-100
|
||||
android:mimeType
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:61:23-75
|
||||
intent-filter#action:name:android.intent.action.INSERT_OR_EDIT+category:name:android.intent.category.DEFAULT+data:mimeType:vnd.android.cursor.item/call_note+data:mimeType:vnd.android.cursor.item/text_note
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:66:13-71:29
|
||||
action#android.intent.action.INSERT_OR_EDIT
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:67:17-79
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:67:25-76
|
||||
intent-filter#action:name:android.intent.action.SEARCH+category:name:android.intent.category.DEFAULT
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:73:13-76:29
|
||||
action#android.intent.action.SEARCH
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:74:17-71
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:74:25-68
|
||||
meta-data#android.app.searchable
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:78:13-80:54
|
||||
android:resource
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:80:17-51
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:79:17-54
|
||||
provider#net.micode.notes.data.NotesProvider
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:83:9-86:43
|
||||
android:authorities
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:85:13-47
|
||||
android:multiprocess
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:86:13-40
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:84:13-63
|
||||
receiver#net.micode.notes.widget.NoteWidgetProvider_2x
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:88:9-101:20
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:90:13-50
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:91:13-37
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:89:13-57
|
||||
intent-filter#action:name:android.appwidget.action.APPWIDGET_DELETED+action:name:android.appwidget.action.APPWIDGET_UPDATE+action:name:android.intent.action.PRIVACY_MODE_CHANGED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:92:13-96:29
|
||||
action#android.appwidget.action.APPWIDGET_UPDATE
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:93:17-84
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:93:25-81
|
||||
action#android.appwidget.action.APPWIDGET_DELETED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:94:17-85
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:94:25-82
|
||||
action#android.intent.action.PRIVACY_MODE_CHANGED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:95:17-85
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:95:25-82
|
||||
meta-data#android.appwidget.provider
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:98:13-100:58
|
||||
android:resource
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:100:17-55
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:99:17-58
|
||||
receiver#net.micode.notes.widget.NoteWidgetProvider_4x
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:102:9-116:20
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:104:13-50
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:105:13-37
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:103:13-57
|
||||
receiver#net.micode.notes.ui.AlarmInitReceiver
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:118:9-122:20
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:118:56-80
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:118:19-55
|
||||
intent-filter#action:name:android.intent.action.BOOT_COMPLETED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:119:13-121:29
|
||||
action#android.intent.action.BOOT_COMPLETED
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:120:17-79
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:120:25-76
|
||||
receiver#net.micode.notes.ui.AlarmReceiver
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:124:9-127:20
|
||||
android:process
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:126:13-38
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:125:13-61
|
||||
activity#net.micode.notes.ui.AlarmAlertActivity
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:129:9-134:20
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:131:13-45
|
||||
android:launchMode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:132:13-48
|
||||
android:theme
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:133:13-75
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:130:13-50
|
||||
activity#net.micode.notes.ui.NotesPreferenceActivity
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:136:9-141:20
|
||||
android:label
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:138:13-54
|
||||
android:launchMode
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:139:13-43
|
||||
android:theme
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:140:13-60
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:137:13-71
|
||||
service#net.micode.notes.gtask.remote.GTaskSyncService
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:143:9-146:19
|
||||
android:exported
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:145:13-37
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:144:13-74
|
||||
meta-data#android.app.default_searchable
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:148:9-150:52
|
||||
android:value
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:150:13-49
|
||||
android:name
|
||||
ADDED from D:\Notes-master3\app\src\main\AndroidManifest.xml:149:13-58
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="1"
|
||||
android:versionName="0.1" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/icon_app"
|
||||
android:label="@string/app_name" >
|
||||
<activity
|
||||
android:name=".ui.NotesListActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoteTheme"
|
||||
android:uiOptions="splitActionBarWhenNarrow"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.NoteEditActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoteTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="vnd.android.cursor.item/text_note" android:scheme="content" android:host="com.example.notes.provider" android:path="/notes" />
|
||||
<data android:mimeType="vnd.android.cursor.item/call_note" android:scheme="content" android:host="com.example.notes.provider" android:path="/notes" />
|
||||
</intent-filter>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.INSERT_OR_EDIT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="vnd.android.cursor.item/text_note" />
|
||||
<data android:mimeType="vnd.android.cursor.item/call_note" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="net.micode.notes.data.NotesProvider"
|
||||
android:authorities="micode_notes"
|
||||
android:multiprocess="true" />
|
||||
|
||||
<receiver
|
||||
android:name=".widget.NoteWidgetProvider_2x"
|
||||
android:label="@string/app_widget2x2"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
|
||||
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_2x_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".widget.NoteWidgetProvider_4x"
|
||||
android:label="@string/app_widget4x4"
|
||||
android:exported="false">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
|
||||
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_4x_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".ui.AlarmInitReceiver" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="net.micode.notes.ui.AlarmReceiver"
|
||||
android:process=":remote" >
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".ui.AlarmAlertActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="net.micode.notes.ui.NotesPreferenceActivity"
|
||||
android:label="@string/preferences_title"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.Holo.Light" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
|
||||
android:exported="false" >
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".ui.NoteEditActivity" />
|
||||
</application>
|
||||
</manifest>
|
@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.data;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.DataConstants;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
|
||||
|
||||
public class NotesDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String DB_NAME = "note.db";
|
||||
|
||||
private static final int DB_VERSION = 4;
|
||||
|
||||
public interface TABLE {
|
||||
public static final String NOTE = "note";
|
||||
|
||||
public static final String DATA = "data";
|
||||
}
|
||||
|
||||
private static final String TAG = "NotesDatabaseHelper";
|
||||
|
||||
private static NotesDatabaseHelper mInstance;
|
||||
|
||||
private static final String CREATE_NOTE_TABLE_SQL =
|
||||
"CREATE TABLE " + TABLE.NOTE + "(" +
|
||||
NoteColumns.ID + " INTEGER PRIMARY KEY," +
|
||||
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
|
||||
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
|
||||
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
|
||||
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
|
||||
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
|
||||
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
|
||||
")";
|
||||
|
||||
private static final String CREATE_DATA_TABLE_SQL =
|
||||
"CREATE TABLE " + TABLE.DATA + "(" +
|
||||
DataColumns.ID + " INTEGER PRIMARY KEY," +
|
||||
DataColumns.MIME_TYPE + " TEXT NOT NULL," +
|
||||
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +
|
||||
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
|
||||
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
|
||||
DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," +
|
||||
DataColumns.DATA1 + " INTEGER," +
|
||||
DataColumns.DATA2 + " INTEGER," +
|
||||
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
|
||||
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
|
||||
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
|
||||
")";
|
||||
|
||||
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
|
||||
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
|
||||
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
|
||||
|
||||
/**
|
||||
* Increase folder's note count when move note to the folder
|
||||
*/
|
||||
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
|
||||
"CREATE TRIGGER increase_folder_count_on_update "+
|
||||
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
|
||||
" BEGIN " +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
|
||||
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Decrease folder's note count when move note from folder
|
||||
*/
|
||||
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
|
||||
"CREATE TRIGGER decrease_folder_count_on_update " +
|
||||
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
|
||||
" BEGIN " +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
|
||||
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
|
||||
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Increase folder's note count when insert new note to the folder
|
||||
*/
|
||||
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
|
||||
"CREATE TRIGGER increase_folder_count_on_insert " +
|
||||
" AFTER INSERT ON " + TABLE.NOTE +
|
||||
" BEGIN " +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
|
||||
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Decrease folder's note count when delete note from the folder
|
||||
*/
|
||||
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
|
||||
"CREATE TRIGGER decrease_folder_count_on_delete " +
|
||||
" AFTER DELETE ON " + TABLE.NOTE +
|
||||
" BEGIN " +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
|
||||
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
|
||||
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Update note's content when insert data with type {@link DataConstants#NOTE}
|
||||
*/
|
||||
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
|
||||
"CREATE TRIGGER update_note_content_on_insert " +
|
||||
" AFTER INSERT ON " + TABLE.DATA +
|
||||
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
|
||||
" BEGIN" +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
|
||||
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Update note's content when data with {@link DataConstants#NOTE} type has changed
|
||||
*/
|
||||
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
|
||||
"CREATE TRIGGER update_note_content_on_update " +
|
||||
" AFTER UPDATE ON " + TABLE.DATA +
|
||||
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
|
||||
" BEGIN" +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
|
||||
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
|
||||
*/
|
||||
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
|
||||
"CREATE TRIGGER update_note_content_on_delete " +
|
||||
" AFTER delete ON " + TABLE.DATA +
|
||||
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
|
||||
" BEGIN" +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.SNIPPET + "=''" +
|
||||
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Delete datas belong to note which has been deleted
|
||||
*/
|
||||
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
|
||||
"CREATE TRIGGER delete_data_on_delete " +
|
||||
" AFTER DELETE ON " + TABLE.NOTE +
|
||||
" BEGIN" +
|
||||
" DELETE FROM " + TABLE.DATA +
|
||||
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Delete notes belong to folder which has been deleted
|
||||
*/
|
||||
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
|
||||
"CREATE TRIGGER folder_delete_notes_on_delete " +
|
||||
" AFTER DELETE ON " + TABLE.NOTE +
|
||||
" BEGIN" +
|
||||
" DELETE FROM " + TABLE.NOTE +
|
||||
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
|
||||
" END";
|
||||
|
||||
/**
|
||||
* Move notes belong to folder which has been moved to trash folder
|
||||
*/
|
||||
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
|
||||
"CREATE TRIGGER folder_move_notes_on_trash " +
|
||||
" AFTER UPDATE ON " + TABLE.NOTE +
|
||||
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
|
||||
" BEGIN" +
|
||||
" UPDATE " + TABLE.NOTE +
|
||||
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
|
||||
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
|
||||
" END";
|
||||
|
||||
public NotesDatabaseHelper(Context context) {
|
||||
super(context, DB_NAME, null, DB_VERSION);
|
||||
}
|
||||
|
||||
public void createNoteTable(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_NOTE_TABLE_SQL);
|
||||
reCreateNoteTableTriggers(db);
|
||||
createSystemFolder(db);
|
||||
Log.d(TAG, "note table has been created");
|
||||
}
|
||||
|
||||
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
|
||||
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
|
||||
|
||||
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
|
||||
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
|
||||
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
|
||||
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
|
||||
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
|
||||
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
|
||||
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
|
||||
}
|
||||
|
||||
private void createSystemFolder(SQLiteDatabase db) {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
/**
|
||||
* call record foler for call notes
|
||||
*/
|
||||
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
|
||||
db.insert(TABLE.NOTE, null, values);
|
||||
|
||||
/**
|
||||
* root folder which is default folder
|
||||
*/
|
||||
values.clear();
|
||||
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
|
||||
db.insert(TABLE.NOTE, null, values);
|
||||
|
||||
/**
|
||||
* temporary folder which is used for moving note
|
||||
*/
|
||||
values.clear();
|
||||
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
|
||||
db.insert(TABLE.NOTE, null, values);
|
||||
|
||||
/**
|
||||
* create trash folder
|
||||
*/
|
||||
values.clear();
|
||||
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
|
||||
db.insert(TABLE.NOTE, null, values);
|
||||
}
|
||||
|
||||
public void createDataTable(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_DATA_TABLE_SQL);
|
||||
reCreateDataTableTriggers(db);
|
||||
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
|
||||
Log.d(TAG, "data table has been created");
|
||||
}
|
||||
|
||||
private void reCreateDataTableTriggers(SQLiteDatabase db) {
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
|
||||
|
||||
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
|
||||
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
|
||||
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
|
||||
}
|
||||
|
||||
static synchronized NotesDatabaseHelper getInstance(Context context) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new NotesDatabaseHelper(context);
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
createNoteTable(db);
|
||||
createDataTable(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
boolean reCreateTriggers = false;
|
||||
boolean skipV2 = false;
|
||||
|
||||
if (oldVersion == 1) {
|
||||
upgradeToV2(db);
|
||||
skipV2 = true; // this upgrade including the upgrade from v2 to v3
|
||||
oldVersion++;
|
||||
}
|
||||
|
||||
if (oldVersion == 2 && !skipV2) {
|
||||
upgradeToV3(db);
|
||||
reCreateTriggers = true;
|
||||
oldVersion++;
|
||||
}
|
||||
|
||||
if (oldVersion == 3) {
|
||||
upgradeToV4(db);
|
||||
oldVersion++;
|
||||
}
|
||||
|
||||
if (reCreateTriggers) {
|
||||
reCreateNoteTableTriggers(db);
|
||||
reCreateDataTableTriggers(db);
|
||||
}
|
||||
|
||||
if (oldVersion != newVersion) {
|
||||
throw new IllegalStateException("Upgrade notes database to version " + newVersion
|
||||
+ "fails");
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeToV2(SQLiteDatabase db) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
|
||||
createNoteTable(db);
|
||||
createDataTable(db);
|
||||
}
|
||||
|
||||
private void upgradeToV3(SQLiteDatabase db) {
|
||||
// drop unused triggers
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
|
||||
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
|
||||
// add a column for gtask id
|
||||
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
|
||||
+ " TEXT NOT NULL DEFAULT ''");
|
||||
// add a trash system folder
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
|
||||
db.insert(TABLE.NOTE, null, values);
|
||||
}
|
||||
|
||||
private void upgradeToV4(SQLiteDatabase db) {
|
||||
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
|
||||
+ " INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.data;
|
||||
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
|
||||
|
||||
|
||||
public class NotesProvider extends ContentProvider {
|
||||
private static final UriMatcher mMatcher;
|
||||
|
||||
private NotesDatabaseHelper mHelper;
|
||||
|
||||
private static final String TAG = "NotesProvider";
|
||||
|
||||
private static final int URI_NOTE = 1;
|
||||
private static final int URI_NOTE_ITEM = 2;
|
||||
private static final int URI_DATA = 3;
|
||||
private static final int URI_DATA_ITEM = 4;
|
||||
|
||||
private static final int URI_SEARCH = 5;
|
||||
private static final int URI_SEARCH_SUGGEST = 6;
|
||||
|
||||
static {
|
||||
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
|
||||
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
|
||||
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
|
||||
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
|
||||
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
|
||||
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
|
||||
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* x'0A' represents the '\n' character in sqlite. For title and content in the search result,
|
||||
* we will trim '\n' and white space in order to show more information.
|
||||
*/
|
||||
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
|
||||
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
|
||||
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
|
||||
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
|
||||
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
|
||||
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
|
||||
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
|
||||
|
||||
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
|
||||
+ " FROM " + TABLE.NOTE
|
||||
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
|
||||
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
|
||||
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
mHelper = NotesDatabaseHelper.getInstance(getContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
Cursor c = null;
|
||||
SQLiteDatabase db = mHelper.getReadableDatabase();
|
||||
String id = null;
|
||||
switch (mMatcher.match(uri)) {
|
||||
case URI_NOTE:
|
||||
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
|
||||
sortOrder);
|
||||
break;
|
||||
case URI_NOTE_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
|
||||
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
|
||||
break;
|
||||
case URI_DATA:
|
||||
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
|
||||
sortOrder);
|
||||
break;
|
||||
case URI_DATA_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
|
||||
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
|
||||
break;
|
||||
case URI_SEARCH:
|
||||
case URI_SEARCH_SUGGEST:
|
||||
if (sortOrder != null || projection != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
|
||||
}
|
||||
|
||||
String searchString = null;
|
||||
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
|
||||
if (uri.getPathSegments().size() > 1) {
|
||||
searchString = uri.getPathSegments().get(1);
|
||||
}
|
||||
} else {
|
||||
searchString = uri.getQueryParameter("pattern");
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(searchString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
searchString = String.format("%%%s%%", searchString);
|
||||
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
|
||||
new String[] { searchString });
|
||||
} catch (IllegalStateException ex) {
|
||||
Log.e(TAG, "got exception: " + ex.toString());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
if (c != null) {
|
||||
c.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
SQLiteDatabase db = mHelper.getWritableDatabase();
|
||||
long dataId = 0, noteId = 0, insertedId = 0;
|
||||
switch (mMatcher.match(uri)) {
|
||||
case URI_NOTE:
|
||||
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
|
||||
break;
|
||||
case URI_DATA:
|
||||
if (values.containsKey(DataColumns.NOTE_ID)) {
|
||||
noteId = values.getAsLong(DataColumns.NOTE_ID);
|
||||
} else {
|
||||
Log.d(TAG, "Wrong data format without note id:" + values.toString());
|
||||
}
|
||||
insertedId = dataId = db.insert(TABLE.DATA, null, values);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
// Notify the note uri
|
||||
if (noteId > 0) {
|
||||
getContext().getContentResolver().notifyChange(
|
||||
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
|
||||
}
|
||||
|
||||
// Notify the data uri
|
||||
if (dataId > 0) {
|
||||
getContext().getContentResolver().notifyChange(
|
||||
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
|
||||
}
|
||||
|
||||
return ContentUris.withAppendedId(uri, insertedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
int count = 0;
|
||||
String id = null;
|
||||
SQLiteDatabase db = mHelper.getWritableDatabase();
|
||||
boolean deleteData = false;
|
||||
switch (mMatcher.match(uri)) {
|
||||
case URI_NOTE:
|
||||
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
|
||||
count = db.delete(TABLE.NOTE, selection, selectionArgs);
|
||||
break;
|
||||
case URI_NOTE_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
/**
|
||||
* ID that smaller than 0 is system folder which is not allowed to
|
||||
* trash
|
||||
*/
|
||||
long noteId = Long.valueOf(id);
|
||||
if (noteId <= 0) {
|
||||
break;
|
||||
}
|
||||
count = db.delete(TABLE.NOTE,
|
||||
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
|
||||
break;
|
||||
case URI_DATA:
|
||||
count = db.delete(TABLE.DATA, selection, selectionArgs);
|
||||
deleteData = true;
|
||||
break;
|
||||
case URI_DATA_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
count = db.delete(TABLE.DATA,
|
||||
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
|
||||
deleteData = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
if (count > 0) {
|
||||
if (deleteData) {
|
||||
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
int count = 0;
|
||||
String id = null;
|
||||
SQLiteDatabase db = mHelper.getWritableDatabase();
|
||||
boolean updateData = false;
|
||||
switch (mMatcher.match(uri)) {
|
||||
case URI_NOTE:
|
||||
increaseNoteVersion(-1, selection, selectionArgs);
|
||||
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
|
||||
break;
|
||||
case URI_NOTE_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
|
||||
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
|
||||
+ parseSelection(selection), selectionArgs);
|
||||
break;
|
||||
case URI_DATA:
|
||||
count = db.update(TABLE.DATA, values, selection, selectionArgs);
|
||||
updateData = true;
|
||||
break;
|
||||
case URI_DATA_ITEM:
|
||||
id = uri.getPathSegments().get(1);
|
||||
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
|
||||
+ parseSelection(selection), selectionArgs);
|
||||
updateData = true;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
if (updateData) {
|
||||
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private String parseSelection(String selection) {
|
||||
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
|
||||
}
|
||||
|
||||
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
|
||||
StringBuilder sql = new StringBuilder(120);
|
||||
sql.append("UPDATE ");
|
||||
sql.append(TABLE.NOTE);
|
||||
sql.append(" SET ");
|
||||
sql.append(NoteColumns.VERSION);
|
||||
sql.append("=" + NoteColumns.VERSION + "+1 ");
|
||||
|
||||
if (id > 0 || !TextUtils.isEmpty(selection)) {
|
||||
sql.append(" WHERE ");
|
||||
}
|
||||
if (id > 0) {
|
||||
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
|
||||
}
|
||||
if (!TextUtils.isEmpty(selection)) {
|
||||
String selectString = id > 0 ? parseSelection(selection) : selection;
|
||||
for (String args : selectionArgs) {
|
||||
selectString = selectString.replaceFirst("\\?", args);
|
||||
}
|
||||
sql.append(selectString);
|
||||
}
|
||||
|
||||
mHelper.getWritableDatabase().execSQL(sql.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.tool.GTaskStringUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class MetaData extends Task {
|
||||
private final static String TAG = MetaData.class.getSimpleName();
|
||||
|
||||
private String mRelatedGid = null;
|
||||
|
||||
public void setMeta(String gid, JSONObject metaInfo) {
|
||||
try {
|
||||
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "failed to put related gid");
|
||||
}
|
||||
setNotes(metaInfo.toString());
|
||||
setName(GTaskStringUtils.META_NOTE_NAME);
|
||||
}
|
||||
|
||||
public String getRelatedGid() {
|
||||
return mRelatedGid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWorthSaving() {
|
||||
return getNotes() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentByRemoteJSON(JSONObject js) {
|
||||
super.setContentByRemoteJSON(js);
|
||||
if (getNotes() != null) {
|
||||
try {
|
||||
JSONObject metaInfo = new JSONObject(getNotes().trim());
|
||||
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, "failed to get related gid");
|
||||
mRelatedGid = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentByLocalJSON(JSONObject js) {
|
||||
// this function should not be called
|
||||
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getLocalJSONFromContent() {
|
||||
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSyncAction(Cursor c) {
|
||||
throw new IllegalAccessError("MetaData:getSyncAction should not be called");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class Node {
|
||||
public static final int SYNC_ACTION_NONE = 0;
|
||||
|
||||
public static final int SYNC_ACTION_ADD_REMOTE = 1;
|
||||
|
||||
public static final int SYNC_ACTION_ADD_LOCAL = 2;
|
||||
|
||||
public static final int SYNC_ACTION_DEL_REMOTE = 3;
|
||||
|
||||
public static final int SYNC_ACTION_DEL_LOCAL = 4;
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
|
||||
|
||||
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
|
||||
|
||||
public static final int SYNC_ACTION_ERROR = 8;
|
||||
|
||||
private String mGid;
|
||||
|
||||
private String mName;
|
||||
|
||||
private long mLastModified;
|
||||
|
||||
private boolean mDeleted;
|
||||
|
||||
public Node() {
|
||||
mGid = null;
|
||||
mName = "";
|
||||
mLastModified = 0;
|
||||
mDeleted = false;
|
||||
}
|
||||
|
||||
public abstract JSONObject getCreateAction(int actionId);
|
||||
|
||||
public abstract JSONObject getUpdateAction(int actionId);
|
||||
|
||||
public abstract void setContentByRemoteJSON(JSONObject js);
|
||||
|
||||
public abstract void setContentByLocalJSON(JSONObject js);
|
||||
|
||||
public abstract JSONObject getLocalJSONFromContent();
|
||||
|
||||
public abstract int getSyncAction(Cursor c);
|
||||
|
||||
public void setGid(String gid) {
|
||||
this.mGid = gid;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.mName = name;
|
||||
}
|
||||
|
||||
public void setLastModified(long lastModified) {
|
||||
this.mLastModified = lastModified;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.mDeleted = deleted;
|
||||
}
|
||||
|
||||
public String getGid() {
|
||||
return this.mGid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.mName;
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return this.mLastModified;
|
||||
}
|
||||
|
||||
public boolean getDeleted() {
|
||||
return this.mDeleted;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.DataConstants;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
|
||||
import net.micode.notes.gtask.exception.ActionFailureException;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class SqlData {
|
||||
private static final String TAG = SqlData.class.getSimpleName();
|
||||
|
||||
private static final int INVALID_ID = -99999;
|
||||
|
||||
public static final String[] PROJECTION_DATA = new String[] {
|
||||
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
|
||||
DataColumns.DATA3
|
||||
};
|
||||
|
||||
public static final int DATA_ID_COLUMN = 0;
|
||||
|
||||
public static final int DATA_MIME_TYPE_COLUMN = 1;
|
||||
|
||||
public static final int DATA_CONTENT_COLUMN = 2;
|
||||
|
||||
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
|
||||
|
||||
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private boolean mIsCreate;
|
||||
|
||||
private long mDataId;
|
||||
|
||||
private String mDataMimeType;
|
||||
|
||||
private String mDataContent;
|
||||
|
||||
private long mDataContentData1;
|
||||
|
||||
private String mDataContentData3;
|
||||
|
||||
private ContentValues mDiffDataValues;
|
||||
|
||||
public SqlData(Context context) {
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = true;
|
||||
mDataId = INVALID_ID;
|
||||
mDataMimeType = DataConstants.NOTE;
|
||||
mDataContent = "";
|
||||
mDataContentData1 = 0;
|
||||
mDataContentData3 = "";
|
||||
mDiffDataValues = new ContentValues();
|
||||
}
|
||||
|
||||
public SqlData(Context context, Cursor c) {
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = false;
|
||||
loadFromCursor(c);
|
||||
mDiffDataValues = new ContentValues();
|
||||
}
|
||||
|
||||
private void loadFromCursor(Cursor c) {
|
||||
mDataId = c.getLong(DATA_ID_COLUMN);
|
||||
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
|
||||
mDataContent = c.getString(DATA_CONTENT_COLUMN);
|
||||
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
|
||||
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
|
||||
}
|
||||
|
||||
public void setContent(JSONObject js) throws JSONException {
|
||||
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
|
||||
if (mIsCreate || mDataId != dataId) {
|
||||
mDiffDataValues.put(DataColumns.ID, dataId);
|
||||
}
|
||||
mDataId = dataId;
|
||||
|
||||
String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)
|
||||
: DataConstants.NOTE;
|
||||
if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {
|
||||
mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);
|
||||
}
|
||||
mDataMimeType = dataMimeType;
|
||||
|
||||
String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";
|
||||
if (mIsCreate || !mDataContent.equals(dataContent)) {
|
||||
mDiffDataValues.put(DataColumns.CONTENT, dataContent);
|
||||
}
|
||||
mDataContent = dataContent;
|
||||
|
||||
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;
|
||||
if (mIsCreate || mDataContentData1 != dataContentData1) {
|
||||
mDiffDataValues.put(DataColumns.DATA1, dataContentData1);
|
||||
}
|
||||
mDataContentData1 = dataContentData1;
|
||||
|
||||
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
|
||||
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
|
||||
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
|
||||
}
|
||||
mDataContentData3 = dataContentData3;
|
||||
}
|
||||
|
||||
public JSONObject getContent() throws JSONException {
|
||||
if (mIsCreate) {
|
||||
Log.e(TAG, "it seems that we haven't created this in database yet");
|
||||
return null;
|
||||
}
|
||||
JSONObject js = new JSONObject();
|
||||
js.put(DataColumns.ID, mDataId);
|
||||
js.put(DataColumns.MIME_TYPE, mDataMimeType);
|
||||
js.put(DataColumns.CONTENT, mDataContent);
|
||||
js.put(DataColumns.DATA1, mDataContentData1);
|
||||
js.put(DataColumns.DATA3, mDataContentData3);
|
||||
return js;
|
||||
}
|
||||
|
||||
public void commit(long noteId, boolean validateVersion, long version) {
|
||||
|
||||
if (mIsCreate) {
|
||||
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
|
||||
mDiffDataValues.remove(DataColumns.ID);
|
||||
}
|
||||
|
||||
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
|
||||
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
|
||||
try {
|
||||
mDataId = Long.valueOf(uri.getPathSegments().get(1));
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Get note id error :" + e.toString());
|
||||
throw new ActionFailureException("create note failed");
|
||||
}
|
||||
} else {
|
||||
if (mDiffDataValues.size() > 0) {
|
||||
int result = 0;
|
||||
if (!validateVersion) {
|
||||
result = mContentResolver.update(ContentUris.withAppendedId(
|
||||
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
|
||||
} else {
|
||||
result = mContentResolver.update(ContentUris.withAppendedId(
|
||||
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
|
||||
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE
|
||||
+ " WHERE " + NoteColumns.VERSION + "=?)", new String[] {
|
||||
String.valueOf(noteId), String.valueOf(version)
|
||||
});
|
||||
}
|
||||
if (result == 0) {
|
||||
Log.w(TAG, "there is no update. maybe user updates note when syncing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDiffDataValues.clear();
|
||||
mIsCreate = false;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return mDataId;
|
||||
}
|
||||
}
|
@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.gtask.exception.ActionFailureException;
|
||||
import net.micode.notes.tool.GTaskStringUtils;
|
||||
import net.micode.notes.tool.ResourceParser;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public class SqlNote {
|
||||
private static final String TAG = SqlNote.class.getSimpleName();
|
||||
|
||||
private static final int INVALID_ID = -99999;
|
||||
|
||||
public static final String[] PROJECTION_NOTE = new String[] {
|
||||
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
|
||||
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
|
||||
NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE,
|
||||
NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID,
|
||||
NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID,
|
||||
NoteColumns.VERSION
|
||||
};
|
||||
|
||||
public static final int ID_COLUMN = 0;
|
||||
|
||||
public static final int ALERTED_DATE_COLUMN = 1;
|
||||
|
||||
public static final int BG_COLOR_ID_COLUMN = 2;
|
||||
|
||||
public static final int CREATED_DATE_COLUMN = 3;
|
||||
|
||||
public static final int HAS_ATTACHMENT_COLUMN = 4;
|
||||
|
||||
public static final int MODIFIED_DATE_COLUMN = 5;
|
||||
|
||||
public static final int NOTES_COUNT_COLUMN = 6;
|
||||
|
||||
public static final int PARENT_ID_COLUMN = 7;
|
||||
|
||||
public static final int SNIPPET_COLUMN = 8;
|
||||
|
||||
public static final int TYPE_COLUMN = 9;
|
||||
|
||||
public static final int WIDGET_ID_COLUMN = 10;
|
||||
|
||||
public static final int WIDGET_TYPE_COLUMN = 11;
|
||||
|
||||
public static final int SYNC_ID_COLUMN = 12;
|
||||
|
||||
public static final int LOCAL_MODIFIED_COLUMN = 13;
|
||||
|
||||
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
|
||||
|
||||
public static final int GTASK_ID_COLUMN = 15;
|
||||
|
||||
public static final int VERSION_COLUMN = 16;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private boolean mIsCreate;
|
||||
|
||||
private long mId;
|
||||
|
||||
private long mAlertDate;
|
||||
|
||||
private int mBgColorId;
|
||||
|
||||
private long mCreatedDate;
|
||||
|
||||
private int mHasAttachment;
|
||||
|
||||
private long mModifiedDate;
|
||||
|
||||
private long mParentId;
|
||||
|
||||
private String mSnippet;
|
||||
|
||||
private int mType;
|
||||
|
||||
private int mWidgetId;
|
||||
|
||||
private int mWidgetType;
|
||||
|
||||
private long mOriginParent;
|
||||
|
||||
private long mVersion;
|
||||
|
||||
private ContentValues mDiffNoteValues;
|
||||
|
||||
private ArrayList<SqlData> mDataList;
|
||||
|
||||
public SqlNote(Context context) {
|
||||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = true;
|
||||
mId = INVALID_ID;
|
||||
mAlertDate = 0;
|
||||
mBgColorId = ResourceParser.getDefaultBgId(context);
|
||||
mCreatedDate = System.currentTimeMillis();
|
||||
mHasAttachment = 0;
|
||||
mModifiedDate = System.currentTimeMillis();
|
||||
mParentId = 0;
|
||||
mSnippet = "";
|
||||
mType = Notes.TYPE_NOTE;
|
||||
mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
|
||||
mOriginParent = 0;
|
||||
mVersion = 0;
|
||||
mDiffNoteValues = new ContentValues();
|
||||
mDataList = new ArrayList<SqlData>();
|
||||
}
|
||||
|
||||
public SqlNote(Context context, Cursor c) {
|
||||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = false;
|
||||
loadFromCursor(c);
|
||||
mDataList = new ArrayList<SqlData>();
|
||||
if (mType == Notes.TYPE_NOTE)
|
||||
loadDataContent();
|
||||
mDiffNoteValues = new ContentValues();
|
||||
}
|
||||
|
||||
public SqlNote(Context context, long id) {
|
||||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
mIsCreate = false;
|
||||
loadFromCursor(id);
|
||||
mDataList = new ArrayList<SqlData>();
|
||||
if (mType == Notes.TYPE_NOTE)
|
||||
loadDataContent();
|
||||
mDiffNoteValues = new ContentValues();
|
||||
|
||||
}
|
||||
|
||||
private void loadFromCursor(long id) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
|
||||
new String[] {
|
||||
String.valueOf(id)
|
||||
}, null);
|
||||
if (c != null) {
|
||||
c.moveToNext();
|
||||
loadFromCursor(c);
|
||||
} else {
|
||||
Log.w(TAG, "loadFromCursor: cursor = null");
|
||||
}
|
||||
} finally {
|
||||
if (c != null)
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromCursor(Cursor c) {
|
||||
mId = c.getLong(ID_COLUMN);
|
||||
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
|
||||
mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
|
||||
mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
|
||||
mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
|
||||
mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN);
|
||||
mParentId = c.getLong(PARENT_ID_COLUMN);
|
||||
mSnippet = c.getString(SNIPPET_COLUMN);
|
||||
mType = c.getInt(TYPE_COLUMN);
|
||||
mWidgetId = c.getInt(WIDGET_ID_COLUMN);
|
||||
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
|
||||
mVersion = c.getLong(VERSION_COLUMN);
|
||||
}
|
||||
|
||||
private void loadDataContent() {
|
||||
Cursor c = null;
|
||||
mDataList.clear();
|
||||
try {
|
||||
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
|
||||
"(note_id=?)", new String[] {
|
||||
String.valueOf(mId)
|
||||
}, null);
|
||||
if (c != null) {
|
||||
if (c.getCount() == 0) {
|
||||
Log.w(TAG, "it seems that the note has not data");
|
||||
return;
|
||||
}
|
||||
while (c.moveToNext()) {
|
||||
SqlData data = new SqlData(mContext, c);
|
||||
mDataList.add(data);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "loadDataContent: cursor = null");
|
||||
}
|
||||
} finally {
|
||||
if (c != null)
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setContent(JSONObject js) {
|
||||
try {
|
||||
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
|
||||
Log.w(TAG, "cannot set system folder");
|
||||
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
|
||||
// for folder we can only update the snnipet and type
|
||||
String snippet = note.has(NoteColumns.SNIPPET) ? note
|
||||
.getString(NoteColumns.SNIPPET) : "";
|
||||
if (mIsCreate || !mSnippet.equals(snippet)) {
|
||||
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
|
||||
}
|
||||
mSnippet = snippet;
|
||||
|
||||
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
|
||||
: Notes.TYPE_NOTE;
|
||||
if (mIsCreate || mType != type) {
|
||||
mDiffNoteValues.put(NoteColumns.TYPE, type);
|
||||
}
|
||||
mType = type;
|
||||
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
|
||||
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
|
||||
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
|
||||
if (mIsCreate || mId != id) {
|
||||
mDiffNoteValues.put(NoteColumns.ID, id);
|
||||
}
|
||||
mId = id;
|
||||
|
||||
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
|
||||
.getLong(NoteColumns.ALERTED_DATE) : 0;
|
||||
if (mIsCreate || mAlertDate != alertDate) {
|
||||
mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
|
||||
}
|
||||
mAlertDate = alertDate;
|
||||
|
||||
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
|
||||
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
|
||||
if (mIsCreate || mBgColorId != bgColorId) {
|
||||
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
|
||||
}
|
||||
mBgColorId = bgColorId;
|
||||
|
||||
long createDate = note.has(NoteColumns.CREATED_DATE) ? note
|
||||
.getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
|
||||
if (mIsCreate || mCreatedDate != createDate) {
|
||||
mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
|
||||
}
|
||||
mCreatedDate = createDate;
|
||||
|
||||
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note
|
||||
.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
|
||||
if (mIsCreate || mHasAttachment != hasAttachment) {
|
||||
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
|
||||
}
|
||||
mHasAttachment = hasAttachment;
|
||||
|
||||
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note
|
||||
.getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
|
||||
if (mIsCreate || mModifiedDate != modifiedDate) {
|
||||
mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
|
||||
}
|
||||
mModifiedDate = modifiedDate;
|
||||
|
||||
long parentId = note.has(NoteColumns.PARENT_ID) ? note
|
||||
.getLong(NoteColumns.PARENT_ID) : 0;
|
||||
if (mIsCreate || mParentId != parentId) {
|
||||
mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
|
||||
}
|
||||
mParentId = parentId;
|
||||
|
||||
String snippet = note.has(NoteColumns.SNIPPET) ? note
|
||||
.getString(NoteColumns.SNIPPET) : "";
|
||||
if (mIsCreate || !mSnippet.equals(snippet)) {
|
||||
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
|
||||
}
|
||||
mSnippet = snippet;
|
||||
|
||||
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
|
||||
: Notes.TYPE_NOTE;
|
||||
if (mIsCreate || mType != type) {
|
||||
mDiffNoteValues.put(NoteColumns.TYPE, type);
|
||||
}
|
||||
mType = type;
|
||||
|
||||
int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID)
|
||||
: AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||
if (mIsCreate || mWidgetId != widgetId) {
|
||||
mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);
|
||||
}
|
||||
mWidgetId = widgetId;
|
||||
|
||||
int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note
|
||||
.getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;
|
||||
if (mIsCreate || mWidgetType != widgetType) {
|
||||
mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);
|
||||
}
|
||||
mWidgetType = widgetType;
|
||||
|
||||
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
|
||||
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
|
||||
if (mIsCreate || mOriginParent != originParent) {
|
||||
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
|
||||
}
|
||||
mOriginParent = originParent;
|
||||
|
||||
for (int i = 0; i < dataArray.length(); i++) {
|
||||
JSONObject data = dataArray.getJSONObject(i);
|
||||
SqlData sqlData = null;
|
||||
if (data.has(DataColumns.ID)) {
|
||||
long dataId = data.getLong(DataColumns.ID);
|
||||
for (SqlData temp : mDataList) {
|
||||
if (dataId == temp.getId()) {
|
||||
sqlData = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sqlData == null) {
|
||||
sqlData = new SqlData(mContext);
|
||||
mDataList.add(sqlData);
|
||||
}
|
||||
|
||||
sqlData.setContent(data);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public JSONObject getContent() {
|
||||
try {
|
||||
JSONObject js = new JSONObject();
|
||||
|
||||
if (mIsCreate) {
|
||||
Log.e(TAG, "it seems that we haven't created this in database yet");
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject note = new JSONObject();
|
||||
if (mType == Notes.TYPE_NOTE) {
|
||||
note.put(NoteColumns.ID, mId);
|
||||
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
|
||||
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
|
||||
note.put(NoteColumns.CREATED_DATE, mCreatedDate);
|
||||
note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
|
||||
note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
|
||||
note.put(NoteColumns.PARENT_ID, mParentId);
|
||||
note.put(NoteColumns.SNIPPET, mSnippet);
|
||||
note.put(NoteColumns.TYPE, mType);
|
||||
note.put(NoteColumns.WIDGET_ID, mWidgetId);
|
||||
note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
|
||||
note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
|
||||
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
|
||||
|
||||
JSONArray dataArray = new JSONArray();
|
||||
for (SqlData sqlData : mDataList) {
|
||||
JSONObject data = sqlData.getContent();
|
||||
if (data != null) {
|
||||
dataArray.put(data);
|
||||
}
|
||||
}
|
||||
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
|
||||
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
|
||||
note.put(NoteColumns.ID, mId);
|
||||
note.put(NoteColumns.TYPE, mType);
|
||||
note.put(NoteColumns.SNIPPET, mSnippet);
|
||||
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
|
||||
}
|
||||
|
||||
return js;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setParentId(long id) {
|
||||
mParentId = id;
|
||||
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
|
||||
}
|
||||
|
||||
public void setGtaskId(String gid) {
|
||||
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
|
||||
}
|
||||
|
||||
public void setSyncId(long syncId) {
|
||||
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
|
||||
}
|
||||
|
||||
public void resetLocalModified() {
|
||||
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public long getParentId() {
|
||||
return mParentId;
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return mSnippet;
|
||||
}
|
||||
|
||||
public boolean isNoteType() {
|
||||
return mType == Notes.TYPE_NOTE;
|
||||
}
|
||||
|
||||
public void commit(boolean validateVersion) {
|
||||
if (mIsCreate) {
|
||||
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
|
||||
mDiffNoteValues.remove(NoteColumns.ID);
|
||||
}
|
||||
|
||||
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
|
||||
try {
|
||||
mId = Long.valueOf(uri.getPathSegments().get(1));
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Get note id error :" + e.toString());
|
||||
throw new ActionFailureException("create note failed");
|
||||
}
|
||||
if (mId == 0) {
|
||||
throw new IllegalStateException("Create thread id failed");
|
||||
}
|
||||
|
||||
if (mType == Notes.TYPE_NOTE) {
|
||||
for (SqlData sqlData : mDataList) {
|
||||
sqlData.commit(mId, false, -1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
|
||||
Log.e(TAG, "No such note");
|
||||
throw new IllegalStateException("Try to update note with invalid id");
|
||||
}
|
||||
if (mDiffNoteValues.size() > 0) {
|
||||
mVersion ++;
|
||||
int result = 0;
|
||||
if (!validateVersion) {
|
||||
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
|
||||
+ NoteColumns.ID + "=?)", new String[] {
|
||||
String.valueOf(mId)
|
||||
});
|
||||
} else {
|
||||
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
|
||||
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
|
||||
new String[] {
|
||||
String.valueOf(mId), String.valueOf(mVersion)
|
||||
});
|
||||
}
|
||||
if (result == 0) {
|
||||
Log.w(TAG, "there is no update. maybe user updates note when syncing");
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == Notes.TYPE_NOTE) {
|
||||
for (SqlData sqlData : mDataList) {
|
||||
sqlData.commit(mId, validateVersion, mVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refresh local info
|
||||
loadFromCursor(mId);
|
||||
if (mType == Notes.TYPE_NOTE)
|
||||
loadDataContent();
|
||||
|
||||
mDiffNoteValues.clear();
|
||||
mIsCreate = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.gtask.data;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.DataConstants;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.gtask.exception.ActionFailureException;
|
||||
import net.micode.notes.tool.GTaskStringUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class Task extends Node {
|
||||
private static final String TAG = Task.class.getSimpleName();
|
||||
|
||||
private boolean mCompleted;
|
||||
|
||||
private String mNotes;
|
||||
|
||||
private JSONObject mMetaInfo;
|
||||
|
||||
private Task mPriorSibling;
|
||||
|
||||
private TaskList mParent;
|
||||
|
||||
public Task() {
|
||||
super();
|
||||
mCompleted = false;
|
||||
mNotes = null;
|
||||
mPriorSibling = null;
|
||||
mParent = null;
|
||||
mMetaInfo = null;
|
||||
}
|
||||
|
||||
public JSONObject getCreateAction(int actionId) {
|
||||
JSONObject js = new JSONObject();
|
||||
|
||||
try {
|
||||
// action_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
|
||||
|
||||
// action_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
|
||||
|
||||
// index
|
||||
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
|
||||
|
||||
// entity_delta
|
||||
JSONObject entity = new JSONObject();
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_TYPE_TASK);
|
||||
if (getNotes() != null) {
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
|
||||
}
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
|
||||
|
||||
// parent_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());
|
||||
|
||||
// dest_parent_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_TYPE_GROUP);
|
||||
|
||||
// list_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());
|
||||
|
||||
// prior_sibling_id
|
||||
if (mPriorSibling != null) {
|
||||
js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to generate task-create jsonobject");
|
||||
}
|
||||
|
||||
return js;
|
||||
}
|
||||
|
||||
public JSONObject getUpdateAction(int actionId) {
|
||||
JSONObject js = new JSONObject();
|
||||
|
||||
try {
|
||||
// action_type
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
|
||||
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
|
||||
|
||||
// action_id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
|
||||
|
||||
// id
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
|
||||
|
||||
// entity_delta
|
||||
JSONObject entity = new JSONObject();
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
|
||||
if (getNotes() != null) {
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
|
||||
}
|
||||
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
|
||||
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to generate task-update jsonobject");
|
||||
}
|
||||
|
||||
return js;
|
||||
}
|
||||
|
||||
public void setContentByRemoteJSON(JSONObject js) {
|
||||
if (js != null) {
|
||||
try {
|
||||
// id
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
|
||||
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
|
||||
}
|
||||
|
||||
// last_modified
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
|
||||
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
|
||||
}
|
||||
|
||||
// name
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
|
||||
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
|
||||
}
|
||||
|
||||
// notes
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) {
|
||||
setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));
|
||||
}
|
||||
|
||||
// deleted
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) {
|
||||
setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED));
|
||||
}
|
||||
|
||||
// completed
|
||||
if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) {
|
||||
setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
throw new ActionFailureException("fail to get task content from jsonobject");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentByLocalJSON(JSONObject js) {
|
||||
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
|
||||
|| !js.has(GTaskStringUtils.META_HEAD_DATA)) {
|
||||
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
|
||||
|
||||
if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {
|
||||
Log.e(TAG, "invalid type");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dataArray.length(); i++) {
|
||||
JSONObject data = dataArray.getJSONObject(i);
|
||||
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
|
||||
setName(data.getString(DataColumns.CONTENT));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject getLocalJSONFromContent() {
|
||||
String name = getName();
|
||||
try {
|
||||
if (mMetaInfo == null) {
|
||||
// new task created from web
|
||||
if (name == null) {
|
||||
Log.w(TAG, "the note seems to be an empty one");
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject js = new JSONObject();
|
||||
JSONObject note = new JSONObject();
|
||||
JSONArray dataArray = new JSONArray();
|
||||
JSONObject data = new JSONObject();
|
||||
data.put(DataColumns.CONTENT, name);
|
||||
dataArray.put(data);
|
||||
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
|
||||
note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
|
||||
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
|
||||
return js;
|
||||
} else {
|
||||
// synced task
|
||||
JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
|
||||
|
||||
for (int i = 0; i < dataArray.length(); i++) {
|
||||
JSONObject data = dataArray.getJSONObject(i);
|
||||
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
|
||||
data.put(DataColumns.CONTENT, getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
|
||||
return mMetaInfo;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMetaInfo(MetaData metaData) {
|
||||
if (metaData != null && metaData.getNotes() != null) {
|
||||
try {
|
||||
mMetaInfo = new JSONObject(metaData.getNotes());
|
||||
} catch (JSONException e) {
|
||||
Log.w(TAG, e.toString());
|
||||
mMetaInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getSyncAction(Cursor c) {
|
||||
try {
|
||||
JSONObject noteInfo = null;
|
||||
if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) {
|
||||
noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
|
||||
}
|
||||
|
||||
if (noteInfo == null) {
|
||||
Log.w(TAG, "it seems that note meta has been deleted");
|
||||
return SYNC_ACTION_UPDATE_REMOTE;
|
||||
}
|
||||
|
||||
if (!noteInfo.has(NoteColumns.ID)) {
|
||||
Log.w(TAG, "remote note id seems to be deleted");
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
|
||||
// validate the note id now
|
||||
if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {
|
||||
Log.w(TAG, "note id doesn't match");
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
|
||||
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
|
||||
// there is no local update
|
||||
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
|
||||
// no update both side
|
||||
return SYNC_ACTION_NONE;
|
||||
} else {
|
||||
// apply remote to local
|
||||
return SYNC_ACTION_UPDATE_LOCAL;
|
||||
}
|
||||
} else {
|
||||
// validate gtask id
|
||||
if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
|
||||
Log.e(TAG, "gtask id doesn't match");
|
||||
return SYNC_ACTION_ERROR;
|
||||
}
|
||||
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
|
||||
// local modification only
|
||||
return SYNC_ACTION_UPDATE_REMOTE;
|
||||
} else {
|
||||
return SYNC_ACTION_UPDATE_CONFLICT;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return SYNC_ACTION_ERROR;
|
||||
}
|
||||
|
||||
public boolean isWorthSaving() {
|
||||
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|
||||
|| (getNotes() != null && getNotes().trim().length() > 0);
|
||||
}
|
||||
|
||||
public void setCompleted(boolean completed) {
|
||||
this.mCompleted = completed;
|
||||
}
|
||||
|
||||
public void setNotes(String notes) {
|
||||
this.mNotes = notes;
|
||||
}
|
||||
|
||||
public void setPriorSibling(Task priorSibling) {
|
||||
this.mPriorSibling = priorSibling;
|
||||
}
|
||||
|
||||
public void setParent(TaskList parent) {
|
||||
this.mParent = parent;
|
||||
}
|
||||
|
||||
public boolean getCompleted() {
|
||||
return this.mCompleted;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return this.mNotes;
|
||||
}
|
||||
|
||||
public Task getPriorSibling() {
|
||||
return this.mPriorSibling;
|
||||
}
|
||||
|
||||
public TaskList getParent() {
|
||||
return this.mParent;
|
||||
}
|
||||
|
||||
}
|
@ -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,125 @@
|
||||
|
||||
/*
|
||||
* 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);
|
||||
notification.contentIntent = 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,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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface.OnDismissListener;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.tool.DataUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
|
||||
private long mNoteId;
|
||||
private String mSnippet;
|
||||
private static final int SNIPPET_PREW_MAX_LEN = 60;
|
||||
MediaPlayer mPlayer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
final Window win = getWindow();
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
|
||||
if (!isScreenOn()) {
|
||||
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
try {
|
||||
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
|
||||
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
|
||||
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
|
||||
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
|
||||
: mSnippet;
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
mPlayer = new MediaPlayer();
|
||||
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
|
||||
showActionDialog();
|
||||
playAlarmSound();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isScreenOn() {
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
return pm.isScreenOn();
|
||||
}
|
||||
|
||||
private void playAlarmSound() {
|
||||
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
|
||||
|
||||
int silentModeStreams = Settings.System.getInt(getContentResolver(),
|
||||
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
|
||||
|
||||
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
|
||||
mPlayer.setAudioStreamType(silentModeStreams);
|
||||
} else {
|
||||
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
|
||||
}
|
||||
try {
|
||||
mPlayer.setDataSource(this, url);
|
||||
mPlayer.prepare();
|
||||
mPlayer.setLooping(true);
|
||||
mPlayer.start();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void showActionDialog() {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||
dialog.setTitle(R.string.app_name);
|
||||
dialog.setMessage(mSnippet);
|
||||
dialog.setPositiveButton(R.string.notealert_ok, this);
|
||||
if (isScreenOn()) {
|
||||
dialog.setNegativeButton(R.string.notealert_enter, this);
|
||||
}
|
||||
dialog.show().setOnDismissListener(this);
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
Intent intent = new Intent(this, NoteEditActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(Intent.EXTRA_UID, mNoteId);
|
||||
startActivity(intent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
stopAlarmSound();
|
||||
finish();
|
||||
}
|
||||
|
||||
private void stopAlarmSound() {
|
||||
if (mPlayer != null) {
|
||||
mPlayer.stop();
|
||||
mPlayer.release();
|
||||
mPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue