diff options
author | Andrew Dolgov <[email protected]> | 2011-12-06 14:29:57 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-12-06 14:29:57 +0300 |
commit | 19cb77aa354590fd4c44c032497c5c38afec1ffb (patch) | |
tree | 176bfb9eed3c8e65970a1451366184f68f9669ff | |
parent | 797860e5172d84ff20ae6401b6c8c5363995e057 (diff) |
implement synchronization of offline stuff back to the mothership
-rw-r--r-- | res/values/strings.xml | 5 | ||||
-rw-r--r-- | src/org/fox/ttrss/ApiRequest.java | 6 | ||||
-rw-r--r-- | src/org/fox/ttrss/FeedsFragment.java | 8 | ||||
-rw-r--r-- | src/org/fox/ttrss/HeadlinesFragment.java | 8 | ||||
-rw-r--r-- | src/org/fox/ttrss/MainActivity.java | 239 | ||||
-rw-r--r-- | src/org/fox/ttrss/OfflineFeedsFragment.java | 8 |
6 files changed, 247 insertions, 27 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index c2637c9d..3a508cc5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -86,6 +86,7 @@ <string name="error_other_error">Error: unknown error (see log)</string> <string name="error_api_disabled">Error: API disabled for this user</string> <string name="error_api_unknown">Error: unknown API error (see log)</string> + <string name="error_api_incorrect_usage">Error: incorrect API usage</string> <string name="error_login_failed">Error: username or password incorrect</string> <string name="error_invalid_api_url">Error: invalid API URL</string> <string name="combined_mode_summary">Displays full article text inline, instead of a separate panel</string> @@ -96,4 +97,8 @@ <string name="offline_switch_error">Failed to prepare offline mode (see log)</string> <string name="no_feeds">No feeds to display</string> <string name="no_headlines">No articles to display</string> + <string name="dialog_offline_prompt">Login failed, but you have stored offline data. Would you like to go offline?</string> + <string name="dialog_offline_go">Go offline</string> + <string name="dialog_cancel">Cancel</string> + <string name="syncing_offline_data">Synchronizing offline data...</string> </resources>
\ No newline at end of file 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
|