Commit 2dbe75cd authored by Theresa's avatar Theresa Committed by Commit Bot

[EoC] Create suggestions views after sheet is opened

Wait to create views for the suggested content until after the sheet is
opened. This will reduce memory cost if the sheet is never opened.

BUG=822955

Change-Id: I657f601cc1862227514eb599df205da2c5dee1ad
Reviewed-on: https://chromium-review.googlesource.com/990773
Commit-Queue: Theresa <twellington@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548084}
parent 74430c52
...@@ -27,11 +27,11 @@ import org.chromium.ui.base.WindowAndroid; ...@@ -27,11 +27,11 @@ import org.chromium.ui.base.WindowAndroid;
* {@link ContextualSuggestionsCoordinator} and lifecycle of sub-component objects. * {@link ContextualSuggestionsCoordinator} and lifecycle of sub-component objects.
*/ */
class ContentCoordinator { class ContentCoordinator {
private final ContextualSuggestionsModel mModel; private final SuggestionsRecyclerView mRecyclerView;
private final WindowAndroid mWindowAndroid;
private final ContextMenuManager mContextMenuManager;
private SuggestionsRecyclerView mRecyclerView; private ContextualSuggestionsModel mModel;
private WindowAndroid mWindowAndroid;
private ContextMenuManager mContextMenuManager;
private RecyclerViewModelChangeProcessor<ClusterListObservable, NewTabPageViewHolder> private RecyclerViewModelChangeProcessor<ClusterListObservable, NewTabPageViewHolder>
mModelChangeProcessor; mModelChangeProcessor;
...@@ -39,6 +39,26 @@ class ContentCoordinator { ...@@ -39,6 +39,26 @@ class ContentCoordinator {
* Construct a new {@link ContentCoordinator}. * Construct a new {@link ContentCoordinator}.
* @param context The {@link Context} used to retrieve resources. * @param context The {@link Context} used to retrieve resources.
* @param parentView The parent {@link View} to which the content will eventually be attached. * @param parentView The parent {@link View} to which the content will eventually be attached.
*/
ContentCoordinator(Context context, ViewGroup parentView) {
mRecyclerView = (SuggestionsRecyclerView) LayoutInflater.from(context).inflate(
R.layout.contextual_suggestions_layout, parentView, false);
}
/** @return The content {@link View}. */
View getView() {
return mRecyclerView;
}
/** @return The vertical scroll offset of the content view. */
int getVerticalScrollOffset() {
return mRecyclerView.computeVerticalScrollOffset();
}
/**
* Show suggestions, retrieved from the model, in the content view.
*
* @param context The {@link Context} used to retrieve resources.
* @param profile The regular {@link Profile}. * @param profile The regular {@link Profile}.
* @param uiDelegate The {@link SuggestionsUiDelegate} used to help construct items in the * @param uiDelegate The {@link SuggestionsUiDelegate} used to help construct items in the
* content view. * content view.
...@@ -46,15 +66,12 @@ class ContentCoordinator { ...@@ -46,15 +66,12 @@ class ContentCoordinator {
* @param windowAndroid The {@link WindowAndroid} for attaching a context menu listener. * @param windowAndroid The {@link WindowAndroid} for attaching a context menu listener.
* @param closeContextMenuCallback The callback when a context menu is closed. * @param closeContextMenuCallback The callback when a context menu is closed.
*/ */
ContentCoordinator(Context context, ViewGroup parentView, Profile profile, void showSuggestions(Context context, Profile profile, SuggestionsUiDelegate uiDelegate,
SuggestionsUiDelegate uiDelegate, ContextualSuggestionsModel model, ContextualSuggestionsModel model, WindowAndroid windowAndroid,
WindowAndroid windowAndroid, Runnable closeContextMenuCallback) { Runnable closeContextMenuCallback) {
mModel = model; mModel = model;
mWindowAndroid = windowAndroid; mWindowAndroid = windowAndroid;
mRecyclerView = (SuggestionsRecyclerView) LayoutInflater.from(context).inflate(
R.layout.contextual_suggestions_layout, parentView, false);
mContextMenuManager = new ContextMenuManager(uiDelegate.getNavigationDelegate(), mContextMenuManager = new ContextMenuManager(uiDelegate.getNavigationDelegate(),
mRecyclerView::setTouchEnabled, closeContextMenuCallback); mRecyclerView::setTouchEnabled, closeContextMenuCallback);
mWindowAndroid.addContextMenuCloseListener(mContextMenuManager); mWindowAndroid.addContextMenuCloseListener(mContextMenuManager);
...@@ -76,21 +93,15 @@ class ContentCoordinator { ...@@ -76,21 +93,15 @@ class ContentCoordinator {
}); });
} }
/** @return The content {@link View}. */
View getView() {
return mRecyclerView;
}
/** @return The vertical scroll offset of the content view. */
int getVerticalScrollOffset() {
return mRecyclerView.computeVerticalScrollOffset();
}
/** Destroy the content component. */ /** Destroy the content component. */
void destroy() { void destroy() {
// The model outlives the content sub-component. Remove the observer so that this object // The model outlives the content sub-component. Remove the observer so that this object
// can be garbage collected. // can be garbage collected.
mModel.mClusterListObservable.removeObserver(mModelChangeProcessor); if (mModelChangeProcessor != null) {
mWindowAndroid.removeContextMenuCloseListener(mContextMenuManager); mModel.mClusterListObservable.removeObserver(mModelChangeProcessor);
}
if (mWindowAndroid != null) {
mWindowAndroid.removeContextMenuCloseListener(mContextMenuManager);
}
} }
} }
...@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl; ...@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
/** /**
* The coordinator for the contextual suggestions UI component. Manages communication with other * The coordinator for the contextual suggestions UI component. Manages communication with other
...@@ -64,13 +65,45 @@ public class ContextualSuggestionsCoordinator { ...@@ -64,13 +65,45 @@ public class ContextualSuggestionsCoordinator {
} }
/** /**
* Preload the contextual suggestions in the {@link BottomSheet}; content won't actually be * Preload the contextual suggestions content in the {@link BottomSheet}; the sheet won't
* shown until {@link #showSuggestions()} is called. * actually be shown until {@link #showContentInSheet()} is called.
*/
void preloadContentInSheet() {
mToolbarCoordinator =
new ToolbarCoordinator(mActivity, mBottomSheetController.getBottomSheet(), mModel);
mContentCoordinator =
new ContentCoordinator(mActivity, mBottomSheetController.getBottomSheet());
mBottomSheetContent = new ContextualSuggestionsBottomSheetContent(
mContentCoordinator, mToolbarCoordinator);
// TODO(twellington): Handle the case where preload returns false.
mBottomSheetController.requestContentPreload(mBottomSheetContent);
}
/**
* Show the contextual suggestions content in the {@link BottomSheet}.
* {@link #preloadContentInSheet()} must be called prior to calling this method.
*
* Only the views needed for peeking the bottom sheet will be constructed. Another
* call to {@link #displaySuggestions()} is needed to finish inflating views for the
* suggestions cards.
*/
void showContentInSheet() {
// #preloadContentInSheet will always be called before this method, regardless of
// whether content was actually put in the sheet (meaning mBottomSheetContent should never
// be null). If content is not successfully preloaded
// BottomSheetController#requestContentPreload will return false.
assert mBottomSheetContent != null;
mBottomSheetController.requestShowContent(mBottomSheetContent, false);
}
/**
* Finish showing the contextual suggestions in the {@link BottomSheet}.
* {@link #showContentInSheet()} must be called prior to calling this method.
* *
* @param suggestionsSource The {@link SuggestionsSource} used to retrieve additional things * @param suggestionsSource The {@link SuggestionsSource} used to retrieve additional things
* needed to display suggestions (e.g. favicons, thumbnails). * needed to display suggestions (e.g. favicons, thumbnails).
*/ */
void preloadSuggestionsInSheet(SuggestionsSource suggestionsSource) { void showSuggestions(SuggestionsSource suggestionsSource) {
SuggestionsNavigationDelegate navigationDelegate = new SuggestionsNavigationDelegateImpl( SuggestionsNavigationDelegate navigationDelegate = new SuggestionsNavigationDelegateImpl(
mActivity, mProfile, mBottomSheetController.getBottomSheet(), mTabModelSelector); mActivity, mProfile, mBottomSheetController.getBottomSheet(), mTabModelSelector);
SuggestionsUiDelegateImpl uiDelegate = SuggestionsUiDelegateImpl uiDelegate =
...@@ -79,31 +112,24 @@ public class ContextualSuggestionsCoordinator { ...@@ -79,31 +112,24 @@ public class ContextualSuggestionsCoordinator {
mActivity.getChromeApplication().getReferencePool(), mActivity.getChromeApplication().getReferencePool(),
mActivity.getSnackbarManager()); mActivity.getSnackbarManager());
// TODO(twellington): Introduce another method that creates bottom sheet content with only mContentCoordinator.showSuggestions(mActivity, mProfile, uiDelegate, mModel,
// a toolbar view when suggestions are fist available, and use this method to construct the
// content view when the sheet is opened.
mToolbarCoordinator =
new ToolbarCoordinator(mActivity, mBottomSheetController.getBottomSheet(), mModel);
mContentCoordinator = new ContentCoordinator(mActivity,
mBottomSheetController.getBottomSheet(), mProfile, uiDelegate, mModel,
mActivity.getWindowAndroid(), mActivity::closeContextMenu); mActivity.getWindowAndroid(), mActivity::closeContextMenu);
mBottomSheetContent = new ContextualSuggestionsBottomSheetContent(
mContentCoordinator, mToolbarCoordinator);
// TODO(twellington): Handle the case where preload returns false.
mBottomSheetController.requestContentPreload(mBottomSheetContent);
} }
/** /**
* Show the contextual suggestions in the {@link BottomSheet}. * Add an observer to the {@link BottomSheet}.
* {@link #preloadSuggestionsInSheet()} must be called prior to calling this method. * @param observer The observer to add.
*/ */
void showSuggestions() { void addBottomSheetObserver(BottomSheetObserver observer) {
// #preloadSuggestionsInSheet will always be called before this method, regardless of mBottomSheetController.getBottomSheet().addObserver(observer);
// whether content was actually put in the sheet (meaning mBottomSheetContent should never }
// be null). If content is not successfully preloaded
// BottomSheetController#requestContentPreload will return false. /**
assert mBottomSheetContent != null; * Remove an observer to the {@link BottomSheet}.
mBottomSheetController.requestShowContent(mBottomSheetContent, false); * @param observer The observer to remove.
*/
void removeBottomSheetObserver(BottomSheetObserver observer) {
mBottomSheetController.getBottomSheet().removeObserver(observer);
} }
/** Removes contextual suggestions from the {@link BottomSheet}. */ /** Removes contextual suggestions from the {@link BottomSheet}. */
......
...@@ -17,6 +17,9 @@ import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; ...@@ -17,6 +17,9 @@ import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.MathUtils;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.StateChangeReason;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.ui.widget.Toast; import org.chromium.ui.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -37,12 +40,10 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -37,12 +40,10 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
private final EnabledStateMonitor mEnabledStateMonitor; private final EnabledStateMonitor mEnabledStateMonitor;
private final ChromeFullscreenManager mFullscreenManager; private final ChromeFullscreenManager mFullscreenManager;
@Nullable private @Nullable SnippetsBridge mBridge;
private SnippetsBridge mBridge; private @Nullable FetchHelper mFetchHelper;
@Nullable private @Nullable String mCurrentRequestUrl;
private FetchHelper mFetchHelper; private @Nullable BottomSheetObserver mSheetObserver;
@Nullable
private String mCurrentRequestUrl;
private boolean mDidSuggestionsShowForTab; private boolean mDidSuggestionsShowForTab;
...@@ -82,8 +83,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -82,8 +83,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
// the top controls. // the top controls.
if (!mDidSuggestionsShowForTab && mModel.hasSuggestions() if (!mDidSuggestionsShowForTab && mModel.hasSuggestions()
&& areBrowserControlsHidden() && mBridge != null) { && areBrowserControlsHidden() && mBridge != null) {
mDidSuggestionsShowForTab = true; showContentInSheet();
mCoordinator.showSuggestions();
} }
} }
...@@ -97,8 +97,17 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -97,8 +97,17 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
/** Destroys the mediator. */ /** Destroys the mediator. */
void destroy() { void destroy() {
if (mFetchHelper != null) mFetchHelper.destroy();
mEnabledStateMonitor.destroy(); mEnabledStateMonitor.destroy();
if (mFetchHelper != null) {
mFetchHelper.destroy();
mFetchHelper = null;
}
if (mBridge != null) {
mBridge.destroy();
mBridge = null;
}
} }
/** /**
...@@ -117,11 +126,15 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -117,11 +126,15 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
} else { } else {
clearSuggestions(); clearSuggestions();
mFetchHelper.destroy(); if (mFetchHelper != null) {
mFetchHelper = null; mFetchHelper.destroy();
mFetchHelper = null;
}
mBridge.destroy(); if (mBridge != null) {
mBridge = null; mBridge.destroy();
mBridge = null;
}
} }
} }
...@@ -129,6 +142,8 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -129,6 +142,8 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
public void requestSuggestions(String url) { public void requestSuggestions(String url) {
mCurrentRequestUrl = url; mCurrentRequestUrl = url;
mBridge.fetchContextualSuggestions(url, (suggestions) -> { mBridge.fetchContextualSuggestions(url, (suggestions) -> {
if (mBridge == null) return;
// Avoiding double fetches causing suggestions for incorrect context. // Avoiding double fetches causing suggestions for incorrect context.
if (!TextUtils.equals(url, mCurrentRequestUrl)) return; if (!TextUtils.equals(url, mCurrentRequestUrl)) return;
...@@ -137,10 +152,10 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -137,10 +152,10 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
.show(); .show();
if (suggestions.size() > 0) { if (suggestions.size() > 0) {
preloadSuggestions(generateClusterList(suggestions), suggestions.get(0).mTitle); preloadContentInSheet(generateClusterList(suggestions), suggestions.get(0).mTitle);
// If the controls are already off-screen, show the suggestions immediately so they // If the controls are already off-screen, show the suggestions immediately so they
// are available on reverse scroll. // are available on reverse scroll.
if (areBrowserControlsHidden()) mCoordinator.showSuggestions(); if (areBrowserControlsHidden()) showContentInSheet();
} }
}); });
} }
...@@ -163,15 +178,35 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -163,15 +178,35 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
mModel.setTitle(null); mModel.setTitle(null);
mCoordinator.removeSuggestions(); mCoordinator.removeSuggestions();
mCurrentRequestUrl = ""; mCurrentRequestUrl = "";
if (mSheetObserver != null) {
mCoordinator.removeBottomSheetObserver(mSheetObserver);
}
} }
private void preloadSuggestions(ClusterList clusters, String title) { private void preloadContentInSheet(ClusterList clusters, String title) {
if (mBridge == null) return; if (mBridge == null) return;
mModel.setClusterList(clusters); mModel.setClusterList(clusters);
mModel.setCloseButtonOnClickListener(view -> { clearSuggestions(); }); mModel.setCloseButtonOnClickListener(view -> { clearSuggestions(); });
mModel.setTitle(mContext.getString(R.string.contextual_suggestions_toolbar_title, title)); mModel.setTitle(mContext.getString(R.string.contextual_suggestions_toolbar_title, title));
mCoordinator.preloadSuggestionsInSheet(mBridge); mCoordinator.preloadContentInSheet();
}
private void showContentInSheet() {
mDidSuggestionsShowForTab = true;
mSheetObserver = new EmptyBottomSheetObserver() {
@Override
public void onSheetOpened(@StateChangeReason int reason) {
mCoordinator.showSuggestions(mBridge);
mCoordinator.removeBottomSheetObserver(this);
mSheetObserver = null;
}
};
mCoordinator.addBottomSheetObserver(mSheetObserver);
mCoordinator.showContentInSheet();
} }
// TODO(twellington): Remove after clusters are returned from the backend. // TODO(twellington): Remove after clusters are returned from the backend.
......
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