summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-12-06 14:29:57 +0300
committerAndrew Dolgov <[email protected]>2011-12-06 14:29:57 +0300
commit19cb77aa354590fd4c44c032497c5c38afec1ffb (patch)
tree176bfb9eed3c8e65970a1451366184f68f9669ff /src
parent797860e5172d84ff20ae6401b6c8c5363995e057 (diff)
implement synchronization of offline stuff back to the mothership
Diffstat (limited to 'src')
-rw-r--r--src/org/fox/ttrss/ApiRequest.java6
-rw-r--r--src/org/fox/ttrss/FeedsFragment.java8
-rw-r--r--src/org/fox/ttrss/HeadlinesFragment.java8
-rw-r--r--src/org/fox/ttrss/MainActivity.java239
-rw-r--r--src/org/fox/ttrss/OfflineFeedsFragment.java8
5 files changed, 242 insertions, 27 deletions
diff --git a/src/org/fox/ttrss/ApiRequest.java b/src/org/fox/ttrss/ApiRequest.java
index 56a7a498..d1b832c8 100644
--- a/src/org/fox/ttrss/ApiRequest.java
+++ b/src/org/fox/ttrss/ApiRequest.java
@@ -37,7 +37,7 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
private final String TAG = this.getClass().getSimpleName();
public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND,
- HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, API_UNKNOWN, LOGIN_FAILED, INVALID_URL };
+ HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, API_UNKNOWN, LOGIN_FAILED, INVALID_URL, INCORRECT_USAGE };
public static final int API_STATUS_OK = 0;
public static final int API_STATUS_ERR = 1;
@@ -94,6 +94,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
return R.string.error_login_failed;
case INVALID_URL:
return R.string.error_invalid_api_url;
+ case INCORRECT_USAGE:
+ return R.string.error_api_incorrect_usage;
default:
Log.d(TAG, "getErrorMessage: unknown error code=" + m_lastError);
return R.string.error_unknown;
@@ -202,6 +204,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE
m_lastError = ApiError.LOGIN_FAILED;
} else if (error.equals("NOT_LOGGED_IN")) {
m_lastError = ApiError.LOGIN_FAILED;
+ } else if (error.equals("INCORRECT_USAGE")) {
+ m_lastError = ApiError.INCORRECT_USAGE;
} else {
Log.d(TAG, "Unknown API error: " + error);
m_lastError = ApiError.API_UNKNOWN;
diff --git a/src/org/fox/ttrss/FeedsFragment.java b/src/org/fox/ttrss/FeedsFragment.java
index 47d66198..fa160d93 100644
--- a/src/org/fox/ttrss/FeedsFragment.java
+++ b/src/org/fox/ttrss/FeedsFragment.java
@@ -127,7 +127,7 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
ListView list = (ListView)view.findViewById(R.id.feeds);
m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, (ArrayList<Feed>)m_feeds);
list.setAdapter(m_adapter);
- list.setEmptyView(view.findViewById(R.id.no_feeds));
+ //list.setEmptyView(view.findViewById(R.id.no_feeds));
list.setOnItemClickListener(this);
registerForContextMenu(list);
@@ -308,9 +308,9 @@ public class FeedsFragment extends Fragment implements OnItemClickListener, OnSh
sortFeeds();
- //if (m_feeds.size() == 0)
- // setLoadingStatus(R.string.no_feeds_to_display, false);
- //else
+ if (m_feeds.size() == 0)
+ setLoadingStatus(R.string.no_feeds_to_display, false);
+ else
setLoadingStatus(R.string.blank, false);
if (m_enableFeedIcons && !m_feedIconsChecked) getFeedIcons();
diff --git a/src/org/fox/ttrss/HeadlinesFragment.java b/src/org/fox/ttrss/HeadlinesFragment.java
index cf437258..ac4797b2 100644
--- a/src/org/fox/ttrss/HeadlinesFragment.java
+++ b/src/org/fox/ttrss/HeadlinesFragment.java
@@ -127,7 +127,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
list.setAdapter(m_adapter);
list.setOnItemClickListener(this);
list.setOnScrollListener(this);
- list.setEmptyView(view.findViewById(R.id.no_headlines));
+ //list.setEmptyView(view.findViewById(R.id.no_headlines));
registerForContextMenu(list);
Log.d(TAG, "onCreateView, feed=" + m_feed);
@@ -279,9 +279,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
m_adapter.notifyDataSetChanged();
- //if (m_articles.size() == 0)
- // setLoadingStatus(R.string.no_headlines_to_display, false);
- //else
+ if (m_articles.size() == 0)
+ setLoadingStatus(R.string.no_headlines_to_display, false);
+ else
setLoadingStatus(R.string.blank, false);
m_refreshInProgress = false;
diff --git a/src/org/fox/ttrss/MainActivity.java b/src/org/fox/ttrss/MainActivity.java
index 455e34bc..4111b03f 100644
--- a/src/org/fox/ttrss/MainActivity.java
+++ b/src/org/fox/ttrss/MainActivity.java
@@ -13,6 +13,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.net.ConnectivityManager;
@@ -79,6 +80,36 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
return m_apiLevel;
}
+ public boolean hasPendingOfflineData() {
+ Cursor c = getReadableDb().query("articles",
+ new String[] { "COUNT(*)" }, "modified = 1", null, null, null, null);
+ if (c.moveToFirst()) {
+ int modified = c.getInt(0);
+ c.close();
+
+ return modified > 0;
+ }
+
+ return false;
+ }
+
+ public void clearPendingOfflineData() {
+ getWritableDb().execSQL("UPDATE articles SET modified = 0");
+ }
+
+ public boolean hasOfflineData() {
+ Cursor c = getReadableDb().query("articles",
+ new String[] { "COUNT(*)" }, null, null, null, null, null);
+ if (c.moveToFirst()) {
+ int modified = c.getInt(0);
+ c.close();
+
+ return modified > 0;
+ }
+
+ return false;
+ }
+
@SuppressWarnings({ "unchecked", "serial" })
public void saveArticleUnread(final Article article) {
ApiRequest req = new ApiRequest(getApplicationContext());
@@ -1031,6 +1062,135 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
m_writableDb.close();
}
+
+ private void syncOfflineRead() {
+ Log.d(TAG, "syncing modified offline data... (read)");
+
+ final String ids = getOfflineModifiedIds(ModifiedCriteria.READ);
+
+ if (ids.length() > 0) {
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ syncOfflineMarked();
+ } else {
+ setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String,String> map = new HashMap<String,String>() {
+ {
+ put("sid", m_sessionId);
+ put("op", "updateArticle");
+ put("article_ids", ids);
+ put("mode", "0");
+ put("field", "2");
+ }
+ };
+
+ req.execute(map);
+ } else {
+ syncOfflineMarked();
+ }
+ }
+
+ private void syncOfflineMarked() {
+ Log.d(TAG, "syncing modified offline data... (marked)");
+
+ final String ids = getOfflineModifiedIds(ModifiedCriteria.MARKED);
+
+ if (ids.length() > 0) {
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ syncOfflinePublished();
+ } else {
+ setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String,String> map = new HashMap<String,String>() {
+ {
+ put("sid", m_sessionId);
+ put("op", "updateArticle");
+ put("article_ids", ids);
+ put("mode", "0");
+ put("field", "0");
+ }
+ };
+
+ req.execute(map);
+ } else {
+ syncOfflinePublished();
+ }
+ }
+
+ private void syncOfflinePublished() {
+ Log.d(TAG, "syncing modified offline data... (published)");
+
+ final String ids = getOfflineModifiedIds(ModifiedCriteria.MARKED);
+
+ if (ids.length() > 0) {
+ ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @Override
+ protected void onPostExecute(JsonElement result) {
+ if (result != null) {
+ loginSuccessInitUI();
+ loginSuccess();
+ clearPendingOfflineData();
+ } else {
+ setLoadingStatus(getErrorMessage(), false);
+ }
+ }
+ };
+
+ @SuppressWarnings("serial")
+ HashMap<String,String> map = new HashMap<String,String>() {
+ {
+ put("sid", m_sessionId);
+ put("op", "updateArticle");
+ put("article_ids", ids);
+ put("mode", "0");
+ put("field", "1");
+ }
+ };
+
+ req.execute(map);
+ } else {
+ loginSuccessInitUI();
+ loginSuccess();
+ clearPendingOfflineData();
+ }
+ }
+
+ private void syncOfflineData() {
+ setLoadingStatus(R.string.syncing_offline_data, true);
+ syncOfflineRead();
+ }
+
+ private void loginSuccessInitUI() {
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+
+ if (m_enableCats) {
+ FeedCategoriesFragment frag = new FeedCategoriesFragment();
+ ft.replace(R.id.cats_fragment, frag);
+ } else {
+ FeedsFragment frag = new FeedsFragment();
+ ft.replace(R.id.feeds_fragment, frag);
+ }
+
+ try {
+ ft.commit();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ }
+ }
private void loginSuccess() {
findViewById(R.id.loading_container).setVisibility(View.INVISIBLE);
@@ -1056,6 +1216,42 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
m_refreshTimer.schedule(m_refreshTask, 60*1000L, 120*1000L);
}
+ private enum ModifiedCriteria { READ, MARKED, PUBLISHED };
+
+ private String getOfflineModifiedIds(ModifiedCriteria criteria) {
+
+ String criteriaStr = "";
+
+ switch (criteria) {
+ case READ:
+ criteriaStr = "unread = 0";
+ break;
+ case MARKED:
+ criteriaStr = "marked = 1";
+ break;
+ case PUBLISHED:
+ criteriaStr = "published = 1";
+ break;
+ }
+
+ Cursor c = getReadableDb().query("articles",
+ null, "modified = 1 AND " + criteriaStr, null, null, null, null);
+
+ String tmp = "";
+
+ while (c.moveToNext()) {
+ tmp += c.getInt(0) + ",";
+ }
+
+ tmp = tmp.replaceAll(",$", "");
+
+ //Log.d(TAG, "getOfflineModifiedIds " + criteria + " = " + tmp);
+
+ c.close();
+
+ return tmp;
+ }
+
private class LoginRequest extends ApiRequest {
public LoginRequest(Context context) {
super(context);
@@ -1081,20 +1277,14 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
Log.d(TAG, "Received API level: " + m_apiLevel);
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-
- if (m_enableCats) {
- FeedCategoriesFragment frag = new FeedCategoriesFragment();
- ft.replace(R.id.cats_fragment, frag);
- } else {
- FeedsFragment frag = new FeedsFragment();
- ft.replace(R.id.feeds_fragment, frag);
- }
+ if (hasPendingOfflineData()) {
- try {
- ft.commit();
- } catch (IllegalStateException e) {
- e.printStackTrace();
+ syncOfflineData();
+
+ //loginSuccess();
+ } else {
+ loginSuccessInitUI();
+ loginSuccess();
}
}
@@ -1112,7 +1302,6 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
setLoadingStatus(R.string.loading_message, true);
- loginSuccess();
return;
}
@@ -1124,6 +1313,26 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
m_sessionId = null;
setLoadingStatus(getErrorMessage(), false);
+
+ if (hasOfflineData()) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(m_context).
+ setMessage(R.string.dialog_offline_prompt).
+ setPositiveButton(R.string.dialog_offline_go, new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ switchOfflineSuccess();
+ }
+ }).
+ setNegativeButton(R.string.dialog_cancel, new Dialog.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ //
+ }
+ });
+
+ AlertDialog dlg = builder.create();
+ dlg.show();
+ }
+
//m_menu.findItem(R.id.login).setVisible(true);
}
@@ -1278,7 +1487,7 @@ public class MainActivity extends FragmentActivity implements FeedsFragment.OnFe
} else {
- LoginRequest ar = new LoginRequest(getApplicationContext());
+ LoginRequest ar = new LoginRequest(this); // do not use getApplicationContext() here because alertdialog chokes on it
HashMap<String,String> map = new HashMap<String,String>() {
{
diff --git a/src/org/fox/ttrss/OfflineFeedsFragment.java b/src/org/fox/ttrss/OfflineFeedsFragment.java
index 369ca5db..b76dd4d4 100644
--- a/src/org/fox/ttrss/OfflineFeedsFragment.java
+++ b/src/org/fox/ttrss/OfflineFeedsFragment.java
@@ -249,9 +249,11 @@ public class OfflineFeedsFragment extends Fragment implements OnItemClickListene
}
public void sortFeeds() {
- // TODO implement
-
- m_adapter.notifyDataSetInvalidated();
+ try {
+ refresh();
+ } catch (IllegalStateException e) {
+ // we're probably closing and DB is gone already
+ }
}
@Override