#1812 Scroll the stories list to the last user read story once reading session ends

This commit is contained in:
sictiru 2023-09-26 18:07:18 -07:00
parent ae720e9947
commit bea1140ea2
7 changed files with 107 additions and 14 deletions

View file

@ -21,7 +21,7 @@ public class AllStoriesItemsList extends ItemsList {
setIntent(intent); setIntent(intent);
if (getIntent().getBooleanExtra(EXTRA_WIDGET_STORY, false)) { if (getIntent().getBooleanExtra(EXTRA_WIDGET_STORY, false)) {
String hash = (String) getIntent().getSerializableExtra(EXTRA_STORY_HASH); String hash = (String) getIntent().getSerializableExtra(EXTRA_STORY_HASH);
UIUtils.startReadingActivity(fs, hash, this); UIUtils.startReadingActivity(this, fs, hash, readingActivityLaunch);
} }
} }

View file

@ -4,8 +4,13 @@ import static com.newsblur.service.NBSyncReceiver.UPDATE_REBUILD;
import static com.newsblur.service.NBSyncReceiver.UPDATE_STATUS; import static com.newsblur.service.NBSyncReceiver.UPDATE_STATUS;
import static com.newsblur.service.NBSyncReceiver.UPDATE_STORY; import static com.newsblur.service.NBSyncReceiver.UPDATE_STORY;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
@ -29,6 +34,7 @@ import com.newsblur.service.NBSyncService;
import com.newsblur.util.AppConstants; import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedSet; import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils; import com.newsblur.util.FeedUtils;
import com.newsblur.util.Log;
import com.newsblur.util.ReadingActionListener; import com.newsblur.util.ReadingActionListener;
import com.newsblur.util.PrefsUtils; import com.newsblur.util.PrefsUtils;
import com.newsblur.util.Session; import com.newsblur.util.Session;
@ -65,8 +71,13 @@ public abstract class ItemsList extends NbActivity implements ReadingActionListe
private ItemListContextMenuDelegate contextMenuDelegate; private ItemListContextMenuDelegate contextMenuDelegate;
@Nullable @Nullable
private SessionDataSource sessionDataSource; private SessionDataSource sessionDataSource;
@Override @NonNull
protected ActivityResultLauncher<Intent> readingActivityLaunch = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), this::handleReadingActivityResult
);
@Override
protected void onCreate(Bundle bundle) { protected void onCreate(Bundle bundle) {
Trace.beginSection("ItemsListOnCreate"); Trace.beginSection("ItemsListOnCreate");
super.onCreate(bundle); super.onCreate(bundle);
@ -84,11 +95,11 @@ public abstract class ItemsList extends NbActivity implements ReadingActionListe
feedUtils.prepareReadingSession(this, fs, false); feedUtils.prepareReadingSession(this, fs, false);
if (getIntent().getBooleanExtra(EXTRA_WIDGET_STORY, false)) { if (getIntent().getBooleanExtra(EXTRA_WIDGET_STORY, false)) {
String hash = (String) getIntent().getSerializableExtra(EXTRA_STORY_HASH); String hash = (String) getIntent().getSerializableExtra(EXTRA_STORY_HASH);
UIUtils.startReadingActivity(fs, hash, this); UIUtils.startReadingActivity(this, fs, hash, readingActivityLaunch);
} else if (PrefsUtils.isAutoOpenFirstUnread(this)) { } else if (PrefsUtils.isAutoOpenFirstUnread(this)) {
StateFilter intelState = PrefsUtils.getStateFilter(this); StateFilter intelState = PrefsUtils.getStateFilter(this);
if (dbHelper.getUnreadCount(fs, intelState) > 0) { if (dbHelper.getUnreadCount(fs, intelState) > 0) {
UIUtils.startReadingActivity(fs, Reading.FIND_FIRST_UNREAD, this); UIUtils.startReadingActivity(this, fs, Reading.FIND_FIRST_UNREAD, readingActivityLaunch);
} }
} }
@ -285,6 +296,20 @@ public abstract class ItemsList extends NbActivity implements ReadingActionListe
itemSetFragment.scrollToTop(); itemSetFragment.scrollToTop();
} }
public void startReadingActivity(FeedSet feedSet, String storyHash) {
UIUtils.startReadingActivity(this, feedSet, storyHash, readingActivityLaunch);
}
private void handleReadingActivityResult(ActivityResult result) {
if (result.getData() != null) {
int lastReadingPosition = result.getData().getIntExtra(Reading.LAST_READING_POS, -1);
if (lastReadingPosition > 1) {
Log.d(this.getClass().getName(), "Scrolling to last reading position " + lastReadingPosition);
itemSetFragment.scrollToPosition(lastReadingPosition);
}
}
}
@Override @Override
public void finish() { public void finish() {
super.finish(); super.finish();

View file

@ -1,5 +1,6 @@
package com.newsblur.activity package com.newsblur.activity
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
@ -10,6 +11,7 @@ import android.view.KeyEvent
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -32,8 +34,20 @@ import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_REBUILD
import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_STATUS import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_STATUS
import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_STORY import com.newsblur.service.NBSyncReceiver.Companion.UPDATE_STORY
import com.newsblur.service.NBSyncService import com.newsblur.service.NBSyncService
import com.newsblur.util.* import com.newsblur.util.AppConstants
import com.newsblur.util.CursorFilters
import com.newsblur.util.DefaultFeedView
import com.newsblur.util.FeedSet
import com.newsblur.util.FeedUtils
import com.newsblur.util.ImageLoader
import com.newsblur.util.MarkStoryReadBehavior
import com.newsblur.util.PrefConstants.ThemeValue import com.newsblur.util.PrefConstants.ThemeValue
import com.newsblur.util.PrefsUtils
import com.newsblur.util.StateFilter
import com.newsblur.util.UIUtils
import com.newsblur.util.ViewUtils
import com.newsblur.util.VolumeKeyNavigation
import com.newsblur.util.executeAsyncTask
import com.newsblur.view.ReadingScrollView.ScrollChangeListener import com.newsblur.view.ReadingScrollView.ScrollChangeListener
import com.newsblur.viewModel.StoriesViewModel import com.newsblur.viewModel.StoriesViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -42,7 +56,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Runnable
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -142,6 +155,7 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
setupViews() setupViews()
setupListeners() setupListeners()
setupObservers() setupObservers()
setupOnBackPressed()
getActiveStoriesCursor(this, true) getActiveStoriesCursor(this, true)
} }
@ -226,6 +240,17 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
} }
} }
/**
* Overrides on back pressed to use overridden [Reading.finish] method
*/
private fun setupOnBackPressed() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
finish()
}
})
}
private fun getActiveStoriesCursor(context: Context, finishOnInvalidFs: Boolean = false) { private fun getActiveStoriesCursor(context: Context, finishOnInvalidFs: Boolean = false) {
fs?.let { fs?.let {
val cursorFilters = CursorFilters(context, it) val cursorFilters = CursorFilters(context, it)
@ -843,6 +868,20 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
} }
} }
/**
* Overrides the [Reading] finish method and
* passes back the last read item position from the pager
*/
override fun finish() {
setResult(Activity.RESULT_OK, Intent().apply {
pager?.currentItem?.let { position ->
com.newsblur.util.Log.d(this@Reading.javaClass.name, "Finish reading at position $position")
putExtra(LAST_READING_POS, position)
}
})
super.finish()
}
companion object { companion object {
const val EXTRA_FEEDSET = "feed_set" const val EXTRA_FEEDSET = "feed_set"
const val EXTRA_STORY_HASH = "story_hash" const val EXTRA_STORY_HASH = "story_hash"
@ -857,5 +896,7 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
/** The minimum screen width (in DP) needed to show all the overlay controls. */ /** The minimum screen width (in DP) needed to show all the overlay controls. */
private const val OVERLAY_MIN_WIDTH_DP = 355 private const val OVERLAY_MIN_WIDTH_DP = 355
const val LAST_READING_POS = "last_reading_pos"
} }
} }

