summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java11
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java2
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java2
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java79
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java2
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java1
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/FeedCategoriesFragment.java446
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java596
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsLoader.java19
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java1
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java38
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java156
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java7
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java138
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java1
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java80
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java1
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java1
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java95
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java58
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java41
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedList.java41
-rw-r--r--org.fox.ttrss/src/main/res/drawable/baseline_folder_special_24.xml5
-rw-r--r--org.fox.ttrss/src/main/res/drawable/baseline_label_24.xml5
-rw-r--r--org.fox.ttrss/src/main/res/drawable/outline_folder_special_24.xml5
-rw-r--r--org.fox.ttrss/src/main/res/drawable/outline_label_24.xml5
-rwxr-xr-xorg.fox.ttrss/src/main/res/layout/drawer_header.xml70
-rw-r--r--org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml (renamed from org.fox.ttrss/src/main/res/layout/drawer_divider.xml)0
-rwxr-xr-xorg.fox.ttrss/src/main/res/layout/feeds_row_goback.xml (renamed from org.fox.ttrss/src/main/res/layout/feeds_goback.xml)0
-rwxr-xr-xorg.fox.ttrss/src/main/res/layout/fragment_feeds.xml96
-rwxr-xr-xorg.fox.ttrss/src/main/res/xml/preferences.xml6
31 files changed, 777 insertions, 1231 deletions
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/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/ArticlePager.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java
index cf43f4f0..52f78cf0 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java
@@ -15,8 +15,8 @@ import androidx.viewpager2.widget.ViewPager2;
import org.fox.ttrss.types.Article;
import org.fox.ttrss.types.ArticleList;
import org.fox.ttrss.types.Feed;
-import org.fox.ttrss.util.DiffFragmentStateAdapter;
import org.fox.ttrss.util.ArticleDiffItemCallback;
+import org.fox.ttrss.util.DiffFragmentStateAdapter;
public class ArticlePager extends androidx.fragment.app.Fragment {
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 b37f7386..00000000
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java
+++ /dev/null
@@ -1,79 +0,0 @@
-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;
-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.drawer_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.drawer_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/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java
index bca2aa40..500eccb9 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java
@@ -19,7 +19,6 @@ import com.google.android.material.bottomappbar.BottomAppBar;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.fox.ttrss.types.Article;
-import org.fox.ttrss.types.ArticleList;
import org.fox.ttrss.types.Feed;
public class DetailActivity extends OnlineActivity implements HeadlinesEventListener {
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<JsonElement> {
- 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<JsonElement> onCreateLoader(int id, Bundle args) {
- final String sessionId = m_activity.getSessionId();
- final boolean unreadOnly = m_activity.getUnreadOnly();
-
- HashMap<String, String> 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<JsonElement> 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<List<FeedCategory>>() {}.getType();
- final List<FeedCategory> 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<FeedCategory> 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<JsonElement> loader) {
- Log.d(TAG, "onLoaderReset: " + loader);
-
- /*m_cats.clear();
- m_adapter.notifyDataSetChanged();*/
- }
-
- @SuppressLint("DefaultLocale")
- static class CatUnreadComparator implements Comparator<FeedCategory> {
- @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<FeedCategory> {
-
- @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<FeedCategory> {
-
- @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<FeedCategory> {
- private final ArrayList<FeedCategory> 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<FeedCategory> 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 c198343d..513eaeb7 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
@@ -3,13 +3,12 @@ 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;
@@ -17,75 +16,79 @@ 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.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 org.fox.ttrss.types.FeedList;
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;
+import java.util.stream.IntStream;
-public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener,
+public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener,
LoaderManager.LoaderCallbacks<JsonElement> {
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 SwipeRefreshLayout m_swipeLayout;
- boolean m_enableParentBtn = false;
- private ListView m_list;
-
- public void initialize(FeedCategory cat, boolean enableParentBtn) {
- m_activeCategory = cat;
- m_enableParentBtn = enableParentBtn;
+ protected SharedPreferences m_prefs;
+ protected MasterActivity m_activity;
+ protected Feed m_rootFeed;
+ private Feed m_selectedFeed;
+ protected SwipeRefreshLayout m_swipeLayout;
+ private boolean m_enableParentBtn = false;
+ protected FeedsAdapter m_adapter;
+ private RecyclerView m_list;
+ private RecyclerView.LayoutManager m_layoutManager;
+
+ public void initialize(@NonNull Feed rootFeed, boolean enableParentBtn) {
+ Log.d(TAG, "initialize, feed=" + rootFeed);
+
+ m_rootFeed = rootFeed;
+ m_enableParentBtn = enableParentBtn;
}
+ @NonNull
@Override
public Loader<JsonElement> 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);
+ if (m_swipeLayout != null)
+ m_swipeLayout.setRefreshing(true);
HashMap<String,String> 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(catId));
-
- if (unreadOnly)
- params.put("unread_only", "true");
+ params.put("cat_id", String.valueOf(m_rootFeed.id));
- return new FeedsLoader(getActivity().getApplicationContext(), params, catId);
+ return new ApiLoader(getContext(), params);
}
@Override
- public void onLoadFinished(Loader<JsonElement> loader, JsonElement result) {
+ public void onLoadFinished(@NonNull Loader<JsonElement> loader, JsonElement result) {
if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false);
if (result != null) {
@@ -94,64 +97,55 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
if (content != null) {
Type listType = new TypeToken<List<Feed>>() {}.getType();
- final List<Feed> feeds = new Gson().fromJson(content, listType);
-
- m_feeds.clear();
-
- int catUnread = 0;
+ List<Feed> feedsJson = new Gson().fromJson(content, listType);
+ List<Feed> feeds = new ArrayList<>();
- int catId = ((FeedsLoader) loader).getCatId();
+ if (m_activity.getUnreadOnly() && m_rootFeed.id != Feed.CAT_SPECIAL)
+ feedsJson = feedsJson.stream()
+ .filter(f -> f.unread > 0)
+ .collect(Collectors.toList());
- for (Feed f : feeds)
- if (f.id > -10 || catId != -4) { // skip labels for flat feedlist for now
- if (m_activeCategory != null || f.id >= 0) {
- m_feeds.add(f);
- catUnread += f.unread;
- }
+ sortFeeds(feedsJson, m_rootFeed);
- if (m_activeCategory != null && m_activeCategory.id == -1)
- f.title = Feed.getSpecialFeedTitleById(m_activity, f.id);
- }
-
- sortFeeds();
+ if (m_enableParentBtn) {
+ feeds.add(0, new Feed(Feed.TYPE_GOBACK));
- if (m_activeCategory == null) {
- Feed feed = new Feed(-1, "Special", true);
- feed.unread = catUnread;
+ if (m_rootFeed.id >= 0 && !feedsJson.isEmpty()) {
+ Feed feed = new Feed(m_rootFeed.id, m_rootFeed.title, true);
- m_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);
+ }
}
- 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);
+ feeds.addAll(feedsJson);
- m_feeds.add(0, feed);
- }
+ feeds.add(new Feed(Feed.TYPE_DIVIDER));
+ feeds.add(new Feed(Feed.TYPE_TOGGLE_UNREAD, getString(R.string.unread_only), true));
- m_adapter.notifyDataSetChanged();
+ m_adapter.submitList(feeds);
return;
}
} catch (Exception e) {
- e.printStackTrace();
+ m_activity.toast(e.getMessage());
}
}
- ApiLoader al = (ApiLoader) loader;
+ ApiLoader apiLoader = (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());
+ 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 +163,7 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
else
return a.title.toUpperCase().compareTo(b.title.toUpperCase());
}
-
+
}
@@ -193,6 +187,17 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
}
@SuppressLint("DefaultLocale")
+ static class SpecialOrderComparator implements Comparator<Feed> {
+ static List<Integer> 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<Feed> {
@Override
@@ -222,46 +227,44 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
@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 tmpFeed = new Feed(feed);
+
+ if (!neverOpenHeadlines(feed))
+ tmpFeed.always_open_headlines = true;
+
+ m_activity.onFeedSelected(tmpFeed);
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);
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);
}
@@ -269,22 +272,23 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
@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) {
+ 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);
@@ -295,13 +299,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
- ArrayList<Feed> list = savedInstanceState.getParcelableArrayList("m_feeds");
-
- m_feeds.clear();
- m_feeds.addAll(list);
-
+ m_rootFeed = savedInstanceState.getParcelable("m_feed");
m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed");
- m_activeCategory = savedInstanceState.getParcelable("m_activeCategory");
m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn");
}
}
@@ -310,9 +309,8 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
public void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
- out.putParcelableArrayList("m_feeds", m_feeds);
+ out.putParcelable("m_feed", m_rootFeed);
out.putParcelable("m_selectedFeed", m_selectedFeed);
- out.putParcelable("m_activeCategory", m_activeCategory);
out.putBoolean("m_enableParentBtn", m_enableParentBtn);
}
@@ -322,26 +320,45 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
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);
+ 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) {
- View layout = inflater.inflate(R.layout.feeds_goback, m_list, false);
+ m_adapter = new FeedsAdapter();
+ m_list.setAdapter(m_adapter);
- layout.setOnClickListener(view1 -> m_activity.getSupportFragmentManager().popBackStack());
+ TextView login = view.findViewById(R.id.drawer_header_login);
- m_list.addHeaderView(layout, null, false);
+ if (login != null) {
+ login.setText(m_prefs.getString("login", ""));
}
- m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, m_feeds);
- m_list.setAdapter(m_adapter);
- m_list.setOnItemClickListener(this);
+ TextView server = view.findViewById(R.id.drawer_header_server);
- registerForContextMenu(m_list);
+ if (server != null) {
+ try {
+ server.setText(new URL(m_prefs.getString("ttrss_url", "")).getHost());
+ } catch (MalformedURLException e) {
+ server.setText("");
+ }
+ }
+
+ View settingsBtn = view.findViewById(R.id.drawer_settings_btn);
+
+ if (settingsBtn != null) {
+ settingsBtn.setOnClickListener(v -> {
+ Intent intent = new Intent(getActivity(),
+ PreferencesActivity.class);
+
+ startActivityForResult(intent, 0);
+ });
+ }
return view;
}
@@ -359,46 +376,24 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
m_prefs.registerOnSharedPreferenceChangeListener(this);
m_activity = (MasterActivity)activity;
-
}
@Override
public void onResume() {
super.onResume();
- LoaderManager.getInstance(this).initLoader(Application.LOADER_FEEDS, null, this).forceLoad();
-
+ Log.d(TAG, "onResume");
+
+ setSelectedFeed(m_activity.getActiveFeed());
+
+ 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 (!isAdded())
+ return;
if (m_swipeLayout != null) {
m_swipeLayout.setRefreshing(true);
@@ -407,153 +402,250 @@ public class FeedsFragment extends BaseFeedlistFragment implements OnItemClickLi
LoaderManager.getInstance(this).restartLoader(Application.LOADER_FEEDS, null, this).forceLoad();
}
- private class FeedListAdapter extends ArrayAdapter<Feed> {
- private final ArrayList<Feed> items;
+ private class FeedViewHolder extends RecyclerView.ViewHolder {
- public static final int VIEW_NORMAL = 0;
- public static final int VIEW_SELECTED = 1;
+ private View view;
+ private ImageView icon;
+ private TextView title;
+ private TextView unreadCounter;
+ private MaterialSwitch rowSwitch;
- public static final int VIEW_COUNT = VIEW_SELECTED+1;
+ public FeedViewHolder(@NonNull View itemView) {
+ super(itemView);
- public FeedListAdapter(Context context, int textViewResourceId, ArrayList<Feed> items) {
- super(context, textViewResourceId, items);
- this.items = items;
+ 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);
}
+ }
- @Override
- public boolean isEmpty() {
- return !m_enableParentBtn && super.isEmpty();
- }
+ private class FeedDiffUtilItemCallback extends DiffUtil.ItemCallback<Feed> {
- @Override
- public int getViewTypeCount() {
- return VIEW_COUNT;
+ @Override
+ public boolean areItemsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) {
+ return oldItem.id == newItem.id;
}
@Override
- public int getItemViewType(int position) {
- Feed feed = items.get(position);
+ 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 (/*!m_activity.isSmallScreen() &&*/ m_selectedFeed != null && feed.id == m_selectedFeed.id) {
- return VIEW_SELECTED;
- } else {
- return VIEW_NORMAL;
+ protected class FeedsAdapter extends ListAdapter<Feed, FeedViewHolder> {
+ 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_TOGGLE_UNREAD = 4;
+ public static final int VIEW_DIVIDER = 5;
+
+ 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_DIVIDER:
+ layoutId = R.layout.feeds_row_divider;
+ break;
}
+
+ return new FeedViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false));
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = convertView;
+ public void onBindViewHolder(@NonNull FeedViewHolder holder, int position) {
+ Feed feed = getItem(position);
- Feed feed = items.get(position);
+ if (holder.icon != null) {
+ holder.icon.setImageResource(getIconForFeed(feed));
+ }
- if (v == null) {
- int layoutId = R.layout.feeds_row;
+ if (holder.title != null) {
+ holder.title.setText(feed.title);
- if (getItemViewType(position) == VIEW_SELECTED) {
- layoutId = R.layout.feeds_row_selected;
- }
+ if (feed.always_open_headlines || (!feed.is_cat && feed.id == -4)) {
+ holder.title.setTypeface(null, Typeface.BOLD);
+ } else {
+ holder.title.setTypeface(null, Typeface.NORMAL);
+ }
- LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- v = vi.inflate(layoutId, null);
+ }
+ if (holder.unreadCounter != null) {
+ holder.unreadCounter.setText(String.valueOf(feed.unread));
+ holder.unreadCounter.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE);
}
- ImageView icon = v.findViewById(R.id.icon);
-
- if (icon != null) {
- if (feed.id == 0 && !feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_archive_24);
- } else if (feed.id == -1 && !feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_star_24);
- } else if (feed.id == -2 && !feed.is_cat) {
- icon.setImageResource(R.drawable.rss);
- } else if (feed.id == -3 && !feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_local_fire_department_24);
- } else if (feed.id == -4 && !feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_inbox_24);
- } else if (feed.id == -6 && !feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_restore_24);
- } else if (feed.is_cat) {
- icon.setImageResource(R.drawable.baseline_folder_open_24);
- } else {
- icon.setImageResource(R.drawable.rss);
- }
+ // there's only one kind of row with checkbox atm
+ if (holder.rowSwitch != null) {
+ holder.rowSwitch.setChecked(m_activity.getUnreadOnly());
+
+ holder.rowSwitch.setOnCheckedChangeListener((button, isChecked) -> {
+ m_activity.setUnreadOnly(isChecked);
+ });
}
- TextView tt = v.findViewById(R.id.title);
+ 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;
+ });
+
+ // 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 (tt != null) {
- tt.setText(feed.display_title != null ? feed.display_title : feed.title);
+ if (!neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines)
+ tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false);
- if (feed.always_display_as_feed || (!feed.is_cat && feed.id == -4)) {
- tt.setTypeface(null, Typeface.BOLD);
- } else {
- tt.setTypeface(null, Typeface.NORMAL);
- }
+ m_activity.onFeedSelected(tmpFeed);
+ }
+ });
+ }
+ @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_TOGGLE_UNREAD) {
+ return VIEW_TOGGLE_UNREAD;
+ } else if (m_selectedFeed != null && feed.id == m_selectedFeed.id && feed.is_cat == m_selectedFeed.is_cat) {
+ return VIEW_SELECTED;
+ } else {
+ return VIEW_NORMAL;
}
+ }
- TextView tu = v.findViewById(R.id.unread_counter);
+ public int getPositionOf(Feed feed) {
+ List<Feed> feeds = getCurrentList();
- if (tu != null) {
- tu.setText(String.valueOf(feed.unread));
- tu.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE);
- }
+ return IntStream.range(0, feeds.size())
+ .sequential()
+ .filter(i -> {
+ Feed f = feeds.get(i);
- return v;
+ return f.id == feed.id && f.is_cat == feed.is_cat;
+ })
+ .findFirst()
+ .orElse(-1);
}
}
- public void sortFeeds() {
+ /** 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<Feed> feeds, Feed feed) {
Comparator<Feed> cmp;
-
- if (m_prefs.getBoolean("sort_feeds_by_unread", false)) {
- cmp = new FeedUnreadComparator();
+
+ if (feed.id == -1) {
+ cmp = new SpecialOrderComparator();
} else {
- if (m_activity.getApiLevel() >= 3) {
- cmp = new FeedOrderComparator();
+ if (m_prefs.getBoolean("sort_feeds_by_unread", false)) {
+ cmp = new FeedUnreadComparator();
} else {
- cmp = new FeedTitleComparator();
+ if (m_activity.getApiLevel() >= 3) {
+ cmp = new FeedOrderComparator();
+ } else {
+ cmp = new FeedTitleComparator();
+ }
}
}
-
+
try {
- m_feeds.sort(cmp);
+ 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
+ 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) {
- sortFeeds();
- }
+ // Can't access ViewModels from detached fragment (= backstack)
+ if (isAdded()) {
+ String[] filter = new String[] { "sort_feeds_by_unread", "show_unread_only" };
- public Feed getFeedAtPosition(int position) {
- try {
- return (Feed) m_list.getItemAtPosition(position);
- } catch (ArrayIndexOutOfBoundsException e) {
- e.printStackTrace();
+ if (Arrays.asList(filter).contains(key))
+ refresh();
}
-
- return null;
}
- public void setSelectedfeed(Feed feed) {
- m_selectedFeed = feed;
-
+ public void setSelectedFeed(Feed feed) {
if (m_adapter != null) {
- m_adapter.notifyDataSetChanged();
+
+ int oldPosition = -1;
+
+ if (m_selectedFeed != null)
+ oldPosition = m_adapter.getPositionOf(m_selectedFeed);
+
+ int newPosition = m_adapter.getPositionOf(feed);
+
+ m_selectedFeed = feed;
+
+ 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/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<String, String> 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/HeadlinesEventListener.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java
index 27e00bbf..14215a83 100644
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java
@@ -1,7 +1,6 @@
package org.fox.ttrss;
import org.fox.ttrss.types.Article;
-import org.fox.ttrss.types.ArticleList;
public interface HeadlinesEventListener {
void onArticleListSelectionChange();
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..a784ea8a 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;
@@ -129,12 +130,17 @@ 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;
}
public void initialize(Feed feed, int activeArticleId, boolean compactMode) {
m_feed = feed;
- m_compactLayoutMode = compactMode;
+ m_compactLayoutMode = compactMode;
m_activeArticleId = activeArticleId;
}
@@ -245,6 +251,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);
@@ -293,7 +305,11 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
m_swipeLayout = view.findViewById(R.id.headlines_swipe_container);
- m_swipeLayout.setOnRefreshListener(() -> refresh(false));
+ // see below re: viewpager2
+ if (!(m_activity instanceof DetailActivity))
+ m_swipeLayout.setOnRefreshListener(() -> refresh(false));
+ else
+ m_swipeLayout.setEnabled(false);
m_list = view.findViewById(R.id.headlines_list);
registerForContextMenu(m_list);
@@ -309,7 +325,9 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
refresh(false);
}
- if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) /*&& !m_prefs.getBoolean("headlines_mark_read_scroll", false) */) {
+ // we disable this because default implementationof viewpager2 does not support removing/reordering/changing items
+ // https://stackoverflow.com/questions/69368198/delete-item-in-android-viewpager2
+ if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) && !(m_activity instanceof DetailActivity)) {
ItemTouchHelper swipeHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
@@ -454,10 +472,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
@@ -555,6 +569,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
public void refresh(final boolean append) {
ArticleModel model = Application.getArticlesModel();
+ // we do not support non-append refreshes while in DetailActivity because of viewpager2
+ if (m_activity instanceof DetailActivity && !append)
+ return;
+
if (!append)
m_activeArticleId = -1;
@@ -993,12 +1011,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/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java
index 458f1f76..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
@@ -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;
@@ -45,8 +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 boolean m_userFeedSelected = false;
+ protected Feed m_activeFeed;
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,16 +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);
- }
+ // 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
@@ -180,26 +173,23 @@ 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);
}
ft.commit();
- m_feedIsSelected = true;
-
} else { // savedInstanceState != null
- m_feedIsSelected = savedInstanceState.getBoolean("m_feedIsSelected");
- m_userFeedSelected = savedInstanceState.getBoolean("m_userFeedSelected");
+ m_activeFeed = savedInstanceState.getParcelable("m_activeFeed");
- if (m_drawerLayout != null && !m_feedIsSelected) {
+ if (m_drawerLayout != null && m_activeFeed == null) {
m_drawerLayout.openDrawer(GravityCompat.START);
}
}
@@ -246,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();
}
@@ -256,91 +247,55 @@ 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) {
-
- FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS);
-
- if (ff != null && ff.isAdded()) {
- ff.setSelectedfeed(feed);
- }
-
- if (m_drawerLayout != null) {
- m_drawerLayout.closeDrawers();
- }
-
- Application.getArticles().clear();
-
- new Handler().postDelayed(() -> {
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
-
- HeadlinesFragment hf = new HeadlinesFragment();
- hf.initialize(feed);
-
- ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
-
- ft.commit();
-
- m_feedIsSelected = true;
- m_userFeedSelected = selectedByUser;
-
- }, 250);
-
- Date date = new Date();
-
- 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);
- }
+ public void onFeedSelected(Feed feed) {
+ // show subfolder of feeds below current level
+ if (feed.is_cat && !feed.always_open_headlines) {
FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
FeedsFragment ff = new FeedsFragment();
- ff.initialize(cat, true);
+ ff.initialize(feed, true);
ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS);
ft.addToBackStack(null);
ft.commit();
} else {
-
- if (fc != null) {
- fc.setSelectedCategory(cat);
+ // actualy open the feed (i.e. show headlines)
+
+ setActiveFeed(feed);
+
+ if (m_drawerLayout != null) {
+ m_drawerLayout.closeDrawers();
}
- Feed feed = new Feed(cat.id, cat.title, true);
- onFeedSelected(feed);
+ HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES);
+
+ if (hf != null) {
+ hf.initialize(feed);
+ hf.refresh(false);
+ } else {
+ FragmentTransaction ft = getSupportFragmentManager()
+ .beginTransaction();
+
+ hf = new HeadlinesFragment();
+ hf.initialize(feed);
+
+ ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES);
+
+ ft.commit();
+ }
}
}
-
- public void onCatSelected(FeedCategory cat) {
- onCatSelected(cat, m_prefs.getBoolean("browse_cats_like_feeds", false));
- }
+
@Override
public void logout() {
@@ -407,7 +362,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 {
@@ -431,8 +386,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList
public void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
- out.putBoolean("m_feedIsSelected", m_feedIsSelected);
- out.putBoolean("m_userFeedSelected", m_userFeedSelected);
+ out.putParcelable("m_activeFeed", m_activeFeed);
Application.getInstance().save(out);
}
@@ -474,10 +428,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);
}
@@ -511,10 +461,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) {
@@ -553,4 +500,21 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList
}
+ public Feed getActiveFeed() {
+ 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/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..b61a2736
--- /dev/null
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java
@@ -0,0 +1,138 @@
+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<JsonElement> onCreateLoader(int id, Bundle args) {
+ HashMap<String, String> 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");
+
+ return new ApiLoader(getContext(), params);
+ }
+
+ @SuppressLint("DefaultLocale")
+ static class CatOrderComparator implements Comparator<Feed> {
+
+ @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<Feed> 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 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;
+ }
+
+ @Override
+ public void onLoadFinished(@NonNull Loader<JsonElement> 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<List<Feed>>() {}.getType();
+ List<Feed> feedsJson = new Gson().fromJson(content, listType);
+
+ List<Feed> feeds = new ArrayList<>();
+
+ sortFeeds(feedsJson, m_rootFeed);
+
+ // 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), true));
+ feeds.add(1, new Feed(-1, getString(R.string.cat_special), true));
+ feeds.add(new Feed(0, getString(R.string.cat_uncategorized), true));
+ }
+
+ if (m_activity.getUnreadOnly())
+ feedsJson = feedsJson.stream()
+ .filter(f -> f.id == Feed.CAT_SPECIAL || f.unread > 0)
+ .collect(Collectors.toList());
+
+ feedsJson = feedsJson.stream()
+ .peek(f -> f.is_cat = true)
+ .collect(Collectors.toList());
+
+ feeds.addAll(feedsJson);
+
+ 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/share/CommonActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java
index 9c9187d9..7ece9e4f 100644
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java
@@ -1,7 +1,6 @@
package org.fox.ttrss.share;
import android.app.Activity;
-import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.widget.Toast;
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java
index 82e2d52d..b894f213 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java
@@ -1,5 +1,6 @@
package org.fox.ttrss.share;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -21,16 +22,15 @@ import com.google.gson.reflect.TypeToken;
import org.fox.ttrss.ApiCommon;
import org.fox.ttrss.ApiRequest;
import org.fox.ttrss.R;
-import org.fox.ttrss.types.FeedCategory;
-import org.fox.ttrss.types.FeedCategoryList;
+import org.fox.ttrss.types.Feed;
import java.lang.reflect.Type;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
public class SubscribeActivity extends CommonShareActivity {
private final String TAG = this.getClass().getSimpleName();
@@ -39,17 +39,17 @@ public class SubscribeActivity extends CommonShareActivity {
private Button m_catButton;
private CatListAdapter m_catAdapter;
private FeedListAdapter m_feedAdapter;
- private final FeedCategoryList m_cats = new FeedCategoryList();
+ private final List<Feed> m_cats = new ArrayList<>();
private final ArrayList<Map.Entry<String, JsonElement>> m_feeds = new ArrayList<>();
private ProgressBar m_progressBar;
private static final int REQ_CATS = 1;
private static final int REQ_POST = 2;
- static class CatTitleComparator implements Comparator<FeedCategory> {
+ static class TitleComparator implements Comparator<Feed> {
@Override
- public int compare(FeedCategory a, FeedCategory b) {
+ public int compare(Feed a, Feed b) {
if (a.id >= 0 && b.id >= 0)
return a.title.compareTo(b.title);
else
@@ -59,13 +59,10 @@ public class SubscribeActivity extends CommonShareActivity {
}
public void sortCats() {
- Comparator<FeedCategory> cmp = new CatTitleComparator();
-
- Collections.sort(m_cats, cmp);
- try {
+
+ if (m_catAdapter != null) {
+ m_cats.sort(new TitleComparator());
m_catAdapter.notifyDataSetChanged();
- } catch (NullPointerException e) {
- // adapter missing
}
}
@@ -82,30 +79,26 @@ public class SubscribeActivity extends CommonShareActivity {
if (savedInstanceState != null) {
urlValue = savedInstanceState.getString("url");
-
- ArrayList<FeedCategory> list = savedInstanceState.getParcelableArrayList("cats");
-
- m_cats.addAll(list);
}
setContentView(R.layout.activity_subscribe);
setSmallScreen(false);
- m_progressBar = (ProgressBar) findViewById(R.id.subscribe_progress);
- Spinner catList = (Spinner) findViewById(R.id.category_spinner);
+ m_progressBar = findViewById(R.id.subscribe_progress);
+ Spinner catList = findViewById(R.id.category_spinner);
- if (m_cats.isEmpty()) m_cats.add(new FeedCategory(0, "Uncategorized", 0));
+ if (m_cats.isEmpty()) m_cats.add(new Feed(0, "Uncategorized", true));
m_catAdapter = new CatListAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_cats);
catList.setAdapter(m_catAdapter);
- final Spinner feedList = (Spinner) findViewById(R.id.feed_spinner);
+ final Spinner feedList = findViewById(R.id.feed_spinner);
feedList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String feed = m_feedAdapter.getItemURL(position);
- EditText feedUrl = (EditText) findViewById(R.id.feed_url);
+ EditText feedUrl = findViewById(R.id.feed_url);
if (feed != null && feedUrl != null) {
feedUrl.setText(feed);
@@ -140,24 +133,22 @@ public class SubscribeActivity extends CommonShareActivity {
public void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
- EditText url = (EditText) findViewById(R.id.url);
+ EditText url = findViewById(R.id.url);
if (url != null) {
out.putString("url", url.getText().toString());
}
-
- out.putParcelableArrayList("cats", m_cats);
}
private void subscribeToFeed() {
m_postButton.setEnabled(false);
- ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) {
protected void onPostExecute(JsonElement result) {
m_progressBar.setVisibility(View.INVISIBLE);
- if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) {
+ if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) {
toast(getErrorMessage());
} else {
try {
@@ -166,7 +157,7 @@ public class SubscribeActivity extends CommonShareActivity {
try {
rc = result.getAsJsonObject().get("status").getAsJsonObject().get("code").getAsInt();
} catch (Exception e) {
- e.printStackTrace();
+ toast(e.getMessage());
}
switch (rc) {
@@ -212,8 +203,7 @@ public class SubscribeActivity extends CommonShareActivity {
}
} catch (Exception e) {
- toast(R.string.error_while_subscribing);
- e.printStackTrace();
+ toast(e.getMessage());
}
}
@@ -221,10 +211,10 @@ public class SubscribeActivity extends CommonShareActivity {
}
};
- Spinner catSpinner = (Spinner) findViewById(R.id.category_spinner);
+ Spinner catSpinner = findViewById(R.id.category_spinner);
- final FeedCategory cat = (FeedCategory) m_catAdapter.getCategory(catSpinner.getSelectedItemPosition());
- final EditText feedUrl = (EditText) findViewById(R.id.feed_url);
+ final Feed cat = m_catAdapter.getCategory(catSpinner.getSelectedItemPosition());
+ final EditText feedUrl = findViewById(R.id.feed_url);
if (feedUrl != null ) {
HashMap<String, String> map = new HashMap<>();
@@ -255,29 +245,25 @@ public class SubscribeActivity extends CommonShareActivity {
}
private void updateCats() {
- ApiRequest req = new ApiRequest(getApplicationContext()) {
+ @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) {
protected void onPostExecute(JsonElement result) {
m_progressBar.setVisibility(View.INVISIBLE);
- if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) {
+ if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) {
toast(getErrorMessage());
} else {
JsonArray content = result.getAsJsonArray();
if (content != null) {
- Type listType = new TypeToken<List<FeedCategory>>() {}.getType();
- final List<FeedCategory> cats = new Gson().fromJson(content, listType);
-
+ Type listType = new TypeToken<List<Feed>>() {}.getType();
+ final List<Feed> catsJson = new Gson().fromJson(content, listType);
+
m_cats.clear();
-
- for (FeedCategory c : cats) {
- if (c.id > 0)
- m_cats.add(c);
- }
-
+ m_cats.addAll(catsJson.stream().filter(f -> f.id > 0).collect(Collectors.toList()));
+
sortCats();
- m_cats.add(0, new FeedCategory(0, "Uncategorized", 0));
+ m_cats.add(0, new Feed(0, "Uncategorized", true));
m_catAdapter.notifyDataSetChanged();
@@ -316,10 +302,10 @@ public class SubscribeActivity extends CommonShareActivity {
}
private static class CatListAdapter extends ArrayAdapter<String> {
- private final List<FeedCategory> m_items;
+ private final List<Feed> m_items;
public CatListAdapter(Context context, int resource,
- List<FeedCategory> items) {
+ List<Feed> items) {
super(context, resource);
m_items = items;
@@ -330,7 +316,7 @@ public class SubscribeActivity extends CommonShareActivity {
return m_items.get(item).title;
}
- public FeedCategory getCategory(int item) {
+ public Feed getCategory(int item) {
try {
return m_items.get(item);
} catch (ArrayIndexOutOfBoundsException e) {
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 = "";
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java
index 6724acb0..8a8a763f 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/ArticleList.java
@@ -1,6 +1,5 @@
package org.fox.ttrss.types;
-import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
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..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,12 +1,15 @@
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<Feed>, Parcelable {
+ 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;
@@ -16,33 +19,75 @@ public class Feed implements Comparable<Feed>, 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;
+ this.title = "ID:" + id;
+ this.is_cat = false;
+ }
+
public Feed(int id, String title, boolean is_cat) {
this.id = id;
this.title = title;
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) {
@@ -87,8 +132,7 @@ public class Feed implements Comparable<Feed>, 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) {
@@ -101,8 +145,7 @@ public class Feed implements Comparable<Feed>, 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/java/org/fox/ttrss/types/FeedCategory.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java
deleted file mode 100644
index c8193f94..00000000
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.fox.ttrss.types;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class FeedCategory implements Parcelable {
- public int id;
- public String title;
- public int unread;
- public int order_id;
-
- public FeedCategory(Parcel in) {
- readFromParcel(in);
- }
-
- public FeedCategory(int id, String title, int unread) {
- this.id = id;
- this.title = title;
- this.unread = unread;
- this.order_id = 0;
- }
-
- public FeedCategory() {
-
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(id);
- out.writeString(title);
- out.writeInt(unread);
- out.writeInt(order_id);
- }
-
- public void readFromParcel(Parcel in) {
- id = in.readInt();
- title = in.readString();
- unread = in.readInt();
- order_id = in.readInt();
- }
-
- @SuppressWarnings("rawtypes")
- public static final Parcelable.Creator CREATOR =
- new Parcelable.Creator() {
- public FeedCategory createFromParcel(Parcel in) {
- return new FeedCategory(in);
- }
-
- public FeedCategory[] newArray(int size) {
- return new FeedCategory[size];
- }
- };
-}
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
deleted file mode 100644
index ba8caaec..00000000
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedCategoryList.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.fox.ttrss.types;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-
-public class FeedCategoryList extends ArrayList<FeedCategory> implements Parcelable {
-
- public FeedCategoryList() { }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeList(this);
- }
-
- public void readFromParcel(Parcel in) {
- in.readList(this, getClass().getClassLoader());
- }
-
- public FeedCategoryList(Parcel in) {
- readFromParcel(in);
- }
-
- @SuppressWarnings("rawtypes")
- public static final Parcelable.Creator CREATOR =
- new Parcelable.Creator() {
- public FeedCategoryList createFromParcel(Parcel in) {
- return new FeedCategoryList(in);
- }
-
- public FeedCategoryList[] newArray(int size) {
- return new FeedCategoryList[size];
- }
- };
- }
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedList.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedList.java
deleted file mode 100644
index 08164361..00000000
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/FeedList.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.fox.ttrss.types;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-
-public class FeedList extends ArrayList<Feed> implements Parcelable {
-
- public FeedList() { }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeList(this);
- }
-
- public void readFromParcel(Parcel in) {
- in.readList(this, getClass().getClassLoader());
- }
-
- public FeedList(Parcel in) {
- readFromParcel(in);
- }
-
- @SuppressWarnings("rawtypes")
- public static final Parcelable.Creator CREATOR =
- new Parcelable.Creator() {
- public FeedList createFromParcel(Parcel in) {
- return new FeedList(in);
- }
-
- public FeedList[] newArray(int size) {
- return new FeedList[size];
- }
- };
- }
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM17.94,17L15,15.28 12.06,17l0.78,-3.33 -2.59,-2.24 3.41,-0.29L15,8l1.34,3.14 3.41,0.29 -2.59,2.24 0.78,3.33z"/>
+
+</vector>
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z"/>
+
+</vector>
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,6h5.17l2,2L20,8v10zM13.08,14.04L12.39,17 15,15.47 17.61,17l-0.69,-2.96 2.3,-1.99 -3.03,-0.26L15,9l-1.19,2.79 -3.03,0.26z"/>
+
+</vector>
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 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16zM16,17H5V7h11l3.55,5L16,17z"/>
+
+</vector>
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 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="72dp"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:weightSum="1"
- android:clickable="false">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.google.android.material.button.MaterialButton
- style="?attr/materialIconButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleX="1.5"
- android:scaleY="1.5"
- app:icon="@drawable/baseline_settings_24"
- android:id="@+id/drawer_settings_btn"
- android:layout_gravity="center_vertical|end"
- android:layout_marginEnd="8dp"
- android:transitionName="SETTINGS_REVEAL"
- />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:id="@+id/linearLayout"
- android:layout_gravity="center_horizontal|bottom">
-
- <TextView
- android:id="@+id/drawer_header_login"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- tools:text="user"
- android:textAppearance="?attr/textAppearanceHeadlineSmall"
- android:textColor="?attr/colorOnSurface"/>
-
- <TextView
- android:id="@+id/drawer_header_server"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp"
- tools:text="example.org"
- android:textAppearance="?attr/textAppearanceTitleSmall"
- android:textColor="?attr/colorOnSurfaceVariant"/>
- </LinearLayout>
- </FrameLayout>
-
- <ProgressBar
- style="?android:attr/progressBarStyleHorizontal"
- android:visibility="invisible"
- android:indeterminate="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/feeds_loading_bar"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:layout_gravity="bottom" />
-</FrameLayout> \ No newline at end of file
diff --git a/org.fox.ttrss/src/main/res/layout/drawer_divider.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml
index 55aa5fdc..55aa5fdc 100644
--- a/org.fox.ttrss/src/main/res/layout/drawer_divider.xml
+++ b/org.fox.ttrss/src/main/res/layout/feeds_row_divider.xml
diff --git a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml b/org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml
index 75ba8fe1..75ba8fe1 100755
--- a/org.fox.ttrss/src/main/res/layout/feeds_goback.xml
+++ b/org.fox.ttrss/src/main/res/layout/feeds_row_goback.xml
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 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/feeds_fragment"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
- android:id="@+id/feeds_swipe_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ListView
- android:id="@+id/feeds"
- android:dividerHeight="4dp"
- android:divider="@null"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ <com.google.android.material.appbar.AppBarLayout
+ android:background="@null"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true">
- </ListView>
+ android:layout_height="wrap_content">
+
+ <com.google.android.material.button.MaterialButton
+ style="?attr/materialIconButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleX="1.5"
+ android:scaleY="1.5"
+ app:icon="@drawable/baseline_settings_24"
+ android:id="@+id/drawer_settings_btn"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginEnd="8dp"
+ android:transitionName="SETTINGS_REVEAL"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:id="@+id/linearLayout"
+ android:layout_gravity="center_horizontal|bottom">
+
+ <TextView
+ android:id="@+id/drawer_header_login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ tools:text="user"
+ android:textAppearance="?attr/textAppearanceHeadlineSmall"
+ android:textColor="?attr/colorOnSurface"/>
+
+ <TextView
+ android:id="@+id/drawer_header_server"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ tools:text="example.org"
+ android:textAppearance="?attr/textAppearanceTitleSmall"
+ android:textColor="?attr/colorOnSurfaceVariant"/>
+ </LinearLayout>
+ </FrameLayout>
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+ android:id="@+id/feeds_swipe_container"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <org.fox.ttrss.util.ContextMenuRecyclerView
+ android:id="@+id/feeds"
+ android:scrollbars="vertical"
+
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
-</RelativeLayout> \ No newline at end of file
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
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
@@ -44,13 +44,7 @@
android:title="@string/sort_feeds_by_unread" />
<SwitchPreferenceCompat
- android:defaultValue="true"
- android:key="enable_cats"
- android:title="@string/enable_cats" />
-
- <SwitchPreferenceCompat
android:defaultValue="false"
- android:dependency="enable_cats"
android:key="browse_cats_like_feeds"
android:summary="@string/browse_cats_like_feeds_summary"
android:title="@string/browse_cats_like_feeds" />