implement background loading in ArticlePager
various loading and state related fixes
This commit is contained in:
parent
4f8cc7aeeb
commit
219531f138
@ -1,7 +1,13 @@
|
|||||||
package org.fox.ttrss;
|
package org.fox.ttrss;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import org.fox.ttrss.types.Article;
|
import org.fox.ttrss.types.Article;
|
||||||
import org.fox.ttrss.types.ArticleList;
|
import org.fox.ttrss.types.ArticleList;
|
||||||
|
import org.fox.ttrss.types.Feed;
|
||||||
|
import org.fox.ttrss.util.HeadlinesRequest;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -9,6 +15,7 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -20,6 +27,8 @@ public class ArticlePager extends Fragment {
|
|||||||
private HeadlinesEventListener m_onlineServices;
|
private HeadlinesEventListener m_onlineServices;
|
||||||
private Article m_article;
|
private Article m_article;
|
||||||
private ArticleList m_articles;
|
private ArticleList m_articles;
|
||||||
|
private OnlineActivity m_activity;
|
||||||
|
private String m_searchQuery = "";
|
||||||
|
|
||||||
private class PagerAdapter extends FragmentStatePagerAdapter {
|
private class PagerAdapter extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
@ -55,6 +64,10 @@ public class ArticlePager extends Fragment {
|
|||||||
m_article = article;
|
m_article = article;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSearchQuery(String searchQuery) {
|
||||||
|
m_searchQuery = searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.article_pager, container, false);
|
View view = inflater.inflate(R.layout.article_pager, container, false);
|
||||||
@ -98,9 +111,8 @@ public class ArticlePager extends Fragment {
|
|||||||
//Log.d(TAG, "Page #" + position + "/" + m_adapter.getCount());
|
//Log.d(TAG, "Page #" + position + "/" + m_adapter.getCount());
|
||||||
|
|
||||||
if (position == m_adapter.getCount() - 5) {
|
if (position == m_adapter.getCount() - 5) {
|
||||||
// FIXME load more articles somehow
|
Log.d(TAG, "loading more articles...");
|
||||||
//m_hf.refresh(true);
|
loadMoreArticles();
|
||||||
m_adapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +121,58 @@ public class ArticlePager extends Fragment {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "serial" })
|
||||||
|
private void loadMoreArticles() {
|
||||||
|
m_activity.setLoadingStatus(R.string.blank, true);
|
||||||
|
|
||||||
|
HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity) {
|
||||||
|
protected void onPostExecute(JsonElement result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
m_adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Feed feed = TinyApplication.getInstance().m_activeFeed;
|
||||||
|
|
||||||
|
final String sessionId = m_activity.getSessionId();
|
||||||
|
final boolean showUnread = m_activity.getUnreadArticlesOnly();
|
||||||
|
int skip = 0;
|
||||||
|
|
||||||
|
for (Article a : m_articles) {
|
||||||
|
if (a.unread) ++skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip == 0) skip = m_articles.size();
|
||||||
|
|
||||||
|
final int fskip = skip;
|
||||||
|
|
||||||
|
req.setOffset(skip);
|
||||||
|
|
||||||
|
HashMap<String,String> map = new HashMap<String,String>() {
|
||||||
|
{
|
||||||
|
put("op", "getHeadlines");
|
||||||
|
put("sid", sessionId);
|
||||||
|
put("feed_id", String.valueOf(feed.id));
|
||||||
|
put("show_content", "true");
|
||||||
|
put("include_attachments", "true");
|
||||||
|
put("limit", String.valueOf(HeadlinesFragment.HEADLINES_REQUEST_SIZE));
|
||||||
|
put("offset", String.valueOf(0));
|
||||||
|
put("view_mode", showUnread ? "adaptive" : "all_articles");
|
||||||
|
put("skip", String.valueOf(fskip));
|
||||||
|
|
||||||
|
if (feed.is_cat) put("is_cat", "true");
|
||||||
|
|
||||||
|
if (m_searchQuery != null && m_searchQuery.length() != 0) {
|
||||||
|
put("search", m_searchQuery);
|
||||||
|
put("search_mode", "");
|
||||||
|
put("match_on", "both");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
req.execute(map);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle out) {
|
public void onSaveInstanceState(Bundle out) {
|
||||||
super.onSaveInstanceState(out);
|
super.onSaveInstanceState(out);
|
||||||
@ -122,6 +186,7 @@ public class ArticlePager extends Fragment {
|
|||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
|
|
||||||
m_onlineServices = (HeadlinesEventListener)activity;
|
m_onlineServices = (HeadlinesEventListener)activity;
|
||||||
|
m_activity = (OnlineActivity)activity;
|
||||||
|
|
||||||
m_articles = TinyApplication.getInstance().m_loadedArticles;
|
m_articles = TinyApplication.getInstance().m_loadedArticles;
|
||||||
}
|
}
|
||||||
@ -130,7 +195,7 @@ public class ArticlePager extends Fragment {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
((OnlineActivity)getActivity()).initMenu();
|
m_activity.initMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Article getSelectedArticle() {
|
public Article getSelectedArticle() {
|
||||||
|
@ -32,7 +32,7 @@ public class CommonActivity extends FragmentActivity {
|
|||||||
m_smallScreenMode = smallScreen;
|
m_smallScreenMode = smallScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setLoadingStatus(int status, boolean showProgress) {
|
public void setLoadingStatus(int status, boolean showProgress) {
|
||||||
TextView tv = (TextView) findViewById(R.id.loading_message);
|
TextView tv = (TextView) findViewById(R.id.loading_message);
|
||||||
|
|
||||||
if (tv != null) {
|
if (tv != null) {
|
||||||
|
@ -45,50 +45,55 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
|
|||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
if (intent.getParcelableExtra("feed") != null || intent.getParcelableExtra("category") != null ||
|
if (savedInstanceState == null) {
|
||||||
|
|
||||||
|
if (intent.getParcelableExtra("feed") != null || intent.getParcelableExtra("category") != null ||
|
||||||
intent.getParcelableExtra("article") != null) {
|
intent.getParcelableExtra("article") != null) {
|
||||||
|
|
||||||
Feed feed = (Feed) intent.getParcelableExtra("feed");
|
Feed feed = (Feed) intent.getParcelableExtra("feed");
|
||||||
FeedCategory cat = (FeedCategory) intent.getParcelableExtra("category");
|
FeedCategory cat = (FeedCategory) intent.getParcelableExtra("category");
|
||||||
Article article = (Article) intent.getParcelableExtra("article");
|
Article article = (Article) intent.getParcelableExtra("article");
|
||||||
|
|
||||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
if (feed != null) {
|
if (feed != null) {
|
||||||
HeadlinesFragment hf = new HeadlinesFragment(feed);
|
HeadlinesFragment hf = new HeadlinesFragment(feed);
|
||||||
ft.replace(R.id.feeds_fragment, hf, FRAG_HEADLINES);
|
ft.replace(R.id.feeds_fragment, hf, FRAG_HEADLINES);
|
||||||
|
|
||||||
setTitle(feed.title);
|
setTitle(feed.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cat != null) {
|
||||||
|
FeedsFragment ff = new FeedsFragment(cat);
|
||||||
|
ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
|
||||||
|
|
||||||
|
setTitle(cat.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (article != null) {
|
||||||
|
Article original = TinyApplication.getInstance().m_loadedArticles.findById(article.id);
|
||||||
|
|
||||||
|
ArticlePager ap = new ArticlePager(original != null ? original : article);
|
||||||
|
ft.replace(R.id.feeds_fragment, ap, FRAG_ARTICLE);
|
||||||
|
|
||||||
|
ap.setSearchQuery(intent.getStringExtra("searchQuery"));
|
||||||
|
|
||||||
|
setTitle(intent.getStringExtra("feedTitle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ft.commit();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
|
if (m_prefs.getBoolean("enable_cats", false)) {
|
||||||
|
ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
|
||||||
|
} else {
|
||||||
|
ft.replace(R.id.feeds_fragment, new FeedsFragment(), FRAG_FEEDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ft.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cat != null) {
|
|
||||||
FeedsFragment ff = new FeedsFragment(cat);
|
|
||||||
ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
|
|
||||||
|
|
||||||
setTitle(cat.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (article != null) {
|
|
||||||
Article original = TinyApplication.getInstance().m_loadedArticles.findById(article.id);
|
|
||||||
|
|
||||||
ArticlePager ap = new ArticlePager(original != null ? original : article);
|
|
||||||
ft.replace(R.id.feeds_fragment, ap, FRAG_ARTICLE);
|
|
||||||
|
|
||||||
setTitle(intent.getStringExtra("feedTitle"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ft.commit();
|
|
||||||
|
|
||||||
} else if (savedInstanceState == null) {
|
|
||||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
|
||||||
|
|
||||||
if (m_prefs.getBoolean("enable_cats", false)) {
|
|
||||||
ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS);
|
|
||||||
} else {
|
|
||||||
ft.replace(R.id.feeds_fragment, new FeedsFragment(), FRAG_FEEDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ft.commit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +262,7 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
|
|||||||
|
|
||||||
intent.putExtra("feedTitle", hf.getFeed().title);
|
intent.putExtra("feedTitle", hf.getFeed().title);
|
||||||
intent.putExtra("article", article);
|
intent.putExtra("article", article);
|
||||||
|
intent.putExtra("searchQuery", hf.getSearchQuery());
|
||||||
|
|
||||||
startActivityForResult(intent, 0);
|
startActivityForResult(intent, 0);
|
||||||
|
|
||||||
@ -268,6 +274,7 @@ public class FeedsActivity extends OnlineActivity implements HeadlinesEventListe
|
|||||||
|
|
||||||
intent.putExtra("feed", hf.getFeed());
|
intent.putExtra("feed", hf.getFeed());
|
||||||
intent.putExtra("article", article);
|
intent.putExtra("article", article);
|
||||||
|
intent.putExtra("searchQuery", hf.getSearchQuery());
|
||||||
|
|
||||||
startActivityForResult(intent, 0);
|
startActivityForResult(intent, 0);
|
||||||
}
|
}
|
||||||
|
@ -50,10 +50,14 @@ public class HeadlinesActivity extends OnlineActivity implements HeadlinesEventL
|
|||||||
if (i.getExtras() != null) {
|
if (i.getExtras() != null) {
|
||||||
Feed feed = i.getParcelableExtra("feed");
|
Feed feed = i.getParcelableExtra("feed");
|
||||||
Article article = i.getParcelableExtra("article");
|
Article article = i.getParcelableExtra("article");
|
||||||
|
String searchQuery = i.getStringExtra("searchQuery");
|
||||||
|
|
||||||
HeadlinesFragment hf = new HeadlinesFragment(feed, article);
|
HeadlinesFragment hf = new HeadlinesFragment(feed, article);
|
||||||
ArticlePager af = new ArticlePager(hf.getArticleById(article.id));
|
ArticlePager af = new ArticlePager(hf.getArticleById(article.id));
|
||||||
|
|
||||||
|
hf.setSearchQuery(searchQuery);
|
||||||
|
af.setSearchQuery(searchQuery);
|
||||||
|
|
||||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||||
|
|
||||||
ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
|
ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
|
||||||
|
@ -15,6 +15,7 @@ import org.fox.ttrss.types.Article;
|
|||||||
import org.fox.ttrss.types.ArticleList;
|
import org.fox.ttrss.types.ArticleList;
|
||||||
import org.fox.ttrss.types.Attachment;
|
import org.fox.ttrss.types.Attachment;
|
||||||
import org.fox.ttrss.types.Feed;
|
import org.fox.ttrss.types.Feed;
|
||||||
|
import org.fox.ttrss.util.HeadlinesRequest;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@ -67,10 +68,9 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
private Feed m_feed;
|
private Feed m_feed;
|
||||||
private Article m_activeArticle;
|
private Article m_activeArticle;
|
||||||
private boolean m_refreshInProgress = false;
|
|
||||||
private boolean m_canLoadMore = false;
|
|
||||||
private boolean m_combinedMode = true;
|
private boolean m_combinedMode = true;
|
||||||
private String m_searchQuery = "";
|
private String m_searchQuery = "";
|
||||||
|
private boolean m_refreshInProgress = false;
|
||||||
|
|
||||||
private SharedPreferences m_prefs;
|
private SharedPreferences m_prefs;
|
||||||
|
|
||||||
@ -268,7 +268,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
//m_articles = savedInstanceState.getParcelable("articles");
|
//m_articles = savedInstanceState.getParcelable("articles");
|
||||||
m_activeArticle = savedInstanceState.getParcelable("activeArticle");
|
m_activeArticle = savedInstanceState.getParcelable("activeArticle");
|
||||||
m_selectedArticles = savedInstanceState.getParcelable("selectedArticles");
|
m_selectedArticles = savedInstanceState.getParcelable("selectedArticles");
|
||||||
m_canLoadMore = savedInstanceState.getBoolean("canLoadMore");
|
|
||||||
m_combinedMode = savedInstanceState.getBoolean("combinedMode");
|
m_combinedMode = savedInstanceState.getBoolean("combinedMode");
|
||||||
m_searchQuery = (String) savedInstanceState.getCharSequence("searchQuery");
|
m_searchQuery = (String) savedInstanceState.getCharSequence("searchQuery");
|
||||||
}
|
}
|
||||||
@ -355,7 +354,13 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
public void refresh(boolean append) {
|
public void refresh(boolean append) {
|
||||||
m_refreshInProgress = true;
|
m_refreshInProgress = true;
|
||||||
|
|
||||||
HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext());
|
HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity) {
|
||||||
|
protected void onPostExecute(JsonElement result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
m_refreshInProgress = false;
|
||||||
|
m_adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
final String sessionId = m_activity.getSessionId();
|
final String sessionId = m_activity.getSessionId();
|
||||||
final boolean showUnread = m_listener.getUnreadArticlesOnly();
|
final boolean showUnread = m_listener.getUnreadArticlesOnly();
|
||||||
@ -394,7 +399,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
if (isCat) put("is_cat", "true");
|
if (isCat) put("is_cat", "true");
|
||||||
|
|
||||||
if (m_searchQuery.length() != 0) {
|
if (m_searchQuery != null && m_searchQuery.length() != 0) {
|
||||||
put("search", m_searchQuery);
|
put("search", m_searchQuery);
|
||||||
put("search_mode", "");
|
put("search_mode", "");
|
||||||
put("match_on", "both");
|
put("match_on", "both");
|
||||||
@ -413,7 +418,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
//out.putParcelable("articles", m_articles);
|
//out.putParcelable("articles", m_articles);
|
||||||
out.putParcelable("activeArticle", m_activeArticle);
|
out.putParcelable("activeArticle", m_activeArticle);
|
||||||
out.putParcelable("selectedArticles", m_selectedArticles);
|
out.putParcelable("selectedArticles", m_selectedArticles);
|
||||||
out.putBoolean("canLoadMore", m_canLoadMore);
|
|
||||||
out.putBoolean("combinedMode", m_combinedMode);
|
out.putBoolean("combinedMode", m_combinedMode);
|
||||||
out.putCharSequence("searchQuery", m_searchQuery);
|
out.putCharSequence("searchQuery", m_searchQuery);
|
||||||
}
|
}
|
||||||
@ -431,7 +435,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
getActivity().setProgressBarIndeterminateVisibility(showProgress);
|
getActivity().setProgressBarIndeterminateVisibility(showProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HeadlinesRequest extends ApiRequest {
|
/* private class HeadlinesRequest extends ApiRequest {
|
||||||
int m_offset = 0;
|
int m_offset = 0;
|
||||||
|
|
||||||
public HeadlinesRequest(Context context) {
|
public HeadlinesRequest(Context context) {
|
||||||
@ -494,7 +498,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
public void setOffset(int skip) {
|
public void setOffset(int skip) {
|
||||||
m_offset = skip;
|
m_offset = skip;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
private class ArticleListAdapter extends ArrayAdapter<Article> {
|
private class ArticleListAdapter extends ArrayAdapter<Article> {
|
||||||
private ArrayList<Article> items;
|
private ArrayList<Article> items;
|
||||||
@ -842,7 +846,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||||
if (!m_refreshInProgress && m_canLoadMore && firstVisibleItem + visibleItemCount == m_articles.size()) {
|
if (!m_refreshInProgress && m_articles.findById(-1) != null && firstVisibleItem + visibleItemCount == m_articles.size()) {
|
||||||
refresh(true);
|
refresh(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -864,6 +868,10 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSearchQuery() {
|
||||||
|
return m_searchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSearchQuery(String query) {
|
public void setSearchQuery(String query) {
|
||||||
if (!m_searchQuery.equals(query)) {
|
if (!m_searchQuery.equals(query)) {
|
||||||
m_searchQuery = query;
|
m_searchQuery = query;
|
||||||
|
@ -131,7 +131,7 @@ public class OnlineActivity extends CommonActivity {
|
|||||||
setContentView(R.layout.online);
|
setContentView(R.layout.online);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void login() {
|
public void login() {
|
||||||
if (m_prefs.getString("ttrss_url", "").trim().length() == 0) {
|
if (m_prefs.getString("ttrss_url", "").trim().length() == 0) {
|
||||||
|
|
||||||
setLoadingStatus(R.string.login_need_configure, false);
|
setLoadingStatus(R.string.login_need_configure, false);
|
||||||
|
@ -1,257 +0,0 @@
|
|||||||
package org.fox.ttrss.util;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.auth.AuthScope;
|
|
||||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
|
||||||
import org.apache.http.client.CredentialsProvider;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
|
||||||
import org.apache.http.client.protocol.ClientContext;
|
|
||||||
import org.apache.http.conn.scheme.Scheme;
|
|
||||||
import org.apache.http.entity.StringEntity;
|
|
||||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
|
||||||
import org.apache.http.protocol.BasicHttpContext;
|
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
import org.fox.ttrss.R;
|
|
||||||
import org.fox.ttrss.R.string;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.http.AndroidHttpClient;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
|
|
||||||
public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonElement> {
|
|
||||||
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, INCORRECT_USAGE };
|
|
||||||
|
|
||||||
public static final int API_STATUS_OK = 0;
|
|
||||||
public static final int API_STATUS_ERR = 1;
|
|
||||||
|
|
||||||
private String m_api;
|
|
||||||
private boolean m_trustAny = false;
|
|
||||||
private boolean m_transportDebugging = false;
|
|
||||||
protected int m_httpStatusCode = 0;
|
|
||||||
protected int m_apiStatusCode = 0;
|
|
||||||
protected Context m_context;
|
|
||||||
private SharedPreferences m_prefs;
|
|
||||||
|
|
||||||
protected ApiError m_lastError;
|
|
||||||
|
|
||||||
public ApiRequest(Context context) {
|
|
||||||
m_context = context;
|
|
||||||
|
|
||||||
m_prefs = PreferenceManager.getDefaultSharedPreferences(m_context);
|
|
||||||
|
|
||||||
m_api = m_prefs.getString("ttrss_url", null).trim();
|
|
||||||
m_trustAny = m_prefs.getBoolean("ssl_trust_any", false);
|
|
||||||
m_transportDebugging = m_prefs.getBoolean("transport_debugging", false);
|
|
||||||
m_lastError = ApiError.NO_ERROR;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getErrorMessage() {
|
|
||||||
switch (m_lastError) {
|
|
||||||
case NO_ERROR:
|
|
||||||
return R.string.error_unknown;
|
|
||||||
case HTTP_UNAUTHORIZED:
|
|
||||||
return R.string.error_http_unauthorized;
|
|
||||||
case HTTP_FORBIDDEN:
|
|
||||||
return R.string.error_http_forbidden;
|
|
||||||
case HTTP_NOT_FOUND:
|
|
||||||
return R.string.error_http_not_found;
|
|
||||||
case HTTP_SERVER_ERROR:
|
|
||||||
return R.string.error_http_server_error;
|
|
||||||
case HTTP_OTHER_ERROR:
|
|
||||||
return R.string.error_http_other_error;
|
|
||||||
case SSL_REJECTED:
|
|
||||||
return R.string.error_ssl_rejected;
|
|
||||||
case PARSE_ERROR:
|
|
||||||
return R.string.error_parse_error;
|
|
||||||
case IO_ERROR:
|
|
||||||
return R.string.error_io_error;
|
|
||||||
case OTHER_ERROR:
|
|
||||||
return R.string.error_other_error;
|
|
||||||
case API_DISABLED:
|
|
||||||
return R.string.error_api_disabled;
|
|
||||||
case API_UNKNOWN:
|
|
||||||
return R.string.error_api_unknown;
|
|
||||||
case LOGIN_FAILED:
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected JsonElement doInBackground(HashMap<String, String>... params) {
|
|
||||||
|
|
||||||
Gson gson = new Gson();
|
|
||||||
|
|
||||||
String requestStr = gson.toJson(new HashMap<String,String>(params[0]));
|
|
||||||
|
|
||||||
if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api);
|
|
||||||
|
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS");
|
|
||||||
|
|
||||||
if (m_trustAny) {
|
|
||||||
client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", new EasySSLSocketFactory(), 443));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
HttpPost httpPost;
|
|
||||||
|
|
||||||
try {
|
|
||||||
httpPost = new HttpPost(m_api + "/api/");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
m_lastError = ApiError.INVALID_URL;
|
|
||||||
e.printStackTrace();
|
|
||||||
client.close();
|
|
||||||
return null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
m_lastError = ApiError.OTHER_ERROR;
|
|
||||||
e.printStackTrace();
|
|
||||||
client.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpContext context = null;
|
|
||||||
|
|
||||||
String httpLogin = m_prefs.getString("http_login", "").trim();
|
|
||||||
String httpPassword = m_prefs.getString("http_password", "").trim();
|
|
||||||
|
|
||||||
if (httpLogin.length() > 0) {
|
|
||||||
if (m_transportDebugging) Log.d(TAG, "Using HTTP Basic authentication.");
|
|
||||||
|
|
||||||
URL targetUrl;
|
|
||||||
try {
|
|
||||||
targetUrl = new URL(m_api);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
m_lastError = ApiError.INVALID_URL;
|
|
||||||
e.printStackTrace();
|
|
||||||
client.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpHost targetHost = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), targetUrl.getProtocol());
|
|
||||||
CredentialsProvider cp = new BasicCredentialsProvider();
|
|
||||||
context = new BasicHttpContext();
|
|
||||||
|
|
||||||
cp.setCredentials(
|
|
||||||
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
|
|
||||||
new UsernamePasswordCredentials(httpLogin, httpPassword));
|
|
||||||
|
|
||||||
context.setAttribute(ClientContext.CREDS_PROVIDER, cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
httpPost.setEntity(new StringEntity(requestStr, "utf-8"));
|
|
||||||
HttpResponse execute = client.execute(httpPost, context);
|
|
||||||
|
|
||||||
m_httpStatusCode = execute.getStatusLine().getStatusCode();
|
|
||||||
|
|
||||||
switch (m_httpStatusCode) {
|
|
||||||
case 200:
|
|
||||||
InputStream content = execute.getEntity().getContent();
|
|
||||||
|
|
||||||
BufferedReader buffer = new BufferedReader(
|
|
||||||
new InputStreamReader(content), 8192);
|
|
||||||
|
|
||||||
String s = "";
|
|
||||||
String response = "";
|
|
||||||
|
|
||||||
while ((s = buffer.readLine()) != null) {
|
|
||||||
response += s;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_transportDebugging) Log.d(TAG, "<<< " + response);
|
|
||||||
|
|
||||||
JsonParser parser = new JsonParser();
|
|
||||||
|
|
||||||
JsonElement result = parser.parse(response);
|
|
||||||
JsonObject resultObj = result.getAsJsonObject();
|
|
||||||
|
|
||||||
m_apiStatusCode = resultObj.get("status").getAsInt();
|
|
||||||
|
|
||||||
client.close();
|
|
||||||
|
|
||||||
switch (m_apiStatusCode) {
|
|
||||||
case API_STATUS_OK:
|
|
||||||
return result.getAsJsonObject().get("content");
|
|
||||||
case API_STATUS_ERR:
|
|
||||||
JsonObject contentObj = resultObj.get("content").getAsJsonObject();
|
|
||||||
String error = contentObj.get("error").getAsString();
|
|
||||||
|
|
||||||
if (error.equals("LOGIN_ERROR")) {
|
|
||||||
m_lastError = ApiError.LOGIN_FAILED;
|
|
||||||
} else if (error.equals("API_DISABLED")) {
|
|
||||||
m_lastError = ApiError.API_DISABLED;
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
case 401:
|
|
||||||
m_lastError = ApiError.HTTP_UNAUTHORIZED;
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
m_lastError = ApiError.HTTP_FORBIDDEN;
|
|
||||||
break;
|
|
||||||
case 404:
|
|
||||||
m_lastError = ApiError.HTTP_NOT_FOUND;
|
|
||||||
break;
|
|
||||||
case 500:
|
|
||||||
m_lastError = ApiError.HTTP_SERVER_ERROR;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
m_lastError = ApiError.HTTP_OTHER_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.close();
|
|
||||||
return null;
|
|
||||||
} catch (javax.net.ssl.SSLPeerUnverifiedException e) {
|
|
||||||
m_lastError = ApiError.SSL_REJECTED;
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IOException e) {
|
|
||||||
m_lastError = ApiError.IO_ERROR;
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (com.google.gson.JsonSyntaxException e) {
|
|
||||||
m_lastError = ApiError.PARSE_ERROR;
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
m_lastError = ApiError.OTHER_ERROR;
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
client.close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
85
src/org/fox/ttrss/util/HeadlinesRequest.java
Normal file
85
src/org/fox/ttrss/util/HeadlinesRequest.java
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package org.fox.ttrss.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.fox.ttrss.ApiRequest;
|
||||||
|
import org.fox.ttrss.OnlineActivity;
|
||||||
|
import org.fox.ttrss.R;
|
||||||
|
import org.fox.ttrss.TinyApplication;
|
||||||
|
import org.fox.ttrss.ApiRequest.ApiError;
|
||||||
|
import org.fox.ttrss.types.Article;
|
||||||
|
import org.fox.ttrss.types.ArticleList;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.widget.Adapter;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
|
public class HeadlinesRequest extends ApiRequest {
|
||||||
|
public static final int HEADLINES_REQUEST_SIZE = 30;
|
||||||
|
public static final int HEADLINES_BUFFER_MAX = 500;
|
||||||
|
|
||||||
|
private int m_offset = 0;
|
||||||
|
private OnlineActivity m_activity;
|
||||||
|
private ArticleList m_articles = TinyApplication.getInstance().m_loadedArticles;
|
||||||
|
|
||||||
|
public HeadlinesRequest(Context context, OnlineActivity activity) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
m_activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(JsonElement result) {
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
JsonArray content = result.getAsJsonArray();
|
||||||
|
if (content != null) {
|
||||||
|
Type listType = new TypeToken<List<Article>>() {}.getType();
|
||||||
|
final List<Article> articles = new Gson().fromJson(content, listType);
|
||||||
|
|
||||||
|
while (m_articles.size() > HEADLINES_BUFFER_MAX)
|
||||||
|
m_articles.remove(0);
|
||||||
|
|
||||||
|
if (m_offset == 0)
|
||||||
|
m_articles.clear();
|
||||||
|
else
|
||||||
|
if (m_articles.get(m_articles.size()-1).id == -1)
|
||||||
|
m_articles.remove(m_articles.size()-1); // remove previous placeholder
|
||||||
|
|
||||||
|
for (Article f : articles)
|
||||||
|
m_articles.add(f);
|
||||||
|
|
||||||
|
if (articles.size() == HEADLINES_REQUEST_SIZE) {
|
||||||
|
Article placeholder = new Article(-1);
|
||||||
|
m_articles.add(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_articles.size() == 0)
|
||||||
|
m_activity.setLoadingStatus(R.string.no_headlines_to_display, false);
|
||||||
|
else
|
||||||
|
m_activity.setLoadingStatus(R.string.blank, false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_lastError == ApiError.LOGIN_FAILED) {
|
||||||
|
m_activity.login();
|
||||||
|
} else {
|
||||||
|
m_activity.setLoadingStatus(getErrorMessage(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOffset(int skip) {
|
||||||
|
m_offset = skip;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user