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