diff options
author | Andrew Dolgov <[email protected]> | 2017-05-31 18:32:26 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2017-05-31 18:32:26 +0300 |
commit | 56a0993a90f722e487e92b1327360773b0b0dc99 (patch) | |
tree | e43322841a6e56aac28724d7520b835c73c68793 | |
parent | 432228011c962e8e2436ff9884b9ad6145a90f7a (diff) | |
parent | e4a8c575d4a29ed07c069218ec6d1248d55d2c27 (diff) |
Merge branch 'master' of git.fakecake.org:tt-rss-android
15 files changed, 694 insertions, 850 deletions
diff --git a/org.fox.ttrss/src/main/AndroidManifest.xml b/org.fox.ttrss/src/main/AndroidManifest.xml index ca404254..571f7d48 100755 --- a/org.fox.ttrss/src/main/AndroidManifest.xml +++ b/org.fox.ttrss/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.fox.ttrss" - android:versionCode="424" - android:versionName="1.190" > + android:versionCode="425" + android:versionName="1.191" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java new file mode 100644 index 00000000..b7308000 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java @@ -0,0 +1,125 @@ +package org.fox.ttrss; + +import android.os.Build; +import android.util.Log; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +public class ApiCommon { + public static final String TAG = "ApiCommon"; + + public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, + HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, SSL_HOSTNAME_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, + API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD } + + public static int getErrorMessage(ApiError error) { + switch (error) { + 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 SSL_HOSTNAME_REJECTED: + return R.string.error_ssl_hostname_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 API_UNKNOWN_METHOD: + return R.string.error_api_unknown_method; + case LOGIN_FAILED: + return R.string.error_login_failed; + case INVALID_URL: + return R.string.error_invalid_api_url; + case API_INCORRECT_USAGE: + return R.string.error_api_incorrect_usage; + case NETWORK_UNAVAILABLE: + return R.string.error_network_unavailable; + default: + Log.d(TAG, "getErrorMessage: unknown error code=" + error); + return R.string.error_unknown; + } + + } + + public static void trustAllHosts(boolean trustAnyCert, boolean trustAnyHost) { + try { + if (trustAnyCert) { + X509TrustManager easyTrustManager = new X509TrustManager() { + + public void checkClientTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + // Oh, I am easy! + } + + public void checkServerTrusted( + X509Certificate[] chain, + String authType) throws CertificateException { + // Oh, I am easy! + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + }; + + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; + + // Install the all-trusting trust manager + + SSLContext sc = SSLContext.getInstance("TLS"); + + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } + + if (trustAnyHost) { + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("deprecation") + protected static void disableConnectionReuseIfNecessary() { + // HTTP connection reuse which was buggy pre-froyo + if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { + System.setProperty("http.keepAlive", "false"); + } + } + +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java new file mode 100644 index 00000000..414a8a9d --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java @@ -0,0 +1,261 @@ +package org.fox.ttrss; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.preference.PreferenceManager; +import android.support.v4.content.AsyncTaskLoader; +import android.util.Base64; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import org.fox.ttrss.ApiCommon.ApiError; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; + +public class ApiLoader extends AsyncTaskLoader<JsonElement> { + private final String TAG = this.getClass().getSimpleName(); + + + public static final int API_STATUS_OK = 0; + public static final int API_STATUS_ERR = 1; + + private String m_api; + private boolean m_transportDebugging = false; + protected int m_responseCode = 0; + protected String m_responseMessage; + protected int m_apiStatusCode = 0; + protected boolean m_canUseProgress = false; + protected Context m_context; + private SharedPreferences m_prefs; + protected String m_lastErrorMessage; + protected ApiError m_lastError; + protected HashMap<String,String> m_params; + protected JsonElement m_data; + + public ApiLoader(Context context, HashMap<String,String> params) { + super(context); + + m_context = context; + + m_prefs = PreferenceManager.getDefaultSharedPreferences(m_context); + + m_api = m_prefs.getString("ttrss_url", "").trim(); + m_transportDebugging = m_prefs.getBoolean("transport_debugging", false); + m_lastError = ApiError.NO_ERROR; + m_params = params; + + } + + @Override + protected void onStartLoading() { + if (m_data != null) { + deliverResult(m_data); + } else { + forceLoad(); + } + } + + @Override + public void deliverResult(JsonElement data) { + m_data = data; + + super.deliverResult(data); + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + public ApiError getLastError() { + return m_lastError; + } + + public String getLastErrorMessage() { + return m_lastErrorMessage; + } + + @Override + public JsonElement loadInBackground() { + + if (!isNetworkAvailable()) { + m_lastError = ApiError.NETWORK_UNAVAILABLE; + return null; + } + + Gson gson = new Gson(); + + String requestStr = gson.toJson(new HashMap<>(m_params)); + byte[] postData = null; + + try { + postData = requestStr.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + m_lastError = ApiError.OTHER_ERROR; + e.printStackTrace(); + return null; + } + + if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api); + + URL url; + + try { + url = new URL(m_api + "/api/"); + } catch (Exception e) { + m_lastError = ApiError.INVALID_URL; + e.printStackTrace(); + return null; + } + + try { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + + 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."); + + conn.setRequestProperty("Authorization", "Basic " + + Base64.encodeToString((httpLogin + ":" + httpPassword).getBytes("UTF-8"), Base64.NO_WRAP)); + } + + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setUseCaches(false); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Length", Integer.toString(postData.length)); + + OutputStream out = conn.getOutputStream(); + out.write(postData); + out.close(); + + m_responseCode = conn.getResponseCode(); + m_responseMessage = conn.getResponseMessage(); + + switch (m_responseCode) { + case HttpURLConnection.HTTP_OK: + StringBuffer response = new StringBuffer(); + InputStreamReader in = new InputStreamReader(conn.getInputStream(), "UTF-8"); + char[] buf = new char[256]; + int read = 0; + int total = 0; + + int contentLength = conn.getHeaderFieldInt("Api-Content-Length", -1); + + m_canUseProgress = (contentLength != -1); + + while ((read = in.read(buf)) >= 0) { + response.append(buf, 0, read); + total += read; + //publishProgress(Integer.valueOf(total), Integer.valueOf(contentLength)); + } + + if (m_transportDebugging) Log.d(TAG, "<<< " + response); + + JsonParser parser = new JsonParser(); + + JsonElement result = parser.parse(response.toString()); + JsonObject resultObj = result.getAsJsonObject(); + + m_apiStatusCode = resultObj.get("status").getAsInt(); + + conn.disconnect(); + + 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.API_INCORRECT_USAGE; + } else if (error.equals("UNKNOWN_METHOD")) { + m_lastError = ApiError.API_UNKNOWN_METHOD; + } else { + Log.d(TAG, "Unknown API error: " + error); + m_lastError = ApiError.API_UNKNOWN; + } + } + + return null; + case HttpURLConnection.HTTP_UNAUTHORIZED: + m_lastError = ApiError.HTTP_UNAUTHORIZED; + break; + case HttpURLConnection.HTTP_FORBIDDEN: + m_lastError = ApiError.HTTP_FORBIDDEN; + break; + case HttpURLConnection.HTTP_NOT_FOUND: + m_lastError = ApiError.HTTP_NOT_FOUND; + break; + case HttpURLConnection.HTTP_INTERNAL_ERROR: + m_lastError = ApiError.HTTP_SERVER_ERROR; + break; + default: + Log.d(TAG, "HTTP response code: " + m_responseCode + "(" + m_responseMessage + ")"); + m_lastError = ApiError.HTTP_OTHER_ERROR; + break; + } + + conn.disconnect(); + return null; + } catch (javax.net.ssl.SSLPeerUnverifiedException e) { + m_lastError = ApiError.SSL_REJECTED; + m_lastErrorMessage = e.getMessage(); + e.printStackTrace(); + } catch (IOException e) { + m_lastError = ApiError.IO_ERROR; + m_lastErrorMessage = e.getMessage(); + + if (e.getMessage() != null) { + if (e.getMessage().matches("Hostname [^ ]+ was not verified")) { + m_lastError = ApiError.SSL_HOSTNAME_REJECTED; + } + } + + e.printStackTrace(); + } catch (com.google.gson.JsonSyntaxException e) { + m_lastError = ApiError.PARSE_ERROR; + m_lastErrorMessage = e.getMessage(); + e.printStackTrace(); + } catch (Exception e) { + m_lastError = ApiError.OTHER_ERROR; + m_lastErrorMessage = e.getMessage(); + e.printStackTrace(); + } + + return null; + } + + protected boolean isNetworkAvailable() { + ConnectivityManager cm = (ConnectivityManager) + m_context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + + // if no network is available networkInfo will be null + // otherwise check if we are connected + if (networkInfo != null && networkInfo.isConnected()) { + return true; + } + return false; + } +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java index 03a2769f..ab52a0c3 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java @@ -22,24 +22,13 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.HashMap; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; +import static org.fox.ttrss.ApiCommon.ApiError; 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, SSL_HOSTNAME_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, - API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD } - public static final int API_STATUS_OK = 0; public static final int API_STATUS_ERR = 1; @@ -76,47 +65,7 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE } public 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 SSL_HOSTNAME_REJECTED: - return R.string.error_ssl_hostname_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 API_UNKNOWN_METHOD: - return R.string.error_api_unknown_method; - case LOGIN_FAILED: - return R.string.error_login_failed; - case INVALID_URL: - return R.string.error_invalid_api_url; - case API_INCORRECT_USAGE: - return R.string.error_api_incorrect_usage; - case NETWORK_UNAVAILABLE: - return R.string.error_network_unavailable; - default: - Log.d(TAG, "getErrorMessage: unknown error code=" + m_lastError); - return R.string.error_unknown; - } + return ApiCommon.getErrorMessage(m_lastError); } @Override @@ -140,13 +89,8 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE return null; } - /* disableConnectionReuseIfNecessary(); */ - if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api); - /* ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), - m_prefs.getBoolean("ssl_trust_any_host", false)); */ - URL url; try { @@ -283,64 +227,7 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE return null; } - - public static void trustAllHosts(boolean trustAnyCert, boolean trustAnyHost) { - try { - if (trustAnyCert) { - X509TrustManager easyTrustManager = new X509TrustManager() { - - public void checkClientTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public void checkServerTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } - - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - }; - - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; - - // Install the all-trusting trust manager - - SSLContext sc = SSLContext.getInstance("TLS"); - - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - } - - if (trustAnyHost) { - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - @SuppressWarnings("deprecation") - protected static void disableConnectionReuseIfNecessary() { - // HTTP connection reuse which was buggy pre-froyo - if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) { - System.setProperty("http.keepAlive", "false"); - } - } - + protected boolean isNetworkAvailable() { ConnectivityManager cm = (ConnectivityManager) m_context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java index ac621191..c6133f8f 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java @@ -3,7 +3,6 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.app.Activity; import android.content.SharedPreferences; -import android.net.Uri; import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; @@ -216,7 +215,7 @@ public class ArticlePager extends Fragment { } } else { - if (m_lastError == ApiError.LOGIN_FAILED) { + if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(true); } else { m_activity.toast(getErrorMessage()); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java index 24be6292..750fb91c 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java @@ -10,6 +10,8 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.util.TypedValue; @@ -43,7 +45,8 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; -public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener { +public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener, + LoaderManager.LoaderCallbacks<JsonElement> { private final String TAG = this.getClass().getSimpleName(); private FeedCategoryListAdapter m_adapter; private FeedCategoryList m_cats = new FeedCategoryList(); @@ -53,6 +56,131 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt private ListView m_list; protected SharedPreferences m_prefs; + @Override + public Loader<JsonElement> onCreateLoader(int id, Bundle args) { + final String sessionId = m_activity.getSessionId(); + final boolean unreadOnly = m_activity.getUnreadOnly(); + + @SuppressWarnings("serial") + HashMap<String, String> params = new HashMap<String, String>() { + { + put("op", "getCategories"); + put("sid", sessionId); + put("enable_nested", "true"); + if (unreadOnly) { + put("unread_only", String.valueOf(unreadOnly)); + } + } + }; + + return new ApiLoader(getContext(), params); + } + + @Override + public void onLoadFinished(Loader<JsonElement> loader, JsonElement result) { + Log.d(TAG, "onLoadFinished: " + loader + " " + result); + + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); + + View loadingBar = getView().findViewById(R.id.feeds_loading_bar); + + if (loadingBar != null) { + loadingBar.setVisibility(View.INVISIBLE); + } + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + Type listType = new TypeToken<List<FeedCategory>>() {}.getType(); + final List<FeedCategory> cats = new Gson().fromJson(content, listType); + + m_cats.clear(); + + int apiLevel = m_activity.getApiLevel(); + + boolean specialCatFound = false; + + // virtual cats implemented in getCategories since api level 1 + if (apiLevel == 0) { + m_cats.add(new FeedCategory(-1, "Special", 0)); + m_cats.add(new FeedCategory(-2, "Labels", 0)); + m_cats.add(new FeedCategory(0, "Uncategorized", 0)); + + specialCatFound = true; + } + + for (FeedCategory c : cats) { + if (c.id == -1) { + specialCatFound = true; + } + } + + m_cats.addAll(cats); + + sortCats(); + + if (!specialCatFound) { + m_cats.add(0, new FeedCategory(-1, "Special", 0)); + } + + //m_adapter.notifyDataSetChanged(); (done by sortCats) + //m_activity.setLoadingStatus(R.string.blank, false); + + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + ApiLoader al = (ApiLoader) loader; + + if (al.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(true); + } else { + if (al.getLastErrorMessage() != null) { + m_activity.toast(getString(al.getErrorMessage()) + "\n" + al.getLastErrorMessage()); + } else { + m_activity.toast(al.getErrorMessage()); + } + } + } + + public void sortCats() { + Comparator<FeedCategory> cmp; + + if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { + cmp = new CatUnreadComparator(); + } else { + if (m_activity.getApiLevel() >= 3) { + cmp = new CatOrderComparator(); + } else { + cmp = new CatTitleComparator(); + } + } + + try { + Collections.sort(m_cats, cmp); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + try { + m_adapter.notifyDataSetChanged(); + } catch (NullPointerException e) { + // adapter missing + } + } + + @Override + public void onLoaderReset(Loader<JsonElement> loader) { + Log.d(TAG, "onLoaderReset: " + loader); + + m_cats.clear(); + m_adapter.notifyDataSetChanged(); + } + @SuppressLint("DefaultLocale") class CatUnreadComparator implements Comparator<FeedCategory> { @Override @@ -199,7 +327,7 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState != null) { m_selectedCat = savedInstanceState.getParcelable("selectedCat"); - m_cats = savedInstanceState.getParcelable("cats"); + //m_cats = savedInstanceState.getParcelable("cats"); } View view = inflater.inflate(R.layout.fragment_cats, container, false); @@ -247,9 +375,9 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt @Override public void onResume() { super.onResume(); - - refresh(false); - + + getLoaderManager().initLoader(0, null, this); + m_activity.invalidateOptionsMenu(); } @@ -259,165 +387,15 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt out.setClassLoader(getClass().getClassLoader()); out.putParcelable("selectedCat", m_selectedCat); - out.putParcelable("cats", m_cats); + //out.putParcelable("cats", m_cats); } - /* private void setLoadingStatus(int status, boolean showProgress) { - if (getView() != null) { - TextView tv = (TextView)getView().findViewById(R.id.loading_message); - - if (tv != null) { - tv.setText(status); - } - } - - m_activity.setProgressBarIndeterminateVisibility(showProgress); - } */ - public void refresh(boolean background) { - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - CatsRequest req = new CatsRequest(getActivity().getApplicationContext()); - - final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = m_activity.getUnreadOnly(); - - if (sessionId != null) { - //m_activity.setLoadingStatus(R.string.blank, true); - //m_activity.setProgressBarVisibility(true); - - @SuppressWarnings("serial") - HashMap<String,String> map = new HashMap<String,String>() { - { - put("op", "getCategories"); - put("sid", sessionId); - put("enable_nested", "true"); - if (unreadOnly) { - put("unread_only", String.valueOf(unreadOnly)); - } - } - }; - - req.execute(map); - } - } - - private class CatsRequest extends ApiRequest { - - public CatsRequest(Context context) { - super(context); - } - - @Override - protected void onProgressUpdate(Integer... progress) { - m_activity.setProgress(Math.round((((float)progress[0] / (float)progress[1]) * 10000))); - } - - @Override - protected void onPostExecute(JsonElement result) { - if (isDetached() || !isAdded()) return; - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - - if (getView() != null) { - View loadingBar = getView().findViewById(R.id.feeds_loading_bar); - - if (loadingBar != null) { - loadingBar.setVisibility(View.INVISIBLE); - } - } - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - Type listType = new TypeToken<List<FeedCategory>>() {}.getType(); - final List<FeedCategory> cats = new Gson().fromJson(content, listType); - - m_cats.clear(); - - int apiLevel = m_activity.getApiLevel(); - - boolean specialCatFound = false; - - // virtual cats implemented in getCategories since api level 1 - if (apiLevel == 0) { - m_cats.add(new FeedCategory(-1, "Special", 0)); - m_cats.add(new FeedCategory(-2, "Labels", 0)); - m_cats.add(new FeedCategory(0, "Uncategorized", 0)); - - specialCatFound = true; - } - - for (FeedCategory c : cats) { - if (c.id == -1) { - specialCatFound = true; - } - - m_cats.add(c); - } - - sortCats(); - - if (!specialCatFound) { - m_cats.add(0, new FeedCategory(-1, "Special", 0)); - } - - /* if (m_cats.size() == 0) - setLoadingStatus(R.string.no_feeds_to_display, false); - else */ - - //m_adapter.notifyDataSetChanged(); (done by sortCats) - //m_activity.setLoadingStatus(R.string.blank, false); - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (m_lastError == ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - - if (m_lastErrorMessage != null) { - m_activity.toast(getString(getErrorMessage()) + "\n" + m_lastErrorMessage); - } else { - m_activity.toast(getErrorMessage()); - } - - //m_activity.setLoadingStatus(getErrorMessage(), false); - } - } + if (!isAdded()) return; - } + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - public void sortCats() { - Comparator<FeedCategory> cmp; - - if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { - cmp = new CatUnreadComparator(); - } else { - if (m_activity.getApiLevel() >= 3) { - cmp = new CatOrderComparator(); - } else { - cmp = new CatTitleComparator(); - } - } - - try { - Collections.sort(m_cats, cmp); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - try { - m_adapter.notifyDataSetChanged(); - } catch (NullPointerException e) { - // adapter missing - } - + getLoaderManager().restartLoader(0, null, this).forceLoad(); } private class FeedCategoryListAdapter extends ArrayAdapter<FeedCategory> { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java index 6d567ff8..a15ffb0d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java @@ -11,6 +11,8 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.graphics.Typeface; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.util.TypedValue; @@ -45,7 +47,8 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; -public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener { +public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener, + LoaderManager.LoaderCallbacks<JsonElement> { private final String TAG = this.getClass().getSimpleName(); private SharedPreferences m_prefs; private FeedListAdapter m_adapter; @@ -53,9 +56,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi private MasterActivity m_activity; private Feed m_selectedFeed; private FeedCategory m_activeCategory; - private static final String ICON_PATH = "/icons/"; - //private boolean m_enableFeedIcons; - private boolean m_feedIconsChecked = false; private SwipeRefreshLayout m_swipeLayout; private boolean m_enableParentBtn = false; private ListView m_list; @@ -64,7 +64,114 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_activeCategory = cat; m_enableParentBtn = enableParentBtn; } - + + @Override + public Loader<JsonElement> onCreateLoader(int id, Bundle args) { + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); + + final int catId = (m_activeCategory != null) ? m_activeCategory.id : -4; + + final String sessionId = m_activity.getSessionId(); + final boolean unreadOnly = m_activity.getUnreadOnly() && (m_activeCategory == null || m_activeCategory.id != -1); + + HashMap<String,String> params = new HashMap<String,String>() { + { + put("op", "getFeeds"); + put("sid", sessionId); + put("include_nested", "true"); + put("cat_id", String.valueOf(catId)); + if (unreadOnly) { + put("unread_only", String.valueOf(unreadOnly)); + } + } + }; + + return new FeedsLoader(getActivity().getApplicationContext(), params, catId); + } + + @Override + public void onLoadFinished(Loader<JsonElement> loader, JsonElement result) { + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); + + View loadingBar = getView().findViewById(R.id.feeds_loading_bar); + + if (loadingBar != null) { + loadingBar.setVisibility(View.INVISIBLE); + } + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + + Type listType = new TypeToken<List<Feed>>() {}.getType(); + final List<Feed> feeds = new Gson().fromJson(content, listType); + + m_feeds.clear(); + + int catUnread = 0; + + int catId = ((FeedsLoader) loader).getCatId(); + + for (Feed f : feeds) + if (f.id > -10 || catId != -4) { // skip labels for flat feedlist for now + if (m_activeCategory != null || f.id >= 0) { + m_feeds.add(f); + catUnread += f.unread; + } + } + + sortFeeds(); + + if (m_activeCategory == null) { + Feed feed = new Feed(-1, "Special", true); + feed.unread = catUnread; + + m_feeds.add(0, feed); + m_adapter.notifyDataSetChanged(); + + } + + if (m_enableParentBtn && m_activeCategory != null && m_activeCategory.id >= 0 && m_feeds.size() > 0) { + Feed feed = new Feed(m_activeCategory.id, m_activeCategory.title, true); + feed.unread = catUnread; + feed.always_display_as_feed = true; + feed.display_title = getString(R.string.feed_all_articles); + + m_feeds.add(0, feed); + m_adapter.notifyDataSetChanged(); + } + + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + ApiLoader al = (ApiLoader) loader; + + if (al.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(true); + } else { + + if (al.getLastErrorMessage() != null) { + m_activity.toast(getString(al.getErrorMessage()) + "\n" + al.getLastErrorMessage()); + } else { + m_activity.toast(al.getErrorMessage()); + } + + //m_activity.setLoadingStatus(getErrorMessage(), false); + } + } + + @Override + public void onLoaderReset(Loader<JsonElement> loader) { + m_feeds.clear(); + m_adapter.notifyDataSetChanged(); + } + @SuppressLint("DefaultLocale") class FeedUnreadComparator implements Comparator<Feed> { @@ -227,7 +334,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - //ListView list = (ListView) getView().findViewById(R.id.feeds); Feed feed = (Feed) m_list.getItemAtPosition(info.position); menu.setHeaderTitle(feed.display_title != null ? feed.display_title : feed.title); @@ -249,8 +355,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi if (savedInstanceState != null) { m_selectedFeed = savedInstanceState.getParcelable("selectedFeed"); - m_feeds = savedInstanceState.getParcelable("feeds"); - m_feedIconsChecked = savedInstanceState.getBoolean("feedIconsChecked"); m_activeCategory = savedInstanceState.getParcelable("activeCat"); m_enableParentBtn = savedInstanceState.getBoolean("enableParentBtn"); } @@ -266,21 +370,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi } }); - /* Button parentBtn = (Button) view.findViewById(R.id.open_parent); - - if (parentBtn != null) { - if (m_enableParentBtn) { - parentBtn.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - m_activity.getSupportFragmentManager().popBackStack(); - } - }); - } else { - parentBtn.setVisibility(View.GONE); - } - } */ - m_list = (ListView)view.findViewById(R.id.feeds); initDrawerHeader(inflater, view, m_list, m_activity, m_prefs, !m_enableParentBtn); @@ -298,23 +387,18 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_list.addHeaderView(layout, null, false); } - m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, (ArrayList<Feed>)m_feeds); + m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, m_feeds); m_list.setAdapter(m_adapter); m_list.setOnItemClickListener(this); registerForContextMenu(m_list); - //m_enableFeedIcons = m_prefs.getBoolean("download_feed_icons", false); - - View loadingBar = (View) view.findViewById(R.id.feeds_loading_bar); + View loadingBar = view.findViewById(R.id.feeds_loading_bar); if (loadingBar != null) { loadingBar.setVisibility(View.VISIBLE); } - //Log.d(TAG, "mpTRA=" + m_activity.m_pullToRefreshAttacher); - //m_activity.m_pullToRefreshAttacher.addRefreshableView(list, this); - return view; } @@ -337,8 +421,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi @Override public void onResume() { super.onResume(); - - refresh(false); + + getLoaderManager().initLoader(0, null, this); m_activity.invalidateOptionsMenu(); } @@ -349,8 +433,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi out.setClassLoader(getClass().getClassLoader()); out.putParcelable("selectedFeed", m_selectedFeed); - out.putParcelable("feeds", m_feeds); - out.putBoolean("feedIconsChecked", m_feedIconsChecked); out.putParcelable("activeCat", m_activeCategory); out.putBoolean("enableParentBtn", m_enableParentBtn); } @@ -384,202 +466,11 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi @SuppressWarnings({ "serial" }) public void refresh(boolean background) { - //FeedCategory cat = m_onlineServices.getActiveCategory(); + if (!isAdded()) return; - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - final int catId = (m_activeCategory != null) ? m_activeCategory.id : -4; - - final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = m_activity.getUnreadOnly() && (m_activeCategory == null || m_activeCategory.id != -1); + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - FeedsRequest req = new FeedsRequest(getActivity().getApplicationContext(), catId); - - if (sessionId != null) { - //m_activity.setLoadingStatus(R.string.blank, true); - //m_activity.setProgressBarVisibility(true); - - HashMap<String,String> map = new HashMap<String,String>() { - { - put("op", "getFeeds"); - put("sid", sessionId); - put("include_nested", "true"); - put("cat_id", String.valueOf(catId)); - if (unreadOnly) { - put("unread_only", String.valueOf(unreadOnly)); - } - } - }; - - req.execute(map); - - } - } - - /* private void setLoadingStatus(int status, boolean showProgress) { - if (getView() != null) { - TextView tv = (TextView)getView().findViewById(R.id.loading_message); - - if (tv != null) { - tv.setText(status); - } - } - - if (getActivity() != null) - getActivity().setProgressBarIndeterminateVisibility(showProgress); - } */ - - @SuppressWarnings({ "serial" }) - /* public void getFeedIcons() { - - ApiRequest req = new ApiRequest(getActivity().getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - if (isDetached()) return; - - if (result != null) { - - try { - JsonElement iconsUrl = result.getAsJsonObject().get("icons_url"); - - if (iconsUrl != null) { - String iconsStr = iconsUrl.getAsString(); - String baseUrl = ""; - - if (!iconsStr.contains("://")) { - baseUrl = m_prefs.getString("ttrss_url", "").trim() + "/" + iconsStr; - } else { - baseUrl = iconsStr; - } - - GetIconsTask git = new GetIconsTask(baseUrl); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) - git.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, m_feeds); - else - git.execute(m_feeds); - - m_feedIconsChecked = true; - } - } catch (Exception e) { - Log.d(TAG, "Error receiving icons configuration"); - e.printStackTrace(); - } - - } - } - }; - - final String sessionId = m_activity.getSessionId(); - - HashMap<String,String> map = new HashMap<String,String>() { - { - put("sid", sessionId); - put("op", "getConfig"); - } - }; - - req.execute(map); - } */ - - private class FeedsRequest extends ApiRequest { - private int m_catId; - - public FeedsRequest(Context context, int catId) { - super(context); - m_catId = catId; - } - - @Override - protected void onProgressUpdate(Integer... progress) { - m_activity.setProgress(Math.round((((float)progress[0] / (float)progress[1]) * 10000))); - } - - @Override - protected void onPostExecute(JsonElement result) { - if (isDetached() || !isAdded()) return; - - if (getView() != null) { - View loadingBar = getView().findViewById(R.id.feeds_loading_bar); - - if (loadingBar != null) { - loadingBar.setVisibility(View.INVISIBLE); - } - } - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - - Type listType = new TypeToken<List<Feed>>() {}.getType(); - final List<Feed> feeds = new Gson().fromJson(content, listType); - - m_feeds.clear(); - - int catUnread = 0; - - for (Feed f : feeds) - if (f.id > -10 || m_catId != -4) { // skip labels for flat feedlist for now - if (m_activeCategory != null || f.id >= 0) { - m_feeds.add(f); - catUnread += f.unread; - } - } - - sortFeeds(); - - if (m_activeCategory == null) { - Feed feed = new Feed(-1, "Special", true); - feed.unread = catUnread; - - m_feeds.add(0, feed); - m_adapter.notifyDataSetChanged(); - - } - - if (m_enableParentBtn && m_activeCategory != null && m_activeCategory.id >= 0 && m_feeds.size() > 0) { - Feed feed = new Feed(m_activeCategory.id, m_activeCategory.title, true); - feed.unread = catUnread; - feed.always_display_as_feed = true; - feed.display_title = getString(R.string.feed_all_articles); - - m_feeds.add(0, feed); - m_adapter.notifyDataSetChanged(); - } - - /*if (m_feeds.size() == 0) - setLoadingStatus(R.string.no_feeds_to_display, false); - else */ - - //m_activity.setLoadingStatus(R.string.blank, false); - //m_adapter.notifyDataSetChanged(); (done by sortFeeds) - - /* if (m_enableFeedIcons && !m_feedIconsChecked && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) - getFeedIcons(); */ - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (m_lastError == ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - - if (m_lastErrorMessage != null) { - m_activity.toast(getString(getErrorMessage()) + "\n" + m_lastErrorMessage); - } else { - m_activity.toast(getErrorMessage()); - } - - //m_activity.setLoadingStatus(getErrorMessage(), false); - } - } + getLoaderManager().restartLoader(0, null, this).forceLoad(); } private class FeedListAdapter extends ArrayAdapter<Feed> { @@ -612,8 +503,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi if (!m_activity.isSmallScreen() && m_selectedFeed != null && feed.id == m_selectedFeed.id) { return VIEW_SELECTED; } else { - return VIEW_NORMAL; - } + return VIEW_NORMAL; + } } @Override @@ -624,13 +515,13 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi if (v == null) { int layoutId = R.layout.feeds_row; - + switch (getItemViewType(position)) { case VIEW_SELECTED: layoutId = R.layout.feeds_row_selected; break; } - + LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(layoutId, null); @@ -685,16 +576,16 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi tu.setText(String.valueOf(feed.unread)); tu.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); } - + /*ImageButton ib = (ImageButton) v.findViewById(R.id.feed_menu_button); - + if (ib != null) { - ib.setOnClickListener(new OnClickListener() { + ib.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { getActivity().openContextMenu(v); } - }); + }); }*/ return v; @@ -727,95 +618,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi // adapter missing } } - - /* public class GetIconsTask extends AsyncTask<FeedList, Integer, Integer> { - - private String m_baseUrl; - - public GetIconsTask(String baseUrl) { - m_baseUrl = baseUrl.trim(); - } - - @Override - protected Integer doInBackground(FeedList... params) { - - FeedList localList = new FeedList(); - - try { - localList.addAll(params[0]); - - File storage = m_activity.getExternalCacheDir(); - final File iconPath = new File(storage.getAbsolutePath() + ICON_PATH); - if (!iconPath.exists()) iconPath.mkdirs(); - - if (iconPath.exists()) { - for (Feed feed : localList) { - if (feed.id > 0 && feed.has_icon && !feed.is_cat) { - File outputFile = new File(iconPath.getAbsolutePath() + "/" + feed.id + ".ico"); - String fetchUrl = m_baseUrl + "/" + feed.id + ".ico"; - - if (!outputFile.exists()) { - downloadFile(fetchUrl, outputFile.getAbsolutePath()); - Thread.sleep(2000); - } - } - } - } - } catch (Exception e) { - Log.d(TAG, "Error while downloading feed icons"); - e.printStackTrace(); - } - return null; - } - - protected void downloadFile(String fetchUrl, String outputFile) { - AndroidHttpClient client = AndroidHttpClient.newInstance("Tiny Tiny RSS"); - - try { - URL url = new URL(fetchUrl); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - - conn.setConnectTimeout(1000); - conn.setReadTimeout(5000); - - Log.d(TAG, "[downloadFile] " + url); - - String httpLogin = m_prefs.getString("http_login", ""); - String httpPassword = m_prefs.getString("http_password", ""); - - if (httpLogin.length() > 0) { - conn.setRequestProperty("Authorization", "Basic " + - Base64.encodeToString((httpLogin + ":" + httpPassword).getBytes("UTF-8"), Base64.NO_WRAP)); - } - - InputStream content = conn.getInputStream(); - - BufferedInputStream is = new BufferedInputStream(content, 1024); - FileOutputStream fos = new FileOutputStream(outputFile); - - byte[] buffer = new byte[1024]; - int len = 0; - while ((len = is.read(buffer)) != -1) { - fos.write(buffer, 0, len); - } - - fos.close(); - is.close(); - - } catch (Exception e) { - e.printStackTrace(); - } - - client.close(); - } - - protected void onPostExecute(Integer result) { - if (isDetached()) return; - - m_adapter.notifyDataSetChanged(); - } - - } */ @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @@ -835,22 +637,4 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi return null; } } - - /* public Feed getSelectedFeed() { - return m_selectedFeed; - } - - public void setSelectedFeed(Feed feed) { - m_selectedFeed = feed; - - if (m_adapter != null) { - m_adapter.notifyDataSetChanged(); - } - } */ - - /* @Override - public void onRefreshStarted(View view) { - refresh(false); - } */ - } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java new file mode 100644 index 00000000..4df392f6 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java @@ -0,0 +1,19 @@ +package org.fox.ttrss; + +import android.content.Context; + +import java.util.HashMap; + +class FeedsLoader extends ApiLoader { + private int m_catId; + + public FeedsLoader(Context context, HashMap<String, String> params, int catId) { + super(context, params); + + m_catId = catId; + } + + public int getCatId() { + return m_catId; + } +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java index 1967f5cb..19fcaeed 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java @@ -36,7 +36,6 @@ import android.view.Display; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -63,7 +62,6 @@ import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationA import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView; import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager; import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback; -import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.SimpleSwipeUndoAdapter; import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.TimedUndoAdapter; import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter; import com.nostra13.universalimageloader.core.DisplayImageOptions; @@ -100,9 +98,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, public static final int HEADLINES_REQUEST_SIZE = 30; public static final int HEADLINES_BUFFER_MAX = 500; - //public static final int ARTICLE_SPECIAL_LOADMORE = -1; - //public static final int ARTICLE_SPECIAL_TOP_CHANGED = -3; - private final String TAG = this.getClass().getSimpleName(); private Feed m_feed; @@ -262,157 +257,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, return true; } - /*@Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); - - switch (item.getItemId()) { - case R.id.set_labels: - if (true) { - Article article = getArticleAtPosition(info.position); - - if (article != null) { - if (m_activity.getApiLevel() != 7) { - m_activity.editArticleLabels(article); - } else { - m_activity.toast(R.string.server_function_not_available); - } - } - } - return true; - case R.id.article_set_note: - if (true) { - Article article = getArticleAtPosition(info.position); - - if (article != null) { - m_activity.editArticleNote(article); - } - } - return true; - - case R.id.headlines_article_link_copy: - if (true) { - Article article = getArticleAtPosition(info.position); - - if (article != null) { - m_activity.copyToClipboard(article.link); - } - } - return true; - case R.id.headlines_article_link_open: - if (true) { - Article article = getArticleAtPosition(info.position); - - if (article != null) { - m_activity.openUri(Uri.parse(article.link)); - - if (article.unread) { - article.unread = false; - m_activity.saveArticleUnread(article); - } - } - } - return true; - case R.id.selection_toggle_marked: - if (true) { - ArticleList selected = getSelectedArticles(); - - if (selected.size() > 0) { - for (Article a : selected) - a.marked = !a.marked; - - m_activity.toggleArticlesMarked(selected); - //updateHeadlines(); - } else { - Article article = getArticleAtPosition(info.position); - if (article != null) { - article.marked = !article.marked; - m_activity.saveArticleMarked(article); - //updateHeadlines(); - } - } - m_adapter.notifyDataSetChanged(); - } - return true; - case R.id.selection_toggle_published: - if (true) { - ArticleList selected = getSelectedArticles(); - - if (selected.size() > 0) { - for (Article a : selected) - a.published = !a.published; - - m_activity.toggleArticlesPublished(selected); - //updateHeadlines(); - } else { - Article article = getArticleAtPosition(info.position); - if (article != null) { - article.published = !article.published; - m_activity.saveArticlePublished(article); - //updateHeadlines(); - } - } - m_adapter.notifyDataSetChanged(); - } - return true; - case R.id.selection_toggle_unread: - if (true) { - ArticleList selected = getSelectedArticles(); - - if (selected.size() > 0) { - for (Article a : selected) - a.unread = !a.unread; - - m_activity.toggleArticlesUnread(selected); - //updateHeadlines(); - } else { - Article article = getArticleAtPosition(info.position); - if (article != null) { - article.unread = !article.unread; - m_activity.saveArticleUnread(article); - //updateHeadlines(); - } - } - m_adapter.notifyDataSetChanged(); - } - return true; - case R.id.headlines_share_article: - if (true) { - Article article = getArticleAtPosition(info.position); - if (article != null) - m_activity.shareArticle(article); - } - return true; - case R.id.catchup_above: - if (true) { - Article article = getArticleAtPosition(info.position); - if (article != null) { - ArticleList articles = getAllArticles(); - ArticleList tmp = new ArticleList(); - for (Article a : articles) { - if (article.id == a.id) - break; - - if (a.unread) { - a.unread = false; - tmp.add(a); - } - } - if (tmp.size() > 0) { - m_activity.toggleArticlesUnread(tmp); - //updateHeadlines(); - } - } - m_adapter.notifyDataSetChanged(); - } - return true; - default: - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - } */ - public HeadlinesFragment() { super(); @@ -506,12 +350,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, } m_listLoadingView = inflater.inflate(R.layout.headlines_row_loadmore, m_list, false); - //m_list.addFooterView(m_listLoadingView, null, false); - //m_listLoadingView.setVisibility(View.GONE); - m_topChangedView = inflater.inflate(R.layout.headlines_row_top_changed, m_list, false); - //m_list.addFooterView(m_topChangedView, null, false); - //m_topChangedView.setVisibility(View.GONE);*/ if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { WindowManager wm = (WindowManager) m_activity.getSystemService(Context.WINDOW_SERVICE); @@ -539,7 +378,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, m_animationAdapter.setAbsListView(m_list); m_list.setAdapter(m_animationAdapter); - if (enableSwipeToDismiss) { + if (enableSwipeToDismiss && !m_prefs.getBoolean("headlines_mark_read_scroll", false)) { TimedUndoAdapter swipeUndoAdapter = new TimedUndoAdapter(m_adapter, m_activity, new OnDismissCallback() { @@ -604,32 +443,14 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, if (m_adapter != null) m_adapter.notifyDataSetChanged(); - /* if (Application.getInstance().m_activeArticle != null) { - m_activeArticle = Application.getInstance().m_activeArticle; - Application.getInstance().m_activeArticle = null; - } */ - if (m_activeArticle != null) { setActiveArticle(m_activeArticle); } - /* if (!(m_activity instanceof DetailActivity)) { - refresh(false); - } */ - if (m_articles.size() == 0) { refresh(false); } - /* if (m_articles.size() == 0 || !m_feed.equals(Application.getInstance().m_activeFeed)) { - if (m_activity.getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_ARTICLE) == null) { - refresh(false); - Application.getInstance().m_activeFeed = m_feed; - } - } else { - notifyUpdated(); - } */ - m_activity.invalidateOptionsMenu(); } @@ -681,10 +502,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - /* if (!m_feed.equals(Application.getInstance().m_activeFeed)) { - append = false; - } */ - // new stuff may appear on top, scroll back to show it if (!append) { if (getView() != null) { @@ -731,11 +548,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, if (m_firstIdChanged) { m_lazyLoadDisabled = true; - //m_activity.toast(R.string.headlines_row_top_changed); - - //m_topChangedView.setVisibility(View.VISIBLE); - //m_articles.add(new Article(ARTICLE_SPECIAL_TOP_CHANGED)); - m_list.addFooterView(m_topChangedView, null, false); } @@ -758,7 +570,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, //m_listLoadingView.setVisibility(m_amountLoaded == HEADLINES_REQUEST_SIZE ? View.VISIBLE : View.GONE); } else { - if (m_lastError == ApiError.LOGIN_FAILED) { + if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(true); } else { @@ -767,8 +579,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, } else { m_activity.toast(getErrorMessage()); } - - //m_activity.setLoadingStatus(getErrorMessage(), false); } } @@ -910,9 +720,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, public static final int VIEW_UNREAD = 1; public static final int VIEW_SELECTED = 2; public static final int VIEW_SELECTED_UNREAD = 3; - //public static final int VIEW_LOADMORE = 4; - //public static final int VIEW_TOP_CHANGED = 4; - + public static final int VIEW_COUNT = VIEW_SELECTED_UNREAD + 1; private final Integer[] origTitleColors = new Integer[VIEW_COUNT]; @@ -966,11 +774,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, public int getItemViewType(int position) { Article a = items.get(position); - /*if (a.id == ARTICLE_SPECIAL_LOADMORE) { - return VIEW_LOADMORE; */ - /*if (a.id == ARTICLE_SPECIAL_TOP_CHANGED) { - return VIEW_TOP_CHANGED; - } else */ if (m_activeArticle != null && a.id == m_activeArticle.id && a.unread) { + if (m_activeArticle != null && a.id == m_activeArticle.id && a.unread) { return VIEW_SELECTED_UNREAD; } else if (m_activeArticle != null && a.id == m_activeArticle.id) { return VIEW_SELECTED; @@ -1066,12 +870,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row; switch (getItemViewType(position)) { - /*case VIEW_LOADMORE: - layoutId = R.layout.headlines_row_loadmore; - break; - case VIEW_TOP_CHANGED: - layoutId = R.layout.headlines_row_top_changed; - break;*/ case VIEW_UNREAD: layoutId = m_compactLayoutMode ? R.layout.headlines_row_unread_compact : R.layout.headlines_row_unread; break; @@ -1760,7 +1558,7 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, m_listPreviousVisibleItem = firstVisibleItem; } - if (!m_refreshInProgress && !m_lazyLoadDisabled && /*m_articles.findById(ARTICLE_SPECIAL_LOADMORE) != null &&*/ firstVisibleItem + visibleItemCount == m_articles.size()) { + if (!m_refreshInProgress && !m_lazyLoadDisabled && firstVisibleItem + visibleItemCount == m_articles.size()) { refresh(true); } } @@ -1796,14 +1594,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, return -1; } - /* public int getArticlePosition(Article article) { - try { - return m_adapter.getPosition(article); - } catch (NullPointerException e) { - return -1; - } - } */ - public String getSearchQuery() { return m_searchQuery; } @@ -1823,10 +1613,6 @@ public class HeadlinesFragment extends Fragment implements OnItemClickListener, return m_feed; } - /*public ArticleList getArticles() { - return m_articles; - }*/ - public void setArticles(ArticleList articles) { m_articles.clear(); m_articles.addAll(articles); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java index b00b829f..a7d42739 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java @@ -136,7 +136,7 @@ public class OnlineActivity extends CommonActivity { @Override public void onCreate(Bundle savedInstanceState) { - ApiRequest.disableConnectionReuseIfNecessary(); + ApiCommon.disableConnectionReuseIfNecessary(); // we use that before parent onCreate so let's init locally m_prefs = PreferenceManager @@ -1184,7 +1184,7 @@ public class OnlineActivity extends CommonActivity { public void onResume() { super.onResume(); - ApiRequest.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), + ApiCommon.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), m_prefs.getBoolean("ssl_trust_any_host", false)); IntentFilter filter = new IntentFilter(); @@ -1581,7 +1581,7 @@ public class OnlineActivity extends CommonActivity { } catch (Exception e) { e.printStackTrace(); } - } else if (m_lastError != ApiError.API_UNKNOWN_METHOD) { + } else if (m_lastError != ApiCommon.ApiError.API_UNKNOWN_METHOD) { // Unknown method means old tt-rss, in that case we assume API 0 and continue setLoadingStatus(getErrorMessage(), false); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java index 85e18b6b..46e89b28 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java @@ -9,6 +9,7 @@ import android.widget.EditText; import com.google.gson.JsonElement; +import org.fox.ttrss.ApiCommon; import org.fox.ttrss.ApiRequest; import org.fox.ttrss.R; @@ -95,7 +96,7 @@ public class ShareActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { setProgressBarIndeterminateVisibility(false); - if (m_lastError != ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.NO_ERROR) { toast(getErrorMessage()); } else { toast(R.string.share_article_posted); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java index a3b7d060..9b92630a 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java @@ -18,6 +18,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import org.fox.ttrss.ApiCommon; import org.fox.ttrss.ApiRequest; import org.fox.ttrss.R; import org.fox.ttrss.types.FeedCategory; @@ -168,7 +169,7 @@ public class SubscribeActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { m_progressBar.setVisibility(View.INVISIBLE); - if (m_lastError != ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.NO_ERROR) { toast(getErrorMessage()); } else { try { @@ -273,7 +274,7 @@ public class SubscribeActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { m_progressBar.setVisibility(View.INVISIBLE); - if (m_lastError != ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.NO_ERROR) { toast(getErrorMessage()); } else { JsonArray content = result.getAsJsonArray(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/tasker/TaskerReceiver.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/tasker/TaskerReceiver.java index 4b6d81a9..6d521022 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/tasker/TaskerReceiver.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/tasker/TaskerReceiver.java @@ -1,5 +1,6 @@ package org.fox.ttrss.tasker; +import org.fox.ttrss.ApiCommon; import org.fox.ttrss.ApiRequest; import org.fox.ttrss.CommonActivity; import org.fox.ttrss.OnlineActivity; @@ -80,7 +81,7 @@ public class TaskerReceiver extends BroadcastReceiver { String login = prefs.getString("login", "").trim(); String password = prefs.getString("password", "").trim(); String ttrssUrl = prefs.getString("ttrss_url", "").trim(); - ApiRequest.trustAllHosts(prefs.getBoolean("ssl_trust_any", false), prefs.getBoolean("ssl_trust_any_host", false)); + ApiCommon.trustAllHosts(prefs.getBoolean("ssl_trust_any", false), prefs.getBoolean("ssl_trust_any_host", false)); if (ttrssUrl.equals("")) { Toast toast = Toast.makeText(fContext, "Could not download articles: not configured?", Toast.LENGTH_SHORT); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java index 54df9da4..8cb1a3c1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java @@ -9,8 +9,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; +import org.fox.ttrss.ApiCommon; import org.fox.ttrss.ApiRequest; -import org.fox.ttrss.HeadlinesFragment; import org.fox.ttrss.OnlineActivity; import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; @@ -121,7 +121,7 @@ public class HeadlinesRequest extends ApiRequest { } } - if (m_lastError == ApiError.LOGIN_FAILED) { + if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(); } else { diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index 43ed1bb0..050f7763 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -86,6 +86,7 @@ <org.fox.ttrss.util.LessBrokenSwitchPreference android:defaultValue="true" + android:dependency="headlines_mark_read_scroll" android:key="headlines_swipe_to_dismiss" android:summary="@string/pref_headlines_swipe_to_dismiss_long" android:title="@string/pref_headlines_swipe_to_dismiss" /> @@ -103,6 +104,7 @@ <org.fox.ttrss.util.LessBrokenSwitchPreference android:defaultValue="false" + android:disableDependentsState="true" android:key="headlines_mark_read_scroll" android:summary="@string/pref_headlines_mark_read_scroll_long" android:title="@string/pref_headlines_mark_read_scroll" /> |