Compare commits
2 Commits
master
...
zxy_branch
| Author | SHA1 | Date |
|---|---|---|
|
|
926c2306c6 | 3 years ago |
|
|
4086638dc2 | 3 years ago |
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17_PREVIEW" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17_PREVIEW" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@ -1,288 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AndroidLayouts">
|
|
||||||
<shared>
|
|
||||||
<config />
|
|
||||||
</shared>
|
|
||||||
</component>
|
|
||||||
<component name="AutoImportSettings">
|
|
||||||
<option name="autoReloadType" value="NONE" />
|
|
||||||
</component>
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="2531eea7-ec98-43ef-9f7f-09fe1004db56" name="变更" comment="庞浩的注释">
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/小米便签开源代码的泛读报告.docx" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/Contact.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/Note.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/Notes.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/NotesDatabaseHelper.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/NotesProvider.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/庞浩注释的代码/WorkingNote.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/ActionFailureException.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/GTaskASyncTask.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/GTaskClient.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/MetaData.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/NetworkFailureException.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/Node.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/SqlData.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/SqlNote.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/Task.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/蔡玉祥注释的代码/TaskList.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/BackupUtils.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/DataUtils.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/GTaskStringUtils.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/NoteWidgetProvider.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/NoteWidgetProvider_2x.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/NoteWidgetProvider_4x.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/NotesPreferenceActivity.java" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/doc/邹兴云注释的代码/ResourceParser.java" beforeDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[C:\Users\庞浩的电脑\.android\avd\Pixel_2_Edited_API_30.avd]" />
|
|
||||||
<component name="ExternalProjectsData">
|
|
||||||
<projectState path="$PROJECT_DIR$">
|
|
||||||
<ProjectState />
|
|
||||||
</projectState>
|
|
||||||
</component>
|
|
||||||
<component name="Git.Settings">
|
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
||||||
</component>
|
|
||||||
<component name="MarkdownSettingsMigration">
|
|
||||||
<option name="stateVersion" value="1" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectId" id="2O5GlHnjCn5qBz61G2RsTAQaajO" />
|
|
||||||
<component name="ProjectLevelVcsManager">
|
|
||||||
<ConfirmationsSetting value="2" id="Add" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
|
||||||
"keyToString": {
|
|
||||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
|
||||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
|
||||||
"cidr.known.project.marker": "true",
|
|
||||||
"last_opened_file_path": "D:/softwareengineering/gitProject/src",
|
|
||||||
"project.structure.last.edited": "SDK",
|
|
||||||
"project.structure.proportion": "0.17",
|
|
||||||
"project.structure.side.proportion": "0.2",
|
|
||||||
"settings.editor.selected.configurable": "preferences.customizations"
|
|
||||||
}
|
|
||||||
}]]></component>
|
|
||||||
<component name="RunManager" selected="Android App.app">
|
|
||||||
<configuration name="app" type="AndroidRunConfigurationType" factoryName="Android App">
|
|
||||||
<module name="minote.app.main" />
|
|
||||||
<option name="DEPLOY" value="true" />
|
|
||||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
|
||||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
|
||||||
<option name="ARTIFACT_NAME" value="" />
|
|
||||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
|
||||||
<option name="ALL_USERS" value="false" />
|
|
||||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
|
||||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
|
||||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
|
||||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
|
||||||
<option name="MODE" value="default_activity" />
|
|
||||||
<option name="CLEAR_LOGCAT" value="false" />
|
|
||||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
|
||||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
|
||||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
|
||||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
|
||||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
|
||||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
|
||||||
<Auto>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
</Auto>
|
|
||||||
<Hybrid>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
</Hybrid>
|
|
||||||
<Java />
|
|
||||||
<Native>
|
|
||||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
|
||||||
<option name="SHOW_STATIC_VARS" value="true" />
|
|
||||||
<option name="WORKING_DIR" value="" />
|
|
||||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
|
||||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
|
||||||
</Native>
|
|
||||||
<Profilers>
|
|
||||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
|
||||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
|
||||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
|
||||||
</Profilers>
|
|
||||||
<option name="DEEP_LINK" value="" />
|
|
||||||
<option name="ACTIVITY_CLASS" value="" />
|
|
||||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
|
||||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration name="D:/softwareengineering/minote/app/build.gradle" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$/app/build.gradle" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<recent_temporary>
|
|
||||||
<list>
|
|
||||||
<item itemvalue="Gradle.D:/softwareengineering/minote/app/build.gradle" />
|
|
||||||
</list>
|
|
||||||
</recent_temporary>
|
|
||||||
</component>
|
|
||||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
|
|
||||||
<component name="TaskManager">
|
|
||||||
<task active="true" id="Default" summary="默认任务">
|
|
||||||
<changelist id="2531eea7-ec98-43ef-9f7f-09fe1004db56" name="变更" comment="" />
|
|
||||||
<created>1680843179165</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1680843179165</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00001" summary="更新">
|
|
||||||
<created>1680848243607</created>
|
|
||||||
<option name="number" value="00001" />
|
|
||||||
<option name="presentableId" value="LOCAL-00001" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1680848243608</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00002" summary="注释">
|
|
||||||
<created>1680850445899</created>
|
|
||||||
<option name="number" value="00002" />
|
|
||||||
<option name="presentableId" value="LOCAL-00002" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1680850445899</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00003" summary="注释">
|
|
||||||
<created>1680942675194</created>
|
|
||||||
<option name="number" value="00003" />
|
|
||||||
<option name="presentableId" value="LOCAL-00003" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1680942675194</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00004" summary="庞浩的注释">
|
|
||||||
<created>1680942721752</created>
|
|
||||||
<option name="number" value="00004" />
|
|
||||||
<option name="presentableId" value="LOCAL-00004" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1680942721752</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00005" summary="庞浩的注释">
|
|
||||||
<created>1681292428634</created>
|
|
||||||
<option name="number" value="00005" />
|
|
||||||
<option name="presentableId" value="LOCAL-00005" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1681292428635</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00006" summary="庞浩的注释">
|
|
||||||
<created>1681312464473</created>
|
|
||||||
<option name="number" value="00006" />
|
|
||||||
<option name="presentableId" value="LOCAL-00006" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1681312464473</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00007" summary="庞浩的注释">
|
|
||||||
<created>1681747296097</created>
|
|
||||||
<option name="number" value="00007" />
|
|
||||||
<option name="presentableId" value="LOCAL-00007" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1681747296097</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00008" summary="庞浩的注释">
|
|
||||||
<created>1681747316246</created>
|
|
||||||
<option name="number" value="00008" />
|
|
||||||
<option name="presentableId" value="LOCAL-00008" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1681747316246</updated>
|
|
||||||
</task>
|
|
||||||
<option name="localTasksCounter" value="9" />
|
|
||||||
<servers />
|
|
||||||
</component>
|
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
|
||||||
<option name="TAB_STATES">
|
|
||||||
<map>
|
|
||||||
<entry key="0cba35e6-7d90-42f0-b34c-310376a71413">
|
|
||||||
<value>
|
|
||||||
<State>
|
|
||||||
<option name="SHOW_ONLY_AFFECTED_CHANGES" value="true" />
|
|
||||||
<option name="FILTERS">
|
|
||||||
<map>
|
|
||||||
<entry key="branch">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="HEAD" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="roots">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</State>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="MAIN">
|
|
||||||
<value>
|
|
||||||
<State>
|
|
||||||
<option name="FILTERS">
|
|
||||||
<map>
|
|
||||||
<entry key="branch">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="master" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</State>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
<option name="OPEN_GENERIC_TABS">
|
|
||||||
<map>
|
|
||||||
<entry key="0cba35e6-7d90-42f0-b34c-310376a71413" value="TOOL_WINDOW" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="VcsManagerConfiguration">
|
|
||||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
|
||||||
<MESSAGE value="更新" />
|
|
||||||
<MESSAGE value="注释" />
|
|
||||||
<MESSAGE value="庞浩的注释" />
|
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="庞浩的注释" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@ -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,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,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,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.micode.notes.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CursorAdapter;
|
||||||
|
|
||||||
|
import net.micode.notes.data.Notes;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
|
public class NotesListAdapter extends CursorAdapter {
|
||||||
|
private static final String TAG = "NotesListAdapter";
|
||||||
|
private Context mContext;
|
||||||
|
private HashMap<Integer, Boolean> mSelectedIndex;
|
||||||
|
private int mNotesCount;
|
||||||
|
private boolean mChoiceMode;
|
||||||
|
|
||||||
|
public static class AppWidgetAttribute {
|
||||||
|
public int widgetId;
|
||||||
|
public int widgetType;
|
||||||
|
};
|
||||||
|
|
||||||
|
public NotesListAdapter(Context context) {
|
||||||
|
super(context, null);
|
||||||
|
mSelectedIndex = new HashMap<Integer, Boolean>();
|
||||||
|
mContext = context;
|
||||||
|
mNotesCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return new NotesListItem(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
if (view instanceof NotesListItem) {
|
||||||
|
NoteItemData itemData = new NoteItemData(context, cursor);
|
||||||
|
((NotesListItem) view).bind(context, itemData, mChoiceMode,
|
||||||
|
isSelectedItem(cursor.getPosition()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckedItem(final int position, final boolean checked) {
|
||||||
|
mSelectedIndex.put(position, checked);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInChoiceMode() {
|
||||||
|
return mChoiceMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChoiceMode(boolean mode) {
|
||||||
|
mSelectedIndex.clear();
|
||||||
|
mChoiceMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectAll(boolean checked) {
|
||||||
|
Cursor cursor = getCursor();
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
if (cursor.moveToPosition(i)) {
|
||||||
|
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
|
||||||
|
setCheckedItem(i, checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashSet<Long> getSelectedItemIds() {
|
||||||
|
HashSet<Long> itemSet = new HashSet<Long>();
|
||||||
|
for (Integer position : mSelectedIndex.keySet()) {
|
||||||
|
if (mSelectedIndex.get(position) == true) {
|
||||||
|
Long id = getItemId(position);
|
||||||
|
if (id == Notes.ID_ROOT_FOLDER) {
|
||||||
|
Log.d(TAG, "Wrong item id, should not happen");
|
||||||
|
} else {
|
||||||
|
itemSet.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashSet<AppWidgetAttribute> getSelectedWidget() {
|
||||||
|
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
|
||||||
|
for (Integer position : mSelectedIndex.keySet()) {
|
||||||
|
if (mSelectedIndex.get(position) == true) {
|
||||||
|
Cursor c = (Cursor) getItem(position);
|
||||||
|
if (c != null) {
|
||||||
|
AppWidgetAttribute widget = new AppWidgetAttribute();
|
||||||
|
NoteItemData item = new NoteItemData(mContext, c);
|
||||||
|
widget.widgetId = item.getWidgetId();
|
||||||
|
widget.widgetType = item.getWidgetType();
|
||||||
|
itemSet.add(widget);
|
||||||
|
/**
|
||||||
|
* Don't close cursor here, only the adapter could close it
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Invalid cursor");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return itemSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSelectedCount() {
|
||||||
|
Collection<Boolean> values = mSelectedIndex.values();
|
||||||
|
if (null == values) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Iterator<Boolean> iter = values.iterator();
|
||||||
|
int count = 0;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
if (true == iter.next()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllSelected() {
|
||||||
|
int checkedCount = getSelectedCount();
|
||||||
|
return (checkedCount != 0 && checkedCount == mNotesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelectedItem(final int position) {
|
||||||
|
if (null == mSelectedIndex.get(position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mSelectedIndex.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onContentChanged() {
|
||||||
|
super.onContentChanged();
|
||||||
|
calcNotesCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeCursor(Cursor cursor) {
|
||||||
|
super.changeCursor(cursor);
|
||||||
|
calcNotesCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calcNotesCount() {
|
||||||
|
mNotesCount = 0;
|
||||||
|
for (int i = 0; i < getCount(); i++) {
|
||||||
|
Cursor c = (Cursor) getItem(i);
|
||||||
|
if (c != null) {
|
||||||
|
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
|
||||||
|
mNotesCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Invalid cursor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int retCount() {
|
||||||
|
int NotesCount = mNotesCount;
|
||||||
|
int ItemCount = getCount();
|
||||||
|
for (int i = 0; i < ItemCount; i++) {
|
||||||
|
Cursor c = (Cursor) getItem(i);
|
||||||
|
if (c != null) {
|
||||||
|
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
|
||||||
|
NoteItemData NoteItem = new NoteItemData(mContext, c);
|
||||||
|
NotesCount += NoteItem.getNotesCount();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Invalid cursor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NotesCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 554 KiB After Width: | Height: | Size: 554 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |