├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── ic_launcher-web.png
├── libs
├── android-support-v4.jar
├── gson-2.3.jar
├── okhttp-2.0.0.jar
├── okhttp-urlconnection-2.0.0.jar
├── okio-1.0.1.jar
└── retrofit-1.7.0.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ ├── ic_action_refresh.png
│ └── ic_launcher.png
├── drawable-mdpi
│ ├── ic_action_new.png
│ ├── ic_action_refresh.png
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── ic_action_refresh.png
│ └── ic_launcher.png
├── drawable-xxhdpi
│ ├── ic_action_refresh.png
│ └── ic_launcher.png
├── layout
│ ├── add_task.xml
│ ├── main.xml
│ └── task_list_row.xml
├── menu
│ └── main.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
├── values
│ ├── strings.xml
│ └── styles.xml
└── xml
│ └── syncadapter.xml
└── src
└── com
└── rogansoft
└── tasksdemo
├── AddTaskActivity.java
├── MainActivity.java
├── PrefsUtil.java
├── TaskArrayAdapter.java
├── Util.java
├── api
├── GoogleTaskApi.java
├── GoogleTaskApiService.java
└── TaskApi.java
├── db
└── TaskDb.java
├── domain
├── Task.java
└── TaskList.java
└── sync
├── TaskSyncAdapter.java
├── TaskSyncAdapterService.java
├── TaskSyncContentProvider.java
├── TaskSyncLocalDatastore.java
└── TaskSyncRemoteDatastore.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 |
19 | # Local configuration file (sdk path, etc)
20 | local.properties
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Log Files
26 | *.log
27 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | SyncManagerDemoGoogleTasks
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
12 |
14 |
16 |
18 |
19 |
24 |
25 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
48 |
49 |
50 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 sschendel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SyncManagerAndroid-DemoGoogleTasks
2 | ==================================
3 |
4 | Android Google Task app to demonstrate use of [SyncManagerAndroid][repo-syncman] library.
5 |
6 | Overview
7 | --------------------
8 | Simple [Google Tasks API][gtasks] integrated app that demostrates synchronization of Google Task data between local SQLite database and remote RESTful Google Tasks API.
9 |
10 |
11 | Some key Android concepts demonstrated
12 | --------------------
13 | * Android sync framework integration - with sync implementation provided by [SyncManagerAndroid][repo-syncman]
14 | * SQLite database
15 | * Retrofit REST client integration
16 | * Custom ArrayAdapter
17 |
18 | Libraries used
19 | --------------------
20 | * [SyncManagerAndroid][repo-syncman] - 2-way sync library
21 | * [Retrofit][repo-retrofit] - REST client from [Square][gh-square]
22 |
23 | [repo-syncman]: https://github.com/sschendel/SyncManagerAndroid
24 | [repo-retrofit]: http://square.github.io/retrofit/
25 | [gh-square]: http://square.github.io/
26 | [gtasks]: https://developers.google.com/google-apps/tasks/
27 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/gson-2.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/gson-2.3.jar
--------------------------------------------------------------------------------
/libs/okhttp-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/okhttp-2.0.0.jar
--------------------------------------------------------------------------------
/libs/okhttp-urlconnection-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/okhttp-urlconnection-2.0.0.jar
--------------------------------------------------------------------------------
/libs/okio-1.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/okio-1.0.1.jar
--------------------------------------------------------------------------------
/libs/retrofit-1.7.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/libs/retrofit-1.7.0.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 | -keep class * extends java.util.ListResourceBundle {
22 | protected Object[][] getContents();
23 | }
24 |
25 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
26 | public static final *** NULL;
27 | }
28 |
29 | -keepnames @com.google.android.gms.common.annotation.KeepName class *
30 | -keepclassmembernames class * {
31 | @com.google.android.gms.common.annotation.KeepName *;
32 | }
33 |
34 | -keepnames class * implements android.os.Parcelable {
35 | public static final ** CREATOR;
36 | }
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library.reference.1=..\\..\\..\\..\\Source\\google-play-services_lib
16 | android.library.reference.2=../../../../Source/SyncManager
17 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-hdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_action_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-mdpi/ic_action_new.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-mdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-xhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-xxhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sschendel/SyncManagerAndroid-DemoGoogleTasks/d59e894e3d520d17c6dfdc073fbf2e194b4498fb/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/add_task.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
15 |
18 |
24 |
28 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
18 |
19 |
20 |
29 |
34 |
42 |
43 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/res/layout/task_list_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
24 |
--------------------------------------------------------------------------------
/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SyncManagerDemoGoogleTasks
4 | Sync
5 | Task title
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/res/xml/syncadapter.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/AddTaskActivity.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo;
2 |
3 | import com.rogansoft.syncmanagerexample.R;
4 |
5 | import android.app.Activity;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.widget.Button;
11 | import android.widget.EditText;
12 |
13 | public class AddTaskActivity extends Activity {
14 | //private static final String TAG = "AddTaskActivity";
15 |
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.add_task);
20 |
21 | Button saveButton = (Button) findViewById(R.id.add_task_save);
22 | saveButton.setOnClickListener(new View.OnClickListener() {
23 | public void onClick(View view) {
24 | Bundle bundle = buildTaskBundle();
25 | if (bundle != null) {
26 | Intent mIntent = new Intent();
27 | mIntent.putExtras(bundle);
28 | setResult(RESULT_OK, mIntent);
29 | finish();
30 | }
31 | }
32 | });
33 |
34 | }
35 |
36 | @Override
37 | public boolean onOptionsItemSelected(
38 | MenuItem item) {
39 | boolean result;
40 | switch (item.getItemId()) {
41 | case android.R.id.home:
42 | finish();
43 | result = true;
44 | break;
45 | default:
46 | result = super.onOptionsItemSelected(item);
47 | }
48 | return result;
49 | }
50 |
51 | private Bundle buildTaskBundle() {
52 | Bundle bundle = null;
53 | EditText titleEditText = (EditText) findViewById(R.id.add_task_title);
54 | String title = titleEditText.getText().toString();
55 |
56 | if ( (title.length() > 0) ) {
57 |
58 |
59 | bundle = new Bundle();
60 | bundle.putString("title", title);
61 | }
62 |
63 |
64 | return bundle;
65 |
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo;
2 | import java.io.IOException;
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.accounts.Account;
7 | import android.accounts.AccountManager;
8 | import android.app.Activity;
9 | import android.content.ContentResolver;
10 | import android.content.Intent;
11 | import android.database.ContentObserver;
12 | import android.os.AsyncTask;
13 | import android.os.Bundle;
14 | import android.os.Handler;
15 | import android.util.Log;
16 | import android.view.Menu;
17 | import android.view.MenuItem;
18 | import android.view.View;
19 | import android.widget.ListView;
20 | import android.widget.Toast;
21 |
22 | import com.google.android.gms.auth.GoogleAuthException;
23 | import com.google.android.gms.auth.GoogleAuthUtil;
24 | import com.google.android.gms.auth.UserRecoverableAuthException;
25 | import com.google.android.gms.common.AccountPicker;
26 | import com.google.android.gms.common.GooglePlayServicesUtil;
27 | import com.rogansoft.syncmanagerexample.R;
28 | import com.rogansoft.tasksdemo.api.GoogleTaskApi;
29 | import com.rogansoft.tasksdemo.api.GoogleTaskApiService;
30 | import com.rogansoft.tasksdemo.api.TaskApi;
31 | import com.rogansoft.tasksdemo.db.TaskDb;
32 | import com.rogansoft.tasksdemo.domain.Task;
33 | import com.rogansoft.tasksdemo.domain.TaskList;
34 | import com.rogansoft.tasksdemo.sync.TaskSyncContentProvider;
35 |
36 |
37 | public class MainActivity extends Activity {
38 | private static final String TAG = "MainActivity";
39 |
40 | static final int REQUEST_CODE_PICK_ACCOUNT = 1000;
41 | static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 2000;
42 | static final int ACTIVITY_ADD_TASK = 3000;
43 |
44 | private String mGoogleAccountName;
45 | private ListView mList;
46 | private ArrayList mTasks;
47 |
48 | private TaskDb mDb;
49 |
50 | private Handler handler;
51 | private TaskContentObserver mTaskContentObserver;
52 |
53 | class TaskContentObserver extends ContentObserver {
54 |
55 | public TaskContentObserver(Handler handler) {
56 | super(handler);
57 | }
58 |
59 | @Override
60 | public void onChange(boolean selfChange) {
61 | super.onChange(selfChange);
62 | Log.d(TAG, "Task content changed!");
63 | loadModel();
64 | updateUI();
65 | }
66 |
67 | }
68 |
69 |
70 | // Set up content observer for our content provider
71 | private void registerContentObservers() {
72 | ContentResolver cr = getContentResolver();
73 | handler = new Handler();
74 | mTaskContentObserver = new TaskContentObserver(handler);
75 | cr.registerContentObserver(TaskSyncContentProvider.CONTENT_URI, true,
76 | mTaskContentObserver);
77 | }
78 |
79 | private void unregisterContentObservers() {
80 | ContentResolver cr = getContentResolver();
81 | if (mTaskContentObserver != null) { // just paranoia
82 | cr.unregisterContentObserver(mTaskContentObserver);
83 | mTaskContentObserver = null;
84 | handler = null;
85 | }
86 | }
87 |
88 | @Override
89 | protected void onCreate(Bundle savedInstanceState) {
90 | super.onCreate(savedInstanceState);
91 | setContentView(R.layout.main);
92 |
93 | mDb = new TaskDb(this);
94 | mDb.open();
95 |
96 | mList = (ListView) findViewById(R.id.task_list);
97 |
98 | final View relLayoutButton = (View) findViewById(R.id.button);
99 | relLayoutButton.setOnClickListener(new View.OnClickListener() {
100 |
101 | @Override
102 | public void onClick(View v) {
103 | //Log.d(TAG, "TBD: Add task view...");
104 | launchActivityAddTask();
105 | }
106 | });
107 |
108 | }
109 |
110 | @Override
111 | protected void onDestroy() {
112 | mDb.close();
113 | super.onDestroy();
114 | }
115 |
116 | @Override
117 | protected void onStart() {
118 | super.onStart();
119 | registerContentObservers();
120 | }
121 |
122 | @Override
123 | protected void onStop() {
124 | unregisterContentObservers();
125 | super.onStop();
126 | }
127 |
128 | @Override
129 | protected void onResume() {
130 | super.onResume();
131 | int conResult = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
132 | Log.d(TAG, "GooglePlayServices available? "+ conResult);
133 |
134 | mGoogleAccountName = PrefsUtil.retrieveGoogleTasksUser(this);
135 | if (mGoogleAccountName == null) {
136 | pickUserAccount();
137 | }
138 |
139 | loadModel();
140 | updateUI();
141 | }
142 |
143 |
144 | @Override
145 | public boolean onCreateOptionsMenu(Menu menu) {
146 | getMenuInflater().inflate(R.menu.main, menu);
147 | return super.onCreateOptionsMenu(menu);
148 | }
149 |
150 | @Override
151 | public boolean onOptionsItemSelected(MenuItem item) {
152 | switch (item.getItemId()) {
153 | case R.id.action_sync:
154 | initiateSync();
155 | return true;
156 | default:
157 | return super.onOptionsItemSelected(item);
158 | }
159 | }
160 |
161 | private void initiateSync() {
162 | Log.d(TAG, "initiateSync... google account:"+ mGoogleAccountName);
163 | Account account = getGoogleAccount(mGoogleAccountName);
164 | if (account != null) {
165 | Log.d(TAG, "Found account! init sync...");
166 | // Pass the settings flags by inserting them in a bundle
167 | Bundle settingsBundle = new Bundle();
168 | settingsBundle.putBoolean(
169 | ContentResolver.SYNC_EXTRAS_MANUAL, true);
170 | settingsBundle.putBoolean(
171 | ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
172 | /*
173 | * Request the sync for the default account, authority, and
174 | * manual sync settings
175 | */
176 | ContentResolver.requestSync(account, TaskSyncContentProvider.AUTHORITY, settingsBundle);
177 |
178 | }
179 | }
180 |
181 | private void loadModel() {
182 | mTasks = mDb.fetchNonDeletedTasks();
183 | for(Task t : mTasks) {
184 | Log.d(TAG,t.toString());
185 | }
186 | }
187 |
188 |
189 | private void updateUI() {
190 | TaskArrayAdapter adapter = new TaskArrayAdapter(this, R.layout.task_list_row, mTasks, mDb);
191 | mList.setAdapter(adapter);
192 | }
193 |
194 | private void launchActivityAddTask() {
195 | Intent i = new Intent(this, AddTaskActivity.class);
196 | startActivityForResult(i, ACTIVITY_ADD_TASK);
197 | }
198 |
199 | private Account getGoogleAccount(String userName) {
200 | Account result = null;
201 | AccountManager manager = AccountManager.get(this);
202 | Account[] accounts = manager.getAccountsByType("com.google");
203 | for(Account account : accounts) {
204 | Log.d(TAG, "check account:"+account.name);
205 | if (account.name.equals(userName)) {
206 | Log.d(TAG, "Found! Setting syncable");
207 | result = account;
208 | break;
209 | }
210 | }
211 | return result;
212 | }
213 |
214 | private void setGoogleAccountSync(String userName) {
215 | Log.d(TAG, "setGoogleAccountSync:"+userName);
216 | Account account = getGoogleAccount(userName);
217 | if (account != null) {
218 | Log.d(TAG, "Found! Setting syncable");
219 | ContentResolver.setIsSyncable(account, "com.rogansoft.syncmanagerexample.sync.examplesyncadapterservice", 1);
220 | }
221 | }
222 |
223 |
224 | protected void pickUserAccount() {
225 | String[] accountTypes = new String[]{"com.google"};
226 | Intent intent = AccountPicker.newChooseAccountIntent(null, null,
227 | accountTypes, false, null, null, null, null);
228 | startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT);
229 | }
230 |
231 | @Override
232 | protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
233 | if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
234 | // Receiving a result from the AccountPicker
235 | if (resultCode == RESULT_OK) {
236 | mGoogleAccountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
237 | PrefsUtil.saveGoogleTasksUser(this, mGoogleAccountName);
238 | setGoogleAccountSync(mGoogleAccountName);
239 | // With the account name acquired, go get the auth token
240 | initiateGoogleAuthTokenRequest();
241 | } else if (resultCode == RESULT_CANCELED) {
242 | // The account picker dialog closed without selecting an account.
243 | // Notify users that they must pick an account to proceed.
244 | Toast.makeText(this, "You must choose a Google account", Toast.LENGTH_SHORT).show();
245 | }
246 | } else if (requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) {
247 | if (resultCode == RESULT_OK) {
248 | initiateGoogleAuthTokenRequest();
249 | }
250 | } else if (requestCode == ACTIVITY_ADD_TASK) {
251 | if (intent != null) { // may be null if user backed up
252 | Bundle extras = intent.getExtras();
253 | String title = extras.getString("title");
254 |
255 | Task newTask = new Task();
256 | newTask.setTitle(title);
257 | newTask.setId(mDb.createTask(newTask));
258 | loadModel();
259 | updateUI();
260 | }
261 |
262 | }
263 | }
264 |
265 | private void initiateGoogleAuthTokenRequest() {
266 | if (mGoogleAccountName == null) {
267 | pickUserAccount();
268 | } else {
269 | //if (isDeviceOnline()) {
270 | new GetGoogleAuthTokenTask(mGoogleAccountName, GoogleTaskApiService.SCOPE).execute();
271 | //} else {
272 | // Toast.makeText(this, R.string.not_online, Toast.LENGTH_LONG).show();
273 | // }
274 | }
275 | }
276 |
277 | protected void handleException(UserRecoverableAuthException e) {
278 | // Unable to authenticate, such as when the user has not yet granted
279 | // the app access to the account, but the user can fix this.
280 | // Forward the user to an activity in Google Play services.
281 | Intent intent = e.getIntent();
282 | startActivityForResult(intent,
283 | REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR);
284 | }
285 |
286 | public class GetGoogleTaskListTasksTask extends AsyncTask>{
287 | private TaskApi mApi;
288 | private String mAuthToken;
289 |
290 | public GetGoogleTaskListTasksTask(String authToken) {
291 | mApi = new GoogleTaskApi(authToken);
292 | mAuthToken = authToken;
293 | }
294 |
295 | @Override
296 | protected List doInBackground(Void... params) {
297 | return mApi.get();
298 | }
299 |
300 | @Override
301 | protected void onPostExecute(List result) {
302 | Log.d(TAG, "done");
303 |
304 | for(Task t : result){
305 | Log.d(TAG, t.toString());
306 |
307 | if(t.getRemoteId().equals("MDc1OTAzMTA4NTg1NDI2MDc5ODI6MDoxMjU0MDgwNDQ5")) {
308 | Log.d(TAG, "Updating this task...");
309 | t.setTitle("Updated task title through API");
310 | new UpdateGoogleTaskTask(mAuthToken, t).execute();
311 | }
312 |
313 | }
314 | }
315 | }
316 |
317 | public class GetGoogleTaskListTask extends AsyncTask>{
318 | private TaskApi mApi;
319 |
320 | public GetGoogleTaskListTask(String authToken) {
321 | mApi = new GoogleTaskApi(authToken);
322 | }
323 |
324 | @Override
325 | protected List doInBackground(Void... params) {
326 | return mApi.getTaskList();
327 | }
328 |
329 | @Override
330 | protected void onPostExecute(List result) {
331 | Log.d(TAG, "done");
332 |
333 | for(TaskList tl : result){
334 | Log.d(TAG, tl.toString());
335 | }
336 | }
337 | }
338 |
339 |
340 | public class AddGoogleTaskTask extends AsyncTask{
341 | private TaskApi mApi;
342 | private Task mTask;
343 |
344 | public AddGoogleTaskTask(String authToken, Task task) {
345 | mApi = new GoogleTaskApi(authToken);
346 | mTask = task;
347 | }
348 |
349 | @Override
350 | protected Task doInBackground(Void... params) {
351 | return mApi.post(mTask);
352 | }
353 |
354 | @Override
355 | protected void onPostExecute(Task result) {
356 | Log.d(TAG, "AddGoogleTaskTask done");
357 |
358 | Log.d(TAG, result.toString());
359 |
360 | }
361 | }
362 |
363 |
364 | public class UpdateGoogleTaskTask extends AsyncTask{
365 | private TaskApi mApi;
366 | private Task mTask;
367 |
368 | public UpdateGoogleTaskTask(String authToken, Task task) {
369 | mApi = new GoogleTaskApi(authToken);
370 | mTask = task;
371 | }
372 |
373 | @Override
374 | protected Task doInBackground(Void... params) {
375 | return mApi.put(mTask);
376 | }
377 |
378 | @Override
379 | protected void onPostExecute(Task result) {
380 | Log.d(TAG, "UpdateGoogleTaskTask done");
381 |
382 | Log.d(TAG, result.toString());
383 | }
384 | }
385 |
386 |
387 | public class GetGoogleAuthTokenTask extends AsyncTask{
388 | String mScope;
389 | String mEmailText;
390 | String mTokenText;
391 |
392 | UserRecoverableAuthException mException;
393 |
394 | GetGoogleAuthTokenTask(String name, String scope) {
395 | this.mScope = scope;
396 | this.mEmailText = name;
397 | }
398 |
399 | /**
400 | * Executes the asynchronous job. This runs when you call execute()
401 | * on the AsyncTask instance.
402 | */
403 | @Override
404 | protected String doInBackground(Void... params) {
405 | try {
406 | String token = fetchToken();
407 | if (token != null) {
408 | // Insert the good stuff here.
409 | // Use the token to access the user's Google data.
410 | mTokenText = token;
411 | }
412 | } catch (IOException e) {
413 | // The fetchToken() method handles Google-specific exceptions,
414 | // so this indicates something went wrong at a higher level.
415 | // TIP: Check for network connectivity before starting the AsyncTask.
416 | Log.e(TAG, e.getMessage(), e);
417 | }
418 | return null;
419 | }
420 |
421 | @Override
422 | protected void onPostExecute(String result) {
423 | if (mException != null ) {
424 | MainActivity.this.handleException(mException);
425 | }
426 | else {
427 | if (mTokenText != null){
428 | Log.d(TAG,"token:"+mTokenText);
429 | //new GetGoogleTaskListTask(mTokenText).execute();
430 | new GetGoogleTaskListTasksTask(mTokenText).execute();
431 |
432 | // Task task = new Task();
433 | // task.setTitle("New posted from API!");
434 | // new AddGoogleTaskTask(mTokenText, task).execute();
435 | }
436 | }
437 |
438 | }
439 |
440 |
441 | /**
442 | * Gets an authentication token from Google and handles any
443 | * GoogleAuthException that may occur.
444 | */
445 | protected String fetchToken() throws IOException {
446 | try {
447 | return GoogleAuthUtil.getToken(MainActivity.this, mEmailText, mScope);
448 | } catch (UserRecoverableAuthException userRecoverableException) {
449 | // GooglePlayServices.apk is either old, disabled, or not present
450 | // so we need to show the user some UI in the activity to recover.
451 | //mActivity.handleException(userRecoverableException);
452 | //Log.e(TAG, userRecoverableException.getMessage(), userRecoverableException);
453 |
454 | mException = userRecoverableException;
455 | } catch (GoogleAuthException fatalException) {
456 | // Some other type of unrecoverable exception has occurred.
457 | // Report and log the error as appropriate for your app.
458 | Log.e(TAG, fatalException.getMessage(), fatalException);
459 | }
460 | return null;
461 | }
462 | }
463 |
464 |
465 | }
466 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/PrefsUtil.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo;
2 |
3 | import android.content.Context;
4 |
5 | public class PrefsUtil {
6 | private static final String GOOGLE_TASKS_PREFERENCES = "GOOGLE_TASKS_PREFERENCES";
7 |
8 | private static final String KEY_GOOGLE_TASKS_USER = "KEY_GOOGLE_TASKS_USER";
9 |
10 | public static void saveGoogleTasksUser(Context context, String userName) {
11 | context.getSharedPreferences(GOOGLE_TASKS_PREFERENCES, Context.MODE_MULTI_PROCESS)
12 | .edit()
13 | .putString(KEY_GOOGLE_TASKS_USER, userName)
14 | .commit();
15 | }
16 |
17 | public static String retrieveGoogleTasksUser(Context context) {
18 | return context.getSharedPreferences(GOOGLE_TASKS_PREFERENCES, Context.MODE_MULTI_PROCESS)
19 | .getString(KEY_GOOGLE_TASKS_USER, null);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/TaskArrayAdapter.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 |
6 | import android.content.Context;
7 | import android.graphics.Paint;
8 | import android.util.Log;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.ArrayAdapter;
13 | import android.widget.CheckBox;
14 | import android.widget.TextView;
15 |
16 | import com.rogansoft.syncmanagerexample.R;
17 | import com.rogansoft.tasksdemo.db.TaskDb;
18 | import com.rogansoft.tasksdemo.domain.Task;
19 |
20 | public class TaskArrayAdapter extends ArrayAdapter {
21 | private static final String TAG = "TaskArrayAdapter";
22 |
23 | private ArrayList mTasks;
24 | private Context mContext;
25 | private TaskDb mDb;
26 |
27 | public TaskArrayAdapter(Context context, int textViewResourceId, ArrayList objects, TaskDb db) {
28 | super(context, textViewResourceId, objects);
29 |
30 | mTasks = objects;
31 | mContext = context;
32 | mDb = db;
33 |
34 | }
35 |
36 | private void updateViewGivenStatus(boolean isCompleted, TextView title, CheckBox status) {
37 | status.setChecked(isCompleted);
38 | if (isCompleted) {
39 | title.setPaintFlags(title.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
40 | } else {
41 | title.setPaintFlags(title.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
42 | }
43 | }
44 |
45 | @Override
46 | public View getView(int position, View convertView, ViewGroup parent) {
47 | View v = convertView;
48 | if (v == null) {
49 | LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
50 | v = vi.inflate(R.layout.task_list_row, null);
51 | }
52 | final Task task = mTasks.get(position);
53 | if (task != null) {
54 | final TextView title = (TextView) v.findViewById(R.id.task_title);
55 | title.setText(task.getTitle());
56 | final CheckBox status = (CheckBox) v.findViewById(R.id.task_status);
57 | boolean isCompleted = checkIsTaskCompleted(task);
58 | updateViewGivenStatus(isCompleted, title, status);
59 | status.setOnClickListener(new View.OnClickListener() {
60 | @Override
61 | public void onClick(View viewArg) {
62 | CheckBox cb = (CheckBox) viewArg;
63 | Log.d(TAG, "checked! "+task.getTitle());
64 | String statusTxt = "needsAction";
65 | if (cb.isChecked()) {
66 | statusTxt = "completed";
67 | }
68 | task.setStatus(statusTxt);
69 | //Calendar cal = Calendar.getInstance();
70 | Date date = new Date();
71 | long updatedSequence = date.getTime() / 1000;
72 |
73 |
74 | task.setLastUpdatedSequence(updatedSequence);
75 | Log.d(TAG, "last update: "+updatedSequence);
76 | mDb.updateTask(task);
77 | updateViewGivenStatus(cb.isChecked(), title, status);
78 | }
79 | });
80 | }
81 | return v;
82 | }
83 |
84 | private boolean checkIsTaskCompleted(Task task) {
85 | boolean result = false;
86 | String taskStatus = task.getStatus();
87 | if (taskStatus != null) {
88 | result = taskStatus.equalsIgnoreCase("completed");
89 | }
90 | return result;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/Util.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | public class Util {
7 | //private static final String TAG = "Util";
8 |
9 | public static Date parseRFC3339Date(String datestring)
10 | throws java.text.ParseException, IndexOutOfBoundsException
11 | {
12 | //Log.d(TAG, datestring);
13 | Date d = new Date();
14 |
15 | // if there is no time zone, we don't need to do any special parsing.
16 | if (datestring.endsWith("Z")) {
17 | // Replace Z with standard time zone UTC
18 | //http://stackoverflow.com/questions/2580925/simpledateformat-parsing-date-with-z-literal
19 | datestring = datestring.replaceAll("Z$", "+0000");
20 | //Log.d(TAG, datestring);
21 | try {
22 | SimpleDateFormat s = new SimpleDateFormat(
23 | "yyyy-MM-dd'T'HH:mm:ssZ");// spec for RFC3339
24 | d = s.parse(datestring);
25 | } catch (java.text.ParseException pe) {// try again with optional
26 | // decimals
27 | //Log.d(TAG, "first parse failed...");
28 | SimpleDateFormat s = new SimpleDateFormat(
29 | "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ");// spec for RFC3339
30 | // (with fractional
31 | // seconds)
32 | s.setLenient(true);
33 | d = s.parse(datestring);
34 | //Log.d(TAG, "date: "+d.toString());
35 | //Log.d(TAG, "value: "+(d.getTime()/1000));
36 | }
37 | return d;
38 | }
39 |
40 | // step one, split off the timezone.
41 | String firstpart = datestring.substring(0, datestring.lastIndexOf('-'));
42 | String secondpart = datestring.substring(datestring.lastIndexOf('-'));
43 |
44 | // step two, remove the colon from the timezone offset
45 | secondpart = secondpart.substring(0, secondpart.indexOf(':'))
46 | + secondpart.substring(secondpart.indexOf(':') + 1);
47 | datestring = firstpart + secondpart;
48 | SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");// spec
49 | // for
50 | // RFC3339
51 | try {
52 | d = s.parse(datestring);
53 | } catch (java.text.ParseException pe) {// try again with optional
54 | // decimals
55 | s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ");// spec
56 | // for
57 | // RFC3339
58 | // (with
59 | // fractional
60 | // seconds)
61 | s.setLenient(true);
62 | d = s.parse(datestring);
63 | }
64 | return d;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/api/GoogleTaskApi.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.api;
2 |
3 | import java.io.IOException;
4 | import java.lang.reflect.Type;
5 | import java.text.ParseException;
6 | import java.util.Date;
7 | import java.util.List;
8 |
9 | import retrofit.RequestInterceptor;
10 | import retrofit.RestAdapter;
11 | import retrofit.converter.GsonConverter;
12 |
13 | import com.google.gson.FieldNamingPolicy;
14 | import com.google.gson.Gson;
15 | import com.google.gson.GsonBuilder;
16 | import com.google.gson.JsonDeserializationContext;
17 | import com.google.gson.JsonDeserializer;
18 | import com.google.gson.JsonElement;
19 | import com.google.gson.JsonParseException;
20 | import com.google.gson.TypeAdapter;
21 | import com.google.gson.stream.JsonReader;
22 | import com.google.gson.stream.JsonWriter;
23 | import com.rogansoft.tasksdemo.Util;
24 | import com.rogansoft.tasksdemo.domain.Task;
25 | import com.rogansoft.tasksdemo.domain.TaskList;
26 |
27 | public class GoogleTaskApi implements TaskApi {
28 | //private static final String TAG = "TaskApi";
29 |
30 | private RestAdapter mRestAdapter;
31 | private GoogleTaskApiService mService;
32 |
33 | class TaskTypeAdapter extends TypeAdapter {
34 |
35 | private long parseRFC3339Date(String dateStr) {
36 | long result = 0;
37 | try {
38 | Date date = Util.parseRFC3339Date(dateStr);
39 | result = date.getTime() / 1000;
40 | } catch (IndexOutOfBoundsException e) {
41 | // TODO Auto-generated catch block
42 | e.printStackTrace();
43 | } catch (ParseException e) {
44 | // TODO Auto-generated catch block
45 | e.printStackTrace();
46 | }
47 | return result;
48 | }
49 |
50 | @Override
51 | public Task read(JsonReader in) throws IOException {
52 | final Task task = new Task();
53 |
54 | in.beginObject();
55 |
56 | while(in.hasNext()) {
57 | //Log.d(TAG, "json peek: "+in.peek().toString());
58 | String name = in.nextName();
59 | //Log.d(TAG, "json name: "+name);
60 | if (name.equals("id")){
61 | task.setServerId(in.nextString());
62 | } else if (name.equals("title")) {
63 | task.setTitle(in.nextString());
64 | } else if (name.equals("updated")) {
65 | task.setUpdated(parseRFC3339Date(in.nextString()));
66 | } else if (name.equals("status")) {
67 | task.setStatus(in.nextString());
68 | } else if (name.equals("position")) {
69 | task.setPosition(in.nextString());
70 | } else if (name.equals("deleted")) {
71 | //Log.d(TAG, "json deleted peek: "+in.peek().toString());
72 | task.setDeleted(in.nextBoolean());
73 | //Log.d(TAG, "task deleted:"+task.isDeleted());
74 | } else {
75 | in.skipValue();
76 | }
77 | }
78 |
79 | in.endObject();
80 | return task;
81 | }
82 |
83 | @Override
84 | public void write(JsonWriter out, Task task) throws IOException {
85 | out.beginObject();
86 | if (task.getServerId() != null) {
87 | out.name("id").value(task.getServerId());
88 | }
89 | out.name("title").value(task.getTitle());
90 | out.name("status").value(task.getStatus());
91 | out.endObject();
92 | }
93 |
94 | }
95 |
96 |
97 | class TaskListDeserializer implements JsonDeserializer{
98 |
99 | @Override
100 | public T deserialize(JsonElement je, Type type,
101 | JsonDeserializationContext jdc) throws JsonParseException {
102 | // Get the "content" element from the parsed JSON
103 | JsonElement items = je.getAsJsonObject().get("items");
104 |
105 | Gson gson = new GsonBuilder()
106 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
107 | .registerTypeAdapter(Task.class, new TaskTypeAdapter())
108 | .create();
109 |
110 | // Deserialize it. You use a new instance of Gson to avoid infinite recursion
111 | // to this deserializer
112 | return gson.fromJson(items, type);
113 |
114 | }
115 | }
116 |
117 |
118 | public GoogleTaskApi(String authToken) {
119 | final String token = authToken;
120 |
121 | RequestInterceptor requestInterceptor = new RequestInterceptor() {
122 | @Override
123 | public void intercept(RequestFacade request) {
124 | request.addHeader("Authorization", "Bearer "+token);
125 | }
126 | };
127 |
128 | Gson gson =
129 | new GsonBuilder()
130 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
131 | .registerTypeAdapter(List.class, new TaskListDeserializer>())
132 | .registerTypeAdapter(List.class, new TaskListDeserializer>())
133 | .registerTypeAdapter(Task.class, new TaskTypeAdapter())
134 | .create();
135 |
136 | mRestAdapter = new RestAdapter.Builder()
137 | .setRequestInterceptor(requestInterceptor)
138 | .setEndpoint(GoogleTaskApiService.API_BASE_URL)
139 | .setConverter(new GsonConverter(gson))
140 | .build();
141 |
142 | //Log.d(TAG, "retrofit log level:"+mRestAdapter.getLogLevel());
143 | //mRestAdapter.setLogLevel(LogLevel.FULL);
144 | //Log.d(TAG, "retrofit log level after setting full:"+mRestAdapter.getLogLevel());
145 |
146 | mService = mRestAdapter.create(GoogleTaskApiService.class);
147 | }
148 |
149 | public List getTaskList(){
150 | return mService.getTaskLists();
151 | }
152 |
153 | @Override
154 | public Task get(String remoteId) {
155 | return mService.get(GoogleTaskApiService.GOOGLE_TASK_LIST_ID, remoteId);
156 | }
157 |
158 | @Override
159 | public List get() {
160 | return mService.get(GoogleTaskApiService.GOOGLE_TASK_LIST_ID);
161 | }
162 |
163 | @Override
164 | public Task post(Task t) {
165 | return mService.post(GoogleTaskApiService.GOOGLE_TASK_LIST_ID, t);
166 | }
167 |
168 | @Override
169 | public Task put(Task t) {
170 | return mService.put(GoogleTaskApiService.GOOGLE_TASK_LIST_ID, t.getRemoteId(), t);
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/api/GoogleTaskApiService.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.api;
2 |
3 | import java.util.List;
4 |
5 | import retrofit.http.Body;
6 | import retrofit.http.GET;
7 | import retrofit.http.POST;
8 | import retrofit.http.PUT;
9 | import retrofit.http.Path;
10 |
11 | import com.rogansoft.tasksdemo.domain.Task;
12 | import com.rogansoft.tasksdemo.domain.TaskList;
13 |
14 | public interface GoogleTaskApiService {
15 | static final String API_BASE_URL = "https://www.googleapis.com/tasks/v1";
16 |
17 | // Your Google Tasks task list id - see https://developers.google.com/google-apps/tasks/
18 | static final String GOOGLE_TASK_LIST_ID = "MDc1OTAzMTA4NTg1NDI2MDc5ODI6MDow";
19 |
20 | static final String API_KEY = "93945578907-dhmfkndf15mr093fbndvbvdecl5fb0e4.apps.googleusercontent.com";
21 | static final String SCOPE =
22 | "oauth2:https://www.googleapis.com/auth/tasks";
23 |
24 | @GET("/users/@me/lists?key="+API_KEY)
25 | List getTaskLists();
26 |
27 | @GET("/lists/{taskListId}/tasks?key="+API_KEY+"&showDeleted=true")
28 | List get(@Path("taskListId") String taskListId);
29 |
30 | @GET("/lists/{taskListId}/tasks/{taskId}?key="+API_KEY)
31 | Task get(@Path("taskListId") String taskListId, @Path("taskId") String taskId);
32 |
33 | @POST("/lists/{taskListId}/tasks?key="+API_KEY)
34 | Task post(@Path("taskListId") String taskListId, @Body Task task);
35 |
36 | @PUT("/lists/{taskListId}/tasks/{taskId}?key="+API_KEY)
37 | Task put(@Path("taskListId") String taskListId, @Path("taskId") String taskId, @Body Task task);
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/api/TaskApi.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.api;
2 |
3 | import java.util.List;
4 |
5 | import com.rogansoft.tasksdemo.domain.Task;
6 | import com.rogansoft.tasksdemo.domain.TaskList;
7 |
8 | public interface TaskApi {
9 |
10 | List getTaskList();
11 |
12 | Task get(String remoteId);
13 | List get();
14 | Task post(Task t);
15 | Task put(Task t);
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/db/TaskDb.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.db;
2 |
3 | import java.util.ArrayList;
4 |
5 | import com.rogansoft.tasksdemo.domain.Task;
6 |
7 | import android.content.ContentValues;
8 | import android.content.Context;
9 | import android.database.Cursor;
10 | import android.database.SQLException;
11 | import android.database.sqlite.SQLiteDatabase;
12 | import android.database.sqlite.SQLiteOpenHelper;
13 | import android.util.Log;
14 |
15 | public class TaskDb {
16 | private static final String TAG = "TaskDb";
17 | private static final String DATABASE_NAME = "task_db";
18 | private static final int DATABASE_VERSION = 1;
19 |
20 | // Table names
21 | private static final String TASK_TABLE_NAME = "task";
22 |
23 | private DatabaseHelper mDbHelper;
24 | private SQLiteDatabase mDb;
25 | private final Context mCtx;
26 |
27 | // task table column names
28 | public static String TASK_FIELD_NAME_ID = "id"; // long
29 | public static String TASK_FIELD_NAME_UPDATED = "updated"; // long
30 | public static String TASK_FIELD_NAME_TITLE = "title"; // String
31 | public static String TASK_FIELD_NAME_SERVER_ID = "server_id"; // String
32 | public static String TASK_FIELD_NAME_POSITION = "position"; // String
33 | public static String TASK_FIELD_NAME_STATUS = "status"; // String
34 | public static String TASK_FIELD_NAME_DELETED = "deleted"; // boolean
35 |
36 | //task table creation sql
37 | private static final String TASK_CREATE_QUERY =
38 | "create table "+TASK_TABLE_NAME+" (" +
39 | TASK_FIELD_NAME_ID + " integer primary key autoincrement not null," +
40 | TASK_FIELD_NAME_UPDATED + " integer null," +
41 | TASK_FIELD_NAME_TITLE + " text not null," +
42 | TASK_FIELD_NAME_SERVER_ID + " text null," +
43 | TASK_FIELD_NAME_POSITION + " text null," +
44 | TASK_FIELD_NAME_STATUS + " text null," +
45 | TASK_FIELD_NAME_DELETED + " integer null" +
46 | ");";
47 |
48 | // task all fields String[]
49 | private final String[] TASK_ALL_FIELDS = new String[] {
50 | TASK_FIELD_NAME_ID,
51 | TASK_FIELD_NAME_UPDATED,
52 | TASK_FIELD_NAME_TITLE,
53 | TASK_FIELD_NAME_SERVER_ID,
54 | TASK_FIELD_NAME_POSITION,
55 | TASK_FIELD_NAME_STATUS,
56 | TASK_FIELD_NAME_DELETED
57 | };
58 |
59 | private static class DatabaseHelper extends SQLiteOpenHelper {
60 |
61 | public DatabaseHelper(Context context) {
62 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
63 | }
64 |
65 | @Override
66 | public void onCreate(SQLiteDatabase db) {
67 | Log.d(TAG, "onCreate...");
68 | initialDbBuild(db);
69 | }
70 |
71 | @Override
72 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
73 | Log.d(TAG, "onUpgrade... oldversion:"+oldVersion+" newVersion:"+newVersion);
74 |
75 | initialDbBuild(db);
76 |
77 | }
78 |
79 | private void initialDbBuild(SQLiteDatabase db) {
80 | Log.d(TAG, "initialDbBuild");
81 | db.execSQL("DROP TABLE IF EXISTS "+TASK_TABLE_NAME);
82 | db.execSQL(TASK_CREATE_QUERY);
83 |
84 | }
85 |
86 | }
87 |
88 | public TaskDb(Context ctx) {
89 | this.mCtx = ctx;
90 | }
91 |
92 |
93 | public TaskDb open() throws SQLException {
94 | mDbHelper = new DatabaseHelper(mCtx);
95 | mDb = mDbHelper.getWritableDatabase();
96 | return this;
97 | }
98 |
99 | public void close() {
100 | mDbHelper.close();
101 | }
102 |
103 | public static int fieldByNameInt(Cursor cur, String fldName) {
104 | return cur.getInt(cur.getColumnIndex(fldName));
105 | }
106 |
107 | public static String fieldByNameString(Cursor cur, String fldName) {
108 | return cur.getString(cur.getColumnIndex(fldName));
109 | }
110 |
111 | public static long fieldByNameLong(Cursor cur, String fldName) {
112 | return cur.getLong(cur.getColumnIndex(fldName));
113 | }
114 |
115 | public static double fieldByNameDouble(Cursor cur, String fldName) {
116 | return cur.getDouble(cur.getColumnIndex(fldName));
117 | }
118 |
119 | public static boolean fieldByNameBoolean(Cursor cur, String fldName) {
120 | return cur.getInt(cur.getColumnIndex(fldName)) == 1;
121 | }
122 |
123 | // Task CRUD
124 |
125 | // Used by user of content provider to build Task object from a cursor row.
126 | public static Task taskFromCursorRow(Cursor cur) {
127 | Task result;
128 | result =
129 | new Task(
130 | fieldByNameLong(cur, TASK_FIELD_NAME_ID),
131 | fieldByNameLong(cur, TASK_FIELD_NAME_UPDATED),
132 | fieldByNameString(cur, TASK_FIELD_NAME_TITLE),
133 | fieldByNameString(cur, TASK_FIELD_NAME_SERVER_ID),
134 | fieldByNameString(cur, TASK_FIELD_NAME_POSITION),
135 | fieldByNameString(cur, TASK_FIELD_NAME_STATUS),
136 | fieldByNameBoolean(cur, TASK_FIELD_NAME_DELETED)
137 | );
138 | return result;
139 | }
140 |
141 | // Used by user of content provider to setup fields for an insert
142 | public static ContentValues contentValuesFromTask(Task task, boolean includeId) {
143 | ContentValues initialValues = new ContentValues();
144 | if (includeId) { initialValues.put(TASK_FIELD_NAME_ID, task.getId()); }
145 | initialValues.put(TASK_FIELD_NAME_UPDATED, task.getUpdated());
146 | initialValues.put(TASK_FIELD_NAME_TITLE, task.getTitle());
147 | initialValues.put(TASK_FIELD_NAME_SERVER_ID, task.getServerId());
148 | initialValues.put(TASK_FIELD_NAME_POSITION, task.getPosition());
149 | initialValues.put(TASK_FIELD_NAME_STATUS, task.getStatus());
150 | initialValues.put(TASK_FIELD_NAME_DELETED, task.isDeleted());
151 | return initialValues;
152 | }
153 |
154 | // create
155 | static private long createTask(SQLiteDatabase db, Task task) {
156 | ContentValues initialValues = contentValuesFromTask(task, false);
157 | long result = db.insert(TASK_TABLE_NAME, null, initialValues);
158 | Log.d(TAG, "createTask result:"+result);
159 | for(String key : initialValues.keySet()) {
160 | Log.d(TAG, "key:"+key);
161 | if (key.equals(TASK_FIELD_NAME_SERVER_ID)) {
162 | Log.d(TAG, "serverid: "+initialValues.getAsString(key));
163 | }
164 | }
165 | return result;
166 | }
167 |
168 | public long createTask(Task task) {
169 | return createTask(mDb, task);
170 | }
171 |
172 | // delete
173 | public int deleteTask(String where, String[] whereArgs) {
174 | return mDb.delete(TASK_TABLE_NAME, where, whereArgs);
175 | }
176 |
177 | public int deleteTask(long rowId) {
178 | return deleteTask(TASK_FIELD_NAME_ID + "=" + rowId, null);
179 | }
180 |
181 | public int deleteAllTasks() {
182 | return deleteTask(null, null);
183 | }
184 |
185 | // read
186 | public Task fetchTask(long rowId) throws SQLException {
187 | Task result = null;
188 |
189 | Cursor cur =
190 | mDb.query(true, TASK_TABLE_NAME, TASK_ALL_FIELDS, TASK_FIELD_NAME_ID + "=" + rowId, null,
191 | null, null, null, null);
192 |
193 | if (cur.moveToFirst()) {
194 | result = taskFromCursorRow(cur);
195 | }
196 | cur.close();
197 | return result;
198 | }
199 |
200 | public ArrayList fetchAllTasks() {
201 | ArrayList result = new ArrayList();
202 |
203 | Cursor cur = mDb.query(TASK_TABLE_NAME, TASK_ALL_FIELDS,
204 | null, null, null, null, TASK_FIELD_NAME_POSITION);
205 |
206 | if (cur.moveToFirst()) {
207 | do {
208 | Task task = taskFromCursorRow(cur);
209 | result.add(task);
210 | } while (cur.moveToNext());
211 | }
212 | cur.close();
213 | return result;
214 | }
215 |
216 | public ArrayList fetchNonDeletedTasks() {
217 | ArrayList result = new ArrayList();
218 |
219 | Cursor cur = mDb.query(TASK_TABLE_NAME, TASK_ALL_FIELDS,
220 | TASK_FIELD_NAME_DELETED+" IS NULL OR "+TASK_FIELD_NAME_DELETED+" = 0", null, null, null, TASK_FIELD_NAME_POSITION);
221 |
222 | if (cur.moveToFirst()) {
223 | do {
224 | Task task = taskFromCursorRow(cur);
225 | result.add(task);
226 | } while (cur.moveToNext());
227 | }
228 | cur.close();
229 | return result;
230 | }
231 |
232 |
233 | // update
234 | public int updateTask(Task task) {
235 | ContentValues values = contentValuesFromTask(task, true);
236 | return mDb.update(TASK_TABLE_NAME, values, TASK_FIELD_NAME_ID + "=" + task.getId(), null);
237 | }
238 |
239 | }
240 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/domain/Task.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.domain;
2 |
3 | import com.rogansoft.sync.Syncable;
4 |
5 | public class Task implements Syncable {
6 |
7 |
8 | // fields
9 | private long id;
10 | private long updated;
11 | private String title;
12 | private String serverId;
13 | private String position;
14 | private String status;
15 | private boolean deleted;
16 |
17 | // getters
18 |
19 | public long getId() {
20 | return id;
21 | }
22 |
23 | public long getUpdated() {
24 | return updated;
25 | }
26 |
27 | public String getTitle() {
28 | return title;
29 | }
30 |
31 | public String getServerId() {
32 | return serverId;
33 | }
34 |
35 | public String getPosition() {
36 | return position;
37 | }
38 |
39 | public String getStatus() {
40 | return status;
41 | }
42 |
43 | public boolean isDeleted() {
44 | return deleted;
45 | }
46 |
47 | // setters
48 | public void setId(long id) {
49 | this.id = id;
50 | }
51 |
52 | public void setUpdated(long updated) {
53 | this.updated = updated;
54 | }
55 |
56 | public void setTitle(String title) {
57 | this.title = title;
58 | }
59 |
60 | public void setServerId(String serverId) {
61 | this.serverId = serverId;
62 | }
63 |
64 | public void setPosition(String position) {
65 | this.position = position;
66 | }
67 |
68 | public void setStatus(String status) {
69 | this.status = status;
70 | }
71 |
72 | public void setDeleted(boolean deleted) {
73 | this.deleted = deleted;
74 | }
75 |
76 | // copy
77 | public Task copy() {
78 | Task result = new Task(
79 | this.id ,
80 | this.updated ,
81 | this.title ,
82 | this.serverId ,
83 | this.position ,
84 | this.status ,
85 | this.deleted
86 | );
87 | return result;
88 | }
89 |
90 | // constructor
91 | public Task(long id, long updated, String title, String serverId, String position, String status, boolean deleted) {
92 | super();
93 | this.id = id;
94 | this.updated = updated;
95 | this.title = title;
96 | this.serverId = serverId;
97 | this.position = position;
98 | this.status = status;
99 | this.deleted = deleted;
100 | }
101 |
102 | public Task() {
103 | super();
104 | }
105 |
106 | // Implement Syncable...
107 |
108 | @Override
109 | public String getRemoteId() {
110 | return serverId;
111 | }
112 |
113 | @Override
114 | public void setRemoteId(String id) {
115 | this.serverId = id;
116 | }
117 |
118 | @Override
119 | public Long getLastUpdatedSequence() {
120 | return updated;
121 | }
122 |
123 | @Override
124 | public void setLastUpdatedSequence(Long value) {
125 | this.updated = value;
126 |
127 | }
128 |
129 | @Override
130 | public void mapFromRemote(Syncable remote) {
131 | Task remoteTask = (Task) remote;
132 |
133 | setTitle(remoteTask.getTitle());
134 | setStatus(remoteTask.getStatus());
135 |
136 | // server read only
137 | setPosition(remoteTask.getPosition());
138 | setServerId(remote.getRemoteId());
139 | setLastUpdatedSequence(remote.getLastUpdatedSequence());
140 | setDeleted(remoteTask.isDeleted());
141 |
142 | }
143 |
144 | @Override
145 | public void mapFromLocal(Syncable local) {
146 | Task localTask = (Task) local;
147 |
148 | setTitle(localTask.getTitle());
149 | setStatus(localTask.getStatus());
150 | setDeleted(localTask.isDeleted());
151 |
152 | }
153 |
154 | @Override
155 | public String toString() {
156 | return "Task [id=" + id + ", updated=" + updated + ", title=" + title
157 | + ", serverId=" + serverId + ", position=" + position
158 | + ", status=" + status + ", deleted="+deleted+"]";
159 | }
160 |
161 | }
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/domain/TaskList.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.domain;
2 |
3 | public class TaskList {
4 | private String kind;
5 | private String id;
6 | private String title;
7 | private String updated;
8 | private String selfLink;
9 |
10 | public String getKind() {
11 | return kind;
12 | }
13 | public void setKind(String kind) {
14 | this.kind = kind;
15 | }
16 | public String getId() {
17 | return id;
18 | }
19 | public void setId(String id) {
20 | this.id = id;
21 | }
22 | public String getTitle() {
23 | return title;
24 | }
25 | public void setTitle(String title) {
26 | this.title = title;
27 | }
28 | public String getUpdated() {
29 | return updated;
30 | }
31 | public void setUpdated(String updated) {
32 | this.updated = updated;
33 | }
34 | public String getSelfLink() {
35 | return selfLink;
36 | }
37 | public void setSelfLink(String selfLink) {
38 | this.selfLink = selfLink;
39 | }
40 | @Override
41 | public String toString() {
42 | return "TaskList [kind=" + kind + ", id=" + id + ", title=" + title
43 | + ", updated=" + updated + ", selfLink=" + selfLink + "]";
44 | }
45 |
46 |
47 |
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/sync/TaskSyncAdapter.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.sync;
2 |
3 | import java.io.IOException;
4 |
5 | import android.accounts.Account;
6 | import android.content.AbstractThreadedSyncAdapter;
7 | import android.content.ContentProviderClient;
8 | import android.content.Context;
9 | import android.content.SyncResult;
10 | import android.os.Bundle;
11 | import android.util.Log;
12 |
13 | import com.google.android.gms.auth.GoogleAuthException;
14 | import com.google.android.gms.auth.GoogleAuthUtil;
15 | import com.google.android.gms.auth.UserRecoverableNotifiedException;
16 | import com.rogansoft.sync.SyncManager;
17 | import com.rogansoft.tasksdemo.PrefsUtil;
18 | import com.rogansoft.tasksdemo.api.GoogleTaskApi;
19 | import com.rogansoft.tasksdemo.api.GoogleTaskApiService;
20 | import com.rogansoft.tasksdemo.api.TaskApi;
21 | import com.rogansoft.tasksdemo.db.TaskDb;
22 | import com.rogansoft.tasksdemo.domain.Task;
23 |
24 | public class TaskSyncAdapter extends AbstractThreadedSyncAdapter {
25 | private static final String TAG = "TaskSyncAdapter";
26 |
27 | private Context mContext;
28 |
29 | public TaskSyncAdapter(Context context, boolean autoInitialize) {
30 | super(context, autoInitialize);
31 |
32 | mContext = context;
33 |
34 | }
35 |
36 | public TaskSyncAdapter(Context context, boolean autoInitialize,
37 | boolean allowParallelSyncs) {
38 | super(context, autoInitialize, allowParallelSyncs);
39 | mContext = context;
40 | }
41 |
42 |
43 | private String getGoogleAuthToken() throws IOException {
44 | String googleUserName = PrefsUtil.retrieveGoogleTasksUser(mContext);
45 |
46 | String token = "";
47 | try {
48 | Log.d(TAG, "getGoogleAuthToken... "+googleUserName);
49 | token = GoogleAuthUtil.getTokenWithNotification(mContext,
50 | googleUserName, GoogleTaskApiService.SCOPE, null);
51 | } catch (UserRecoverableNotifiedException userNotifiedException) {
52 | // Notification has already been pushed.
53 | // Continue without token or stop background task.
54 | } catch (GoogleAuthException authEx) {
55 | // This is likely unrecoverable.
56 | Log.e(TAG,
57 | "Unrecoverable authentication exception: "
58 | + authEx.getMessage(), authEx);
59 | }
60 | return token;
61 | }
62 |
63 | @Override
64 | public void onPerformSync(Account account, Bundle extras, String authority,
65 | ContentProviderClient provider, SyncResult syncResult) {
66 | Log.d(TAG, "onPerformSync");
67 | try {
68 | TaskApi api = new GoogleTaskApi(getGoogleAuthToken());
69 | TaskDb db = new TaskDb(mContext);
70 |
71 | db.open();
72 | try {
73 | TaskSyncLocalDatastore localDatastore = new TaskSyncLocalDatastore(db);
74 | TaskSyncRemoteDatastore remoteDatastore = new TaskSyncRemoteDatastore(api);
75 | SyncManager syncManager = new SyncManager(localDatastore, remoteDatastore);
76 |
77 | syncManager.sync();
78 |
79 | } finally {
80 | db.close();
81 | }
82 | getContext().getContentResolver().notifyChange(TaskSyncContentProvider.CONTENT_URI, null);
83 |
84 | } catch (Exception e) {
85 | Log.e(TAG, "syncFailed:" + e.getMessage());
86 | }
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/sync/TaskSyncAdapterService.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.sync;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.IBinder;
6 |
7 | public class TaskSyncAdapterService extends Service {
8 | private TaskSyncAdapter mSyncAdapter = null;
9 |
10 | private static final Object mSyncAdapterLock = new Object();
11 |
12 | @Override
13 | public void onCreate() {
14 | super.onCreate();
15 |
16 | synchronized (mSyncAdapterLock) {
17 | if(mSyncAdapter == null) {
18 | mSyncAdapter = new TaskSyncAdapter(getApplicationContext(), true);
19 | }
20 | }
21 | }
22 |
23 | @Override
24 | public IBinder onBind(Intent intent) {
25 | return mSyncAdapter.getSyncAdapterBinder();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/sync/TaskSyncContentProvider.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.sync;
2 |
3 | import android.content.ContentProvider;
4 | import android.content.ContentValues;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 |
8 | public class TaskSyncContentProvider extends ContentProvider {
9 | public static String AUTHORITY = "com.rogansoft.tasksdemo.sync.tasksynccontentprovider";
10 | public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/tasks");
11 |
12 | @Override
13 | public boolean onCreate() {
14 | // TODO Auto-generated method stub
15 | return false;
16 | }
17 |
18 | @Override
19 | public Cursor query(Uri uri, String[] projection, String selection,
20 | String[] selectionArgs, String sortOrder) {
21 | // TODO Auto-generated method stub
22 | return null;
23 | }
24 |
25 | @Override
26 | public String getType(Uri uri) {
27 | // TODO Auto-generated method stub
28 | return null;
29 | }
30 |
31 | @Override
32 | public Uri insert(Uri uri, ContentValues values) {
33 | // TODO Auto-generated method stub
34 | return null;
35 | }
36 |
37 | @Override
38 | public int delete(Uri uri, String selection, String[] selectionArgs) {
39 | // TODO Auto-generated method stub
40 | return 0;
41 | }
42 |
43 | @Override
44 | public int update(Uri uri, ContentValues values, String selection,
45 | String[] selectionArgs) {
46 | // TODO Auto-generated method stub
47 | return 0;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/sync/TaskSyncLocalDatastore.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.sync;
2 |
3 | import java.util.List;
4 |
5 | import com.rogansoft.sync.Datastore;
6 | import com.rogansoft.tasksdemo.db.TaskDb;
7 | import com.rogansoft.tasksdemo.domain.Task;
8 |
9 | public class TaskSyncLocalDatastore implements Datastore {
10 | private TaskDb mDb;
11 |
12 | @Override
13 | public List get() {
14 | return mDb.fetchAllTasks();
15 | }
16 |
17 | @Override
18 | public Task create() {
19 | return new Task();
20 | }
21 |
22 | @Override
23 | public Task add(Task localDataInstance) {
24 | long id = mDb.createTask(localDataInstance);
25 | Task result = mDb.fetchTask(id);
26 | return result;
27 | }
28 |
29 | @Override
30 | public Task update(Task localDataInstance) {
31 | mDb.updateTask(localDataInstance);
32 | Task result = mDb.fetchTask(localDataInstance.getId());
33 | return result;
34 | }
35 |
36 | public TaskSyncLocalDatastore(TaskDb mDb) {
37 | super();
38 | this.mDb = mDb;
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/com/rogansoft/tasksdemo/sync/TaskSyncRemoteDatastore.java:
--------------------------------------------------------------------------------
1 | package com.rogansoft.tasksdemo.sync;
2 |
3 | import java.util.List;
4 |
5 | import android.util.Log;
6 |
7 | import com.rogansoft.sync.Datastore;
8 | import com.rogansoft.tasksdemo.api.TaskApi;
9 | import com.rogansoft.tasksdemo.domain.Task;
10 |
11 | public class TaskSyncRemoteDatastore implements Datastore {
12 | private static final String TAG = "TaskSyncRemoteDatastore";
13 |
14 | private TaskApi mRemoteApi;
15 |
16 | @Override
17 | public List get() {
18 | return mRemoteApi.get();
19 | }
20 |
21 | @Override
22 | public Task create() {
23 | return new Task();
24 | }
25 |
26 | @Override
27 | public Task add(Task item) {
28 | Log.d(TAG, "addRemote:"+item.toString());
29 | Task result = mRemoteApi.post(item);
30 | Log.d(TAG, "afterPost:"+result.toString());
31 | return result;
32 | }
33 |
34 | @Override
35 | public Task update(Task item) {
36 | Log.d(TAG, "updateRemote:"+item.toString());
37 | Task result = mRemoteApi.put(item);
38 | return result;
39 | }
40 |
41 | public TaskSyncRemoteDatastore(TaskApi api) {
42 | super();
43 | this.mRemoteApi = api;
44 | }
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------