View file

@ -80,6 +80,7 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private final ExecutorService executorService; private final ExecutorService executorService;
private final NbActivity context; private final NbActivity context;
private final ItemSetFragment fragment; private final ItemSetFragment fragment;
private final OnStoryClickListener listener;
private FeedSet fs; private FeedSet fs;
private StoryListStyle listStyle; private StoryListStyle listStyle;
private boolean ignoreReadStatus; private boolean ignoreReadStatus;
@ -96,7 +97,8 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
StoryListStyle listStyle, StoryListStyle listStyle,
ImageLoader iconLoader, ImageLoader iconLoader,
ImageLoader thumbnailLoader, ImageLoader thumbnailLoader,
FeedUtils feedUtils) { FeedUtils feedUtils,
OnStoryClickListener listener) {
this.context = context; this.context = context;
this.fragment = fragment; this.fragment = fragment;
this.fs = fs; this.fs = fs;
@ -104,6 +106,7 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
this.iconLoader = iconLoader; this.iconLoader = iconLoader;
this.thumbnailLoader = thumbnailLoader; this.thumbnailLoader = thumbnailLoader;
this.feedUtils = feedUtils; this.feedUtils = feedUtils;
this.listener = listener;
if (fs.isGlobalShared()) {ignoreReadStatus = false; ignoreIntel = true; singleFeed = false;} if (fs.isGlobalShared()) {ignoreReadStatus = false; ignoreIntel = true; singleFeed = false;}
if (fs.isAllSocial()) {ignoreReadStatus = false; ignoreIntel = false; singleFeed = false;} if (fs.isAllSocial()) {ignoreReadStatus = false; ignoreIntel = false; singleFeed = false;}
@ -392,7 +395,7 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
return; return;
} }
if (gestureL2R || gestureR2L) return; if (gestureL2R || gestureR2L) return;
UIUtils.startReadingActivity(fs, story.storyHash, context); listener.onStoryClicked(fs, story.storyHash);
} }
@Override @Override
@ -817,4 +820,8 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
notifyItemRangeChanged(0, getItemCount()); notifyItemRangeChanged(0, getItemCount());
} }
public interface OnStoryClickListener {
void onStoryClicked(FeedSet feedSet, String storyHash);
}
} }

