diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 6234bb2e8..3b4e21569 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -31,3 +31,10 @@ jobs: - name: Build run: ./gradlew build + - name: Assemble Debug APK + run: ./gradlew assembleDebug + - name: Upload Debug APK + uses: actions/upload-artifact@v4 + with: + name: app-debug-apk + path: app/build/outputs/apk/debug/*.apk diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 23ac9e20b..5b43d14d5 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/actions/wrapper-validation@v4 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 640a584d7..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 @@ -2,6 +2,8 @@ import android.app.Activity; import android.content.Context; +import android.net.Uri; +import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuInflater; @@ -14,8 +16,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; @@ -24,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 { @@ -83,6 +91,8 @@ public class ViewHolder extends RecyclerView.ViewHolder ImageView favourite; ImageView read; TextView readingTime; + ImageView previewPicture; + TextView publishedAt; ViewHolder(View itemView, OnItemClickListener listener) { super(itemView); @@ -93,6 +103,9 @@ 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); + publishedAt= itemView.findViewById(R.id.publishedAt); + itemView.setOnClickListener(this); itemView.setOnCreateContextMenuListener(this); @@ -104,6 +117,31 @@ 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()) { + url.setText(url.getText() + " (" + article.getAuthors() + ")"); + } + } + boolean showFavourite = false; boolean showRead = false; switch (listType) { @@ -125,6 +163,28 @@ 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 + publishedAt.setVisibility(View.GONE); + if(settings.getArticleListShowPublishedAt()) { + java.util.Date displayDate=null; + if (article.getPublishedAt() != null) { + displayDate=article.getPublishedAt(); + } + else if(article.getCreationDate()!=null){ + displayDate=article.getCreationDate(); + } + + if(displayDate!=null) { + publishedAt.setVisibility(View.VISIBLE); + var stringBuilder = new StringBuilder(); + stringBuilder.append(android.text.format.DateFormat.getDateFormat(context).format(displayDate)); + stringBuilder.append(' '); + stringBuilder.append(android.text.format.DateFormat.getTimeFormat(context).format(displayDate)); + publishedAt.setText(stringBuilder.toString()); + } + } + } @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 4e11794b8..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 @@ -239,6 +239,54 @@ 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 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); + } + + 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 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); } @@ -401,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/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/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..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); @@ -433,7 +437,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 +641,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/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 559e3e145..fa1f09dec 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; @@ -590,6 +591,7 @@ private void showDisableTouchToast() { private void initButtons() { initMarkAsReadButtonView(); + intiDeleteButtonView(); initPrevNextButtons(); } @@ -612,6 +614,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); @@ -875,6 +883,20 @@ private String getHtmlPage() { cssName = "solarized"; highContrast = false; break; + + case DAY_NIGHT_CONTRAST: + highContrast = true; + 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<>(); @@ -941,11 +963,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)); 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/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/java/fr/gaulupeau/apps/Poche/ui/Themes.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java index 1bc3ec14b..55d237ef2 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,7 +88,22 @@ 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 + ), + + DAY_NIGHT_CONTRAST( + R.string.themeName_day_night_contrast, + R.style.DayNightThemeContrast, + R.style.DayNightThemeContrast_NoActionBar, + R.style.DayNightDialogTheme + );; + + private int nameId; private int resId; 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/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java index 8451f8134..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 @@ -95,6 +96,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 +241,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 +332,11 @@ private void applyChanges() { WallabagConnection.resetWallabagService(); } + if(selfsignedtrustChanged){ + selfsignedtrustChanged=false; + + } + if(imageCachingChanged) { imageCachingChanged = false; @@ -421,6 +433,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/layout/article.xml b/app/src/main/res/layout/article.xml index a71305279..26d1f108c 100644 --- a/app/src/main/res/layout/article.xml +++ b/app/src/main/res/layout/article.xml @@ -92,6 +92,12 @@ android:text="@string/btnMarkUnread" android:layout_weight="6" android:visibility="gone"/> +