diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListContext.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListContext.java new file mode 100644 index 000000000..3291ff9da --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/ListContext.java @@ -0,0 +1,275 @@ +package fr.gaulupeau.apps.Poche.data; + +import android.database.DatabaseUtils; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import org.greenrobot.greendao.query.QueryBuilder; +import org.greenrobot.greendao.query.WhereCondition; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import fr.gaulupeau.apps.InThePoche.R; +import fr.gaulupeau.apps.Poche.App; +import fr.gaulupeau.apps.Poche.data.dao.ArticleDao; +import fr.gaulupeau.apps.Poche.data.dao.ArticleTagsJoinDao; +import fr.gaulupeau.apps.Poche.data.dao.FtsDao; +import fr.gaulupeau.apps.Poche.data.dao.TagDao; +import fr.gaulupeau.apps.Poche.data.dao.entities.Article; +import fr.gaulupeau.apps.Poche.data.dao.entities.ArticleTagsJoin; +import fr.gaulupeau.apps.Poche.data.dao.entities.Tag; +import fr.gaulupeau.apps.Poche.ui.Sortable; + +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.readBool; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.readBoolean; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.readEnum; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.readList; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.readString; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.writeBool; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.writeBoolean; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.writeEnum; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.writeList; +import static fr.gaulupeau.apps.Poche.service.ParcelableUtils.writeString; + +public class ListContext implements Parcelable { + + protected Boolean archived; + protected Boolean favorite; + + protected String searchQuery; + + protected String tagLabel; + protected boolean tagsCached; + protected boolean untagged; + protected List tagIds; + + protected Sortable.SortOrder sortOrder; + + public ListContext() {} + + public Boolean getArchived() { + return archived; + } + + public void setArchived(Boolean archived) { + this.archived = archived; + } + + public Boolean getFavorite() { + return favorite; + } + + public void setFavorite(Boolean favorite) { + this.favorite = favorite; + } + + public String getSearchQuery() { + return searchQuery; + } + + public boolean setSearchQuery(String searchQuery) { + if (TextUtils.equals(searchQuery, this.searchQuery)) return false; + + this.searchQuery = searchQuery; + return true; + } + + public String getTagLabel() { + return tagLabel; + } + + public boolean setTagLabel(String tagLabel) { + if (TextUtils.equals(tagLabel, this.tagLabel)) return false; + + this.tagLabel = tagLabel; + resetTagsCache(); + return true; + } + + protected boolean isUntagged(TagDao tagDao) { + cacheTags(tagDao); + return untagged; + } + + protected List getTagIds(TagDao tagDao) { + cacheTags(tagDao); + return tagIds; + } + + public Sortable.SortOrder getSortOrder() { + return sortOrder; + } + + public boolean setSortOrder(Sortable.SortOrder sortOrder) { + if (Objects.equals(sortOrder, this.sortOrder)) return false; + + this.sortOrder = sortOrder; + return true; + } + + public QueryBuilder applyForTags(QueryBuilder qb) { + if (!TextUtils.isEmpty(searchQuery)) { + qb.where(TagDao.Properties.Label.like("%" + searchQuery + "%")); + } + + Sortable.SortOrder sortOrder = this.sortOrder != null + ? this.sortOrder : Sortable.SortOrder.ASC; + switch (sortOrder) { + case ASC: + qb.orderAsc(TagDao.Properties.Label); + break; + + case DESC: + qb.orderDesc(TagDao.Properties.Label); + break; + + default: + throw new IllegalStateException("Sort order not implemented: " + sortOrder); + } + + return qb; + } + + public QueryBuilder
applyForArticles(QueryBuilder
qb, TagDao tagDao) { + return applyArticlesOrder(applyForArticlesWithoutOrder(qb, tagDao)); + } + + public QueryBuilder
applyForArticlesWithoutOrder( + QueryBuilder
qb, TagDao tagDao) { + if (isUntagged(tagDao)) { + qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " NOT IN (" + + "select " + ArticleTagsJoinDao.Properties.ArticleId.columnName + + " from " + ArticleTagsJoinDao.TABLENAME + + ")")); + } else { + List tagIds = getTagIds(tagDao); + if (tagIds != null && !tagIds.isEmpty()) { + // TODO: try subquery + qb.join(ArticleTagsJoin.class, ArticleTagsJoinDao.Properties.ArticleId) + .where(ArticleTagsJoinDao.Properties.TagId.in(tagIds)); + } + } + + if (archived != null) { + qb.where(ArticleDao.Properties.Archive.eq(archived)); + } + if (favorite != null) { + qb.where(ArticleDao.Properties.Favorite.eq(favorite)); + } + + if (!TextUtils.isEmpty(searchQuery)) { + qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " IN (" + + FtsDao.getQueryString() + DatabaseUtils.sqlEscapeString(searchQuery) + + ")")); + } + + return qb; + } + + public QueryBuilder
applyArticlesOrder(QueryBuilder
qb) { + Sortable.SortOrder sortOrder = this.sortOrder != null + ? this.sortOrder : Sortable.SortOrder.DESC; + switch (sortOrder) { + case ASC: + qb.orderAsc(ArticleDao.Properties.ArticleId); + break; + + case DESC: + qb.orderDesc(ArticleDao.Properties.ArticleId); + break; + + default: + throw new IllegalStateException("Sort order not implemented: " + getSortOrder()); + } + + return qb; + } + + public void resetCache() { + resetTagsCache(); + } + + protected void resetTagsCache() { + tagsCached = false; + untagged = false; + tagIds = null; + } + + protected void cacheTags(TagDao tagDao) { + if (tagsCached) return; + + if (App.getInstance().getString(R.string.untagged).equals(tagLabel)) { + untagged = true; + } else if (tagLabel != null) { + List tags = tagDao.queryBuilder() + .where(TagDao.Properties.Label.eq(tagLabel)) + .orderDesc(TagDao.Properties.Label) + .list(); + + tagIds = new ArrayList<>(tags.size()); + for (Tag t : tags) { + tagIds.add(t.getId()); + } + } + + tagsCached = true; + } + + // Parcelable implementation + + protected ListContext(Parcel parcel) { + this(); + readFromParcel(parcel); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + writeBoolean(archived, dest); + writeBoolean(favorite, dest); + + writeString(searchQuery, dest); + + writeString(tagLabel, dest); + writeBool(tagsCached, dest); + writeBool(untagged, dest); + writeList(dest, tagIds, Parcel::writeLong); + + writeEnum(sortOrder, dest); + } + + protected void readFromParcel(Parcel in) { + archived = readBoolean(in); + favorite = readBoolean(in); + + searchQuery = readString(in); + + tagLabel = readString(in); + tagsCached = readBool(in); + untagged = readBool(in); + tagIds = readList(in, Parcel::readLong); + + sortOrder = readEnum(Sortable.SortOrder.class, in); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public ListContext createFromParcel(Parcel in) { + return new ListContext(in); + } + + @Override + public ListContext[] newArray(int size) { + return new ListContext[size]; + } + }; + +} diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParcelableUtils.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParcelableUtils.java index 7ab93ecd6..89f6f556e 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParcelableUtils.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/ParcelableUtils.java @@ -2,6 +2,9 @@ import android.os.Parcel; +import java.util.ArrayList; +import java.util.List; + public class ParcelableUtils { public static void writeLong(Long value, Parcel out) { @@ -52,10 +55,18 @@ public static String readString(Parcel in) { return in.readString(); } + public static void writeBool(boolean value, Parcel out) { + out.writeByte((byte) (value ? 1 : 0)); + } + + public static boolean readBool(Parcel in) { + return in.readByte() != 0; + } + public static void writeBoolean(Boolean value, Parcel out) { out.writeByte((byte) (value == null ? 0 : 1)); - if (value != null) out.writeByte((byte) (value ? 1 : 0)); + if (value != null) writeBool(value, out); } public static Boolean readBoolean(Parcel in) { @@ -64,16 +75,47 @@ public static Boolean readBoolean(Parcel in) { return in.readByte() == 0 ? Boolean.FALSE : Boolean.TRUE; } - public static void writeEnum(Enum value, Parcel out) { + public static void writeEnum(Enum value, Parcel out) { out.writeByte((byte) (value == null ? 0 : 1)); if (value != null) out.writeInt(value.ordinal()); } - public static T readEnum(Class enumClass, Parcel in) { + public static > T readEnum(Class enumClass, Parcel in) { if (in.readByte() == 0) return null; return enumClass.getEnumConstants()[in.readInt()]; } + public interface Writer { + void write(Parcel parcel, T value); + } + + public interface Reader { + T read(Parcel parcel); + } + + public static void writeList(Parcel out, List list, Writer writer) { + if (list == null) { + writeInteger(null, out); + return; + } + + writeInteger(list.size(), out); + for (T item : list) { + writer.write(out, item); + } + } + + public static List readList(Parcel in, Reader reader) { + Integer size = readInteger(in); + if (size == null) return null; + + List list = new ArrayList<>(size); + for (int i = size; i > 0; i--) { + list.add(reader.read(in)); + } + return list; + } + } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java index 1bbaa895e..68c135b85 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ArticleListFragment.java @@ -2,8 +2,6 @@ import android.app.Activity; import android.content.Context; -import android.content.Intent; -import android.database.DatabaseUtils; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -17,10 +15,9 @@ import org.greenrobot.greendao.query.LazyList; import org.greenrobot.greendao.query.QueryBuilder; -import org.greenrobot.greendao.query.WhereCondition; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; import fr.gaulupeau.apps.InThePoche.R; @@ -28,13 +25,9 @@ import fr.gaulupeau.apps.Poche.data.DbConnection; import fr.gaulupeau.apps.Poche.data.ListAdapter; import fr.gaulupeau.apps.Poche.data.dao.ArticleDao; -import fr.gaulupeau.apps.Poche.data.dao.ArticleTagsJoinDao; import fr.gaulupeau.apps.Poche.data.dao.DaoSession; -import fr.gaulupeau.apps.Poche.data.dao.FtsDao; import fr.gaulupeau.apps.Poche.data.dao.TagDao; import fr.gaulupeau.apps.Poche.data.dao.entities.Article; -import fr.gaulupeau.apps.Poche.data.dao.entities.ArticleTagsJoin; -import fr.gaulupeau.apps.Poche.data.dao.entities.Tag; import static fr.gaulupeau.apps.Poche.data.ListTypes.LIST_TYPE_ARCHIVED; import static fr.gaulupeau.apps.Poche.data.ListTypes.LIST_TYPE_FAVORITES; @@ -55,9 +48,6 @@ public interface OnFragmentInteractionListener { private static final int PER_PAGE_LIMIT = 30; private int listType; - private String tagLabel; - private boolean untagged; - private List tagIDs; private OnFragmentInteractionListener host; @@ -81,21 +71,33 @@ public ArticleListFragment() {} @Override public void onCreate(Bundle savedInstanceState) { - Bundle arguments = getArguments(); - if (arguments != null) { - listType = arguments.getInt(LIST_TYPE_PARAM, LIST_TYPE_UNREAD); - tagLabel = arguments.getString(TAG_PARAM); - } + Bundle arguments = Objects.requireNonNull(getArguments()); + listType = arguments.getInt(LIST_TYPE_PARAM, LIST_TYPE_UNREAD); + + super.onCreate(savedInstanceState); Log.v(TAG, "Fragment " + listType + " onCreate()"); + switch (listType) { + case LIST_TYPE_ARCHIVED: + listContext.setArchived(true); + break; + + case LIST_TYPE_FAVORITES: + listContext.setFavorite(true); + break; + + default: + listContext.setArchived(false); + break; + } + listContext.setTagLabel(arguments.getString(TAG_PARAM)); + DaoSession daoSession = DbConnection.getSession(); articleDao = daoSession.getArticleDao(); tagDao = daoSession.getTagDao(); setHasOptionsMenu(true); - - super.onCreate(savedInstanceState); } @Override @@ -156,22 +158,7 @@ protected ListAdapter createListAdapter(List
list) { @Override protected void resetContent() { - untagged = false; - tagIDs = null; - - if (getString(R.string.untagged).equals(tagLabel)) { - untagged = true; - } else if (tagLabel != null) { - List tags = tagDao.queryBuilder() - .where(TagDao.Properties.Label.eq(tagLabel)) - .orderDesc(TagDao.Properties.Label) - .list(); - - tagIDs = new ArrayList<>(tags.size()); - for (Tag t : tags) { - tagIDs.add(t.getId()); - } - } + listContext.resetCache(); super.resetContent(); @@ -194,49 +181,7 @@ private QueryBuilder
getQueryBuilder() { QueryBuilder
qb = articleDao.queryBuilder() .where(ArticleDao.Properties.ArticleId.isNotNull()); - if (untagged) { - qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " NOT IN " - + "(select " + ArticleTagsJoinDao.Properties.ArticleId.columnName - + " from " + ArticleTagsJoinDao.TABLENAME + ")")); - } else if (tagIDs != null && !tagIDs.isEmpty()) { - // TODO: try subquery - qb.join(ArticleTagsJoin.class, ArticleTagsJoinDao.Properties.ArticleId) - .where(ArticleTagsJoinDao.Properties.TagId.in(tagIDs)); - } - - switch (listType) { - case LIST_TYPE_ARCHIVED: - qb.where(ArticleDao.Properties.Archive.eq(true)); - break; - - case LIST_TYPE_FAVORITES: - qb.where(ArticleDao.Properties.Favorite.eq(true)); - break; - - default: - qb.where(ArticleDao.Properties.Archive.eq(false)); - break; - } - - if (!TextUtils.isEmpty(searchQuery)) { - qb.where(new WhereCondition.PropertyCondition(ArticleDao.Properties.Id, " IN (" + - FtsDao.getQueryString() + DatabaseUtils.sqlEscapeString(searchQuery) + ")")); - } - - switch (sortOrder) { - case ASC: - qb.orderAsc(ArticleDao.Properties.ArticleId); - break; - - case DESC: - qb.orderDesc(ArticleDao.Properties.ArticleId); - break; - - default: - throw new IllegalStateException("Sort order not implemented: " + sortOrder); - } - - return qb; + return listContext.applyForArticles(qb, tagDao); } // removes articles from cache: necessary for DiffUtil to work @@ -274,26 +219,10 @@ private void openRandomArticle() { articles.close(); } - // TODO: include more info (order, search query, tag) private void openArticle(long id) { Activity activity = getActivity(); if (activity != null) { - Intent intent = new Intent(activity, ReadArticleActivity.class); - intent.putExtra(ReadArticleActivity.EXTRA_ID, id); - - switch (listType) { - case LIST_TYPE_FAVORITES: - intent.putExtra(ReadArticleActivity.EXTRA_LIST_FAVORITES, true); - break; - case LIST_TYPE_ARCHIVED: - intent.putExtra(ReadArticleActivity.EXTRA_LIST_ARCHIVED, true); - break; - default: - intent.putExtra(ReadArticleActivity.EXTRA_LIST_ARCHIVED, false); - break; - } - - startActivity(intent); + startActivity(ReadArticleActivity.getIntent(activity, id, listContext)); } } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/EditAddedArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/EditAddedArticleActivity.java index 3bfaa21e4..06c42c298 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/EditAddedArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/EditAddedArticleActivity.java @@ -206,9 +206,7 @@ private boolean canOpen() { } private void openArticle() { - Intent intent = new Intent(this, ReadArticleActivity.class); - intent.putExtra(ReadArticleActivity.EXTRA_ID, article.getId()); - startActivity(intent); + startActivity(ReadArticleActivity.getIntent(this, article.getId(), null)); finish(); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java index 0973d2eee..8dee56543 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java @@ -23,6 +23,7 @@ import androidx.annotation.IdRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -31,6 +32,7 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import com.google.android.material.navigation.NavigationView; import com.mikepenz.aboutlibraries.Libs; @@ -240,6 +242,17 @@ private void updateTime() { updateNavigationUI(currentFragmentType); } + getSupportFragmentManager().registerFragmentLifecycleCallbacks( + new FragmentManager.FragmentLifecycleCallbacks() { + @Override + public void onFragmentCreated(@NonNull FragmentManager fm, + @NonNull Fragment f, + @Nullable Bundle savedInstanceState) { + Log.d(TAG, "onFragmentCreated() fragment: " + f); + setParametersToFragment(f); + } + }, false); + EventBus.getDefault().register(this); } @@ -959,4 +972,11 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { return super.onKeyDown(keyCode, event); } + + @Override + public void onAttachFragment(@NonNull Fragment fragment) { + super.onAttachFragment(fragment); + + Log.d(TAG, "onAttachFragment() fragment: " + fragment); + } } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 741825fb9..a84e2cb73 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -1,6 +1,7 @@ package fr.gaulupeau.apps.Poche.ui; import android.annotation.SuppressLint; +import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.os.Build; @@ -49,9 +50,11 @@ import fr.gaulupeau.apps.InThePoche.R; import fr.gaulupeau.apps.Poche.App; import fr.gaulupeau.apps.Poche.data.DbConnection; +import fr.gaulupeau.apps.Poche.data.ListContext; import fr.gaulupeau.apps.Poche.data.Settings; import fr.gaulupeau.apps.Poche.data.StorageHelper; import fr.gaulupeau.apps.Poche.data.dao.ArticleDao; +import fr.gaulupeau.apps.Poche.data.dao.TagDao; import fr.gaulupeau.apps.Poche.data.dao.entities.Annotation; import fr.gaulupeau.apps.Poche.data.dao.entities.Article; import fr.gaulupeau.apps.Poche.data.dao.entities.Tag; @@ -67,9 +70,8 @@ public class ReadArticleActivity extends BaseActionBarActivity { - public static final String EXTRA_ID = "ReadArticleActivity.id"; - public static final String EXTRA_LIST_ARCHIVED = "ReadArticleActivity.archived"; - public static final String EXTRA_LIST_FAVORITES = "ReadArticleActivity.favorites"; + private static final String EXTRA_ID = "ReadArticleActivity.id"; + private static final String EXTRA_LIST_CONTEXT = "ReadArticleActivity.listContext"; private static final String TAG = ReadArticleActivity.class.getSimpleName(); @@ -102,14 +104,16 @@ public class ReadArticleActivity extends BaseActionBarActivity { ArticlesChangedEvent.ChangeType.UNARCHIVED, ArticlesChangedEvent.ChangeType.FAVORITED, ArticlesChangedEvent.ChangeType.UNFAVORITED, - ArticlesChangedEvent.ChangeType.CREATED_DATE_CHANGED); + ArticlesChangedEvent.ChangeType.CREATED_DATE_CHANGED, + ArticlesChangedEvent.ChangeType.TAG_SET_CHANGED, + ArticlesChangedEvent.ChangeType.TAGS_CHANGED_GLOBALLY); - private Boolean contextFavorites; - private Boolean contextArchived; + private ListContext listContext; private Settings settings; private ArticleDao articleDao; + private TagDao tagDao; private ArticleActionsHelper articleActionsHelper = new ArticleActionsHelper(); @@ -154,6 +158,15 @@ public class ReadArticleActivity extends BaseActionBarActivity { private boolean onPageFinishedCallPostponedUntilResume; private boolean loadingFinished; + public static Intent getIntent(Context context, long articleId, ListContext listContext) { + Intent intent = new Intent(context, ReadArticleActivity.class); + + intent.putExtra(EXTRA_ID, articleId); + if (listContext != null) intent.putExtra(EXTRA_LIST_CONTEXT, listContext); + + return intent; + } + // TODO: remove after updating to ~"androidx.appcompat:appcompat:1.2.0" // https://issuetracker.google.com/issues/141132133 // https://stackoverflow.com/q/58028821 @@ -192,14 +205,11 @@ public void onCreate(Bundle savedInstanceState) { long articleID = intent.getLongExtra(EXTRA_ID, -1); Log.d(TAG, "onCreate() articleId: " + articleID); - if (intent.hasExtra(EXTRA_LIST_FAVORITES)) { - contextFavorites = intent.getBooleanExtra(EXTRA_LIST_FAVORITES, false); - } - if (intent.hasExtra(EXTRA_LIST_ARCHIVED)) { - contextArchived = intent.getBooleanExtra(EXTRA_LIST_ARCHIVED, false); - } + listContext = intent.getParcelableExtra(EXTRA_LIST_CONTEXT); + if (listContext == null) listContext = new ListContext(); articleDao = DbConnection.getSession().getArticleDao(); + tagDao = DbConnection.getSession().getTagDao(); if (!loadArticle(articleID)) { Log.e(TAG, "onCreate() did not find article with ID: " + articleID); @@ -428,9 +438,11 @@ public void onArticlesChangedEvent(ArticlesChangedEvent event) { updatePrevNext = true; } else { EnumSet changes; - if (contextArchived != null) { - changes = contextArchived ? event.getArchiveFeedChanges() : event.getMainFeedChanges(); - } else if (contextFavorites != null && contextFavorites) { + // TODO: check logic + if (listContext.getArchived() != null) { + changes = listContext.getArchived() + ? event.getArchiveFeedChanges() : event.getMainFeedChanges(); + } else if (listContext.getFavorite() != null && listContext.getFavorite()) { changes = event.getFavoriteFeedChanges(); } else { changes = EnumSet.copyOf(event.getMainFeedChanges()); @@ -593,6 +605,8 @@ private void initPrevNextButtons() { } private void updatePrevNextButtons() { + listContext.resetCache(); + previousArticleID = getAdjacentArticle(true); nextArticleID = getAdjacentArticle(false); @@ -1120,11 +1134,8 @@ private void openArticle(Long id) { ttsFragment.onOpenNewArticle(); } - Intent intent = new Intent(this, ReadArticleActivity.class); + Intent intent = getIntent(this, id, listContext); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(ReadArticleActivity.EXTRA_ID, id); - if (contextFavorites != null) intent.putExtra(EXTRA_LIST_FAVORITES, contextFavorites); - if (contextArchived != null) intent.putExtra(EXTRA_LIST_ARCHIVED, contextArchived); startActivity(intent); } @@ -1303,14 +1314,19 @@ private Long getAdjacentArticle(boolean previous) { QueryBuilder
qb = articleDao.queryBuilder() .where(ArticleDao.Properties.ArticleId.isNotNull()); - if (previous) qb.where(ArticleDao.Properties.ArticleId.gt(article.getArticleId())); - else qb.where(ArticleDao.Properties.ArticleId.lt(article.getArticleId())); + listContext.applyForArticlesWithoutOrder(qb, tagDao); - if (contextFavorites != null) qb.where(ArticleDao.Properties.Favorite.eq(contextFavorites)); - if (contextArchived != null) qb.where(ArticleDao.Properties.Archive.eq(contextArchived)); + if (listContext.getSortOrder() == Sortable.SortOrder.ASC) { + previous = !previous; + } - if (previous) qb.orderAsc(ArticleDao.Properties.ArticleId); - else qb.orderDesc(ArticleDao.Properties.ArticleId); + if (previous) { + qb.where(ArticleDao.Properties.ArticleId.gt(article.getArticleId())); + qb.orderAsc(ArticleDao.Properties.ArticleId); + } else { + qb.where(ArticleDao.Properties.ArticleId.lt(article.getArticleId())); + qb.orderDesc(ArticleDao.Properties.ArticleId); + } List
l = qb.limit(1).list(); if (!l.isEmpty()) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java index 4bbf943c5..ee8556916 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/RecyclerViewListFragment.java @@ -1,7 +1,6 @@ package fr.gaulupeau.apps.Poche.ui; import android.os.Bundle; -import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -20,17 +19,16 @@ import java.util.List; import fr.gaulupeau.apps.InThePoche.R; +import fr.gaulupeau.apps.Poche.data.ListContext; public abstract class RecyclerViewListFragment> extends Fragment implements Sortable, Searchable { private static final String TAG = "RecyclerVLFragment"; - protected static final String STATE_SORT_ORDER = "sort_order"; - protected static final String STATE_SEARCH_QUERY = "search_query"; + protected static final String STATE_LIST_CONTEXT = "list_context"; - protected Sortable.SortOrder sortOrder; - protected String searchQuery; + protected ListContext listContext = new ListContext(); protected SwipeRefreshLayout refreshLayout; protected RecyclerView recyclerView; @@ -54,14 +52,9 @@ public void onCreate(Bundle savedInstanceState) { if (savedInstanceState != null) { Log.v(TAG, "onCreate() restoring state"); - if (sortOrder == null) { - sortOrder = Sortable.SortOrder.values()[savedInstanceState.getInt(STATE_SORT_ORDER)]; - } - if (searchQuery == null) { - searchQuery = savedInstanceState.getString(STATE_SEARCH_QUERY); - } + listContext = savedInstanceState.getParcelable(STATE_LIST_CONTEXT); + if (listContext == null) listContext = new ListContext(); } - if (sortOrder == null) sortOrder = Sortable.SortOrder.DESC; itemList = new ArrayList<>(); @@ -122,24 +115,17 @@ public void onSaveInstanceState(@NonNull Bundle outState) { Log.v(TAG, "onSaveInstanceState()"); - if (sortOrder != null) outState.putInt(STATE_SORT_ORDER, sortOrder.ordinal()); - if (searchQuery != null) outState.putString(STATE_SEARCH_QUERY, searchQuery); + outState.putParcelable(STATE_LIST_CONTEXT, listContext); } @Override public void setSortOrder(Sortable.SortOrder sortOrder) { - Sortable.SortOrder oldSortOrder = this.sortOrder; - this.sortOrder = sortOrder; - - if (sortOrder != oldSortOrder) invalidateList(); + if (listContext.setSortOrder(sortOrder)) invalidateList(); } @Override public void setSearchQuery(String searchQuery) { - String oldSearchQuery = this.searchQuery; - this.searchQuery = searchQuery; - - if (!TextUtils.equals(oldSearchQuery, searchQuery)) invalidateList(); + if (listContext.setSearchQuery(searchQuery)) invalidateList(); } public void invalidateList() { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java index b6c187271..ea309c3cc 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/TagListFragment.java @@ -37,9 +37,9 @@ public TagListFragment() {} @Override public void onCreate(Bundle savedInstanceState) { - tagDao = DbConnection.getSession().getTagDao(); - super.onCreate(savedInstanceState); + + tagDao = DbConnection.getSession().getTagDao(); } @Override @@ -78,7 +78,7 @@ protected List getItems(int page) { List tags = detachObjects(qb.list()); - if (page == 0 && TextUtils.isEmpty(searchQuery)) { + if (page == 0 && TextUtils.isEmpty(listContext.getSearchQuery())) { tags.add(0, new Tag(null, null, getString(R.string.untagged))); } @@ -86,26 +86,7 @@ protected List getItems(int page) { } private QueryBuilder getQueryBuilder() { - QueryBuilder qb = tagDao.queryBuilder(); - - if (!TextUtils.isEmpty(searchQuery)) { - qb.where(TagDao.Properties.Label.like("%" + searchQuery + "%")); - } - - switch (sortOrder) { - case ASC: - qb.orderAsc(TagDao.Properties.Label); - break; - - case DESC: - qb.orderDesc(TagDao.Properties.Label); - break; - - default: - throw new IllegalStateException("Sort order not implemented: " + sortOrder); - } - - return qb; + return listContext.applyForTags(tagDao.queryBuilder()); } // removes tags from cache: necessary for DiffUtil to work