View file

@ -185,7 +185,7 @@ public class ItemSetFragment extends NbFragment {
} }
}); });
adapter = new StoryViewAdapter(((NbActivity) getActivity()), this, getFeedSet(), listStyle, iconLoader, thumbnailLoader, feedUtils); adapter = new StoryViewAdapter(((NbActivity) getActivity()), this, getFeedSet(), listStyle, iconLoader, thumbnailLoader, feedUtils, getOnStoryClickListener());
adapter.addFooterView(footerView); adapter.addFooterView(footerView);
adapter.addFooterView(fleuronBinding.getRoot()); adapter.addFooterView(fleuronBinding.getRoot());
binding.itemgridfragmentGrid.setAdapter(adapter); binding.itemgridfragmentGrid.setAdapter(adapter);
@ -261,6 +261,13 @@ public class ItemSetFragment extends NbFragment {
layoutManager.scrollToPositionWithOffset(0, 0); layoutManager.scrollToPositionWithOffset(0, 0);
} }
public void scrollToPosition(int position) {
int layoutTotalPositions = layoutManager.getItemCount() - 1;
if (position > 0 && position <= layoutTotalPositions) {
layoutManager.scrollToPosition(position);
}
}
protected FeedSet getFeedSet() { protected FeedSet getFeedSet() {
return ((ItemsList) getActivity()).getFeedSet(); return ((ItemsList) getActivity()).getFeedSet();
} }
@ -546,6 +553,13 @@ public class ItemSetFragment extends NbFragment {
fleuronBinding.getRoot().setLayoutParams(newLayout); fleuronBinding.getRoot().setLayoutParams(newLayout);
} }
private StoryViewAdapter.OnStoryClickListener getOnStoryClickListener() {
return (feedSet, storyHash) -> {
ItemsList activity = ((ItemsList) getActivity());
if (activity != null) activity.startReadingActivity(feedSet, storyHash);
};
}
@Override @Override
public void onSaveInstanceState (Bundle outState) { public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);

View file

@ -135,7 +135,7 @@ abstract class ProfileActivityDetailsFragment : Fragment(), OnItemClickListener
//context.startActivity(intent); //context.startActivity(intent);
} }
} else if (activity.category == ActivityDetails.Category.STAR) { } else if (activity.category == ActivityDetails.Category.STAR) {
UIUtils.startReadingActivity(FeedSet.allSaved(), activity.storyHash, context) UIUtils.startReadingActivity(context, FeedSet.allSaved(), activity.storyHash)
} else if (isSocialFeedCategory(activity)) { } else if (isSocialFeedCategory(activity)) {
// Strip the social: prefix from feedId // Strip the social: prefix from feedId
val socialFeedId = activity.feedId.substring(7) val socialFeedId = activity.feedId.substring(7)
@ -143,7 +143,7 @@ abstract class ProfileActivityDetailsFragment : Fragment(), OnItemClickListener
if (feed == null) { if (feed == null) {
Toast.makeText(context, R.string.profile_do_not_follow, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.profile_do_not_follow, Toast.LENGTH_SHORT).show()
} else { } else {
UIUtils.startReadingActivity(FeedSet.singleSocialFeed(feed.userId, feed.username), activity.storyHash, context) UIUtils.startReadingActivity(context, FeedSet.singleSocialFeed(feed.userId, feed.username), activity.storyHash)
} }
} }
} }

View file

@ -36,6 +36,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@ -265,7 +266,11 @@ public class UIUtils {
}); });
} }
public static void startReadingActivity(FeedSet fs, String startingHash, Context context) { public static void startReadingActivity(Context context, FeedSet fs, String startingHash) {
startReadingActivity(context, fs, startingHash, null);
}
public static void startReadingActivity(Context context, FeedSet fs, String startingHash, @Nullable ActivityResultLauncher<Intent> readingActivityLauncher) {
Class activityClass; Class activityClass;
if (fs.isAllSaved()) { if (fs.isAllSaved()) {
activityClass = SavedStoriesReading.class; activityClass = SavedStoriesReading.class;
@ -294,7 +299,8 @@ public class UIUtils {
Intent i = new Intent(context, activityClass); Intent i = new Intent(context, activityClass);
i.putExtra(Reading.EXTRA_FEEDSET, fs); i.putExtra(Reading.EXTRA_FEEDSET, fs);
i.putExtra(Reading.EXTRA_STORY_HASH, startingHash); i.putExtra(Reading.EXTRA_STORY_HASH, startingHash);
context.startActivity(i); if (readingActivityLauncher != null) readingActivityLauncher.launch(i);
else context.startActivity(i);
} }
public static String getMemoryUsageDebug(Context context) { public static String getMemoryUsageDebug(Context context) {