From 06b8a9d17b4451b16415cd22e583a2fd96099e23 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 16:08:30 +0300 Subject: stop initializing note to null twice, stop invoking notifyDataSetChanged() when selecting article in detail mode --- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 6 ------ org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java | 1 - 2 files changed, 7 deletions(-) (limited to 'org.fox.ttrss/src') 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 8d15f5ec..9757f090 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 @@ -993,12 +993,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { // this is needed if our flavor image goes behind base listview element holder.headlineHeader.setOnClickListener(v -> { m_listener.onArticleSelected(article); - - // only set active article when it makes sense (in DetailActivity) - if (getActivity() instanceof DetailActivity) { - m_activeArticleId = article.id; - m_adapter.notifyDataSetChanged(); - } }); holder.headlineHeader.setOnLongClickListener(v -> { 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 55a2bf42..43a46d92 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 @@ -319,7 +319,6 @@ public class Article implements Parcelable { 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 = ""; if (comments_link == null) comments_link = ""; -- cgit v1.2.3-54-g00ecf From 8e61244404cf5027b7a4f9c7dd0e2f7419dba8ce Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 17:40:03 +0300 Subject: wip recycler feeds --- .../java/org/fox/ttrss/BaseFeedlistFragment.java | 5 +- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 385 ++++++++++++--------- .../src/main/java/org/fox/ttrss/FeedsLoader.java | 19 - .../main/java/org/fox/ttrss/MasterActivity.java | 13 +- .../src/main/java/org/fox/ttrss/types/Feed.java | 13 +- .../src/main/res/layout/drawer_divider.xml | 15 - .../src/main/res/layout/drawer_header.xml | 70 ---- org.fox.ttrss/src/main/res/layout/feeds_goback.xml | 38 -- .../src/main/res/layout/feeds_row_divider.xml | 15 + .../src/main/res/layout/feeds_row_goback.xml | 38 ++ .../src/main/res/layout/feeds_row_header.xml | 70 ++++ .../main/res/layout/fragment_feeds_recycler.xml | 18 + 12 files changed, 390 insertions(+), 309 deletions(-) delete mode 100644 org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java delete mode 100644 org.fox.ttrss/src/main/res/layout/drawer_divider.xml delete mode 100755 org.fox.ttrss/src/main/res/layout/drawer_header.xml delete mode 100755 org.fox.ttrss/src/main/res/layout/feeds_goback.xml create mode 100644 org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml create mode 100755 org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml create mode 100755 org.fox.ttrss/src/main/res/layout/feeds_row_header.xml create mode 100755 org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml (limited to 'org.fox.ttrss/src') 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 b37f7386..4f48a19e 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 @@ -2,7 +2,6 @@ package org.fox.ttrss; import android.content.Intent; import android.content.SharedPreferences; -import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; @@ -19,7 +18,7 @@ public abstract class BaseFeedlistFragment extends androidx.fragment.app.Fragmen 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); + View layout = inflater.inflate(R.layout.feeds_row_header, list, false); list.addHeaderView(layout, null, false); TextView login = view.findViewById(R.id.drawer_header_login); @@ -49,7 +48,7 @@ public abstract class BaseFeedlistFragment extends androidx.fragment.app.Fragmen /* deal with ~material~ footers */ // divider - final View footer = inflater.inflate(R.layout.drawer_divider, list, false); + final View footer = inflater.inflate(R.layout.feeds_row_divider, list, false); footer.setOnClickListener(v -> { // }); 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 c198343d..edc75c9d 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 @@ -2,34 +2,33 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.Dialog; -import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; -import android.util.TypedValue; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; import android.widget.ImageView; -import android.widget.ListView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; 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.LinearLayoutManager; +import androidx.recyclerview.widget.ListAdapter; +import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -37,7 +36,6 @@ import com.google.gson.reflect.TypeToken; import org.fox.ttrss.types.Feed; import org.fox.ttrss.types.FeedCategory; -import org.fox.ttrss.types.FeedList; import java.lang.reflect.Type; import java.util.ArrayList; @@ -45,43 +43,41 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; -public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener, +public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener, LoaderManager.LoaderCallbacks { private final String TAG = this.getClass().getSimpleName(); private SharedPreferences m_prefs; - private FeedListAdapter m_adapter; - private final FeedList m_feeds = new FeedList(); private MasterActivity m_activity; - Feed m_selectedFeed; - FeedCategory m_activeCategory; + private int m_catId; + private int m_selectedFeedId; private SwipeRefreshLayout m_swipeLayout; - boolean m_enableParentBtn = false; - private ListView m_list; + private boolean m_enableParentBtn = false; + private FeedsAdapter m_adapter; + private RecyclerView m_list; + private RecyclerView.LayoutManager m_layoutManager; - public void initialize(FeedCategory cat, boolean enableParentBtn) { - m_activeCategory = cat; - m_enableParentBtn = enableParentBtn; + public void initialize(int catId) { + m_catId = catId; } @Override public Loader onCreateLoader(int id, Bundle args) { if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - final int catId = (m_activeCategory != null) ? m_activeCategory.id : -4; - final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = m_activity.getUnreadOnly() && (m_activeCategory == null || m_activeCategory.id != -1); + final boolean unreadOnly = false; + // final boolean unreadOnly = m_activity.getUnreadOnly() && (m_activeCategory == null || m_activeCategory.id != -1); HashMap params = new HashMap<>(); params.put("op", "getFeeds"); params.put("sid", sessionId); params.put("include_nested", "true"); - params.put("cat_id", String.valueOf(catId)); + params.put("cat_id", String.valueOf(m_catId)); if (unreadOnly) params.put("unread_only", "true"); - return new FeedsLoader(getActivity().getApplicationContext(), params, catId); + return new ApiLoader(getContext(), params); } @Override @@ -94,16 +90,14 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi if (content != null) { Type listType = new TypeToken>() {}.getType(); - final List feeds = new Gson().fromJson(content, listType); - - m_feeds.clear(); + final List feedsJson = new Gson().fromJson(content, listType); - int catUnread = 0; + List feeds = new ArrayList<>(); - int catId = ((FeedsLoader) loader).getCatId(); + // int catUnread = 0; - for (Feed f : feeds) - if (f.id > -10 || catId != -4) { // skip labels for flat feedlist for now + /* for (Feed f : feeds) + if (f.id > -10 || m_catId != -4) { // skip labels for flat feedlist for now if (m_activeCategory != null || f.id >= 0) { m_feeds.add(f); catUnread += f.unread; @@ -132,7 +126,19 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_feeds.add(0, feed); } - m_adapter.notifyDataSetChanged(); + m_adapter.notifyDataSetChanged(); */ + + // m_adapter.sortFeeds(feedsJson); + + feeds.add(new Feed(Feed.TYPE_HEADER)); + feeds.add(new Feed(Feed.TYPE_GOBACK)); + + feeds.addAll(feedsJson); + + feeds.add(new Feed(Feed.TYPE_DIVIDER)); + feeds.add(new Feed(Feed.TYPE_TOGGLE_UNREAD)); + + m_adapter.submitList(feeds); return; } @@ -142,16 +148,17 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi } } - ApiLoader al = (ApiLoader) loader; - - if (al.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { + ApiLoader apiLoader = (ApiLoader) loader; - if (al.getLastErrorMessage() != null) { - m_activity.toast(getString(al.getErrorMessage()) + "\n" + al.getLastErrorMessage()); + if (apiLoader.getLastError() != null && apiLoader.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (apiLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(true); } else { - m_activity.toast(al.getErrorMessage()); + if (apiLoader.getLastErrorMessage() != null) { + m_activity.toast(getString(apiLoader.getErrorMessage()) + "\n" + apiLoader.getLastErrorMessage()); + } else { + m_activity.toast(apiLoader.getErrorMessage()); + } } } } @@ -169,7 +176,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi else return a.title.toUpperCase().compareTo(b.title.toUpperCase()); } - + } @@ -222,7 +229,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi @Override public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item + /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item .getMenuInfo(); int itemId = item.getItemId(); if (itemId == R.id.browse_headlines) { @@ -261,7 +268,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_activity.catchupDialog(feed); } return true; - } + } */ + Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); return super.onContextItemSelected(item); } @@ -270,7 +278,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - getActivity().getMenuInflater().inflate(R.menu.context_feed, menu); + /* getActivity().getMenuInflater().inflate(R.menu.context_feed, menu); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; @@ -284,7 +292,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi if (feed.id <= 0) { menu.findItem(R.id.unsubscribe_feed).setVisible(false); - } + } */ super.onCreateContextMenu(menu, v, menuInfo); @@ -295,13 +303,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi super.onCreate(savedInstanceState); if (savedInstanceState != null) { - ArrayList list = savedInstanceState.getParcelableArrayList("m_feeds"); - - m_feeds.clear(); - m_feeds.addAll(list); - - m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed"); - m_activeCategory = savedInstanceState.getParcelable("m_activeCategory"); + m_catId = savedInstanceState.getInt("m_catId"); + m_selectedFeedId = savedInstanceState.getInt("m_selectedId"); m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn"); } } @@ -310,26 +313,31 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); - out.putParcelableArrayList("m_feeds", m_feeds); - out.putParcelable("m_selectedFeed", m_selectedFeed); - out.putParcelable("m_activeCategory", m_activeCategory); + out.putInt("m_catId", m_catId); + out.putInt("m_selectedId", m_selectedFeedId); out.putBoolean("m_enableParentBtn", m_enableParentBtn); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_feeds, container, false); + View view = inflater.inflate(R.layout.fragment_feeds_recycler, container, false); m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); - + m_swipeLayout.setOnRefreshListener(this::refresh); m_list = view.findViewById(R.id.feeds); + registerForContextMenu(m_list); - initDrawerHeader(inflater, view, m_list, m_activity, m_prefs); + m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext()); + m_list.setLayoutManager(m_layoutManager); + m_list.setItemAnimator(new DefaultItemAnimator()); - if (m_enableParentBtn) { + m_adapter = new FeedsAdapter(); + m_list.setAdapter(m_adapter); + + /* if (m_enableParentBtn) { View layout = inflater.inflate(R.layout.feeds_goback, m_list, false); layout.setOnClickListener(view1 -> m_activity.getSupportFragmentManager().popBackStack()); @@ -341,7 +349,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_list.setAdapter(m_adapter); m_list.setOnItemClickListener(this); - registerForContextMenu(m_list); + registerForContextMenu(m_list); */ return view; } @@ -359,7 +367,6 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi m_prefs.registerOnSharedPreferenceChangeListener(this); m_activity = (MasterActivity)activity; - } @Override @@ -367,11 +374,11 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi super.onResume(); LoaderManager.getInstance(this).initLoader(Application.LOADER_FEEDS, null, this).forceLoad(); - + m_activity.invalidateOptionsMenu(); } - @Override + /* @Override public void onItemClick(AdapterView av, View view, int position, long id) { ListView list = (ListView)av; @@ -395,11 +402,9 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi //m_selectedFeed = feed; //m_adapter.notifyDataSetChanged(); } - } + } */ public void refresh() { - if (!isAdded()) return; - if (m_swipeLayout != null) { m_swipeLayout.setRefreshing(true); } @@ -407,128 +412,189 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi LoaderManager.getInstance(this).restartLoader(Application.LOADER_FEEDS, null, this).forceLoad(); } - private class FeedListAdapter extends ArrayAdapter { - private final ArrayList items; - - public static final int VIEW_NORMAL = 0; - public static final int VIEW_SELECTED = 1; - - public static final int VIEW_COUNT = VIEW_SELECTED+1; - - public FeedListAdapter(Context context, int textViewResourceId, ArrayList items) { - super(context, textViewResourceId, items); - this.items = items; + private class FeedViewHolder extends RecyclerView.ViewHolder { + + private View view; + private ImageView icon; + private TextView title; + private TextView unreadCounter; + private TextView rowSwitch; + private View settingsBtn; + private TextView login; + private TextView server; + + public FeedViewHolder(@NonNull View itemView) { + super(itemView); + + view = itemView; + icon = itemView.findViewById(R.id.icon); + title = itemView.findViewById(R.id.title); + unreadCounter = itemView.findViewById(R.id.unread_counter); + rowSwitch = itemView.findViewById(R.id.row_switch); + settingsBtn = itemView.findViewById(R.id.drawer_settings_btn); + login = itemView.findViewById(R.id.drawer_header_login); + server = itemView.findViewById(R.id.drawer_header_server); } + } - @Override - public boolean isEmpty() { - return !m_enableParentBtn && super.isEmpty(); - } - - @Override - public int getViewTypeCount() { - return VIEW_COUNT; - } + private class FeedDiffUtilItemCallback extends DiffUtil.ItemCallback { @Override - public int getItemViewType(int position) { - Feed feed = items.get(position); - - if (/*!m_activity.isSmallScreen() &&*/ m_selectedFeed != null && feed.id == m_selectedFeed.id) { - return VIEW_SELECTED; - } else { - return VIEW_NORMAL; - } + public boolean areItemsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { + return oldItem.id == newItem.id; } @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - - Feed feed = items.get(position); - - if (v == null) { - int layoutId = R.layout.feeds_row; + public boolean areContentsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { + return oldItem.id == newItem.id && + oldItem.is_cat == newItem.is_cat && + oldItem.title.equals(newItem.title) && + oldItem.unread == newItem.unread; + } + } - if (getItemViewType(position) == VIEW_SELECTED) { - layoutId = R.layout.feeds_row_selected; - } + private class FeedsAdapter extends ListAdapter { + public static final int VIEW_NORMAL = 0; + public static final int VIEW_SELECTED = 1; + public static final int VIEW_GOBACK = 2; + public static final int VIEW_HEADER = 3; + public static final int VIEW_TOGGLE_UNREAD = 4; + public static final int VIEW_DIVIDER = 5; - LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(layoutId, null); + protected FeedsAdapter() { + super(new FeedDiffUtilItemCallback()); + } + @NonNull + @Override + public FeedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + int layoutId = R.layout.feeds_row; + + switch (viewType) { + case VIEW_SELECTED: + layoutId = R.layout.feeds_row_selected; + break; + case VIEW_GOBACK: + layoutId = R.layout.feeds_row_goback; + break; + case VIEW_TOGGLE_UNREAD: + layoutId = R.layout.feeds_row_toggle; + break; + case VIEW_HEADER: + layoutId = R.layout.feeds_row_header; + break; + case VIEW_DIVIDER: + layoutId = R.layout.feeds_row_divider; + break; } - ImageView icon = v.findViewById(R.id.icon); + return new FeedViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)); + } - if (icon != null) { - if (feed.id == 0 && !feed.is_cat) { - icon.setImageResource(R.drawable.baseline_archive_24); + @Override + public void onBindViewHolder(@NonNull FeedViewHolder holder, int position) { + Feed feed = getItem(position); + + if (holder.icon != null) { + if (feed.id == Feed.TYPE_GOBACK) { + holder.icon.setImageResource(R.drawable.baseline_arrow_back_24); + } else if (feed.id == 0 && !feed.is_cat) { + holder.icon.setImageResource(R.drawable.baseline_archive_24); } else if (feed.id == -1 && !feed.is_cat) { - icon.setImageResource(R.drawable.baseline_star_24); + holder.icon.setImageResource(R.drawable.baseline_star_24); } else if (feed.id == -2 && !feed.is_cat) { - icon.setImageResource(R.drawable.rss); + holder.icon.setImageResource(R.drawable.rss); } else if (feed.id == -3 && !feed.is_cat) { - icon.setImageResource(R.drawable.baseline_local_fire_department_24); + holder.icon.setImageResource(R.drawable.baseline_local_fire_department_24); } else if (feed.id == -4 && !feed.is_cat) { - icon.setImageResource(R.drawable.baseline_inbox_24); + holder.icon.setImageResource(R.drawable.baseline_inbox_24); } else if (feed.id == -6 && !feed.is_cat) { - icon.setImageResource(R.drawable.baseline_restore_24); + holder.icon.setImageResource(R.drawable.baseline_restore_24); } else if (feed.is_cat) { - icon.setImageResource(R.drawable.baseline_folder_open_24); + holder.icon.setImageResource(R.drawable.baseline_folder_open_24); } else { - icon.setImageResource(R.drawable.rss); + holder.icon.setImageResource(R.drawable.rss); } } - TextView tt = v.findViewById(R.id.title); + if (holder.title != null) { + holder.title.setText(feed.display_title != null ? feed.display_title : feed.title); - if (tt != null) { - tt.setText(feed.display_title != null ? feed.display_title : feed.title); + if (feed.always_display_as_feed || (!feed.is_cat && feed.id == -4)) { + holder.title.setTypeface(null, Typeface.BOLD); + } else { + holder.title.setTypeface(null, Typeface.NORMAL); + } - if (feed.always_display_as_feed || (!feed.is_cat && feed.id == -4)) { - tt.setTypeface(null, Typeface.BOLD); - } else { - tt.setTypeface(null, Typeface.NORMAL); - } + } + if (holder.unreadCounter != null) { + holder.unreadCounter.setText(String.valueOf(feed.unread)); + holder.unreadCounter.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); } - TextView tu = v.findViewById(R.id.unread_counter); + if (holder.settingsBtn != null) { + holder.settingsBtn.setOnClickListener(view -> { + Intent intent = new Intent(getActivity(), + PreferencesActivity.class); - if (tu != null) { - tu.setText(String.valueOf(feed.unread)); - tu.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); + startActivityForResult(intent, 0); + }); } - return v; + holder.view.setOnClickListener(view -> { + if (feed.is_cat) { + if (feed.always_display_as_feed) { + m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), true); + } else if (feed.id < 0) { + m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); + } else { + m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread)); + } + } else { + m_activity.onFeedSelected(feed); + } + }); } - } - public void sortFeeds() { - Comparator cmp; - - if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { - cmp = new FeedUnreadComparator(); - } else { - if (m_activity.getApiLevel() >= 3) { - cmp = new FeedOrderComparator(); + @Override + public int getItemViewType(int position) { + Feed feed = getItem(position); + + if (feed.id == Feed.TYPE_GOBACK) { + return VIEW_GOBACK; + } else if (feed.id == Feed.TYPE_DIVIDER) { + return VIEW_DIVIDER; + } else if (feed.id == Feed.TYPE_HEADER) { + return VIEW_HEADER; + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { + return VIEW_TOGGLE_UNREAD; + } else if (feed.id == m_selectedFeedId) { + return VIEW_SELECTED; } else { - cmp = new FeedTitleComparator(); + return VIEW_NORMAL; } } - - try { - m_feeds.sort(cmp); - } catch (IllegalArgumentException e) { - // sort order got changed in prefs or something - e.printStackTrace(); - } - try { - m_adapter.notifyDataSetChanged(); - } catch (NullPointerException e) { - // adapter missing + public void sortFeeds(List feeds) { + Comparator cmp; + + if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { + cmp = new FeedUnreadComparator(); + } else { + if (m_activity.getApiLevel() >= 3) { + cmp = new FeedOrderComparator(); + } else { + cmp = new FeedTitleComparator(); + } + } + + try { + feeds.sort(cmp); + } catch (IllegalArgumentException e) { + // sort order got changed in prefs or something + e.printStackTrace(); + } } } @@ -536,10 +602,10 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - sortFeeds(); + refresh(); } - public Feed getFeedAtPosition(int position) { + /* public Feed getFeedAtPosition(int position) { try { return (Feed) m_list.getItemAtPosition(position); } catch (ArrayIndexOutOfBoundsException e) { @@ -547,13 +613,14 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi } return null; - } - - public void setSelectedfeed(Feed feed) { - m_selectedFeed = feed; + } */ + public void setSelectedFeedId(int feedId) { if (m_adapter != null) { - m_adapter.notifyDataSetChanged(); + m_selectedFeedId = feedId; + + // TODO handle properly + m_adapter.notifyDataSetChanged(); } } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java deleted file mode 100644 index 92ce64d2..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.fox.ttrss; - -import android.content.Context; - -import java.util.HashMap; - -class FeedsLoader extends ApiLoader { - private final int m_catId; - - public FeedsLoader(Context context, HashMap params, int catId) { - super(context, params); - - m_catId = catId; - } - - public int getCatId() { - return m_catId; - } -} 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 458f1f76..31a0a23b 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,11 +164,16 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - if (m_prefs.getBoolean("enable_cats", true)) { + /* 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); - } + } */ + + FeedsFragment ff = new FeedsFragment(); + ff.initialize(12); + + ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); // allow overriding feed to open on startup in non-shortcut mode, default to // open_on_startup prefs setting and not-category @@ -273,7 +278,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); if (ff != null && ff.isAdded()) { - ff.setSelectedfeed(feed); + ff.setSelectedFeedId(feed.id); } if (m_drawerLayout != null) { @@ -321,7 +326,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList .beginTransaction(); FeedsFragment ff = new FeedsFragment(); - ff.initialize(cat, true); + ff.initialize(cat.id); ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); ft.addToBackStack(null); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java index 9cbe9f83..5432bad1 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java @@ -7,6 +7,11 @@ import android.os.Parcelable; import org.fox.ttrss.R; public class Feed implements Comparable, Parcelable { + public static final int TYPE_HEADER = -10000; + public static final int TYPE_GOBACK = -10001; + public static final int TYPE_DIVIDER = -10002; + public static final int TYPE_TOGGLE_UNREAD = -10003; + public String feed_url; public String title; public int id; @@ -18,7 +23,13 @@ public class Feed implements Comparable, Parcelable { public boolean is_cat; public boolean always_display_as_feed; public String display_title; - + + public Feed(int id) { + this.id = id; + this.title = "ID:" + id; + this.is_cat = false; + } + public Feed(int id, String title, boolean is_cat) { this.id = id; this.title = title; diff --git a/org.fox.ttrss/src/main/res/layout/drawer_divider.xml b/org.fox.ttrss/src/main/res/layout/drawer_divider.xml deleted file mode 100644 index 55aa5fdc..00000000 --- a/org.fox.ttrss/src/main/res/layout/drawer_divider.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/drawer_header.xml b/org.fox.ttrss/src/main/res/layout/drawer_header.xml deleted file mode 100755 index f4954259..00000000 --- a/org.fox.ttrss/src/main/res/layout/drawer_header.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml b/org.fox.ttrss/src/main/res/layout/feeds_goback.xml deleted file mode 100755 index 75ba8fe1..00000000 --- a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml new file mode 100644 index 00000000..55aa5fdc --- /dev/null +++ b/org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml new file mode 100755 index 00000000..75ba8fe1 --- /dev/null +++ b/org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml @@ -0,0 +1,38 @@ + + + + + + + + \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml new file mode 100755 index 00000000..f4954259 --- /dev/null +++ b/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml b/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml new file mode 100755 index 00000000..1ed85a5b --- /dev/null +++ b/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file -- cgit v1.2.3-54-g00ecf From ef4705f2387ef6158f3d66e8c94ebb9fcc28732a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 May 2025 17:50:58 +0300 Subject: more wip --- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 26 ++++++++++++++++------ .../main/java/org/fox/ttrss/MasterActivity.java | 4 ++-- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'org.fox.ttrss/src') 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 edc75c9d..da0af1de 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 @@ -48,7 +48,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL private final String TAG = this.getClass().getSimpleName(); private SharedPreferences m_prefs; private MasterActivity m_activity; - private int m_catId; + private FeedCategory m_category; private int m_selectedFeedId; private SwipeRefreshLayout m_swipeLayout; private boolean m_enableParentBtn = false; @@ -56,8 +56,9 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL private RecyclerView m_list; private RecyclerView.LayoutManager m_layoutManager; - public void initialize(int catId) { - m_catId = catId; + public void initialize(@NonNull FeedCategory cat, boolean enableParentBtn) { + m_category = cat; + m_enableParentBtn = enableParentBtn; } @Override @@ -65,14 +66,13 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = false; - // final boolean unreadOnly = m_activity.getUnreadOnly() && (m_activeCategory == null || m_activeCategory.id != -1); + final boolean unreadOnly = m_activity.getUnreadOnly() && m_category.id != -1; /* starred */ HashMap params = new HashMap<>(); params.put("op", "getFeeds"); params.put("sid", sessionId); params.put("include_nested", "true"); - params.put("cat_id", String.valueOf(m_catId)); + params.put("cat_id", String.valueOf(m_category.id)); if (unreadOnly) params.put("unread_only", "true"); @@ -131,7 +131,19 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL // m_adapter.sortFeeds(feedsJson); feeds.add(new Feed(Feed.TYPE_HEADER)); - feeds.add(new Feed(Feed.TYPE_GOBACK)); + + if (m_enableParentBtn) { + feeds.add(new Feed(Feed.TYPE_GOBACK)); + + if (m_enableParentBtn && m_category.id >= 0 && !feedsJson.isEmpty()) { + Feed feed = new Feed(m_category.id, m_category.title, true); + feed.unread = catUnread; + feed.always_display_as_feed = true; + feed.display_title = getString(R.string.feed_all_articles); + + feeds.add(0, feed); + } + } feeds.addAll(feedsJson); 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 31a0a23b..11265769 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 @@ -171,7 +171,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList } */ FeedsFragment ff = new FeedsFragment(); - ff.initialize(12); + ff.initialize(-4, false); ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); @@ -326,7 +326,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList .beginTransaction(); FeedsFragment ff = new FeedsFragment(); - ff.initialize(cat.id); + ff.initialize(cat.id, true); ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); ft.addToBackStack(null); -- cgit v1.2.3-54-g00ecf From 191ba10cf398212e25e80114a43100dfdcfa55ab Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 08:05:26 +0300 Subject: switch feeds to recycler view while removing most of legacy code --- .../src/main/java/org/fox/ttrss/ArticleModel.java | 2 +- .../java/org/fox/ttrss/BaseFeedlistFragment.java | 78 ---- .../main/java/org/fox/ttrss/CommonActivity.java | 2 +- .../java/org/fox/ttrss/FeedCategoriesFragment.java | 446 --------------------- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 356 ++++++++-------- .../main/java/org/fox/ttrss/HeadlinesFragment.java | 13 +- .../main/java/org/fox/ttrss/MasterActivity.java | 149 ++++--- .../main/java/org/fox/ttrss/OnlineActivity.java | 7 - .../java/org/fox/ttrss/RootCategoriesFragment.java | 134 +++++++ .../src/main/java/org/fox/ttrss/types/Feed.java | 84 ++-- .../src/main/res/drawable/baseline_label_24.xml | 5 + .../src/main/res/layout/feeds_row_header.xml | 70 ---- .../src/main/res/layout/fragment_feeds.xml | 96 ++++- .../main/res/layout/fragment_feeds_recycler.xml | 18 - org.fox.ttrss/src/main/res/xml/preferences.xml | 6 - 15 files changed, 543 insertions(+), 923 deletions(-) delete mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java delete mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java create mode 100755 org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java create mode 100644 org.fox.ttrss/src/main/res/drawable/baseline_label_24.xml delete mode 100755 org.fox.ttrss/src/main/res/layout/feeds_row_header.xml delete mode 100755 org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml (limited to 'org.fox.ttrss/src') 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 80467f63..7a79887d 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 @@ -80,7 +80,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle } public void startLoading(boolean append, @NonNull Feed feed, int resizeWidth) { - Log.d(TAG, "startLoading append=" + append); + Log.d(TAG, "startLoading append=" + append + " feed id=" + feed.id + " cat=" + feed.is_cat); m_resizeWidth = resizeWidth; 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 deleted file mode 100755 index 4f48a19e..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.fox.ttrss; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.appcompat.widget.SwitchCompat; - -import java.net.MalformedURLException; -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) { - - View layout = inflater.inflate(R.layout.feeds_row_header, list, false); - list.addHeaderView(layout, null, false); - - TextView login = view.findViewById(R.id.drawer_header_login); - TextView server = view.findViewById(R.id.drawer_header_server); - - login.setText(prefs.getString("login", "")); - try { - server.setText(new URL(prefs.getString("ttrss_url", "")).getHost()); - } catch (MalformedURLException e) { - server.setText(""); - } - - View settings = view.findViewById(R.id.drawer_settings_btn); - - settings.setOnClickListener(v -> { - try { - Intent intent = new Intent(getActivity(), - PreferencesActivity.class); - - startActivityForResult(intent, 0); - - } catch (Exception e) { - e.printStackTrace(); - } - }); - - /* deal with ~material~ footers */ - - // divider - final View footer = inflater.inflate(R.layout.feeds_row_divider, list, false); - footer.setOnClickListener(v -> { - // - }); - list.addFooterView(footer); - - // unread only checkbox - final View rowToggle = inflater.inflate(R.layout.feeds_row_toggle, list, false); - list.addFooterView(rowToggle); - TextView text = rowToggle.findViewById(R.id.title); - text.setText(R.string.unread_only); - - ImageView icon = rowToggle.findViewById(R.id.icon); - icon.setImageResource(R.drawable.baseline_filter_alt_24); - - final SwitchCompat rowSwitch = rowToggle.findViewById(R.id.row_switch); - rowSwitch.setChecked(activity.getUnreadOnly()); - - rowSwitch.setOnCheckedChangeListener((button, isChecked) -> { - activity.setUnreadOnly(isChecked); - refresh(); - }); - - footer.setOnClickListener(v -> rowSwitch.setChecked(!rowSwitch.isChecked())); - - } - -} 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 97e356c4..d5d593ae 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 @@ -310,7 +310,7 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc setAppTheme(sharedPreferences); } - String[] filter = new String[] { "enable_cats", "headline_mode", "widget_update_interval", + String[] filter = new String[] { "headline_mode", "widget_update_interval", "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size", "force_phone_layout", "open_on_startup"}; 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 deleted file mode 100755 index 34127013..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java +++ /dev/null @@ -1,446 +0,0 @@ -package org.fox.ttrss; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.os.Bundle; -import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.preference.PreferenceManager; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.types.Feed; -import org.fox.ttrss.types.FeedCategory; -import org.fox.ttrss.types.FeedCategoryList; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - -public class FeedCategoriesFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener, - LoaderManager.LoaderCallbacks { - private final String TAG = this.getClass().getSimpleName(); - private FeedCategoryListAdapter m_adapter; - private final FeedCategoryList m_cats = new FeedCategoryList(); - FeedCategory m_selectedCat; - private MasterActivity m_activity; - private SwipeRefreshLayout m_swipeLayout; - private ListView m_list; - protected SharedPreferences m_prefs; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = m_activity.getUnreadOnly(); - - HashMap params = new HashMap<>(); - params.put("op", "getCategories"); - params.put("sid", sessionId); - params.put("enable_nested", "true"); - - if (unreadOnly) - params.put("unread_only", "true"); - - return new ApiLoader(getContext(), params); - } - - @Override - public void onLoadFinished(Loader loader, JsonElement result) { - Log.d(TAG, "onLoadFinished: " + loader + " " + result); - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - - if (result != null) { - try { - JsonArray content = result.getAsJsonArray(); - if (content != null) { - Type listType = new TypeToken>() {}.getType(); - final List cats = new Gson().fromJson(content, listType); - - m_cats.clear(); - - int apiLevel = m_activity.getApiLevel(); - - boolean specialCatFound = false; - - // virtual cats implemented in getCategories since api level 1 - if (apiLevel == 0) { - m_cats.add(new FeedCategory(-1, getString(R.string.cat_special), 0)); - m_cats.add(new FeedCategory(-2, getString(R.string.cat_labels), 0)); - m_cats.add(new FeedCategory(0, getString(R.string.cat_uncategorized), 0)); - - specialCatFound = true; - } - - for (FeedCategory c : cats) { - if (c.id == -1) { - specialCatFound = true; - break; - } - } - - m_cats.addAll(cats); - - sortCats(); - - if (!specialCatFound) { - m_cats.add(0, new FeedCategory(-1, getString(R.string.cat_special), 0)); - } - - m_adapter.notifyDataSetChanged(); - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - ApiLoader al = (ApiLoader) loader; - - if (al.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - if (al.getLastErrorMessage() != null) { - m_activity.toast(getString(al.getErrorMessage()) + "\n" + al.getLastErrorMessage()); - } else { - m_activity.toast(al.getErrorMessage()); - } - } - } - - public void sortCats() { - Comparator cmp; - - if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { - cmp = new CatUnreadComparator(); - } else { - if (m_activity.getApiLevel() >= 3) { - cmp = new CatOrderComparator(); - } else { - cmp = new CatTitleComparator(); - } - } - - try { - m_cats.sort(cmp); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - try { - m_adapter.notifyDataSetChanged(); - } catch (NullPointerException e) { - // adapter missing - } - } - - @Override - public void onLoaderReset(Loader loader) { - Log.d(TAG, "onLoaderReset: " + loader); - - /*m_cats.clear(); - m_adapter.notifyDataSetChanged();*/ - } - - @SuppressLint("DefaultLocale") - static class CatUnreadComparator implements Comparator { - @Override - public int compare(FeedCategory a, FeedCategory b) { - if (a.unread != b.unread) - return b.unread - a.unread; - else - return a.title.toUpperCase().compareTo(b.title.toUpperCase()); - } - } - - - @SuppressLint("DefaultLocale") - static class CatTitleComparator implements Comparator { - - @Override - public int compare(FeedCategory a, FeedCategory b) { - if (a.id >= 0 && b.id >= 0) - return a.title.toUpperCase().compareTo(b.title.toUpperCase()); - else - return a.id - b.id; - } - - } - - @SuppressLint("DefaultLocale") - static class CatOrderComparator implements Comparator { - - @Override - public int compare(FeedCategory a, FeedCategory b) { - if (a.id >= 0 && b.id >= 0) - if (a.order_id != 0 && b.order_id != 0) - return a.order_id - b.order_id; - else - return a.title.toUpperCase().compareTo(b.title.toUpperCase()); - else - return a.id - b.id; - } - - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); - - int itemId = item.getItemId(); - if (itemId == R.id.browse_headlines) { - FeedCategory cat = getCategoryAtPosition(info.position); - if (cat != null) { - m_activity.onCatSelected(cat, true); - //setSelectedCategory(cat); - } - return true; - } else if (itemId == R.id.browse_feeds) { - FeedCategory cat = getCategoryAtPosition(info.position); - if (cat != null) { - m_activity.onCatSelected(cat, false); - //cf.setSelectedCategory(cat); - } - return true; - } else if (itemId == R.id.catchup_category) { - final FeedCategory cat = getCategoryAtPosition(info.position); - - if (cat != null) { - m_activity.catchupDialog(new Feed(cat.id, cat.title, true)); - } - return true; - } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - m_activity.getMenuInflater().inflate(R.menu.context_category, menu); - - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - FeedCategory cat = (FeedCategory) m_list.getItemAtPosition(info.position); - - if (cat != null) - menu.setHeaderTitle(cat.title); - - super.onCreateContextMenu(menu, v, menuInfo); - - } - - public FeedCategory getCategoryAtPosition(int position) { - try { - return (FeedCategory) m_list.getItemAtPosition(position); - } catch (NullPointerException e) { - return null; - } catch (IndexOutOfBoundsException e) { - return null; - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (savedInstanceState != null) { - m_selectedCat = savedInstanceState.getParcelable("m_selectedCat"); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - View view = inflater.inflate(R.layout.fragment_feeds, container, false); - - m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); - - m_swipeLayout.setOnRefreshListener(this::refresh); - - 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); - - m_list.setAdapter(m_adapter); - m_list.setOnItemClickListener(this); - registerForContextMenu(m_list); - - return view; - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_activity = (MasterActivity)activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_prefs.registerOnSharedPreferenceChangeListener(this); - - } - - @Override - public void onResume() { - super.onResume(); - - LoaderManager.getInstance(this).initLoader(Application.LOADER_CATS, null, this).forceLoad(); - - m_activity.invalidateOptionsMenu(); - } - - public void refresh() { - if (!isAdded()) - return; - - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(true); - - LoaderManager.getInstance(this).restartLoader(Application.LOADER_CATS, null, this).forceLoad(); - } - - private class FeedCategoryListAdapter extends ArrayAdapter { - private final ArrayList items; - - public static final int VIEW_NORMAL = 0; - public static final int VIEW_SELECTED = 1; - - public static final int VIEW_COUNT = VIEW_SELECTED+1; - - public FeedCategoryListAdapter(Context context, int textViewResourceId, ArrayList items) { - super(context, textViewResourceId, items); - this.items = items; - } - - public int getViewTypeCount() { - return VIEW_COUNT; - } - - @Override - public int getItemViewType(int position) { - FeedCategory cat = items.get(position); - - if (/*!m_activity.isSmallScreen() &&*/ m_selectedCat != null && cat.id == m_selectedCat.id) { - return VIEW_SELECTED; - } else { - return VIEW_NORMAL; - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - - FeedCategory cat = items.get(position); - - if (v == null) { - int layoutId = R.layout.feeds_row; - - if (getItemViewType(position) == VIEW_SELECTED) { - layoutId = R.layout.feeds_row_selected; - } - - LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(layoutId, null); - - } - - ImageView icon = v.findViewById(R.id.icon); - - if (icon != null) { - icon.setImageResource(R.drawable.baseline_folder_open_24); - - } - - TextView tt = v.findViewById(R.id.title); - - if (tt != null) { - tt.setText(cat.title); - } - - TextView tu = v.findViewById(R.id.unread_counter); - - if (tu != null) { - tu.setText(String.valueOf(cat.unread)); - tu.setVisibility((cat.unread > 0) ? View.VISIBLE : View.INVISIBLE); - } - - return v; - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - - sortCats(); - - } - - @Override - public void onItemClick(AdapterView av, View view, int position, long id) { - ListView list = (ListView)av; - - Log.d(TAG, "onItemClick=" + position); - - if (list != null) { - - FeedCategory cat = (FeedCategory)list.getItemAtPosition(position); - - m_selectedCat = null; - m_adapter.notifyDataSetChanged(); - - if (cat != null) { - if (cat.id < 0) { - m_activity.onCatSelected(cat, false); - } else { - m_activity.onCatSelected(cat); - } - - } - } - } - - public void setSelectedCategory(FeedCategory cat) { - m_selectedCat = cat; - - if (m_adapter != null) { - m_adapter.notifyDataSetChanged(); - } - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putParcelable("m_selectedCat", m_selectedCat); - } -} 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 da0af1de..f66758ee 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 @@ -2,6 +2,7 @@ package org.fox.ttrss; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.Dialog; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; @@ -14,6 +15,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.ImageView; import android.widget.TextView; @@ -29,35 +31,42 @@ import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.google.android.material.materialswitch.MaterialSwitch; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.reflect.TypeToken; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.types.FeedCategory; import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener, LoaderManager.LoaderCallbacks { private final String TAG = this.getClass().getSimpleName(); - private SharedPreferences m_prefs; - private MasterActivity m_activity; - private FeedCategory m_category; - private int m_selectedFeedId; - private SwipeRefreshLayout m_swipeLayout; + protected SharedPreferences m_prefs; + protected MasterActivity m_activity; + protected Feed m_feed; + private Feed m_selectedFeed; + protected SwipeRefreshLayout m_swipeLayout; private boolean m_enableParentBtn = false; - private FeedsAdapter m_adapter; + protected FeedsAdapter m_adapter; private RecyclerView m_list; private RecyclerView.LayoutManager m_layoutManager; - public void initialize(@NonNull FeedCategory cat, boolean enableParentBtn) { - m_category = cat; + public void initialize(@NonNull Feed feed, boolean enableParentBtn) { + Log.d(TAG, "initialize, feed=" + feed); + + m_feed = feed; m_enableParentBtn = enableParentBtn; } @@ -65,23 +74,21 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL public Loader onCreateLoader(int id, Bundle args) { if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - final String sessionId = m_activity.getSessionId(); - final boolean unreadOnly = m_activity.getUnreadOnly() && m_category.id != -1; /* starred */ - HashMap params = new HashMap<>(); params.put("op", "getFeeds"); - params.put("sid", sessionId); + params.put("sid", m_activity.getSessionId()); params.put("include_nested", "true"); - params.put("cat_id", String.valueOf(m_category.id)); + params.put("cat_id", String.valueOf(m_feed.id)); - if (unreadOnly) + /* except marked */ + if (m_activity.getUnreadOnly() && m_feed.id != -1) params.put("unread_only", "true"); return new ApiLoader(getContext(), params); } @Override - public void onLoadFinished(Loader loader, JsonElement result) { + public void onLoadFinished(@NonNull Loader loader, JsonElement result) { if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); if (result != null) { @@ -90,7 +97,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL if (content != null) { Type listType = new TypeToken>() {}.getType(); - final List feedsJson = new Gson().fromJson(content, listType); + List feedsJson = new Gson().fromJson(content, listType); List feeds = new ArrayList<>(); @@ -130,25 +137,32 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL // m_adapter.sortFeeds(feedsJson); - feeds.add(new Feed(Feed.TYPE_HEADER)); + //List feedsJsonFiltered = feedsJson.stream().filter(a -> a.id >= -10).collect(Collectors.toList()); + + if (m_feed.id == Feed.ALL_ARTICLES) + feedsJson = feedsJson.stream().filter(a -> a.id > -10).collect(Collectors.toList()); + + sortFeeds(feedsJson, m_feed); + + // feeds.add(new Feed(Feed.TYPE_HEADER)); if (m_enableParentBtn) { - feeds.add(new Feed(Feed.TYPE_GOBACK)); + feeds.add(0, new Feed(Feed.TYPE_GOBACK)); - if (m_enableParentBtn && m_category.id >= 0 && !feedsJson.isEmpty()) { - Feed feed = new Feed(m_category.id, m_category.title, true); - feed.unread = catUnread; - feed.always_display_as_feed = true; - feed.display_title = getString(R.string.feed_all_articles); + if (m_feed.id >= 0 && !feedsJson.isEmpty()) { + Feed feed = new Feed(m_feed.id, m_feed.title, true); - feeds.add(0, feed); + feed.unread = feedsJson.stream().map(a -> a.unread).reduce(0, Integer::sum); + feed.always_open_headlines = true; + + feeds.add(1, feed); } } feeds.addAll(feedsJson); feeds.add(new Feed(Feed.TYPE_DIVIDER)); - feeds.add(new Feed(Feed.TYPE_TOGGLE_UNREAD)); + feeds.add(new Feed(Feed.TYPE_TOGGLE_UNREAD, getString(R.string.unread_only), true)); m_adapter.submitList(feeds); @@ -156,7 +170,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } } catch (Exception e) { - e.printStackTrace(); + m_activity.toast(e.getMessage()); } } @@ -211,6 +225,17 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } + @SuppressLint("DefaultLocale") + static class SpecialOrderComparator implements Comparator { + static List order = Arrays.asList(Feed.ALL_ARTICLES, Feed.FRESH, Feed.MARKED, + Feed.PUBLISHED, Feed.ARCHIVED, Feed.RECENTLY_READ); + + @Override + public int compare(Feed a, Feed b) { + return Integer.valueOf(order.indexOf(a.id)).compareTo(order.indexOf(b.id)); + } + } + @SuppressLint("DefaultLocale") static class FeedOrderComparator implements Comparator { @@ -241,46 +266,42 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL @Override public boolean onContextItemSelected(MenuItem item) { - /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item .getMenuInfo(); + + final Feed feed = m_adapter.getCurrentList().get(info.position); + + Log.d(TAG, "context for feed=" + feed.id); + int itemId = item.getItemId(); if (itemId == R.id.browse_headlines) { - Feed feed = getFeedAtPosition(info.position); - if (feed != null) { - m_activity.onFeedSelected(feed); - } + Feed forceFeed = new Feed(feed); + forceFeed.always_open_headlines = true; + + m_activity.onFeedSelected(forceFeed); return true; } else if (itemId == R.id.browse_feeds) { - Feed feed = getFeedAtPosition(info.position); - if (feed != null) { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); - } + m_activity.onFeedSelected(feed); + //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); return true; } else if (itemId == R.id.unsubscribe_feed) { - final Feed feed = getFeedAtPosition(info.position); - if (feed != null) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) - .setMessage(getString(R.string.unsubscribe_from_prompt, feed.title)) - .setPositiveButton(R.string.unsubscribe, - (dialog, which) -> m_activity.unsubscribeFeed(feed)) - .setNegativeButton(R.string.dialog_cancel, - (dialog, which) -> { - - }); - - Dialog dlg = builder.create(); - dlg.show(); - } + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) + .setMessage(getString(R.string.unsubscribe_from_prompt, feed.title)) + .setPositiveButton(R.string.unsubscribe, + (dialog, which) -> m_activity.unsubscribeFeed(feed)) + .setNegativeButton(R.string.dialog_cancel, + (dialog, which) -> { + + }); + + Dialog dlg = builder.create(); + dlg.show(); return true; } else if (itemId == R.id.catchup_feed) { - Feed feed = getFeedAtPosition(info.position); - - if (feed != null) { - m_activity.catchupDialog(feed); - } + m_activity.catchupDialog(feed); return true; - } */ + } Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); return super.onContextItemSelected(item); @@ -289,14 +310,14 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + + m_activity.getMenuInflater().inflate(R.menu.context_feed, menu); - /* getActivity().getMenuInflater().inflate(R.menu.context_feed, menu); - - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Feed feed = (Feed) m_list.getItemAtPosition(info.position); + Feed feed = m_adapter.getCurrentList().get(info.position); - menu.setHeaderTitle(feed.display_title != null ? feed.display_title : feed.title); + menu.setHeaderTitle(feed.title); if (!feed.is_cat) { menu.findItem(R.id.browse_feeds).setVisible(false); @@ -304,7 +325,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL if (feed.id <= 0) { menu.findItem(R.id.unsubscribe_feed).setVisible(false); - } */ + } super.onCreateContextMenu(menu, v, menuInfo); @@ -315,8 +336,8 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL super.onCreate(savedInstanceState); if (savedInstanceState != null) { - m_catId = savedInstanceState.getInt("m_catId"); - m_selectedFeedId = savedInstanceState.getInt("m_selectedId"); + m_feed = savedInstanceState.getParcelable("m_feed"); + m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed"); m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn"); } } @@ -325,15 +346,15 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); - out.putInt("m_catId", m_catId); - out.putInt("m_selectedId", m_selectedFeedId); + out.putParcelable("m_feed", m_feed); + out.putParcelable("m_selectedFeed", m_selectedFeed); out.putBoolean("m_enableParentBtn", m_enableParentBtn); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_feeds_recycler, container, false); + View view = inflater.inflate(R.layout.fragment_feeds, container, false); m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); @@ -349,19 +370,32 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL m_adapter = new FeedsAdapter(); m_list.setAdapter(m_adapter); - /* if (m_enableParentBtn) { - View layout = inflater.inflate(R.layout.feeds_goback, m_list, false); + TextView login = view.findViewById(R.id.drawer_header_login); - layout.setOnClickListener(view1 -> m_activity.getSupportFragmentManager().popBackStack()); + if (login != null) { + login.setText(m_prefs.getString("login", "")); + } - m_list.addHeaderView(layout, null, false); + TextView server = view.findViewById(R.id.drawer_header_server); + + if (server != null) { + try { + server.setText(new URL(m_prefs.getString("ttrss_url", "")).getHost()); + } catch (MalformedURLException e) { + server.setText(""); + } } - m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, m_feeds); - m_list.setAdapter(m_adapter); - m_list.setOnItemClickListener(this); + View settingsBtn = view.findViewById(R.id.drawer_settings_btn); - registerForContextMenu(m_list); */ + if (settingsBtn != null) { + settingsBtn.setOnClickListener(v -> { + Intent intent = new Intent(getActivity(), + PreferencesActivity.class); + + startActivityForResult(intent, 0); + }); + } return view; } @@ -385,38 +419,17 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL public void onResume() { super.onResume(); - LoaderManager.getInstance(this).initLoader(Application.LOADER_FEEDS, null, this).forceLoad(); + Log.d(TAG, "onResume"); + + refresh(); m_activity.invalidateOptionsMenu(); } - - /* @Override - public void onItemClick(AdapterView av, View view, int position, long id) { - ListView list = (ListView)av; - - if (list != null) { - Feed feed = (Feed)list.getItemAtPosition(position); - - if (feed != null) { - if (feed.is_cat) { - if (feed.always_display_as_feed) { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), true); - } else if (feed.id < 0) { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); - } else { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread)); - } - } else { - m_activity.onFeedSelected(feed); - } - } - - //m_selectedFeed = feed; - //m_adapter.notifyDataSetChanged(); - } - } */ public void refresh() { + if (!isAdded()) + return; + if (m_swipeLayout != null) { m_swipeLayout.setRefreshing(true); } @@ -430,10 +443,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL private ImageView icon; private TextView title; private TextView unreadCounter; - private TextView rowSwitch; - private View settingsBtn; - private TextView login; - private TextView server; + private MaterialSwitch rowSwitch; public FeedViewHolder(@NonNull View itemView) { super(itemView); @@ -443,9 +453,6 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL title = itemView.findViewById(R.id.title); unreadCounter = itemView.findViewById(R.id.unread_counter); rowSwitch = itemView.findViewById(R.id.row_switch); - settingsBtn = itemView.findViewById(R.id.drawer_settings_btn); - login = itemView.findViewById(R.id.drawer_header_login); - server = itemView.findViewById(R.id.drawer_header_server); } } @@ -465,11 +472,10 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } } - private class FeedsAdapter extends ListAdapter { + protected class FeedsAdapter extends ListAdapter { public static final int VIEW_NORMAL = 0; public static final int VIEW_SELECTED = 1; public static final int VIEW_GOBACK = 2; - public static final int VIEW_HEADER = 3; public static final int VIEW_TOGGLE_UNREAD = 4; public static final int VIEW_DIVIDER = 5; @@ -492,9 +498,6 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL case VIEW_TOGGLE_UNREAD: layoutId = R.layout.feeds_row_toggle; break; - case VIEW_HEADER: - layoutId = R.layout.feeds_row_header; - break; case VIEW_DIVIDER: layoutId = R.layout.feeds_row_divider; break; @@ -508,31 +511,13 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL Feed feed = getItem(position); if (holder.icon != null) { - if (feed.id == Feed.TYPE_GOBACK) { - holder.icon.setImageResource(R.drawable.baseline_arrow_back_24); - } else if (feed.id == 0 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_archive_24); - } else if (feed.id == -1 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_star_24); - } else if (feed.id == -2 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.rss); - } else if (feed.id == -3 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_local_fire_department_24); - } else if (feed.id == -4 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_inbox_24); - } else if (feed.id == -6 && !feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_restore_24); - } else if (feed.is_cat) { - holder.icon.setImageResource(R.drawable.baseline_folder_open_24); - } else { - holder.icon.setImageResource(R.drawable.rss); - } + holder.icon.setImageResource(getIconForFeed(feed)); } if (holder.title != null) { - holder.title.setText(feed.display_title != null ? feed.display_title : feed.title); + holder.title.setText(feed.title); - if (feed.always_display_as_feed || (!feed.is_cat && feed.id == -4)) { + if (feed.always_open_headlines || (!feed.is_cat && feed.id == -4)) { holder.title.setTypeface(null, Typeface.BOLD); } else { holder.title.setTypeface(null, Typeface.NORMAL); @@ -545,25 +530,42 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL holder.unreadCounter.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); } - if (holder.settingsBtn != null) { - holder.settingsBtn.setOnClickListener(view -> { - Intent intent = new Intent(getActivity(), - PreferencesActivity.class); + // there's only one kind of row with checkbox atm + if (holder.rowSwitch != null) { + holder.rowSwitch.setChecked(m_activity.getUnreadOnly()); - startActivityForResult(intent, 0); + holder.rowSwitch.setOnCheckedChangeListener((button, isChecked) -> { + m_activity.setUnreadOnly(isChecked); + refresh(); }); } + holder.view.setOnLongClickListener(view -> { + if (feed.id != Feed.TYPE_TOGGLE_UNREAD && feed.id != Feed.TYPE_DIVIDER && feed.id != Feed.TYPE_GOBACK && feed.id != Feed.ALL_ARTICLES) { + m_list.showContextMenuForChild(view); + } + return true; + }); + holder.view.setOnClickListener(view -> { - if (feed.is_cat) { + if (feed.id == Feed.TYPE_GOBACK) { + m_activity.getSupportFragmentManager().popBackStack(); + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD || feed.id == Feed.TYPE_DIVIDER) { + // + } else { + + /* if (feed.is_cat) { if (feed.always_display_as_feed) { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), true); + //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), true); } else if (feed.id < 0) { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); + //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); } else { - m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread)); + //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread)); } } else { + m_activity.onFeedSelected(feed); + } */ + m_activity.onFeedSelected(feed); } }); @@ -577,20 +579,22 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL return VIEW_GOBACK; } else if (feed.id == Feed.TYPE_DIVIDER) { return VIEW_DIVIDER; - } else if (feed.id == Feed.TYPE_HEADER) { - return VIEW_HEADER; } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { return VIEW_TOGGLE_UNREAD; - } else if (feed.id == m_selectedFeedId) { + } else if (m_selectedFeed != null && feed.id == m_selectedFeed.id && feed.is_cat && m_selectedFeed.is_cat) { return VIEW_SELECTED; } else { return VIEW_NORMAL; } } + } - public void sortFeeds(List feeds) { - Comparator cmp; + protected void sortFeeds(List feeds, Feed feed) { + Comparator cmp; + if (feed.id == -1) { + cmp = new SpecialOrderComparator(); + } else { if (m_prefs.getBoolean("sort_feeds_by_unread", false)) { cmp = new FeedUnreadComparator(); } else { @@ -600,36 +604,54 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL cmp = new FeedTitleComparator(); } } + } - try { - feeds.sort(cmp); - } catch (IllegalArgumentException e) { - // sort order got changed in prefs or something - e.printStackTrace(); - } + try { + feeds.sort(cmp); + } catch (IllegalArgumentException e) { + // } } + protected int getIconForFeed(Feed feed) { + if (feed.id == Feed.TYPE_GOBACK) { + return R.drawable.baseline_arrow_back_24; + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { + return R.drawable.baseline_filter_alt_24; + } else if (feed.id == 0 && !feed.is_cat) { + return R.drawable.baseline_archive_24; + } else if (feed.id == -1 && !feed.is_cat) { + return R.drawable.baseline_star_24; + } else if (feed.id == -2 && !feed.is_cat) { + return R.drawable.rss; + } else if (feed.id == -3 && !feed.is_cat) { + return R.drawable.baseline_local_fire_department_24; + } else if (feed.id == -4 && !feed.is_cat) { + return R.drawable.baseline_inbox_24; + } else if (feed.id == -6 && !feed.is_cat) { + return R.drawable.baseline_restore_24; + } else if (feed.is_cat) { + return R.drawable.baseline_folder_open_24; + } else if (feed.id < -10 && !feed.is_cat) { + return R.drawable.baseline_label_24; + } else { + return R.drawable.rss; + } + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - refresh(); + // Can't access ViewModels from detached fragment (= backstack) + if (isAdded()) + refresh(); } - /* public Feed getFeedAtPosition(int position) { - try { - return (Feed) m_list.getItemAtPosition(position); - } catch (ArrayIndexOutOfBoundsException e) { - e.printStackTrace(); - } - - return null; - } */ - public void setSelectedFeedId(int feedId) { + public void setSelectedFeed(Feed feed) { if (m_adapter != null) { - m_selectedFeedId = feedId; + m_selectedFeed = feed; // TODO handle properly 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 9757f090..77ef1f8b 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 @@ -34,6 +34,7 @@ import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.CheckBox; import android.widget.EditText; @@ -134,7 +135,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { public void initialize(Feed feed, int activeArticleId, boolean compactMode) { m_feed = feed; - m_compactLayoutMode = compactMode; + m_compactLayoutMode = compactMode; m_activeArticleId = activeArticleId; } @@ -245,6 +246,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { getActivity().getMenuInflater().inflate(R.menu.context_headlines, menu); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + + Article article = m_adapter.getCurrentList().get(info.position); + + menu.setHeaderTitle(article.title); + menu.findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); menu.findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); @@ -454,10 +461,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } }); - if (m_activity.isSmallScreen() && m_feed != null) { - m_activity.setTitle(m_feed.title); - } - ArticleModel model = Application.getArticlesModel(); // this gets notified on network update 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 11265769..c92123dd 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 @@ -7,7 +7,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.MenuItem; import android.view.View; @@ -28,9 +27,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.gson.JsonElement; import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; -import org.fox.ttrss.types.FeedCategory; import java.util.Date; import java.util.HashMap; @@ -46,7 +43,6 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList protected long m_lastWidgetRefresh = 0; protected boolean m_feedIsSelected = false; - protected boolean m_userFeedSelected = false; private ActionBarDrawerToggle m_drawerToggle; private DrawerLayout m_drawerLayout; @@ -139,11 +135,11 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList // app shortcuts are not allowed to pass string extras if (feedTitle == null) - feedTitle = Feed.getSpecialFeedTitleById(MasterActivity.this, feedId); + feedTitle = getString(Feed.getSpecialFeedTitleId(feedId, isCat)); Feed tmpFeed = new Feed(feedId, feedTitle, isCat); - onFeedSelected(tmpFeed, false); + onFeedSelected(tmpFeed); } @Override @@ -159,21 +155,13 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList lr.execute(map); } - - //m_pullToRefreshAttacher.setRefreshing(true); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + RootCategoriesFragment fc = new RootCategoriesFragment(); - /* 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); - } */ - - FeedsFragment ff = new FeedsFragment(); - ff.initialize(-4, false); - - ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); + // it doesn't matter which feed is used here + fc.initialize(new Feed(-1, getString(R.string.cat_special), true), false); + ft.replace(R.id.feeds_fragment, fc, FRAG_FEEDS); // allow overriding feed to open on startup in non-shortcut mode, default to // open_on_startup prefs setting and not-category @@ -185,12 +173,12 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList String openFeedTitle = i.getStringExtra("feed_title"); if (openFeedTitle == null) - openFeedTitle = Feed.getSpecialFeedTitleById(this, openFeedId); + openFeedTitle = getString(Feed.getSpecialFeedTitleId(openFeedId, openFeedIsCat)); if (!shortcutMode && openFeedId != 0) { Log.d(TAG, "opening feed id: " + openFeedId); - onFeedSelected(new Feed(openFeedId, openFeedTitle, openFeedIsCat), false); + onFeedSelected(new Feed(openFeedId, openFeedTitle, openFeedIsCat)); } else if (m_drawerLayout != null) { m_drawerLayout.openDrawer(GravityCompat.START); } @@ -202,7 +190,6 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList } else { // savedInstanceState != null m_feedIsSelected = savedInstanceState.getBoolean("m_feedIsSelected"); - m_userFeedSelected = savedInstanceState.getBoolean("m_userFeedSelected"); if (m_drawerLayout != null && !m_feedIsSelected) { m_drawerLayout.openDrawer(GravityCompat.START); @@ -261,91 +248,96 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList if (m_menu != null && getSessionId() != null) { Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - Fragment cf = getSupportFragmentManager().findFragmentByTag(FRAG_CATS); HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - m_menu.setGroupVisible(R.id.menu_group_feeds, (ff != null && ff.isAdded()) || (cf != null && cf.isAdded())); + m_menu.setGroupVisible(R.id.menu_group_feeds, ff != null && ff.isAdded()); m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded()); } } - public void onFeedSelected(Feed feed) { - onFeedSelected(feed, true); - } - - public void onFeedSelected(final Feed feed, final boolean selectedByUser) { + public void onFeedSelected(Feed feed) { - FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + if (isSmallScreen()) + setTitle(feed.title); - if (ff != null && ff.isAdded()) { - ff.setSelectedFeedId(feed.id); - } + if (feed.is_cat && !feed.always_open_headlines) { + FragmentTransaction ft = getSupportFragmentManager() + .beginTransaction(); - if (m_drawerLayout != null) { - m_drawerLayout.closeDrawers(); - } + FeedsFragment ff = new FeedsFragment(); + ff.initialize(feed, true); + ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); - Application.getArticles().clear(); + ft.addToBackStack(null); + ft.commit(); - new Handler().postDelayed(() -> { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); + } else { + FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - HeadlinesFragment hf = new HeadlinesFragment(); - hf.initialize(feed); + if (ff != null) { + ff.setSelectedFeed(feed); + } - ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); + if (m_drawerLayout != null) { + m_drawerLayout.closeDrawers(); + } - ft.commit(); + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - m_feedIsSelected = true; - m_userFeedSelected = selectedByUser; + if (hf != null) { + hf.initialize(feed); + hf.refresh(false); + } else { + FragmentTransaction ft = getSupportFragmentManager() + .beginTransaction(); - }, 250); + hf = new HeadlinesFragment(); + hf.initialize(feed); - Date date = new Date(); + ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - if (date.getTime() - m_lastRefresh > 30*1000) { - m_lastRefresh = date.getTime(); - refresh(false); - } - } - - public void onCatSelected(FeedCategory cat, boolean openAsFeed) { - FeedCategoriesFragment fc = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS); - - //m_pullToRefreshAttacher.setRefreshing(true); - - if (!openAsFeed) { - - if (fc != null && fc.isAdded()) { - fc.setSelectedCategory(null); + ft.commit(); } - FragmentTransaction ft = getSupportFragmentManager() + /* FragmentTransaction ft = getSupportFragmentManager() .beginTransaction(); - FeedsFragment ff = new FeedsFragment(); - ff.initialize(cat.id, true); - ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); + HeadlinesFragment hf = new HeadlinesFragment(); + hf.initialize(feed); + hf.refresh(false); + + ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - ft.addToBackStack(null); ft.commit(); - } else { - - if (fc != null) { - fc.setSelectedCategory(cat); - } + m_feedIsSelected = true; - Feed feed = new Feed(cat.id, cat.title, true); - onFeedSelected(feed); + Date date = new Date(); + + if (date.getTime() - m_lastRefresh > 30 * 1000) { + m_lastRefresh = date.getTime(); + refresh(false); + } */ } } - public void onCatSelected(FeedCategory cat) { - onCatSelected(cat, m_prefs.getBoolean("browse_cats_like_feeds", false)); - } + /* public void onCatSelected(Feed cat) { + FeedCategoriesFragment fc = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS); + + if (fc != null) { + fc.setSelectedCategory(null); + } + + FragmentTransaction ft = getSupportFragmentManager() + .beginTransaction(); + + FeedsFragment ff = new FeedsFragment(); + ff.initialize(, true); + ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); + + ft.addToBackStack(null); + ft.commit(); + } */ @Override public void logout() { @@ -412,7 +404,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList @Override public void onBackPressed() { if (m_drawerLayout != null && !m_drawerLayout.isDrawerOpen(GravityCompat.START) && - (getSupportFragmentManager().getBackStackEntryCount() > 0 || m_userFeedSelected)) { + (getSupportFragmentManager().getBackStackEntryCount() > 0)) { m_drawerLayout.openDrawer(GravityCompat.START); } else { @@ -437,7 +429,6 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList super.onSaveInstanceState(out); out.putBoolean("m_feedIsSelected", m_feedIsSelected); - out.putBoolean("m_userFeedSelected", m_userFeedSelected); Application.getInstance().save(out); } 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 ffca776b..c946f405 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 @@ -8,7 +8,6 @@ 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; @@ -1231,12 +1230,6 @@ article.score = Integer.parseInt(edit.getText().toString()); } protected void refresh(boolean includeHeadlines) { - FeedCategoriesFragment cf = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS); - - if (cf != null) { - cf.refresh(); - } - FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); if (ff != null) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java new file mode 100755 index 00000000..cb91cc14 --- /dev/null +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java @@ -0,0 +1,134 @@ +package org.fox.ttrss; + +import android.annotation.SuppressLint; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.loader.content.Loader; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +import org.fox.ttrss.types.Feed; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class RootCategoriesFragment extends FeedsFragment { + private final String TAG = this.getClass().getSimpleName(); + + @Override + @NonNull + public Loader onCreateLoader(int id, Bundle args) { + HashMap params = new HashMap<>(); + params.put("op", "getCategories"); + params.put("sid", m_activity.getSessionId()); + + // this confusingly named option means "return top level categories only" + params.put("enable_nested", "true"); + + if (m_activity.getUnreadOnly()) + params.put("unread_only", "true"); + + return new ApiLoader(getContext(), params); + } + + @SuppressLint("DefaultLocale") + static class CatOrderComparator implements Comparator { + + @Override + public int compare(Feed a, Feed b) { + if (a.id >= 0 && b.id >= 0) + if (a.order_id != 0 && b.order_id != 0) + return a.order_id - b.order_id; + else + return a.title.toUpperCase().compareTo(b.title.toUpperCase()); + else + return a.id - b.id; + } + + } + + @Override + protected void sortFeeds(List feeds, Feed feed) { + try { + feeds.sort(new CatOrderComparator()); + } catch (IllegalArgumentException e) { + // + } + } + + @Override + protected int getIconForFeed(Feed feed) { + if (feed.id == Feed.TYPE_TOGGLE_UNREAD) + return super.getIconForFeed(feed); + else + return R.drawable.baseline_folder_open_24; + } + + @Override + public void onLoadFinished(Loader loader, JsonElement result) { + if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); + + if (result != null) { + try { + JsonArray content = result.getAsJsonArray(); + if (content != null) { + + Type listType = new TypeToken>() {}.getType(); + List feedsJson = new Gson().fromJson(content, listType); + + List feeds = new ArrayList<>(); + + sortFeeds(feedsJson, m_feed); + + // virtual cats implemented in getCategories since api level 1 + if (m_activity.getApiLevel() == 0) { + feeds.add(0, new Feed(-2, getString(R.string.cat_labels), false)); + feeds.add(1, new Feed(-1, getString(R.string.cat_special), false)); + feeds.add(new Feed(0, getString(R.string.cat_uncategorized), false)); + } + + if (feedsJson.stream().noneMatch(a -> a.id == -1)) + feeds.add(new Feed(-1, getString(R.string.cat_special), true)); + + for (Feed f : feedsJson) { + f.is_cat = true; + feeds.add(f); + } + + feeds.add(new Feed(Feed.TYPE_DIVIDER)); + feeds.add(new Feed(Feed.TYPE_TOGGLE_UNREAD, getString(R.string.unread_only), true)); + + m_adapter.submitList(feeds); + + return; + } + + } catch (Exception e) { + m_activity.toast(e.getMessage()); + } + } + + ApiLoader apiLoader = (ApiLoader) loader; + + if (apiLoader.getLastError() != null && apiLoader.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (apiLoader.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(true); + } else { + if (apiLoader.getLastErrorMessage() != null) { + m_activity.toast(getString(apiLoader.getErrorMessage()) + "\n" + apiLoader.getLastErrorMessage()); + } else { + m_activity.toast(apiLoader.getErrorMessage()); + } + } + } + } +} + diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java index 5432bad1..f8a2be8f 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java @@ -1,13 +1,11 @@ package org.fox.ttrss.types; -import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import org.fox.ttrss.R; public class Feed implements Comparable, Parcelable { - public static final int TYPE_HEADER = -10000; public static final int TYPE_GOBACK = -10001; public static final int TYPE_DIVIDER = -10002; public static final int TYPE_TOGGLE_UNREAD = -10003; @@ -21,8 +19,7 @@ public class Feed implements Comparable, Parcelable { public int last_updated; public int order_id; public boolean is_cat; - public boolean always_display_as_feed; - public String display_title; + transient public boolean always_open_headlines; public Feed(int id) { this.id = id; @@ -36,24 +33,61 @@ public class Feed implements Comparable, Parcelable { this.is_cat = is_cat; } - // TODO: maybe add special categories? (, bool isCat) - public static String getSpecialFeedTitleById(Context context, int feedId) { - switch (feedId) { - case -1: - return context.getString(R.string.feed_starred_articles); - case -2: - return context.getString(R.string.feed_published_articles); - case -3: - return context.getString(R.string.fresh_articles); - case -4: - return context.getString(R.string.feed_all_articles); - case -6: - return context.getString(R.string.feed_recently_read); - case 0: - return context.getString(R.string.feed_archived_articles); - default: - return null; - } + public Feed(Feed feed) { + id = feed.id; + feed_url = feed.feed_url; + title = feed.title; + unread = feed.unread; + has_icon = feed.has_icon; + cat_id = feed.cat_id; + last_updated = feed.last_updated; + order_id = feed.order_id; + is_cat = feed.is_cat; + always_open_headlines = feed.always_open_headlines; + } + + public static final int MARKED = -1; + public static final int PUBLISHED = -2; + public static final int FRESH = -3; + public static final int ALL_ARTICLES = -4; + public static final int RECENTLY_READ = -6; + public static final int ARCHIVED = 0; + + public static final int CAT_SPECIAL = -1; + public static final int CAT_LABELS = -2; + public static final int CAT_UNCATEGORIZED = 0; + + public static int getSpecialFeedTitleId(int feedId, boolean isCat) { + if (!isCat) + switch (feedId) { + case MARKED: + return R.string.feed_starred_articles; + case PUBLISHED: + return R.string.feed_published_articles; + case FRESH: + return R.string.fresh_articles; + case ALL_ARTICLES: + return R.string.feed_all_articles; + case RECENTLY_READ: + return R.string.feed_recently_read; + case ARCHIVED: + return R.string.feed_archived_articles; + case TYPE_TOGGLE_UNREAD: + return R.string.unread_only; + default: + throw new IllegalArgumentException("Invalid special feed id: " + feedId); + } + else + switch (feedId) { + case CAT_LABELS: + return R.string.cat_labels; + case CAT_SPECIAL: + return R.string.cat_special; + case CAT_UNCATEGORIZED: + return R.string.cat_uncategorized; + default: + throw new IllegalArgumentException("Invalid special category id: " + feedId); + } } public Feed(Parcel in) { @@ -98,8 +132,7 @@ public class Feed implements Comparable, Parcelable { out.writeInt(last_updated); out.writeInt(is_cat ? 1 : 0); out.writeInt(order_id); - out.writeInt(always_display_as_feed ? 1 : 0); - out.writeString(display_title); + out.writeInt(always_open_headlines ? 1 : 0); } public void readFromParcel(Parcel in) { @@ -112,8 +145,7 @@ public class Feed implements Comparable, Parcelable { last_updated = in.readInt(); is_cat = in.readInt() == 1; order_id = in.readInt(); - always_display_as_feed = in.readInt() == 1; - display_title = in.readString(); + always_open_headlines = in.readInt() == 1; } @SuppressWarnings("rawtypes") diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_label_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_label_24.xml new file mode 100644 index 00000000..02371e76 --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/baseline_label_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml deleted file mode 100755 index f4954259..00000000 --- a/org.fox.ttrss/src/main/res/layout/feeds_row_header.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/fragment_feeds.xml b/org.fox.ttrss/src/main/res/layout/fragment_feeds.xml index bf26e3fc..9f57dda2 100755 --- a/org.fox.ttrss/src/main/res/layout/fragment_feeds.xml +++ b/org.fox.ttrss/src/main/res/layout/fragment_feeds.xml @@ -1,24 +1,82 @@ - + android:layout_width="match_parent" + android:layout_height="wrap_content"> - - - + + - + android:layout_height="wrap_content"> + + + + + + + + + + + + + + + + + - \ No newline at end of file + + diff --git a/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml b/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml deleted file mode 100755 index 1ed85a5b..00000000 --- a/org.fox.ttrss/src/main/res/layout/fragment_feeds_recycler.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index 8d064587..27bf2a8a 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -43,14 +43,8 @@ android:key="sort_feeds_by_unread" android:title="@string/sort_feeds_by_unread" /> - - -- cgit v1.2.3-54-g00ecf From 0c2bb079818b80f95f379ef5fefcd07e5e9265ba Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 08:19:05 +0300 Subject: cleanup, bump okhttp timeouts --- .../src/main/java/org/fox/ttrss/ApiCommon.java | 11 ++-- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 46 +--------------- .../main/java/org/fox/ttrss/MasterActivity.java | 63 +++++----------------- .../java/org/fox/ttrss/types/FeedCategoryList.java | 1 + 4 files changed, 22 insertions(+), 99 deletions(-) (limited to 'org.fox.ttrss/src') 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 adc0881d..b6a9ff32 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 @@ -19,6 +19,7 @@ import com.google.gson.JsonParser; import java.io.IOException; import java.util.HashMap; import java.util.Locale; +import java.util.concurrent.TimeUnit; import okhttp3.Credentials; import okhttp3.MediaType; @@ -137,9 +138,13 @@ public class ApiCommon { Request request = requestBuilder.build(); - Response response = new OkHttpClient() - .newCall(request) - .execute(); + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build(); + + Response response = client.newCall(request).execute(); if (response.isSuccessful()) { String payloadReceived = response.body().string(); 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 f66758ee..353d965c 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 @@ -101,56 +101,13 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL List feeds = new ArrayList<>(); - // int catUnread = 0; - - /* for (Feed f : feeds) - if (f.id > -10 || m_catId != -4) { // skip labels for flat feedlist for now - if (m_activeCategory != null || f.id >= 0) { - m_feeds.add(f); - catUnread += f.unread; - } - - if (m_activeCategory != null && m_activeCategory.id == -1) - f.title = Feed.getSpecialFeedTitleById(m_activity, f.id); - } - - sortFeeds(); - - if (m_activeCategory == null) { - Feed feed = new Feed(-1, "Special", true); - feed.unread = catUnread; - - m_feeds.add(0, feed); - - } - - if (m_enableParentBtn && m_activeCategory != null && m_activeCategory.id >= 0 && !m_feeds.isEmpty()) { - Feed feed = new Feed(m_activeCategory.id, m_activeCategory.title, true); - feed.unread = catUnread; - feed.always_display_as_feed = true; - feed.display_title = getString(R.string.feed_all_articles); - - m_feeds.add(0, feed); - } - - m_adapter.notifyDataSetChanged(); */ - - // m_adapter.sortFeeds(feedsJson); - - //List feedsJsonFiltered = feedsJson.stream().filter(a -> a.id >= -10).collect(Collectors.toList()); - - if (m_feed.id == Feed.ALL_ARTICLES) - feedsJson = feedsJson.stream().filter(a -> a.id > -10).collect(Collectors.toList()); - sortFeeds(feedsJson, m_feed); - // feeds.add(new Feed(Feed.TYPE_HEADER)); - if (m_enableParentBtn) { feeds.add(0, new Feed(Feed.TYPE_GOBACK)); if (m_feed.id >= 0 && !feedsJson.isEmpty()) { - Feed feed = new Feed(m_feed.id, m_feed.title, true); + Feed feed = new Feed(m_feed.id, getString(R.string.feed_all_articles), true); feed.unread = feedsJson.stream().map(a -> a.unread).reduce(0, Integer::sum); feed.always_open_headlines = true; @@ -282,7 +239,6 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL return true; } else if (itemId == R.id.browse_feeds) { m_activity.onFeedSelected(feed); - //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); return true; } else if (itemId == R.id.unsubscribe_feed) { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) 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 c92123dd..a12c5c44 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 @@ -42,7 +42,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList protected long m_lastRefresh = 0; protected long m_lastWidgetRefresh = 0; - protected boolean m_feedIsSelected = false; + protected Feed m_activeFeed; private ActionBarDrawerToggle m_drawerToggle; private DrawerLayout m_drawerLayout; @@ -185,13 +185,11 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList ft.commit(); - m_feedIsSelected = true; - } else { // savedInstanceState != null - m_feedIsSelected = savedInstanceState.getBoolean("m_feedIsSelected"); + m_activeFeed = savedInstanceState.getParcelable("m_activeFeed"); - if (m_drawerLayout != null && !m_feedIsSelected) { + if (m_drawerLayout != null && m_activeFeed == null) { m_drawerLayout.openDrawer(GravityCompat.START); } } @@ -238,6 +236,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); + // Sync the toggle state after onRestoreInstanceState has occurred. if (m_drawerToggle != null) m_drawerToggle.syncState(); } @@ -257,6 +256,8 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList public void onFeedSelected(Feed feed) { + m_activeFeed = feed; + if (isSmallScreen()) setTitle(feed.title); @@ -298,46 +299,9 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList ft.commit(); } - - /* FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - - HeadlinesFragment hf = new HeadlinesFragment(); - hf.initialize(feed); - hf.refresh(false); - - ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - - ft.commit(); - - m_feedIsSelected = true; - - Date date = new Date(); - - if (date.getTime() - m_lastRefresh > 30 * 1000) { - m_lastRefresh = date.getTime(); - refresh(false); - } */ } } - - /* public void onCatSelected(Feed cat) { - FeedCategoriesFragment fc = (FeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS); - - if (fc != null) { - fc.setSelectedCategory(null); - } - - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - - FeedsFragment ff = new FeedsFragment(); - ff.initialize(, true); - ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); - ft.addToBackStack(null); - ft.commit(); - } */ @Override public void logout() { @@ -428,7 +392,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); - out.putBoolean("m_feedIsSelected", m_feedIsSelected); + out.putParcelable("m_activeFeed", m_activeFeed); Application.getInstance().save(out); } @@ -470,10 +434,6 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList intent.putExtra("searchQuery", hf.getSearchQuery()); intent.putExtra("openedArticleId", article.id); - // 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(); - startActivityForResult(intent, HEADLINES_REQUEST); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); } @@ -507,10 +467,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList } @Override - public void onHeadlinesLoaded(boolean appended) { - // TODO Auto-generated method stub - - } + public void onHeadlinesLoaded(boolean appended) { } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -549,4 +506,8 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList } + public Feed getActiveFeed() { + return m_activeFeed; + } + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java index ba8caaec..b5a3a935 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java @@ -5,6 +5,7 @@ import android.os.Parcelable; import java.util.ArrayList; +@Deprecated public class FeedCategoryList extends ArrayList implements Parcelable { public FeedCategoryList() { } -- cgit v1.2.3-54-g00ecf From 0cb76d17e78c6c7c42790c229bc08dbc95dcfe0e Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 08:19:40 +0300 Subject: bump feedcategory class as deprecated --- org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java | 1 + 1 file changed, 1 insertion(+) (limited to 'org.fox.ttrss/src') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java index c8193f94..e9b4b489 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java @@ -3,6 +3,7 @@ package org.fox.ttrss.types; import android.os.Parcel; import android.os.Parcelable; +@Deprecated public class FeedCategory implements Parcelable { public int id; public String title; -- cgit v1.2.3-54-g00ecf From 1be5611e66d8d4a152f1d431d7a7d76b2888e3f3 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:02:29 +0300 Subject: deal with issues related to feed selection --- .../main/java/org/fox/ttrss/CommonActivity.java | 2 +- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 99 ++++++++++++++-------- .../main/java/org/fox/ttrss/MasterActivity.java | 25 ++++-- .../java/org/fox/ttrss/RootCategoriesFragment.java | 3 +- 4 files changed, 82 insertions(+), 47 deletions(-) (limited to 'org.fox.ttrss/src') 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 d5d593ae..096eb75a 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 @@ -310,7 +310,7 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc setAppTheme(sharedPreferences); } - String[] filter = new String[] { "headline_mode", "widget_update_interval", + String[] filter = new String[] { "browse_cats_like_feeds", "headline_mode", "widget_update_interval", "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size", "force_phone_layout", "open_on_startup"}; 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 353d965c..65581da6 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 @@ -48,14 +48,14 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener, LoaderManager.LoaderCallbacks { private final String TAG = this.getClass().getSimpleName(); protected SharedPreferences m_prefs; protected MasterActivity m_activity; - protected Feed m_feed; + protected Feed m_rootFeed; private Feed m_selectedFeed; protected SwipeRefreshLayout m_swipeLayout; private boolean m_enableParentBtn = false; @@ -63,10 +63,10 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL private RecyclerView m_list; private RecyclerView.LayoutManager m_layoutManager; - public void initialize(@NonNull Feed feed, boolean enableParentBtn) { - Log.d(TAG, "initialize, feed=" + feed); + public void initialize(@NonNull Feed rootFeed, boolean enableParentBtn) { + Log.d(TAG, "initialize, feed=" + rootFeed); - m_feed = feed; + m_rootFeed = rootFeed; m_enableParentBtn = enableParentBtn; } @@ -78,10 +78,10 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL params.put("op", "getFeeds"); params.put("sid", m_activity.getSessionId()); params.put("include_nested", "true"); - params.put("cat_id", String.valueOf(m_feed.id)); + params.put("cat_id", String.valueOf(m_rootFeed.id)); /* except marked */ - if (m_activity.getUnreadOnly() && m_feed.id != -1) + if (m_activity.getUnreadOnly() && m_rootFeed.id != -1) params.put("unread_only", "true"); return new ApiLoader(getContext(), params); @@ -101,13 +101,13 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL List feeds = new ArrayList<>(); - sortFeeds(feedsJson, m_feed); + sortFeeds(feedsJson, m_rootFeed); if (m_enableParentBtn) { feeds.add(0, new Feed(Feed.TYPE_GOBACK)); - if (m_feed.id >= 0 && !feedsJson.isEmpty()) { - Feed feed = new Feed(m_feed.id, getString(R.string.feed_all_articles), true); + if (m_rootFeed.id >= 0 && !feedsJson.isEmpty()) { + Feed feed = new Feed(m_rootFeed.id, m_rootFeed.title, true); feed.unread = feedsJson.stream().map(a -> a.unread).reduce(0, Integer::sum); feed.always_open_headlines = true; @@ -232,10 +232,12 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL int itemId = item.getItemId(); if (itemId == R.id.browse_headlines) { - Feed forceFeed = new Feed(feed); - forceFeed.always_open_headlines = true; + Feed tmpFeed = new Feed(feed); - m_activity.onFeedSelected(forceFeed); + if (neverOpenHeadlines(feed)) + tmpFeed.always_open_headlines = true; + + m_activity.onFeedSelected(tmpFeed); return true; } else if (itemId == R.id.browse_feeds) { m_activity.onFeedSelected(feed); @@ -275,13 +277,14 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL menu.setHeaderTitle(feed.title); - if (!feed.is_cat) { + if (!feed.is_cat) menu.findItem(R.id.browse_feeds).setVisible(false); - } - if (feed.id <= 0) { + if (neverOpenHeadlines(feed)) + menu.findItem(R.id.browse_headlines).setVisible(false); + + if (feed.id <= 0 || feed.is_cat) menu.findItem(R.id.unsubscribe_feed).setVisible(false); - } super.onCreateContextMenu(menu, v, menuInfo); @@ -292,7 +295,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL super.onCreate(savedInstanceState); if (savedInstanceState != null) { - m_feed = savedInstanceState.getParcelable("m_feed"); + m_rootFeed = savedInstanceState.getParcelable("m_feed"); m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed"); m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn"); } @@ -302,7 +305,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL public void onSaveInstanceState(Bundle out) { super.onSaveInstanceState(out); - out.putParcelable("m_feed", m_feed); + out.putParcelable("m_feed", m_rootFeed); out.putParcelable("m_selectedFeed", m_selectedFeed); out.putBoolean("m_enableParentBtn", m_enableParentBtn); } @@ -377,6 +380,8 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL Log.d(TAG, "onResume"); + setSelectedFeed(m_activity.getActiveFeed()); + refresh(); m_activity.invalidateOptionsMenu(); @@ -503,26 +508,19 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL return true; }); + // default open handler (i.e. tap) holder.view.setOnClickListener(view -> { if (feed.id == Feed.TYPE_GOBACK) { m_activity.getSupportFragmentManager().popBackStack(); } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD || feed.id == Feed.TYPE_DIVIDER) { // } else { + Feed tmpFeed = new Feed(feed); - /* if (feed.is_cat) { - if (feed.always_display_as_feed) { - //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), true); - } else if (feed.id < 0) { - //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread), false); - } else { - //m_activity.onCatSelected(new FeedCategory(feed.id, feed.title, feed.unread)); - } - } else { - m_activity.onFeedSelected(feed); - } */ + if (neverOpenHeadlines(feed)) + tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); - m_activity.onFeedSelected(feed); + m_activity.onFeedSelected(tmpFeed); } }); } @@ -537,12 +535,32 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL return VIEW_DIVIDER; } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { return VIEW_TOGGLE_UNREAD; - } else if (m_selectedFeed != null && feed.id == m_selectedFeed.id && feed.is_cat && m_selectedFeed.is_cat) { + } else if (m_selectedFeed != null && feed.id == m_selectedFeed.id && feed.is_cat == m_selectedFeed.is_cat) { return VIEW_SELECTED; } else { return VIEW_NORMAL; } } + + /** TODO there has to be a better way -- Feed.equals()? */ + public int getPositionOf(Feed feed) { + List feeds = getCurrentList(); + + Optional maybeFeed = feeds.stream().filter(a -> a.id == feed.id && a.is_cat == feed.is_cat).findFirst(); + + if (maybeFeed.isPresent()) { + Feed foundFeed = maybeFeed.get(); + + return feeds.indexOf(foundFeed); + } + + return -1; + } + } + + /** we always show Labels and Special contents, regardless of the setting */ + private boolean neverOpenHeadlines(Feed feed) { + return feed.id != Feed.CAT_SPECIAL && feed.id != Feed.CAT_LABELS; } protected void sortFeeds(List feeds, Feed feed) { @@ -604,13 +622,24 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL refresh(); } - public void setSelectedFeed(Feed feed) { if (m_adapter != null) { + + int oldPosition = -1; + + if (m_selectedFeed != null) + oldPosition = m_adapter.getPositionOf(m_selectedFeed); + + int newPosition = m_adapter.getPositionOf(feed); + m_selectedFeed = feed; - // TODO handle properly - m_adapter.notifyDataSetChanged(); + if (oldPosition != -1) + m_adapter.notifyItemChanged(oldPosition); + + if (newPosition != -1) + m_adapter.notifyItemChanged(newPosition); + } } } 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 a12c5c44..bb750c01 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 @@ -256,11 +256,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList public void onFeedSelected(Feed feed) { - m_activeFeed = feed; - - if (isSmallScreen()) - setTitle(feed.title); - + // show subfolder of feeds below current level if (feed.is_cat && !feed.always_open_headlines) { FragmentTransaction ft = getSupportFragmentManager() .beginTransaction(); @@ -273,11 +269,9 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList ft.commit(); } else { - FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + // actualy open the feed (i.e. show headlines) - if (ff != null) { - ff.setSelectedFeed(feed); - } + setActiveFeed(feed); if (m_drawerLayout != null) { m_drawerLayout.closeDrawers(); @@ -510,4 +504,17 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList return m_activeFeed; } + public void setActiveFeed(Feed feed) { + m_activeFeed = feed; + + setTitle(feed.title); + + FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + + if (ff != null) { + ff.setSelectedFeed(feed); + } + + } + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java index cb91cc14..21381964 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; public class RootCategoriesFragment extends FeedsFragment { private final String TAG = this.getClass().getSimpleName(); @@ -86,7 +85,7 @@ public class RootCategoriesFragment extends FeedsFragment { List feeds = new ArrayList<>(); - sortFeeds(feedsJson, m_feed); + sortFeeds(feedsJson, m_rootFeed); // virtual cats implemented in getCategories since api level 1 if (m_activity.getApiLevel() == 0) { -- cgit v1.2.3-54-g00ecf From c86d0399ec9b9f51d99f15470a268442a2ac7405 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:16:26 +0300 Subject: rework getPositionOf to use intstream --- .../src/main/java/org/fox/ttrss/FeedsFragment.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'org.fox.ttrss/src') 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 65581da6..680a62a4 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 @@ -49,6 +49,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Optional; +import java.util.stream.IntStream; public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener, LoaderManager.LoaderCallbacks { @@ -234,7 +235,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL if (itemId == R.id.browse_headlines) { Feed tmpFeed = new Feed(feed); - if (neverOpenHeadlines(feed)) + if (neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) tmpFeed.always_open_headlines = true; m_activity.onFeedSelected(tmpFeed); @@ -517,7 +518,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } else { Feed tmpFeed = new Feed(feed); - if (neverOpenHeadlines(feed)) + if (neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); m_activity.onFeedSelected(tmpFeed); @@ -542,19 +543,18 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } } - /** TODO there has to be a better way -- Feed.equals()? */ public int getPositionOf(Feed feed) { List feeds = getCurrentList(); - Optional maybeFeed = feeds.stream().filter(a -> a.id == feed.id && a.is_cat == feed.is_cat).findFirst(); + return IntStream.range(0, feeds.size()) + .sequential() + .filter(i -> { + Feed f = feeds.get(i); - if (maybeFeed.isPresent()) { - Feed foundFeed = maybeFeed.get(); - - return feeds.indexOf(foundFeed); - } - - return -1; + return f.id == feed.id && f.is_cat == feed.is_cat; + }) + .findFirst() + .orElse(-1); } } -- cgit v1.2.3-54-g00ecf From c132719a4bce5253f306dd02131a2c64f9b84480 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:23:35 +0300 Subject: simplify previous --- org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'org.fox.ttrss/src') 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 680a62a4..15915876 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 @@ -235,7 +235,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL if (itemId == R.id.browse_headlines) { Feed tmpFeed = new Feed(feed); - if (neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) + if (!neverOpenHeadlines(feed)) tmpFeed.always_open_headlines = true; m_activity.onFeedSelected(tmpFeed); @@ -518,7 +518,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } else { Feed tmpFeed = new Feed(feed); - if (neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) + if (!neverOpenHeadlines(feed)) tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); m_activity.onFeedSelected(tmpFeed); @@ -560,7 +560,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL /** we always show Labels and Special contents, regardless of the setting */ private boolean neverOpenHeadlines(Feed feed) { - return feed.id != Feed.CAT_SPECIAL && feed.id != Feed.CAT_LABELS; + return feed.id == Feed.CAT_SPECIAL || feed.id == Feed.CAT_LABELS; } protected void sortFeeds(List feeds, Feed feed) { -- cgit v1.2.3-54-g00ecf From 45b948dfeda6e500c976db5fe51e839e539e7df5 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:33:41 +0300 Subject: clear headlines when feed is switched --- org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java | 2 +- org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'org.fox.ttrss/src') 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 15915876..f17e392c 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 @@ -518,7 +518,7 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL } else { Feed tmpFeed = new Feed(feed); - if (!neverOpenHeadlines(feed)) + if (!neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); m_activity.onFeedSelected(tmpFeed); 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 77ef1f8b..12450411 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 @@ -130,6 +130,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } public void initialize(Feed feed) { + + // clear loaded headlines before switching feed + if (feed != m_feed) + Application.getArticlesModel().update(new ArticleList()); + m_feed = feed; } -- cgit v1.2.3-54-g00ecf From 50ef8ede653dab9a3e089d9c45f4c145788cc0f2 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:40:06 +0300 Subject: cleaner implementation for shared preference change listener / unread toggling --- org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java | 2 +- org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'org.fox.ttrss/src') 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 096eb75a..d5d593ae 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 @@ -310,7 +310,7 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc setAppTheme(sharedPreferences); } - String[] filter = new String[] { "browse_cats_like_feeds", "headline_mode", "widget_update_interval", + String[] filter = new String[] { "headline_mode", "widget_update_interval", "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size", "force_phone_layout", "open_on_startup"}; 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 f17e392c..5ff66eff 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 @@ -498,7 +498,6 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL holder.rowSwitch.setOnCheckedChangeListener((button, isChecked) -> { m_activity.setUnreadOnly(isChecked); - refresh(); }); } @@ -618,8 +617,12 @@ public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeL String key) { // Can't access ViewModels from detached fragment (= backstack) - if (isAdded()) - refresh(); + if (isAdded()) { + String[] filter = new String[] { "sort_feeds_by_unread", "show_unread_only" }; + + if (Arrays.asList(filter).contains(key)) + refresh(); + } } public void setSelectedFeed(Feed feed) { -- cgit v1.2.3-54-g00ecf From ea14333cd8ac9b9d1216360ef75c9111f745afd5 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 May 2025 09:45:12 +0300 Subject: use separate icons for labels & special folders --- .../src/main/java/org/fox/ttrss/RootCategoriesFragment.java | 4 ++++ org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml | 5 +++++ org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml | 5 +++++ org.fox.ttrss/src/main/res/drawable/outline_label_24.xml | 5 +++++ 4 files changed, 19 insertions(+) create mode 100644 org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml create mode 100644 org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml create mode 100644 org.fox.ttrss/src/main/res/drawable/outline_label_24.xml (limited to 'org.fox.ttrss/src') diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java index 21381964..ffa7b027 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java @@ -67,6 +67,10 @@ public class RootCategoriesFragment extends FeedsFragment { protected int getIconForFeed(Feed feed) { if (feed.id == Feed.TYPE_TOGGLE_UNREAD) return super.getIconForFeed(feed); + else if (feed.id == Feed.CAT_LABELS) + return R.drawable.outline_label_24; + else if (feed.id == Feed.CAT_SPECIAL) + return R.drawable.baseline_folder_special_24; else return R.drawable.baseline_folder_open_24; } diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml new file mode 100644 index 00000000..d6017f8c --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml b/org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml new file mode 100644 index 00000000..3add00b7 --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/org.fox.ttrss/src/main/res/drawable/outline_label_24.xml b/org.fox.ttrss/src/main/res/drawable/outline_label_24.xml new file mode 100644 index 00000000..f05b7a20 --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/outline_label_24.xml @@ -0,0 +1,5 @@ + + + + + -- cgit v1.2.3-54-g00ecf