From c31152c7147c847edaf8b1f44f270c462d212427 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 12 May 2025 22:42:15 +0300 Subject: experimental headlines loader with diffutil --- .../src/main/java/org/fox/ttrss/Application.java | 16 +- .../src/main/java/org/fox/ttrss/ArticlePager.java | 8 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 155 +++++++++-- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 292 +++++++++++++++++++++ .../src/main/java/org/fox/ttrss/types/Article.java | 4 +- 5 files changed, 454 insertions(+), 21 deletions(-) create mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index 07f1a3b5..9ad069f1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -1,5 +1,8 @@ package org.fox.ttrss; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Bundle; import org.fox.ttrss.types.ArticleList; @@ -17,6 +20,7 @@ public class Application extends android.app.Application { private String m_sessionId; private int m_apiLevel; public LinkedHashMap m_customSortModes = new LinkedHashMap<>(); + ConnectivityManager m_cmgr; public static Application getInstance(){ return m_singleton; @@ -31,6 +35,7 @@ public class Application extends android.app.Application { super.onCreate(); m_singleton = this; + m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); } public String getSessionId() { @@ -68,6 +73,15 @@ public class Application extends android.app.Application { m_customSortModes.clear(); m_customSortModes.putAll(tmp); } - } + + public boolean isWifiConnected() { + NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + + if (wifi != null) + return wifi.isConnected(); + + return false; + } + } 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 da9dc8f3..46cf36ee 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 @@ -146,8 +146,12 @@ public class ArticlePager extends androidx.fragment.app.Fragment { return view; } - + protected void refresh(final boolean append) { + // + } + + /* protected void refresh(final boolean append) { if (!append) { m_lazyLoadDisabled = false; @@ -291,7 +295,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { Log.d(TAG, "[AP] request more headlines, firstId=" + m_firstId); req.execute(map); - } + } */ @Override public void onAttach(@NonNull Activity activity) { 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 ad023a7e..0030d2a6 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 @@ -44,11 +44,16 @@ import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.core.view.ViewCompat; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -84,7 +89,106 @@ import java.util.TimeZone; import jp.wasabeef.glide.transformations.CropCircleTransformation; -public class HeadlinesFragment extends androidx.fragment.app.Fragment { +public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { + + public class HeadlinesDiffutilCallback extends DiffUtil.Callback { + private ArticleList m_oldList; + private ArticleList m_newList; + + public HeadlinesDiffutilCallback(ArticleList oldList, ArticleList newList) { + m_oldList = oldList; + m_newList = newList; + } + + @Override + public int getOldListSize() { + return m_oldList != null ? m_oldList.size() : 0; + } + + @Override + public int getNewListSize() { + return m_newList != null ? m_newList.size() : 0; + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return false; + } + } + + + private HeadlinesLoader m_loader; + + @NonNull + @Override + public Loader onCreateLoader(int id, @Nullable Bundle args) { + return new HeadlinesLoader(getContext(), m_feed, m_activity.getResizeWidth()); + } + + @Override + public void onLoadFinished(@NonNull Loader loader, ArticleList data) { + Log.d(TAG, "onLoadFinished loader=" + loader + " count=" + data.size()); + + HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; + + // successful update + if (data != null) { + ArticleList articles = Application.getArticles(); + + articles.stripFooters(); + + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(articles, data)); + + articles.clear(); + articles.addAll(data); + + diffResult.dispatchUpdatesTo(m_adapter); + + // detail activity does not use footers (see above) + if (!(m_activity instanceof DetailActivity)) { + articles.add(new Article(Article.TYPE_AMR_FOOTER)); + m_adapter.notifyItemInserted(articles.size()); + } + + if (!headlinesLoader.getAppend()) + m_list.scrollToPosition(0); + + //m_adapter.notifyDataSetChanged(); + + if (headlinesLoader.getFirstIdChanged()) { + //if (m_activity.isSmallScreen() || !m_activity.isPortrait()) { + Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) + .setAction(R.string.reload, v -> refresh(false)).show(); + //} + } + + } else { + if (headlinesLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(); + } else { + + if (headlinesLoader.getLastErrorMessage() != null) { + m_activity.toast(m_activity.getString(headlinesLoader.getErrorMessage()) + "\n" + headlinesLoader.getLastErrorMessage()); + } else { + m_activity.toast(headlinesLoader.getErrorMessage()); + } + } + + } + + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(false); + } + + @Override + public void onLoaderReset(@NonNull Loader loader) { + + } public enum ArticlesSelection { ALL, NONE, UNREAD } @@ -98,7 +202,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { private String m_searchQuery = ""; private boolean m_refreshInProgress = false; private int m_firstId = 0; - private boolean m_lazyLoadDisabled = false; + //private boolean m_lazyLoadDisabled = false; private SharedPreferences m_prefs; @@ -251,7 +355,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activeArticleId = savedInstanceState.getInt("m_activeArticleId"); m_searchQuery = savedInstanceState.getString("m_searchQuery"); m_firstId = savedInstanceState.getInt("m_firstId"); - m_lazyLoadDisabled = savedInstanceState.getBoolean("m_lazyLoadDisabled"); m_compactLayoutMode = savedInstanceState.getBoolean("m_compactLayoutMode"); } @@ -268,7 +371,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { out.putInt("m_activeArticleId", m_activeArticleId); out.putString("m_searchQuery", m_searchQuery); out.putInt("m_firstId", m_firstId); - out.putBoolean("m_lazyLoadDisabled", m_lazyLoadDisabled); out.putBoolean("m_compactLayoutMode", m_compactLayoutMode); } @@ -287,7 +389,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_swipeLayout = view.findViewById(R.id.headlines_swipe_container); - m_swipeLayout.setOnRefreshListener(() -> refresh(false, true)); + m_swipeLayout.setOnRefreshListener(() -> refresh(false)); m_list = view.findViewById(R.id.headlines_list); registerForContextMenu(m_list); @@ -394,6 +496,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { new Handler().postDelayed(() -> m_activity.refresh(false), 100); } + + int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); + + if (lastVisibleItem >= Application.getArticles().size() - 5) + new Handler().postDelayed(() -> refresh(true), 100); } } @@ -423,11 +530,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } } - if (!m_refreshInProgress && !m_lazyLoadDisabled && lastVisibleItem >= Application.getArticles().size() - 5) { + /*if (!m_refreshInProgress && !m_lazyLoadDisabled && lastVisibleItem >= Application.getArticles().size() - 5) { m_refreshInProgress = true; new Handler().postDelayed(() -> refresh(true), 100); - } + }*/ + /* if (lastVisibleItem >= Application.getArticles().size() - 5) + new Handler().postDelayed(() -> refresh(true), 100); */ } }); @@ -444,7 +553,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void onResume() { super.onResume(); - if (Application.getArticles().isEmpty()) { + m_loader = (HeadlinesLoader) LoaderManager.getInstance(this).initLoader(0, null, this); + + if (Application.getArticles().getSizeWithoutFooters() == 0) { refresh(false); } else { Article activeArticle = Application.getArticles().getById(m_activeArticleId); @@ -464,11 +575,20 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_listener = (HeadlinesEventListener) activity; } - public void refresh(boolean append) { - refresh(append, false); + public void refresh(final boolean append) { + + if (!(m_activity instanceof DetailActivity)) { + // detail activity does not use footers because it would break 1-to-1 mapping with pager view + // pager will need to work on a footerless subset of shared article view before this is possible + + Application.getArticles().add(new Article(Article.TYPE_LOADMORE)); + m_adapter.notifyDataSetChanged(); + } + + m_loader.refresh(append); } - public void refresh(final boolean append, boolean userInitiated) { + /* public void __refresh(final boolean append) { Application.getArticles().stripFooters(); m_adapter.notifyDataSetChanged(); @@ -557,10 +677,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { final int skip = getSkip(append); final boolean allowForceUpdate = m_activity.getApiLevel() >= 9 && - !m_feed.is_cat && m_feed.id > 0 && !append && userInitiated && - skip == 0; + !m_feed.is_cat && m_feed.id > 0 && !append && skip == 0; - Log.d(TAG, "allowForceUpdate=" + allowForceUpdate + " userInitiated=" + userInitiated + " skip=" + skip); + Log.d(TAG, "allowForceUpdate=" + allowForceUpdate + " skip=" + skip); req.setOffset(skip); @@ -636,7 +755,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } return skip; - } + } */ static class ArticleViewHolder extends RecyclerView.ViewHolder { public View view; @@ -1531,11 +1650,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void setSelection(ArticlesSelection select) { - for (Article a : Application.getArticles()) + ArticleList articlesWithoutFooters = Application.getArticles().getWithoutFooters(); + + for (Article a : articlesWithoutFooters) a.selected = false; if (select != ArticlesSelection.NONE) { - for (Article a : Application.getArticles()) { + for (Article a : articlesWithoutFooters) { if (select == ArticlesSelection.ALL || select == ArticlesSelection.UNREAD && a.unread) { a.selected = true; } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java new file mode 100755 index 00000000..5cd7503c --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -0,0 +1,292 @@ +package org.fox.ttrss; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import androidx.loader.content.AsyncTaskLoader; +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +import org.fox.ttrss.ApiCommon.ApiError; +import org.fox.ttrss.types.Article; +import org.fox.ttrss.types.ArticleList; +import org.fox.ttrss.types.Feed; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; + +public class HeadlinesLoader extends AsyncTaskLoader implements ApiCommon.ApiCaller { + private final String TAG = this.getClass().getSimpleName(); + + private final int m_responseCode = 0; + protected String m_responseMessage; + private int m_apiStatusCode = 0; + + private Context m_context; + private String m_lastErrorMessage; + private ApiError m_lastError; + private ArticleList m_articles; + private Feed m_feed; + private SharedPreferences m_prefs; + private int m_firstId; + private String m_searchQuery = ""; + private boolean m_firstIdChanged; + private int m_offset; + private int m_amountLoaded; + private int m_resizeWidth; + private boolean m_append; + private boolean m_lazyLoadEnabled; + private boolean m_loadingInProgress; + + HeadlinesLoader(Context context, Feed feed, int resizeWidth) { + super(context); + + m_context = context; + m_lastError = ApiError.NO_ERROR; + m_feed = feed; + m_articles = new ArticleList(); + m_resizeWidth = resizeWidth; + m_prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + protected void refresh(boolean append) { + Log.d(TAG, "refresh, append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled); + + if (!append) { + m_append = false; + m_lazyLoadEnabled = true; + + forceLoad(); + } else if (!m_loadingInProgress && m_lazyLoadEnabled) { + m_append = true; + forceLoad(); + } else { + deliverResult(m_articles); + } + } + + @Override + protected void onStartLoading() { + if (m_articles != null) { + deliverResult(m_articles); + } else { + forceLoad(); + } + } + + @Override + public void deliverResult(ArticleList data) { + m_articles = data; + + super.deliverResult(m_articles); + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + ApiError getLastError() { + return m_lastError; + } + + String getLastErrorMessage() { + return m_lastErrorMessage; + } + + public boolean lazyLoadEnabled() { + return m_lazyLoadEnabled; + } + + @Override + public ArticleList loadInBackground() { + + m_loadingInProgress = true; + + final int skip = getSkip(m_append); + final boolean allowForceUpdate = Application.getInstance().getApiLevel() >= 9 && + !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; + + HashMap params = new HashMap<>(); + + params.put("op", "getHeadlines"); + params.put("sid", Application.getInstance().getSessionId()); + params.put("feed_id", String.valueOf(m_feed.id)); + params.put("show_excerpt", "true"); + params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); + params.put("show_content", "true"); + params.put("include_attachments", "true"); + params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); + params.put("limit", m_prefs.getString("headlines_request_size", "15")); + params.put("skip", String.valueOf(skip)); + params.put("include_nested", "true"); + params.put("has_sandbox", "true"); + params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); + + if (m_prefs.getBoolean("enable_image_downsampling", false)) { + if (m_prefs.getBoolean("always_downsample_images", false) || !Application.getInstance().isWifiConnected()) { + params.put("resize_width", String.valueOf(m_resizeWidth)); + } + } + + if (m_feed.is_cat) + params.put("is_cat", "true"); + + if (allowForceUpdate) { + params.put("force_update", "true"); + } + + if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + params.put("search", m_searchQuery); + params.put("search_mode", ""); + params.put("match_on", "both"); + } + + if (m_firstId > 0) + params.put("check_first_id", String.valueOf(m_firstId)); + + if (Application.getInstance().getApiLevel() >= 12) { + params.put("include_header", "true"); + } + + Log.d(TAG, "request more headlines, firstId=" + m_firstId + ", append=" + m_append + ", skip=" + skip); + + JsonElement result = ApiCommon.performRequest(m_context, params, this); + + Log.d(TAG, "got result=" + result); + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + final List
articlesJson; + final JsonObject header; + + if (Application.getInstance().getApiLevel() >= 12) { + header = content.get(0).getAsJsonObject(); + + m_firstIdChanged = header.get("first_id_changed") != null; + + try { + m_firstId = header.get("first_id").getAsInt(); + } catch (NumberFormatException e) { + m_firstId = 0; + } + + Log.d(TAG, "firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); + + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content.get(1), listType); + } else { + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content, listType); + } + + if (skip == 0) + m_articles.clear(); + /* else + m_articles.stripFooters(); */ + + m_amountLoaded = articlesJson.size(); + + for (Article f : articlesJson) + if (!m_articles.containsId(f.id)) { + f.collectMediaInfo(); + f.cleanupExcerpt(); + m_articles.add(f); + } + + if (m_firstIdChanged) { + Log.d(TAG, "first id changed, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { + Log.d(TAG, "amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + m_loadingInProgress = false; + + return m_articles; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + m_loadingInProgress = false; + + return null; + + /* TODO move to onLoaderFinished() if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(); + } else { + + if (m_lastErrorMessage != null) { + m_activity.toast(m_activity.getString(getErrorMessage()) + "\n" + m_lastErrorMessage); + } else { + m_activity.toast(getErrorMessage()); + } + //m_activity.setLoadingStatus(getErrorMessage(), false); + } */ + } + + private int getSkip(boolean append) { + int skip = 0; + + if (append) { + // adaptive, all_articles, marked, published, unread + String viewMode = m_prefs.getString("view_mode", "adaptive"); + + int numUnread = Math.toIntExact(m_articles.getUnreadCount()); + int numAll = Math.toIntExact(m_articles.getSizeWithoutFooters()); + + if ("marked".equals(viewMode)) { + skip = numAll; + } else if ("published".equals(viewMode)) { + skip = numAll; + } else if ("unread".equals(viewMode)) { + skip = numUnread; + } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + skip = numAll; + } else if ("adaptive".equals(viewMode)) { + skip = numUnread > 0 ? numUnread : numAll; + } else { + skip = numAll; + } + } + + return skip; + } + + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } + + @Override + public void setLastError(ApiError lastError) { + m_lastError = lastError; + } + + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } + + public boolean getFirstIdChanged() { + return m_firstIdChanged; + } + + public boolean getAppend() { + return m_append; + } +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index 7d476ad6..ec620337 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -3,6 +3,8 @@ package org.fox.ttrss.types; import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.NonNull; + import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -268,7 +270,7 @@ public class Article implements Parcelable { return false; } } - + @SuppressWarnings("rawtypes") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { -- cgit v1.2.3-54-g00ecf From 29cb7c23487c7768d0b1854a377b0e8317208000 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 07:30:16 +0300 Subject: better deal with article type loadmore --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 35 ++++++++-------------- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 6 ++-- 2 files changed, 16 insertions(+), 25 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 0030d2a6..abd848d7 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 @@ -138,28 +138,28 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements // successful update if (data != null) { - ArticleList articles = Application.getArticles(); - articles.stripFooters(); + // detail activity does not use footers + if (!(m_activity instanceof DetailActivity)) { - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(articles, data)); + if (headlinesLoader.lazyLoadEnabled()) + data.add(new Article(Article.TYPE_LOADMORE)); - articles.clear(); - articles.addAll(data); + data.add(new Article(Article.TYPE_AMR_FOOTER)); + } - diffResult.dispatchUpdatesTo(m_adapter); + ArticleList sharedArticles = Application.getArticles(); - // detail activity does not use footers (see above) - if (!(m_activity instanceof DetailActivity)) { - articles.add(new Article(Article.TYPE_AMR_FOOTER)); - m_adapter.notifyItemInserted(articles.size()); - } + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(sharedArticles, data)); + + sharedArticles.clear(); + sharedArticles.addAll(data); + + diffResult.dispatchUpdatesTo(m_adapter); if (!headlinesLoader.getAppend()) m_list.scrollToPosition(0); - //m_adapter.notifyDataSetChanged(); - if (headlinesLoader.getFirstIdChanged()) { //if (m_activity.isSmallScreen() || !m_activity.isPortrait()) { Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) @@ -576,15 +576,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } public void refresh(final boolean append) { - - if (!(m_activity instanceof DetailActivity)) { - // detail activity does not use footers because it would break 1-to-1 mapping with pager view - // pager will need to work on a footerless subset of shared article view before this is possible - - Application.getArticles().add(new Article(Article.TYPE_LOADMORE)); - m_adapter.notifyDataSetChanged(); - } - m_loader.refresh(append); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index 5cd7503c..a837e0d9 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -42,7 +42,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api private int m_amountLoaded; private int m_resizeWidth; private boolean m_append; - private boolean m_lazyLoadEnabled; + private boolean m_lazyLoadEnabled = true; private boolean m_loadingInProgress; HeadlinesLoader(Context context, Feed feed, int resizeWidth) { @@ -190,8 +190,8 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api if (skip == 0) m_articles.clear(); - /* else - m_articles.stripFooters(); */ + else + m_articles.stripFooters(); m_amountLoaded = articlesJson.size(); -- cgit v1.2.3-54-g00ecf From 8bdf07f3d92f457823088364cc2d3e85d12b1ebc Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 07:32:19 +0300 Subject: invoke onHeadlinesLoaded --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'org.fox.ttrss/src/main/java') 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 abd848d7..a052e670 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 @@ -167,6 +167,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements //} } + m_listener.onHeadlinesLoaded(headlinesLoader.getAppend()); + } else { if (headlinesLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(); -- cgit v1.2.3-54-g00ecf From c2413dd121e509ac1012ad7026accbae5e16a242 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 07:55:01 +0300 Subject: implement search query stuff into headlines loader, split loader IDs --- .../src/main/java/org/fox/ttrss/Application.java | 4 ++++ .../src/main/java/org/fox/ttrss/DetailActivity.java | 10 +++++----- .../main/java/org/fox/ttrss/FeedCategoriesFragment.java | 4 ++-- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 4 ++-- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 15 +++++---------- .../src/main/java/org/fox/ttrss/HeadlinesLoader.java | 8 ++++++++ .../src/main/java/org/fox/ttrss/OnlineActivity.java | 1 + 7 files changed, 27 insertions(+), 19 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index 9ad069f1..81ae7bdf 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -11,6 +11,10 @@ import java.util.HashMap; import java.util.LinkedHashMap; public class Application extends android.app.Application { + public static final int LOADER_HEADLINES = 0; + public static final int LOADER_FEEDS = 1; + public static final int LOADER_CATS = 2; + private static Application m_singleton; // this is the only instance of a (large) object which contains all currently loaded articles and is diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 6558c2d1..048156ce 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -149,17 +149,17 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - final HeadlinesFragment hf = new HeadlinesFragment(); + HeadlinesFragment hf = new HeadlinesFragment(); hf.initialize(feed, openedArticleId, true); hf.setSearchQuery(searchQuery); ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - ArticlePager af = new ArticlePager(); - af.initialize(openedArticleId, feed); - af.setSearchQuery(searchQuery); + ArticlePager ap = new ArticlePager(); + ap.initialize(openedArticleId, feed); + ap.setSearchQuery(searchQuery); - ft.replace(R.id.article_fragment, af, FRAG_ARTICLE); + ft.replace(R.id.article_fragment, ap, FRAG_ARTICLE); ft.commit(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java index 08a656e4..abdb320d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java @@ -312,7 +312,7 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt public void onResume() { super.onResume(); - LoaderManager.getInstance(this).initLoader(0, null, this).forceLoad(); + LoaderManager.getInstance(this).initLoader(Application.LOADER_CATS, null, this).forceLoad(); m_activity.invalidateOptionsMenu(); } @@ -324,7 +324,7 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - LoaderManager.getInstance(this).restartLoader(0, null, this).forceLoad(); + LoaderManager.getInstance(this).restartLoader(Application.LOADER_CATS, null, this).forceLoad(); } private class FeedCategoryListAdapter extends ArrayAdapter { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java index 495180bb..7c6a2237 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java @@ -366,7 +366,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi public void onResume() { super.onResume(); - LoaderManager.getInstance(this).initLoader(0, null, this).forceLoad(); + LoaderManager.getInstance(this).initLoader(Application.LOADER_FEEDS, null, this).forceLoad(); m_activity.invalidateOptionsMenu(); } @@ -404,7 +404,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_swipeLayout.setRefreshing(true); } - LoaderManager.getInstance(this).restartLoader(0, null, this).forceLoad(); + LoaderManager.getInstance(this).restartLoader(Application.LOADER_FEEDS, null, this).forceLoad(); } private class FeedListAdapter extends ArrayAdapter { 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 a052e670..22601897 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 @@ -121,9 +121,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - - private HeadlinesLoader m_loader; - @NonNull @Override public Loader onCreateLoader(int id, @Nullable Bundle args) { @@ -555,8 +552,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public void onResume() { super.onResume(); - m_loader = (HeadlinesLoader) LoaderManager.getInstance(this).initLoader(0, null, this); - if (Application.getArticles().getSizeWithoutFooters() == 0) { refresh(false); } else { @@ -578,7 +573,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } public void refresh(final boolean append) { - m_loader.refresh(append); + HeadlinesLoader loader = (HeadlinesLoader) LoaderManager.getInstance(this).initLoader(Application.LOADER_HEADLINES, null, this); + + loader.setSearchQuery(getSearchQuery()); + loader.refresh(append); } /* public void __refresh(final boolean append) { @@ -1673,10 +1671,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (!m_searchQuery.equals(query)) { m_searchQuery = query; - // could be called before fragment view has been initialized - if (m_list != null) { - refresh(false); - } + refresh(false); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index a837e0d9..7f3dddac 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -289,4 +289,12 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api public boolean getAppend() { return m_append; } + + public void setSearchQuery(String searchQuery) { + m_searchQuery = searchQuery; + } + + public String getSearchQuery() { + return m_searchQuery; + } } 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 92a3f219..ec51cc98 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 @@ -391,6 +391,7 @@ public class OnlineActivity extends CommonActivity { } else if (itemId == R.id.search) { if (hf != null) { final EditText edit = new EditText(this); + edit.setText(hf.getSearchQuery()); MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) .setTitle(R.string.search) -- cgit v1.2.3-54-g00ecf From 524dc199ea76d5cc9e08e79c9279de2ed76afe69 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 08:48:58 +0300 Subject: prevent loader from triggering callbacks twice, cleanup --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 7 - .../main/java/org/fox/ttrss/HeadlinesFragment.java | 211 ++------------------- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 41 ++-- .../src/main/java/org/fox/ttrss/types/Article.java | 2 - .../main/java/org/fox/ttrss/types/ArticleList.java | 3 - 5 files changed, 31 insertions(+), 233 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 46cf36ee..3cd73734 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java @@ -3,7 +3,6 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.app.Activity; import android.content.SharedPreferences; -import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; import android.util.Log; @@ -18,14 +17,8 @@ import androidx.preference.PreferenceManager; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; -import com.google.android.material.snackbar.Snackbar; -import com.google.gson.JsonElement; - import org.fox.ttrss.types.Article; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.util.HeadlinesRequest; - -import java.util.HashMap; public class ArticlePager extends androidx.fragment.app.Fragment { 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 22601897..df59d863 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 @@ -1,6 +1,5 @@ package org.fox.ttrss; -import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; @@ -70,21 +69,18 @@ import com.bumptech.glide.request.target.Target; import com.google.android.material.button.MaterialButton; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; -import com.google.gson.JsonElement; import org.fox.ttrss.glide.ProgressTarget; import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Attachment; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.util.HeadlinesRequest; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.HashMap; import java.util.TimeZone; import jp.wasabeef.glide.transformations.CropCircleTransformation; @@ -129,7 +125,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public void onLoadFinished(@NonNull Loader loader, ArticleList data) { - Log.d(TAG, "onLoadFinished loader=" + loader + " count=" + data.size()); + Log.d(TAG, "onLoadFinished loader=" + loader); HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; @@ -154,9 +150,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements diffResult.dispatchUpdatesTo(m_adapter); - if (!headlinesLoader.getAppend()) - m_list.scrollToPosition(0); - if (headlinesLoader.getFirstIdChanged()) { //if (m_activity.isSmallScreen() || !m_activity.isPortrait()) { Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) @@ -170,14 +163,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (headlinesLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(); } else { - if (headlinesLoader.getLastErrorMessage() != null) { m_activity.toast(m_activity.getString(headlinesLoader.getErrorMessage()) + "\n" + headlinesLoader.getLastErrorMessage()); } else { m_activity.toast(headlinesLoader.getErrorMessage()); } } - } if (m_swipeLayout != null) @@ -199,7 +190,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements private Feed m_feed; private int m_activeArticleId; private String m_searchQuery = ""; - private boolean m_refreshInProgress = false; + private HeadlinesLoader m_loader; private int m_firstId = 0; //private boolean m_lazyLoadDisabled = false; @@ -483,14 +474,15 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (!m_readArticles.isEmpty()) { m_activity.toggleArticlesUnread(m_readArticles); - for (Article a : m_readArticles) + for (Article a : m_readArticles) { a.unread = false; + m_adapter.notifyItemChanged(Application.getArticles().getPositionById(a.id)); + } + if (m_feed != null) m_feed.unread -= m_readArticles.size(); - m_adapter.notifyDataSetChanged(); - m_readArticles.clear(); new Handler().postDelayed(() -> m_activity.refresh(false), 100); @@ -499,7 +491,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); if (lastVisibleItem >= Application.getArticles().size() - 5) - new Handler().postDelayed(() -> refresh(true), 100); + refresh(true); + + new Handler().postDelayed(() -> refresh(true), 0); } } @@ -529,13 +523,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - /*if (!m_refreshInProgress && !m_lazyLoadDisabled && lastVisibleItem >= Application.getArticles().size() - 5) { - m_refreshInProgress = true; - new Handler().postDelayed(() -> refresh(true), 100); - }*/ - /* if (lastVisibleItem >= Application.getArticles().size() - 5) - new Handler().postDelayed(() -> refresh(true), 100); */ + new Handler().postDelayed(() -> refresh(true), 1000); */ } }); @@ -573,180 +562,16 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } public void refresh(final boolean append) { - HeadlinesLoader loader = (HeadlinesLoader) LoaderManager.getInstance(this).initLoader(Application.LOADER_HEADLINES, null, this); - - loader.setSearchQuery(getSearchQuery()); - loader.refresh(append); - } - - /* public void __refresh(final boolean append) { - Application.getArticles().stripFooters(); - m_adapter.notifyDataSetChanged(); - - if (!append) m_lazyLoadDisabled = false; - - if (m_activity != null && isAdded() && m_feed != null) { - m_refreshInProgress = true; - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - if (!append) { - m_activity.getSupportActionBar().show(); - Application.getArticles().clear(); - m_adapter.notifyDataSetChanged(); - } else if (!(m_activity instanceof DetailActivity)) { - // detail activity does not use footers because it would break 1-to-1 mapping with pager view - // pager will need to work on a footerless subset of shared article view before this is possible - Application.getArticles().add(new Article(Article.TYPE_LOADMORE)); - m_adapter.notifyDataSetChanged(); - } - - final String sessionId = m_activity.getSessionId(); - final boolean isCat = m_feed.is_cat; - - @SuppressLint("StaticFieldLeak") HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity, Application.getArticles()) { - @Override - protected void onPostExecute(JsonElement result) { - if (isDetached() || !isAdded()) return; - - super.onPostExecute(result); - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - - m_refreshInProgress = false; - - if (result != null) { - - // is this needed? - if (!Application.getArticles().containsId(m_activeArticleId)) - m_activeArticleId = 0; - - if (m_firstIdChanged) { - m_lazyLoadDisabled = true; - - Log.d(TAG, "first id changed, disabling lazy load"); - - // article pager deals with this in tablet landscape view - if (m_activity.isSmallScreen() || !m_activity.isPortrait()) { - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); - } - } - - if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - // Log.d(TAG, "amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); - m_lazyLoadDisabled = true; - } - - HeadlinesFragment.this.m_firstId = m_firstId; - - m_adapter.notifyDataSetChanged(); - m_listener.onHeadlinesLoaded(append); - - } else { - m_lazyLoadDisabled = true; - - if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - if (m_lastErrorMessage != null) { - m_activity.toast(getString(getErrorMessage()) + "\n" + m_lastErrorMessage); - } else { - m_activity.toast(getErrorMessage()); - } - } - } - - // detail activity does not use footers (see above) - if (!(m_activity instanceof DetailActivity)) { - Application.getArticles().add(new Article(Article.TYPE_AMR_FOOTER)); - m_adapter.notifyDataSetChanged(); - } - } - }; - - final int skip = getSkip(append); - - final boolean allowForceUpdate = m_activity.getApiLevel() >= 9 && - !m_feed.is_cat && m_feed.id > 0 && !append && skip == 0; - - Log.d(TAG, "allowForceUpdate=" + allowForceUpdate + " skip=" + skip); - - req.setOffset(skip); - - HashMap map = new HashMap<>(); - map.put("op", "getHeadlines"); - map.put("sid", sessionId); - map.put("feed_id", String.valueOf(m_feed.id)); - map.put("show_excerpt", "true"); - map.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); - map.put("show_content", "true"); - map.put("include_attachments", "true"); - map.put("view_mode", m_activity.getViewMode()); - map.put("limit", m_prefs.getString("headlines_request_size", "15")); - map.put("offset", String.valueOf(0)); - map.put("skip", String.valueOf(skip)); - map.put("include_nested", "true"); - map.put("has_sandbox", "true"); - map.put("order_by", m_activity.getSortMode()); - - if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (m_prefs.getBoolean("always_downsample_images", false) || !m_activity.isWifiConnected()) { - map.put("resize_width", String.valueOf(m_activity.getResizeWidth())); - } - } - - if (isCat) map.put("is_cat", "true"); - - if (allowForceUpdate) { - map.put("force_update", "true"); - } - - if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - map.put("search", m_searchQuery); - map.put("search_mode", ""); - map.put("match_on", "both"); - } - - if (m_firstId > 0) map.put("check_first_id", String.valueOf(m_firstId)); - - if (m_activity.getApiLevel() >= 12) { - map.put("include_header", "true"); - } - - Log.d(TAG, "[HP] request more headlines, firstId=" + m_firstId); - - req.execute(map); + // if we try to initLoader() all the time, onLoadFinished() might be sent twice + // https://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice + if (m_loader == null) { + m_loader = (HeadlinesLoader) LoaderManager.getInstance(this). + initLoader(Application.LOADER_HEADLINES, null, this); } - } - private int getSkip(boolean append) { - int skip = 0; - - if (append) { - // adaptive, all_articles, marked, published, unread - String viewMode = m_activity.getViewMode(); - - int numUnread = Math.toIntExact(Application.getArticles().getUnreadCount()); - int numAll = Math.toIntExact(Application.getArticles().getSizeWithoutFooters()); - - if ("marked".equals(viewMode)) { - skip = numAll; - } else if ("published".equals(viewMode)) { - skip = numAll; - } else if ("unread".equals(viewMode)) { - skip = numUnread; - } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - skip = numAll; - } else if ("adaptive".equals(viewMode)) { - skip = numUnread > 0 ? numUnread : numAll; - } else { - skip = numAll; - } - } - - return skip; - } */ + m_loader.setSearchQuery(getSearchQuery()); + m_loader.startLoading(append); + } static class ArticleViewHolder extends RecyclerView.ViewHolder { public View view; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index 7f3dddac..63a1bae4 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -56,36 +56,27 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_prefs = PreferenceManager.getDefaultSharedPreferences(context); } - protected void refresh(boolean append) { - Log.d(TAG, "refresh, append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled); + protected void startLoading(boolean append) { + // Log.d(TAG, this + " refresh, append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled); if (!append) { m_append = false; m_lazyLoadEnabled = true; forceLoad(); - } else if (!m_loadingInProgress && m_lazyLoadEnabled) { + } else if (m_lazyLoadEnabled && !m_loadingInProgress) { m_append = true; forceLoad(); - } else { - deliverResult(m_articles); - } - } - - @Override - protected void onStartLoading() { - if (m_articles != null) { - deliverResult(m_articles); - } else { - forceLoad(); + /* } else { + deliverResult(m_articles); */ } } @Override public void deliverResult(ArticleList data) { - m_articles = data; + Log.d(TAG, "deliverResult=" + data); - super.deliverResult(m_articles); + super.deliverResult(data); } public int getErrorMessage() { @@ -106,6 +97,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api @Override public ArticleList loadInBackground() { + Log.d(TAG, "loadInBackground append=" + m_append + " offset=" + m_offset); m_loadingInProgress = true; @@ -212,6 +204,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_lazyLoadEnabled = false; } + m_offset += m_amountLoaded; m_loadingInProgress = false; return m_articles; @@ -225,18 +218,6 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_loadingInProgress = false; return null; - - /* TODO move to onLoaderFinished() if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(); - } else { - - if (m_lastErrorMessage != null) { - m_activity.toast(m_activity.getString(getErrorMessage()) + "\n" + m_lastErrorMessage); - } else { - m_activity.toast(getErrorMessage()); - } - //m_activity.setLoadingStatus(getErrorMessage(), false); - } */ } private int getSkip(boolean append) { @@ -297,4 +278,8 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api public String getSearchQuery() { return m_searchQuery; } + + public int getOffset() { + return m_offset; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index ec620337..800ca55f 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -3,8 +3,6 @@ package org.fox.ttrss.types; import android.os.Parcel; import android.os.Parcelable; -import androidx.annotation.NonNull; - import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java index 873b311b..01af780c 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java @@ -1,8 +1,5 @@ package org.fox.ttrss.types; -import android.os.Parcel; -import android.os.Parcelable; - import java.util.ListIterator; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; -- cgit v1.2.3-54-g00ecf From 29fbead156856281895163c87ec91c20e1651b3e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 08:51:42 +0300 Subject: move diffutil callback to an external file --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 31 +------------------ .../fox/ttrss/util/HeadlinesDiffutilCallback.java | 35 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java (limited to 'org.fox.ttrss/src/main/java') 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 df59d863..e008173a 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 @@ -75,6 +75,7 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Attachment; import org.fox.ttrss.types.Feed; +import org.fox.ttrss.util.HeadlinesDiffutilCallback; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -87,36 +88,6 @@ import jp.wasabeef.glide.transformations.CropCircleTransformation; public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { - public class HeadlinesDiffutilCallback extends DiffUtil.Callback { - private ArticleList m_oldList; - private ArticleList m_newList; - - public HeadlinesDiffutilCallback(ArticleList oldList, ArticleList newList) { - m_oldList = oldList; - m_newList = newList; - } - - @Override - public int getOldListSize() { - return m_oldList != null ? m_oldList.size() : 0; - } - - @Override - public int getNewListSize() { - return m_newList != null ? m_newList.size() : 0; - } - - @Override - public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; - } - - @Override - public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - return false; - } - } - @NonNull @Override public Loader onCreateLoader(int id, @Nullable Bundle args) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java new file mode 100644 index 00000000..b968e285 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java @@ -0,0 +1,35 @@ +package org.fox.ttrss.util; + +import androidx.recyclerview.widget.DiffUtil; + +import org.fox.ttrss.types.ArticleList; + +public class HeadlinesDiffutilCallback extends DiffUtil.Callback { + private ArticleList m_oldList; + private ArticleList m_newList; + + public HeadlinesDiffutilCallback(ArticleList oldList, ArticleList newList) { + m_oldList = oldList; + m_newList = newList; + } + + @Override + public int getOldListSize() { + return m_oldList != null ? m_oldList.size() : 0; + } + + @Override + public int getNewListSize() { + return m_newList != null ? m_newList.size() : 0; + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return false; + } + } -- cgit v1.2.3-54-g00ecf From f42eccb9cabde4bb8db1541626150fcdeaefdbcc Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 09:25:05 +0300 Subject: better handle detail activity crash on activity result --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 30 ++++++++++++++++------ .../main/java/org/fox/ttrss/MasterActivity.java | 14 +++++++--- 2 files changed, 33 insertions(+), 11 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 e008173a..b98aa6c8 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 @@ -459,12 +459,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements new Handler().postDelayed(() -> m_activity.refresh(false), 100); } - int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); + /* int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); - if (lastVisibleItem >= Application.getArticles().size() - 5) + if (lastVisibleItem >= Application.getArticles().size() - 5) { refresh(true); - - new Handler().postDelayed(() -> refresh(true), 0); + } */ } } @@ -494,8 +493,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - /* if (lastVisibleItem >= Application.getArticles().size() - 5) - new Handler().postDelayed(() -> refresh(true), 1000); */ + if (lastVisibleItem >= Application.getArticles().size() - 5) + new Handler().postDelayed(() -> refresh(true), 1000); } }); @@ -1423,16 +1422,31 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } public void scrollToArticleId(int id) { - m_list.scrollToPosition(Application.getArticles().getPositionById(id)); + int position = Application.getArticles().getPositionById(id); + + if (position != -1) + m_list.scrollToPosition(position); } public void setActiveArticleId(int articleId) { if (m_list != null && articleId != m_activeArticleId) { + ArticleList articles = Application.getArticles(); + + int oldPosition = articles.getPositionById(m_activeArticleId); + int newPosition = articles.getPositionById(articleId); + m_activeArticleId = articleId; - m_adapter.notifyDataSetChanged(); + + if (oldPosition != -1) + m_adapter.notifyItemChanged(oldPosition); + + m_adapter.notifyItemChanged(newPosition); scrollToArticleId(articleId); + + if (newPosition >= articles.size() - 5) + refresh(true); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 9430ddc2..911d8ed7 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -530,10 +530,18 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList if (hf != null) { hf.notifyUpdated(); - // this makes position in headlines in master activity (not quite) randomly jump around when returning - // even if active article hasn't been changed, i guess keeping it as-is is a lesser evil? - hf.scrollToArticleId(data.getIntExtra("activeArticleId", 0)); + // data might be null if detailactivity crashed + if (data != null && data.getExtras() != null) { + int activeArticleId = data.getIntExtra("activeArticleId", 0); + + Log.d(TAG, "got back from detail activity, scrolling to id=" + activeArticleId); + + hf.scrollToArticleId(activeArticleId); + } } + + // just in case + new Handler().postDelayed((Runnable) () -> hf.refresh(true), 250); } } -- cgit v1.2.3-54-g00ecf From 5c3026d6c1b4a4e70b2e8ab0e93bc1731e1104d4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 09:40:01 +0300 Subject: cleanup refresh code from article pager, drop uri preload stuff --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 170 +-------------------- .../main/java/org/fox/ttrss/CommonActivity.java | 16 -- .../main/java/org/fox/ttrss/DetailActivity.java | 7 - .../main/java/org/fox/ttrss/HeadlinesFragment.java | 8 +- .../main/java/org/fox/ttrss/MasterActivity.java | 5 +- .../main/java/org/fox/ttrss/OnlineActivity.java | 6 - 6 files changed, 8 insertions(+), 204 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 3cd73734..eba87ab9 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 @@ -27,12 +27,8 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private HeadlinesEventListener m_listener; private int m_articleId; private OnlineActivity m_activity; - private String m_searchQuery = ""; private Feed m_feed; - private SharedPreferences m_prefs; private int m_firstId = 0; - private boolean m_refreshInProgress; - private boolean m_lazyLoadDisabled; private ViewPager2 m_pager; private static class PagerAdapter extends FragmentStateAdapter { @@ -72,10 +68,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_feed = feed; } - public void setSearchQuery(String searchQuery) { - m_searchQuery = searchQuery; - } - @Override public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); @@ -124,15 +116,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { if (article != null) { m_articleId = article.id; - new Handler().postDelayed(() -> m_listener.onArticleSelected(article, false), 250); - - //Log.d(TAG, "Page #" + position + "/" + m_adapter.getCount()); - - if (!m_refreshInProgress && !m_lazyLoadDisabled && (m_activity.isSmallScreen() || m_activity.isPortrait()) && position >= m_adapter.getItemCount() - 5) { - Log.d(TAG, "loading more articles..."); - - new Handler().postDelayed(() -> refresh(true), 100); - } + m_listener.onArticleSelected(article, false); } } }); @@ -140,164 +124,12 @@ public class ArticlePager extends androidx.fragment.app.Fragment { return view; } - protected void refresh(final boolean append) { - // - } - - /* protected void refresh(final boolean append) { - - if (!append) { - m_lazyLoadDisabled = false; - } - - m_refreshInProgress = true; - - @SuppressLint("StaticFieldLeak") HeadlinesRequest req = new HeadlinesRequest(getActivity().getApplicationContext(), m_activity, Application.getArticles()) { - @Override - protected void onPostExecute(JsonElement result) { - if (isDetached() || !isAdded()) return; - - if (!append) { - m_pager.setCurrentItem(0, false); - Application.getArticles().clear(); - } - - super.onPostExecute(result); - - m_refreshInProgress = false; - - if (result != null) { - - if (m_firstIdChanged) { - m_lazyLoadDisabled = true; - } - - if (m_firstIdChanged && !(m_activity instanceof DetailActivity && !m_activity.isPortrait())) { - //m_activity.toast(R.string.headlines_row_top_changed); - - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); - } - - if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - m_lazyLoadDisabled = true; - } - - ArticlePager.this.m_firstId = m_firstId; - - try { - m_adapter.notifyDataSetChanged(); - } catch (BadParcelableException e) { - if (getActivity() != null) { - getActivity().finish(); - return; - } - } - - if (!Application.getArticles().isEmpty()) { - if (Application.getArticles().getById(m_articleId) == null) { - Article article = Application.getArticles().get(0); - - m_articleId = article.id; - m_listener.onArticleSelected(article, false); - } - } - - } else { - m_lazyLoadDisabled = true; - - if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - m_activity.toast(getErrorMessage()); - } - } - } - }; - - final Feed feed = m_feed; - - final String sessionId = m_activity.getSessionId(); - int skip = 0; - - if (append) { - // adaptive, all_articles, marked, published, unread - String viewMode = m_activity.getViewMode(); - int numUnread = 0; - int numAll = Application.getArticles().size(); - - for (Article a : Application.getArticles()) { - if (a.unread) ++numUnread; - } - - if ("marked".equals(viewMode)) { - skip = numAll; - } else if ("published".equals(viewMode)) { - skip = numAll; - } else if ("unread".equals(viewMode)) { - skip = numUnread; - } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - skip = numAll; - } else if ("adaptive".equals(viewMode)) { - skip = numUnread > 0 ? numUnread : numAll; - } else { - skip = numAll; - } - } - - final int fskip = skip; - - req.setOffset(skip); - - HashMap map = new HashMap<>(); - map.put("op", "getHeadlines"); - map.put("sid", sessionId); - map.put("feed_id", String.valueOf(feed.id)); - map.put("show_excerpt", "true"); - map.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); - map.put("show_content", "true"); - map.put("include_attachments", "true"); - map.put("limit", m_prefs.getString("headlines_request_size", "15")); - map.put("offset", String.valueOf(0)); - map.put("view_mode", m_activity.getViewMode()); - map.put("skip", String.valueOf(fskip)); - map.put("include_nested", "true"); - map.put("has_sandbox", "true"); - map.put("order_by", m_activity.getSortMode()); - - if (feed.is_cat) map.put("is_cat", "true"); - - if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - map.put("search", m_searchQuery); - map.put("search_mode", ""); - map.put("match_on", "both"); - } - - if (m_firstId > 0) map.put("check_first_id", String.valueOf(m_firstId)); - - if (m_activity.getApiLevel() >= 12) { - map.put("include_header", "true"); - } - - if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (m_prefs.getBoolean("always_downsample_images", false) || !m_activity.isWifiConnected()) { - map.put("resize_width", String.valueOf(m_activity.getResizeWidth())); - } - } - - Log.d(TAG, "[AP] request more headlines, firstId=" + m_firstId); - - req.execute(map); - } */ - @Override public void onAttach(@NonNull Activity activity) { super.onAttach(activity); m_listener = (HeadlinesEventListener)activity; m_activity = (OnlineActivity)activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); } @SuppressLint("NewApi") 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 cdc261b7..5fac1f5b 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 @@ -330,22 +330,6 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc }); } - protected void preloadUriIfAllowed(Uri uri) { - boolean enableCustomTabs = m_prefs.getBoolean("enable_custom_tabs", true); - - if (m_customTabClient != null && enableCustomTabs) { - ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo info = cm.getActiveNetworkInfo(); - - if (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI) { - CustomTabsSession session = getCustomTabSession(); - session.mayLaunchUrl(uri, null, null); - - //toast("Preloading: " + uri.toString()); - } - } - } - protected Intent getShareIntent(String text, String subject) { Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 048156ce..f0716443 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -157,7 +157,6 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList ArticlePager ap = new ArticlePager(); ap.initialize(openedArticleId, feed); - ap.setSearchQuery(searchQuery); ft.replace(R.id.article_fragment, ap, FRAG_ARTICLE); @@ -280,12 +279,6 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList saveArticleUnread(article); } - try { - preloadUriIfAllowed(Uri.parse(article.link)); - } catch (Exception e) { - e.printStackTrace(); - } - if (!getSupportActionBar().isShowing()) getSupportActionBar().show(); if (open) { 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 b98aa6c8..52fe39c9 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 @@ -459,11 +459,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements new Handler().postDelayed(() -> m_activity.refresh(false), 100); } - /* int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); + int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); if (lastVisibleItem >= Application.getArticles().size() - 5) { refresh(true); - } */ + } } } @@ -493,8 +493,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - if (lastVisibleItem >= Application.getArticles().size() - 5) - new Handler().postDelayed(() -> refresh(true), 1000); + /* if (lastVisibleItem >= Application.getArticles().size() - 5) + new Handler().postDelayed(() -> refresh(true), 1000); */ } }); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 911d8ed7..fac3c0c0 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -528,14 +528,15 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) { + + // articles might've been changed while in detail activity hf.notifyUpdated(); // data might be null if detailactivity crashed - if (data != null && data.getExtras() != null) { + if (data != null) { int activeArticleId = data.getIntExtra("activeArticleId", 0); Log.d(TAG, "got back from detail activity, scrolling to id=" + activeArticleId); - hf.scrollToArticleId(activeArticleId); } } 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 ec51cc98..22c23ea1 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 @@ -1190,12 +1190,6 @@ article.score = Integer.parseInt(edit.getText().toString()); if (hf != null) { hf.refresh(false); } - - ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - if (af != null) { - af.refresh(false); - } } } -- cgit v1.2.3-54-g00ecf From 10644021b2e62ddc13fdaf33ba52485cf2213bdd Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 09:56:28 +0300 Subject: fix lazy load not working if catchup on scroll is disabled --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 3 --- .../main/java/org/fox/ttrss/DetailActivity.java | 22 ++++++---------- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 30 ++++++++++------------ 3 files changed, 22 insertions(+), 33 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 eba87ab9..91c31ef3 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 @@ -28,7 +28,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private int m_articleId; private OnlineActivity m_activity; private Feed m_feed; - private int m_firstId = 0; private ViewPager2 m_pager; private static class PagerAdapter extends FragmentStateAdapter { @@ -74,7 +73,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { out.putInt("m_articleId", m_articleId); out.putParcelable("m_feed", m_feed); - out.putInt("m_firstId", m_firstId); } @Override @@ -84,7 +82,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { if (savedInstanceState != null) { m_articleId = savedInstanceState.getInt("m_articleId"); m_feed = savedInstanceState.getParcelable("m_feed"); - m_firstId = savedInstanceState.getInt("m_firstId"); } setRetainInstance(true); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index f0716443..780fd723 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -270,10 +270,8 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList } @Override - public void onArticleSelected(final Article article, boolean open) { - - if (article == null) return; - + public void onArticleSelected(Article article, boolean open) { + if (article.unread) { article.unread = false; saveArticleUnread(article); @@ -281,18 +279,14 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList if (!getSupportActionBar().isShowing()) getSupportActionBar().show(); - if (open) { - - new Handler().postDelayed(() -> { - ArticlePager ap = (ArticlePager) DetailActivity.this.getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - if (ap != null) { - ap.setActiveArticleId(article.id); - } - }, 250); + ArticlePager ap = (ArticlePager) DetailActivity.this.getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + if (open) { + if (ap != null) { + ap.setActiveArticleId(article.id); + } } else { - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) { hf.setActiveArticleId(article.id); } 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 52fe39c9..92131da5 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 @@ -96,7 +96,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public void onLoadFinished(@NonNull Loader loader, ArticleList data) { - Log.d(TAG, "onLoadFinished loader=" + loader); + Log.d(TAG, "onLoadFinished loader=" + loader + " size=" + (data != null ? data.size() : "N/A (null)")); HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; @@ -162,8 +162,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements private int m_activeArticleId; private String m_searchQuery = ""; private HeadlinesLoader m_loader; - private int m_firstId = 0; - //private boolean m_lazyLoadDisabled = false; private SharedPreferences m_prefs; @@ -315,7 +313,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_feed = savedInstanceState.getParcelable("m_feed"); m_activeArticleId = savedInstanceState.getInt("m_activeArticleId"); m_searchQuery = savedInstanceState.getString("m_searchQuery"); - m_firstId = savedInstanceState.getInt("m_firstId"); m_compactLayoutMode = savedInstanceState.getBoolean("m_compactLayoutMode"); } @@ -331,7 +328,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements out.putParcelable("m_feed", m_feed); out.putInt("m_activeArticleId", m_activeArticleId); out.putString("m_searchQuery", m_searchQuery); - out.putInt("m_firstId", m_firstId); out.putBoolean("m_compactLayoutMode", m_compactLayoutMode); } @@ -441,22 +437,24 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - if (newState == RecyclerView.SCROLL_STATE_IDLE && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - if (!m_readArticles.isEmpty()) { - m_activity.toggleArticlesUnread(m_readArticles); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + if (!m_readArticles.isEmpty()) { + m_activity.toggleArticlesUnread(m_readArticles); - for (Article a : m_readArticles) { - a.unread = false; + for (Article a : m_readArticles) { + a.unread = false; - m_adapter.notifyItemChanged(Application.getArticles().getPositionById(a.id)); - } + m_adapter.notifyItemChanged(Application.getArticles().getPositionById(a.id)); + } - if (m_feed != null) - m_feed.unread -= m_readArticles.size(); + if (m_feed != null) + m_feed.unread -= m_readArticles.size(); - m_readArticles.clear(); + m_readArticles.clear(); - new Handler().postDelayed(() -> m_activity.refresh(false), 100); + new Handler().postDelayed(() -> m_activity.refresh(false), 100); + } } int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); -- cgit v1.2.3-54-g00ecf From 7a227f6789e9485d21cef818921ce8ca1d2c9a18 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 10:15:51 +0300 Subject: bind pager adapter to its parent fragment when created (instead of an activity) --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 91c31ef3..95cac5b5 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 @@ -31,9 +31,9 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private ViewPager2 m_pager; private static class PagerAdapter extends FragmentStateAdapter { - - public PagerAdapter(FragmentActivity fa) { - super(fa); + + public PagerAdapter(@NonNull Fragment fragment) { + super(fragment); } @Override @@ -91,7 +91,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - m_adapter = new PagerAdapter(getActivity()); + m_adapter = new PagerAdapter(this); m_pager = view.findViewById(R.id.article_pager); -- cgit v1.2.3-54-g00ecf From 589825304449c841f577845e37dfca64fe054396 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 10:47:11 +0300 Subject: let's try setting active article when going back from detail mode, also stop forced lazy updates on return --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 24 +++++++++++----------- .../main/java/org/fox/ttrss/MasterActivity.java | 7 ++----- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 92131da5..aa7cdddf 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 @@ -99,6 +99,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements Log.d(TAG, "onLoadFinished loader=" + loader + " size=" + (data != null ? data.size() : "N/A (null)")); HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; + ArticleList sharedArticles = Application.getArticles(); // successful update if (data != null) { @@ -112,8 +113,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements data.add(new Article(Article.TYPE_AMR_FOOTER)); } - ArticleList sharedArticles = Application.getArticles(); - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(sharedArticles, data)); sharedArticles.clear(); @@ -121,12 +120,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements diffResult.dispatchUpdatesTo(m_adapter); - if (headlinesLoader.getFirstIdChanged()) { - //if (m_activity.isSmallScreen() || !m_activity.isPortrait()) { - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); - //} - } + if (headlinesLoader.getFirstIdChanged()) + Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) + .setAction(R.string.reload, v -> refresh(false)).show(); + + if (!headlinesLoader.getAppend()) + m_list.scrollToPosition(0); m_listener.onHeadlinesLoaded(headlinesLoader.getAppend()); @@ -754,12 +753,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements holder.view.setOnClickListener(v -> { m_listener.onArticleSelected(article); + setActiveArticleId(article.id); // only set active article when it makes sense (in DetailActivity) - if (getActivity() instanceof DetailActivity) { - m_activeArticleId = article.id; - m_adapter.notifyDataSetChanged(); - } + //if (getActivity() instanceof DetailActivity) { + //m_activeArticleId = article.id; + //m_adapter.notifyDataSetChanged(); + //} }); // block footer clicks to make button/selection clicking easier diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index fac3c0c0..d74a58a3 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -528,7 +528,6 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) { - // articles might've been changed while in detail activity hf.notifyUpdated(); @@ -537,12 +536,10 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList int activeArticleId = data.getIntExtra("activeArticleId", 0); Log.d(TAG, "got back from detail activity, scrolling to id=" + activeArticleId); - hf.scrollToArticleId(activeArticleId); + hf.setActiveArticleId(activeArticleId); + //hf.scrollToArticleId(activeArticleId); } } - - // just in case - new Handler().postDelayed((Runnable) () -> hf.refresh(true), 250); } } -- cgit v1.2.3-54-g00ecf From 485356be541de32c2eb827c9ce49d40f4497d0b3 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 10:59:56 +0300 Subject: handle adapter state in selection without notifydatasetchanged --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 26 +++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 aa7cdddf..4956d37e 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 @@ -1451,20 +1451,24 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public void setSelection(ArticlesSelection select) { ArticleList articlesWithoutFooters = Application.getArticles().getWithoutFooters(); - for (Article a : articlesWithoutFooters) - a.selected = false; + for (Article a : articlesWithoutFooters) { + if (select == ArticlesSelection.ALL || select == ArticlesSelection.UNREAD && a.unread) { + a.selected = true; - if (select != ArticlesSelection.NONE) { - for (Article a : articlesWithoutFooters) { - if (select == ArticlesSelection.ALL || select == ArticlesSelection.UNREAD && a.unread) { - a.selected = true; - } + int position = Application.getArticles().getPositionById(a.id); + + if (position != -1) + m_adapter.notifyItemChanged(position); + + } else if (a.selected) { + a.selected = false; + + int position = Application.getArticles().getPositionById(a.id); + + if (position != -1) + m_adapter.notifyItemChanged(position); } } - - if (m_adapter != null) { - m_adapter.notifyDataSetChanged(); - } } public int getActiveArticleId() { -- cgit v1.2.3-54-g00ecf From 09d65139099e240fc16ab82ff657262e9b471394 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 11:04:59 +0300 Subject: notify specific item changed when editing note --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java | 8 +++++--- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 5 +++++ .../src/main/java/org/fox/ttrss/OnlineActivity.java | 12 ++++++++---- 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 95cac5b5..b3ff916d 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 @@ -133,9 +133,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { @Override public void onResume() { super.onResume(); - - //if (m_adapter != null) m_adapter.notifyDataSetChanged(); - m_activity.invalidateOptionsMenu(); } @@ -171,6 +168,11 @@ public class ArticlePager extends androidx.fragment.app.Fragment { return m_articleId; } + public void notifyItemChanged(int position) { + if (m_adapter != null) + m_adapter.notifyItemChanged(position); + } + public void notifyUpdated() { 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 4956d37e..d813faa8 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 @@ -150,6 +150,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } + public void notifyItemChanged(int position) { + if (m_adapter != null) + m_adapter.notifyItemChanged(position); + } + public enum ArticlesSelection { ALL, NONE, UNREAD } public static final int FLAVOR_IMG_MIN_SIZE = 128; 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 22c23ea1..a5be1d5b 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 @@ -714,11 +714,15 @@ public class OnlineActivity extends CommonActivity { saveArticleNote(article, note); - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - if (hf != null) hf.notifyUpdated(); + int position = Application.getArticles().getPositionById(article.id); - ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - if (ap != null) ap.notifyUpdated(); + if (position != -1) { + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + if (hf != null) hf.notifyItemChanged(position); + + ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + if (ap != null) ap.notifyItemChanged(position); + } }); builder.setNegativeButton(R.string.dialog_cancel, (dialog, which) -> { -- cgit v1.2.3-54-g00ecf From cbc8ab1cfb4ae8100dae7d2690209971a7574a09 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 11:08:18 +0300 Subject: stop notifying entire dataset changed on catchup above --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 d813faa8..80735817 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 @@ -253,19 +253,24 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements private void catchupAbove(Article article) { ArticleList tmp = new ArticleList(); - for (Article a : Application.getArticles()) { + ArticleList articles = Application.getArticles(); + for (Article a : articles) { if (article.equalsById(a)) break; if (a.unread) { a.unread = false; tmp.add(a); + + int position = articles.getPositionById(a.id); + + if (position != -1) + m_adapter.notifyItemChanged(position); } } if (!tmp.isEmpty()) { m_activity.setArticlesUnread(tmp, Article.UPDATE_SET_FALSE); - m_adapter.notifyDataSetChanged(); } } -- cgit v1.2.3-54-g00ecf From b89f4c615ffda5b85c748484766e04261b196ac8 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 11:37:26 +0300 Subject: drop headlinesrequest --- .../java/org/fox/ttrss/util/HeadlinesRequest.java | 106 --------------------- 1 file changed, 106 deletions(-) delete mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java deleted file mode 100755 index 82698ffb..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesRequest.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.fox.ttrss.util; - -import android.content.Context; -import android.util.Log; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.ApiCommon; -import org.fox.ttrss.ApiRequest; -import org.fox.ttrss.Application; -import org.fox.ttrss.OnlineActivity; -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; - -import java.lang.reflect.Type; -import java.util.List; - -public class HeadlinesRequest extends ApiRequest { - private final String TAG = this.getClass().getSimpleName(); - - private int m_offset = 0; - private final OnlineActivity m_activity; - protected boolean m_firstIdChanged = false; - protected int m_firstId = 0; - protected int m_amountLoaded = 0; - - public HeadlinesRequest(Context context, OnlineActivity activity, ArticleList articles) { - super(context); - - m_activity = activity; - } - - protected void onPostExecute(JsonElement result) { - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - final List
articlesJson; - final JsonObject header; - - if (m_activity.getApiLevel() >= 12) { - header = content.get(0).getAsJsonObject(); - - //Log.d(TAG, "headerID:" + header.get("top_id_changed")); - - m_firstIdChanged = header.get("first_id_changed") != null; - try { - m_firstId = header.get("first_id").getAsInt(); - } catch (NumberFormatException e) { - m_firstId = 0; - } - - Log.d(TAG, "firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); - - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content.get(1), listType); - } else { - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content, listType); - } - - ArticleList articles = Application.getArticles(); - - if (m_offset == 0) - articles.clear(); - else - articles.stripFooters(); - - m_amountLoaded = articlesJson.size(); - - for (Article f : articlesJson) - if (!articles.containsId(f.id)) { - f.collectMediaInfo(); - f.cleanupExcerpt(); - articles.add(f); - } - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (m_lastError == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(); - } else { - - if (m_lastErrorMessage != null) { - m_activity.toast(m_activity.getString(getErrorMessage()) + "\n" + m_lastErrorMessage); - } else { - m_activity.toast(getErrorMessage()); - } - //m_activity.setLoadingStatus(getErrorMessage(), false); - } - } - - public void setOffset(int skip) { - m_offset = skip; - } -} -- cgit v1.2.3-54-g00ecf From d39e4a85a666a8a4e256b334bdf476801faea28d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 11:59:03 +0300 Subject: try keeping local articlelist with footers on headlines fragment --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 35 ++++++++++++++++++---- .../main/java/org/fox/ttrss/MasterActivity.java | 4 +-- 2 files changed, 32 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 80735817..be72d52e 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 @@ -88,6 +88,8 @@ import jp.wasabeef.glide.transformations.CropCircleTransformation; public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { + private ArticleList m_articles = new ArticleList(); + @NonNull @Override public Loader onCreateLoader(int id, @Nullable Bundle args) { @@ -99,11 +101,16 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements Log.d(TAG, "onLoadFinished loader=" + loader + " size=" + (data != null ? data.size() : "N/A (null)")); HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; - ArticleList sharedArticles = Application.getArticles(); + //ArticleList sharedArticles = Application.getArticles(); // successful update if (data != null) { + // shared article list contains raw returned data without footers + ArticleList sharedArticles = Application.getArticles(); + sharedArticles.clear(); + sharedArticles.addAll(data); + // detail activity does not use footers if (!(m_activity instanceof DetailActivity)) { @@ -113,10 +120,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements data.add(new Article(Article.TYPE_AMR_FOOTER)); } - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(sharedArticles, data)); + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, data)); - sharedArticles.clear(); - sharedArticles.addAll(data); + m_articles.clear(); + m_articles.addAll(data); diffResult.dispatchUpdatesTo(m_adapter); @@ -364,7 +371,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_list.setLayoutManager(m_layoutManager); m_list.setItemAnimator(new DefaultItemAnimator()); - m_adapter = new ArticleListAdapter(Application.getArticles()); + m_articles.clear(); + m_articles.addAll(Application.getArticles()); + + m_adapter = new ArticleListAdapter(m_articles); m_list.setAdapter(m_adapter); @@ -539,6 +549,21 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } public void refresh(final boolean append) { + if (!append) { + m_activeArticleId = -1; + + int size = m_articles.size(); + + m_articles.clear(); + + if (m_adapter != null) { + m_adapter.notifyItemRangeRemoved(0, size); + + m_articles.add(new Article(Article.TYPE_LOADMORE)); + m_adapter.notifyItemInserted(m_articles.size()); + } + } + // if we try to initLoader() all the time, onLoadFinished() might be sent twice // https://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice if (m_loader == null) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index d74a58a3..5c148a35 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -476,7 +476,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList // we use shared article list, but detail activity does not use special footers // we will append those back (if needed) in onActivityResult() - Application.getArticles().stripFooters(); + // Application.getArticles().stripFooters(); startActivityForResult(intent, HEADLINES_REQUEST); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); @@ -523,7 +523,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList if (requestCode == HEADLINES_REQUEST) { // we add back footers stripped when this was passed to DetailActivity - Application.getArticles().add(new Article(Article.TYPE_AMR_FOOTER)); + // Application.getArticles().add(new Article(Article.TYPE_AMR_FOOTER)); HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); -- cgit v1.2.3-54-g00ecf From f29c0e06b0c77102898a64ce191ea9f184d8fd3a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:06:03 +0300 Subject: better deal with headlines not refreshing in masteractivity when going back from detail activity --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 38 ++++++++++---------- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 40 +++++++++++++++++----- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 6 ---- 3 files changed, 49 insertions(+), 35 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 b3ff916d..33f50a8e 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 @@ -39,20 +39,12 @@ public class ArticlePager extends androidx.fragment.app.Fragment { @Override @NonNull public Fragment createFragment(int position) { - try { - Article article = Application.getArticles().get(position); + Article article = Application.getArticles().get(position); - if (article != null) { - ArticleFragment af = new ArticleFragment(); - af.initialize(article); + ArticleFragment af = new ArticleFragment(); + af.initialize(article); - return af; - } - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - - return null; + return af; } @Override @@ -95,25 +87,30 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_pager = view.findViewById(R.id.article_pager); - int position = Application.getArticles().getPositionById(m_articleId); - m_listener.onArticleSelected(Application.getArticles().getById(m_articleId), false); m_pager.setAdapter(m_adapter); m_pager.setOffscreenPageLimit(3); - m_pager.setCurrentItem(position, false); + int position = Application.getArticles().getPositionById(m_articleId); + + if (position != -1) + m_pager.setCurrentItem(position, false); + m_pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { Log.d(TAG, "onPageSelected: " + position); - final Article article = Application.getArticles().get(position); + // wtf + if (position != -1) { + Article article = Application.getArticles().get(position); - if (article != null) { - m_articleId = article.id; + if (article != null) { + m_articleId = article.id; - m_listener.onArticleSelected(article, false); + m_listener.onArticleSelected(article, false); + } } } }); @@ -140,7 +137,8 @@ public class ArticlePager extends androidx.fragment.app.Fragment { if (m_pager != null && articleId != m_articleId) { int position = Application.getArticles().getPositionById(articleId); - m_pager.setCurrentItem(position, false); + if (position != -1) + m_pager.setCurrentItem(position, false); } } 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 be72d52e..50c6eb0e 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 @@ -101,29 +101,30 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements Log.d(TAG, "onLoadFinished loader=" + loader + " size=" + (data != null ? data.size() : "N/A (null)")); HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; - //ArticleList sharedArticles = Application.getArticles(); // successful update if (data != null) { - // shared article list contains raw returned data without footers ArticleList sharedArticles = Application.getArticles(); sharedArticles.clear(); sharedArticles.addAll(data); + ArticleList tmp = new ArticleList(); + tmp.addAll(data); + // detail activity does not use footers if (!(m_activity instanceof DetailActivity)) { if (headlinesLoader.lazyLoadEnabled()) - data.add(new Article(Article.TYPE_LOADMORE)); + tmp.add(new Article(Article.TYPE_LOADMORE)); - data.add(new Article(Article.TYPE_AMR_FOOTER)); + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); } - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, data)); + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, tmp)); m_articles.clear(); - m_articles.addAll(data); + m_articles.addAll(tmp); diffResult.dispatchUpdatesTo(m_adapter); @@ -479,7 +480,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); if (lastVisibleItem >= Application.getArticles().size() - 5) { - refresh(true); + new Handler().postDelayed(() -> refresh(true), 0); } } } @@ -528,6 +529,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public void onResume() { super.onResume(); + syncToSharedArticles(); + if (Application.getArticles().getSizeWithoutFooters() == 0) { refresh(false); } else { @@ -560,7 +563,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_adapter.notifyItemRangeRemoved(0, size); m_articles.add(new Article(Article.TYPE_LOADMORE)); - m_adapter.notifyItemInserted(m_articles.size()); + m_articles.add(new Article(Article.TYPE_AMR_FOOTER)); + + m_adapter.notifyItemRangeInserted(0, m_articles.size()); } } @@ -1479,7 +1484,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements scrollToArticleId(articleId); if (newPosition >= articles.size() - 5) - refresh(true); + new Handler().postDelayed(() -> refresh(true), 0); } } @@ -1533,4 +1538,21 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements releaseSurface(); } + private void syncToSharedArticles() { + ArticleList tmp = new ArticleList(); + tmp.addAll(Application.getArticles()); + + if (m_loader != null && m_loader.lazyLoadEnabled()) + tmp.add(new Article(Article.TYPE_LOADMORE)); + + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, tmp)); + + diffResult.dispatchUpdatesTo(m_adapter); + + m_articles.clear(); + m_articles.addAll(tmp); + } + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index 63a1bae4..6ff06975 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -67,15 +67,11 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api } else if (m_lazyLoadEnabled && !m_loadingInProgress) { m_append = true; forceLoad(); - /* } else { - deliverResult(m_articles); */ } } @Override public void deliverResult(ArticleList data) { - Log.d(TAG, "deliverResult=" + data); - super.deliverResult(data); } @@ -182,8 +178,6 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api if (skip == 0) m_articles.clear(); - else - m_articles.stripFooters(); m_amountLoaded = articlesJson.size(); -- cgit v1.2.3-54-g00ecf From 9a3fb1fa03a578389638e9044b0d722dc4497864 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:23:01 +0300 Subject: disable scroll to id when going back from detail activity for now --- org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 5c148a35..76fd0ad6 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -532,13 +532,12 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList hf.notifyUpdated(); // data might be null if detailactivity crashed - if (data != null) { + /* if (data != null) { int activeArticleId = data.getIntExtra("activeArticleId", 0); Log.d(TAG, "got back from detail activity, scrolling to id=" + activeArticleId); - hf.setActiveArticleId(activeArticleId); - //hf.scrollToArticleId(activeArticleId); - } + hf.scrollToArticleId(activeArticleId); + } */ } } } -- cgit v1.2.3-54-g00ecf From 3d2602ac1d1601a23b7c165d8d09955186430523 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:26:08 +0300 Subject: disable force notify on onActivityResult because of headlines fragment onResume resync --- org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 76fd0ad6..33d949f7 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -521,23 +521,22 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList super.onActivityResult(requestCode, resultCode, data); if (requestCode == HEADLINES_REQUEST) { - // we add back footers stripped when this was passed to DetailActivity // Application.getArticles().add(new Article(Article.TYPE_AMR_FOOTER)); HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) { - // articles might've been changed while in detail activity - hf.notifyUpdated(); + // articles might've been changed while in detail activity (resync with shared articles is handled in fragment onResume) + //hf.notifyUpdated(); // data might be null if detailactivity crashed - /* if (data != null) { + if (data != null) { int activeArticleId = data.getIntExtra("activeArticleId", 0); Log.d(TAG, "got back from detail activity, scrolling to id=" + activeArticleId); hf.scrollToArticleId(activeArticleId); - } */ + } } } } -- cgit v1.2.3-54-g00ecf From afab27f905476d7f1a97206a3973486858d1cfb3 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:45:25 +0300 Subject: remove some usages of notifyUpdated() when not needed --- .../main/java/org/fox/ttrss/OnlineActivity.java | 56 ++++++++++++++-------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 a5be1d5b..5d04208a 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 @@ -562,28 +562,37 @@ public class OnlineActivity extends CommonActivity { if (ap != null) { Article selectedArticle = Application.getArticles().getById(ap.getSelectedArticleId()); - if (selectedArticle != null) - setArticleScore(selectedArticle); + if (selectedArticle != null) { + setArticleScore(selectedArticle); + + hf.notifyItemChanged(Application.getArticles().indexOf(selectedArticle)); + } } return true; } else if (itemId == R.id.toggle_marked) { if (ap != null) { Article selectedArticle = Application.getArticles().getById(ap.getSelectedArticleId()); - selectedArticle.marked = !selectedArticle.marked; - saveArticleMarked(selectedArticle); + if (selectedArticle != null) { + selectedArticle.marked = !selectedArticle.marked; - if (hf != null) hf.notifyUpdated(); + saveArticleMarked(selectedArticle); + + hf.notifyItemChanged(Application.getArticles().indexOf(selectedArticle)); + } } return true; } else if (itemId == R.id.toggle_unread) { if (ap != null) { Article selectedArticle = Application.getArticles().getById(ap.getSelectedArticleId()); - selectedArticle.unread = !selectedArticle.unread; - saveArticleUnread(selectedArticle); + if (selectedArticle != null) { + selectedArticle.unread = !selectedArticle.unread; + + saveArticleUnread(selectedArticle); - if (hf != null) hf.notifyUpdated(); + hf.notifyItemChanged(Application.getArticles().indexOf(selectedArticle)); + } } return true; } else if (itemId == R.id.selection_toggle_unread) { @@ -591,11 +600,13 @@ public class OnlineActivity extends CommonActivity { ArticleList selected = hf.getSelectedArticles(); if (!selected.isEmpty()) { - for (Article a : selected) + for (Article a : selected) { a.unread = !a.unread; + hf.notifyItemChanged(Application.getArticles().indexOf(a)); + } + toggleArticlesUnread(selected); - hf.notifyUpdated(); invalidateOptionsMenu(); } } @@ -605,11 +616,13 @@ public class OnlineActivity extends CommonActivity { ArticleList selected = hf.getSelectedArticles(); if (!selected.isEmpty()) { - for (Article a : selected) + for (Article a : selected) { a.marked = !a.marked; + hf.notifyItemChanged(Application.getArticles().indexOf(a)); + } + toggleArticlesMarked(selected); - hf.notifyUpdated(); invalidateOptionsMenu(); } } @@ -619,24 +632,26 @@ public class OnlineActivity extends CommonActivity { ArticleList selected = hf.getSelectedArticles(); if (!selected.isEmpty()) { - for (Article a : selected) + for (Article a : selected) { a.published = !a.published; + hf.notifyItemChanged(Application.getArticles().indexOf(a)); + } + toggleArticlesPublished(selected); - hf.notifyUpdated(); invalidateOptionsMenu(); } } return true; } else if (itemId == R.id.toggle_published) { - if (ap != null) { + if (ap != null && hf != null) { Article selectedArticle = Application.getArticles().getById(ap.getSelectedArticleId()); if (selectedArticle != null) { selectedArticle.published = !selectedArticle.published; saveArticlePublished(selectedArticle); - if (hf != null) hf.notifyUpdated(); + hf.notifyItemChanged(Application.getArticles().indexOf(selectedArticle)); } } return true; @@ -689,12 +704,17 @@ public class OnlineActivity extends CommonActivity { if (a.unread) { a.unread = false; tmp.add(a); + + if (hf != null) { + int position = Application.getArticles().indexOf(a); + + hf.notifyItemChanged(position); + } } } if (!tmp.isEmpty()) { setArticlesUnread(tmp, Article.UPDATE_SET_FALSE); - hf.notifyUpdated(); invalidateOptionsMenu(); } } @@ -1086,8 +1106,6 @@ article.score = Integer.parseInt(edit.getText().toString()); } public void setArticlesUnread(final ArticleList articles, int mode) { - ApiRequest req = new ApiRequest(getApplicationContext()); - setArticleField(articles, Article.UPDATE_FIELD_UNREAD, mode); } -- cgit v1.2.3-54-g00ecf From cbd3f4d4be12c3f9f7c7959b513d8d74af7d13b8 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:53:01 +0300 Subject: remove some more usages of notifyUpdated --- .../src/main/java/org/fox/ttrss/DetailActivity.java | 2 +- .../src/main/java/org/fox/ttrss/OnlineActivity.java | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 780fd723..2e61e7f9 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -94,7 +94,7 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList saveArticleUnread(article); if (hf != null) { - hf.notifyUpdated(); + hf.notifyItemChanged(Application.getArticles().indexOf(article)); } } } 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 5d04208a..db06415f 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 @@ -1030,7 +1030,8 @@ article.score = Integer.parseInt(edit.getText().toString()); if (selectedArticle != null) { selectedArticle.unread = !selectedArticle.unread; saveArticleUnread(selectedArticle); - if (hf != null) hf.notifyUpdated(); + + hf.notifyItemChanged(Application.getArticles().indexOf(selectedArticle)); } } return true; @@ -1122,11 +1123,19 @@ article.score = Integer.parseInt(edit.getText().toString()); protected void onPostExecute(JsonElement result) { Log.d(TAG, "setArticleField operation complete"); - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - if (hf != null) hf.notifyUpdated(); + // currently this is generally handled before operation completes (but after POJO is modified) + /* HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - if (ap != null) ap.notifyUpdated(); + + for (Article a : articles) { + int position = Application.getArticles().getPositionById(a.id); + + if (position != -1) { + if (hf != null) hf.notifyItemChanged(position); + if (ap != null) ap.notifyItemChanged(position); + } + } */ } }; -- cgit v1.2.3-54-g00ecf From 9234e88fa6ced7918cffa0f58b0670afab617154 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 13:57:20 +0300 Subject: only show flavor image holder if we're about to show a picture to reduce convertview layout jumping issues --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'org.fox.ttrss/src/main/java') 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 50c6eb0e..986bc6d7 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 @@ -1004,6 +1004,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements holder.flavorVideoKindView.setVisibility(View.GONE); holder.flavorImageOverflow.setVisibility(View.GONE); holder.flavorVideoView.setVisibility(View.GONE); + holder.flavorImageHolder.setVisibility(View.GONE); Glide.clear(holder.flavorImageView); @@ -1067,6 +1068,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements holder.flavorImageView.setVisibility(View.VISIBLE); holder.flavorImageView.setMaxHeight((int)(m_screenHeight * 0.6f)); + // only show holder if we're about to display a picture + holder.flavorImageHolder.setVisibility(View.VISIBLE); + // prevent lower listiew entries from jumping around if this row is modified if (article.flavorViewHeight > 0) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); -- cgit v1.2.3-54-g00ecf From d395af72ea34e9e793c7ad2f822f76a11b5aac22 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 15:33:37 +0300 Subject: optimize imports --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java | 4 ---- org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java | 2 -- org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java | 1 - 3 files changed, 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 33f50a8e..6069591f 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 @@ -2,9 +2,7 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.SharedPreferences; import android.os.Bundle; -import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -12,8 +10,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.preference.PreferenceManager; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; 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 5fac1f5b..0c77cbce 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,8 +15,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 2e61e7f9..e97d6cc6 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -6,7 +6,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; import android.util.Log; import android.view.Menu; import android.view.MenuItem; -- cgit v1.2.3-54-g00ecf From 36335a93484ac3d5fb2dd128fbea51dc387a3e23 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 15:34:32 +0300 Subject: fix case for diffutil callback --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 6 ++-- .../fox/ttrss/util/HeadlinesDiffUtilCallback.java | 35 ++++++++++++++++++++++ .../fox/ttrss/util/HeadlinesDiffutilCallback.java | 35 ---------------------- 3 files changed, 38 insertions(+), 38 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java (limited to 'org.fox.ttrss/src/main/java') 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 986bc6d7..6e6cb472 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 @@ -75,7 +75,7 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Attachment; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.util.HeadlinesDiffutilCallback; +import org.fox.ttrss.util.HeadlinesDiffUtilCallback; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -121,7 +121,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements tmp.add(new Article(Article.TYPE_AMR_FOOTER)); } - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, tmp)); + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); m_articles.clear(); m_articles.addAll(tmp); @@ -1551,7 +1551,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffutilCallback(m_articles, tmp)); + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); diffResult.dispatchUpdatesTo(m_adapter); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java new file mode 100644 index 00000000..80265758 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java @@ -0,0 +1,35 @@ +package org.fox.ttrss.util; + +import androidx.recyclerview.widget.DiffUtil; + +import org.fox.ttrss.types.ArticleList; + +public class HeadlinesDiffUtilCallback extends DiffUtil.Callback { + private ArticleList m_oldList; + private ArticleList m_newList; + + public HeadlinesDiffUtilCallback(ArticleList oldList, ArticleList newList) { + m_oldList = oldList; + m_newList = newList; + } + + @Override + public int getOldListSize() { + return m_oldList != null ? m_oldList.size() : 0; + } + + @Override + public int getNewListSize() { + return m_newList != null ? m_newList.size() : 0; + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return false; + } + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java deleted file mode 100644 index b968e285..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffutilCallback.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.fox.ttrss.util; - -import androidx.recyclerview.widget.DiffUtil; - -import org.fox.ttrss.types.ArticleList; - -public class HeadlinesDiffutilCallback extends DiffUtil.Callback { - private ArticleList m_oldList; - private ArticleList m_newList; - - public HeadlinesDiffutilCallback(ArticleList oldList, ArticleList newList) { - m_oldList = oldList; - m_newList = newList; - } - - @Override - public int getOldListSize() { - return m_oldList != null ? m_oldList.size() : 0; - } - - @Override - public int getNewListSize() { - return m_newList != null ? m_newList.size() : 0; - } - - @Override - public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; - } - - @Override - public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - return false; - } - } -- cgit v1.2.3-54-g00ecf From 53031bc50daeef1eef30a13ca580bb133738afef Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 13 May 2025 15:50:50 +0300 Subject: use diffutil to sync articlepager to shared article list on headlines load --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 32 +++++++++++++++++++--- .../main/java/org/fox/ttrss/DetailActivity.java | 2 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 10 +++---- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 6069591f..016bc370 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 @@ -10,11 +10,14 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DiffUtil; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; import org.fox.ttrss.types.Article; +import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; +import org.fox.ttrss.util.HeadlinesDiffUtilCallback; public class ArticlePager extends androidx.fragment.app.Fragment { @@ -27,15 +30,31 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private ViewPager2 m_pager; private static class PagerAdapter extends FragmentStateAdapter { + private ArticleList m_articles = new ArticleList(); - public PagerAdapter(@NonNull Fragment fragment) { + public PagerAdapter(@NonNull Fragment fragment, ArticleList initialArticles) { super(fragment); + + m_articles.clear(); + m_articles.addAll(initialArticles); + } + + private void syncToSharedArticles() { + ArticleList tmp = new ArticleList(); + tmp.addAll(Application.getArticles()); + + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); + + diffResult.dispatchUpdatesTo(this); + + m_articles.clear(); + m_articles.addAll(tmp); } @Override @NonNull public Fragment createFragment(int position) { - Article article = Application.getArticles().get(position); + Article article = m_articles.get(position); ArticleFragment af = new ArticleFragment(); af.initialize(article); @@ -45,7 +64,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { @Override public int getItemCount() { - return Application.getArticles().size(); + return m_articles.size(); } } @@ -79,7 +98,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - m_adapter = new PagerAdapter(this); + m_adapter = new PagerAdapter(this, Application.getArticles()); m_pager = view.findViewById(R.id.article_pager); @@ -170,4 +189,9 @@ public class ArticlePager extends androidx.fragment.app.Fragment { public void notifyUpdated() { m_adapter.notifyDataSetChanged(); } + + public void syncToSharedArticles() { + if (m_adapter != null) + m_adapter.syncToSharedArticles(); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index e97d6cc6..b0f5796d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -300,7 +300,7 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); if (ap != null) { - ap.notifyUpdated(); + ap.syncToSharedArticles(); } if (hf != null) { 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 6e6cb472..d233e87f 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 @@ -793,13 +793,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements holder.view.setOnClickListener(v -> { m_listener.onArticleSelected(article); - setActiveArticleId(article.id); // only set active article when it makes sense (in DetailActivity) - //if (getActivity() instanceof DetailActivity) { - //m_activeArticleId = article.id; - //m_adapter.notifyDataSetChanged(); - //} + if (getActivity() instanceof DetailActivity) { + m_activeArticleId = article.id; + + m_adapter.notifyItemChanged(position); + } }); // block footer clicks to make button/selection clicking easier -- cgit v1.2.3-54-g00ecf From 8e278d9225bcaa10614f689b61e6bca935e2a043 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 07:42:18 +0300 Subject: stop refreshing headlines on resume, fix order (diff, update, dispatch) when syncing to shared article list --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 26 ++++++++-------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 d233e87f..e1d24a7e 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 @@ -350,6 +350,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Log.d(TAG, "onCreateView"); String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); @@ -372,13 +373,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_list.setLayoutManager(m_layoutManager); m_list.setItemAnimator(new DefaultItemAnimator()); - m_articles.clear(); - m_articles.addAll(Application.getArticles()); - m_adapter = new ArticleListAdapter(m_articles); - m_list.setAdapter(m_adapter); + if (savedInstanceState == null && Application.getArticles().isEmpty()) { + refresh(false); + } + if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) && !m_prefs.getBoolean("headlines_mark_read_scroll", false) ) { ItemTouchHelper swipeHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { @@ -520,8 +521,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_activity.setTitle(m_feed.title); } - Log.d(TAG, "onCreateView, feed=" + m_feed); - return view; } @@ -529,16 +528,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public void onResume() { super.onResume(); - syncToSharedArticles(); + Log.d(TAG, "onResume"); - if (Application.getArticles().getSizeWithoutFooters() == 0) { - refresh(false); - } else { - Article activeArticle = Application.getArticles().getById(m_activeArticleId); - - if (activeArticle != null) - scrollToArticle(activeArticle); - } + syncToSharedArticles(); m_activity.invalidateOptionsMenu(); } @@ -1553,10 +1545,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); - diffResult.dispatchUpdatesTo(m_adapter); - m_articles.clear(); m_articles.addAll(tmp); + + diffResult.dispatchUpdatesTo(m_adapter); } } -- cgit v1.2.3-54-g00ecf From c60baf17c56607917a35aea4d012a84e93b6cfbd Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 07:46:49 +0300 Subject: remove inconsistent footer/detailactivity check in onloadfinished --- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 e1d24a7e..c18fd9d5 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 @@ -112,14 +112,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements ArticleList tmp = new ArticleList(); tmp.addAll(data); - // detail activity does not use footers - if (!(m_activity instanceof DetailActivity)) { + if (headlinesLoader.lazyLoadEnabled()) + tmp.add(new Article(Article.TYPE_LOADMORE)); - if (headlinesLoader.lazyLoadEnabled()) - tmp.add(new Article(Article.TYPE_LOADMORE)); - - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - } + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); -- cgit v1.2.3-54-g00ecf From 6f42b0904207789ba3e7cc9b51e82bc8b1d7f1d7 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 07:54:48 +0300 Subject: move lazyload to onScrolled() but lock it on fragment side --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 25 +++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 c18fd9d5..bd80b112 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 @@ -89,6 +89,7 @@ import jp.wasabeef.glide.transformations.CropCircleTransformation; public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { private ArticleList m_articles = new ArticleList(); + private boolean m_isLazyLoading; @NonNull @Override @@ -147,6 +148,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); + + m_isLazyLoading = false; } @Override @@ -473,12 +476,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements new Handler().postDelayed(() -> m_activity.refresh(false), 100); } } - - int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); - - if (lastVisibleItem >= Application.getArticles().size() - 5) { - new Handler().postDelayed(() -> refresh(true), 0); - } } } @@ -508,8 +505,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - /* if (lastVisibleItem >= Application.getArticles().size() - 5) - new Handler().postDelayed(() -> refresh(true), 1000); */ + if (!m_isLazyLoading && lastVisibleItem >= Application.getArticles().size() - 5) { + m_isLazyLoading = true; + + // this has to be dispatched delayed, consequent adapter updates are forbidden in scroll handler + new Handler().postDelayed(() -> refresh(true), 0); + } } }); @@ -528,6 +529,14 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements syncToSharedArticles(); + // we only set this in detail activity + if (m_activeArticleId > 0) { + Article activeArticle = Application.getArticles().getById(m_activeArticleId); + + if (activeArticle != null) + scrollToArticle(activeArticle); + } + m_activity.invalidateOptionsMenu(); } -- cgit v1.2.3-54-g00ecf From 964a5b76b78e2d573924b7b15b6f404dcdabad1e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 08:36:38 +0300 Subject: lazy loading and item diff related tweaks --- .../main/java/org/fox/ttrss/ArticleFragment.java | 4 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 48 +++++++++++++++------- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 13 +++--- .../src/main/java/org/fox/ttrss/types/Article.java | 14 +++++-- .../fox/ttrss/util/HeadlinesDiffUtilCallback.java | 19 ++++++++- 5 files changed, 71 insertions(+), 27 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 3ee42976..8794e653 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 @@ -157,7 +157,7 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { View noteContainer = view.findViewById(R.id.note_container); if (note != null && noteContainer != null) { - if (m_article.note != null && !m_article.note.isEmpty()) { + if (!m_article.note.isEmpty()) { note.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_articleSmallFontSize); note.setText(m_article.note); noteContainer.setVisibility(View.VISIBLE); @@ -259,7 +259,7 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { String linkHexColor = String.format("#%06X", (0xFFFFFF & tvColorPrimary.data)); cssOverride += " a:link {color: "+linkHexColor+";} a:visited { color: "+linkHexColor+";}"; - String articleContent = m_article.content != null ? m_article.content : ""; + String articleContent = m_article.content; ws.setJavaScriptEnabled(false); 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 bd80b112..9674cf2a 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 @@ -55,6 +55,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -113,13 +114,32 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements ArticleList tmp = new ArticleList(); tmp.addAll(data); - if (headlinesLoader.lazyLoadEnabled()) - tmp.add(new Article(Article.TYPE_LOADMORE)); - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); + /* diffResult.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.d(TAG, "[DIFF] onInserted! pos=" + position + " count=" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.d(TAG, "[DIFF] onRemoved! pos=" + position + " count=" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.d(TAG, "[DIFF] onMoved! from=" + fromPosition + " to=" + toPosition); + } + + @Override + public void onChanged(int position, int count, @Nullable Object payload) { + Log.d(TAG, "[DIFF] onChanged! pos=" + position + " count=" + count + " payload=" + payload); + } + }); */ + m_articles.clear(); m_articles.addAll(tmp); @@ -154,7 +174,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public void onLoaderReset(@NonNull Loader loader) { - + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(false); } public void notifyItemChanged(int position) { @@ -505,9 +526,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - if (!m_isLazyLoading && lastVisibleItem >= Application.getArticles().size() - 5) { + if (dy > 0 && !m_isLazyLoading && (m_loader == null || m_loader.lazyLoadEnabled()) && + lastVisibleItem >= Application.getArticles().size() - 5) { + m_isLazyLoading = true; + Log.d(TAG, "attempting to lazy load more articles..."); + // this has to be dispatched delayed, consequent adapter updates are forbidden in scroll handler new Handler().postDelayed(() -> refresh(true), 0); } @@ -559,7 +584,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (m_adapter != null) { m_adapter.notifyItemRangeRemoved(0, size); - m_articles.add(new Article(Article.TYPE_LOADMORE)); m_articles.add(new Article(Article.TYPE_AMR_FOOTER)); m_adapter.notifyItemRangeInserted(0, m_articles.size()); @@ -573,6 +597,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements initLoader(Application.LOADER_HEADLINES, null, this); } + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(true); + m_loader.setSearchQuery(getSearchQuery()); m_loader.startLoading(append); } @@ -685,7 +712,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements public static final int VIEW_UNREAD = 1; public static final int VIEW_ACTIVE = 2; public static final int VIEW_ACTIVE_UNREAD = 3; - public static final int VIEW_LOADMORE = 4; public static final int VIEW_AMR_FOOTER = 5; public static final int VIEW_COUNT = VIEW_AMR_FOOTER + 1; @@ -742,9 +768,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements case VIEW_AMR_FOOTER: layoutId = R.layout.headlines_footer; break; - case VIEW_LOADMORE: - layoutId = R.layout.headlines_row_loadmore; - break; case VIEW_UNREAD: layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact_unread : R.layout.headlines_row_unread; break; @@ -1299,8 +1322,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (a.id == Article.TYPE_AMR_FOOTER) { return VIEW_AMR_FOOTER; - } else if (a.id == Article.TYPE_LOADMORE) { - return VIEW_LOADMORE; } else if (a.id == m_activeArticleId && a.unread) { return VIEW_ACTIVE_UNREAD; } else if (a.id == m_activeArticleId) { @@ -1543,9 +1564,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements ArticleList tmp = new ArticleList(); tmp.addAll(Application.getArticles()); - if (m_loader != null && m_loader.lazyLoadEnabled()) - tmp.add(new Article(Article.TYPE_LOADMORE)); - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index 6ff06975..a7755f5b 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -67,6 +67,8 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api } else if (m_lazyLoadEnabled && !m_loadingInProgress) { m_append = true; forceLoad(); + } else { + deliverResult(m_articles); } } @@ -181,11 +183,12 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_amountLoaded = articlesJson.size(); - for (Article f : articlesJson) - if (!m_articles.containsId(f.id)) { - f.collectMediaInfo(); - f.cleanupExcerpt(); - m_articles.add(f); + for (Article article : articlesJson) + if (!m_articles.containsId(article.id)) { + article.collectMediaInfo(); + article.cleanupExcerpt(); + article.fixNullFields(); + m_articles.add(article); } if (m_firstIdChanged) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index 800ca55f..76a62c58 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -14,7 +14,6 @@ import java.util.regex.Pattern; // TODO: serialize Labels public class Article implements Parcelable { - public static final int TYPE_LOADMORE = -1; public static final int TYPE_AMR_FOOTER = -2; public static final int FLAVOR_KIND_ALBUM = 1; @@ -194,8 +193,7 @@ public class Article implements Parcelable { public Article(int id) { this.id = id; this.title = "ID:" + id; - this.link = ""; - this.tags = new ArrayList<>(); + fixNullFields(); } @Override @@ -280,4 +278,14 @@ public class Article implements Parcelable { return new Article[size]; } }; + + /** set fields which might be missing during JSON deserialization to sane values */ + public void fixNullFields() { + if (note == null) note = ""; + if (link == null) link = ""; + if (tags == null) tags = new ArrayList<>(); + if (note == null) note = ""; + if (excerpt == null) excerpt = ""; + if (content == null) content = ""; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java index 80265758..e1292f99 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java @@ -1,10 +1,14 @@ package org.fox.ttrss.util; +import android.util.Log; + import androidx.recyclerview.widget.DiffUtil; +import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; public class HeadlinesDiffUtilCallback extends DiffUtil.Callback { + private final String TAG = this.getClass().getSimpleName(); private ArticleList m_oldList; private ArticleList m_newList; @@ -25,11 +29,22 @@ public class HeadlinesDiffUtilCallback extends DiffUtil.Callback { @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return m_newList.get(newItemPosition).id == m_oldList.get(oldItemPosition).id; + Article a1 = m_oldList.get(oldItemPosition); + Article a2 = m_newList.get(newItemPosition); + + // Log.d(TAG, "[DIFF] areItemsTheSame a1=" + a1.title + " a2=" + a2.title); + + return a1.id == a2.id; } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - return false; + Article a1 = m_oldList.get(oldItemPosition); + Article a2 = m_newList.get(newItemPosition); + + // Log.d(TAG, "[DIFF] areContentsTheSame a1=" + a1.title + " a2=" + a2.title); + + return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked + && a1.published == a2.published && a1.note.equals(a2.note); } } -- cgit v1.2.3-54-g00ecf From d260fc8072a8f89e6b31262483a20638fa7936c1 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 09:14:30 +0300 Subject: switch to recycler listview/submitlist --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 94 ++++++---------------- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 2 +- .../main/java/org/fox/ttrss/MasterActivity.java | 6 -- .../fox/ttrss/util/HeadlinesDiffItemCallback.java | 23 ++++++ .../fox/ttrss/util/HeadlinesDiffUtilCallback.java | 2 - .../src/main/res/layout/fragment_headlines.xml | 1 - 6 files changed, 48 insertions(+), 80 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java (limited to 'org.fox.ttrss/src/main/java') 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 9674cf2a..d0f4fa94 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 @@ -52,10 +52,9 @@ import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -76,7 +75,7 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Attachment; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.util.HeadlinesDiffUtilCallback; +import org.fox.ttrss.util.HeadlinesDiffItemCallback; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -89,7 +88,6 @@ import jp.wasabeef.glide.transformations.CropCircleTransformation; public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { - private ArticleList m_articles = new ArticleList(); private boolean m_isLazyLoading; @NonNull @@ -106,52 +104,30 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements // successful update if (data != null) { + // shared article list contains raw returned data without footers ArticleList sharedArticles = Application.getArticles(); sharedArticles.clear(); sharedArticles.addAll(data); ArticleList tmp = new ArticleList(); - tmp.addAll(data); - - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); - - /* diffResult.dispatchUpdatesTo(new ListUpdateCallback() { - @Override - public void onInserted(int position, int count) { - Log.d(TAG, "[DIFF] onInserted! pos=" + position + " count=" + count); - } - @Override - public void onRemoved(int position, int count) { - Log.d(TAG, "[DIFF] onRemoved! pos=" + position + " count=" + count); - } - - @Override - public void onMoved(int fromPosition, int toPosition) { - Log.d(TAG, "[DIFF] onMoved! from=" + fromPosition + " to=" + toPosition); - } + tmp.addAll(data); - @Override - public void onChanged(int position, int count, @Nullable Object payload) { - Log.d(TAG, "[DIFF] onChanged! pos=" + position + " count=" + count + " payload=" + payload); - } - }); */ + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - m_articles.clear(); - m_articles.addAll(tmp); + final boolean appended = headlinesLoader.getAppend(); - diffResult.dispatchUpdatesTo(m_adapter); + m_adapter.submitList(tmp, () -> { + if (!appended) + m_list.scrollToPosition(0); + }); if (headlinesLoader.getFirstIdChanged()) Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) .setAction(R.string.reload, v -> refresh(false)).show(); - if (!headlinesLoader.getAppend()) - m_list.scrollToPosition(0); - m_listener.onHeadlinesLoaded(headlinesLoader.getAppend()); } else { @@ -393,7 +369,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_list.setLayoutManager(m_layoutManager); m_list.setItemAnimator(new DefaultItemAnimator()); - m_adapter = new ArticleListAdapter(m_articles); + m_adapter = new ArticleListAdapter(); m_list.setAdapter(m_adapter); if (savedInstanceState == null && Application.getArticles().isEmpty()) { @@ -577,16 +553,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (!append) { m_activeArticleId = -1; - int size = m_articles.size(); - - m_articles.clear(); - if (m_adapter != null) { - m_adapter.notifyItemRangeRemoved(0, size); - - m_articles.add(new Article(Article.TYPE_AMR_FOOTER)); - - m_adapter.notifyItemRangeInserted(0, m_articles.size()); + ArticleList tmp = new ArticleList(); + m_adapter.submitList(tmp); } } @@ -705,14 +674,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - private class ArticleListAdapter extends RecyclerView.Adapter { - private final ArticleList items; - + private class ArticleListAdapter extends ListAdapter { public static final int VIEW_NORMAL = 0; public static final int VIEW_UNREAD = 1; public static final int VIEW_ACTIVE = 2; public static final int VIEW_ACTIVE_UNREAD = 3; - public static final int VIEW_AMR_FOOTER = 5; + public static final int VIEW_AMR_FOOTER = 4; public static final int VIEW_COUNT = VIEW_AMR_FOOTER + 1; @@ -744,9 +711,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements return false; } - public ArticleListAdapter(ArticleList items) { - super(); - this.items = items; + public ArticleListAdapter() { + super(new HeadlinesDiffItemCallback()); Display display = m_activity.getWindowManager().getDefaultDisplay(); Point size = new Point(); @@ -788,7 +754,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public void onBindViewHolder(final ArticleViewHolder holder, int position) { - holder.article = items.get(position); + holder.article = getItem(position); int headlineFontSize = m_prefs.getInt("headlines_font_size_sp_int", 13); int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2)); @@ -1318,7 +1284,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @Override public int getItemViewType(int position) { - Article a = items.get(position); + Article a = getItem(position); if (a.id == Article.TYPE_AMR_FOOTER) { return VIEW_AMR_FOOTER; @@ -1333,11 +1299,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - @Override - public int getItemCount() { - return items.size(); - } - private void updateTextCheckedState(final ArticleViewHolder holder, final Article article) { String tmp = !article.title.isEmpty() ? article.title.substring(0, 1).toUpperCase() : "?"; @@ -1473,10 +1434,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - public void notifyUpdated() { - m_adapter.notifyDataSetChanged(); - } - public void scrollToArticle(Article article) { scrollToArticleId(article.id); } @@ -1562,16 +1519,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements private void syncToSharedArticles() { ArticleList tmp = new ArticleList(); - tmp.addAll(Application.getArticles()); - - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); + tmp.addAll(Application.getArticles()); - m_articles.clear(); - m_articles.addAll(tmp); + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - diffResult.dispatchUpdatesTo(m_adapter); + m_adapter.submitList(tmp); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index a7755f5b..38ae19f4 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -68,7 +68,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_append = true; forceLoad(); } else { - deliverResult(m_articles); + reset(); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 33d949f7..fcca41bb 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -521,15 +521,9 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList super.onActivityResult(requestCode, resultCode, data); if (requestCode == HEADLINES_REQUEST) { - // we add back footers stripped when this was passed to DetailActivity - // Application.getArticles().add(new Article(Article.TYPE_AMR_FOOTER)); - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) { - // articles might've been changed while in detail activity (resync with shared articles is handled in fragment onResume) - //hf.notifyUpdated(); - // data might be null if detailactivity crashed if (data != null) { int activeArticleId = data.getIntExtra("activeArticleId", 0); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java new file mode 100644 index 00000000..b652946a --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java @@ -0,0 +1,23 @@ +package org.fox.ttrss.util; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; + +import org.fox.ttrss.types.Article; + +public class HeadlinesDiffItemCallback extends DiffUtil.ItemCallback
{ + @Override + public boolean areItemsTheSame(@NonNull Article a1, @NonNull Article a2) { + // Log.d(TAG, "[DIFF] areItemsTheSame a1=" + a1.title + " a2=" + a2.title); + + return a1.id == a2.id; + } + + @Override + public boolean areContentsTheSame(@NonNull Article a1, @NonNull Article a2) { + // Log.d(TAG, "[DIFF] areContentsTheSame a1=" + a1.title + " a2=" + a2.title); + + return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked + && a1.published == a2.published && a1.note.equals(a2.note); + } +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java index e1292f99..084fb9fc 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java @@ -1,7 +1,5 @@ package org.fox.ttrss.util; -import android.util.Log; - import androidx.recyclerview.widget.DiffUtil; import org.fox.ttrss.types.Article; diff --git a/org.fox.ttrss/src/main/res/layout/fragment_headlines.xml b/org.fox.ttrss/src/main/res/layout/fragment_headlines.xml index 2783a1e4..647c3ae9 100755 --- a/org.fox.ttrss/src/main/res/layout/fragment_headlines.xml +++ b/org.fox.ttrss/src/main/res/layout/fragment_headlines.xml @@ -12,7 +12,6 @@ -- cgit v1.2.3-54-g00ecf From 9da98b2abb5eddfec8c8a5ef51f5bdbbcaeecf21 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 09:56:10 +0300 Subject: switch to asyncdiff implementation of pager adapter --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 39 +++++++--------- .../main/java/org/fox/ttrss/DetailActivity.java | 6 +-- .../main/java/org/fox/ttrss/MasterActivity.java | 2 + .../fox/ttrss/util/DiffFragmentStateAdapter.java | 54 ++++++++++++++++++++++ 4 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/DiffFragmentStateAdapter.java (limited to 'org.fox.ttrss/src/main/java') 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 016bc370..46c42768 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 @@ -17,11 +17,13 @@ import androidx.viewpager2.widget.ViewPager2; import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; +import org.fox.ttrss.util.DiffFragmentStateAdapter; +import org.fox.ttrss.util.HeadlinesDiffItemCallback; import org.fox.ttrss.util.HeadlinesDiffUtilCallback; public class ArticlePager extends androidx.fragment.app.Fragment { - private final String TAG = "ArticlePager"; + private final String TAG = this.getClass().getSimpleName(); private PagerAdapter m_adapter; private HeadlinesEventListener m_listener; private int m_articleId; @@ -29,44 +31,39 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private Feed m_feed; private ViewPager2 m_pager; - private static class PagerAdapter extends FragmentStateAdapter { - private ArticleList m_articles = new ArticleList(); + private class PagerAdapter extends DiffFragmentStateAdapter
{ - public PagerAdapter(@NonNull Fragment fragment, ArticleList initialArticles) { - super(fragment); + public PagerAdapter(@NonNull Fragment fragment) { + super(fragment, new HeadlinesDiffItemCallback()); - m_articles.clear(); - m_articles.addAll(initialArticles); + syncToSharedArticles(); } private void syncToSharedArticles() { + Log.d(TAG, "syncToSharedArticles"); + ArticleList tmp = new ArticleList(); tmp.addAll(Application.getArticles()); - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new HeadlinesDiffUtilCallback(m_articles, tmp)); + Log.d(TAG, "shared size=" + Application.getArticles().size() + " local size=" + getItemCount()); - diffResult.dispatchUpdatesTo(this); + for (Article a : tmp) { + Log.d(TAG, a.title); + } - m_articles.clear(); - m_articles.addAll(tmp); + submitList(tmp); } @Override @NonNull public Fragment createFragment(int position) { - Article article = m_articles.get(position); + Article article = getItem(position); ArticleFragment af = new ArticleFragment(); af.initialize(article); return af; } - - @Override - public int getItemCount() { - return m_articles.size(); - } - } public void initialize(int articleId, Feed feed) { @@ -98,7 +95,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - m_adapter = new PagerAdapter(this, Application.getArticles()); + m_adapter = new PagerAdapter(this); m_pager = view.findViewById(R.id.article_pager); @@ -186,10 +183,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_adapter.notifyItemChanged(position); } - public void notifyUpdated() { - m_adapter.notifyDataSetChanged(); - } - public void syncToSharedArticles() { if (m_adapter != null) m_adapter.syncToSharedArticles(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index b0f5796d..ed237fed 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -299,11 +299,11 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - if (ap != null) { + if (ap != null) { ap.syncToSharedArticles(); } - if (hf != null) { + /* if (hf != null) { Article article = Application.getArticles().getById(hf.getActiveArticleId()); if (article == null && !Application.getArticles().isEmpty()) { @@ -321,7 +321,7 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList ft.replace(R.id.article_fragment, af, FRAG_ARTICLE); ft.commitAllowingStateLoss(); } - } + } */ } @Override diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index fcca41bb..452c720b 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -520,6 +520,8 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); + Log.d(TAG, "onActivityResult:" + requestCode + " "+ resultCode + " " + data); + if (requestCode == HEADLINES_REQUEST) { HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DiffFragmentStateAdapter.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DiffFragmentStateAdapter.java new file mode 100644 index 00000000..c423b02d --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DiffFragmentStateAdapter.java @@ -0,0 +1,54 @@ +package org.fox.ttrss.util; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.Lifecycle; +import androidx.recyclerview.widget.AsyncListDiffer; +import androidx.recyclerview.widget.DiffUtil; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import java.util.List; + +// https://gist.github.com/Gnzlt/7e8a23ba0c3b046ed33c824b284d7270 +public abstract class DiffFragmentStateAdapter extends FragmentStateAdapter { + + private final AsyncListDiffer differ; + + protected DiffFragmentStateAdapter(FragmentActivity fragmentActivity, DiffUtil.ItemCallback diffCallback) { + super(fragmentActivity); + differ = new AsyncListDiffer<>(this, diffCallback); + } + + protected DiffFragmentStateAdapter(Fragment fragment, DiffUtil.ItemCallback diffCallback) { + super(fragment); + differ = new AsyncListDiffer<>(this, diffCallback); + } + + protected DiffFragmentStateAdapter(FragmentManager fragmentManager, Lifecycle lifecycle, DiffUtil.ItemCallback diffCallback) { + super(fragmentManager, lifecycle); + differ = new AsyncListDiffer<>(this, diffCallback); + } + + public void submitList(List list, Runnable commitCallback) { + differ.submitList(list, commitCallback); + } + + public void submitList(List list) { + differ.submitList(list, null); + } + + public List getCurrentList() { + return differ.getCurrentList(); + } + + protected T getItem(int position) { + return differ.getCurrentList().get(position); + } + + @Override + public int getItemCount() { + return differ.getCurrentList().size(); + } +} + -- cgit v1.2.3-54-g00ecf From fc11ab32a9747467749f86076592e89c2ef5ad6b Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 10:14:14 +0300 Subject: add workaround for headlines loader being recreated for different activities --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 3 ++- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 23 ++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 d0f4fa94..a3a1bb46 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 @@ -93,7 +93,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements @NonNull @Override public Loader onCreateLoader(int id, @Nullable Bundle args) { - return new HeadlinesLoader(getContext(), m_feed, m_activity.getResizeWidth()); + return new HeadlinesLoader(getContext(), m_feed, m_activity.getResizeWidth(), Application.getArticles()); } @Override @@ -564,6 +564,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements if (m_loader == null) { m_loader = (HeadlinesLoader) LoaderManager.getInstance(this). initLoader(Application.LOADER_HEADLINES, null, this); + } if (m_swipeLayout != null) diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java index 38ae19f4..812a745e 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java @@ -45,19 +45,24 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api private boolean m_lazyLoadEnabled = true; private boolean m_loadingInProgress; - HeadlinesLoader(Context context, Feed feed, int resizeWidth) { + HeadlinesLoader(Context context, Feed feed, int resizeWidth, ArticleList articles) { super(context); + Log.d(TAG, "HeadlinesLoader created!"); + m_context = context; m_lastError = ApiError.NO_ERROR; m_feed = feed; m_articles = new ArticleList(); + + m_articles.addAll(articles); + m_resizeWidth = resizeWidth; m_prefs = PreferenceManager.getDefaultSharedPreferences(context); } protected void startLoading(boolean append) { - // Log.d(TAG, this + " refresh, append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled); + Log.d(TAG, this + " startLoading append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled + "localSize="+ m_articles.size()); if (!append) { m_append = false; @@ -68,7 +73,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_append = true; forceLoad(); } else { - reset(); + deliverResult(m_articles); } } @@ -95,7 +100,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api @Override public ArticleList loadInBackground() { - Log.d(TAG, "loadInBackground append=" + m_append + " offset=" + m_offset); + Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); m_loadingInProgress = true; @@ -145,7 +150,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api params.put("include_header", "true"); } - Log.d(TAG, "request more headlines, firstId=" + m_firstId + ", append=" + m_append + ", skip=" + skip); + Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + m_articles.size()); JsonElement result = ApiCommon.performRequest(m_context, params, this); @@ -169,7 +174,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api m_firstId = 0; } - Log.d(TAG, "firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); + Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); Type listType = new TypeToken>() {}.getType(); articlesJson = new Gson().fromJson(content.get(1), listType); @@ -178,7 +183,7 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api articlesJson = new Gson().fromJson(content, listType); } - if (skip == 0) + if (!m_append) m_articles.clear(); m_amountLoaded = articlesJson.size(); @@ -197,13 +202,15 @@ public class HeadlinesLoader extends AsyncTaskLoader implements Api } if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - Log.d(TAG, "amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); m_lazyLoadEnabled = false; } m_offset += m_amountLoaded; m_loadingInProgress = false; + Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + m_articles.size()); + return m_articles; } -- cgit v1.2.3-54-g00ecf From 3dfd1a4c9c978fd0a6f1e58a7d8fd235ed6d997a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 10:39:20 +0300 Subject: make pager adapter static --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 46c42768..f1ff8384 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 @@ -31,7 +31,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private Feed m_feed; private ViewPager2 m_pager; - private class PagerAdapter extends DiffFragmentStateAdapter
{ + private static class PagerAdapter extends DiffFragmentStateAdapter
{ public PagerAdapter(@NonNull Fragment fragment) { super(fragment, new HeadlinesDiffItemCallback()); @@ -40,17 +40,9 @@ public class ArticlePager extends androidx.fragment.app.Fragment { } private void syncToSharedArticles() { - Log.d(TAG, "syncToSharedArticles"); - ArticleList tmp = new ArticleList(); tmp.addAll(Application.getArticles()); - Log.d(TAG, "shared size=" + Application.getArticles().size() + " local size=" + getItemCount()); - - for (Article a : tmp) { - Log.d(TAG, a.title); - } - submitList(tmp); } -- cgit v1.2.3-54-g00ecf From 5dac45ba85a786b4a95038d11f5925ce09594898 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 13:10:27 +0300 Subject: wip implementation of viewmodel/executor/livedata headlines --- .../src/main/java/org/fox/ttrss/ApiCommon.java | 8 +- .../src/main/java/org/fox/ttrss/ApiLoader.java | 2 +- .../src/main/java/org/fox/ttrss/ApiRequest.java | 2 +- .../src/main/java/org/fox/ttrss/Application.java | 11 +- .../src/main/java/org/fox/ttrss/ArticlePager.java | 11 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 127 ++++----- .../main/java/org/fox/ttrss/HeadlinesLoader.java | 289 ------------------- .../main/java/org/fox/ttrss/HeadlinesModel.java | 317 +++++++++++++++++++++ .../java/org/fox/ttrss/share/ShareActivity.java | 2 +- .../org/fox/ttrss/share/SubscribeActivity.java | 4 +- org.fox.ttrss/src/main/res/values/strings.xml | 1 + 11 files changed, 401 insertions(+), 373 deletions(-) delete mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java (limited to 'org.fox.ttrss/src/main/java') 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 75963c96..adc0881d 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 @@ -41,13 +41,16 @@ public class ApiCommon { void setLastErrorMessage(String message); } - public enum ApiError { NO_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, + public enum ApiError { + SUCCESS, UNKNOWN_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, SSL_HOSTNAME_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD } public static int getErrorMessage(ApiError error) { switch (error) { - case NO_ERROR: + case SUCCESS: + return R.string.error_success; + case UNKNOWN_ERROR: return R.string.error_unknown; case HTTP_UNAUTHORIZED: return R.string.error_http_unauthorized; @@ -154,6 +157,7 @@ public class ApiCommon { switch (statusCode) { case API_STATUS_OK: + caller.setLastError(ApiError.SUCCESS); return result.getAsJsonObject().get("content"); case API_STATUS_ERR: JsonObject contentObj = resultObj.get("content").getAsJsonObject(); 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 259d7a28..8d83e7bd 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 @@ -27,7 +27,7 @@ public class ApiLoader extends AsyncTaskLoader implements ApiCommon super(context); m_context = context; - m_lastError = ApiError.NO_ERROR; + m_lastError = ApiError.UNKNOWN_ERROR; m_params = params; } 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 409047f0..ba8dad20 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 @@ -21,7 +21,7 @@ public class ApiRequest extends AsyncTask, Integer, JsonE public ApiRequest(Context context) { m_context = context; - m_lastError = ApiError.NO_ERROR; + m_lastError = ApiError.UNKNOWN_ERROR; } @SuppressLint("NewApi") diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index 81ae7bdf..08beaa99 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -19,19 +19,24 @@ public class Application extends android.app.Application { // this is the only instance of a (large) object which contains all currently loaded articles and is // used by all fragments and activities concurrently - private final ArticleList m_articles = new ArticleList(); + // private final ArticleList m_articles = new ArticleList(); private String m_sessionId; private int m_apiLevel; public LinkedHashMap m_customSortModes = new LinkedHashMap<>(); ConnectivityManager m_cmgr; + HeadlinesModel m_headlinesModel; public static Application getInstance(){ return m_singleton; } public static ArticleList getArticles() { - return getInstance().m_articles; + return getInstance().m_headlinesModel.getArticles(); + } + + public HeadlinesModel getHeadlinesModel() { + return getInstance().m_headlinesModel; } @Override @@ -40,6 +45,7 @@ public class Application extends android.app.Application { m_singleton = this; m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + m_headlinesModel = new HeadlinesModel(this); } public String getSessionId() { @@ -87,5 +93,4 @@ public class Application extends android.app.Application { return false; } - } 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 f1ff8384..72b873a2 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 @@ -35,8 +35,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { public PagerAdapter(@NonNull Fragment fragment) { super(fragment, new HeadlinesDiffItemCallback()); - - syncToSharedArticles(); } private void syncToSharedArticles() { @@ -88,6 +86,15 @@ public class ArticlePager extends androidx.fragment.app.Fragment { View view = inflater.inflate(R.layout.fragment_article_pager, container, false); m_adapter = new PagerAdapter(this); + m_adapter.submitList(Application.getArticles()); + + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + + // deal with further updates + model.getLiveData().observe(getActivity(), articles -> { + Log.d(TAG, "observed article list size=" + articles.size()); + m_adapter.submitList(articles); + }); m_pager = view.findViewById(R.id.article_pager); 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 a3a1bb46..6ca33b60 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 @@ -48,7 +48,6 @@ import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.core.view.ViewCompat; -import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DefaultItemAnimator; @@ -86,74 +85,10 @@ import java.util.TimeZone; import jp.wasabeef.glide.transformations.CropCircleTransformation; -public class HeadlinesFragment extends androidx.fragment.app.Fragment implements LoaderManager.LoaderCallbacks { +public class HeadlinesFragment extends androidx.fragment.app.Fragment { private boolean m_isLazyLoading; - @NonNull - @Override - public Loader onCreateLoader(int id, @Nullable Bundle args) { - return new HeadlinesLoader(getContext(), m_feed, m_activity.getResizeWidth(), Application.getArticles()); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, ArticleList data) { - Log.d(TAG, "onLoadFinished loader=" + loader + " size=" + (data != null ? data.size() : "N/A (null)")); - - HeadlinesLoader headlinesLoader = (HeadlinesLoader) loader; - - // successful update - if (data != null) { - - // shared article list contains raw returned data without footers - ArticleList sharedArticles = Application.getArticles(); - sharedArticles.clear(); - sharedArticles.addAll(data); - - ArticleList tmp = new ArticleList(); - - tmp.addAll(data); - - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - - final boolean appended = headlinesLoader.getAppend(); - - m_adapter.submitList(tmp, () -> { - if (!appended) - m_list.scrollToPosition(0); - }); - - if (headlinesLoader.getFirstIdChanged()) - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); - - m_listener.onHeadlinesLoaded(headlinesLoader.getAppend()); - - } else { - if (headlinesLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(); - } else { - if (headlinesLoader.getLastErrorMessage() != null) { - m_activity.toast(m_activity.getString(headlinesLoader.getErrorMessage()) + "\n" + headlinesLoader.getLastErrorMessage()); - } else { - m_activity.toast(headlinesLoader.getErrorMessage()); - } - } - } - - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(false); - - m_isLazyLoading = false; - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(false); - } - public void notifyItemChanged(int position) { if (m_adapter != null) m_adapter.notifyItemChanged(position); @@ -169,7 +104,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements private Feed m_feed; private int m_activeArticleId; private String m_searchQuery = ""; - private HeadlinesLoader m_loader; + //private HeadlinesLoader m_loader; private SharedPreferences m_prefs; @@ -502,7 +437,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements } } - if (dy > 0 && !m_isLazyLoading && (m_loader == null || m_loader.lazyLoadEnabled()) && + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + + if (dy > 0 && !m_isLazyLoading && model.lazyLoadEnabled() && lastVisibleItem >= Application.getArticles().size() - 5) { m_isLazyLoading = true; @@ -519,6 +456,47 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements m_activity.setTitle(m_feed.title); } + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + + model.getLiveData().observe(getActivity(), articles -> { + Log.d(TAG, "observed article list size=" + articles.size()); + + ArticleList tmp = new ArticleList(); + + tmp.addAll(articles); + + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + + final boolean appended = model.getAppend(); + + m_adapter.submitList(tmp, () -> { + if (!appended) + m_list.scrollToPosition(0); + }); + + if (model.getFirstIdChanged()) + Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) + .setAction(R.string.reload, v -> refresh(false)).show(); + + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(false); + + m_isLazyLoading = false; + + if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(); + } else if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (model.getLastErrorMessage() != null) { + m_activity.toast(m_activity.getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); + } else { + m_activity.toast(model.getErrorMessage()); + } + } + + m_listener.onHeadlinesLoaded(appended); + }); + return view; } @@ -561,17 +539,22 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment implements // if we try to initLoader() all the time, onLoadFinished() might be sent twice // https://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice - if (m_loader == null) { + /* if (m_loader == null) { m_loader = (HeadlinesLoader) LoaderManager.getInstance(this). initLoader(Application.LOADER_HEADLINES, null, this); - } + } */ if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - m_loader.setSearchQuery(getSearchQuery()); - m_loader.startLoading(append); + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + + model.setSearchQuery(getSearchQuery()); + model.startLoading(append, m_feed, m_activity.getResizeWidth()); + + //m_loader.setSearchQuery(getSearchQuery()); + //m_loader.startLoading(append); } static class ArticleViewHolder extends RecyclerView.ViewHolder { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java deleted file mode 100755 index 812a745e..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesLoader.java +++ /dev/null @@ -1,289 +0,0 @@ -package org.fox.ttrss; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import androidx.loader.content.AsyncTaskLoader; -import androidx.preference.PreferenceManager; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.ApiCommon.ApiError; -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; -import org.fox.ttrss.types.Feed; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; - -public class HeadlinesLoader extends AsyncTaskLoader implements ApiCommon.ApiCaller { - private final String TAG = this.getClass().getSimpleName(); - - private final int m_responseCode = 0; - protected String m_responseMessage; - private int m_apiStatusCode = 0; - - private Context m_context; - private String m_lastErrorMessage; - private ApiError m_lastError; - private ArticleList m_articles; - private Feed m_feed; - private SharedPreferences m_prefs; - private int m_firstId; - private String m_searchQuery = ""; - private boolean m_firstIdChanged; - private int m_offset; - private int m_amountLoaded; - private int m_resizeWidth; - private boolean m_append; - private boolean m_lazyLoadEnabled = true; - private boolean m_loadingInProgress; - - HeadlinesLoader(Context context, Feed feed, int resizeWidth, ArticleList articles) { - super(context); - - Log.d(TAG, "HeadlinesLoader created!"); - - m_context = context; - m_lastError = ApiError.NO_ERROR; - m_feed = feed; - m_articles = new ArticleList(); - - m_articles.addAll(articles); - - m_resizeWidth = resizeWidth; - m_prefs = PreferenceManager.getDefaultSharedPreferences(context); - } - - protected void startLoading(boolean append) { - Log.d(TAG, this + " startLoading append=" + append + " inProgress=" + m_loadingInProgress + " lazyLoadEnabled=" + m_lazyLoadEnabled + "localSize="+ m_articles.size()); - - if (!append) { - m_append = false; - m_lazyLoadEnabled = true; - - forceLoad(); - } else if (m_lazyLoadEnabled && !m_loadingInProgress) { - m_append = true; - forceLoad(); - } else { - deliverResult(m_articles); - } - } - - @Override - public void deliverResult(ArticleList data) { - super.deliverResult(data); - } - - public int getErrorMessage() { - return ApiCommon.getErrorMessage(m_lastError); - } - - ApiError getLastError() { - return m_lastError; - } - - String getLastErrorMessage() { - return m_lastErrorMessage; - } - - public boolean lazyLoadEnabled() { - return m_lazyLoadEnabled; - } - - @Override - public ArticleList loadInBackground() { - Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); - - m_loadingInProgress = true; - - final int skip = getSkip(m_append); - final boolean allowForceUpdate = Application.getInstance().getApiLevel() >= 9 && - !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; - - HashMap params = new HashMap<>(); - - params.put("op", "getHeadlines"); - params.put("sid", Application.getInstance().getSessionId()); - params.put("feed_id", String.valueOf(m_feed.id)); - params.put("show_excerpt", "true"); - params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); - params.put("show_content", "true"); - params.put("include_attachments", "true"); - params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); - params.put("limit", m_prefs.getString("headlines_request_size", "15")); - params.put("skip", String.valueOf(skip)); - params.put("include_nested", "true"); - params.put("has_sandbox", "true"); - params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); - - if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (m_prefs.getBoolean("always_downsample_images", false) || !Application.getInstance().isWifiConnected()) { - params.put("resize_width", String.valueOf(m_resizeWidth)); - } - } - - if (m_feed.is_cat) - params.put("is_cat", "true"); - - if (allowForceUpdate) { - params.put("force_update", "true"); - } - - if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - params.put("search", m_searchQuery); - params.put("search_mode", ""); - params.put("match_on", "both"); - } - - if (m_firstId > 0) - params.put("check_first_id", String.valueOf(m_firstId)); - - if (Application.getInstance().getApiLevel() >= 12) { - params.put("include_header", "true"); - } - - Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + m_articles.size()); - - JsonElement result = ApiCommon.performRequest(m_context, params, this); - - Log.d(TAG, "got result=" + result); - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - final List
articlesJson; - final JsonObject header; - - if (Application.getInstance().getApiLevel() >= 12) { - header = content.get(0).getAsJsonObject(); - - m_firstIdChanged = header.get("first_id_changed") != null; - - try { - m_firstId = header.get("first_id").getAsInt(); - } catch (NumberFormatException e) { - m_firstId = 0; - } - - Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); - - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content.get(1), listType); - } else { - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content, listType); - } - - if (!m_append) - m_articles.clear(); - - m_amountLoaded = articlesJson.size(); - - for (Article article : articlesJson) - if (!m_articles.containsId(article.id)) { - article.collectMediaInfo(); - article.cleanupExcerpt(); - article.fixNullFields(); - m_articles.add(article); - } - - if (m_firstIdChanged) { - Log.d(TAG, "first id changed, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - m_offset += m_amountLoaded; - m_loadingInProgress = false; - - Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + m_articles.size()); - - return m_articles; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - m_loadingInProgress = false; - - return null; - } - - private int getSkip(boolean append) { - int skip = 0; - - if (append) { - // adaptive, all_articles, marked, published, unread - String viewMode = m_prefs.getString("view_mode", "adaptive"); - - int numUnread = Math.toIntExact(m_articles.getUnreadCount()); - int numAll = Math.toIntExact(m_articles.getSizeWithoutFooters()); - - if ("marked".equals(viewMode)) { - skip = numAll; - } else if ("published".equals(viewMode)) { - skip = numAll; - } else if ("unread".equals(viewMode)) { - skip = numUnread; - } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - skip = numAll; - } else if ("adaptive".equals(viewMode)) { - skip = numUnread > 0 ? numUnread : numAll; - } else { - skip = numAll; - } - } - - return skip; - } - - @Override - public void setStatusCode(int statusCode) { - m_apiStatusCode = statusCode; - } - - @Override - public void setLastError(ApiError lastError) { - m_lastError = lastError; - } - - @Override - public void setLastErrorMessage(String message) { - m_lastErrorMessage = message; - } - - public boolean getFirstIdChanged() { - return m_firstIdChanged; - } - - public boolean getAppend() { - return m_append; - } - - public void setSearchQuery(String searchQuery) { - m_searchQuery = searchQuery; - } - - public String getSearchQuery() { - return m_searchQuery; - } - - public int getOffset() { - return m_offset; - } -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java new file mode 100644 index 00000000..08adb73b --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java @@ -0,0 +1,317 @@ +package org.fox.ttrss; + +import android.app.Application; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.loader.app.LoaderManager; +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +import org.fox.ttrss.types.Article; +import org.fox.ttrss.types.ArticleList; +import org.fox.ttrss.types.Feed; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class HeadlinesModel extends AndroidViewModel implements ApiCommon.ApiCaller { + private final String TAG = this.getClass().getSimpleName(); + private final MutableLiveData m_articles = new MutableLiveData<>(new ArticleList()); + private SharedPreferences m_prefs; + private final int m_responseCode = 0; + protected String m_responseMessage; + private int m_apiStatusCode = 0; + + private String m_lastErrorMessage; + private ApiCommon.ApiError m_lastError; + private Feed m_feed; + private int m_firstId; + private String m_searchQuery = ""; + private boolean m_firstIdChanged; + private int m_offset; + private int m_amountLoaded; + private int m_resizeWidth; + private boolean m_append; + private boolean m_lazyLoadEnabled = true; + private boolean m_loadingInProgress; + private ExecutorService m_executor; + private Handler m_mainHandler = new Handler(Looper.getMainLooper()); + + public HeadlinesModel(@NonNull Application application) { + super(application); + + m_prefs = PreferenceManager.getDefaultSharedPreferences(application); + + // do we need concurrency or not? + m_executor = Executors.newSingleThreadExecutor(); + } + + public LiveData getLiveData() { + return m_articles; + + } + + public ArticleList getArticles() { + return m_articles.getValue(); + } + + public void update(ArticleList articles) { + m_articles.postValue(articles); + } + + public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { + Log.d(TAG, "startLoading append=" + append); + + m_resizeWidth = resizeWidth; + + if (!append) { + m_append = false; + m_lazyLoadEnabled = true; + m_feed = feed; + + forceLoad(); + } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { + m_append = true; + m_feed = feed; + forceLoad(); + } else { + ArticleList tmp = new ArticleList(); + tmp.addAll(m_articles.getValue()); + + m_articles.postValue(tmp); + } + } + + public void forceLoad() { + Log.d(TAG, "forceLoad"); + + m_articles.postValue(loadInBackground()); + } + + public ArticleList loadInBackground() { + Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); + + ArticleList articlesWork = new ArticleList(); + articlesWork.addAll(m_articles.getValue()); + + m_loadingInProgress = true; + + final int skip = getSkip(m_append, articlesWork); + final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && + !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; + + HashMap params = new HashMap<>(); + + params.put("op", "getHeadlines"); + params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); + params.put("feed_id", String.valueOf(m_feed.id)); + params.put("show_excerpt", "true"); + params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); + params.put("show_content", "true"); + params.put("include_attachments", "true"); + params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); + params.put("limit", m_prefs.getString("headlines_request_size", "15")); + params.put("skip", String.valueOf(skip)); + params.put("include_nested", "true"); + params.put("has_sandbox", "true"); + params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); + + if (m_prefs.getBoolean("enable_image_downsampling", false)) { + if (m_prefs.getBoolean("always_downsample_images", false) || !org.fox.ttrss.Application.getInstance().isWifiConnected()) { + params.put("resize_width", String.valueOf(m_resizeWidth)); + } + } + + if (m_feed.is_cat) + params.put("is_cat", "true"); + + if (allowForceUpdate) { + params.put("force_update", "true"); + } + + if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + params.put("search", m_searchQuery); + params.put("search_mode", ""); + params.put("match_on", "both"); + } + + if (m_firstId > 0) + params.put("check_first_id", String.valueOf(m_firstId)); + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + params.put("include_header", "true"); + } + + Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + articlesWork.size()); + + m_executor.execute(() -> { + JsonElement result = ApiCommon.performRequest(getApplication(), params, this); + + Log.d(TAG, "got result=" + result); + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + final List
articlesJson; + final JsonObject header; + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + header = content.get(0).getAsJsonObject(); + + m_firstIdChanged = header.get("first_id_changed") != null; + + try { + m_firstId = header.get("first_id").getAsInt(); + } catch (NumberFormatException e) { + m_firstId = 0; + } + + Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); + + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content.get(1), listType); + } else { + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content, listType); + } + + if (!m_append) + articlesWork.clear(); + + m_amountLoaded = articlesJson.size(); + + for (Article article : articlesJson) + if (!articlesWork.containsId(article.id)) { + article.collectMediaInfo(); + article.cleanupExcerpt(); + article.fixNullFields(); + articlesWork.add(article); + } + + if (m_firstIdChanged) { + Log.d(TAG, "first id changed, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { + Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + m_offset += m_amountLoaded; + m_loadingInProgress = false; + + Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + articlesWork.size()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + m_mainHandler.post(() -> { + m_articles.postValue(articlesWork); + }); + }); + + m_loadingInProgress = false; + + return articlesWork; + } + + private int getSkip(boolean append, ArticleList articles) { + int skip = 0; + + if (append) { + // adaptive, all_articles, marked, published, unread + String viewMode = m_prefs.getString("view_mode", "adaptive"); + + int numUnread = Math.toIntExact(articles.getUnreadCount()); + int numAll = Math.toIntExact(articles.size()); + + if ("marked".equals(viewMode)) { + skip = numAll; + } else if ("published".equals(viewMode)) { + skip = numAll; + } else if ("unread".equals(viewMode)) { + skip = numUnread; + } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + skip = numAll; + } else if ("adaptive".equals(viewMode)) { + skip = numUnread > 0 ? numUnread : numAll; + } else { + skip = numAll; + } + } + + return skip; + } + + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } + + @Override + public void setLastError(ApiCommon.ApiError lastError) { + m_lastError = lastError; + } + + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } + + public boolean getFirstIdChanged() { + return m_firstIdChanged; + } + + public boolean getAppend() { + return m_append; + } + + public void setSearchQuery(String searchQuery) { + m_searchQuery = searchQuery; + } + + public String getSearchQuery() { + return m_searchQuery; + } + + public int getOffset() { + return m_offset; + } + + public boolean lazyLoadEnabled() { + return m_lazyLoadEnabled; + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + ApiCommon.ApiError getLastError() { + return m_lastError; + } + + String getLastErrorMessage() { + return m_lastErrorMessage; + } + +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java index 3a1553b6..66a77aef 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java @@ -86,7 +86,7 @@ public class ShareActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { setProgressBarIndeterminateVisibility(false); - if (m_lastError != ApiCommon.ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) { toast(getErrorMessage()); } else { toast(R.string.share_article_posted); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java index e71583d3..82e2d52d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java @@ -157,7 +157,7 @@ public class SubscribeActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { m_progressBar.setVisibility(View.INVISIBLE); - if (m_lastError != ApiCommon.ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) { toast(getErrorMessage()); } else { try { @@ -259,7 +259,7 @@ public class SubscribeActivity extends CommonShareActivity { protected void onPostExecute(JsonElement result) { m_progressBar.setVisibility(View.INVISIBLE); - if (m_lastError != ApiCommon.ApiError.NO_ERROR) { + if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) { toast(getErrorMessage()); } else { JsonArray content = result.getAsJsonArray(); diff --git a/org.fox.ttrss/src/main/res/values/strings.xml b/org.fox.ttrss/src/main/res/values/strings.xml index 6a67013a..fb7e6309 100755 --- a/org.fox.ttrss/src/main/res/values/strings.xml +++ b/org.fox.ttrss/src/main/res/values/strings.xml @@ -303,4 +303,5 @@ Check for updates Nothing Open on startup + Operation completed successfully -- cgit v1.2.3-54-g00ecf From b37c1c4cf08ebb3f66375f136626c9a30bd54d96 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 14:30:22 +0300 Subject: return selection based on adapter item contents --- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 6ca33b60..5431eb70 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 @@ -82,6 +82,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; +import java.util.stream.Collectors; import jp.wasabeef.glide.transformations.CropCircleTransformation; @@ -120,14 +121,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { private MediaPlayer m_mediaPlayer; private TextureView m_activeTexture; - public ArticleList getSelectedArticles() { - ArticleList tmp = new ArticleList(); - - for (Article a : Application.getArticles()) { - if (a.selected) tmp.add(a); - } - - return tmp; + public @NonNull ArticleList getSelectedArticles() { + if (m_adapter != null) + return m_adapter.getCurrentList() + .stream() + .filter(a -> a.selected).collect(Collectors.toCollection(ArticleList::new)); + else + return new ArticleList(); } public void initialize(Feed feed) { -- cgit v1.2.3-54-g00ecf From f632cb913aebfcc647db0165552eab330bb75a30 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 15:07:55 +0300 Subject: add some onclick workarounds for viewholder, drop articlelist from onArticleListSelectionChange because its not used anywhere and gathering it for the callback is pointless --- .../main/java/org/fox/ttrss/DetailActivity.java | 2 +- .../java/org/fox/ttrss/HeadlinesEventListener.java | 2 +- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 56 +++++++++++----------- .../main/java/org/fox/ttrss/MasterActivity.java | 2 +- 4 files changed, 32 insertions(+), 30 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index ed237fed..3995014b 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -259,7 +259,7 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList } @Override - public void onArticleListSelectionChange(ArticleList m_selectedArticles) { + public void onArticleListSelectionChange() { invalidateOptionsMenu(); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java index 5494bb2b..27e00bbf 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java @@ -4,7 +4,7 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; public interface HeadlinesEventListener { - void onArticleListSelectionChange(ArticleList m_selectedArticles); + void onArticleListSelectionChange(); void onArticleSelected(Article article); void onArticleSelected(Article article, boolean open); void onHeadlinesLoaded(boolean appended); 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 5431eb70..8f15272c 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 @@ -121,13 +121,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { private MediaPlayer m_mediaPlayer; private TextureView m_activeTexture; - public @NonNull ArticleList getSelectedArticles() { - if (m_adapter != null) - return m_adapter.getCurrentList() - .stream() - .filter(a -> a.selected).collect(Collectors.toCollection(ArticleList::new)); - else - return new ArticleList(); + public ArticleList getSelectedArticles() { + return Application.getArticles() + .stream() + .filter(a -> a.selected).collect(Collectors.toCollection(ArticleList::new)); } public void initialize(Feed feed) { @@ -559,7 +556,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { static class ArticleViewHolder extends RecyclerView.ViewHolder { public View view; - public Article article; public TextView titleView; public TextView feedTitleView; @@ -584,6 +580,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public MaterialButton attachmentsView; public ProgressTarget flavorProgressTarget; + int flavorViewHeight; + public ArticleViewHolder(View v) { super(v); @@ -593,7 +591,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { View flavorImage = view.findViewById(R.id.flavor_image); if (flavorImage != null) { - article.flavorViewHeight = flavorImage.getMeasuredHeight(); + flavorViewHeight = flavorImage.getMeasuredHeight(); } return true; @@ -624,6 +622,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { if (flavorImageView != null && flavorImageLoadingBar != null) { flavorProgressTarget = new FlavorProgressTarget<>(new GlideDrawableImageViewTarget(flavorImageView), flavorImageLoadingBar); } + } public void clearAnimation() { @@ -738,12 +737,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { @Override public void onBindViewHolder(final ArticleViewHolder holder, int position) { - holder.article = getItem(position); - int headlineFontSize = m_prefs.getInt("headlines_font_size_sp_int", 13); int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2)); - final Article article = holder.article; + Article article = getItem(position); if (article.id == Article.TYPE_AMR_FOOTER && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { WindowManager wm = (WindowManager) m_activity.getSystemService(Context.WINDOW_SERVICE); @@ -780,18 +777,18 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } if (holder.textImage != null) { - updateTextCheckedState(holder, article); + updateTextCheckedState(holder, position); holder.textImage.setOnClickListener(view -> { - Log.d(TAG, "textImage : onclicked"); + Article selectedArticle = getItem(position); - article.selected = !article.selected; + Log.d(TAG, "textImage onClick pos=" + position + " article=" + article); - updateTextCheckedState(holder, article); + selectedArticle.selected = !selectedArticle.selected; - m_listener.onArticleListSelectionChange(getSelectedArticles()); + updateTextCheckedState(holder, position); - Log.d(TAG, "num selected: " + getSelectedArticles().size()); + m_listener.onArticleListSelectionChange(); }); ViewCompat.setTransitionName(holder.textImage, "gallery:" + article.flavorImageUri); @@ -1042,9 +1039,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.flavorImageHolder.setVisibility(View.VISIBLE); // prevent lower listiew entries from jumping around if this row is modified - if (article.flavorViewHeight > 0) { + // TODO this doesn't work right now + if (holder.flavorViewHeight > 0) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); - lp.height = article.flavorViewHeight; + lp.height = holder.flavorViewHeight; } holder.flavorProgressTarget.setModel(article.flavorImageUri); @@ -1237,18 +1235,19 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { if (holder.selectionBoxView != null) { holder.selectionBoxView.setChecked(article.selected); holder.selectionBoxView.setOnClickListener(view -> { - CheckBox cb = (CheckBox)view; + Article currentArticle = getItem(position); - article.selected = cb.isChecked(); + Log.d(TAG, "selectionCb onClick pos=" + position + " article=" + article); - m_listener.onArticleListSelectionChange(getSelectedArticles()); + CheckBox cb = (CheckBox)view; + + currentArticle.selected = cb.isChecked(); - Log.d(TAG, "num selected: " + getSelectedArticles().size()); + m_listener.onArticleListSelectionChange(); }); } if (holder.menuButtonView != null) { - holder.menuButtonView.setOnClickListener(v -> { PopupMenu popup = new PopupMenu(getActivity(), v); @@ -1258,7 +1257,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { popup.getMenu().findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); popup.getMenu().findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); - popup.setOnMenuItemClickListener(item -> onArticleMenuItemSelected(item, article, + popup.setOnMenuItemClickListener(item -> onArticleMenuItemSelected(item, + getItem(position), m_list.getChildAdapterPosition(holder.view))); popup.show(); @@ -1283,7 +1283,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } } - private void updateTextCheckedState(final ArticleViewHolder holder, final Article article) { + private void updateTextCheckedState(final ArticleViewHolder holder, int position) { + Article article = getItem(position); + String tmp = !article.title.isEmpty() ? article.title.substring(0, 1).toUpperCase() : "?"; if (article.selected) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 452c720b..e63234cd 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -445,7 +445,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList } @Override - public void onArticleListSelectionChange(ArticleList m_selectedArticles) { + public void onArticleListSelectionChange() { invalidateOptionsMenu(); } -- cgit v1.2.3-54-g00ecf From 248fd95982a2464082a770ee8c8542de7aa8cfc0 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 15:08:47 +0300 Subject: drop diffutil callback --- .../src/main/java/org/fox/ttrss/ArticlePager.java | 3 -- .../fox/ttrss/util/HeadlinesDiffUtilCallback.java | 48 ---------------------- 2 files changed, 51 deletions(-) delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java (limited to 'org.fox.ttrss/src/main/java') 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 72b873a2..89043cbc 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 @@ -10,8 +10,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.DiffUtil; -import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; import org.fox.ttrss.types.Article; @@ -19,7 +17,6 @@ import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; import org.fox.ttrss.util.DiffFragmentStateAdapter; import org.fox.ttrss.util.HeadlinesDiffItemCallback; -import org.fox.ttrss.util.HeadlinesDiffUtilCallback; public class ArticlePager extends androidx.fragment.app.Fragment { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java deleted file mode 100644 index 084fb9fc..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffUtilCallback.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.fox.ttrss.util; - -import androidx.recyclerview.widget.DiffUtil; - -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; - -public class HeadlinesDiffUtilCallback extends DiffUtil.Callback { - private final String TAG = this.getClass().getSimpleName(); - private ArticleList m_oldList; - private ArticleList m_newList; - - public HeadlinesDiffUtilCallback(ArticleList oldList, ArticleList newList) { - m_oldList = oldList; - m_newList = newList; - } - - @Override - public int getOldListSize() { - return m_oldList != null ? m_oldList.size() : 0; - } - - @Override - public int getNewListSize() { - return m_newList != null ? m_newList.size() : 0; - } - - @Override - public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - Article a1 = m_oldList.get(oldItemPosition); - Article a2 = m_newList.get(newItemPosition); - - // Log.d(TAG, "[DIFF] areItemsTheSame a1=" + a1.title + " a2=" + a2.title); - - return a1.id == a2.id; - } - - @Override - public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - Article a1 = m_oldList.get(oldItemPosition); - Article a2 = m_newList.get(newItemPosition); - - // Log.d(TAG, "[DIFF] areContentsTheSame a1=" + a1.title + " a2=" + a2.title); - - return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked - && a1.published == a2.published && a1.note.equals(a2.note); - } - } -- cgit v1.2.3-54-g00ecf From 48f48eb404fc533747835c7a0039c40a92879fa0 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 15:11:29 +0300 Subject: account for selected status with diffing articles, trigger selection change event on reload --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 1 + .../src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 8f15272c..e75d4ba4 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 @@ -492,6 +492,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } m_listener.onHeadlinesLoaded(appended); + m_listener.onArticleListSelectionChange(); }); return view; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java index b652946a..c236610c 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java @@ -15,9 +15,8 @@ public class HeadlinesDiffItemCallback extends DiffUtil.ItemCallback
{ @Override public boolean areContentsTheSame(@NonNull Article a1, @NonNull Article a2) { - // Log.d(TAG, "[DIFF] areContentsTheSame a1=" + a1.title + " a2=" + a2.title); - return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked - && a1.published == a2.published && a1.note.equals(a2.note); + && a1.selected == a2.selected && a1.published == a2.published + && a1.note.equals(a2.note); } } -- cgit v1.2.3-54-g00ecf From 49e4eca4dba721390043140e816c23cb8846d5a6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 15:38:41 +0300 Subject: trigger lazy load after a short delay, set some model methods to private --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 8 ++++---- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 e75d4ba4..ab9e9f96 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 @@ -436,15 +436,15 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { HeadlinesModel model = Application.getInstance().getHeadlinesModel(); - if (dy > 0 && !m_isLazyLoading && model.lazyLoadEnabled() && + if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.lazyLoadEnabled() && lastVisibleItem >= Application.getArticles().size() - 5) { - m_isLazyLoading = true; - Log.d(TAG, "attempting to lazy load more articles..."); + m_isLazyLoading = true; + // this has to be dispatched delayed, consequent adapter updates are forbidden in scroll handler - new Handler().postDelayed(() -> refresh(true), 0); + new Handler().postDelayed(() -> refresh(true), 250); } } }); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java index 08adb73b..cc8a9d3d 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java @@ -64,7 +64,6 @@ public class HeadlinesModel extends AndroidViewModel implements ApiCommon.ApiCal public LiveData getLiveData() { return m_articles; - } public ArticleList getArticles() { @@ -98,13 +97,13 @@ public class HeadlinesModel extends AndroidViewModel implements ApiCommon.ApiCal } } - public void forceLoad() { + private void forceLoad() { Log.d(TAG, "forceLoad"); m_articles.postValue(loadInBackground()); } - public ArticleList loadInBackground() { + private ArticleList loadInBackground() { Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); ArticleList articlesWork = new ArticleList(); @@ -314,4 +313,7 @@ public class HeadlinesModel extends AndroidViewModel implements ApiCommon.ApiCal return m_lastErrorMessage; } + public boolean isLoading() { + return m_loadingInProgress; + } } -- cgit v1.2.3-54-g00ecf From b735651f0f9dc94687122ad09c9918b4ea477f47 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 15:45:11 +0300 Subject: enable categories ootb like tt-rss main --- org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java | 2 +- org.fox.ttrss/src/main/res/xml/preferences.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index e63234cd..458f1f76 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -164,7 +164,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - if (m_prefs.getBoolean("enable_cats", false)) { + if (m_prefs.getBoolean("enable_cats", true)) { ft.replace(R.id.feeds_fragment, new FeedCategoriesFragment(), FRAG_CATS); } else { ft.replace(R.id.feeds_fragment, new FeedsFragment(), FRAG_FEEDS); diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index 81fbd5f1..94cf71db 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -44,7 +44,7 @@ android:title="@string/sort_feeds_by_unread" /> -- cgit v1.2.3-54-g00ecf From 4c1f2b9ab213f21084583b38aa4106faf2b940ab Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 16:11:02 +0300 Subject: update workaround for caching image height on predraw without depending on articles --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 48 ++++++++++------------ 1 file changed, 22 insertions(+), 26 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 ab9e9f96..0400cd26 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 @@ -81,6 +81,7 @@ import org.jsoup.nodes.Element; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashMap; import java.util.TimeZone; import java.util.stream.Collectors; @@ -121,6 +122,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { private MediaPlayer m_mediaPlayer; private TextureView m_activeTexture; + protected static HashMap m_flavorHeightsCache = new HashMap<>(); + public ArticleList getSelectedArticles() { return Application.getArticles() .stream() @@ -470,17 +473,20 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_adapter.submitList(tmp, () -> { if (!appended) m_list.scrollToPosition(0); + + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(false); + + m_isLazyLoading = false; + + m_listener.onHeadlinesLoaded(appended); + m_listener.onArticleListSelectionChange(); }); if (model.getFirstIdChanged()) Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) .setAction(R.string.reload, v -> refresh(false)).show(); - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(false); - - m_isLazyLoading = false; - if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { m_activity.login(); } else if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { @@ -490,9 +496,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.toast(model.getErrorMessage()); } } - - m_listener.onHeadlinesLoaded(appended); - m_listener.onArticleListSelectionChange(); }); return view; @@ -535,14 +538,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } } - // if we try to initLoader() all the time, onLoadFinished() might be sent twice - // https://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice - /* if (m_loader == null) { - m_loader = (HeadlinesLoader) LoaderManager.getInstance(this). - initLoader(Application.LOADER_HEADLINES, null, this); - - } */ - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); @@ -550,9 +545,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { model.setSearchQuery(getSearchQuery()); model.startLoading(append, m_feed, m_activity.getResizeWidth()); - - //m_loader.setSearchQuery(getSearchQuery()); - //m_loader.startLoading(append); } static class ArticleViewHolder extends RecyclerView.ViewHolder { @@ -580,8 +572,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public TextureView flavorVideoView; public MaterialButton attachmentsView; public ProgressTarget flavorProgressTarget; - - int flavorViewHeight; + int articleId; public ArticleViewHolder(View v) { super(v); @@ -592,7 +583,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { View flavorImage = view.findViewById(R.id.flavor_image); if (flavorImage != null) { - flavorViewHeight = flavorImage.getMeasuredHeight(); + HeadlinesFragment.m_flavorHeightsCache.put(articleId, flavorImage.getMeasuredHeight()); } return true; @@ -743,6 +734,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { Article article = getItem(position); + holder.articleId = article.id; + if (article.id == Article.TYPE_AMR_FOOTER && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { WindowManager wm = (WindowManager) m_activity.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); @@ -1040,10 +1033,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.flavorImageHolder.setVisibility(View.VISIBLE); // prevent lower listiew entries from jumping around if this row is modified - // TODO this doesn't work right now - if (holder.flavorViewHeight > 0) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); - lp.height = holder.flavorViewHeight; + if (m_flavorHeightsCache.containsKey(article.id)) { + int cachedHeight = m_flavorHeightsCache.get(article.id); + + if (cachedHeight > 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); + lp.height = cachedHeight; + } } holder.flavorProgressTarget.setModel(article.flavorImageUri); -- cgit v1.2.3-54-g00ecf From 6b50f1e92dda8807dd071a61550cdc7f45425cd1 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 16:23:46 +0300 Subject: prevent articles being toggled back to unread on auto catchup --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 0400cd26..67afe85d 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 @@ -392,16 +392,17 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { if (!m_readArticles.isEmpty()) { - m_activity.toggleArticlesUnread(m_readArticles); + + m_activity.setArticlesUnread(m_readArticles, Article.UPDATE_SET_FALSE); for (Article a : m_readArticles) { a.unread = false; - m_adapter.notifyItemChanged(Application.getArticles().getPositionById(a.id)); - } + int position = Application.getArticles().getPositionById(a.id); - if (m_feed != null) - m_feed.unread -= m_readArticles.size(); + if (position != -1) + m_adapter.notifyItemChanged(position); + } m_readArticles.clear(); @@ -421,20 +422,21 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // Log.d(TAG, "onScrolled: FVI=" + firstVisibleItem + " LVI=" + lastVisibleItem); if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + Log.d(TAG, "collecting articles to mark as read on scroll..."); for (int i = 0; i < firstVisibleItem; i++) { try { Article article = Application.getArticles().get(i); - if (article.unread && !m_readArticles.contains(article)) { - Log.d(TAG, "adding to mark read=" + article.title); - + if (article.unread && !m_readArticles.contains(article)) m_readArticles.add(article); - } + } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } } + + Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); } HeadlinesModel model = Application.getInstance().getHeadlinesModel(); -- cgit v1.2.3-54-g00ecf From c9373800c9f6e7ac3e089f0325e5d284fe7e7b9a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 16:47:17 +0300 Subject: add some auto catchup debugging, clear list before refreshing from scratch --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 53 +++++++++++++--------- 1 file changed, 32 insertions(+), 21 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 67afe85d..196265b8 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 @@ -80,6 +80,7 @@ import org.jsoup.nodes.Element; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.TimeZone; @@ -389,25 +390,26 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - if (!m_readArticles.isEmpty()) { + if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + Log.d(TAG, "marking articles as read, count=" + m_readArticles.size()); - m_activity.setArticlesUnread(m_readArticles, Article.UPDATE_SET_FALSE); + m_activity.setArticlesUnread(m_readArticles, Article.UPDATE_SET_FALSE); - for (Article a : m_readArticles) { - a.unread = false; + for (Article a : m_readArticles) { + a.unread = false; - int position = Application.getArticles().getPositionById(a.id); + int position = Application.getArticles().getPositionById(a.id); - if (position != -1) - m_adapter.notifyItemChanged(position); - } + if (position != -1) + m_adapter.notifyItemChanged(position); + } - m_readArticles.clear(); + m_readArticles.clear(); - new Handler().postDelayed(() -> m_activity.refresh(false), 100); - } + new Handler().postDelayed(() -> m_activity.refresh(false), 100); } } } @@ -461,7 +463,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { HeadlinesModel model = Application.getInstance().getHeadlinesModel(); model.getLiveData().observe(getActivity(), articles -> { - Log.d(TAG, "observed article list size=" + articles.size()); + Log.d(TAG, "observed article list size=" + articles.size() + " append=" + model.getAppend()); ArticleList tmp = new ArticleList(); @@ -531,22 +533,31 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void refresh(final boolean append) { + HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + if (!append) { m_activeArticleId = -1; if (m_adapter != null) { ArticleList tmp = new ArticleList(); - m_adapter.submitList(tmp); - } - } + m_adapter.submitList(tmp, () -> { - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(true); + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(true); - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + model.setSearchQuery(getSearchQuery()); + model.startLoading(false, m_feed, m_activity.getResizeWidth()); + + }); + } + } else { + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(true); + + model.setSearchQuery(getSearchQuery()); + model.startLoading(true, m_feed, m_activity.getResizeWidth()); + } - model.setSearchQuery(getSearchQuery()); - model.startLoading(append, m_feed, m_activity.getResizeWidth()); } static class ArticleViewHolder extends RecyclerView.ViewHolder { -- cgit v1.2.3-54-g00ecf From 85315002fcdd67b0b738194473b08ec025aa2cc5 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 16:53:40 +0300 Subject: drop unnecessary field --- org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java | 1 - 1 file changed, 1 deletion(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index 76a62c58..624e54e4 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -68,7 +68,6 @@ public class Article implements Parcelable { transient public String flavorStreamUri; transient public String youtubeVid; transient public List mediaList = new ArrayList<>(); - transient public int flavorViewHeight; public Article(Parcel in) { readFromParcel(in); -- cgit v1.2.3-54-g00ecf From ce277d2589502875419a5e430e38630be290b425 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 17:05:49 +0300 Subject: revert clearing list before refresh for now --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 196265b8..849eb8c5 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 @@ -535,29 +535,14 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void refresh(final boolean append) { HeadlinesModel model = Application.getInstance().getHeadlinesModel(); - if (!append) { + if (!append) m_activeArticleId = -1; - if (m_adapter != null) { - ArticleList tmp = new ArticleList(); - m_adapter.submitList(tmp, () -> { - - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(true); - - model.setSearchQuery(getSearchQuery()); - model.startLoading(false, m_feed, m_activity.getResizeWidth()); - - }); - } - } else { - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(true); - - model.setSearchQuery(getSearchQuery()); - model.startLoading(true, m_feed, m_activity.getResizeWidth()); - } + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(true); + model.setSearchQuery(getSearchQuery()); + model.startLoading(append, m_feed, m_activity.getResizeWidth()); } static class ArticleViewHolder extends RecyclerView.ViewHolder { -- cgit v1.2.3-54-g00ecf From d7ff3136f56d366d44545152f48619c4b0f03ff7 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 17:16:13 +0300 Subject: fix swipe to dismiss, allow it working with auto catchup --- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 17 +++++++++++------ org.fox.ttrss/src/main/res/xml/preferences.xml | 1 - 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 849eb8c5..8c881924 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 @@ -312,7 +312,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { refresh(false); } - if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) && !m_prefs.getBoolean("headlines_mark_read_scroll", false) ) { + if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) /*&& !m_prefs.getBoolean("headlines_mark_read_scroll", false) */) { ItemTouchHelper swipeHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { @@ -357,8 +357,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { wasUnread = false; } - Application.getArticles().remove(adapterPosition); - m_adapter.notifyItemRemoved(adapterPosition); + ArticleList tmpRemove = new ArticleList(); + tmpRemove.addAll(Application.getArticles()); + tmpRemove.remove(adapterPosition); + + Application.getInstance().getHeadlinesModel().update(tmpRemove); Snackbar.make(m_list, R.string.headline_undo_row_prompt, Snackbar.LENGTH_LONG) .setAction(getString(R.string.headline_undo_row_button), v -> { @@ -368,9 +371,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.saveArticleUnread(article); } - Application.getArticles().add(adapterPosition, article); - m_adapter.notifyItemInserted(adapterPosition); - m_adapter.notifyItemRangeChanged(adapterPosition, 1); + ArticleList tmpInsert = new ArticleList(); + tmpInsert.addAll(Application.getArticles()); + tmpInsert.add(adapterPosition, article); + + Application.getInstance().getHeadlinesModel().update(tmpInsert); }).show(); } diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index 94cf71db..8d064587 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -108,7 +108,6 @@ -- cgit v1.2.3-54-g00ecf From 1580f921cf2433d27591bf9358a3f23bc3d2e95e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 17:18:13 +0300 Subject: disable auto catchup log spam --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 8c881924..2e6a4323 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 @@ -429,8 +429,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // Log.d(TAG, "onScrolled: FVI=" + firstVisibleItem + " LVI=" + lastVisibleItem); if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - Log.d(TAG, "collecting articles to mark as read on scroll..."); - for (int i = 0; i < firstVisibleItem; i++) { try { Article article = Application.getArticles().get(i); @@ -443,7 +441,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } } - Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); + // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); } HeadlinesModel model = Application.getInstance().getHeadlinesModel(); -- cgit v1.2.3-54-g00ecf From 895b0d9c2473fcd3351c5368176b79e63b1d6079 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 18:43:19 +0300 Subject: refactor to more consistent naming, allow observing articlelist changes and update requests separately --- .../src/main/java/org/fox/ttrss/Application.java | 6 +- .../src/main/java/org/fox/ttrss/ArticlePager.java | 8 +- .../src/main/java/org/fox/ttrss/ArticlesModel.java | 324 +++++++++++++++++++++ .../main/java/org/fox/ttrss/HeadlinesFragment.java | 54 ++-- .../main/java/org/fox/ttrss/HeadlinesModel.java | 319 -------------------- .../main/java/org/fox/ttrss/OnlineActivity.java | 17 +- .../src/main/java/org/fox/ttrss/types/Article.java | 39 ++- .../main/java/org/fox/ttrss/types/ArticleList.java | 4 + .../fox/ttrss/util/ArticleDiffItemCallback.java | 21 ++ .../fox/ttrss/util/HeadlinesDiffItemCallback.java | 22 -- 10 files changed, 437 insertions(+), 377 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index 08beaa99..441929f4 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -25,7 +25,7 @@ public class Application extends android.app.Application { private int m_apiLevel; public LinkedHashMap m_customSortModes = new LinkedHashMap<>(); ConnectivityManager m_cmgr; - HeadlinesModel m_headlinesModel; + ArticlesModel m_headlinesModel; public static Application getInstance(){ return m_singleton; @@ -35,7 +35,7 @@ public class Application extends android.app.Application { return getInstance().m_headlinesModel.getArticles(); } - public HeadlinesModel getHeadlinesModel() { + public static ArticlesModel getHeadlinesModel() { return getInstance().m_headlinesModel; } @@ -45,7 +45,7 @@ public class Application extends android.app.Application { m_singleton = this; m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - m_headlinesModel = new HeadlinesModel(this); + m_headlinesModel = new ArticlesModel(this); } public String getSessionId() { 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 89043cbc..ce3b114e 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 @@ -16,7 +16,7 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; import org.fox.ttrss.util.DiffFragmentStateAdapter; -import org.fox.ttrss.util.HeadlinesDiffItemCallback; +import org.fox.ttrss.util.ArticleDiffItemCallback; public class ArticlePager extends androidx.fragment.app.Fragment { @@ -31,7 +31,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { private static class PagerAdapter extends DiffFragmentStateAdapter
{ public PagerAdapter(@NonNull Fragment fragment) { - super(fragment, new HeadlinesDiffItemCallback()); + super(fragment, new ArticleDiffItemCallback()); } private void syncToSharedArticles() { @@ -85,10 +85,10 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_adapter = new PagerAdapter(this); m_adapter.submitList(Application.getArticles()); - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getInstance().getHeadlinesModel(); // deal with further updates - model.getLiveData().observe(getActivity(), articles -> { + model.getArticlesData().observe(getActivity(), articles -> { Log.d(TAG, "observed article list size=" + articles.size()); m_adapter.submitList(articles); }); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java new file mode 100644 index 00000000..af535580 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java @@ -0,0 +1,324 @@ +package org.fox.ttrss; + +import android.app.Application; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +import org.fox.ttrss.types.Article; +import org.fox.ttrss.types.ArticleList; +import org.fox.ttrss.types.Feed; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ArticlesModel extends AndroidViewModel implements ApiCommon.ApiCaller { + private final String TAG = this.getClass().getSimpleName(); + private final MutableLiveData m_articles = new MutableLiveData<>(new ArticleList()); + private SharedPreferences m_prefs; + private final int m_responseCode = 0; + protected String m_responseMessage; + private int m_apiStatusCode = 0; + + private String m_lastErrorMessage; + private ApiCommon.ApiError m_lastError; + private Feed m_feed; + private int m_firstId; + private String m_searchQuery = ""; + private boolean m_firstIdChanged; + private int m_offset; + private int m_amountLoaded; + private int m_resizeWidth; + private boolean m_append; + private boolean m_lazyLoadEnabled = true; + private boolean m_loadingInProgress; + private ExecutorService m_executor; + private Handler m_mainHandler = new Handler(Looper.getMainLooper()); + private MutableLiveData m_lastUpdate = new MutableLiveData<>(new Long(0)); + + public ArticlesModel(@NonNull Application application) { + super(application); + + m_prefs = PreferenceManager.getDefaultSharedPreferences(application); + + // do we need concurrency or not? + m_executor = Executors.newSingleThreadExecutor(); + } + + public LiveData getUpdatesData() { + return m_lastUpdate; + } + + public LiveData getArticlesData() { + return m_articles; + } + + public ArticleList getArticles() { + return m_articles.getValue(); + } + + public void update(int position, Article article) { + m_articles.getValue().set(position, article); + m_articles.postValue(m_articles.getValue()); + } + + public void update(ArticleList articles) { + m_articles.postValue(articles); + } + + public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { + Log.d(TAG, "startLoading append=" + append); + + m_resizeWidth = resizeWidth; + + if (!append) { + m_append = false; + m_lazyLoadEnabled = true; + m_feed = feed; + + forceLoad(); + } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { + m_append = true; + m_feed = feed; + forceLoad(); + } else { + m_articles.postValue(m_articles.getValue()); + } + } + + private void forceLoad() { + Log.d(TAG, "forceLoad"); + loadInBackground(); + } + + private ArticleList loadInBackground() { + Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); + + ArticleList articlesWork = new ArticleList(); + articlesWork.addAll(m_articles.getValue()); + + m_loadingInProgress = true; + + final int skip = getSkip(m_append, articlesWork); + final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && + !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; + + HashMap params = new HashMap<>(); + + params.put("op", "getHeadlines"); + params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); + params.put("feed_id", String.valueOf(m_feed.id)); + params.put("show_excerpt", "true"); + params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); + params.put("show_content", "true"); + params.put("include_attachments", "true"); + params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); + params.put("limit", m_prefs.getString("headlines_request_size", "15")); + params.put("skip", String.valueOf(skip)); + params.put("include_nested", "true"); + params.put("has_sandbox", "true"); + params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); + + if (m_prefs.getBoolean("enable_image_downsampling", false)) { + if (m_prefs.getBoolean("always_downsample_images", false) || !org.fox.ttrss.Application.getInstance().isWifiConnected()) { + params.put("resize_width", String.valueOf(m_resizeWidth)); + } + } + + if (m_feed.is_cat) + params.put("is_cat", "true"); + + if (allowForceUpdate) { + params.put("force_update", "true"); + } + + if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + params.put("search", m_searchQuery); + params.put("search_mode", ""); + params.put("match_on", "both"); + } + + if (m_firstId > 0) + params.put("check_first_id", String.valueOf(m_firstId)); + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + params.put("include_header", "true"); + } + + Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + articlesWork.size()); + + m_executor.execute(() -> { + JsonElement result = ApiCommon.performRequest(getApplication(), params, this); + + Log.d(TAG, "got result=" + result); + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + final List
articlesJson; + final JsonObject header; + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + header = content.get(0).getAsJsonObject(); + + m_firstIdChanged = header.get("first_id_changed") != null; + + try { + m_firstId = header.get("first_id").getAsInt(); + } catch (NumberFormatException e) { + m_firstId = 0; + } + + Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); + + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content.get(1), listType); + } else { + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content, listType); + } + + if (!m_append) + articlesWork.clear(); + + m_amountLoaded = articlesJson.size(); + + for (Article article : articlesJson) + if (!articlesWork.containsId(article.id)) { + article.collectMediaInfo(); + article.cleanupExcerpt(); + article.fixNullFields(); + articlesWork.add(article); + } + + if (m_firstIdChanged) { + Log.d(TAG, "first id changed, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { + Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + m_offset += m_amountLoaded; + m_loadingInProgress = false; + + Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + articlesWork.size()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + m_mainHandler.post(() -> { + m_articles.setValue(articlesWork); + m_lastUpdate.setValue(System.currentTimeMillis()); + }); + }); + + m_loadingInProgress = false; + + return articlesWork; + } + + private int getSkip(boolean append, ArticleList articles) { + int skip = 0; + + if (append) { + // adaptive, all_articles, marked, published, unread + String viewMode = m_prefs.getString("view_mode", "adaptive"); + + int numUnread = Math.toIntExact(articles.getUnreadCount()); + int numAll = Math.toIntExact(articles.size()); + + if ("marked".equals(viewMode)) { + skip = numAll; + } else if ("published".equals(viewMode)) { + skip = numAll; + } else if ("unread".equals(viewMode)) { + skip = numUnread; + } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + skip = numAll; + } else if ("adaptive".equals(viewMode)) { + skip = numUnread > 0 ? numUnread : numAll; + } else { + skip = numAll; + } + } + + return skip; + } + + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } + + @Override + public void setLastError(ApiCommon.ApiError lastError) { + m_lastError = lastError; + } + + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } + + public boolean getFirstIdChanged() { + return m_firstIdChanged; + } + + public boolean getAppend() { + return m_append; + } + + public void setSearchQuery(String searchQuery) { + m_searchQuery = searchQuery; + } + + public String getSearchQuery() { + return m_searchQuery; + } + + public int getOffset() { + return m_offset; + } + + public boolean lazyLoadEnabled() { + return m_lazyLoadEnabled; + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + ApiCommon.ApiError getLastError() { + return m_lastError; + } + + String getLastErrorMessage() { + return m_lastErrorMessage; + } + + public boolean isLoading() { + return m_loadingInProgress; + } +} 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 2e6a4323..62f32cfa 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 @@ -43,12 +43,9 @@ import android.widget.PopupMenu; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.core.view.ViewCompat; -import androidx.loader.content.Loader; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.ItemTouchHelper; @@ -74,13 +71,12 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Attachment; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.util.HeadlinesDiffItemCallback; +import org.fox.ttrss.util.ArticleDiffItemCallback; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.TimeZone; @@ -395,7 +391,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getInstance().getHeadlinesModel(); if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { @@ -444,7 +440,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); } - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getInstance().getHeadlinesModel(); if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.lazyLoadEnabled() && lastVisibleItem >= Application.getArticles().size() - 5) { @@ -463,14 +459,13 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.setTitle(m_feed.title); } - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getHeadlinesModel(); - model.getLiveData().observe(getActivity(), articles -> { - Log.d(TAG, "observed article list size=" + articles.size() + " append=" + model.getAppend()); + // this gets notified on network update + model.getUpdatesData().observe(getActivity(), lastUpdate -> { + ArticleList tmp = new ArticleList(model.getArticles()); - ArticleList tmp = new ArticleList(); - - tmp.addAll(articles); + Log.d(TAG, "observed last update=" + lastUpdate + " article count=" + tmp.size()); if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) tmp.add(new Article(Article.TYPE_AMR_FOOTER)); @@ -503,6 +498,19 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.toast(model.getErrorMessage()); } } + + }); + + // loaded articles might get modified for all sorts of reasons + model.getArticlesData().observe(getActivity(), articles -> { + Log.d(TAG, "observed article list size=" + articles.size()); + + ArticleList tmp = new ArticleList(model.getArticles()); + + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + + m_adapter.submitList(tmp); }); return view; @@ -536,7 +544,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void refresh(final boolean append) { - HeadlinesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getInstance().getHeadlinesModel(); if (!append) m_activeArticleId = -1; @@ -688,7 +696,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public ArticleListAdapter() { - super(new HeadlinesDiffItemCallback()); + super(new ArticleDiffItemCallback()); Display display = m_activity.getWindowManager().getDefaultDisplay(); Point size = new Point(); @@ -836,11 +844,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.markedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); holder.markedView.setOnClickListener(v -> { - article.marked = !article.marked; + Article selectedArticle = new Article(getItem(position)); + selectedArticle.marked = !selectedArticle.marked; - m_adapter.notifyItemChanged(m_list.getChildAdapterPosition(holder.view)); - - m_activity.saveArticleMarked(article); + m_activity.saveArticleMarked(selectedArticle); + Application.getHeadlinesModel().update(position, selectedArticle); }); } @@ -901,10 +909,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.publishedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); holder.publishedView.setOnClickListener(v -> { - article.published = !article.published; - m_adapter.notifyItemChanged(m_list.getChildAdapterPosition(holder.view)); + Article selectedArticle = new Article(getItem(position)); + selectedArticle.published = !selectedArticle.published; + + m_activity.saveArticlePublished(selectedArticle); - m_activity.saveArticlePublished(article); + Application.getHeadlinesModel().update(position, selectedArticle); }); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java deleted file mode 100644 index cc8a9d3d..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesModel.java +++ /dev/null @@ -1,319 +0,0 @@ -package org.fox.ttrss; - -import android.app.Application; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.lifecycle.AndroidViewModel; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.loader.app.LoaderManager; -import androidx.preference.PreferenceManager; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; -import org.fox.ttrss.types.Feed; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class HeadlinesModel extends AndroidViewModel implements ApiCommon.ApiCaller { - private final String TAG = this.getClass().getSimpleName(); - private final MutableLiveData m_articles = new MutableLiveData<>(new ArticleList()); - private SharedPreferences m_prefs; - private final int m_responseCode = 0; - protected String m_responseMessage; - private int m_apiStatusCode = 0; - - private String m_lastErrorMessage; - private ApiCommon.ApiError m_lastError; - private Feed m_feed; - private int m_firstId; - private String m_searchQuery = ""; - private boolean m_firstIdChanged; - private int m_offset; - private int m_amountLoaded; - private int m_resizeWidth; - private boolean m_append; - private boolean m_lazyLoadEnabled = true; - private boolean m_loadingInProgress; - private ExecutorService m_executor; - private Handler m_mainHandler = new Handler(Looper.getMainLooper()); - - public HeadlinesModel(@NonNull Application application) { - super(application); - - m_prefs = PreferenceManager.getDefaultSharedPreferences(application); - - // do we need concurrency or not? - m_executor = Executors.newSingleThreadExecutor(); - } - - public LiveData getLiveData() { - return m_articles; - } - - public ArticleList getArticles() { - return m_articles.getValue(); - } - - public void update(ArticleList articles) { - m_articles.postValue(articles); - } - - public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { - Log.d(TAG, "startLoading append=" + append); - - m_resizeWidth = resizeWidth; - - if (!append) { - m_append = false; - m_lazyLoadEnabled = true; - m_feed = feed; - - forceLoad(); - } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { - m_append = true; - m_feed = feed; - forceLoad(); - } else { - ArticleList tmp = new ArticleList(); - tmp.addAll(m_articles.getValue()); - - m_articles.postValue(tmp); - } - } - - private void forceLoad() { - Log.d(TAG, "forceLoad"); - - m_articles.postValue(loadInBackground()); - } - - private ArticleList loadInBackground() { - Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); - - ArticleList articlesWork = new ArticleList(); - articlesWork.addAll(m_articles.getValue()); - - m_loadingInProgress = true; - - final int skip = getSkip(m_append, articlesWork); - final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && - !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; - - HashMap params = new HashMap<>(); - - params.put("op", "getHeadlines"); - params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); - params.put("feed_id", String.valueOf(m_feed.id)); - params.put("show_excerpt", "true"); - params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); - params.put("show_content", "true"); - params.put("include_attachments", "true"); - params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); - params.put("limit", m_prefs.getString("headlines_request_size", "15")); - params.put("skip", String.valueOf(skip)); - params.put("include_nested", "true"); - params.put("has_sandbox", "true"); - params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); - - if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (m_prefs.getBoolean("always_downsample_images", false) || !org.fox.ttrss.Application.getInstance().isWifiConnected()) { - params.put("resize_width", String.valueOf(m_resizeWidth)); - } - } - - if (m_feed.is_cat) - params.put("is_cat", "true"); - - if (allowForceUpdate) { - params.put("force_update", "true"); - } - - if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - params.put("search", m_searchQuery); - params.put("search_mode", ""); - params.put("match_on", "both"); - } - - if (m_firstId > 0) - params.put("check_first_id", String.valueOf(m_firstId)); - - if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { - params.put("include_header", "true"); - } - - Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + articlesWork.size()); - - m_executor.execute(() -> { - JsonElement result = ApiCommon.performRequest(getApplication(), params, this); - - Log.d(TAG, "got result=" + result); - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - final List
articlesJson; - final JsonObject header; - - if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { - header = content.get(0).getAsJsonObject(); - - m_firstIdChanged = header.get("first_id_changed") != null; - - try { - m_firstId = header.get("first_id").getAsInt(); - } catch (NumberFormatException e) { - m_firstId = 0; - } - - Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); - - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content.get(1), listType); - } else { - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content, listType); - } - - if (!m_append) - articlesWork.clear(); - - m_amountLoaded = articlesJson.size(); - - for (Article article : articlesJson) - if (!articlesWork.containsId(article.id)) { - article.collectMediaInfo(); - article.cleanupExcerpt(); - article.fixNullFields(); - articlesWork.add(article); - } - - if (m_firstIdChanged) { - Log.d(TAG, "first id changed, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - m_offset += m_amountLoaded; - m_loadingInProgress = false; - - Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + articlesWork.size()); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - m_mainHandler.post(() -> { - m_articles.postValue(articlesWork); - }); - }); - - m_loadingInProgress = false; - - return articlesWork; - } - - private int getSkip(boolean append, ArticleList articles) { - int skip = 0; - - if (append) { - // adaptive, all_articles, marked, published, unread - String viewMode = m_prefs.getString("view_mode", "adaptive"); - - int numUnread = Math.toIntExact(articles.getUnreadCount()); - int numAll = Math.toIntExact(articles.size()); - - if ("marked".equals(viewMode)) { - skip = numAll; - } else if ("published".equals(viewMode)) { - skip = numAll; - } else if ("unread".equals(viewMode)) { - skip = numUnread; - } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - skip = numAll; - } else if ("adaptive".equals(viewMode)) { - skip = numUnread > 0 ? numUnread : numAll; - } else { - skip = numAll; - } - } - - return skip; - } - - @Override - public void setStatusCode(int statusCode) { - m_apiStatusCode = statusCode; - } - - @Override - public void setLastError(ApiCommon.ApiError lastError) { - m_lastError = lastError; - } - - @Override - public void setLastErrorMessage(String message) { - m_lastErrorMessage = message; - } - - public boolean getFirstIdChanged() { - return m_firstIdChanged; - } - - public boolean getAppend() { - return m_append; - } - - public void setSearchQuery(String searchQuery) { - m_searchQuery = searchQuery; - } - - public String getSearchQuery() { - return m_searchQuery; - } - - public int getOffset() { - return m_offset; - } - - public boolean lazyLoadEnabled() { - return m_lazyLoadEnabled; - } - - public int getErrorMessage() { - return ApiCommon.getErrorMessage(m_lastError); - } - - ApiCommon.ApiError getLastError() { - return m_lastError; - } - - String getLastErrorMessage() { - return m_lastErrorMessage; - } - - public boolean isLoading() { - return m_loadingInProgress; - } -} 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 db06415f..4faff8af 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 @@ -864,7 +864,8 @@ public class OnlineActivity extends CommonActivity { protected void setApiLevel(int apiLevel) { Application.getInstance().setApiLevel(apiLevel); } - + + // TODO switch to setArticleField() public void saveArticleUnread(final Article article) { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { @@ -878,11 +879,12 @@ public class OnlineActivity extends CommonActivity { map.put("op", "updateArticle"); map.put("article_ids", String.valueOf(article.id)); map.put("mode", article.unread ? "1" : "0"); - map.put("field", "2"); + map.put("field", String.valueOf(Article.UPDATE_FIELD_UNREAD)); req.execute(map); } + // TODO switch to setArticleField() public void saveArticleScore(final Article article) { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { @@ -896,11 +898,12 @@ public class OnlineActivity extends CommonActivity { map.put("op", "updateArticle"); map.put("article_ids", String.valueOf(article.id)); map.put("data", String.valueOf(article.score)); - map.put("field", "4"); + map.put("field", String.valueOf(Article.UPDATE_FIELD_SCORE)); req.execute(map); } + // TODO switch to setArticleField() public void saveArticleMarked(final Article article) { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { @@ -914,11 +917,12 @@ public class OnlineActivity extends CommonActivity { map.put("op", "updateArticle"); map.put("article_ids", String.valueOf(article.id)); map.put("mode", article.marked ? "1" : "0"); - map.put("field", "0"); + map.put("field", String.valueOf(Article.UPDATE_FIELD_MARKED)); req.execute(map); } + // TODO switch to setArticleField() public void saveArticlePublished(final Article article) { ApiRequest req = new ApiRequest(getApplicationContext()) { @@ -933,11 +937,12 @@ public class OnlineActivity extends CommonActivity { map.put("op", "updateArticle"); map.put("article_ids", String.valueOf(article.id)); map.put("mode", article.published ? "1" : "0"); - map.put("field", "1"); + map.put("field", String.valueOf(Article.UPDATE_FIELD_PUBLISHED)); req.execute(map); } + // TODO switch to setArticleField() public void saveArticleNote(final Article article, final String note) { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { @@ -951,7 +956,7 @@ public class OnlineActivity extends CommonActivity { map.put("article_ids", String.valueOf(article.id)); map.put("mode", "1"); map.put("data", note); - map.put("field", "3"); + map.put("field", String.valueOf(Article.UPDATE_FIELD_NOTE)); req.execute(map); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index 624e54e4..c0f63937 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -69,7 +69,7 @@ public class Article implements Parcelable { transient public String youtubeVid; transient public List mediaList = new ArrayList<>(); - public Article(Parcel in) { + public Article(Parcel in) { readFromParcel(in); } @@ -195,6 +195,43 @@ public class Article implements Parcelable { fixNullFields(); } + public Article(Article clone) { + id = clone.id; + unread = clone.unread; + marked = clone.marked; + published = clone.published; + score = clone.score; + updated = clone.updated; + is_updated = clone.is_updated; + title = clone.title; + link = clone.link; + feed_id = clone.feed_id; + tags = clone.tags; + attachments = clone.attachments; + content = clone.content; + excerpt = clone.excerpt; + labels = clone.labels; + feed_title = clone.feed_title; + comments_count = clone.comments_count; + comments_link = clone.comments_link; + always_display_attachments = clone.always_display_attachments; + author = clone.author; + note = clone.note; + selected = clone.selected; + flavor_image = clone.flavor_image; + flavor_stream = clone.flavor_stream; + flavor_kind = clone.flavor_kind; + site_url = clone.site_url; + + articleDoc = clone.articleDoc; + flavorImage = clone.flavorImage; + + flavorImageUri = clone.flavorImageUri; + flavorStreamUri = clone.flavorStreamUri; + youtubeVid = clone.youtubeVid; + mediaList = new ArrayList<>(clone.mediaList); + } + @Override public int describeContents() { return 0; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java index 01af780c..74df2c33 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java @@ -23,6 +23,10 @@ public class ArticleList extends CopyOnWriteArrayList
{ public ArticleList() { } + public ArticleList(ArticleList clone) { + this.addAll(clone); + } + public ArticleList getWithoutFooters() { return this.stream().filter(a -> { return a.id > 0; }).collect(Collectors.toCollection(ArticleList::new)); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java new file mode 100644 index 00000000..b037eea0 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java @@ -0,0 +1,21 @@ +package org.fox.ttrss.util; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; + +import org.fox.ttrss.types.Article; + +public class ArticleDiffItemCallback extends DiffUtil.ItemCallback
{ + private final String TAG = this.getClass().getSimpleName(); + @Override + public boolean areItemsTheSame(@NonNull Article a1, @NonNull Article a2) { + return a1.id == a2.id; + } + + @Override + public boolean areContentsTheSame(@NonNull Article a1, @NonNull Article a2) { + return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked + && a1.selected == a2.selected && a1.published == a2.published + && a1.note.equals(a2.note); + } +} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java deleted file mode 100644 index c236610c..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeadlinesDiffItemCallback.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.fox.ttrss.util; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.DiffUtil; - -import org.fox.ttrss.types.Article; - -public class HeadlinesDiffItemCallback extends DiffUtil.ItemCallback
{ - @Override - public boolean areItemsTheSame(@NonNull Article a1, @NonNull Article a2) { - // Log.d(TAG, "[DIFF] areItemsTheSame a1=" + a1.title + " a2=" + a2.title); - - return a1.id == a2.id; - } - - @Override - public boolean areContentsTheSame(@NonNull Article a1, @NonNull Article a2) { - return a1.id == a2.id && a1.unread == a2.unread && a1.marked == a2.marked - && a1.selected == a2.selected && a1.published == a2.published - && a1.note.equals(a2.note); - } -} -- cgit v1.2.3-54-g00ecf From 726fc7371a04ea1bf936d9215fc58dd4d7c10c34 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 18:43:51 +0300 Subject: drop pointless method --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java index af535580..7b1db3ee 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java @@ -92,21 +92,17 @@ public class ArticlesModel extends AndroidViewModel implements ApiCommon.ApiCall m_lazyLoadEnabled = true; m_feed = feed; - forceLoad(); + loadInBackground(); } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { m_append = true; m_feed = feed; - forceLoad(); + + loadInBackground(); } else { m_articles.postValue(m_articles.getValue()); } } - private void forceLoad() { - Log.d(TAG, "forceLoad"); - loadInBackground(); - } - private ArticleList loadInBackground() { Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); -- cgit v1.2.3-54-g00ecf From 46c4e0b833fb921e54fba213cc3cada2a20833b2 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 18:47:31 +0300 Subject: switch to list clone constructors --- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java | 5 +---- org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java | 3 +-- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 6 ++---- 3 files changed, 4 insertions(+), 10 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 ce3b114e..77b7d9aa 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 @@ -35,10 +35,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { } private void syncToSharedArticles() { - ArticleList tmp = new ArticleList(); - tmp.addAll(Application.getArticles()); - - submitList(tmp); + submitList(new ArticleList(Application.getArticles())); } @Override diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java index 7b1db3ee..66027966 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java @@ -106,8 +106,7 @@ public class ArticlesModel extends AndroidViewModel implements ApiCommon.ApiCall private ArticleList loadInBackground() { Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); - ArticleList articlesWork = new ArticleList(); - articlesWork.addAll(m_articles.getValue()); + ArticleList articlesWork = new ArticleList(m_articles.getValue()); m_loadingInProgress = true; 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 62f32cfa..8fece790 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 @@ -353,8 +353,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { wasUnread = false; } - ArticleList tmpRemove = new ArticleList(); - tmpRemove.addAll(Application.getArticles()); + ArticleList tmpRemove = new ArticleList(Application.getArticles()); tmpRemove.remove(adapterPosition); Application.getInstance().getHeadlinesModel().update(tmpRemove); @@ -367,8 +366,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.saveArticleUnread(article); } - ArticleList tmpInsert = new ArticleList(); - tmpInsert.addAll(Application.getArticles()); + ArticleList tmpInsert = new ArticleList(Application.getArticles()); tmpInsert.add(adapterPosition, article); Application.getInstance().getHeadlinesModel().update(tmpInsert); -- cgit v1.2.3-54-g00ecf From 4ff411312658a30dd2fc66d92a1fc5cc0d9d9546 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 18:48:52 +0300 Subject: switch to static usages, refactor method name --- .../src/main/java/org/fox/ttrss/Application.java | 10 +++++----- .../src/main/java/org/fox/ttrss/ArticlePager.java | 2 +- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index 441929f4..ad6dbb93 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -25,18 +25,18 @@ public class Application extends android.app.Application { private int m_apiLevel; public LinkedHashMap m_customSortModes = new LinkedHashMap<>(); ConnectivityManager m_cmgr; - ArticlesModel m_headlinesModel; + ArticlesModel m_articlesModel; public static Application getInstance(){ return m_singleton; } public static ArticleList getArticles() { - return getInstance().m_headlinesModel.getArticles(); + return getInstance().m_articlesModel.getArticles(); } - public static ArticlesModel getHeadlinesModel() { - return getInstance().m_headlinesModel; + public static ArticlesModel getArticlesModel() { + return getInstance().m_articlesModel; } @Override @@ -45,7 +45,7 @@ public class Application extends android.app.Application { m_singleton = this; m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - m_headlinesModel = new ArticlesModel(this); + m_articlesModel = new ArticlesModel(this); } public String getSessionId() { 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 77b7d9aa..d2978858 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 @@ -82,7 +82,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_adapter = new PagerAdapter(this); m_adapter.submitList(Application.getArticles()); - ArticlesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getArticlesModel(); // deal with further updates model.getArticlesData().observe(getActivity(), articles -> { 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 8fece790..da1c160b 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 @@ -356,7 +356,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { ArticleList tmpRemove = new ArticleList(Application.getArticles()); tmpRemove.remove(adapterPosition); - Application.getInstance().getHeadlinesModel().update(tmpRemove); + Application.getArticlesModel().update(tmpRemove); Snackbar.make(m_list, R.string.headline_undo_row_prompt, Snackbar.LENGTH_LONG) .setAction(getString(R.string.headline_undo_row_button), v -> { @@ -369,7 +369,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { ArticleList tmpInsert = new ArticleList(Application.getArticles()); tmpInsert.add(adapterPosition, article); - Application.getInstance().getHeadlinesModel().update(tmpInsert); + Application.getArticlesModel().update(tmpInsert); }).show(); } @@ -389,7 +389,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - ArticlesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getArticlesModel(); if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { @@ -438,7 +438,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); } - ArticlesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getArticlesModel(); if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.lazyLoadEnabled() && lastVisibleItem >= Application.getArticles().size() - 5) { @@ -457,7 +457,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.setTitle(m_feed.title); } - ArticlesModel model = Application.getHeadlinesModel(); + ArticlesModel model = Application.getArticlesModel(); // this gets notified on network update model.getUpdatesData().observe(getActivity(), lastUpdate -> { @@ -542,7 +542,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void refresh(final boolean append) { - ArticlesModel model = Application.getInstance().getHeadlinesModel(); + ArticlesModel model = Application.getArticlesModel(); if (!append) m_activeArticleId = -1; @@ -846,7 +846,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { selectedArticle.marked = !selectedArticle.marked; m_activity.saveArticleMarked(selectedArticle); - Application.getHeadlinesModel().update(position, selectedArticle); + Application.getArticlesModel().update(position, selectedArticle); }); } @@ -912,7 +912,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.saveArticlePublished(selectedArticle); - Application.getHeadlinesModel().update(position, selectedArticle); + Application.getArticlesModel().update(position, selectedArticle); }); } -- cgit v1.2.3-54-g00ecf From 6c50858455da798226c38845386d4b33a566f6e9 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 19:29:49 +0300 Subject: refactor some more --- .../src/main/java/org/fox/ttrss/Application.java | 14 +- .../src/main/java/org/fox/ttrss/ArticleModel.java | 316 ++++++++++++++++++++ .../src/main/java/org/fox/ttrss/ArticlePager.java | 4 +- .../src/main/java/org/fox/ttrss/ArticlesModel.java | 319 --------------------- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 14 +- 5 files changed, 330 insertions(+), 337 deletions(-) create mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index ad6dbb93..75542e3c 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -17,26 +17,22 @@ public class Application extends android.app.Application { private static Application m_singleton; - // this is the only instance of a (large) object which contains all currently loaded articles and is - // used by all fragments and activities concurrently - // private final ArticleList m_articles = new ArticleList(); - private String m_sessionId; private int m_apiLevel; public LinkedHashMap m_customSortModes = new LinkedHashMap<>(); ConnectivityManager m_cmgr; - ArticlesModel m_articlesModel; + ArticleModel m_articleModel; public static Application getInstance(){ return m_singleton; } public static ArticleList getArticles() { - return getInstance().m_articlesModel.getArticles(); + return getInstance().m_articleModel.getArticles().getValue(); } - public static ArticlesModel getArticlesModel() { - return getInstance().m_articlesModel; + public static ArticleModel getArticlesModel() { + return getInstance().m_articleModel; } @Override @@ -45,7 +41,7 @@ public class Application extends android.app.Application { m_singleton = this; m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - m_articlesModel = new ArticlesModel(this); + m_articleModel = new ArticleModel(this); } public String getSessionId() { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java new file mode 100644 index 00000000..2496b84c --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java @@ -0,0 +1,316 @@ +package org.fox.ttrss; + +import android.app.Application; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; + +import org.fox.ttrss.types.Article; +import org.fox.ttrss.types.ArticleList; +import org.fox.ttrss.types.Feed; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCaller { + private final String TAG = this.getClass().getSimpleName(); + private final MutableLiveData m_articles = new MutableLiveData<>(new ArticleList()); + private SharedPreferences m_prefs; + private final int m_responseCode = 0; + protected String m_responseMessage; + private int m_apiStatusCode = 0; + + private String m_lastErrorMessage; + private ApiCommon.ApiError m_lastError; + private Feed m_feed; + private int m_firstId; + private String m_searchQuery = ""; + private boolean m_firstIdChanged; + private int m_offset; + private int m_amountLoaded; + private int m_resizeWidth; + private boolean m_append; + private boolean m_lazyLoadEnabled = true; + private boolean m_loadingInProgress; + private ExecutorService m_executor; + private Handler m_mainHandler = new Handler(Looper.getMainLooper()); + private MutableLiveData m_lastUpdate = new MutableLiveData<>(new Long(0)); + + public ArticleModel(@NonNull Application application) { + super(application); + + m_prefs = PreferenceManager.getDefaultSharedPreferences(application); + + // do we need concurrency or not? + m_executor = Executors.newSingleThreadExecutor(); + } + + public LiveData getUpdatesData() { + return m_lastUpdate; + } + + public LiveData getArticles() { + return m_articles; + } + + + public void update(int position, Article article) { + m_articles.getValue().set(position, article); + m_articles.postValue(m_articles.getValue()); + } + + public void update(ArticleList articles) { + m_articles.postValue(articles); + } + + public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { + Log.d(TAG, "startLoading append=" + append); + + m_resizeWidth = resizeWidth; + + if (!append) { + m_append = false; + m_lazyLoadEnabled = true; + m_feed = feed; + + loadInBackground(); + } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { + m_append = true; + m_feed = feed; + + loadInBackground(); + } else { + m_articles.postValue(m_articles.getValue()); + } + } + + private ArticleList loadInBackground() { + Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); + + ArticleList articlesWork = new ArticleList(m_articles.getValue()); + + m_loadingInProgress = true; + + final int skip = getSkip(m_append, articlesWork); + final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && + !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; + + HashMap params = new HashMap<>(); + + params.put("op", "getHeadlines"); + params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); + params.put("feed_id", String.valueOf(m_feed.id)); + params.put("show_excerpt", "true"); + params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); + params.put("show_content", "true"); + params.put("include_attachments", "true"); + params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); + params.put("limit", m_prefs.getString("headlines_request_size", "15")); + params.put("skip", String.valueOf(skip)); + params.put("include_nested", "true"); + params.put("has_sandbox", "true"); + params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); + + if (m_prefs.getBoolean("enable_image_downsampling", false)) { + if (m_prefs.getBoolean("always_downsample_images", false) || !org.fox.ttrss.Application.getInstance().isWifiConnected()) { + params.put("resize_width", String.valueOf(m_resizeWidth)); + } + } + + if (m_feed.is_cat) + params.put("is_cat", "true"); + + if (allowForceUpdate) { + params.put("force_update", "true"); + } + + if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + params.put("search", m_searchQuery); + params.put("search_mode", ""); + params.put("match_on", "both"); + } + + if (m_firstId > 0) + params.put("check_first_id", String.valueOf(m_firstId)); + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + params.put("include_header", "true"); + } + + Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + articlesWork.size()); + + m_executor.execute(() -> { + JsonElement result = ApiCommon.performRequest(getApplication(), params, this); + + Log.d(TAG, "got result=" + result); + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + final List
articlesJson; + final JsonObject header; + + if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { + header = content.get(0).getAsJsonObject(); + + m_firstIdChanged = header.get("first_id_changed") != null; + + try { + m_firstId = header.get("first_id").getAsInt(); + } catch (NumberFormatException e) { + m_firstId = 0; + } + + Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); + + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content.get(1), listType); + } else { + Type listType = new TypeToken>() {}.getType(); + articlesJson = new Gson().fromJson(content, listType); + } + + if (!m_append) + articlesWork.clear(); + + m_amountLoaded = articlesJson.size(); + + for (Article article : articlesJson) + if (!articlesWork.containsId(article.id)) { + article.collectMediaInfo(); + article.cleanupExcerpt(); + article.fixNullFields(); + articlesWork.add(article); + } + + if (m_firstIdChanged) { + Log.d(TAG, "first id changed, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { + Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + m_lazyLoadEnabled = false; + } + + m_offset += m_amountLoaded; + m_loadingInProgress = false; + + Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + articlesWork.size()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + m_mainHandler.post(() -> { + m_articles.setValue(articlesWork); + m_lastUpdate.setValue(System.currentTimeMillis()); + }); + }); + + m_loadingInProgress = false; + + return articlesWork; + } + + private int getSkip(boolean append, ArticleList articles) { + int skip = 0; + + if (append) { + // adaptive, all_articles, marked, published, unread + String viewMode = m_prefs.getString("view_mode", "adaptive"); + + int numUnread = Math.toIntExact(articles.getUnreadCount()); + int numAll = Math.toIntExact(articles.size()); + + if ("marked".equals(viewMode)) { + skip = numAll; + } else if ("published".equals(viewMode)) { + skip = numAll; + } else if ("unread".equals(viewMode)) { + skip = numUnread; + } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { + skip = numAll; + } else if ("adaptive".equals(viewMode)) { + skip = numUnread > 0 ? numUnread : numAll; + } else { + skip = numAll; + } + } + + return skip; + } + + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } + + @Override + public void setLastError(ApiCommon.ApiError lastError) { + m_lastError = lastError; + } + + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } + + public boolean getFirstIdChanged() { + return m_firstIdChanged; + } + + public boolean getAppend() { + return m_append; + } + + public void setSearchQuery(String searchQuery) { + m_searchQuery = searchQuery; + } + + public String getSearchQuery() { + return m_searchQuery; + } + + public int getOffset() { + return m_offset; + } + + public boolean lazyLoadEnabled() { + return m_lazyLoadEnabled; + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + ApiCommon.ApiError getLastError() { + return m_lastError; + } + + String getLastErrorMessage() { + return m_lastErrorMessage; + } + + public boolean isLoading() { + return m_loadingInProgress; + } +} 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 d2978858..cf43f4f0 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 @@ -82,10 +82,10 @@ public class ArticlePager extends androidx.fragment.app.Fragment { m_adapter = new PagerAdapter(this); m_adapter.submitList(Application.getArticles()); - ArticlesModel model = Application.getArticlesModel(); + ArticleModel model = Application.getArticlesModel(); // deal with further updates - model.getArticlesData().observe(getActivity(), articles -> { + model.getArticles().observe(getActivity(), articles -> { Log.d(TAG, "observed article list size=" + articles.size()); m_adapter.submitList(articles); }); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java deleted file mode 100644 index 66027966..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlesModel.java +++ /dev/null @@ -1,319 +0,0 @@ -package org.fox.ttrss; - -import android.app.Application; -import android.content.SharedPreferences; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.lifecycle.AndroidViewModel; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.preference.PreferenceManager; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; -import org.fox.ttrss.types.Feed; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class ArticlesModel extends AndroidViewModel implements ApiCommon.ApiCaller { - private final String TAG = this.getClass().getSimpleName(); - private final MutableLiveData m_articles = new MutableLiveData<>(new ArticleList()); - private SharedPreferences m_prefs; - private final int m_responseCode = 0; - protected String m_responseMessage; - private int m_apiStatusCode = 0; - - private String m_lastErrorMessage; - private ApiCommon.ApiError m_lastError; - private Feed m_feed; - private int m_firstId; - private String m_searchQuery = ""; - private boolean m_firstIdChanged; - private int m_offset; - private int m_amountLoaded; - private int m_resizeWidth; - private boolean m_append; - private boolean m_lazyLoadEnabled = true; - private boolean m_loadingInProgress; - private ExecutorService m_executor; - private Handler m_mainHandler = new Handler(Looper.getMainLooper()); - private MutableLiveData m_lastUpdate = new MutableLiveData<>(new Long(0)); - - public ArticlesModel(@NonNull Application application) { - super(application); - - m_prefs = PreferenceManager.getDefaultSharedPreferences(application); - - // do we need concurrency or not? - m_executor = Executors.newSingleThreadExecutor(); - } - - public LiveData getUpdatesData() { - return m_lastUpdate; - } - - public LiveData getArticlesData() { - return m_articles; - } - - public ArticleList getArticles() { - return m_articles.getValue(); - } - - public void update(int position, Article article) { - m_articles.getValue().set(position, article); - m_articles.postValue(m_articles.getValue()); - } - - public void update(ArticleList articles) { - m_articles.postValue(articles); - } - - public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { - Log.d(TAG, "startLoading append=" + append); - - m_resizeWidth = resizeWidth; - - if (!append) { - m_append = false; - m_lazyLoadEnabled = true; - m_feed = feed; - - loadInBackground(); - } else if (feed != m_feed || m_lazyLoadEnabled && !m_loadingInProgress) { - m_append = true; - m_feed = feed; - - loadInBackground(); - } else { - m_articles.postValue(m_articles.getValue()); - } - } - - private ArticleList loadInBackground() { - Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); - - ArticleList articlesWork = new ArticleList(m_articles.getValue()); - - m_loadingInProgress = true; - - final int skip = getSkip(m_append, articlesWork); - final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && - !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; - - HashMap params = new HashMap<>(); - - params.put("op", "getHeadlines"); - params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); - params.put("feed_id", String.valueOf(m_feed.id)); - params.put("show_excerpt", "true"); - params.put("excerpt_length", String.valueOf(CommonActivity.EXCERPT_MAX_LENGTH)); - params.put("show_content", "true"); - params.put("include_attachments", "true"); - params.put("view_mode", m_prefs.getString("view_mode", "adaptive")); - params.put("limit", m_prefs.getString("headlines_request_size", "15")); - params.put("skip", String.valueOf(skip)); - params.put("include_nested", "true"); - params.put("has_sandbox", "true"); - params.put("order_by", m_prefs.getString("headlines_sort_mode", "default")); - - if (m_prefs.getBoolean("enable_image_downsampling", false)) { - if (m_prefs.getBoolean("always_downsample_images", false) || !org.fox.ttrss.Application.getInstance().isWifiConnected()) { - params.put("resize_width", String.valueOf(m_resizeWidth)); - } - } - - if (m_feed.is_cat) - params.put("is_cat", "true"); - - if (allowForceUpdate) { - params.put("force_update", "true"); - } - - if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - params.put("search", m_searchQuery); - params.put("search_mode", ""); - params.put("match_on", "both"); - } - - if (m_firstId > 0) - params.put("check_first_id", String.valueOf(m_firstId)); - - if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { - params.put("include_header", "true"); - } - - Log.d(TAG, "firstId=" + m_firstId + " append=" + m_append + " skip=" + skip + " localSize=" + articlesWork.size()); - - m_executor.execute(() -> { - JsonElement result = ApiCommon.performRequest(getApplication(), params, this); - - Log.d(TAG, "got result=" + result); - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - final List
articlesJson; - final JsonObject header; - - if (org.fox.ttrss.Application.getInstance().getApiLevel() >= 12) { - header = content.get(0).getAsJsonObject(); - - m_firstIdChanged = header.get("first_id_changed") != null; - - try { - m_firstId = header.get("first_id").getAsInt(); - } catch (NumberFormatException e) { - m_firstId = 0; - } - - Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); - - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content.get(1), listType); - } else { - Type listType = new TypeToken>() {}.getType(); - articlesJson = new Gson().fromJson(content, listType); - } - - if (!m_append) - articlesWork.clear(); - - m_amountLoaded = articlesJson.size(); - - for (Article article : articlesJson) - if (!articlesWork.containsId(article.id)) { - article.collectMediaInfo(); - article.cleanupExcerpt(); - article.fixNullFields(); - articlesWork.add(article); - } - - if (m_firstIdChanged) { - Log.d(TAG, "first id changed, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); - m_lazyLoadEnabled = false; - } - - m_offset += m_amountLoaded; - m_loadingInProgress = false; - - Log.d(TAG, this + " loaded headlines=" + m_amountLoaded + " resultingLocalSize=" + articlesWork.size()); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - m_mainHandler.post(() -> { - m_articles.setValue(articlesWork); - m_lastUpdate.setValue(System.currentTimeMillis()); - }); - }); - - m_loadingInProgress = false; - - return articlesWork; - } - - private int getSkip(boolean append, ArticleList articles) { - int skip = 0; - - if (append) { - // adaptive, all_articles, marked, published, unread - String viewMode = m_prefs.getString("view_mode", "adaptive"); - - int numUnread = Math.toIntExact(articles.getUnreadCount()); - int numAll = Math.toIntExact(articles.size()); - - if ("marked".equals(viewMode)) { - skip = numAll; - } else if ("published".equals(viewMode)) { - skip = numAll; - } else if ("unread".equals(viewMode)) { - skip = numUnread; - } else if (m_searchQuery != null && !m_searchQuery.isEmpty()) { - skip = numAll; - } else if ("adaptive".equals(viewMode)) { - skip = numUnread > 0 ? numUnread : numAll; - } else { - skip = numAll; - } - } - - return skip; - } - - @Override - public void setStatusCode(int statusCode) { - m_apiStatusCode = statusCode; - } - - @Override - public void setLastError(ApiCommon.ApiError lastError) { - m_lastError = lastError; - } - - @Override - public void setLastErrorMessage(String message) { - m_lastErrorMessage = message; - } - - public boolean getFirstIdChanged() { - return m_firstIdChanged; - } - - public boolean getAppend() { - return m_append; - } - - public void setSearchQuery(String searchQuery) { - m_searchQuery = searchQuery; - } - - public String getSearchQuery() { - return m_searchQuery; - } - - public int getOffset() { - return m_offset; - } - - public boolean lazyLoadEnabled() { - return m_lazyLoadEnabled; - } - - public int getErrorMessage() { - return ApiCommon.getErrorMessage(m_lastError); - } - - ApiCommon.ApiError getLastError() { - return m_lastError; - } - - String getLastErrorMessage() { - return m_lastErrorMessage; - } - - public boolean isLoading() { - return m_loadingInProgress; - } -} 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 da1c160b..28ffafec 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 @@ -389,7 +389,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); - ArticlesModel model = Application.getArticlesModel(); + ArticleModel model = Application.getArticlesModel(); if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { @@ -438,7 +438,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); } - ArticlesModel model = Application.getArticlesModel(); + ArticleModel model = Application.getArticlesModel(); if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.lazyLoadEnabled() && lastVisibleItem >= Application.getArticles().size() - 5) { @@ -457,11 +457,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.setTitle(m_feed.title); } - ArticlesModel model = Application.getArticlesModel(); + ArticleModel model = Application.getArticlesModel(); // this gets notified on network update model.getUpdatesData().observe(getActivity(), lastUpdate -> { - ArticleList tmp = new ArticleList(model.getArticles()); + ArticleList tmp = new ArticleList(model.getArticles().getValue()); Log.d(TAG, "observed last update=" + lastUpdate + " article count=" + tmp.size()); @@ -500,10 +500,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { }); // loaded articles might get modified for all sorts of reasons - model.getArticlesData().observe(getActivity(), articles -> { + model.getArticles().observe(getActivity(), articles -> { Log.d(TAG, "observed article list size=" + articles.size()); - ArticleList tmp = new ArticleList(model.getArticles()); + ArticleList tmp = new ArticleList(articles); if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) tmp.add(new Article(Article.TYPE_AMR_FOOTER)); @@ -542,7 +542,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void refresh(final boolean append) { - ArticlesModel model = Application.getArticlesModel(); + ArticleModel model = Application.getArticlesModel(); if (!append) m_activeArticleId = -1; -- cgit v1.2.3-54-g00ecf From 87577e96118478f565696076de7cbdbd796f3117 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Wed, 14 May 2025 20:02:26 +0300 Subject: minor cleanup --- .../src/main/java/org/fox/ttrss/ArticleModel.java | 5 ++--- .../main/java/org/fox/ttrss/BaseFeedlistFragment.java | 2 +- .../src/main/java/org/fox/ttrss/CommonActivity.java | 2 -- .../main/java/org/fox/ttrss/FeedCategoriesFragment.java | 8 ++++---- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 2 +- .../src/main/java/org/fox/ttrss/HeadlinesFragment.java | 5 ----- .../src/main/java/org/fox/ttrss/OnlineActivity.java | 9 --------- .../main/java/org/fox/ttrss/share/CommonActivity.java | 14 -------------- .../src/main/java/org/fox/ttrss/types/Article.java | 6 +----- .../src/main/java/org/fox/ttrss/types/ArticleList.java | 16 ---------------- 10 files changed, 9 insertions(+), 60 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java index 2496b84c..80467f63 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java @@ -50,7 +50,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle private boolean m_loadingInProgress; private ExecutorService m_executor; private Handler m_mainHandler = new Handler(Looper.getMainLooper()); - private MutableLiveData m_lastUpdate = new MutableLiveData<>(new Long(0)); + private MutableLiveData m_lastUpdate = new MutableLiveData<>(Long.valueOf(0)); public ArticleModel(@NonNull Application application) { super(application); @@ -100,7 +100,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle } } - private ArticleList loadInBackground() { + private void loadInBackground() { Log.d(TAG, this + " loadInBackground append=" + m_append + " offset=" + m_offset); ArticleList articlesWork = new ArticleList(m_articles.getValue()); @@ -228,7 +228,6 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle m_loadingInProgress = false; - return articlesWork; } private int getSkip(boolean append, ArticleList articles) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java index 86a31465..def61209 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java @@ -17,7 +17,7 @@ import java.net.URL; public abstract class BaseFeedlistFragment extends androidx.fragment.app.Fragment { abstract public void refresh(); - public void initDrawerHeader(LayoutInflater inflater, View view, ListView list, final CommonActivity activity, final SharedPreferences prefs, boolean isRoot) { + public void initDrawerHeader(LayoutInflater inflater, View view, ListView list, final CommonActivity activity, final SharedPreferences prefs) { View layout = inflater.inflate(R.layout.drawer_header, list, false); list.addHeaderView(layout, null, false); 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 0c77cbce..97e356c4 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 @@ -64,7 +64,6 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc public final static String FRAG_ARTICLE = "article"; public final static String FRAG_FEEDS = "feeds"; public final static String FRAG_CATS = "cats"; - public final static String FRAG_DIALOG = "dialog"; public final static String THEME_DEFAULT = "THEME_FOLLOW_DEVICE"; @@ -72,7 +71,6 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc public final static String NOTIFICATION_CHANNEL_PRIORITY = "channel_priority"; public static final int EXCERPT_MAX_LENGTH = 256; - public static final int EXCERPT_MAX_QUERY_LENGTH = 2048; public static final int LABEL_BASE_INDEX = -1024; public static final int PENDING_INTENT_CHROME_SHARE = 1; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java index abdb320d..1d735490 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java @@ -62,9 +62,9 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt params.put("op", "getCategories"); params.put("sid", sessionId); params.put("enable_nested", "true"); - if (unreadOnly) { - params.put("unread_only", String.valueOf(unreadOnly)); - } + + if (unreadOnly) + params.put("unread_only", "true"); return new ApiLoader(getContext(), params); } @@ -288,7 +288,7 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt m_list = view.findViewById(R.id.feeds); m_adapter = new FeedCategoryListAdapter(getActivity(), R.layout.feeds_row, m_cats); - initDrawerHeader(inflater, view, m_list, m_activity, m_prefs, true); + initDrawerHeader(inflater, view, m_list, m_activity, m_prefs); m_list.setAdapter(m_adapter); m_list.setOnItemClickListener(this); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java index 7c6a2237..f618b5b1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java @@ -327,7 +327,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_list = view.findViewById(R.id.feeds); - initDrawerHeader(inflater, view, m_list, m_activity, m_prefs, !m_enableParentBtn); + initDrawerHeader(inflater, view, m_list, m_activity, m_prefs); if (m_enableParentBtn) { View layout = inflater.inflate(R.layout.feeds_goback, m_list, false); 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 28ffafec..d39ee038 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 @@ -103,7 +103,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { private Feed m_feed; private int m_activeArticleId; private String m_searchQuery = ""; - //private HeadlinesLoader m_loader; private SharedPreferences m_prefs; @@ -1482,10 +1481,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } } - public int getActiveArticleId() { - return m_activeArticleId; - } - public String getSearchQuery() { return m_searchQuery; } 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 4faff8af..26dab50d 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 @@ -1394,15 +1394,6 @@ article.score = Integer.parseInt(edit.getText().toString()); return m_lastImageHitTestUrl; } - public boolean isWifiConnected() { - NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - - if (wifi != null) - return wifi.isConnected(); - - return false; - } - public int getResizeWidth() { Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java index 63458532..9c9187d9 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java @@ -10,7 +10,6 @@ public class CommonActivity extends Activity { private final String TAG = this.getClass().getSimpleName(); private boolean m_smallScreenMode = true; - private boolean m_compatMode = false; protected void setSmallScreen(boolean smallScreen) { Log.d(TAG, "m_smallScreenMode=" + smallScreen); @@ -27,23 +26,10 @@ public class CommonActivity extends Activity { toast.show(); } - @Override - public void onCreate(Bundle savedInstanceState) { - m_compatMode = android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB; - - Log.d(TAG, "m_compatMode=" + m_compatMode); - - super.onCreate(savedInstanceState); - } - public boolean isSmallScreen() { return m_smallScreenMode; } - public boolean isCompatMode() { - return m_compatMode; - } - public boolean isPortrait() { Display display = getWindowManager().getDefaultDisplay(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index c0f63937..f0795dd8 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -296,11 +296,7 @@ public class Article implements Parcelable { } public boolean equalsById(Article article) { - if (article != null && id == article.id) { - return true; - } else { - return false; - } + return article != null && id == article.id; } @SuppressWarnings("rawtypes") diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java index 74df2c33..6724acb0 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java @@ -35,22 +35,6 @@ public class ArticleList extends CopyOnWriteArrayList
{ return this.stream().filter(a -> { return a.unread; }).count(); } - public long getSizeWithoutFooters() { - return this.stream().filter(a -> { return a.id > 0; }).count(); - } - - /** strips all trailing items with negative IDs (Article.TYPE_LOADMORE, Article.TYPE_AMR_FOOTER) */ - public void stripFooters() { - for (ListIterator
iterator = this.listIterator(size()); iterator.hasPrevious();) { - final Article article = iterator.previous(); - - if (article.id < 0) - this.remove(article); - else - break; - } - } - public int getPositionById(int id) { for (int i = 0; i < size(); i++) { if (get(i).id == id) { -- cgit v1.2.3-54-g00ecf From 0f57f56b841a223848454fd4476390c1957c1a1a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 08:01:57 +0300 Subject: ignore first (zero) update --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 d39ee038..5eebd65f 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 @@ -460,42 +460,43 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // this gets notified on network update model.getUpdatesData().observe(getActivity(), lastUpdate -> { - ArticleList tmp = new ArticleList(model.getArticles().getValue()); + if (lastUpdate > 0) { + ArticleList tmp = new ArticleList(model.getArticles().getValue()); - Log.d(TAG, "observed last update=" + lastUpdate + " article count=" + tmp.size()); + Log.d(TAG, "observed last update=" + lastUpdate + " article count=" + tmp.size()); - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - final boolean appended = model.getAppend(); + final boolean appended = model.getAppend(); - m_adapter.submitList(tmp, () -> { - if (!appended) - m_list.scrollToPosition(0); + m_adapter.submitList(tmp, () -> { + if (!appended) + m_list.scrollToPosition(0); - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(false); + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(false); - m_isLazyLoading = false; + m_isLazyLoading = false; - m_listener.onHeadlinesLoaded(appended); - m_listener.onArticleListSelectionChange(); - }); + m_listener.onHeadlinesLoaded(appended); + m_listener.onArticleListSelectionChange(); + }); - if (model.getFirstIdChanged()) - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); + if (model.getFirstIdChanged()) + Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) + .setAction(R.string.reload, v -> refresh(false)).show(); - if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(); - } else if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { - if (model.getLastErrorMessage() != null) { - m_activity.toast(m_activity.getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); - } else { - m_activity.toast(model.getErrorMessage()); + if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(); + } else if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (model.getLastErrorMessage() != null) { + m_activity.toast(m_activity.getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); + } else { + m_activity.toast(model.getErrorMessage()); + } } } - }); // loaded articles might get modified for all sorts of reasons -- cgit v1.2.3-54-g00ecf From 6e1ea990c69f72ac0b3e49f46ce620aa8e873422 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 08:58:39 +0300 Subject: add rss_box icon --- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 27 +++++++--------------- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 5 +--- .../main/java/org/fox/ttrss/OnlineActivity.java | 2 +- org.fox.ttrss/src/main/res/drawable/rss.xml | 1 + org.fox.ttrss/src/main/res/drawable/rss_box.xml | 1 + org.fox.ttrss/src/main/res/layout/feeds_row.xml | 2 +- .../src/main/res/layout/feeds_row_selected.xml | 2 +- .../src/main/res/layout/feeds_row_toggle.xml | 2 +- .../src/main/res/layout/headlines_row.xml | 2 +- .../src/main/res/layout/headlines_row_unread.xml | 4 ++-- org.fox.ttrss/src/main/res/values-night/themes.xml | 1 - org.fox.ttrss/src/main/res/values/attrs.xml | 1 - org.fox.ttrss/src/main/res/values/themes.xml | 1 - 13 files changed, 18 insertions(+), 33 deletions(-) create mode 100644 org.fox.ttrss/src/main/res/drawable/rss.xml create mode 100644 org.fox.ttrss/src/main/res/drawable/rss_box.xml (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java index f618b5b1..c198343d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java @@ -462,34 +462,23 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi ImageView icon = v.findViewById(R.id.icon); if (icon != null) { - TypedValue tv = new TypedValue(); - if (feed.id == 0 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_archive, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_archive_24); } else if (feed.id == -1 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_star, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_star_24); } else if (feed.id == -2 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_rss_box, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.rss); } else if (feed.id == -3 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_fresh, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_local_fire_department_24); } else if (feed.id == -4 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_inbox, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_inbox_24); } else if (feed.id == -6 && !feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_restore, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_restore_24); } else if (feed.is_cat) { - m_activity.getTheme().resolveAttribute(R.attr.ic_folder_outline, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_folder_open_24); } else { - m_activity.getTheme().resolveAttribute(R.attr.ic_rss_box, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.rss); } - } TextView tt = v.findViewById(R.id.title); 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 5eebd65f..1e22f711 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 @@ -896,10 +896,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } if (holder.publishedView != null) { - TypedValue tv = new TypedValue(); - m_activity.getTheme().resolveAttribute(R.attr.ic_rss_box, tv, true); - - holder.publishedView.setIconResource(tv.resourceId); + holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); if (article.published) holder.publishedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); 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 26dab50d..06503dc5 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 @@ -1184,7 +1184,7 @@ article.score = Integer.parseInt(edit.getText().toString()); m_menu.findItem(R.id.toggle_marked).setIcon(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); - m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.baseline_check_box_24 : + m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.rss_box : R.drawable.baseline_rss_feed_24); } } diff --git a/org.fox.ttrss/src/main/res/drawable/rss.xml b/org.fox.ttrss/src/main/res/drawable/rss.xml new file mode 100644 index 00000000..f97e14a2 --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/rss.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/drawable/rss_box.xml b/org.fox.ttrss/src/main/res/drawable/rss_box.xml new file mode 100644 index 00000000..45b40cea --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/rss_box.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/feeds_row.xml b/org.fox.ttrss/src/main/res/layout/feeds_row.xml index 32e20c26..0f9c4e54 100755 --- a/org.fox.ttrss/src/main/res/layout/feeds_row.xml +++ b/org.fox.ttrss/src/main/res/layout/feeds_row.xml @@ -23,7 +23,7 @@ android:layout_weight="0" android:scaleType="fitXY" app:tint="?colorOnPrimaryContainer" - android:src="?ic_rss_box" /> + android:src="@drawable/rss" /> + android:src="@drawable/rss" /> + android:src="@drawable/rss_box" /> + app:icon="@drawable/rss" /> + app:icon="@drawable/rss" /> + app:icon="@drawable/baseline_more_vert_24" /> diff --git a/org.fox.ttrss/src/main/res/values-night/themes.xml b/org.fox.ttrss/src/main/res/values-night/themes.xml index 7d54a029..5f32a7b3 100644 --- a/org.fox.ttrss/src/main/res/values-night/themes.xml +++ b/org.fox.ttrss/src/main/res/values-night/themes.xml @@ -3,7 +3,6 @@ true @style/AppPreferenceThemeOverlay - @drawable/baseline_rss_feed_24 @drawable/baseline_check_box_24 @drawable/baseline_star_24 @drawable/baseline_star_outline_24 diff --git a/org.fox.ttrss/src/main/res/values/attrs.xml b/org.fox.ttrss/src/main/res/values/attrs.xml index daf2323c..e33d1c8a 100755 --- a/org.fox.ttrss/src/main/res/values/attrs.xml +++ b/org.fox.ttrss/src/main/res/values/attrs.xml @@ -1,6 +1,5 @@ - diff --git a/org.fox.ttrss/src/main/res/values/themes.xml b/org.fox.ttrss/src/main/res/values/themes.xml index 68a57811..9969617c 100644 --- a/org.fox.ttrss/src/main/res/values/themes.xml +++ b/org.fox.ttrss/src/main/res/values/themes.xml @@ -4,7 +4,6 @@ true @style/AppPreferenceThemeOverlay - @drawable/baseline_rss_feed_24 @drawable/baseline_check_box_24 @drawable/baseline_star_24 @drawable/baseline_star_outline_24 -- cgit v1.2.3-54-g00ecf From 2122a9b494feaddd953b276b06d8b4d0515d0f27 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 09:05:34 +0300 Subject: drop no longer useful theme attributes in favor of drawable ids --- .../java/org/fox/ttrss/BaseFeedlistFragment.java | 4 +--- .../java/org/fox/ttrss/FeedCategoriesFragment.java | 5 +---- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 17 +++++---------- .../src/main/res/layout/activity_gallery.xml | 2 +- org.fox.ttrss/src/main/res/layout/feeds_goback.xml | 2 +- .../src/main/res/layout/headlines_row.xml | 8 +++---- .../src/main/res/layout/headlines_row_compact.xml | 2 +- .../res/layout/headlines_row_compact_active.xml | 2 +- .../layout/headlines_row_compact_active_unread.xml | 2 +- .../res/layout/headlines_row_compact_unread.xml | 2 +- .../src/main/res/layout/headlines_row_unread.xml | 6 +++--- org.fox.ttrss/src/main/res/values-night/themes.xml | 22 ------------------- org.fox.ttrss/src/main/res/values/attrs.xml | 25 ---------------------- org.fox.ttrss/src/main/res/values/themes.xml | 22 ------------------- 14 files changed, 20 insertions(+), 101 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java index def61209..b37f7386 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java @@ -62,9 +62,7 @@ public abstract class BaseFeedlistFragment extends androidx.fragment.app.Fragmen text.setText(R.string.unread_only); ImageView icon = rowToggle.findViewById(R.id.icon); - TypedValue tv = new TypedValue(); - getActivity().getTheme().resolveAttribute(R.attr.ic_filter_variant, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_filter_alt_24); final SwitchCompat rowSwitch = rowToggle.findViewById(R.id.row_switch); rowSwitch.setChecked(activity.getUnreadOnly()); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java index 1d735490..34127013 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java @@ -376,10 +376,7 @@ public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnIt ImageView icon = v.findViewById(R.id.icon); if (icon != null) { - TypedValue tv = new TypedValue(); - - m_activity.getTheme().resolveAttribute(R.attr.ic_folder_outline, tv, true); - icon.setImageResource(tv.resourceId); + icon.setImageResource(R.drawable.baseline_folder_open_24); } 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 1e22f711..b0a3d2d3 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 @@ -830,11 +830,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.getTheme().resolveAttribute(R.attr.colorPrimary, tvPrimary, true); if (holder.markedView != null) { - TypedValue tv = new TypedValue(); - m_activity.getTheme().resolveAttribute(article.marked ? R.attr.ic_star : R.attr.ic_star_outline, tv, true); - - holder.markedView.setIconResource(tv.resourceId); - + holder.markedView.setIconResource(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); if (article.marked) holder.markedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); @@ -851,17 +847,14 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } if (holder.scoreView != null) { - TypedValue tv = new TypedValue(); - int scoreAttr = R.attr.ic_action_trending_flat; + int scoreDrawable = R.drawable.baseline_trending_flat_24; if (article.score > 0) - scoreAttr = R.attr.ic_action_trending_up; + scoreDrawable = R.drawable.baseline_trending_up_24; else if (article.score < 0) - scoreAttr = R.attr.ic_action_trending_down; - - m_activity.getTheme().resolveAttribute(scoreAttr, tv, true); + scoreDrawable = R.drawable.baseline_trending_down_24; - holder.scoreView.setIconResource(tv.resourceId); + holder.scoreView.setIconResource(scoreDrawable); if (article.score > Article.SCORE_HIGH) holder.scoreView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); diff --git a/org.fox.ttrss/src/main/res/layout/activity_gallery.xml b/org.fox.ttrss/src/main/res/layout/activity_gallery.xml index f44bb74b..b0f67761 100644 --- a/org.fox.ttrss/src/main/res/layout/activity_gallery.xml +++ b/org.fox.ttrss/src/main/res/layout/activity_gallery.xml @@ -17,7 +17,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" - app:icon="?ic_dots_vertical" + app:icon="@drawable/baseline_more_vert_24" android:layout_alignParentRight="true" android:layout_marginTop="48dp" app:iconTint="?colorTertiary" /> diff --git a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml b/org.fox.ttrss/src/main/res/layout/feeds_goback.xml index f4c580a3..75ba8fe1 100755 --- a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml +++ b/org.fox.ttrss/src/main/res/layout/feeds_goback.xml @@ -20,7 +20,7 @@ android:layout_height="21dp" android:layout_weight="0" android:scaleType="fitXY" - android:src="?ic_go_back" + android:src="@drawable/baseline_arrow_back_24" app:tint="?colorTertiary" /> + app:icon="@drawable/baseline_trending_flat_24" /> + app:icon="@drawable/baseline_attachment_24" /> + app:icon="@drawable/baseline_star_outline_24" /> + app:icon="@drawable/baseline_more_vert_24" /> diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml index 352aad91..7090668a 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml @@ -103,6 +103,6 @@ android:layout_gravity="end" android:layout_height="24dp" android:layout_weight="0.5" - app:icon="?ic_star_outline" /> + app:icon="@drawable/baseline_star_outline_24" /> \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active.xml index f6483350..72ae99b8 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active.xml @@ -104,6 +104,6 @@ android:layout_gravity="end" android:layout_height="24dp" android:layout_weight="0.5" - app:icon="?ic_star_outline" /> + app:icon="@drawable/baseline_star_outline_24" /> \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active_unread.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active_unread.xml index 06813e04..b2b3a21e 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active_unread.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_active_unread.xml @@ -105,6 +105,6 @@ android:layout_gravity="end" android:layout_height="24dp" android:layout_weight="0.5" - app:icon="?ic_star_outline" /> + app:icon="@drawable/baseline_star_outline_24" /> \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml index 6aa1bdcd..2fbbe062 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml @@ -105,6 +105,6 @@ android:layout_gravity="end" android:layout_height="24dp" android:layout_weight="0.5" - app:icon="?ic_star_outline" /> + app:icon="@drawable/baseline_star_outline_24" /> \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_unread.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_unread.xml index 74f73e14..3365f476 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_unread.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_unread.xml @@ -212,7 +212,7 @@ android:layout_weight="0" android:paddingLeft="4dp" android:paddingRight="4dp" - app:icon="?ic_action_trending_flat" /> + app:icon="@drawable/baseline_trending_flat_24" /> + app:icon="@drawable/baseline_attachment_24" /> + app:icon="@drawable/baseline_star_outline_24" /> true @style/AppPreferenceThemeOverlay - @drawable/baseline_check_box_24 - @drawable/baseline_star_24 - @drawable/baseline_star_outline_24 - @drawable/baseline_share_24 - @drawable/baseline_inbox_24 - @drawable/baseline_arrow_back_24 - @drawable/baseline_settings_24 - @drawable/baseline_filter_alt_24 - @drawable/baseline_cloud_download_24 - @drawable/baseline_cloud_upload_24 - @drawable/baseline_archive_24 - @drawable/baseline_local_fire_department_24 - @drawable/baseline_restore_24 - @drawable/baseline_folder_open_24 - @drawable/baseline_more_vert_24 - @drawable/outline_more_24 - @drawable/baseline_attachment_24 - @drawable/baseline_attach_file_24 - @drawable/baseline_trending_up_24 - @drawable/baseline_trending_flat_24 - @drawable/baseline_trending_down_24 - @color/md_theme_primary @color/md_theme_onPrimary @color/md_theme_primaryContainer diff --git a/org.fox.ttrss/src/main/res/values/attrs.xml b/org.fox.ttrss/src/main/res/values/attrs.xml index e33d1c8a..55344e51 100755 --- a/org.fox.ttrss/src/main/res/values/attrs.xml +++ b/org.fox.ttrss/src/main/res/values/attrs.xml @@ -1,28 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/values/themes.xml b/org.fox.ttrss/src/main/res/values/themes.xml index 9969617c..15be8c17 100644 --- a/org.fox.ttrss/src/main/res/values/themes.xml +++ b/org.fox.ttrss/src/main/res/values/themes.xml @@ -4,28 +4,6 @@ true @style/AppPreferenceThemeOverlay - @drawable/baseline_check_box_24 - @drawable/baseline_star_24 - @drawable/baseline_star_outline_24 - @drawable/baseline_share_24 - @drawable/baseline_inbox_24 - @drawable/baseline_arrow_back_24 - @drawable/baseline_settings_24 - @drawable/baseline_filter_alt_24 - @drawable/baseline_cloud_download_24 - @drawable/baseline_cloud_upload_24 - @drawable/baseline_archive_24 - @drawable/baseline_local_fire_department_24 - @drawable/baseline_restore_24 - @drawable/baseline_folder_open_24 - @drawable/baseline_more_vert_24 - @drawable/outline_more_24 - @drawable/baseline_attachment_24 - @drawable/baseline_attach_file_24 - @drawable/baseline_trending_up_24 - @drawable/baseline_trending_flat_24 - @drawable/baseline_trending_down_24 - @color/md_theme_primary @color/md_theme_onPrimary @color/md_theme_primaryContainer -- cgit v1.2.3-54-g00ecf From a30eded72dd8d8a5f5157343f250e9049f5b8cd9 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 09:22:31 +0300 Subject: use color tinting for starred actionbar icons --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 7 +++++- .../main/java/org/fox/ttrss/OnlineActivity.java | 27 ++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 b0a3d2d3..c6a2e757 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 @@ -14,6 +14,7 @@ import android.media.MediaPlayer; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.text.Html; @@ -889,7 +890,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } if (holder.publishedView != null) { - holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); + + // otherwise we just use tinting in actionbar + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); + } if (article.published) holder.publishedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); 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 06503dc5..ffca776b 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 @@ -5,12 +5,15 @@ import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.ColorStateList; import android.graphics.Point; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.util.TypedValue; import android.view.Display; import android.view.KeyEvent; import android.view.Menu; @@ -23,6 +26,7 @@ import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -1181,11 +1185,30 @@ article.score = Integer.parseInt(edit.getText().toString()); Article article = Application.getArticles().getById(ap.getSelectedArticleId()); if (article != null) { + m_menu.findItem(R.id.toggle_marked).setIcon(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); - m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.rss_box : - R.drawable.baseline_rss_feed_24); + // TODO we probably shouldn't do this all the time + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + TypedValue tvTertiary = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorTertiary, tvTertiary, true); + + ColorStateList colorStateTertiary = ColorStateList.valueOf(ContextCompat.getColor(this, tvTertiary.resourceId)); + + TypedValue tvNormal = new TypedValue(); + getTheme().resolveAttribute(R.attr.colorControlNormal, tvNormal, true); + + ColorStateList colorStateNormal = ColorStateList.valueOf(ContextCompat.getColor(this, tvNormal.resourceId)); + + m_menu.findItem(R.id.toggle_published).setIconTintList(article.published ? colorStateTertiary : colorStateNormal); + m_menu.findItem(R.id.toggle_marked).setIconTintList(article.marked ? colorStateTertiary : colorStateNormal); + + } else { + m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.rss_box : + R.drawable.baseline_rss_feed_24); + } + } } -- cgit v1.2.3-54-g00ecf From 08e5f41fea8dcb407e6fa38399e8db89780e8feb Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 09:30:19 +0300 Subject: switch to contextcompat to resolve theme colors --- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'org.fox.ttrss/src/main/java') 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 c6a2e757..fbcf0b4c 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 @@ -46,6 +46,7 @@ import android.widget.TextView; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; +import androidx.core.content.ContextCompat; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DefaultItemAnimator; @@ -827,16 +828,16 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { TypedValue tvTertiary = new TypedValue(); m_activity.getTheme().resolveAttribute(R.attr.colorTertiary, tvTertiary, true); + ColorStateList colorTertiary = ColorStateList.valueOf(ContextCompat.getColor(m_activity, tvTertiary.resourceId)); + TypedValue tvPrimary = new TypedValue(); m_activity.getTheme().resolveAttribute(R.attr.colorPrimary, tvPrimary, true); + ColorStateList colorPrimary = ColorStateList.valueOf(ContextCompat.getColor(m_activity, tvPrimary.resourceId)); + if (holder.markedView != null) { holder.markedView.setIconResource(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); - - if (article.marked) - holder.markedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); - else - holder.markedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); + holder.markedView.setIconTint(article.marked ? colorTertiary : colorPrimary); holder.markedView.setOnClickListener(v -> { Article selectedArticle = new Article(getItem(position)); @@ -858,9 +859,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.scoreView.setIconResource(scoreDrawable); if (article.score > Article.SCORE_HIGH) - holder.scoreView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); + holder.scoreView.setIconTint(colorTertiary); else - holder.scoreView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); + holder.scoreView.setIconTint(colorPrimary); if (m_activity.getApiLevel() >= 16) { holder.scoreView.setOnClickListener(v -> { @@ -896,10 +897,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); } - if (article.published) - holder.publishedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); - else - holder.publishedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); + holder.publishedView.setIconTint(article.published ? colorTertiary : colorPrimary); holder.publishedView.setOnClickListener(v -> { Article selectedArticle = new Article(getItem(position)); -- cgit v1.2.3-54-g00ecf From 91e1fdcbe23cd65610d0cef32023ac7bb6297232 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 10:49:43 +0300 Subject: swap unread/read bottombar icons --- org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'org.fox.ttrss/src/main/java') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 3995014b..bca2aa40 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -197,8 +197,8 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_flat_24); } - menu.findItem(R.id.toggle_unread).setIcon(selectedArticle.unread ? R.drawable.baseline_drafts_24 : - R.drawable.baseline_email_24); + menu.findItem(R.id.toggle_unread).setIcon(selectedArticle.unread ? R.drawable.baseline_email_24 : + R.drawable.baseline_drafts_24); menu.findItem(R.id.toggle_attachments).setVisible(selectedArticle.attachments != null && !selectedArticle.attachments.isEmpty()); } -- cgit v1.2.3-54-g00ecf