summaryrefslogtreecommitdiff
path: root/org.fox.ttrss
diff options
context:
space:
mode:
Diffstat (limited to 'org.fox.ttrss')
-rwxr-xr-xorg.fox.ttrss/build.gradle12
-rwxr-xr-xorg.fox.ttrss/src/main/AndroidManifest.xml4
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java217
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java220
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java226
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java3
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java11
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java41
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java11
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java23
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java4
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java24
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/YoutubePlayerActivity.java4
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java4
-rw-r--r--org.fox.ttrss/src/main/res/values-night-v21/style.xml12
-rw-r--r--org.fox.ttrss/src/main/res/values-night/style.xml63
-rwxr-xr-xorg.fox.ttrss/src/main/res/values-v21/style.xml8
-rw-r--r--org.fox.ttrss/src/main/res/values/arrays.xml5
-rwxr-xr-xorg.fox.ttrss/src/main/res/values/strings.xml5
-rwxr-xr-xorg.fox.ttrss/src/main/res/values/style.xml14
-rwxr-xr-xorg.fox.ttrss/src/main/res/xml/preferences.xml11
-rwxr-xr-xorg.fox.ttrss/src/main/res/xml/preferences_network.xml12
22 files changed, 407 insertions, 527 deletions
diff --git a/org.fox.ttrss/build.gradle b/org.fox.ttrss/build.gradle
index c9f6a45d..2e8e7918 100755
--- a/org.fox.ttrss/build.gradle
+++ b/org.fox.ttrss/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 29
buildToolsVersion "28.0.3"
defaultConfig {
@@ -9,7 +9,7 @@ android {
buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
buildConfigField "boolean", "ENABLE_TRIAL", "true"
minSdkVersion 16
- targetSdkVersion 28 // we're not targeting SDK 25 because of this: https://issuetracker.google.com/issues/37103380#makechanges
+ targetSdkVersion 29
}
signingConfigs {
@@ -48,7 +48,7 @@ android {
dependencies {
implementation files('libs/dashclock-api-r1.1.jar')
- implementation 'com.squareup.okhttp3:okhttp:3.10.0'
+ implementation 'com.squareup.okhttp3:okhttp:3.12.5'
implementation('com.github.bumptech.glide:okhttp3-integration:1.5.0') {
exclude group: 'glide-parent'
}
@@ -56,12 +56,12 @@ dependencies {
implementation 'com.bogdwellers:pinchtozoom:0.1'
implementation 'com.github.bumptech.glide:glide:3.8.0'
implementation 'jp.wasabeef:glide-transformations:2.0.2'
- implementation 'androidx.recyclerview:recyclerview:1.0.0'
+ implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
- implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.browser:browser:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
- implementation 'com.google.code.gson:gson:2.8.2'
+ implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.shamanland:fab:0.0.8'
implementation 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32@aar'
implementation 'me.relex:circleindicator:1.2.2@aar'
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"