Commit bdea2c8f authored by spdonghao's avatar spdonghao Committed by Commit Bot

[Instant Start] Make the Feed placeholder support Feed V2.

Please see doc:
https://docs.google.com/document/d/1t5KUt_CnivUO4YKhViXcS_NEa56bBI9WoYqVM1XdsoU/edit#

Bug: 1083428
Change-Id: Ie4b4d43f64e7d95c5d2695a8c24365f4d1e4722b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2349985Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarDan H <harringtond@chromium.org>
Reviewed-by: default avatarXi Han <hanxi@chromium.org>
Commit-Queue: Hao Dong <spdonghao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825347}
parent 86d61d12
......@@ -13,8 +13,6 @@ import android.graphics.drawable.LayerDrawable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
......@@ -71,22 +69,24 @@ public class FeedLoadingLayout extends LinearLayout {
*/
@SuppressLint("InflateParams")
private void setHeader() {
LayoutInflater inflater = LayoutInflater.from(mContext);
View header;
LinearLayout headerView = findViewById(R.id.feed_placeholder_header);
ViewGroup.LayoutParams lp = headerView.getLayoutParams();
// FeedFeatures.cachedIsReportingUserActions uses CachedFeatureFlags for checking feature
// states, but these same features are checked directly with ChromeFeatureList in other
// places. Using the cached check here is deliberate for pre-native usage. This
// inconsistency is fine because the check here is for the Feed header blank size, the
// mismatch is bearable and only once for every change.
if (FeedFeatures.cachedIsReportingUserActions()) {
header = inflater.inflate(
R.layout.new_tab_page_snippets_expandable_header_with_menu, null, false);
header.findViewById(R.id.header_menu).setVisibility(INVISIBLE);
// Header blank size should be consistent with
// R.layout.new_tab_page_snippets_expandable_header_with_menu.
lp.height =
getResources().getDimensionPixelSize(R.dimen.snippets_article_header_menu_size);
} else {
header = inflater.inflate(R.layout.ss_feed_header, null, false);
// Header blank size should be consistent with R.layout.ss_feed_header.
lp.height =
getResources().getDimensionPixelSize(R.dimen.snippets_article_header_height);
}
LinearLayout headerView = findViewById(R.id.feed_placeholder_header);
headerView.addView(header);
headerView.setLayoutParams(lp);
}
private void setPlaceholders() {
......@@ -230,8 +230,9 @@ public class FeedLoadingLayout extends LinearLayout {
* is resized by {@link ViewResizer} in {@link FeedLoadingCoordinator}
*/
private void setPadding() {
int defaultPadding =
mResources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_margin);
int defaultPadding = mResources.getDimensionPixelSize(FeedFeatures.cachedIsV2Enabled()
? R.dimen.content_suggestions_card_modern_margin_v2
: R.dimen.content_suggestions_card_modern_margin);
UiConfig uiConfig = new UiConfig(this);
// mUiConfig.getContext().getResources() is used here instead of mView.getResources()
// because lemon compression, somehow, causes the resources to return a different
......@@ -244,11 +245,16 @@ public class FeedLoadingLayout extends LinearLayout {
} else {
mPaddingPx = defaultPadding;
}
mPaddingPx += FeedFeatures.cachedIsV2Enabled() ? mResources.getDimensionPixelSize(
R.dimen.content_suggestions_card_modern_padding_v2)
: 0;
setPaddingRelative(mPaddingPx, 0, mPaddingPx, 0);
}
private int computePadding() {
int widePadding = mResources.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins);
int widePadding = mResources.getDimensionPixelSize(FeedFeatures.cachedIsV2Enabled()
? R.dimen.ntp_wide_card_lateral_margins_v2
: R.dimen.ntp_wide_card_lateral_margins);
int padding =
dpToPx((int) ((mScreenWidthDp - UiConfig.WIDE_DISPLAY_STYLE_MIN_WIDTH_DP) / 2.f));
padding = Math.max(widePadding, padding);
......
......@@ -44,7 +44,6 @@ import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator;
import org.chromium.chrome.browser.feed.shared.FeedFeatures;
import org.chromium.chrome.browser.feed.shared.stream.Stream;
import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
......@@ -754,14 +753,9 @@ class StartSurfaceMediator
StartSurfaceConfiguration.getFeedArticlesVisibility();
}
// ChromeFeatureList.INTEREST_FEED_V2 is checked directly with ChromeFeatureList#isEnabled()
// in other places. Using CachedFeatureFlags#isEnabled here is deliberate for a pre-native
// check. This mismatch is acceptable, because in our use case in
// FeedSurfaceCoordinator#createStream, we check both versions to avoid the broken UI.
return mSurfaceMode == SurfaceMode.SINGLE_PANE
&& CachedFeatureFlags.isEnabled(ChromeFeatureList.INSTANT_START)
&& StartSurfaceConfiguration.getFeedArticlesVisibility()
&& !FeedFeatures.cachedIsV2Enabled();
&& StartSurfaceConfiguration.getFeedArticlesVisibility();
}
/** This interface builds the feed surface coordinator when showing if needed. */
......
......@@ -37,8 +37,11 @@ import android.widget.ImageView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.BoundedMatcher;
import androidx.test.filters.SmallTest;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.core.AllOf;
import org.junit.Assert;
import org.junit.Rule;
......@@ -701,31 +704,34 @@ public class InstantStartTest {
@SmallTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
@EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,",
ChromeFeatureList.START_SURFACE_ANDROID + "<Study"})
ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2})
// clang-format off
@CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
"force-fieldtrials=Study/Group",
IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"})
public void testFeedLoading() {
// clang-format on
if (!FeedV1.IS_AVAILABLE) return; // Test not yet working for FeedV2.
startMainActivityFromLauncher();
Assert.assertFalse(mActivityTestRule.getActivity().isTablet());
Assert.assertTrue(CachedFeatureFlags.isEnabled(ChromeFeatureList.INSTANT_START));
onView(withId(R.id.placeholders_layout)).check(matches(isDisplayed()));
Assert.assertFalse(LibraryLoader.getInstance().isInitialized());
startAndWaitNativeInitialization();
// Feed background should be non-transparent finally.
ViewUtils.onViewWaiting(
AllOf.allOf(withId(R.id.feed_stream_recycler_view), matchesBackgroundAlpha(255)));
}
@Test
@SmallTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
@EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,",
ChromeFeatureList.START_SURFACE_ANDROID + "<Study"})
ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2})
// clang-format off
@CommandLineFlags.Add({"force-fieldtrials=Study/Group",
IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"})
public void testFeedPlaceholderVisibility() {
public void testCachedFeedVisibility() {
// clang-format on
startMainActivityFromLauncher();
mActivityTestRule.waitForActivityNativeInitializationComplete();
......@@ -760,7 +766,7 @@ public class InstantStartTest {
@SmallTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
@EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,",
ChromeFeatureList.START_SURFACE_ANDROID + "<Study"})
ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2})
// clang-format off
@CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
"force-fieldtrials=Study/Group",
......@@ -770,6 +776,7 @@ public class InstantStartTest {
StartSurfaceConfiguration.setFeedVisibilityForTesting(false);
startMainActivityFromLauncher();
// When cached Feed articles' visibility is invisible, placeholder should be invisible too.
onView(withId(R.id.placeholders_layout)).check(doesNotExist());
}
......@@ -777,17 +784,17 @@ public class InstantStartTest {
@SmallTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
@EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,",
ChromeFeatureList.START_SURFACE_ANDROID + "<Study"})
ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2})
// clang-format off
@CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
"force-fieldtrials=Study/Group",
IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"})
public void testShowPlaceholder() {
// clang-format on
if (!FeedV1.IS_AVAILABLE) return; // Test not yet working for FeedV2.
StartSurfaceConfiguration.setFeedVisibilityForTesting(true);
startMainActivityFromLauncher();
// When cached Feed articles' visibility is visible, placeholder should be visible too.
onView(withId(R.id.placeholders_layout)).check(matches(isDisplayed()));
}
......@@ -889,4 +896,29 @@ public class InstantStartTest {
withText(expanded ? R.string.hide_content : R.string.show_content)));
}
}
public static Matcher<View> matchesBackgroundAlpha(final int expectedAlpha) {
return new BoundedMatcher<View, View>(View.class) {
String mMessage;
int mActualAlpha;
@Override
protected boolean matchesSafely(View item) {
if (item.getBackground() == null) {
mMessage = item.getId() + " does not have a background";
return false;
}
mActualAlpha = item.getBackground().getAlpha();
return mActualAlpha == expectedAlpha;
}
@Override
public void describeTo(final Description description) {
if (expectedAlpha != mActualAlpha) {
mMessage = "Background alpha did not match: Expected " + expectedAlpha + " was "
+ mActualAlpha;
}
description.appendText(mMessage);
}
};
}
}
......@@ -20,6 +20,7 @@ import android.widget.ScrollView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.supplier.Supplier;
......@@ -97,7 +98,7 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
@Nullable
private final View mNtpHeader;
private final boolean mShowDarkBackground;
private final boolean mIsPlaceholderRequested;
private final boolean mIsPlaceholderShownInitially;
private final FeedSurfaceDelegate mDelegate;
private final int mDefaultMarginPixels;
private final int mWideMarginPixels;
......@@ -247,7 +248,7 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
* @param pageNavigationDelegate The {@link NativePageNavigationDelegate}
* that handles page navigation.
* @param profile The current user profile.
* @param isPlaceholderRequested Whether the placeholder should be shown.
* @param isPlaceholderShownInitially Whether the placeholder is shown initially.
* @param bottomSheetController The bottom sheet controller, used in v2.
*/
public FeedSurfaceCoordinator(Activity activity, SnackbarManager snackbarManager,
......@@ -256,7 +257,7 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
@Nullable SectionHeaderView sectionHeaderView, FeedV1ActionOptions actionOptions,
boolean showDarkBackground, FeedSurfaceDelegate delegate,
@Nullable NativePageNavigationDelegate pageNavigationDelegate, Profile profile,
boolean isPlaceholderRequested, BottomSheetController bottomSheetController) {
boolean isPlaceholderShownInitially, BottomSheetController bottomSheetController) {
if (FeedFeatures.isV2Enabled()) {
mStreamWrapper = FeedV2.createStreamWrapper();
} else {
......@@ -268,7 +269,7 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
mNtpHeader = ntpHeader;
mSectionHeaderView = sectionHeaderView;
mShowDarkBackground = showDarkBackground;
mIsPlaceholderRequested = isPlaceholderRequested;
mIsPlaceholderShownInitially = isPlaceholderShownInitially;
mDelegate = delegate;
mPageNavigationDelegate = pageNavigationDelegate;
mBottomSheetController = bottomSheetController;
......@@ -374,7 +375,7 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
mStreamCreatedTimeMs = SystemClock.elapsedRealtime();
mStream = mStreamWrapper.createStream(mProfile, mActivity, mShowDarkBackground,
mSnackbarManager, mPageNavigationDelegate, mUiConfig, mIsPlaceholderRequested,
mSnackbarManager, mPageNavigationDelegate, mUiConfig, mIsPlaceholderShownInitially,
mBottomSheetController, mV1ActionOptions);
mStreamLifecycleManager = mDelegate.createStreamLifecycleManager(mStream, mActivity);
......@@ -493,15 +494,29 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
mSigninPromoView = (PersonalizedSigninPromoView) inflater.inflate(
R.layout.personalized_signin_promo_view_modern_content_suggestions, mRootView,
false);
// If the placeholder is shown in V1, delay to show the sign-in view until the articles
// are shown.
if (mStreamWrapper.isPlaceholderShown()) {
// If the placeholder is shown in Feed v1, delay to show the sign-in view until the
// articles are shown. Feed v2's articles don't have fade-in animations, so sign-in view
// is already shown together with v2 articles.
if (isPlaceholderShown() && !FeedFeatures.isV2Enabled()) {
mSigninPromoView.setVisibility(View.INVISIBLE);
}
}
return mSigninPromoView;
}
/**
* Show the sign-in view with the same fade-in animation as Feed articles' add-animation.
*/
void fadeInSigninView() {
if (mSigninPromoView != null) {
mSigninPromoView.setAlpha(0f);
mSigninPromoView.setVisibility(View.VISIBLE);
mSigninPromoView.animate().alpha(1f).setDuration(
((RecyclerView) mStream.getView()).getItemAnimator().getAddDuration());
}
}
/**
* Update header views in the Stream.
* */
......@@ -563,9 +578,9 @@ public class FeedSurfaceCoordinator implements FeedSurfaceProvider {
}
public void onOverviewShownAtLaunch(long activityCreationTimeMs) {
mMediator.onOverviewShownAtLaunch(activityCreationTimeMs, mIsPlaceholderRequested);
mMediator.onOverviewShownAtLaunch(activityCreationTimeMs, mIsPlaceholderShownInitially);
StartSurfaceConfiguration.recordHistogram(FEED_STREAM_CREATED_TIME_MS_UMA,
mStreamCreatedTimeMs - activityCreationTimeMs, mIsPlaceholderRequested);
mStreamCreatedTimeMs - activityCreationTimeMs, mIsPlaceholderShownInitially);
}
Tracker getFeatureEngagementTracker() {
......
......@@ -14,7 +14,6 @@ import android.widget.ScrollView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.RecyclerView;
import org.chromium.base.MemoryPressureListener;
import org.chromium.base.memory.MemoryPressureCallback;
......@@ -211,13 +210,25 @@ public class FeedSurfaceMediator
public void onContentChanged() {
mStreamContentChanged = true;
if (mSnapScrollHelper != null) mSnapScrollHelper.resetSearchBoxOnScroll(true);
// Feed v2's background is set to be transparent in {@link
// FeedSurfaceCoordinator#createStream} to show the Feed placeholder. When first
// batch of articles are about to show, set recyclerView back to non-transparent.
// Feed v2 doesn't call onAddFinished(), so we hide placeholder here.
if (FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) {
stream.hidePlaceholder();
}
}
@Override
public void onAddFinished() {
// After first batch of articles are loaded, set recyclerView back to
// Feed v1's background is set to be transparent in {@link
// FeedSurfaceCoordinator#createStream} to show the Feed placeholder. After first
// batch of articles finish fade-in animation, set recyclerView back to
// non-transparent.
stream.getView().getBackground().setAlpha(255);
if (!FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) {
stream.hidePlaceholder();
}
if (mContentFirstAvailableTimeMs == 0) {
mContentFirstAvailableTimeMs = SystemClock.elapsedRealtime();
if (mHasPendingUmaRecording) {
......@@ -230,19 +241,11 @@ public class FeedSurfaceMediator
@Override
public void onAddStarting() {
if (!mCoordinator.isPlaceholderShown()) {
return;
}
// If the placeholder is shown, set sign-in box visible back.
RecyclerView recyclerView = (RecyclerView) stream.getView();
if (recyclerView != null) {
View signInView = recyclerView.findViewById(R.id.signin_promo_view_container);
if (signInView != null) {
signInView.setAlpha(0f);
signInView.setVisibility(View.VISIBLE);
signInView.animate().alpha(1f).setDuration(
recyclerView.getItemAnimator().getAddDuration());
}
// Feed v1's sign-in view is set to be invisible in {@link
// FeedSurfaceCoordinator#getSigninPromoView} if the Feed placeholder is shown. Set
// sign-in box visible back when Feed articles are about to show.
if (!FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) {
mCoordinator.fadeInSigninView();
}
}
};
......
......@@ -453,6 +453,17 @@ public class BasicStream implements Stream, ModelProviderObserver, OnLayoutChang
mModelProvider.triggerRefresh(RequestReason.HOST_REQUESTED);
}
@Override
public boolean isPlaceholderShown() {
return mIsPlaceholderShown;
}
@Override
public void hidePlaceholder() {
mRecyclerView.getBackground().setAlpha(255);
mIsPlaceholderShown = false;
}
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
......
......@@ -130,6 +130,16 @@ public interface Stream {
*/
void triggerRefresh();
/**
* @return Whether the placeholder is shown.
*/
boolean isPlaceholderShown();
/**
* Called when the placeholder is shown and the first batch of articles are about to show.
*/
void hidePlaceholder();
/**
* Get whether the first card of Feed is dense in portrait mode.
*/
......
......@@ -25,7 +25,6 @@ import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
*/
public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper {
private @Nullable FeedImageLoader mImageLoader;
private boolean mPlaceholderShown;
private Stream mStream;
public FeedStreamWrapper() {}
......@@ -46,7 +45,6 @@ public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper {
SnackbarManager snackbarManager, NativePageNavigationDelegate pageNavigationDelegate,
UiConfig uiConfig, boolean placeholderShown,
BottomSheetController bottomSheetController, FeedV1ActionOptions v1ActionOptions) {
mPlaceholderShown = placeholderShown;
FeedAppLifecycle appLifecycle = FeedProcessScopeFactory.getFeedAppLifecycle();
appLifecycle.onNTPOpened();
......@@ -63,7 +61,7 @@ public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper {
@Override
public boolean isPlaceholderShown() {
return mPlaceholderShown;
return mStream.isPlaceholderShown();
}
@Override
......
......@@ -53,12 +53,12 @@ public class FeedStream implements Stream {
public FeedStream(Activity activity, boolean isBackgroundDark, SnackbarManager snackbarManager,
NativePageNavigationDelegate nativePageNavigationDelegate,
BottomSheetController bottomSheetController) {
BottomSheetController bottomSheetController, boolean isPlaceholderShown) {
// TODO(petewil): Use isBackgroundDark to turn on dark theme.
this.mActivity = activity;
this.mFeedStreamSurface = new FeedStreamSurface(activity, isBackgroundDark, snackbarManager,
nativePageNavigationDelegate, bottomSheetController,
HelpAndFeedbackLauncherImpl.getInstance());
HelpAndFeedbackLauncherImpl.getInstance(), isPlaceholderShown);
}
@Override
......@@ -196,6 +196,16 @@ public class FeedStream implements Stream {
@Override
public void triggerRefresh() {}
@Override
public boolean isPlaceholderShown() {
return mFeedStreamSurface.isPlaceholderShown();
}
@Override
public void hidePlaceholder() {
mFeedStreamSurface.hidePlaceholder();
}
private void setupRecyclerView() {
mRecyclerView = (RecyclerView) mFeedStreamSurface.getView();
mRecyclerView.setId(R.id.feed_stream_recycler_view);
......
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.feed.v2;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
......@@ -56,6 +58,7 @@ import org.chromium.chrome.browser.xsurface.SurfaceScope;
import org.chromium.chrome.browser.xsurface.SurfaceScopeDependencyProvider;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.widget.animation.Interpolators;
import org.chromium.components.feed.proto.FeedUiProto.SharedState;
import org.chromium.components.feed.proto.FeedUiProto.Slice;
import org.chromium.components.feed.proto.FeedUiProto.StreamUpdate;
......@@ -126,6 +129,7 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
private String mBottomSheetOriginatingSliceId;
private final int mLoadMoreTriggerLookahead;
private boolean mIsLoadingMoreContent;
private boolean mIsPlaceholderShown;
private static ProcessScope sXSurfaceProcessScope;
......@@ -390,7 +394,7 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
public FeedStreamSurface(Activity activity, boolean isBackgroundDark,
SnackbarManager snackbarManager, NativePageNavigationDelegate pageNavigationDelegate,
BottomSheetController bottomSheetController,
HelpAndFeedbackLauncher helpAndFeedbackLauncher) {
HelpAndFeedbackLauncher helpAndFeedbackLauncher, boolean isPlaceholderShown) {
mNativeFeedStreamSurface = FeedStreamSurfaceJni.get().init(FeedStreamSurface.this);
mSnackbarManager = snackbarManager;
mActivity = activity;
......@@ -402,6 +406,8 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
mContentManager = new FeedListContentManager(this, this);
mIsPlaceholderShown = isPlaceholderShown;
Context context = new ContextThemeWrapper(
activity, (isBackgroundDark ? R.style.Dark : R.style.Light));
......@@ -547,7 +553,11 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
}
for (SliceUpdate sliceUpdate : streamUpdate.getUpdatedSlicesList()) {
if (sliceUpdate.hasSlice()) {
newContentList.add(createContentFromSlice(sliceUpdate.getSlice()));
FeedListContentManager.FeedContent content =
createContentFromSlice(sliceUpdate.getSlice());
if (content != null) {
newContentList.add(content);
}
} else {
String existingSliceId = sliceUpdate.getSliceId();
int position = mContentManager.findContentPositionByKey(existingSliceId);
......@@ -643,6 +653,10 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
return new FeedListContentManager.ExternalViewContent(
sliceId, slice.getXsurfaceSlice().getXsurfaceFrame().toByteArray());
} else if (slice.hasLoadingSpinnerSlice()) {
// If the placeholder is shown, spinner is not needed.
if (mIsPlaceholderShown) {
return null;
}
return new FeedListContentManager.NativeViewContent(sliceId, R.layout.feed_spinner);
}
assert slice.hasZeroStateSlice();
......@@ -1024,6 +1038,29 @@ public class FeedStreamSurface implements SurfaceActionsHandler, FeedActionsHand
mScrollReporter.trackScroll(dx, dy);
}
boolean isPlaceholderShown() {
return mIsPlaceholderShown;
}
/**
* Feed v2's background is set to be transparent in {@link FeedSurfaceCoordinator#createStream}
* if the Feed placeholder is shown. After first batch of articles are loaded, set recyclerView
* back to non-transparent. Since Feed v2 doesn't have fade-in animation, we add a fade-in
* animation for Feed background to make the transition smooth.
*/
void hidePlaceholder() {
if (!mIsPlaceholderShown) {
return;
}
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
mRootView.getBackground(), PropertyValuesHolder.ofInt("alpha", 255));
animator.setTarget(mRootView.getBackground());
animator.setDuration(mRootView.getItemAnimator().getAddDuration())
.setInterpolator(Interpolators.LINEAR_INTERPOLATOR);
animator.start();
mIsPlaceholderShown = false;
}
private static Context createFeedContext(Context context) {
if (!BundleUtils.isIsolatedSplitInstalled(context, FEED_SPLIT_NAME)) {
return context;
......
......@@ -20,6 +20,7 @@ import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
* Wraps management of the Feed V2 stream.
*/
public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper {
private Stream mStream;
@Override
public int defaultMarginPixels(Activity activity) {
return activity.getResources().getDimensionPixelSize(
......@@ -37,13 +38,14 @@ public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper {
SnackbarManager snackbarManager, NativePageNavigationDelegate pageNavigationDelegate,
UiConfig uiConfig, boolean placeholderShown,
BottomSheetController bottomSheetController, FeedV1ActionOptions v1ActionOptions) {
return new FeedStream(activity, showDarkBackground, snackbarManager, pageNavigationDelegate,
bottomSheetController);
mStream = new FeedStream(activity, showDarkBackground, snackbarManager,
pageNavigationDelegate, bottomSheetController, placeholderShown);
return mStream;
}
@Override
public boolean isPlaceholderShown() {
return false;
return mStream.isPlaceholderShown();
}
@Override
......
......@@ -365,6 +365,7 @@
<!-- This is in sp because we want the icon to scale with the TextView it sits alongside. -->
<dimen name="content_suggestions_card_modern_margin">12dp</dimen>
<dimen name="content_suggestions_card_modern_margin_v2">0dp</dimen>
<dimen name="content_suggestions_card_modern_padding_v2">16dp</dimen>
<dimen name="md_incognito_ntp_line_spacing">6sp</dimen>
<dimen name="md_incognito_ntp_padding_left">16dp</dimen>
<dimen name="cryptid_height_in_logo_wrapper">60dp</dimen>
......
......@@ -417,7 +417,7 @@ public class NewTabPage implements NativePage, InvalidationAwareThumbnailProvide
new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout),
mNewTabPageLayout, sectionHeaderView, new FeedV1ActionOptions(),
isInNightMode, this, mNewTabPageManager.getNavigationDelegate(), profile,
/* isPlaceholderRequested= */ false, bottomSheetController);
/* isPlaceholderShownInitially= */ false, bottomSheetController);
// Record the timestamp at which the new tab page's construction started.
uma.trackTimeToFirstDraw(mFeedSurfaceProvider.getView(), mConstructedTimeNs);
......
......@@ -155,7 +155,8 @@ public class FeedStreamSurfaceTest {
Profile.setLastUsedProfileForTesting(mProfileMock);
mFeedStreamSurface = Mockito.spy(new FeedStreamSurface(mActivity, false, mSnackbarManager,
mPageNavigationDelegate, mBottomSheetController, mHelpAndFeedbackLauncherImpl));
mPageNavigationDelegate, mBottomSheetController, mHelpAndFeedbackLauncherImpl,
/* isPlaceholderShown= */ false));
mContentManager = mFeedStreamSurface.getFeedListContentManagerForTesting();
mFeedStreamSurface.mRootView = Mockito.spy(mFeedStreamSurface.mRootView);
mRecyclerView = mFeedStreamSurface.mRootView;
......
......@@ -75,7 +75,7 @@ public class FeedStreamTest {
// Surfaces won't open until after startup.
FeedStreamSurface.startup();
mFeedStream = new FeedStream(mActivity, false, mSnackbarManager, mPageNavigationDelegate,
mBottomSheetController);
mBottomSheetController, /* isPlaceholderShown= */ false);
mFeedStream.onCreate(null);
mRecyclerView = (RecyclerView) mFeedStream.getView();
mLayoutManager = new FakeLinearLayoutManager(mActivity);
......
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