From e680271566f82bbaf4404feccd27e12087e27ea5 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Tue, 29 Jul 2025 11:30:14 -0400 Subject: [PATCH 01/21] Add self signed trust ssl. --- .../gaulupeau/apps/Poche/data/Settings.java | 4 ++ .../Poche/network/SelfSignedTrustManager.java | 40 +++++++++++++++++++ .../Poche/network/WallabagConnection.java | 25 ++++++++++++ .../res/values/strings-preference-keys.xml | 1 + 4 files changed, 70 insertions(+) create mode 100644 app/src/main/java/fr/gaulupeau/apps/Poche/network/SelfSignedTrustManager.java diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index 4e11794b8..133ab13f0 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -239,6 +239,10 @@ public void setStringSet(int keyResourceID, Set values) { setStringSet(context.getString(keyResourceID), values); } + public boolean getSelfSignedTrust(){ + return getBoolean(R.string.pref_key_connection_selfsignedtrust,false); + } + public String getUrl() { return getString(R.string.pref_key_connection_url); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/network/SelfSignedTrustManager.java b/app/src/main/java/fr/gaulupeau/apps/Poche/network/SelfSignedTrustManager.java new file mode 100644 index 000000000..c2d97bdb0 --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/network/SelfSignedTrustManager.java @@ -0,0 +1,40 @@ +package fr.gaulupeau.apps.Poche.network; + +import javax.net.ssl.*; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class SelfSignedTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + // Optionnel : implémenter selon tes besoins + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (chain == null || chain.length == 0) throw new IllegalArgumentException("Empty or null certificate chain"); + X509Certificate cert = chain[0]; + // Vérifie si le certificat est auto-signé + if (isSelfSigned(cert)) { + return; + } + // accepter le certificat s'il est valide + cert.checkValidity(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + // Vérifie si le certificat est auto-signé + private boolean isSelfSigned(X509Certificate cert) { + try { + // Un certificat est auto-signé si l’émetteur et le sujet sont identiques et si la signature est valide avec sa propre clé publique + cert.verify(cert.getPublicKey()); + return cert.getSubjectX500Principal().equals(cert.getIssuerX500Principal()); + } catch (Exception e) { + return false; + } + } +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/network/WallabagConnection.java b/app/src/main/java/fr/gaulupeau/apps/Poche/network/WallabagConnection.java index 8ea351182..15d410105 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/network/WallabagConnection.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/network/WallabagConnection.java @@ -6,6 +6,8 @@ import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; + import com.facebook.stetho.okhttp3.StethoInterceptor; import org.conscrypt.Conscrypt; @@ -13,9 +15,15 @@ import java.io.IOException; import java.net.CookieManager; import java.net.CookiePolicy; +import java.security.KeyManagementException; import java.security.Security; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + import fr.gaulupeau.apps.InThePoche.BuildConfig; import fr.gaulupeau.apps.Poche.App; import fr.gaulupeau.apps.Poche.data.Settings; @@ -160,6 +168,23 @@ private static OkHttpClient.Builder getClientBuilder(boolean addCookieManager) { b.addNetworkInterceptor(new StethoInterceptor()); } + if(App.getSettings().getSelfSignedTrust()) { + // validate ssl + try { + TrustManager[] trustManagers = new TrustManager[]{new SelfSignedTrustManager()}; + SSLContext sslContext = SSLContext.getInstance("TLS"); + + sslContext.init(null, trustManagers, new java.security.SecureRandom()); + + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + b.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]); + b.hostnameVerifier((hostname, session) -> true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return b; } diff --git a/app/src/main/res/values/strings-preference-keys.xml b/app/src/main/res/values/strings-preference-keys.xml index ef323c1e4..79b1b0760 100644 --- a/app/src/main/res/values/strings-preference-keys.xml +++ b/app/src/main/res/values/strings-preference-keys.xml @@ -5,6 +5,7 @@ connection connection.category connection.url + connection.selfsignedtrust connection.username connection.password connection.api.clientID From 161d75daad4e0ba7f51f5f36a8a9e9921dec7329 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Tue, 29 Jul 2025 11:54:34 -0400 Subject: [PATCH 02/21] Add self signed trust ssl in setting screen --- .../Poche/ui/preferences/SettingsActivity.java | 14 ++++++++++++++ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 3 +++ 4 files changed, 19 insertions(+) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java index 8451f8134..a66075f29 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java @@ -95,6 +95,8 @@ public static class SettingsFragment extends PreferenceFragment private boolean checkUserChanged; private String oldUrl; + private boolean oldSelfSignedTrust; + private boolean selfsignedtrustChanged; private String oldHttpAuthUsername; private String oldUsername; private String oldApiClientID; @@ -238,6 +240,10 @@ private void resetChanges() { checkUserChanged = false; oldUrl = settings.getUrl(); + + selfsignedtrustChanged=false; + oldSelfSignedTrust = settings.getSelfSignedTrust(); + oldHttpAuthUsername = settings.getHttpAuthUsername(); oldUsername = settings.getUsername(); oldApiClientID = settings.getApiClientID(); @@ -325,6 +331,11 @@ private void applyChanges() { WallabagConnection.resetWallabagService(); } + if(selfsignedtrustChanged){ + selfsignedtrustChanged=false; + + } + if(imageCachingChanged) { imageCachingChanged = false; @@ -421,6 +432,9 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin Log.i(TAG, "onSharedPreferenceChanged() invalidateConfiguration"); invalidateConfiguration = true; break; + case R.string.pref_key_connection_selfsignedtrust: + selfsignedtrustChanged = true; + break; case R.string.pref_key_imageCache_enabled: imageCachingChanged = true; diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 493d11698..91307837e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -163,6 +163,7 @@ \n https://wallabag.example.com \n https://example.com/wallabag \n https://app.wallabag.it + Faites confiance aux certificats SSL auto-signés Identifiant Mot de passe ID Client diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 668194286..2939132a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -186,6 +186,7 @@ https://example.com/wallabag\n https://app.wallabag.it + Trust self signed SSL Username Password Client ID diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5940631ac..660bb0840 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -22,6 +22,9 @@ android:title="@string/pref_name_connection_url" android:summary="@string/pref_desc_connection_url" android:defaultValue="https://"/> + Date: Tue, 29 Jul 2025 12:24:31 -0400 Subject: [PATCH 03/21] wizard connect add checkbox (self singed SSL) --- .../fr/gaulupeau/apps/Poche/data/Settings.java | 4 ++++ .../ui/preferences/ConnectionWizardActivity.java | 16 ++++++++++++++++ ...connection_wizard_generic_config_fragment.xml | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index 133ab13f0..210958be5 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -243,6 +243,10 @@ public boolean getSelfSignedTrust(){ return getBoolean(R.string.pref_key_connection_selfsignedtrust,false); } + public void setSelfSignedTrust(boolean selfSignedTrust){ + setBoolean(R.string.pref_key_connection_selfsignedtrust,selfSignedTrust); + } + public String getUrl() { return getString(R.string.pref_key_connection_url); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java index b89c90bfe..e84896092 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java @@ -12,6 +12,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import android.widget.CheckBox; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.Toast; @@ -43,6 +44,8 @@ public class ConnectionWizardActivity extends BaseActionBarActivity { private static final String DATA_PROVIDER = "provider"; private static final String DATA_URL = "url"; + + private static final String DATA_TRUSTAUTOSIGNEDSSL = "trustautosignedssl"; private static final String DATA_USERNAME = "username"; private static final String DATA_PASSWORD = "password"; private static final String DATA_HTTP_AUTH_USERNAME = "http_auth_username"; @@ -89,6 +92,7 @@ protected void onCreate(Bundle savedInstanceState) { boolean fillOutData = false; String url = null, username = null, password = null; + boolean selfSignedTrust=false; String dataString = intent.getDataString(); if(dataString != null) { @@ -112,6 +116,7 @@ protected void onCreate(Bundle savedInstanceState) { Settings settings = App.getSettings(); url = settings.getUrl(); + selfSignedTrust=settings.getSelfSignedTrust(); username = settings.getUsername(); password = settings.getPassword(); @@ -440,6 +445,7 @@ public static class GenericConfigFragment extends WizardPageFragment protected String url; protected String username, password; + protected boolean selfSignedTrust; protected String clientID, clientSecret; protected String httpAuthUsername, httpAuthPassword; protected boolean tryPossibleURLs = true; @@ -456,6 +462,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa if (v != null) { EditText urlEditText = (EditText)v.findViewById(R.id.wallabag_url); + CheckBox selfSignedTrustCheckBox=(CheckBox)v.findViewById(R.id.wallabag_trustautosignedssl); EditText usernameEditText = (EditText)v.findViewById(R.id.username); EditText passwordEditText = (EditText)v.findViewById(R.id.password); @@ -463,6 +470,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa urlEditText.setText(getArguments().getString(DATA_URL)); } + if(selfSignedTrustCheckBox != null) { + selfSignedTrustCheckBox.setChecked(getArguments().getBoolean(DATA_TRUSTAUTOSIGNEDSSL)); + } + if(usernameEditText != null && getArguments().containsKey(DATA_USERNAME)) { usernameEditText.setText(getArguments().getString(DATA_USERNAME)); } @@ -490,10 +501,12 @@ protected void gatherData() { if(view == null) return; EditText urlEditText = (EditText)view.findViewById(R.id.wallabag_url); + CheckBox selfSignedTrustCheckBox=(CheckBox)view.findViewById(R.id.wallabag_trustautosignedssl); EditText usernameEditText = (EditText)view.findViewById(R.id.username); EditText passwordEditText = (EditText)view.findViewById(R.id.password); if(urlEditText != null) url = urlEditText.getText().toString(); + if(selfSignedTrustCheckBox!=null) selfSignedTrust=selfSignedTrustCheckBox.isChecked(); if(usernameEditText != null) username = usernameEditText.getText().toString(); if(passwordEditText != null) password = passwordEditText.getText().toString(); } @@ -552,6 +565,7 @@ protected void acceptSuggestion(String newUrl) { protected void populateBundleWithConnectionSettings() { Bundle bundle = getArguments(); bundle.putString(DATA_URL, url); + bundle.putBoolean(DATA_TRUSTAUTOSIGNEDSSL, selfSignedTrust); bundle.putString(DATA_USERNAME, username); bundle.putString(DATA_PASSWORD, password); bundle.putString(DATA_API_CLIENT_ID, clientID); @@ -626,6 +640,7 @@ protected void saveSettings() { Settings settings = App.getSettings(); String url = bundle.getString(DATA_URL); + boolean trustAutoSignedSsl=bundle.getBoolean(DATA_TRUSTAUTOSIGNEDSSL,false); String username = bundle.getString(DATA_USERNAME); String httpAuthUsername = bundle.getString(DATA_HTTP_AUTH_USERNAME); String clientID = bundle.getString(DATA_API_CLIENT_ID); @@ -637,6 +652,7 @@ protected void saveSettings() { || !TextUtils.equals(settings.getApiClientID(), clientID); settings.setUrl(url); + settings.setSelfSignedTrust(trustAutoSignedSsl); settings.setUsername(username); settings.setPassword(bundle.getString(DATA_PASSWORD)); settings.setHttpAuthUsername(httpAuthUsername); diff --git a/app/src/main/res/layout/connection_wizard_generic_config_fragment.xml b/app/src/main/res/layout/connection_wizard_generic_config_fragment.xml index cca51dd2e..a9712d4e2 100644 --- a/app/src/main/res/layout/connection_wizard_generic_config_fragment.xml +++ b/app/src/main/res/layout/connection_wizard_generic_config_fragment.xml @@ -56,6 +56,16 @@ + + + From 21e65f52bd71150a98ee32307d566cf5bd05685e Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Thu, 31 Jul 2025 12:32:30 -0400 Subject: [PATCH 04/21] Add settings to show article author and publication date in the list --- .../apps/Poche/data/ListAdapter.java | 22 +++++++++++++++++++ .../gaulupeau/apps/Poche/data/Settings.java | 16 ++++++++++++++ app/src/main/res/values-fr/strings.xml | 5 +++++ .../res/values/strings-preference-keys.xml | 4 ++++ app/src/main/res/values/strings.xml | 5 +++++ app/src/main/res/xml/preferences.xml | 15 +++++++++++++ 6 files changed, 67 insertions(+) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java index 640a584d7..01b85cfc4 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.Context; +import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuInflater; @@ -14,8 +15,12 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.List; +import java.util.Locale; +import fr.gaulupeau.apps.InThePoche.BuildConfig; import fr.gaulupeau.apps.InThePoche.R; import fr.gaulupeau.apps.Poche.data.dao.entities.Article; import fr.gaulupeau.apps.Poche.ui.ArticleActionsHelper; @@ -104,6 +109,13 @@ void bind(Article article) { title.setText(article.getTitle()); url.setText(article.getDomain()); + // show author if available in url TextView + if(settings.getArticleListShowAuthor()) { + if (article.getAuthors() != null && !article.getAuthors().isEmpty()) { + url.setText(url.getText() + " (" + article.getAuthors() + ")"); + } + } + boolean showFavourite = false; boolean showRead = false; switch (listType) { @@ -125,6 +137,16 @@ void bind(Article article) { read.setVisibility(showRead ? View.VISIBLE : View.GONE); readingTime.setText(context.getString(R.string.listItem_estimatedReadingTime, article.getEstimatedReadingTime(settings.getReadingSpeed()))); + + // show date article in readingTime if active in settings + if(settings.getArticleListShowPublishedAt()) { + if (article.getPublishedAt() != null) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.getDefault()); + + readingTime.setText(simpleDateFormat.format(article.getPublishedAt())); + } + } + } @Override diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index 210958be5..f056fe538 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -247,6 +247,22 @@ public void setSelfSignedTrust(boolean selfSignedTrust){ setBoolean(R.string.pref_key_connection_selfsignedtrust,selfSignedTrust); } + public boolean getArticleListShowAuthor(){ + return getBoolean(R.string.pref_key_ui_articleList_showAuthor,false); + } + + public void setArticleListShowAuthor(boolean articleListShowAuthor){ + setBoolean(R.string.pref_key_ui_articleList_showAuthor,articleListShowAuthor); + } + + public boolean getArticleListShowPublishedAt(){ + return getBoolean(R.string.pref_key_ui_articleList_showPublishedAt,false); + } + + public void setArticleListShowPublishedAt(boolean articleListPublishedAt){ + setBoolean(R.string.pref_key_ui_articleList_showPublishedAt,articleListPublishedAt); + } + public String getUrl() { return getString(R.string.pref_key_connection_url); } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 91307837e..a745bb88a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -326,6 +326,11 @@ Empêcher les appareils Onyx de forcer les couleurs des liens en noir Thème alternatif pour Onyx Boox Affiche une fenêtre de dialogue permettant de mettre en favori/archiver/étiqueter un article venant d\'être ajouté. Une notification sera affichée à la place si l\'option est désactivée + Liste des articles + Auteur + Afficher l\'auteur dans la liste des articles + publié à + Afficher la date de publication dans la liste des articles Afficher une fenêtre de dialogue à l\'ajout Ajouter plus d\'étiquettes : Aucune diff --git a/app/src/main/res/values/strings-preference-keys.xml b/app/src/main/res/values/strings-preference-keys.xml index 79b1b0760..e25a457ca 100644 --- a/app/src/main/res/values/strings-preference-keys.xml +++ b/app/src/main/res/values/strings-preference-keys.xml @@ -34,6 +34,10 @@ ui.screenScrolling ui.screenScrolling.percent ui.screenScrolling.smooth + + ui.articleList.category + ui.articleList.showAuthor + ui.articleList.showPublishedAt ui.misc.category ui.article.showArticleAddedDialog ui.article.fullscreen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2939132a2..1c0e78ac0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -213,6 +213,11 @@ Scroll inside window (overflow) Break lines (pre-wrap) + Article list + Author + Show article author in the article list + published at + Show article publication date in the article list Show a dialog when bagging Shows a dialog allowing favoriting/archiving/tagging a newly added article (or a toast message otherwise) Fullscreen articles diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 660bb0840..291b778c3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -147,6 +147,21 @@ android:title="@string/pref_name_ui_screenScrolling_smooth" android:defaultValue="true"/> + + + + Date: Thu, 31 Jul 2025 20:15:09 -0400 Subject: [PATCH 05/21] Add preview image settings to article list --- app/build.gradle | 1 + .../apps/Poche/data/ListAdapter.java | 24 +++++++++++++++++++ .../gaulupeau/apps/Poche/data/Settings.java | 16 +++++++++++++ .../ui/preferences/SettingsActivity.java | 1 + app/src/main/res/layout/list_item.xml | 5 ++++ app/src/main/res/values-fr/strings.xml | 4 ++++ .../res/values/strings-preference-keys.xml | 2 ++ app/src/main/res/values/strings.xml | 6 +++++ app/src/main/res/xml/preferences.xml | 12 ++++++++++ 9 files changed, 71 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index b52220caf..0843594c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,7 @@ dependencies { implementation 'com.google.android.material:material:1.12.0' implementation 'org.greenrobot:eventbus:3.3.1' implementation 'org.greenrobot:greendao:3.3.0' + implementation 'com.github.bumptech.glide:okhttp3-integration:4.16.0' annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1' implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.12.0' diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java index 01b85cfc4..53da5f835 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.content.Context; +import android.net.Uri; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -29,6 +30,8 @@ import static fr.gaulupeau.apps.Poche.data.ListTypes.LIST_TYPE_FAVORITES; import static fr.gaulupeau.apps.Poche.data.ListTypes.LIST_TYPE_UNREAD; +import com.bumptech.glide.Glide; + public class ListAdapter extends RecyclerView.Adapter { public interface OnItemClickListener { @@ -88,6 +91,7 @@ public class ViewHolder extends RecyclerView.ViewHolder ImageView favourite; ImageView read; TextView readingTime; + ImageView previewPicture; ViewHolder(View itemView, OnItemClickListener listener) { super(itemView); @@ -98,6 +102,8 @@ public class ViewHolder extends RecyclerView.ViewHolder favourite = itemView.findViewById(R.id.favourite); read = itemView.findViewById(R.id.read); readingTime = itemView.findViewById(R.id.estimatedReadingTime); + previewPicture = itemView.findViewById(R.id.previewPicture); + itemView.setOnClickListener(this); itemView.setOnCreateContextMenuListener(this); @@ -109,6 +115,24 @@ void bind(Article article) { title.setText(article.getTitle()); url.setText(article.getDomain()); + previewPicture.setVisibility(View.GONE); + if(settings.getArticleListShowPreviewPicture()) { + // set picture height from settings + if (settings.getArticleListPreviewPictureHeight() > 0) { + previewPicture.getLayoutParams().height = settings.getArticleListPreviewPictureHeight(); + } + + previewPicture.setVisibility(View.GONE); + if (article.getPreviewPictureURL() != null && !article.getPreviewPictureURL().isEmpty()) { + previewPicture.setVisibility(View.VISIBLE); + Glide + .with(context) + .load(article.getPreviewPictureURL()) + .centerCrop() + .into(previewPicture); + } + } + // show author if available in url TextView if(settings.getArticleListShowAuthor()) { if (article.getAuthors() != null && !article.getAuthors().isEmpty()) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index f056fe538..a61dc5bbd 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -247,6 +247,22 @@ public void setSelfSignedTrust(boolean selfSignedTrust){ setBoolean(R.string.pref_key_connection_selfsignedtrust,selfSignedTrust); } + public boolean getArticleListShowPreviewPicture(){ + return getBoolean(R.string.pref_key_ui_articleList_showPreviewPicture,false); + } + + public void setArticleListShowPreviewPicture(boolean articleListShowPreviewPicture){ + setBoolean(R.string.pref_key_ui_articleList_showPreviewPicture,articleListShowPreviewPicture); + } + + public int getArticleListPreviewPictureHeight(){ + return getInt(R.string.pref_key_ui_articleList_previewPictureHeight,500); + } + + public void setArticleListPreviewPictureHeight(int articleListPreviewPictureHeight){ + setInt(R.string.pref_key_ui_articleList_previewPictureHeight,articleListPreviewPictureHeight); + } + public boolean getArticleListShowAuthor(){ return getBoolean(R.string.pref_key_ui_articleList_showAuthor,false); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java index a66075f29..830c8fd8f 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java @@ -79,6 +79,7 @@ public static class SettingsFragment extends PreferenceFragment R.string.pref_key_ui_theme, R.string.pref_key_ui_article_fontSize, R.string.pref_key_ui_screenScrolling_percent, + R.string.pref_key_ui_articleList_previewPictureHeight, R.string.pref_key_autoSync_interval, R.string.pref_key_autoSync_type, R.string.pref_key_storage_dbPath diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index aecaffe4d..9b5e997e0 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -6,6 +6,11 @@ android:padding="10dp" android:background="?attr/selectableItemBackground"> + Thème alternatif pour Onyx Boox Affiche une fenêtre de dialogue permettant de mettre en favori/archiver/étiqueter un article venant d\'être ajouté. Une notification sera affichée à la place si l\'option est désactivée Liste des articles + Image + Afficher l\'image dans la liste des articles + Image hauteur + Hauteur de l\'image dans la liste des articles (dp) Auteur Afficher l\'auteur dans la liste des articles publié à diff --git a/app/src/main/res/values/strings-preference-keys.xml b/app/src/main/res/values/strings-preference-keys.xml index e25a457ca..0bb24f726 100644 --- a/app/src/main/res/values/strings-preference-keys.xml +++ b/app/src/main/res/values/strings-preference-keys.xml @@ -36,6 +36,8 @@ ui.screenScrolling.smooth ui.articleList.category + ui.articleList.previewPicture + ui.articleList.pictureHeight ui.articleList.showAuthor ui.articleList.showPublishedAt ui.misc.category diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c0e78ac0..4532da508 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -214,6 +214,12 @@ Break lines (pre-wrap) Article list + Preview image + Show article preview image in the article list + Preview image height + Height of the preview image in the article list (in dp) + Title + Show article title in the article list Author Show article author in the article list published at diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 291b778c3..e19da3c5a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -151,6 +151,18 @@ android:key="@string/pref_key_ui_articleList_category" android:title="@string/pref_categoryName_ui_articleList" android:persistent="false"> + + Date: Fri, 1 Aug 2025 10:29:32 -0400 Subject: [PATCH 06/21] Update default value for article list preview picture height --- app/src/main/res/xml/preferences.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index e19da3c5a..bd5d68146 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -160,7 +160,7 @@ android:key="@string/pref_key_ui_articleList_previewPictureHeight" android:title="@string/pref_name_ui_articleList_previewPictureHeight" android:summary="@string/pref_desc_ui_articleList_previewPictureHeight" - android:defaultValue="200" + android:defaultValue="500" android:inputType="number" /> Date: Fri, 1 Aug 2025 10:45:59 -0400 Subject: [PATCH 07/21] Add setting to show divider between articles in the article list --- .../java/fr/gaulupeau/apps/Poche/data/Settings.java | 8 ++++++++ .../apps/Poche/ui/RecyclerViewListFragment.java | 12 ++++++++++++ app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values/strings-preference-keys.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/preferences.xml | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index a61dc5bbd..9b6c78c7f 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -279,6 +279,14 @@ public void setArticleListShowPublishedAt(boolean articleListPublishedAt){ setBoolean(R.string.pref_key_ui_articleList_showPublishedAt,articleListPublishedAt); } + public boolean getArticleListShowDivider() { + return getBoolean(R.string.pref_key_ui_articleList_showDivider,false); + } + + public void setArticleListShowDivider(boolean articleListShowDivider) { + setBoolean(R.string.pref_key_ui_articleList_showDivider,articleListShowDivider); + } + public String getUrl() { return getString(R.string.pref_key_connection_url); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java index 4bbf943c5..747648d21 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java @@ -12,6 +12,7 @@ import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -20,6 +21,7 @@ import java.util.List; import fr.gaulupeau.apps.InThePoche.R; +import fr.gaulupeau.apps.Poche.App; public abstract class RecyclerViewListFragment> extends Fragment implements Sortable, Searchable { @@ -91,6 +93,16 @@ public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { refreshLayout = view.findViewById(getSwipeContainerResID()); refreshLayout.setOnRefreshListener(this::onSwipeRefresh); + // add separator + if(App.getSettings().getArticleListShowDivider()) { + if (recyclerView.getItemDecorationCount() == 0) { + recyclerView.addItemDecoration(new DividerItemDecoration( + recyclerView.getContext(), + LinearLayoutManager.VERTICAL + )); + } + } + return view; } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8ca09bf37..461a605b9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -335,6 +335,8 @@ Afficher l\'auteur dans la liste des articles publié à Afficher la date de publication dans la liste des articles + Diviseur + Afficher un diviseur entre les articles Afficher une fenêtre de dialogue à l\'ajout Ajouter plus d\'étiquettes : Aucune diff --git a/app/src/main/res/values/strings-preference-keys.xml b/app/src/main/res/values/strings-preference-keys.xml index 0bb24f726..6ca3dd886 100644 --- a/app/src/main/res/values/strings-preference-keys.xml +++ b/app/src/main/res/values/strings-preference-keys.xml @@ -40,6 +40,7 @@ ui.articleList.pictureHeight ui.articleList.showAuthor ui.articleList.showPublishedAt + ui.articleList.showDivider ui.misc.category ui.article.showArticleAddedDialog ui.article.fullscreen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4532da508..321db9ac1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,6 +224,8 @@ Show article author in the article list published at Show article publication date in the article list + Divider + Show a divider between articles in the article list Show a dialog when bagging Shows a dialog allowing favoriting/archiving/tagging a newly added article (or a toast message otherwise) Fullscreen articles diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index bd5d68146..ed045c461 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -173,6 +173,11 @@ android:title="@string/pref_name_ui_articleList_publishedAt" android:summary="@string/pref_desc_ui_articleList_publishedAt" android:defaultValue="false"/> + Date: Fri, 1 Aug 2025 11:10:44 -0400 Subject: [PATCH 08/21] refactoring layout - Add published date display to article list --- .../fr/gaulupeau/apps/Poche/data/ListAdapter.java | 15 ++++++++++++--- app/src/main/res/layout/list_item.xml | 7 +++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java index 53da5f835..cc0114427 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java @@ -92,6 +92,7 @@ public class ViewHolder extends RecyclerView.ViewHolder ImageView read; TextView readingTime; ImageView previewPicture; + TextView publishedAt; ViewHolder(View itemView, OnItemClickListener listener) { super(itemView); @@ -103,6 +104,7 @@ public class ViewHolder extends RecyclerView.ViewHolder read = itemView.findViewById(R.id.read); readingTime = itemView.findViewById(R.id.estimatedReadingTime); previewPicture = itemView.findViewById(R.id.previewPicture); + publishedAt= itemView.findViewById(R.id.publishedAt); itemView.setOnClickListener(this); @@ -163,11 +165,18 @@ void bind(Article article) { article.getEstimatedReadingTime(settings.getReadingSpeed()))); // show date article in readingTime if active in settings + publishedAt.setVisibility(View.GONE); if(settings.getArticleListShowPublishedAt()) { if (article.getPublishedAt() != null) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy", Locale.getDefault()); - - readingTime.setText(simpleDateFormat.format(article.getPublishedAt())); + publishedAt.setVisibility(View.VISIBLE); + //SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy: hh:mm", Locale.getDefault()); + + //publishedAt.setText(simpleDateFormat.format(article.getPublishedAt())); + var stringBuilder = new StringBuilder(); + stringBuilder.append(android.text.format.DateFormat.getDateFormat(context).format(article.getPublishedAt())); + stringBuilder.append(' '); + stringBuilder.append(android.text.format.DateFormat.getTimeFormat(context).format(article.getPublishedAt())); + publishedAt.setText(stringBuilder.toString()); } } diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index 9b5e997e0..44380a7b5 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -60,4 +60,11 @@ android:layout_gravity="right" android:text="@string/listItem_estimatedReadingTime"/> + \ No newline at end of file From c6fd5b2d19407b20d228dc4967dd7c6b839366f8 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sat, 2 Aug 2025 09:53:44 -0400 Subject: [PATCH 09/21] refactor layout - Replace EditText with TextInputEditText in dialog_add.xml same layout dialog_change_title --- app/src/main/res/layout/dialog_add.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_add.xml b/app/src/main/res/layout/dialog_add.xml index b82c5b7fa..9b891bd19 100644 --- a/app/src/main/res/layout/dialog_add.xml +++ b/app/src/main/res/layout/dialog_add.xml @@ -4,7 +4,7 @@ android:padding="16dp" android:layout_height="wrap_content"> - Date: Sat, 2 Aug 2025 19:53:24 -0400 Subject: [PATCH 10/21] refactor ListAdapter - Display creation date if publication date is unavailable; update preference description --- .../fr/gaulupeau/apps/Poche/data/ListAdapter.java | 15 ++++++++++----- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java index cc0114427..6b4ff35d8 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListAdapter.java @@ -167,15 +167,20 @@ void bind(Article article) { // show date article in readingTime if active in settings publishedAt.setVisibility(View.GONE); if(settings.getArticleListShowPublishedAt()) { + java.util.Date displayDate=null; if (article.getPublishedAt() != null) { - publishedAt.setVisibility(View.VISIBLE); - //SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy: hh:mm", Locale.getDefault()); + displayDate=article.getPublishedAt(); + } + else if(article.getCreationDate()!=null){ + displayDate=article.getCreationDate(); + } - //publishedAt.setText(simpleDateFormat.format(article.getPublishedAt())); + if(displayDate!=null) { + publishedAt.setVisibility(View.VISIBLE); var stringBuilder = new StringBuilder(); - stringBuilder.append(android.text.format.DateFormat.getDateFormat(context).format(article.getPublishedAt())); + stringBuilder.append(android.text.format.DateFormat.getDateFormat(context).format(displayDate)); stringBuilder.append(' '); - stringBuilder.append(android.text.format.DateFormat.getTimeFormat(context).format(article.getPublishedAt())); + stringBuilder.append(android.text.format.DateFormat.getTimeFormat(context).format(displayDate)); publishedAt.setText(stringBuilder.toString()); } } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 461a605b9..ed0a43136 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -334,7 +334,7 @@ Auteur Afficher l\'auteur dans la liste des articles publié à - Afficher la date de publication dans la liste des articles + Afficher la date de publication dans la liste des articles.Si la date de publication n\'éxiste pas afficher la date de création. Diviseur Afficher un diviseur entre les articles Afficher une fenêtre de dialogue à l\'ajout diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 321db9ac1..01c10d636 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,7 +223,7 @@ Author Show article author in the article list published at - Show article publication date in the article list + Show article publication date in the article list.If the publication date does not exist, display the creation date. Divider Show a divider between articles in the article list Show a dialog when bagging From cac612b3d2bd8109ccb0d37fc5a687effc41a9d6 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sat, 2 Aug 2025 20:00:21 -0400 Subject: [PATCH 11/21] refactor ReadArticleActivity - Add display for article creation date in stats --- .../gaulupeau/apps/Poche/ui/ReadArticleActivity.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 570854c66..5a2290020 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -939,11 +939,20 @@ private String getExtraHead() { private String getStats() { StringBuilder stats = new StringBuilder(); + stats.append("
  • "); + // Material icon 'today' + stats.append("\t"); + stats.append(android.text.format.DateFormat.getDateFormat(this).format(article.getCreationDate())) + .append(' ') + .append(android.text.format.DateFormat.getTimeFormat(this).format(article.getCreationDate())); + stats.append("
  • "); + + Date publishedAt = article.getPublishedAt(); if (publishedAt != null) { stats.append("
  • "); // Material icon 'today' - stats.append("\t"); + stats.append("\t"); stats.append(android.text.format.DateFormat.getDateFormat(this).format(publishedAt)) .append(' ') .append(android.text.format.DateFormat.getTimeFormat(this).format(publishedAt)); From 3c1e31eeca620916f2e94811aa940d37f41f93e1 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 3 Aug 2025 11:22:43 -0400 Subject: [PATCH 12/21] refactor sorting - Add creation date and estimated reading time sort options issue : #1463 --- .../gaulupeau/apps/Poche/data/Settings.java | 4 +- .../apps/Poche/ui/ArticleListFragment.java | 10 ++- .../gaulupeau/apps/Poche/ui/MainActivity.java | 87 ++++++++++++++++--- .../fr/gaulupeau/apps/Poche/ui/Sortable.java | 6 +- .../apps/Poche/ui/TagListFragment.java | 2 +- app/src/main/res/values-fr/strings.xml | 7 ++ app/src/main/res/values/strings.xml | 7 ++ 7 files changed, 104 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index 9b6c78c7f..155904764 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -449,9 +449,9 @@ public Sortable.SortOrder getListSortOrder() { String sortOrderParam = getString(R.string.pref_key_ui_lists_sortOrder); Sortable.SortOrder sortOrder = null; - if(sortOrderParam != null) { + if(sortOrderParam != null && !sortOrderParam.isEmpty()) { try { - sortOrder = Sortable.SortOrder.valueOf(sortOrderParam); + sortOrder = Sortable.SortOrder.valueOf(sortOrderParam); } catch(IllegalArgumentException ignored) {} } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java index 3d8ace6f4..6f74f4161 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java @@ -236,13 +236,19 @@ private QueryBuilder
    getQueryBuilder() { switch (sortOrder) { case ASC: + case CreationDateASC: qb.orderAsc(ArticleDao.Properties.CreationDate, ArticleDao.Properties.ArticleId); break; - case DESC: + case CreationDateDESC: qb.orderDesc(ArticleDao.Properties.CreationDate, ArticleDao.Properties.ArticleId); break; - + case EstimatedReadingTimeASC: + qb.orderAsc(ArticleDao.Properties.EstimatedReadingTime, ArticleDao.Properties.ArticleId); + break; + case EstimatedReadingTimeDESC: + qb.orderDesc(ArticleDao.Properties.EstimatedReadingTime, ArticleDao.Properties.ArticleId); + break; default: throw new IllegalStateException("Sort order not implemented: " + sortOrder); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java index 8dd7b62ce..5c07f450e 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java @@ -433,7 +433,11 @@ public boolean onQueryTextChange(String newText) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_main_changeSortOrder: - switchSortOrder(); + if(FRAGMENT_TAG_LIST.equals(currentFragmentType)) + switchTagSortOrder(); + else + switchSortOrder(); + return true; case R.id.menu_main_syncQueue: @@ -633,23 +637,80 @@ private void setParametersToFragment(Fragment fragment) { setSortOrder(fragment); setSearchQueryOnFragment(fragment, searchQuery); } + private void switchTagSortOrder() { + AlertDialog.Builder builder = new AlertDialog.Builder(this) + .setTitle(R.string.dialog_switchSortOrder_label); + + int checkedItem = switch (tagsSortOrder) { + case DESC -> 0; + case ASC -> 1; + default -> 1; + }; + + builder.setSingleChoiceItems( + new CharSequence[]{ + getString(R.string.d_switchSortOrder_DESC), + getString(R.string.d_switchSortOrder_ASC) + },checkedItem, (dialog, which) -> { + switch (which) { + case 0: + tagsSortOrder=Sortable.SortOrder.DESC; + break; + case 1: + tagsSortOrder=Sortable.SortOrder.ASC; + break; + } + + settings.setTagListSortOrder(tagsSortOrder); + setSortOrder(currentFragment); + dialog.dismiss(); // Dismiss the dialog after selection + }); + + builder.show(); + + } private void switchSortOrder() { - if (FRAGMENT_TAG_LIST.equals(currentFragmentType)) { - tagsSortOrder = tagsSortOrder == Sortable.SortOrder.DESC - ? Sortable.SortOrder.ASC - : Sortable.SortOrder.DESC; + AlertDialog.Builder builder = new AlertDialog.Builder(this) + .setTitle(R.string.dialog_switchSortOrder_label); + + int checkedItem = switch (sortOrder) { + case CreationDateDESC -> 0; + case CreationDateASC -> 1; + case EstimatedReadingTimeDESC -> 2; + case EstimatedReadingTimeASC -> 3; + default -> 0; + }; + + builder.setSingleChoiceItems( + new CharSequence[]{ + getString(R.string.d_switchSortOrder_creationDateDESC), + getString(R.string.d_switchSortOrder_creationDateASC), + getString(R.string.d_switchSortOrder_estimatedReadingTimeDESC), + getString(R.string.d_switchSortOrder_estimatedReadingTimeASC) + },checkedItem, (dialog, which) -> { + switch (which) { + case 0: + sortOrder=Sortable.SortOrder.CreationDateDESC; + break; + case 1: + sortOrder=Sortable.SortOrder.CreationDateASC; + break; + case 2: + sortOrder=Sortable.SortOrder.EstimatedReadingTimeDESC; + break; + case 3: + sortOrder=Sortable.SortOrder.EstimatedReadingTimeASC; + break; + } - settings.setTagListSortOrder(tagsSortOrder); - } else { - sortOrder = sortOrder == Sortable.SortOrder.DESC - ? Sortable.SortOrder.ASC - : Sortable.SortOrder.DESC; + settings.setListSortOrder(sortOrder); + setSortOrder(currentFragment); + dialog.dismiss(); // Dismiss the dialog after selection + }); - settings.setListSortOrder(sortOrder); - } + builder.show(); - setSortOrder(currentFragment); } private void setSortOrder(Fragment fragment) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Sortable.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Sortable.java index 9dc5d5d70..3c7e19aae 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Sortable.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Sortable.java @@ -3,7 +3,11 @@ public interface Sortable { public enum SortOrder { - DESC, ASC + DESC, ASC, + CreationDateDESC,CreationDateASC, + EstimatedReadingTimeDESC,EstimatedReadingTimeASC + + } void setSortOrder(SortOrder sortOrder); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java index b6c187271..c6f0adae5 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java @@ -102,7 +102,7 @@ private QueryBuilder getQueryBuilder() { break; default: - throw new IllegalStateException("Sort order not implemented: " + sortOrder); + //throw new IllegalStateException("Sort order not implemented: " + sortOrder); } return qb; diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ed0a43136..3c7e0e5c3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -41,6 +41,13 @@ Ouvrir dans le navigateur Ajouter à wallabag Copier dans le presse-papier + Ordre + Décroissant + Croissant + Date de création décroissante + Date de création croissante + Temps lecture décroissant + Temps lecture croissant OK Échec Impossible de trouver une URL dans le texte partagé : diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 01c10d636..ad3d2fcbb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,6 +42,13 @@ Open in browser Add to wallabag Copy to clipboard + Sort + DESC + ASC + Creation date DESC + Creation date ASC + Reading time DESC + Reading time ASC OK Fail Couldn\'t find a URL in share string:\n From d0a43f0f8b52c3a70190920978fdeff1636bb50f Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 3 Aug 2025 11:33:58 -0400 Subject: [PATCH 13/21] refactor CI workflow - Add steps to assemble and upload release APK --- .github/workflows/android.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 6234bb2e8..538833536 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -31,3 +31,11 @@ jobs: - name: Build run: ./gradlew build + + - name: Assemble APK + run: ./gradlew assembleRelease + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: app-release-apk + path: app/build/outputs/apk/release/app-release.apk From ebfcbb29ad9c761de392b17761cc9ee0c9cfe7d6 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 3 Aug 2025 11:40:06 -0400 Subject: [PATCH 14/21] refactor CI workflow - Update to assemble and upload debug APK instead of release APK --- .github/workflows/android.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 538833536..925dd3d3d 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -31,11 +31,10 @@ jobs: - name: Build run: ./gradlew build - - - name: Assemble APK - run: ./gradlew assembleRelease - - name: Upload APK + - name: Assemble Debug APK + run: ./gradlew assembleDebug + - name: Upload Debug APK uses: actions/upload-artifact@v4 with: - name: app-release-apk - path: app/build/outputs/apk/release/app-release.apk + name: app-debug-apk + path: app/build/outputs/apk/debug/app-debug.apk From a58be3bafb54997dc68b534abaab7442fc718521 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 3 Aug 2025 13:51:09 -0400 Subject: [PATCH 15/21] Update android.yml --- .github/workflows/android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 925dd3d3d..3b4e21569 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -37,4 +37,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: app-debug-apk - path: app/build/outputs/apk/debug/app-debug.apk + path: app/build/outputs/apk/debug/*.apk From 2ab447ca41f352b46354a88d418c4ed3c6b7efeb Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 3 Aug 2025 19:58:40 -0400 Subject: [PATCH 16/21] new theme "DAY_NIGHT" Follow system light/dark theme in Android app issue: #1453 --- .../gaulupeau/apps/Poche/ui/MainActivity.java | 4 ++ .../apps/Poche/ui/ReadArticleActivity.java | 12 ++++ .../fr/gaulupeau/apps/Poche/ui/Themes.java | 6 ++ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-night/styles.xml | 44 +++++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 55 +++++++++++++++++++ 7 files changed, 123 insertions(+) create mode 100644 app/src/main/res/values-night/styles.xml diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java index 5c07f450e..d7aa06dc7 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java @@ -26,6 +26,7 @@ import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; @@ -117,6 +118,9 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate()"); + + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + Themes.applyTheme(this, false); super.onCreate(savedInstanceState); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 5a2290020..22f712f71 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -2,6 +2,7 @@ import android.annotation.SuppressLint; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -873,6 +874,17 @@ private String getHtmlPage() { cssName = "solarized"; highContrast = false; break; + + case DAY_NIGHT: + cssName = "main"; + + int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) { + cssName = "dark"; + break; + } + + break; } List additionalClasses = new ArrayList<>(); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java index 1bc3ec14b..15a4ea84b 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java @@ -88,6 +88,12 @@ public enum Theme { R.style.SolarizedTheme, R.style.SolarizedTheme_NoActionBar, R.style.DialogTheme + ), + DAY_NIGHT( + R.string.themeName_day_night, + R.style.DayNightTheme, + R.style.DayNightTheme_NoActionBar, + R.style.DayNightDialogTheme ); private int nameId; diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 3c7e0e5c3..e126d8ca4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -105,6 +105,7 @@ Sombre Sombre (contraste élevé) Solarisé + Clair/Sombre (auto) Augmenter la taille de la police Diminuer la taille de la police Application wallabag pour Android diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..f5189b830 --- /dev/null +++ b/app/src/main/res/values-night/styles.xml @@ -0,0 +1,44 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad3d2fcbb..5d57b1342 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,6 +106,7 @@ Dark Dark (high contrast) Solarized + Light/Dark (auto) Increase font size Decrease font size wallabag app for Android\n diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 39ee51084..9fbcb790b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -68,6 +68,61 @@ ?attr/colorPrimary + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d57b1342..58854caaf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,6 +107,7 @@ Dark (high contrast) Solarized Light/Dark (auto) + Light/Dark (high contrast - auto) Increase font size Decrease font size wallabag app for Android\n From b38c62c9398fadc2e2fba18e55a2c0d489e0b016 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Mon, 4 Aug 2025 10:44:55 -0400 Subject: [PATCH 18/21] update texte --- app/src/main/res/values-fr/strings.xml | 3 ++- app/src/main/res/values/strings.xml | 3 ++- app/src/main/res/xml/preferences.xml | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d349c13c4..d61c2f0a6 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -172,7 +172,8 @@ \n https://wallabag.example.com \n https://example.com/wallabag \n https://app.wallabag.it - Faites confiance aux certificats SSL auto-signés + Certificats SSL auto-signés + Trust self-signed SSL certificates (not recommended) Identifiant Mot de passe ID Client diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 58854caaf..b41cda408 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -195,7 +195,8 @@ https://example.com/wallabag\n https://app.wallabag.it - Trust self signed SSL + Self signed SSL + Trust self-signed SSL certificates (not recommended) Username Password Client ID diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index ed045c461..a74d798d3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -24,7 +24,9 @@ android:defaultValue="https://"/> + android:title="@string/pref_name_connection_selfsignedtrust" + android:summary="@string/pref_desc_connection_selfsignedtrust" + /> Date: Mon, 4 Aug 2025 10:55:25 -0400 Subject: [PATCH 19/21] Update gradle-wrapper-validation.yml update to v4 --- .github/workflows/gradle-wrapper-validation.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 23ac9e20b..24a4bd0dc 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -9,5 +9,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: gradle/wrapper-validation-action@v1 + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v4 From c93768f7c6aa61e8e5fb1ecc3ba1061f836e2071 Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Mon, 4 Aug 2025 10:58:03 -0400 Subject: [PATCH 20/21] Update gradle-wrapper-validation.yml --- .github/workflows/gradle-wrapper-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 24a4bd0dc..5b43d14d5 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -10,4 +10,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v4 + - uses: gradle/actions/wrapper-validation@v4 From 9f90421e6947746e4473dac5f53da9e23f9b3c5c Mon Sep 17 00:00:00 2001 From: Francis Noel Date: Sun, 16 Nov 2025 10:50:02 -0500 Subject: [PATCH 21/21] Add delete button functionality to article view --- .../fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java | 7 +++++++ app/src/main/res/layout/article.xml | 6 ++++++ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ 4 files changed, 16 insertions(+) diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 303de6ec3..39ad09615 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -589,6 +589,7 @@ private void showDisableTouchToast() { private void initButtons() { initMarkAsReadButtonView(); + intiDeleteButtonView(); initPrevNextButtons(); } @@ -611,6 +612,12 @@ private void updateMarkAsReadButton() { findViewById(R.id.btnMarkUnread).setVisibility(archived ? View.VISIBLE : View.GONE); } + private void intiDeleteButtonView() { + Button buttonDelete = findViewById(R.id.btnDelete); + + buttonDelete.setOnClickListener(v -> deleteArticle()); + } + private void initPrevNextButtons() { ImageButton buttonGoPrevious = findViewById(R.id.btnGoPrevious); ImageButton buttonGoNext = findViewById(R.id.btnGoNext); diff --git a/app/src/main/res/layout/article.xml b/app/src/main/res/layout/article.xml index 5f67c882d..3523d5bf3 100644 --- a/app/src/main/res/layout/article.xml +++ b/app/src/main/res/layout/article.xml @@ -91,6 +91,12 @@ android:text="@string/btnMarkUnread" android:layout_weight="6" android:visibility="gone"/> +