diff options
Diffstat (limited to 'org.fox.ttrss/src/main')
21 files changed, 401 insertions, 521 deletions
diff --git a/org.fox.ttrss/src/main/AndroidManifest.xml b/org.fox.ttrss/src/main/AndroidManifest.xml index b73971d1..8489811a 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="505" - android:versionName="1.271"> + android:versionCode="514" + android:versionName="1.280"> <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 index b7308000..ce5089f8 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java @@ -1,21 +1,45 @@ package org.fox.ttrss; -import android.os.Build; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.preference.PreferenceManager; import android.util.Log; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; -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 java.io.IOException; +import java.util.HashMap; +import java.util.Locale; + +import androidx.annotation.NonNull; +import okhttp3.Credentials; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; public class ApiCommon { public static final String TAG = "ApiCommon"; + private static final int API_STATUS_OK = 0; + private static final int API_STATUS_ERR = 1; + + private static final MediaType TYPE_JSON = MediaType.parse("application/json; charset=utf-8"); + + public interface ApiCaller { + void setStatusCode(int statusCode); + void setLastError(ApiError lastError); + void setLastErrorMessage(String message); + } + 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 } @@ -62,64 +86,167 @@ public class ApiCommon { Log.d(TAG, "getErrorMessage: unknown error code=" + error); return R.string.error_unknown; } + } + + static boolean isNetworkAvailable(Context context) { + ConnectivityManager cm = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + // if no network is available networkInfo will be null + // otherwise check if we are connected + return networkInfo != null && networkInfo.isConnected(); } - public static void trustAllHosts(boolean trustAnyCert, boolean trustAnyHost) { + static JsonElement performRequest(Context context, @NonNull HashMap<String, String> m_params, + @NonNull ApiCommon.ApiCaller caller) { try { - if (trustAnyCert) { - X509TrustManager easyTrustManager = new X509TrustManager() { + if (!ApiCommon.isNetworkAvailable(context)) { + caller.setLastError(ApiError.NETWORK_UNAVAILABLE); + return null; + } - public void checkClientTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } + SharedPreferences m_prefs = PreferenceManager.getDefaultSharedPreferences(context); - public void checkServerTrusted( - X509Certificate[] chain, - String authType) throws CertificateException { - // Oh, I am easy! - } + boolean m_transportDebugging = m_prefs.getBoolean("transport_debugging", false); - public X509Certificate[] getAcceptedIssuers() { - return null; - } + Gson gson = new Gson(); - }; + String payload = gson.toJson(new HashMap<>(m_params)); + String apiUrl = m_prefs.getString("ttrss_url", "").trim() + "/api/"; - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] {easyTrustManager}; + if (m_transportDebugging) Log.d(TAG, ">>> " + payload + " -> " + apiUrl); - // Install the all-trusting trust manager + Request.Builder requestBuilder = new Request.Builder() + .url(apiUrl) + .header("User-Agent", getUserAgent(context)) + .post(RequestBody.create(TYPE_JSON, payload)); - SSLContext sc = SSLContext.getInstance("TLS"); + String httpLogin = m_prefs.getString("http_login", "").trim(); + String httpPassword = m_prefs.getString("http_password", "").trim(); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); + if (httpLogin.length() > 0) { + if (m_transportDebugging) Log.d(TAG, "Using HTTP Basic authentication."); + + requestBuilder.addHeader("Authorization", Credentials.basic(httpLogin, httpPassword)); + } - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + Request request = requestBuilder.build(); + + Response response = new OkHttpClient() + .newCall(request) + .execute(); + + if (response.isSuccessful()) { + String payloadReceived = response.body().string(); + + if (m_transportDebugging) Log.d(TAG, "<<< " + payloadReceived); + + JsonParser parser = new JsonParser(); + + JsonElement result = parser.parse(payloadReceived); + JsonObject resultObj = result.getAsJsonObject(); + + int statusCode = resultObj.get("status").getAsInt(); + + caller.setStatusCode(statusCode); + + switch (statusCode) { + 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(); + + switch (error) { + case "LOGIN_ERROR": + case "NOT_LOGGED_IN": + caller.setLastError(ApiError.LOGIN_FAILED); + break; + case "API_DISABLED": + caller.setLastError(ApiError.API_DISABLED); + break; + case "INCORRECT_USAGE": + caller.setLastError(ApiError.API_INCORRECT_USAGE); + break; + case "UNKNOWN_METHOD": + caller.setLastError(ApiError.API_UNKNOWN_METHOD); + break; + default: + Log.d(TAG, "Unknown API error: " + error); + caller.setLastError(ApiError.API_UNKNOWN); + break; + } + } + + } else { + switch (response.code()) { + case 401: + caller.setLastError(ApiError.HTTP_UNAUTHORIZED); + break; + case 403: + caller.setLastError(ApiError.HTTP_FORBIDDEN); + break; + case 404: + caller.setLastError(ApiError.HTTP_NOT_FOUND); + break; + case 500: + case 501: + caller.setLastError(ApiError.HTTP_SERVER_ERROR); + break; + default: + Log.d(TAG, "HTTP response code: " + response.code()); + caller.setLastError(ApiError.HTTP_OTHER_ERROR); + break; + } } - if (trustAnyHost) { - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); + return null; + } catch (javax.net.ssl.SSLPeerUnverifiedException e) { + caller.setLastError(ApiError.SSL_REJECTED); + caller.setLastErrorMessage(e.getMessage()); + e.printStackTrace(); + } catch (IOException e) { + caller.setLastError(ApiError.IO_ERROR); + caller.setLastErrorMessage(e.getMessage()); + + if (e.getMessage() != null) { + if (e.getMessage().matches("Hostname [^ ]+ was not verified")) { + caller.setLastError(ApiError.SSL_HOSTNAME_REJECTED); + } } + e.printStackTrace(); + } catch (com.google.gson.JsonSyntaxException e) { + caller.setLastError(ApiError.PARSE_ERROR); + caller.setLastErrorMessage(e.getMessage()); + e.printStackTrace(); } catch (Exception e) { + caller.setLastError(ApiError.OTHER_ERROR); + caller.setLastErrorMessage(e.getMessage()); e.printStackTrace(); } + + return null; } - @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"); + private static String getUserAgent(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager(). + getPackageInfo(context.getPackageName(), 0); + + return String.format(Locale.ENGLISH, + "Tiny Tiny RSS (Android) %1$s (%2$d) %3$s", + packageInfo.versionName, + packageInfo.versionCode, + System.getProperty("http.agent")); + + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + + return String.format(Locale.ENGLISH, + "Tiny Tiny RSS (Android) Unknown %1$s", + System.getProperty("http.agent")); } } - } 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 index 222a383f..95f7fa87 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java @@ -1,62 +1,34 @@ 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.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.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import androidx.loader.content.AsyncTaskLoader; -public class ApiLoader extends AsyncTaskLoader<JsonElement> { +public class ApiLoader extends AsyncTaskLoader<JsonElement> implements ApiCommon.ApiCaller { private final String TAG = this.getClass().getSimpleName(); + private int m_responseCode = 0; + protected String m_responseMessage; + private int m_apiStatusCode = 0; - public static final int API_STATUS_OK = 0; - public static final int API_STATUS_ERR = 1; + private Context m_context; + private String m_lastErrorMessage; + private ApiError m_lastError; + private HashMap<String,String> m_params; + private JsonElement m_data; - private String m_api; - private boolean m_transportDebugging = false; - protected int m_responseCode = 0; - protected String m_responseMessage; - protected int m_apiStatusCode = 0; - 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) { + 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 @@ -79,171 +51,31 @@ public class ApiLoader extends AsyncTaskLoader<JsonElement> { return ApiCommon.getErrorMessage(m_lastError); } - public ApiError getLastError() { + ApiError getLastError() { return m_lastError; } - public String getLastErrorMessage() { + String getLastErrorMessage() { return m_lastErrorMessage; } @Override public JsonElement loadInBackground() { + return ApiCommon.performRequest(m_context, m_params, this); + } - if (!isNetworkAvailable()) { - m_lastError = ApiError.NETWORK_UNAVAILABLE; - return null; - } - - Gson gson = new Gson(); - - String requestStr = gson.toJson(new HashMap<>(m_params)); - byte[] postData = null; - - postData = requestStr.getBytes(StandardCharsets.UTF_8); - - if (m_transportDebugging) Log.d(TAG, ">>> (" + requestStr + ") " + m_api); - - URL url; - - try { - // canonicalize url just in case - URL baseUrl = new URL(m_api); - File f = new File(baseUrl.getPath() + "/api"); - url = new URL(baseUrl, f.getCanonicalPath() + "/"); - } catch (Exception e) { - m_lastError = ApiError.INVALID_URL; - e.printStackTrace(); - return null; - } + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } - 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(StandardCharsets.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(), StandardCharsets.UTF_8); - char[] buf = new char[256]; - int read = 0; - - while ((read = in.read(buf)) >= 0) { - response.append(buf, 0, read); - } - - 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; + @Override + public void setLastError(ApiError lastError) { + m_lastError = lastError; } - 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 - return networkInfo != null && networkInfo.isConnected(); - } + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } } 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 d839d624..e993a866 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 @@ -2,67 +2,35 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.content.Context; -import android.content.SharedPreferences; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.AsyncTask; -import android.os.Build; -import android.preference.PreferenceManager; -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 java.io.File; -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; import static org.fox.ttrss.ApiCommon.ApiError; -public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonElement> { +public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonElement> implements ApiCommon.ApiCaller { 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; + private int m_responseCode = 0; + private int m_apiStatusCode = 0; + + private Context m_context; protected String m_lastErrorMessage; protected ApiError m_lastError; public ApiRequest(Context 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; - } @SuppressLint("NewApi") @SuppressWarnings("unchecked") public void execute(HashMap<String,String> map) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) - super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, map); - else - super.execute(map); + super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, map); } public int getErrorMessage() { @@ -71,177 +39,21 @@ public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonE @Override protected JsonElement doInBackground(HashMap<String, String>... params) { + return ApiCommon.performRequest(m_context, params[0], this); + } - if (!isNetworkAvailable()) { - m_lastError = ApiError.NETWORK_UNAVAILABLE; - return null; - } - - Gson gson = new Gson(); - - String requestStr = gson.toJson(new HashMap<String,String>(params[0])); - 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 { - // canonicalize url just in case - URL baseUrl = new URL(m_api); - File f = new File(baseUrl.getPath() + "/api"); - url = new URL(baseUrl, f.getCanonicalPath() + "/"); - } 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; - } - } + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } - 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; + @Override + public void setLastError(ApiError lastError) { + m_lastError = lastError; } - 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; - } + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java index a6359086..7e320203 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java @@ -469,8 +469,7 @@ public class ArticleFragment extends StateSavedFragment { ws.setMediaPlaybackRequiresUserGesture(false); } - String theme = m_prefs.getString("theme", CommonActivity.THEME_DEFAULT); - if (CommonActivity.THEME_DARK.equals(theme) || CommonActivity.THEME_AMBER.equals(theme)) { + if (m_activity.isUiNightMode()) { m_web.setBackgroundColor(Color.BLACK); } 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 c885b732..e08934bd 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 @@ -13,6 +13,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; + import com.google.android.material.snackbar.Snackbar; import com.google.gson.JsonElement; import com.viewpagerindicator.UnderlinePageIndicator; @@ -24,10 +29,6 @@ import org.fox.ttrss.util.HeadlinesRequest; import java.util.HashMap; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; import icepick.State; public class ArticlePager extends StateSavedFragment { @@ -339,7 +340,7 @@ public class ArticlePager extends StateSavedFragment { } if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (!m_activity.isWifiConnected()) { + if (m_prefs.getBoolean("always_downsample_images", false) || !m_activity.isWifiConnected()) { put("resize_width", String.valueOf(m_activity.getResizeWidth())); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java index 493676d8..e639a825 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java @@ -15,9 +15,9 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.BitmapFactory; -import android.graphics.Point; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -48,6 +48,7 @@ import java.util.Arrays; import java.util.List; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatDelegate; import androidx.browser.customtabs.CustomTabsCallback; import androidx.browser.customtabs.CustomTabsClient; import androidx.browser.customtabs.CustomTabsIntent; @@ -65,11 +66,7 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc public final static String FRAG_CATS = "cats"; public final static String FRAG_DIALOG = "dialog"; - public final static String THEME_DARK = "THEME_DARK"; - public final static String THEME_LIGHT = "THEME_LIGHT"; - //public final static String THEME_SEPIA = "THEME_SEPIA"; - public final static String THEME_AMBER = "THEME_AMBER"; - public final static String THEME_DEFAULT = CommonActivity.THEME_LIGHT; + public final static String THEME_DEFAULT = "THEME_FOLLOW_DEVICE"; public final static String NOTIFICATION_CHANNEL_NORMAL = "channel_normal"; public final static String NOTIFICATION_CHANNEL_PRIORITY = "channel_priority"; @@ -313,23 +310,41 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc .show(); } + public boolean isUiNightMode() { + try { + int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + return Configuration.UI_MODE_NIGHT_YES == nightModeFlags; + } catch (Exception e) { + return false; + } + } + protected void setAppTheme(SharedPreferences prefs) { String theme = prefs.getString("theme", CommonActivity.THEME_DEFAULT); - - if (theme.equals(THEME_DARK)) { - setTheme(R.style.DarkTheme); - } else if (theme.equals(THEME_AMBER)) { - setTheme(R.style.AmberTheme); + + Log.d(TAG, "setting theme to: " + theme); + + if ("THEME_DARK".equals(theme)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + } else if ("THEME_LIGHT".equals(theme)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); } else { - setTheme(R.style.LightTheme); + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); } + + setTheme(R.style.AppTheme); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Log.d(TAG, "onSharedPreferenceChanged:" + key); - String[] filter = new String[] { "theme", "enable_cats", "headline_mode", "widget_update_interval", + if ("theme".equals(key)) { + setAppTheme(sharedPreferences); + } + + String[] filter = new String[] { "enable_cats", "headline_mode", "widget_update_interval", "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size" }; m_needRestart = Arrays.asList(filter).indexOf(key) != -1; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java index ca2b6a47..0dfce8b2 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; @@ -225,7 +226,9 @@ public class GalleryActivity extends CommonActivity { m_prefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); - setTheme(R.style.DarkTheme); + + getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); @@ -296,7 +299,7 @@ public class GalleryActivity extends CommonActivity { MediaCheckTask mct = new MediaCheckTask() { @Override protected void onProgressUpdate(MediaProgressResult... result) { - m_items.add(result[0].item); + //m_items.add(result[0].item); m_adapter.notifyDataSetChanged(); if (result[0].position < result[0].count) { @@ -311,8 +314,8 @@ public class GalleryActivity extends CommonActivity { @Override protected void onPostExecute(List<GalleryEntry> result) { - //m_items.addAll(result); - //m_adapter.notifyDataSetChanged(); + m_items.addAll(result); + m_adapter.notifyDataSetChanged(); } }; 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 933c4b28..ac94d420 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 @@ -50,6 +50,17 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.appcompat.app.ActionBar; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.util.ColorGenerator; import com.bumptech.glide.Glide; @@ -81,16 +92,6 @@ import java.util.HashMap; import java.util.List; import java.util.TimeZone; -import androidx.appcompat.app.ActionBar; -import androidx.core.app.ActivityCompat; -import androidx.core.app.ActivityOptionsCompat; -import androidx.core.view.ViewCompat; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import icepick.State; import jp.wasabeef.glide.transformations.CropCircleTransformation; @@ -718,7 +719,7 @@ public class HeadlinesFragment extends StateSavedFragment { put("order_by", m_activity.getSortMode()); if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (!m_activity.isWifiConnected()) { + if (m_prefs.getBoolean("always_downsample_images", false) || !m_activity.isWifiConnected()) { put("resize_width", String.valueOf(m_activity.getResizeWidth())); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java index c845a1d7..c9cbcbf9 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java @@ -11,6 +11,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import icepick.State; @@ -25,7 +26,8 @@ public class LogcatActivity extends CommonActivity { @Override public void onCreate(Bundle savedInstanceState) { - setTheme(R.style.DarkTheme); + getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); 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 1cfd830b..e644fc46 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 @@ -20,6 +20,7 @@ import android.net.NetworkInfo; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; +import android.provider.Settings; import android.util.Log; import android.view.Display; import android.view.KeyEvent; @@ -133,8 +134,7 @@ public class OnlineActivity extends CommonActivity { @Override public void onCreate(Bundle savedInstanceState) { - ApiCommon.disableConnectionReuseIfNecessary(); - + // we use that before parent onCreate so let's init locally m_prefs = PreferenceManager .getDefaultSharedPreferences(getApplicationContext()); @@ -275,6 +275,19 @@ public class OnlineActivity extends CommonActivity { } public void login(boolean refresh, OnLoginFinishedListener listener) { + + if (BuildConfig.ENABLE_TRIAL && !BuildConfig.DEBUG) { + String testLabSetting = Settings.System.getString(getContentResolver(), "firebase.test.lab"); + + if ("true".equals(testLabSetting)) { + SharedPreferences.Editor editor = m_prefs.edit(); + editor.putString("ttrss_url", "https://srv.tt-rss.org/tt-rss"); + editor.putString("login", "demo"); + editor.putString("password", "demo"); + editor.apply(); + } + } + if (m_prefs.getString("ttrss_url", "").trim().length() == 0) { setLoadingStatus(R.string.login_need_configure); @@ -991,7 +1004,7 @@ public class OnlineActivity extends CommonActivity { } }; - ApiRequest req = new ApiRequest(m_context); + ApiRequest req = new ApiRequest(OnlineActivity.this); req.execute(map); } @@ -1075,9 +1088,6 @@ public class OnlineActivity extends CommonActivity { public void onResume() { super.onResume(); - ApiCommon.trustAllHosts(m_prefs.getBoolean("ssl_trust_any", false), - m_prefs.getBoolean("ssl_trust_any_host", false)); - IntentFilter filter = new IntentFilter(); //filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS); filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS); @@ -1469,7 +1479,7 @@ public class OnlineActivity extends CommonActivity { } else { - ApiRequest req = new ApiRequest(m_context) { + ApiRequest req = new ApiRequest(OnlineActivity.this) { protected void onPostExecute(JsonElement result) { setApiLevel(0); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/YoutubePlayerActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/YoutubePlayerActivity.java index 464684be..81afbf0e 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/YoutubePlayerActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/YoutubePlayerActivity.java @@ -13,6 +13,7 @@ import com.google.android.youtube.player.YouTubeInitializationResult; import com.google.android.youtube.player.YouTubePlayer; import com.google.android.youtube.player.YouTubePlayerSupportFragment; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import icepick.State; @@ -28,7 +29,8 @@ public class YoutubePlayerActivity extends CommonActivity implements YouTubePlay @Override public void onCreate(Bundle savedInstanceState) { - setTheme(R.style.DarkTheme); + getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); setContentView(R.layout.activity_youtube_player); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java index a0d25be4..ff8c53d8 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java @@ -31,7 +31,6 @@ import android.widget.TextView; import com.shamanland.fab.ShowHideOnScroll; -import org.fox.ttrss.CommonActivity; import org.fox.ttrss.R; import org.fox.ttrss.util.ImageCacheService; import org.fox.ttrss.util.NotifyingScrollView; @@ -322,8 +321,7 @@ public class OfflineArticleFragment extends Fragment { if (m_web != null) { - String theme = m_prefs.getString("theme", CommonActivity.THEME_DEFAULT); - if (CommonActivity.THEME_DARK.equals(theme) || CommonActivity.THEME_AMBER.equals(theme)) { + if (m_activity.isUiNightMode()) { m_web.setBackgroundColor(Color.BLACK); } diff --git a/org.fox.ttrss/src/main/res/values-night-v21/style.xml b/org.fox.ttrss/src/main/res/values-night-v21/style.xml new file mode 100644 index 00000000..c154ed8a --- /dev/null +++ b/org.fox.ttrss/src/main/res/values-night-v21/style.xml @@ -0,0 +1,12 @@ +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="AppTheme.Base" parent="Theme.AppCompat.DayNight.NoActionBar"> + <item name="android:windowDrawsSystemBarBackgrounds">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:windowBackground">@color/window_background_dark</item> + <item name="android:navigationBarColor">@android:color/black</item> + </style> + + <style name="DarkDialogTheme" parent="android:Theme.Material.Dialog"> + </style> + +</resources>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/values-night/style.xml b/org.fox.ttrss/src/main/res/values-night/style.xml new file mode 100644 index 00000000..c81d4b31 --- /dev/null +++ b/org.fox.ttrss/src/main/res/values-night/style.xml @@ -0,0 +1,63 @@ +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="AppTheme" parent="AppTheme.Base"> + <item name="windowActionModeOverlay">true</item> + + <!-- <item name="statusBarHintColor">?colorPrimary</item> --> + <item name="unreadCounterColor">#909090</item> + <item name="feedlistTextColor">@android:color/white</item> + <item name="headlineUnreadTextColor">@android:color/white</item> + <item name="headlineSelectedTextColor">@android:color/white</item> + <item name="headlineExcerptTextColor">#bebebe</item> + <item name="headlineSecondaryTextColor">#909090</item> + <item name="headlineTitleHighScoreUnreadTextColor">#00FF00</item> + <item name="headlineSelectedExcerptTextColor">#bebebe</item> + <item name="headlineSelectedSecondaryTextColor">?headlineSelectedExcerptTextColor</item> + <item name="headlineSelectedBackground">#1c1c1c</item> + <item name="headlineHeaderBackground">#99000000</item> + <item name="headlineUnreadBackground">#101010</item> + <item name="linkColor">#2d92c8</item> + <item name="loadingBackground">@android:color/black</item> + <item name="articleNoteTextColor">#bebebe</item> + <item name="articleNoteBackground">#303030</item> + <item name="parentBtnBackground">#101010</item> + <item name="ttrssHorizontalDivider">@android:drawable/divider_horizontal_dark</item> + <item name="feedlistBackground">#1c1d1e</item> + <item name="headlinesBackground">#1c1d1e</item> + <item name="articleBackground">@android:color/black</item> + <item name="feedsSelectedBackground">#1c1c1c</item> + <item name="feedlistSelectedTextColor">@android:color/white</item> + <item name="articleHeader">@android:color/transparent</item> + <item name="articleHeaderTextColor">@android:color/white</item> + <item name="floatingActionButtonStyle">@style/FabTheme</item> + <item name="articleTextColor">#e0e0e0</item> + <item name="headlineFooterColor">?colorPrimary</item> + <item name="articleHeaderSeparator">#303030</item> + + <item name="colorPrimary">#1e6286</item> + <item name="colorPrimaryDark">#18506e</item> + <item name="colorAccent">#D84315</item> + + <item name="ic_rss_box">@drawable/ic_rss_box</item> + <item name="ic_checkbox_marked">@drawable/ic_checkbox_marked</item> + <item name="ic_star">@drawable/ic_star</item> + <item name="ic_star_outline">@drawable/ic_star_outline</item> + <item name="ic_share">@drawable/ic_share</item> + <item name="ic_inbox">@drawable/ic_inbox</item> + <item name="ic_go_back">@drawable/ic_go_back</item> + <item name="ic_settings">@drawable/ic_settings</item> + <item name="ic_filter_variant">@drawable/ic_filter_variant</item> + <item name="ic_cloud_download">@drawable/ic_cloud_download</item> + <item name="ic_cloud_upload">@drawable/ic_cloud_upload</item> + <item name="ic_archive">@drawable/ic_archive</item> + <item name="ic_fresh">@drawable/ic_fresh</item> + <item name="ic_restore">@drawable/ic_restore</item> + <item name="ic_folder_outline">@drawable/ic_folder_outline</item> + <item name="ic_dots_vertical">@drawable/ic_dots_vertical</item> + <item name="ic_dots_vertical_circle">@drawable/ic_dots_vertical_circle</item> + <item name="ic_attachment">@drawable/ic_attachment</item> + <item name="ic_attachment_vert">@drawable/ic_attachment_vert</item> + + <item name="drawer_header">@drawable/drawer_header_dark</item> + </style> +</resources>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/values-v21/style.xml b/org.fox.ttrss/src/main/res/values-v21/style.xml index 1e5a366a..3e26f96f 100755 --- a/org.fox.ttrss/src/main/res/values-v21/style.xml +++ b/org.fox.ttrss/src/main/res/values-v21/style.xml @@ -1,16 +1,18 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="LightTheme.Base" parent="Theme.AppCompat.Light.NoActionBar"> + <style name="AppTheme.Base" parent="Theme.AppCompat.DayNight.NoActionBar"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:windowBackground">@color/window_background</item> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - <style name="DarkTheme.Base" parent="Theme.AppCompat.NoActionBar"> + <!-- <style name="DarkTheme.Base" parent="Theme.AppCompat.NoActionBar"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:windowBackground">@color/window_background_dark</item> <item name="android:navigationBarColor">@android:color/black</item> - </style> + </style> --> <style name="DarkDialogTheme" parent="android:Theme.Material.Dialog"> </style> diff --git a/org.fox.ttrss/src/main/res/values/arrays.xml b/org.fox.ttrss/src/main/res/values/arrays.xml index 25b2bd58..d2df646a 100644 --- a/org.fox.ttrss/src/main/res/values/arrays.xml +++ b/org.fox.ttrss/src/main/res/values/arrays.xml @@ -1,13 +1,13 @@ <resources> <string-array name="pref_theme_names"> + <item>@string/theme_follow_device</item> <item>@string/theme_light</item> <item>@string/theme_dark</item> - <item>@string/theme_amber</item> </string-array> <string-array name="pref_theme_values" translatable="false"> + <item>THEME_FOLLOW_DEVICE</item> <item>THEME_LIGHT</item> <item>THEME_DARK</item> - <item>THEME_AMBER</item> </string-array> <string-array name="headline_mode_names"> <item>@string/headline_display_mode_default</item> @@ -52,4 +52,5 @@ <item>30</item> <item>45</item> </string-array> + <string name="theme_follow_device">Same as device</string> </resources>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/values/strings.xml b/org.fox.ttrss/src/main/res/values/strings.xml index 4cf36455..a0b0f0b4 100755 --- a/org.fox.ttrss/src/main/res/values/strings.xml +++ b/org.fox.ttrss/src/main/res/values/strings.xml @@ -278,5 +278,8 @@ <string name="prefs_headline_images_wifi_only_long">Overrides previous setting</string> <string name="prefs_inline_video_player">Experimental. Long tap to open separate player</string> <string name="prefs_enable_image_downsampling">Auto-resize images</string> - <string name="prefs_enable_image_downsampling_long">Asks backend to downsample images if not on Wi-Fi. Needs a plugin (ttrss-api-resize).</string> + <string name="prefs_enable_image_downsampling_long">Asks backend to downsample images if not on Wi-Fi. Needs a plugin (ttrss-api-resize)</string> + <string name="prefs_always_downsample_images">Always resize images</string> + <string name="prefs_always_downsample_images_long">Even on Wi-Fi</string> + <string name="prefs_inline_video_player_title">Inline video player</string> </resources> diff --git a/org.fox.ttrss/src/main/res/values/style.xml b/org.fox.ttrss/src/main/res/values/style.xml index 9121638c..6dbda125 100755 --- a/org.fox.ttrss/src/main/res/values/style.xml +++ b/org.fox.ttrss/src/main/res/values/style.xml @@ -1,11 +1,12 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="LightTheme.Base" parent="Theme.AppCompat.Light.NoActionBar"> + <style name="AppTheme.Base" parent="Theme.AppCompat.DayNight"> <item name="android:windowNoTitle">true</item> - + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - <style name="LightTheme" parent="LightTheme.Base"> + <style name="AppTheme" parent="AppTheme.Base"> <item name="windowActionModeOverlay">true</item> <!-- <item name="statusBarHintColor">?colorPrimary</item> --> @@ -66,6 +67,7 @@ <item name="drawer_header">@drawable/drawer_header</item> </style> + <!-- <style name="DarkTheme.Base" parent="Theme.AppCompat.NoActionBar"> </style> @@ -78,10 +80,10 @@ </style> <style name="DarkTheme" parent="DarkTheme.Base"> - <item name="windowActionModeOverlay">true</item> + <item name="windowActionModeOverlay">true</item> --> <!-- <item name="statusBarHintColor">?colorPrimary</item> --> - <item name="unreadCounterColor">#909090</item> + <!-- <item name="unreadCounterColor">#909090</item> <item name="feedlistTextColor">@android:color/white</item> <item name="headlineUnreadTextColor">@android:color/white</item> <item name="headlineSelectedTextColor">@android:color/white</item> @@ -136,7 +138,7 @@ <item name="ic_attachment_vert">@drawable/ic_attachment_vert</item> <item name="drawer_header">@drawable/drawer_header_dark</item> - </style> + </style> --> <style name="DarkDialogTheme" parent="android:Theme"></style> diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index f6f9b9bc..93bc74fa 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -32,7 +32,7 @@ android:key="category_look_and_feel" android:title="@string/look_and_feel" > <ListPreference - android:defaultValue="THEME_LIGHT" + android:defaultValue="THEME_FOLLOW_DEVICE" android:entries="@array/pref_theme_names" android:entryValues="@array/pref_theme_values" android:key="theme" @@ -128,9 +128,16 @@ <org.fox.ttrss.util.LessBrokenSwitchPreference android:defaultValue="false" + android:key="always_downsample_images" + android:dependency="enable_image_downsampling" + android:title="@string/prefs_always_downsample_images" + android:summary="@string/prefs_always_downsample_images_long" /> + + <org.fox.ttrss.util.LessBrokenSwitchPreference + android:defaultValue="false" android:key="inline_video_player" android:summary="@string/prefs_inline_video_player" - android:title="Inline video player" /> + android:title="@string/prefs_inline_video_player_title" /> </PreferenceCategory> diff --git a/org.fox.ttrss/src/main/res/xml/preferences_network.xml b/org.fox.ttrss/src/main/res/xml/preferences_network.xml index 8d1469d5..d44284e6 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences_network.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences_network.xml @@ -1,17 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > - <PreferenceCategory android:title="@string/ssl" > - <org.fox.ttrss.util.LessBrokenSwitchPreference - android:defaultValue="false" - android:key="ssl_trust_any" - android:summary="@string/ssl_trust_any_long" - android:title="@string/ssl_trust_any" /> - <org.fox.ttrss.util.LessBrokenSwitchPreference - android:defaultValue="false" - android:key="ssl_trust_any_host" - android:summary="@string/ssl_trust_any_host_long" - android:title="@string/ssl_trust_any_host" /> - </PreferenceCategory> <PreferenceCategory android:title="@string/http_authentication" > <EditTextPreference android:key="http_login" |