Commit 3a8291a8 authored by Dan Harrington's avatar Dan Harrington Committed by Commit Bot

feedv2: make surface open conditions clear

This change ensures that the surface can't be opened more
than once, and only closes after opening.

Also verifies that all conditions are met prior to opening.

While testing this, I noticed scroll restore wasn't working.
I've fixed this by waiting until the recycler view adapter
has the right number of items before attempting to restore.

Bug: 1115213
Change-Id: I86bae811926fa4a6c1caf1f8dce8e32e80152613
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2354656Reviewed-by: default avatarVincent Boisselle <vincb@google.com>
Commit-Queue: Dan H <harringtond@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798194}
parent 5315399b
...@@ -45,16 +45,12 @@ public class FeedStream implements Stream { ...@@ -45,16 +45,12 @@ public class FeedStream implements Stream {
final FeedStreamSurface mFeedStreamSurface; final FeedStreamSurface mFeedStreamSurface;
private final ObserverList<ScrollListener> mScrollListeners = private final ObserverList<ScrollListener> mScrollListeners =
new ObserverList<ScrollListener>(); new ObserverList<ScrollListener>();
private boolean mShown;
private RecyclerView mRecyclerView; private RecyclerView mRecyclerView;
// setStreamContentVisibility() is always called once after onCreate(). So we can assume the
// stream content is hidden initially and it can be made visible later when
// setStreamContentVisibility() is called.
private boolean mIsStreamContentVisible;
// For loading more content. // For loading more content.
private int mAccumulatedDySinceLastLoadMore; private int mAccumulatedDySinceLastLoadMore;
private String mSavedScrollStateOnHide; private String mScrollStateToRestore;
private RestoreScrollObserver mRestoreScrollObserver = new RestoreScrollObserver();
public FeedStream(Activity activity, boolean isBackgroundDark, SnackbarManager snackbarManager, public FeedStream(Activity activity, boolean isBackgroundDark, SnackbarManager snackbarManager,
NativePageNavigationDelegate nativePageNavigationDelegate, NativePageNavigationDelegate nativePageNavigationDelegate,
...@@ -67,35 +63,27 @@ public class FeedStream implements Stream { ...@@ -67,35 +63,27 @@ public class FeedStream implements Stream {
@Override @Override
public void onCreate(@Nullable String savedInstanceState) { public void onCreate(@Nullable String savedInstanceState) {
mScrollStateToRestore = savedInstanceState;
setupRecyclerView(); setupRecyclerView();
if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
restoreScrollState(savedInstanceState);
}
} }
@Override @Override
public void onShow() { public void onShow() {
mShown = true; mFeedStreamSurface.setStreamVisibility(true);
if (mIsStreamContentVisible && !mFeedStreamSurface.isOpened()) {
mFeedStreamSurface.surfaceOpened();
if (mSavedScrollStateOnHide != null) {
restoreScrollState(mSavedScrollStateOnHide);
mSavedScrollStateOnHide = null;
}
}
} }
@Override @Override
public void onHide() { public void onHide() {
mScrollStateToRestore = null;
if (mFeedStreamSurface.isOpened()) { if (mFeedStreamSurface.isOpened()) {
mSavedScrollStateOnHide = getSavedInstanceStateString(); mScrollStateToRestore = getSavedInstanceStateString();
mFeedStreamSurface.surfaceClosed();
} }
mShown = false; mFeedStreamSurface.setStreamVisibility(false);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
mScrollStateToRestore = null;
mFeedStreamSurface.destroy(); mFeedStreamSurface.destroy();
} }
...@@ -141,18 +129,7 @@ public class FeedStream implements Stream { ...@@ -141,18 +129,7 @@ public class FeedStream implements Stream {
@Override @Override
public void setStreamContentVisibility(boolean visible) { public void setStreamContentVisibility(boolean visible) {
if (visible == mIsStreamContentVisible) { mFeedStreamSurface.setStreamContentVisibility(visible);
return;
}
mIsStreamContentVisible = visible;
if (visible) {
if (mShown) mFeedStreamSurface.surfaceOpened();
} else {
if (mFeedStreamSurface.isOpened()) {
mFeedStreamSurface.surfaceClosed();
}
}
} }
@Override @Override
...@@ -225,8 +202,6 @@ public class FeedStream implements Stream { ...@@ -225,8 +202,6 @@ public class FeedStream implements Stream {
public void triggerRefresh() {} public void triggerRefresh() {}
private void setupRecyclerView() { private void setupRecyclerView() {
assert (!mIsStreamContentVisible);
mRecyclerView = (RecyclerView) mFeedStreamSurface.getView(); mRecyclerView = (RecyclerView) mFeedStreamSurface.getView();
mRecyclerView.setId(R.id.feed_stream_recycler_view); mRecyclerView.setId(R.id.feed_stream_recycler_view);
mRecyclerView.setClipToPadding(false); mRecyclerView.setClipToPadding(false);
...@@ -247,13 +222,12 @@ public class FeedStream implements Stream { ...@@ -247,13 +222,12 @@ public class FeedStream implements Stream {
} }
} }
}); });
mRecyclerView.getAdapter().registerAdapterDataObserver(mRestoreScrollObserver);
} }
@VisibleForTesting @VisibleForTesting
void checkScrollingForLoadMore(int dy) { void checkScrollingForLoadMore(int dy) {
if (!mIsStreamContentVisible) { if (!mFeedStreamSurface.isOpened()) return;
return;
}
mAccumulatedDySinceLastLoadMore += dy; mAccumulatedDySinceLastLoadMore += dy;
if (mAccumulatedDySinceLastLoadMore < TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, if (mAccumulatedDySinceLastLoadMore < TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
...@@ -268,17 +242,45 @@ public class FeedStream implements Stream { ...@@ -268,17 +242,45 @@ public class FeedStream implements Stream {
} }
} }
private void restoreScrollState(String savedInstanceState) { /**
* Restores the scroll state serialized to |savedInstanceState|.
* @return true if the scroll state was restored, or if the state could never be restored.
* false if we need to wait until more items are added to the recycler view to make it
* scrollable.
*/
private boolean restoreScrollState(String savedInstanceState) {
assert (mRecyclerView != null);
int scrollPosition;
int scrollOffset;
try { try {
JSONObject jsonSavedState = new JSONObject(savedInstanceState); JSONObject jsonSavedState = new JSONObject(savedInstanceState);
LinearLayoutManager layoutManager = scrollPosition = jsonSavedState.getInt(SCROLL_POSITION);
(LinearLayoutManager) mRecyclerView.getLayoutManager(); scrollOffset = jsonSavedState.getInt(SCROLL_OFFSET);
if (layoutManager != null) {
layoutManager.scrollToPositionWithOffset(jsonSavedState.getInt(SCROLL_POSITION),
jsonSavedState.getInt(SCROLL_OFFSET));
}
} catch (JSONException e) { } catch (JSONException e) {
Log.d(TAG, "Unable to parse a JSONObject from a string."); Log.d(TAG, "Unable to parse a JSONObject from a string.");
return true;
}
// If too few items exist, defer scrolling until later.
if (mRecyclerView.getAdapter().getItemCount() <= scrollPosition) return false;
LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
if (layoutManager != null) {
layoutManager.scrollToPositionWithOffset(scrollPosition, scrollOffset);
} }
return true;
} }
// Scroll state can't be restored until enough items are added to the recycler view adapter.
// Attempts to restore scroll state every time new items are added to the adapter.
class RestoreScrollObserver extends RecyclerView.AdapterDataObserver {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mScrollStateToRestore != null) {
if (restoreScrollState(mScrollStateToRestore)) {
mScrollStateToRestore = null;
}
}
}
};
} }
...@@ -102,8 +102,10 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -102,8 +102,10 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
private final NativePageNavigationDelegate mPageNavigationDelegate; private final NativePageNavigationDelegate mPageNavigationDelegate;
private final HelpAndFeedback mHelpAndFeedback; private final HelpAndFeedback mHelpAndFeedback;
private final ScrollReporter mScrollReporter = new ScrollReporter(); private final ScrollReporter mScrollReporter = new ScrollReporter();
// True after onSurfaceOpened(), and before onSurfaceClosed().
private boolean mOpened; private boolean mOpened;
private boolean mStreamContentVisible;
private boolean mStreamVisible;
private int mHeaderCount; private int mHeaderCount;
private BottomSheetContent mBottomSheetContent; private BottomSheetContent mBottomSheetContent;
// If the bottom sheet was opened in response to an action on a slice, this is the slice ID. // If the bottom sheet was opened in response to an action on a slice, this is the slice ID.
...@@ -130,13 +132,10 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -130,13 +132,10 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
// We avoid attaching surfaces until after |startup()| is called. This ensures that // We avoid attaching surfaces until after |startup()| is called. This ensures that
// the correct sign-in state is used if attaching the surface triggers a fetch. // the correct sign-in state is used if attaching the surface triggers a fetch.
private static boolean sStartupCalled; private static boolean sStartupCalled;
// Tracks all the surfaces that are waiting to be attached or already attached. When // Tracks all the instances of FeedStreamSurface.
// |sStartupCalled| is false, |startup()| has not been called and thus all the surfaces @VisibleForTesting
// in this set are waiting to be attached. Otherwise, all the surfaces in this set are static HashSet<FeedStreamSurface> sSurfaces;
// already attached.
private static HashSet<FeedStreamSurface> sSurfaces;
public static void startup() { public static void startup() {
if (sStartupCalled) return; if (sStartupCalled) return;
...@@ -145,7 +144,7 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -145,7 +144,7 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
xSurfaceProcessScope(); xSurfaceProcessScope();
if (sSurfaces != null) { if (sSurfaces != null) {
for (FeedStreamSurface surface : sSurfaces) { for (FeedStreamSurface surface : sSurfaces) {
surface.surfaceOpened(); surface.updateSurfaceOpenState();
} }
} }
} }
...@@ -175,13 +174,12 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -175,13 +174,12 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
* Clear all the data related to all surfaces. * Clear all the data related to all surfaces.
*/ */
public static void clearAll() { public static void clearAll() {
FeedStreamSurface[] surfaces = null; ArrayList<FeedStreamSurface> openSurfaces = new ArrayList<FeedStreamSurface>();
if (sSurfaces != null) { for (FeedStreamSurface surface : sSurfaces) {
surfaces = sSurfaces.toArray(new FeedStreamSurface[sSurfaces.size()]); if (surface.isOpened()) openSurfaces.add(surface);
for (FeedStreamSurface surface : surfaces) { }
surface.surfaceClosed(); for (FeedStreamSurface surface : openSurfaces) {
} surface.onSurfaceClosed();
sSurfaces = null;
} }
ProcessScope processScope = xSurfaceProcessScope(); ProcessScope processScope = xSurfaceProcessScope();
...@@ -189,10 +187,8 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -189,10 +187,8 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
processScope.resetAccount(); processScope.resetAccount();
} }
if (surfaces != null) { for (FeedStreamSurface surface : openSurfaces) {
for (FeedStreamSurface surface : surfaces) { surface.updateSurfaceOpenState();
surface.surfaceOpened();
}
} }
} }
...@@ -337,15 +333,16 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -337,15 +333,16 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
} else { } else {
mRootView = null; mRootView = null;
} }
trackSurface(this);
} }
/** /**
* Performs all necessary cleanups. * Performs all necessary cleanups.
*/ */
public void destroy() { public void destroy() {
if (mOpened) { if (mOpened) onSurfaceClosed();
surfaceClosed(); untrackSurface(this);
}
if (mSliceViewTracker != null) { if (mSliceViewTracker != null) {
mSliceViewTracker.destroy(); mSliceViewTracker.destroy();
mSliceViewTracker = null; mSliceViewTracker = null;
...@@ -429,6 +426,9 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -429,6 +426,9 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
*/ */
@CalledByNative @CalledByNative
void onStreamUpdated(byte[] data) { void onStreamUpdated(byte[] data) {
// There should be no updates while the surface is closed. If the surface was recently
// closed, just ignore these.
if (!mOpened) return;
StreamUpdate streamUpdate; StreamUpdate streamUpdate;
try { try {
streamUpdate = StreamUpdate.parseFrom(data); streamUpdate = StreamUpdate.parseFrom(data);
...@@ -781,41 +781,68 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand ...@@ -781,41 +781,68 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
} }
/** /**
* Informs that the surface is opened. We can request the initial set of content now. Once * Informs whether or not feed content should be shown.
* the content is available, onStreamUpdated will be called.
*/ */
public void surfaceOpened() { public void setStreamContentVisibility(boolean visible) {
mOpened = true; if (mStreamContentVisible == visible) return;
trackSurface(this); mStreamContentVisible = visible;
if (sStartupCalled) { updateSurfaceOpenState();
FeedStreamSurfaceJni.get().surfaceOpened( }
mNativeFeedStreamSurface, FeedStreamSurface.this);
mHybridListRenderer.onSurfaceOpened(); /**
* Informs FeedStreamSurface of the visibility of its parent stream.
*/
public void setStreamVisibility(boolean visible) {
if (mStreamVisible == visible) return;
mStreamVisible = visible;
updateSurfaceOpenState();
}
private void updateSurfaceOpenState() {
boolean shouldOpen = sStartupCalled && mStreamContentVisible && mStreamVisible;
if (shouldOpen == mOpened) return;
if (shouldOpen) {
onSurfaceOpened();
} else {
onSurfaceClosed();
} }
} }
/**
* Called when the surface is considered opened. This happens when the feed should be visible
* and enabled on the screen.
*/
private void onSurfaceOpened() {
assert (!mOpened);
assert (sStartupCalled);
assert (mStreamContentVisible);
// No feed content should exist.
assert (mContentManager.getItemCount() == mHeaderCount);
mOpened = true;
FeedStreamSurfaceJni.get().surfaceOpened(mNativeFeedStreamSurface, FeedStreamSurface.this);
mHybridListRenderer.onSurfaceOpened();
}
/** /**
* Informs that the surface is closed. * Informs that the surface is closed.
*/ */
public void surfaceClosed() { private void onSurfaceClosed() {
assert (mOpened);
assert (sStartupCalled);
// Let the hybrid list renderer know that the surface has closed, so it doesn't // Let the hybrid list renderer know that the surface has closed, so it doesn't
// interpret the removal of contents as related to actions otherwise initiated by // interpret the removal of contents as related to actions otherwise initiated by
// the user. // the user.
if (sStartupCalled) { mHybridListRenderer.onSurfaceClosed();
mHybridListRenderer.onSurfaceClosed();
}
// Remove Feed content from the content manager.
int feedCount = mContentManager.getItemCount() - mHeaderCount; int feedCount = mContentManager.getItemCount() - mHeaderCount;
if (feedCount > 0) { if (feedCount > 0) {
mContentManager.removeContents(mHeaderCount, feedCount); mContentManager.removeContents(mHeaderCount, feedCount);
} }
mScrollReporter.onUnbind(); mScrollReporter.onUnbind();
untrackSurface(this); FeedStreamSurfaceJni.get().surfaceClosed(mNativeFeedStreamSurface, FeedStreamSurface.this);
if (sStartupCalled) {
FeedStreamSurfaceJni.get().surfaceClosed(
mNativeFeedStreamSurface, FeedStreamSurface.this);
}
mOpened = false; mOpened = false;
} }
......
...@@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView; ...@@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
...@@ -145,12 +146,18 @@ public class FeedStreamSurfaceTest { ...@@ -145,12 +146,18 @@ public class FeedStreamSurfaceTest {
mRecyclerView = mFeedStreamSurface.mRootView; mRecyclerView = mFeedStreamSurface.mRootView;
mLayoutManager = new FakeLinearLayoutManager(mActivity); mLayoutManager = new FakeLinearLayoutManager(mActivity);
mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setLayoutManager(mLayoutManager);
// Since we use a mockito spy, we need to replace the entry in sSurfaces.
FeedStreamSurface.sSurfaces.clear();
FeedStreamSurface.sSurfaces.add(mFeedStreamSurface);
// Print logs to stdout. // Print logs to stdout.
ShadowLog.stream = System.out; ShadowLog.stream = System.out;
} }
@After @After
public void tearDown() { public void tearDown() {
mFeedStreamSurface.destroy();
FeedStreamSurface.shutdownForTesting(); FeedStreamSurface.shutdownForTesting();
AppHooks.setInstanceForTesting(null); AppHooks.setInstanceForTesting(null);
} }
...@@ -158,6 +165,7 @@ public class FeedStreamSurfaceTest { ...@@ -158,6 +165,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testAddSlicesOnStreamUpdated() { public void testAddSlicesOnStreamUpdated() {
startupAndSetVisible();
// Add 3 new slices at first. // Add 3 new slices at first.
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
...@@ -190,6 +198,7 @@ public class FeedStreamSurfaceTest { ...@@ -190,6 +198,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testAddNewSlicesWithSameIds() { public void testAddNewSlicesWithSameIds() {
startupAndSetVisible();
// Add 2 new slices at first. // Add 2 new slices at first.
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
...@@ -214,6 +223,7 @@ public class FeedStreamSurfaceTest { ...@@ -214,6 +223,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testRemoveSlicesOnStreamUpdated() { public void testRemoveSlicesOnStreamUpdated() {
startupAndSetVisible();
// Add 3 new slices at first. // Add 3 new slices at first.
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
...@@ -245,6 +255,7 @@ public class FeedStreamSurfaceTest { ...@@ -245,6 +255,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testReorderSlicesOnStreamUpdated() { public void testReorderSlicesOnStreamUpdated() {
startupAndSetVisible();
// Add 3 new slices at first. // Add 3 new slices at first.
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
...@@ -285,6 +296,7 @@ public class FeedStreamSurfaceTest { ...@@ -285,6 +296,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testComplexOperationsOnStreamUpdated() { public void testComplexOperationsOnStreamUpdated() {
startupAndSetVisible();
// Add 3 new slices at first. // Add 3 new slices at first.
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
...@@ -325,6 +337,7 @@ public class FeedStreamSurfaceTest { ...@@ -325,6 +337,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testAddHeaderViews() { public void testAddHeaderViews() {
startupAndSetVisible();
View v0 = new View(mActivity); View v0 = new View(mActivity);
View v1 = new View(mActivity); View v1 = new View(mActivity);
...@@ -337,6 +350,7 @@ public class FeedStreamSurfaceTest { ...@@ -337,6 +350,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testUpdateHeaderViews() { public void testUpdateHeaderViews() {
startupAndSetVisible();
View v0 = new View(mActivity); View v0 = new View(mActivity);
View v1 = new View(mActivity); View v1 = new View(mActivity);
...@@ -358,6 +372,7 @@ public class FeedStreamSurfaceTest { ...@@ -358,6 +372,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testComplexOperationsOnStreamUpdatedAfterSetHeaderViews() { public void testComplexOperationsOnStreamUpdatedAfterSetHeaderViews() {
startupAndSetVisible();
// Set 2 header views first. These should always be there throughout stream update. // Set 2 header views first. These should always be there throughout stream update.
View v0 = new View(mActivity); View v0 = new View(mActivity);
View v1 = new View(mActivity); View v1 = new View(mActivity);
...@@ -493,6 +508,8 @@ public class FeedStreamSurfaceTest { ...@@ -493,6 +508,8 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testRemoveContentsOnSurfaceClosed() { public void testRemoveContentsOnSurfaceClosed() {
startupAndSetVisible();
// Set 2 header views first. // Set 2 header views first.
View v0 = new View(mActivity); View v0 = new View(mActivity);
View v1 = new View(mActivity); View v1 = new View(mActivity);
...@@ -512,7 +529,7 @@ public class FeedStreamSurfaceTest { ...@@ -512,7 +529,7 @@ public class FeedStreamSurfaceTest {
assertEquals(headers + 3, mContentManager.getItemCount()); assertEquals(headers + 3, mContentManager.getItemCount());
// Closing the surface should remove all non-header contents. // Closing the surface should remove all non-header contents.
mFeedStreamSurface.surfaceClosed(); mFeedStreamSurface.setStreamVisibility(false);
assertEquals(headers, mContentManager.getItemCount()); assertEquals(headers, mContentManager.getItemCount());
assertEquals(v0, getNativeView(0)); assertEquals(v0, getNativeView(0));
assertEquals(v1, getNativeView(1)); assertEquals(v1, getNativeView(1));
...@@ -521,6 +538,7 @@ public class FeedStreamSurfaceTest { ...@@ -521,6 +538,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testLoadMoreOnDismissal() { public void testLoadMoreOnDismissal() {
startupAndSetVisible();
final int itemCount = 10; final int itemCount = 10;
// loadMore not triggered due to last visible item not falling into lookahead range. // loadMore not triggered due to last visible item not falling into lookahead range.
...@@ -541,6 +559,7 @@ public class FeedStreamSurfaceTest { ...@@ -541,6 +559,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testLoadMoreOnNavigateNewTab() { public void testLoadMoreOnNavigateNewTab() {
startupAndSetVisible();
final int itemCount = 10; final int itemCount = 10;
// loadMore not triggered due to last visible item not falling into lookahead range. // loadMore not triggered due to last visible item not falling into lookahead range.
...@@ -561,6 +580,7 @@ public class FeedStreamSurfaceTest { ...@@ -561,6 +580,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testLoadMoreOnNavigateIncognitoTab() { public void testLoadMoreOnNavigateIncognitoTab() {
startupAndSetVisible();
final int itemCount = 10; final int itemCount = 10;
// loadMore not triggered due to last visible item not falling into lookahead range. // loadMore not triggered due to last visible item not falling into lookahead range.
...@@ -581,33 +601,38 @@ public class FeedStreamSurfaceTest { ...@@ -581,33 +601,38 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testSurfaceOpenedAndClosed() { public void testSurfaceOpenedAndClosed() {
// Calling surfaceOpened() before startup() should not trigger native open call. // The surface won't open until after startup().
mFeedStreamSurface.surfaceOpened(); mFeedStreamSurface.setStreamVisibility(true);
verify(mFeedStreamSurfaceJniMock, never()) mFeedStreamSurface.setStreamContentVisibility(true);
.surfaceOpened(anyLong(), any(FeedStreamSurface.class)); Assert.assertFalse(mFeedStreamSurface.isOpened());
// Calling surfaceClosed() before startup() should not trigger native open call. // Exercise turning off stream visibility while hidden.
mFeedStreamSurface.surfaceClosed(); mFeedStreamSurface.setStreamVisibility(false);
verify(mFeedStreamSurfaceJniMock, never()) Assert.assertFalse(mFeedStreamSurface.isOpened());
.surfaceClosed(anyLong(), any(FeedStreamSurface.class));
// Trigger open.
mFeedStreamSurface.setStreamVisibility(true);
FeedStreamSurface.startup(); FeedStreamSurface.startup();
Assert.assertTrue(mFeedStreamSurface.isOpened());
// Calling surfaceOpened() after startup() should trigger native open call. mFeedStreamSurface.setStreamVisibility(false);
mFeedStreamSurface.surfaceOpened(); Assert.assertFalse(mFeedStreamSurface.isOpened());
verify(mFeedStreamSurfaceJniMock).surfaceOpened(anyLong(), any(FeedStreamSurface.class));
// Calling surfaceClosed() after startup() should trigger native open call. mFeedStreamSurface.setStreamVisibility(true);
mFeedStreamSurface.surfaceClosed(); Assert.assertTrue(mFeedStreamSurface.isOpened());
verify(mFeedStreamSurfaceJniMock).surfaceClosed(anyLong(), any(FeedStreamSurface.class));
mFeedStreamSurface.setStreamContentVisibility(false);
Assert.assertFalse(mFeedStreamSurface.isOpened());
mFeedStreamSurface.setStreamContentVisibility(true);
Assert.assertTrue(mFeedStreamSurface.isOpened());
} }
@Test @Test
@SmallTest @SmallTest
public void testClearAll() { public void testClearAll() {
FeedStreamSurface.startup();
InOrder order = Mockito.inOrder(mFeedStreamSurfaceJniMock, mProcessScope); InOrder order = Mockito.inOrder(mFeedStreamSurfaceJniMock, mProcessScope);
mFeedStreamSurface.surfaceOpened(); startupAndSetVisible();
order.verify(mFeedStreamSurfaceJniMock) order.verify(mFeedStreamSurfaceJniMock)
.surfaceOpened(anyLong(), any(FeedStreamSurface.class)); .surfaceOpened(anyLong(), any(FeedStreamSurface.class));
...@@ -622,6 +647,7 @@ public class FeedStreamSurfaceTest { ...@@ -622,6 +647,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testFindChildViewContainingDescendentNullParameters() { public void testFindChildViewContainingDescendentNullParameters() {
startupAndSetVisible();
View v = new View(mActivity); View v = new View(mActivity);
assertEquals(null, mFeedStreamSurface.findChildViewContainingDescendent(null, v)); assertEquals(null, mFeedStreamSurface.findChildViewContainingDescendent(null, v));
assertEquals(null, mFeedStreamSurface.findChildViewContainingDescendent(v, null)); assertEquals(null, mFeedStreamSurface.findChildViewContainingDescendent(v, null));
...@@ -630,6 +656,7 @@ public class FeedStreamSurfaceTest { ...@@ -630,6 +656,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testFindChildViewContainingDescendentNotADescendent() { public void testFindChildViewContainingDescendentNotADescendent() {
startupAndSetVisible();
View v1 = new View(mActivity); View v1 = new View(mActivity);
LinearLayout v2 = new LinearLayout(mActivity); LinearLayout v2 = new LinearLayout(mActivity);
View v2Child = new View(mActivity); View v2Child = new View(mActivity);
...@@ -642,6 +669,7 @@ public class FeedStreamSurfaceTest { ...@@ -642,6 +669,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testFindChildViewContainingDescendentDirectDescendent() { public void testFindChildViewContainingDescendentDirectDescendent() {
startupAndSetVisible();
LinearLayout parent = new LinearLayout(mActivity); LinearLayout parent = new LinearLayout(mActivity);
View child = new View(mActivity); View child = new View(mActivity);
parent.addView(child); parent.addView(child);
...@@ -652,6 +680,7 @@ public class FeedStreamSurfaceTest { ...@@ -652,6 +680,7 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testFindChildViewContainingDescendentIndirectDescendent() { public void testFindChildViewContainingDescendentIndirectDescendent() {
startupAndSetVisible();
LinearLayout parent = new LinearLayout(mActivity); LinearLayout parent = new LinearLayout(mActivity);
LinearLayout child = new LinearLayout(mActivity); LinearLayout child = new LinearLayout(mActivity);
View grandChild = new View(mActivity); View grandChild = new View(mActivity);
...@@ -662,9 +691,23 @@ public class FeedStreamSurfaceTest { ...@@ -662,9 +691,23 @@ public class FeedStreamSurfaceTest {
child, mFeedStreamSurface.findChildViewContainingDescendent(parent, grandChild)); child, mFeedStreamSurface.findChildViewContainingDescendent(parent, grandChild));
} }
@Test
@SmallTest
public void testOnStreamUpdatedIgnoredWhenNotOpen() {
// Surface not opened initially.
StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b"))
.build();
mFeedStreamSurface.onStreamUpdated(update.toByteArray());
assertEquals(0, mContentManager.getItemCount());
}
@Test @Test
@SmallTest @SmallTest
public void testNavigateReportsCorrectSlice() { public void testNavigateReportsCorrectSlice() {
startupAndSetVisible();
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b"))
...@@ -699,6 +742,8 @@ public class FeedStreamSurfaceTest { ...@@ -699,6 +742,8 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testNavigateFromBottomSheetReportsCorrectSlice() { public void testNavigateFromBottomSheetReportsCorrectSlice() {
startupAndSetVisible();
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b"))
...@@ -739,6 +784,8 @@ public class FeedStreamSurfaceTest { ...@@ -739,6 +784,8 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testNavigateNoSliceFound() { public void testNavigateNoSliceFound() {
startupAndSetVisible();
StreamUpdate update = StreamUpdate.newBuilder() StreamUpdate update = StreamUpdate.newBuilder()
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("a"))
.addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b")) .addUpdatedSlices(createSliceUpdateForNewXSurfaceSlice("b"))
...@@ -762,6 +809,8 @@ public class FeedStreamSurfaceTest { ...@@ -762,6 +809,8 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testScrollIsReportedOnIdle() { public void testScrollIsReportedOnIdle() {
startupAndSetVisible();
mFeedStreamSurface.streamScrolled(0, 100); mFeedStreamSurface.streamScrolled(0, 100);
Robolectric.getForegroundThreadScheduler().advanceBy(1000, TimeUnit.MILLISECONDS); Robolectric.getForegroundThreadScheduler().advanceBy(1000, TimeUnit.MILLISECONDS);
...@@ -774,8 +823,10 @@ public class FeedStreamSurfaceTest { ...@@ -774,8 +823,10 @@ public class FeedStreamSurfaceTest {
@Test @Test
@SmallTest @SmallTest
public void testScrollIsReportedOnClose() { public void testScrollIsReportedOnClose() {
startupAndSetVisible();
mFeedStreamSurface.streamScrolled(0, 100); mFeedStreamSurface.streamScrolled(0, 100);
mFeedStreamSurface.surfaceClosed(); mFeedStreamSurface.setStreamContentVisibility(false);
verify(mFeedStreamSurfaceJniMock) verify(mFeedStreamSurfaceJniMock)
.reportStreamScrollStart(anyLong(), any(FeedStreamSurface.class)); .reportStreamScrollStart(anyLong(), any(FeedStreamSurface.class));
...@@ -807,4 +858,10 @@ public class FeedStreamSurfaceTest { ...@@ -807,4 +858,10 @@ public class FeedStreamSurfaceTest {
assertTrue(view instanceof FrameLayout); assertTrue(view instanceof FrameLayout);
return ((FrameLayout) view).getChildAt(0); return ((FrameLayout) view).getChildAt(0);
} }
void startupAndSetVisible() {
FeedStreamSurface.startup();
mFeedStreamSurface.setStreamContentVisibility(true);
mFeedStreamSurface.setStreamVisibility(true);
}
} }
...@@ -71,7 +71,8 @@ public class FeedStreamTest { ...@@ -71,7 +71,8 @@ public class FeedStreamTest {
when(mFeedServiceBridgeJniMock.getLoadMoreTriggerLookahead()) when(mFeedServiceBridgeJniMock.getLoadMoreTriggerLookahead())
.thenReturn(LOAD_MORE_TRIGGER_LOOKAHEAD); .thenReturn(LOAD_MORE_TRIGGER_LOOKAHEAD);
// Surfaces won't open until after startup.
FeedStreamSurface.startup();
mFeedStream = new FeedStream(mActivity, false, mSnackbarManager, mPageNavigationDelegate, mFeedStream = new FeedStream(mActivity, false, mSnackbarManager, mPageNavigationDelegate,
mBottomSheetController); mBottomSheetController);
mFeedStream.onCreate(null); mFeedStream.onCreate(null);
...@@ -196,6 +197,7 @@ public class FeedStreamTest { ...@@ -196,6 +197,7 @@ public class FeedStreamTest {
@Test @Test
public void testCheckScrollingForLoadMore_StreamContentVisible() { public void testCheckScrollingForLoadMore_StreamContentVisible() {
mFeedStream.onShow();
mFeedStream.setStreamContentVisibility(true); mFeedStream.setStreamContentVisibility(true);
final int triggerDistance = getLoadMoreTriggerScrollDistance(); final int triggerDistance = getLoadMoreTriggerScrollDistance();
final int itemCount = 10; final int itemCount = 10;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment